diff --git a/prod/docs/DUMB_QUESTION_REVIEW_20260723.md b/prod/docs/DUMB_QUESTION_REVIEW_20260723.md new file mode 100644 index 0000000..ee6342e --- /dev/null +++ b/prod/docs/DUMB_QUESTION_REVIEW_20260723.md @@ -0,0 +1,439 @@ +# 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 + `_feed`s 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.*