diff --git a/prod/docs/uv_cert/UV_TESTNET_TRADE_AUDIT_20260710.md b/prod/docs/uv_cert/UV_TESTNET_TRADE_AUDIT_20260710.md index 91a6586..3907cc5 100644 --- a/prod/docs/uv_cert/UV_TESTNET_TRADE_AUDIT_20260710.md +++ b/prod/docs/uv_cert/UV_TESTNET_TRADE_AUDIT_20260710.md @@ -311,3 +311,54 @@ scope for this read-only audit. essentially absent from the bridged trade set. - Fees dominate drag in both eras (~10 bps, matches BingX taker). Enter slippage is systematically adverse (mean ≈ -24 bps) in era B. + +--- + +## 8. 25-row root cause (2026-07-11, Fable — flight-recorder maiden run) + +Instrument: `prod/clean_arch/violet/uv/uv_flight_recorder.py` (read-only +stage tracer: journal → runner log → VST venue; 20 tests, mutation-RED). +Every one of the 25 unmatched rows is now classified from primary evidence: + +| Class | Rows | Evidence | +|---|---|---| +| PHANTOM_KERNEL_REFUSED — SLOT_BUSY (ENTER) | 6 | `KERNEL SUBMIT -> SLOT_BUSY` in runner log (XLM, DASH, ADA, LTC, TRX, THETA enters) | +| PHANTOM_KERNEL_REFUSED — NO_OPEN_POSITION (EXIT) | 9 | `KERNEL SUBMIT -> NO_OPEN_POSITION` (XLM, DASH, ADA, CELR, LTC×2, TRX, THETA, BNB exits) | +| SUBMITTED_VENUE_DEAD — symbol offline on VST | 5 | `bingx_direct … "X is offline currently"` warnings: BAND-USDT ×4 legs, CELR-USDT ENTER. HTTP 200 with error body; kernel still reported OK | +| LOG_GAP — pre-restart window | 4 | ETH pair (07:44/08:05 UTC) + DASH pair (11:04/11:26 UTC) predate the 11:32 UTC log restart; shape matches phantom pairs | +| SUBMITTED_NO_VENUE_EVIDENCE | 1 | THETA EXIT f02ff2f: kernel OK 33 s after its own ENTER was SLOT_BUSY-refused; venue shows **no** order | + +**None of the 25 was submitted-and-venue-rejected at order level; none is a +missing venue record.** They are kernel-doorman refusals, dead-symbol +submits, and a log gap. + +### Mechanism (the real defect, 4 parts) + +1. **Kernel slot state advances on submit-accept, not venue-fill-confirm.** + Venue business-level rejects (HTTP 200 + error body, e.g. offline symbol) + leave the slot occupied by a position that never existed → later + legitimate ENTERs get SLOT_BUSY; later phantom EXITs get kernel OK against + nothing. Desync also runs the other way: THETA 76bbf8ee filled at venue + 13:55:09 UTC yet its SL exit at 14:05 got NO_OPEN_POSITION. +2. **Bridge journals + TPSL watches register regardless of kernel verdict** + (`TPSL WATCH` logs *before* `KERNEL SUBMIT`) → refused intents become + phantom positions that later emit phantom EXITs — the 10 full + identical-qty pairs in §5. +3. **Venue-offline symbols pass asset pick** (BAND, CELR delisted on VST) → + guaranteed-dead submits. DIV-014-adjacent; pick layer needs the tradable + set from `/openApi/swap/v2/quote/contracts`. +4. **`u-` clientOrderId is not propagated to the venue** — venue orders carry + kernel-minted `p-e-…`/`p-x-…` ids. Sentinel check (c) will false-FAIL on + every legitimate order; venue joins must use time+qty until fixed. + +### Live risk found (needs operator action) + +Three refused-EXIT orphans are **open on VST right now**, unprotected, past +their SL doctrine: THETA-USDT SHORT 3298.1 (76bbf8ee, upnl −14.3), +LTC-USDT 11.09 of the 22.2 (41a7d90c), BNB-USDT 0.861 of the 14.73 +(b9c92879) — merged with the 9 known pre-06:00 day-1 orphans (8 venue +positions total, ~−256 USDT unrealized). Manual UI close remains the +protocol. + +Side finding: VST `allOrders` ignores/empties on `startTime`/`endTime` +params — the recorder filters client-side.