Files
sentiment-engine/prod/docs/DUMB_QUESTION_REVIEW_20260723.md
Codex d4b32a6549 docs(dumb-review): correct E1 — single-position engine self-clears; real phantom bug found+fixed
E1 as submitted (release phantom on promotion-SUPPRESSED entries) does not survive
review: the orchestrator is single-position and self-clears exit_manager+self.position
on its own exit each cycle (esf_alpha_orchestrator 491-492), so shadow mode is a faithful
paper mirror and pi's fix would regress shadow parity. The investigation instead found the
real bug — phantom_release left engine.position set → single-position engine bricked on a
proven reject — fixed in f11.1/blue-sizing-parity 98941373. Preserves pi's original E1 text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 19:15:17 +02:00

28 KiB
Raw Blame History

The "Dumb-Question" Review — self-defeating logic bugs in the live UV/VIOLET stack

2026-07-23 · pi (READ-ONLY sweep; nothing edited or run). Scope tree: /root/uv-wt/f10b-acbv6 (branch f11/fill-driven-exit, HEAD b655e32). Priority paths swept: prod/clean_arch/violet/uv/ (exec, tpsl/exit, sizing, promotion, hooks, blue_prime), prod/clean_arch/dita_v2/ (capital/account/fill), prod/bingx/.

What this review hunts (the bar)

ONE class only: egregious, self-defeating logic — a mechanism that obviously defeats its own stated purpose, such that a competent engineer shown it says "that's dumb." Calibrated against the two already-caught bugs:

  1. Mechanism not wired to its target — fill-driven EXIT arming registered ENTRY orders but not the ticker's EXIT orders → exit fills came back UNMATCHED, the watch never disarmed. (Now fixed on this branch — verified: blue_prime/runner.py:635-658 registers ticker EXIT intents via set_on_exit_submit.)
  2. Safeguard guarding the wrong signal — an E-capital staleness guard halted on balance AGE while the venue only pushes on CHANGE. (Now fixed — verified: e_feed.py:711-745 re-stamps off a fresh signed venue read and fail-soft keeps the feed alive; it guards liveness, not age.)

NOT reported here: perf, style, exotic-timing races, "could be cleaner." Anything that did not clearly clear the bar is in the BORDERLINE section or omitted.

Verdict

5 egregious findings as submitted — but E1 was corrected on review (it does not survive; the investigation it prompted found + fixed a different real phantom bug — see the correction block under E1). Net: 4 egregious as-stated + 1 corrected-to-real-fix. Original submitted count below. (2 directly capital-affecting on the live armed path; the other 3 are correctness/safety of the capital-bookkeeping + venue layers). Plus 11 borderline items (self-contradicting comments, unconsumed reconcile verdicts, observability divergences, latent/dead-path sign bugs, one wrong-object hook coupling) recorded for completeness. Every egregious finding below was re-verified by hand against the source — file:line confirmed.

Ranked by egregiousness × blast-radius (capital first):

# Finding Bucket Blast
E1 Phantom-slot release unreachable on promotion-suppressed entries DOES NOT SURVIVE — see correction; real bug found + fixed mechanism-not-wired corrected
E2 E→K account mirror wire dropped by the factory declared-but-dead capital-bookkeeping correctness
E3 Funding sign inverted on the K-capital side right-idea-wrong-coupling capital math (reconcile; latent for sizing)
E4 Tripwire safety verdict dropped by the account contract serializer self-contradicting safeguard safety / correctness (cross-process; latent)
E5 prod/bingx/execution.py is import-dead (whole VST client) declared-but-dead correctness (dead module; not live path)

EGREGIOUS FINDINGS

CORRECTION (2026-07-23, claude — source-verified against the readable kernel). E1 as stated does not survive review, and its suggested fix would be a parity regression — but the investigation it prompted found a different, real bug of the original phantom class, now fixed (commit 98941373, f11.1/blue-sizing-parity).

Why E1-as-stated is wrong: the orchestrator (esf_alpha_orchestrator.process_bar) is single-position. Its entry gate is self.position is None; it self-registers on entry (627/652) and self-clears on its own TP/SL/max-hold exit every cycle (_execute_exit 491-492), evaluating the exit each bar. There is no multi-phantom "accumulation" (one position by construction) and no permanent "freeze" (self-clears). In shadow mode it runs a faithful single-position paper trade identical to BLUE. Releasing the phantom on every promotion-SUPPRESSED entry (pi's fix) would, in shadow mode (gate always inactive), clear self.position every scan → the engine re-enters every scan instead of holding → its own exit alpha never runs → shadow parity regression. So the suggested fix was not implemented.

The REAL bug (found while verifying E1): the shipped phantom_release clears exit_manager.reset_position(trade_id) but not engine.position. Since the entry gate is self.position, and evaluate() returns NO_STATE (never EXIT) for the popped trade_id (alpha_exit_manager.py:124), a proven venue/kernel reject left the single-position engine wedged — never re-enters, never exits the ghost. Real-kernel repro confirmed BRICKED=True. Fix: clear BOTH (mirroring _execute_exit 491-492), guarded to the exact phantom trade_id. This is the genuine finding; pi's specific mechanism (not-wired-to-suppressions) was the wrong diagnosis of a real problem.

The original (as-submitted) E1 text is preserved below for the record.

E1 — Phantom-slot release is unreachable on promotion-SUPPRESSED entries (the common non-fill path)

Egregiousness. phantom_release.py was written to kill F10 bug #2: the engine self-registers a position at DECISION time, before any order is placed, so a non-fill must be unwound or "the engine wrongly believes it holds the asset and will not re-enter it." The release is wired ONLY to the promoted-then-rejected path — the exact case it most needs to cover (promotion suppressed the order, so it was never placed) never reaches it. A safety net that skips the most common way the thing it protects against happens.

Mechanism + intended purpose. _release_phantom_on_reject(engine, intent, receipt) (phantom_release.py:30-58) resets engine.exit_manager's decision-time position on a PROVEN reject. The engine registers the phantom inside harness.step(...) (blue_prime/runner.py:880), which runs BEFORE bridge.try_promote_result(...) (runner.py:950).

Exact failure. The ONLY call site of _release_phantom_on_reject is inside the closure _register_entry_from_receipt (runner.py:1124), and that closure is invoked only at runner.py:1190 (if _promoted and _receipt_future is not None) and runner.py:1197-1198 (elif _promoted and _receipt is not None). Both require _promoted=True. When promotion SUPPRESSES an organic ENTER — book-health reject, governor defer, asset-not-on-venue, stale-capital error, or gate_inactive in shadow mode (promotion.py:528,558,588,605,670) — _promoted=False and the closure never runs, so the decision-time registration already made by the engine is never unwound. The not-promoted branch that DOES exist (runner.py:964-971) releases only the FORCE_ENGAGE concurrency slot, not the engine's phantom position. Verified: grep finds exactly one _release_phantom_on_reject call in the runner; there is no organic not-promoted release.

  • File:line: phantom_release.py:30-58; sole caller runner.py:1124 inside a _promoted-gated closure (runner.py:1190, 1197-1198); registration-before-promotion ordering runner.py:880 vs :950; the not-promoted branch that omits it runner.py:964-971.

Blast-radius — capital / correctness. Armed mode: every book-health / governor / venue-listing / stale-capital suppression leaks a phantom into exit_manager; the engine then believes it holds that asset and stops re-entering it, silently diverging blue_prime's decisions from real BLUE (the whole fidelity premise). Shadow mode: the gate is always inactive, so EVERY organic entry decision leaks a phantom. Accumulating phantoms progressively freeze the decision surface.

The fix would be… call the phantom release on the organic not-promoted branch too (where a suppressed/synchronously-rejected ENTER is known non-live), not only inside the _promoted-gated receipt closure — i.e. unwind the decision-time registration whenever the order provably was not placed.


E2 — The E→K account-truth mirror is built, handed in, then dropped on the floor by its factory

Egregiousness. The whole point of EKBridge is "every venue-accepted account event is mirrored into the kernel's K-account; E stays authority, K mirrors." The armed runner constructs the bridge and passes it into the E-feed factory — which accepts the parameter and then never forwards it to the object that would use it. The mirror can never run in the live process, and its stats read zero, hiding that it never ran.

Mechanism + intended purpose. LiveEFeed stores self._kernel_bridge (e_feed.py:640) and, on every published account observation, calls self._kernel_bridge.mirror_future(converted) under an if self._kernel_bridge is not None: guard (e_feed.py:686-689). The live caller supplies it correctly: _e_feed = maybe_build_e_feed(..., kernel_bridge=EKBridge(_exec_executor)) (blue_prime/runner.py:479-481).

Exact failure. maybe_build_e_feed(plane, *, io_loop=None, kernel_bridge=None) (e_feed.py:853) accepts kernel_bridge but the LiveEFeed(...) construction it returns (e_feed.py:873-879) passes only EFeed(plane), subscribe, account_snapshot, close_source, refresh_interval_s, io_loop — kernel_bridge is never forwarded. So LiveEFeed._kernel_bridge stays None, the mirror guard is always false, and mirror_future is never called. Unlike the fill observer (which has attach_fill_observer, wired post-construction at runner.py:633), there is no attach_kernel_bridge — construction is the only channel, and the factory eats it. Verified: grep confirms the caller supplies kernel_bridge=, the factory signature accepts it, and no attach method exists.

  • File:line: accepts e_feed.py:853; drops e_feed.py:873-879; dead consumer e_feed.py:686-689; live caller that supplies it blue_prime/runner.py:479-481.

Blast-radius — capital-bookkeeping correctness + observability. The DITAv2 kernel's K-account projection never receives venue truth via this path in the live armed process, so anything reading K-account state runs on an unfed projection; EKBridge.stats shows 0 bridged, masking the gap. E remains authoritative (the capital provider still works), which is exactly what keeps this quiet.

The fix would be… forward kernel_bridge into the LiveEFeed(...) constructor in maybe_build_e_feed (or add an attach_kernel_bridge and wire it like the fill observer).


E3 — Funding sign inverted on the K-capital side vs the E-side sibling and the documented convention

Egregiousness. For a single funding event the E-wallet moves UP and K-capital moves DOWN — the two account projections disagree in direction on the same event, and the K side treats received funding as a cost. One of them is provably wrong by the field's own docstring.

Mechanism + intended purpose. AccountProjectionV2 folds funding into K-capital as k_capital = seed + Σrealized − Σfees − Σfunding (account.py:416). ExchangeEvent.funding_amount is documented "positive = received, negative = paid" (exchange_event.py:87).

Exact failure. apply_funding does self._k_funding += amount (account.py:371) and k_capital subtracts _k_funding — so a positive (received) funding reduces K-capital. The E-side sibling does the opposite and correct thing: asex_account_publisher.apply_funding → self._wallet_balance += amount (asex_account_publisher.py:51), so positive funding increases the E-wallet. The adapter passes event.funding_amount straight through with no flip. Verified by reading both sites: same input amount, opposite sign into capital.

  • File:line: account.py:369-372 + :416 (K subtracts received funding); asex_account_publisher.py:49-54 (E adds it); convention exchange_event.py:87.

Blast-radius — capital math (latent). In the observed wiring, published capital comes from E (wallet_balance), so sizing is not directly corrupted today; the damage is a guaranteed 2×funding K-vs-E divergence on reconcile R1 every funding tick, and direct capital corruption on any path that trusts k_capital. (Compounded by E2: K may be under-fed in the live armed process anyway.)

The fix would be… align the K side with the convention and the E sibling — treat a positive funding_amount as an increase to capital (add received funding), not a subtracted cost.


E4 — The account-snapshot serializer drops the tripwire safety verdict it says consumers MUST read

Egregiousness. AccountStateSnapshot's docstring is explicit: "reconcile_ok kept for backward compat — consumers MUST read tripwire_ok." Its own as_dict() serializes the deprecated reconcile_ok and OMITS tripwire_ok. The one field the contract says is the go/no-go safety carrier cannot cross the transport; the field it says not to read is the only one that does.

Mechanism + intended purpose. Rev-4-FINAL replaced K-derived reconcile_ok with a stateless per-event tripwire_ok (contracts.py:413-431) as the account-health carrier. The real shared-memory plane transports the account region via as_dict() (real_zinc_plane.py publish_account) and reconstructs from the payload.

Exact failure. AccountStateSnapshot.as_dict() (contracts.py:433-442) returns wallet_balance, available_margin, used_margin, event_seq, mono_ns, e_live, reconcile_ok — no tripwire_ok, no tripwire_last_event_id. Any cross-process reader that reconstructs from the dict defaults tripwire_ok=True (the frozen default, contracts.py:430), so a tripped tripwire (False) is silently lost and every reader sees "healthy." The authors already know this — e_feed.py:205-208 notes "as_dict currently omits the tripwire fields" and hand-rolls a separate encoder to dodge it — but that workaround does not cover the plane's as_dict path. Verified by reading as_dict.

  • File:line: contracts.py:433-442 (omits the mandatory field, emits the deprecated one); docstring contradiction contracts.py:417-418.

Blast-radius — safety / correctness (latent). Any cross-process consumer of the real account plane can never observe a safety trip. Latent today because the live capital consumer (e_capital_provider.make_e_capital_provider, e_capital_provider.py:31-44) gates only on e_live/wallet_balance/freshness and never reads tripwire_ok at all — so even in-process the verdict is unconsumed. The in-memory test plane passes the object by reference, so the field survives there, which is why this hides in tests and would only bite on the real shared-memory transport.

The fix would be… serialize tripwire_ok + tripwire_last_event_id in as_dict() and reconstruct them on the plane; and have the capital provider actually consult the tripwire before returning wallet_balance.


E5 — prod/bingx/execution.py (the whole VST Nautilus execution client) cannot be imported

Egregiousness. The task named this file as primary live scope; it is dead-on-arrival. Two unconditional top-level imports reference modules that do not exist in the package, so import prod.bingx.execution raises ModuleNotFoundError before any of its listenKey / PostOnly / reduceOnly / drift logic is reachable. An "execution client" that cannot load.

Mechanism + intended purpose. BingxExecutionClient is the Nautilus VST execution client that prod/bingx/factories.py:13 wires and prod/clean_arch/violet/exec/vst_adapter.py documents itself as wrapping.

Exact failure. execution.py:68 from .sizing_mode import build_split_sizing_payload and execution.py:89 from .websocket import BingxUserStream — neither prod/bingx/sizing_mode.py nor prod/bingx/websocket.py exists (the dir has market_stream.py, not websocket.py). Verified: ls confirms both files absent; AST scan confirms both imports are top-level module scope. Consequently the whole client, its LiveExecClientFactory, and vst_adapter.py are non-loadable. The live BingX path does NOT use this file — it runs through prod/clean_arch/adapters/bingx_direct.py + dita_v2/bingx_user_stream.py (which the live E-feed imports at e_feed.py:859).

  • File:line: execution.py:68, :89; would-be consumers prod/bingx/factories.py:13,25,29, prod/clean_arch/violet/exec/vst_adapter.py.

Blast-radius — correctness (dead module, NOT live capital). No live-path impact because live trading goes through bingx_direct.py. The hazard is that a whole VST execution client + its factory + adapter present as usable code but cannot load; anything that ever tries to revive that path fails immediately, and any audit that trusts this file as "the live exec logic" is auditing a corpse. (Everything the task flagged inside this file — listenKey keepalive, PostOnly, use_reduce_only gate, drift check — is unreachable.)

The fix would be… either restore/rename the missing sizing_mode/websocket modules (or fix the imports to the real ones, e.g. market_stream), or delete the dead client + its factory + adapter so nothing presents it as live.


BORDERLINE (real, but did not clearly clear the egregiousness bar / low or latent blast)

Grouped by taxonomy.

safeguard-guarding-the-wrong-thing

  • B1 — BookHealthGate "exit-side liquidity" is hardcoded to the ASK side. Check 6 (exec/book_health_gate.py:392-395) rejects on h.n_ask_levels < self._min_exit_levels with the comment "a short must buy back into the ask." For a LONG the exit SELLs into the BID, so this measures the wrong side. Compounding: is_tradeable receives side (:324) but passes only (asset, notional_usd) to _evaluate (:352), so side never steers the decision — it only stamps telemetry. BORDERLINE because it is a SOFT check, BLUE is short-dominated, and the health snapshot carries only n_ask_levels (no bid data), so it is partly a data limitation, not just a branch. Fix: thread side into the gate and check bid-side depth for longs (requires bid-side data in the snapshot).

  • B2 — Reconcile R2 compares cumulative realized against a single fill's realized. delta_r2 = abs(k.realized_pnl − e.last_fill_realized_pnl) (account.py:529-531): k.realized_pnl is cumulative Σrealized, e.last_fill_realized_pnl is only the latest fill. After the 2nd fill R2 trips on essentially every multi-fill trade. BORDERLINE only because reconcile.status has no located prod consumer (effectively computed-but-unused today). Fix: compare like-for-like (cumulative vs cumulative).

right-idea-wrong-coupling

  • B3 — Reconcile R4 compares open_notional against used_margin. Comment says "open_notional vs exchange notional" but the code does abs(k.open_notional − e.used_margin) (account.py:552-556) with a 0.3%-of-notional band; used_margin = notional/leverage. For any leverage > 1 this diverges by ≈(1 − 1/lev)×notional → permanent ERROR. BORDERLINE: same unconsumed-reconcile.status caveat as B2. Fix: compare notional-to-notional (or margin-to-margin consistently).

  • B11 — Hook 05 tp_threshold syncs the leverage-adjusted soft-TP onto the wrong object; the UV live exit never reads it. BLUE's authoritative reference sets the live TP on the ENGINE — self.eng.set_live_tp_pct(tp_pct) (/opt/dolphin/prod/nautilus_event_trader.py:752). Hook 05 instead computes the leverage-conditioned soft-TP (compute_our_leverage / compute_soft_tp_pct) and pushes it into engine.exit_manager: exit_manager.set_live_tp_pct(tp_pct) else setattr(exit_manager, "live_tp_pct", tp_pct) (hooks/05_tp_threshold.py:48,73-77) — an extra .exit_manager hop BLUE does not take. Verified: across the UV tree live_tp_pct is ONLY ever written (all four refs are inside hook 05); nothing reads it back. The UV live exit path (brain_tpsl.py, tpsl_ticker.py) consumes tp_base_pct (from engine._tp_base_pct/exit_manager.fixed_tp_pct, runner.py:600-607) and never calls compute_soft_tp_pct/compute_our_leverage at all — so the hook's leverage tightening is dropped and UV exits on the base fixed TP. Contrast the sibling hook 04, which correctly targets the engine (engine.set_esof_advisory_score, 04_esof_size_gate.py:50), and hook 11, which correctly targets engine.exit_manager (11_catastrophic_floor.py:124-142) matching BLUE. BORDERLINE (not egregious) for two honest reasons: (1) the adjustment is a "very small leverage-conditioned tightening" per prod/clean_arch/tp_curve.py:50-57, so the PnL magnitude is small; (2) it could not be statically disproven that the compiled D_LIQ exit_manager exposes a working set_live_tp_pct (BLUE calls the engine-level setter, strong evidence the canonical setter is engine-level — but the compiled surface has no .py on disk). Right-idea-wrong-coupling. Fix: set the live TP on the engine (engine.set_live_tp_pct) as BLUE does, and/or have the UV exit path consume the leverage-adjusted TP; confirm against the live engine's method surface.

  • B4 — Commission sign passed through unflipped in the friction path. friction.py:150-156 returns BingX's raw commission (negative = cost) unflipped; consumer execution.py:1065/1526 would book a normal cost fill as a negative commission (a rebate). The live path flips it correctly (bingx_user_stream.py:361-362, fee = -raw_fee). BORDERLINE: the only consumers are the dead execution.py (E5) and characterization.py (a bench), so it is latent, not live. Fix: negate n at the friction seam to match the live convention.

self-contradicting-logic (comment vs behavior)

  • B5 — "SL evaluated before TP" docstring over TP-first code. TpSlHandler.on_tick docstring says "D7: SL evaluated before TP" (brain_tpsl.py:319), but _evaluate runs TP_FLOOR → FIXED_TP → SL (:455-479, with an explicit "BLUE fixed TP precedes SL" comment at :463-464). Behavior is correct (matches BLUE doctrine); the docstring lies. Runtime blast none; review-hazard — an editor trusting it could flip to loss-first. Fix: correct the docstring.

  • B6 — Trailing-TP comment claims "before fixed TP", code runs it last. brain_tpsl.py:481 "Trailing TP (before fixed TP…)" but the _trail_hit return sits AFTER FIXED_TP and SL. Effectively dead (trail arms only under UV_ENABLE_NONPARITY_TRAIL=1, which the runner never sets). Fix: correct the comment or remove the dead branch.

computed-but-unused / fired-into-void

  • B7 — on_bar scan-path state=="closed" cleanup + MAX_HOLD logs are dead in the default dispatch mode. With UV_EXIT_DISPATCH=1 (default), submit_decision returns "dispatched", never "closed"/"rejected" synchronously (brain_tpsl.py:391), so the inline _bars.pop cleanup and both MAX_HOLD "EXIT"/"EXIT REJECTED" log branches (tpsl_ticker.py:316-346) never fire in prod — the intended MAX_HOLD exit signal is lost (reconcile handles the real close). Benign for correctness. Fix: emit the MAX_HOLD log from the dispatch completion path.

  • B8 — Per-scan TP re-stamp feeds pnl_pct=0.0, so the journaled TP curve omits the profit-gated modulation the live exit uses. on_bar calls _resolve_tp(pos, 0.0, max_favorable, …, persist=True) (tpsl_ticker.py:277-283); the profit branches in blue_tp_yield (WITHDRAWAL_STRESS_PROFIT ×0.60, PER_ASSET_WITHDRAWAL_PROFIT ×0.75) only fire when pnl > 0, so the persisted tp_effective_pct/branch systematically omit them, while the live exit path passes the true pct_move (brain_tpsl.py:418). Observability only — the TUI/journal TP diverges from the enforced TP exactly in the profit cases you'd audit. Fix: pass the true pct_move to the diagnostic re-stamp.

act-at-the-wrong-time / fallback-that-masks

  • B9 — ACB day-roll can size on inert beta=0.0 at the UTC boundary before the sink hydrates. on_day_roll runs begin_day (sets beta=0.0 with no ACB injected) then _feeds the provider (acb_beta_wiring.py:224-232); SinkBoostProvider returns None on a missing key and _feed "keeps prior value" (:282-283) — but the prior value at a fresh day is the just-reset 0.0. So at each UTC roll / cold start there is a window of scans that size on neutral beta (the exact inertness this module exists to remove), and it is silent. BORDERLINE — self-heals within the sink refresh cadence; small window. Fix: block/flag entries until a real beta has hydrated for the new date rather than sizing on the reset default.

computed-not-wired-to-this-target

  • B10 — The RateGovernor is not on the live BingX transport, and http.py doesn't recognize BingX's own rate-limit code. prod/bingx/rate_governor.py is built around the 100410 rejection, but nothing in prod/bingx/http.py (the client every order flows through) calls acquire()/note_100410(); the only callers are the tempo lane + blue_prime runner. And http.py:826-833 recognizes only 109400 (+ loose "rate limit" text), not 100410. BORDERLINE — the governor IS used by the tempo lane; whether live orders route through that lane vs straight through bingx_direct was not confirmed. Fix: route the order path through the governor, and teach http.py the 100410 code.

Minor (noted, not the target class)

  • tpsl_ticker.py:455 — LOGGER.warning("TP capital provider failed: %s: %s", exc) has two format specifiers but one argument (malformed log on the capital-provider failure path).

Cleared on inspection (checked, NOT bugs — recorded so they aren't re-hunted)

  • Calibration #1 (fill-driven EXIT wiring): closed — ticker EXIT intents register with the fill-router at submit (blue_prime/runner.py:635-658); exit fills route to _apply_exit → net-qty reduce → disarm.
  • Calibration #2 (E-capital staleness): the live E-feed re-stamps off a fresh signed venue read and fail-soft keeps the feed alive (e_feed.py:711-745) — guards liveness, not age.
  • TP mechanics: TP binds to entry_price reconciled to fill price; TP_FLOOR ratchets one-way on monotonic max_favorable and exits on regression to the anchor (correct direction); SL sign/tighten-widen matches doctrine.
  • Sizing: size_trade binds quantity to effective_notional/mark_price with consistent exchange leverage; capital → sizing binds to the E-anchored provider fresh per promote, fail-closed when armed.
  • ACB leg-A / leg-B: feed_proxy_b IS called before step_bar (blue_prime/runner.py:865); the None-on-read-miss doctrine correctly never resets a good beta mid-day.
  • Live BingX path (bingx_direct.py + bingx_user_stream.py): reduceOnly set on exits; one-way positionSide=BOTH; PostOnly via timeInForce; listenKey keepalive renews at 1800s and forces reconnect on persistent failure; signing clock offset + reactive re-sync; commission sign flipped correctly.
  • use_reduce_only defaults True (config.py:51).
  • governor_gate / FORCE_ENGAGE Poisson clock / exec_control IOC budget: consistent with their stated invariants.

Method: six parallel read-only subagent hunts across exec / tpsl-exit-sizing / dita_v2-capital / bingx-venue / blue_prime-promotion / hooks-advisors, then per-finding hand-verification of every egregious item against source (file:line confirmed). The hooks advisor chain (uv/hooks/01-11) is wired (build_hooks/HookRunner, blue_prime/runner.py:438-445); most hooks are correctly coupled to real consumers (01/10 read back via resolve_effect_bool/_resolve_step_prices at runner.py:847,854; 02/03/08/09 mutate real engine state; 04/11 target the engine and the exit_manager respectively, matching BLUE; 06/07 are shadow-advisory by explicit design). The one coupling defect found is B11 (hook 05's leverage-adjusted TP written to exit_manager — an object the UV live exit never reads), kept in BORDERLINE for the reasons stated there. No egregious computed-but-unused ACTUATION bug was confirmed in the hook chain within this pass.