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 3907cc5..c7f8f98 100644 --- a/prod/docs/uv_cert/UV_TESTNET_TRADE_AUDIT_20260710.md +++ b/prod/docs/uv_cert/UV_TESTNET_TRADE_AUDIT_20260710.md @@ -362,3 +362,29 @@ protocol. Side finding: VST `allOrders` ignores/empties on `startTime`/`endTime` params — the recorder filters client-side. + +### §8 addendum — mechanism refinement + fixes landed (2026-07-11 evening) + +Refinement: the offline-symbol hole is SUBTLER than a code!=0 body. The BingX +HTTP client already raises on code!=0 (`prod/bingx/http.py _unwrap_response`). +VST answers an order on a delisted symbol with **code==0 + EMPTY data** — the +unwrapped ack is `{}`, which fell through `submit_intent` as status "ACKED", +and `fill_qty` fabricated a full fill from `intent.target_size`. The kernel +FSM then opened a phantom position on a synthetic fill. + +Fixes landed (upstream commit 7bc13a6e + violet-side): +1. `adapters/bingx_direct.py`: ack without venue `orderId` (or any code!=0 + leak) → REJECTED; fill fabrication gated off REJECTED/RATE_LIMITED; + u- client id (`intent.metadata promo_client_id`) propagated to the venue + order (p- mint is now the fallback only). +2. `dita_v2/rust_backend.py`: EXIT asset-mismatch guard (sync+async) — EXIT + for asset X while slot holds Y → NO_OPEN_POSITION/EXIT_ASSET_MISMATCH. +3. `violet uv promotion.py`: execution-universe gate (MALKHUT asset + directory; ENTERs for venue-dead symbols suppressed `asset_not_on_venue`, + EXITs always pass) + kernel/venue-rejected submits journal SUPPRESSED + (`receipt:`), closing the §5 unsuppressed-phantom-row class. +4. `violet uv runner.py`: loads `init_asset_universe(UV_EXEC_EXCHANGE)` at + startup — 35 tradable / 15 offline of the 50-symbol feed on VST. +Tests: 9 (dita_v2 test_uv_seam_fixes.py) + 6 (blue_prime +test_promotion_universe_gate.py), mutation-RED verified; blue_prime suite +553 passed / 2 skipped post-change.