Files
siloqy/prod/docs/VIOLET_PART_SPEC_OA_TODO_PASS6.md
Codex 5f636ef723 VIOLET OA: partial spec PASS 6 — execution internals + quirk-injection gate
VIOLET_PART_SPEC_OA_TODO_PASS6.md — the execution internals on top of PASS-5's mock, plus the
quirk-injection layer that flips PASS-5's QuirkProfile seams ON and proves each handler survives.
Six independent units extending contracts_v3 (AccountFrame/PositionFrame/ReconcileCorrection/
OwnershipPolicy):
  25. Account/position frame contracts + ownership predicate (foreign-fill filter basis).
  26. Fill-pump (ownership filter [quirk #2], dedup, idempotent) → ledger deltas.
  27. Reconcile loop (zero-wb guard [#1], reseed-on-update, settle/funding event_seq order [#5]).
  28. TTL requote / cancel-replace (+ setLeverage ordering [#7]) over PASS-5 venue + V2 driver.
  29. Orphan detection (ORPHAN_VENUE adopt, ORPHAN_LOCAL quarantine — never silent flatten/resurrect).
  30. Quirk-injection suite + @gate: flip every QuirkProfile flag ON, assert each is neutralized.

VIOLET's exec reconcile/quirk-handling mirrors PINK's production-tested logic (pink_direct.py is
the authoritative reference — transcribe + cite). New-file-only: quirk injection plugs into PASS-5's
existing seams via NEW fill/frame models, never editing the PASS-5 mock. Real-key boundary smoke
remains MANDATORY before V4-live (injection proves logic, not undocumented venue reality).

Standing ready for PASS 7–9. Added PASS 6 to the review queue in VIOLET_TODO_CRITICAL.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 10:37:32 +02:00

13 KiB
Raw Blame History

VIOLET — partial spec for another agent, PASS 6 (OA TODO): EXECUTION INTERNALS + QUIRK INJECTION

Date: 2026-06-17. Continues PASS 5 (the mock-BingX execution stack). PASS 5 built the NORMATIVE mock (order FSM, venue, fill reducer) with quirk SEAMS left OFF. PASS 6 builds the execution INTERNALS — reconcile, fill-pump, TTL-requote, orphan handling — and IMPLEMENTS the quirk INJECTION + handling logic, flipping each QuirkProfile flag ON and proving the reconcile/filter logic survives it. This reproduces, for VIOLET, the hard-won PINK production fixes.

Read first (authoritative references — READ ONLY, never edit):

  • prod/clean_arch/runtime/pink_direct.py — PINK's live execution runtime: the EXACT reconcile, fill-pump, ownership-filter, zero-wb-guard, orphan, and requote logic this pass mirrors.
  • prod/clean_arch/exec/** — the ExecutionRouter (maker/taker policy, hooks).
  • PASS 5 files: prod/clean_arch/violet/exec/{order_fsm,mock_bingx_venue,fill_reducer,bingx_quirks}.py
    • contracts_v3.py — the substrate PASS 6 composes (READ + IMPORT; do NOT edit).
  • Memory/incident lineage of each quirk: project_pink_orphan_fixes, ditav2_kernel_audit_20260611, incident_pink_spool_diskfill_20260611.

VIOLET's ALPHA models BLUE; VIOLET's EXECUTION reconcile/quirk-handling models PINK's production-tested logic (PINK is the exec-active fork). Transcribe PINK faithfully — cite pink_direct.py:line in each handler's docstring.


0. HARD RULES (identical to PASS 15 — summarized)

  • Never edit shared files (prod/nautilus_event_trader.py, clean_arch/dita_v2/**, dita/decision.py, nautilus_dolphin/**, blue_parity.py, prod/bingx/leverage.py, prod/clean_arch/runtime/pink_direct.py, prod/clean_arch/exec/**). READ only.
  • VIOLET DARK — everything runs against the PASS-5 mock; NO real venue/network/key, no service/HZ control.
  • V-TYPES on all new code; faithful poison-guards only.
  • NEW-FILE-ONLY under prod/clean_arch/violet/exec/ (+ extend contracts_v3.py). Do NOT modify the PASS-5 files (order_fsm.py, mock_bingx_venue.py, fill_reducer.py, bingx_quirks.py) or any in-flight V3.4 file. Quirk INJECTION must be done by NEW fill/frame models + wrappers that plug into PASS-5's existing seams (MockBingxVenue takes an injectable fill_model + a QuirkProfile), NOT by editing the mock.

0a. COMMIT / BRANCH POLICY (3 shared-index collisions on 2026-06-16 — non-negotiable)

Own git worktree (git worktree add ../vp-oa6 -b agent/oa-violet6) strongly preferred. Else never git add -A; git commit -F msg -- <files> with explicit pathspec; verify git show --stat --format="" HEAD lists ONLY your files. One commit/task, prefix VIOLET OA:, Co-Authored-By trailer. Tests on /home/dolphin/siloqy_env/bin/python3. git grep only.


I. SHARED INTERFACE EXTENSIONS (add to contracts_v3.py; never fork a parallel type)

Reuse PASS-3/4/5 types (VenueTick, OpenPositionView, ExecIntent, CapitalState, Order, OrderAck, Fill, OrderStatus, PositionDelta). ADD the account/reconcile vocabulary (all StrictModel / Annotated):

  1. AccountFrame — venue-reported account snapshot: wallet_balance: float (finite, ge=0), available: float (ge=0), ownership_id: str, event_seq: Seq, ts_ns: MonoNs. (Carries the zero-wb seam: wallet_balance may legitimately arrive as 0.0 in a poison frame — quirk #1.)
  2. PositionFrame — venue-reported position: asset: Symbol, qty: float, entry_price: Px, leverage: Annotated[int, Field(ge=1)], ownership_id: str, event_seq: Seq, ts_ns: MonoNs.
  3. ReconcileCorrectionasset: Symbol, kind: str ("DRIFT"/"ORPHAN_LOCAL"/"ORPHAN_VENUE"/"NONE"), local_qty: float, venue_qty: float, action: str ("ADOPT_VENUE"/"FLATTEN"/"QUARANTINE"/"NOOP"), event_seq: Seq.
  4. OwnershipPolicyaccount_id: str, client_order_id_prefix: str — the predicate used to decide a fill/frame is OURS vs foreign (quirk #2). Provide owns(ownership_id) -> bool.

If a task needs another field, ADD it here and note it.


TASK 25 — Account/position frame contracts + ownership policy

Why. The reconcile/filter vocabulary + the ownership predicate every handler needs. Affected files (NEW): extend contracts_v3.py (the §I types); prod/clean_arch/violet/exec/ownership.py (the OwnershipPolicy predicate impl); prod/clean_arch/violet/exec/test_violet_ownership.py. Interface/approach. OwnershipPolicy.owns(ownership_id) returns True iff the id matches our account / client_order_id prefix (transcribe PINK's ownership check — cite pink_direct.py:line). Frames/fills with a non-owned ownership_id are FOREIGN (must be filtered downstream). Pass criteria. owns() correct for own vs foreign ids; frames poison-reject (non-finite wallet_balance rejected, but 0.0 wallet_balance is ACCEPTED into the type — it is a legitimate poison-frame value the reconcile layer must SEE and then ignore, not a type error). No edits outside the new/extended files.

TASK 26 — Fill-pump (ownership filter + dedup → ledger deltas) [quirk #2]

Why. Drain fills from the venue, drop foreign fills, dedupe, convert to PositionDelta via the PASS-5 fill reducer, feed the PASS-4 economics ledger. PINK's fill-pump is the reference. Affected files (NEW): prod/clean_arch/violet/exec/fill_pump.py, prod/clean_arch/violet/exec/test_violet_fill_pump.py. Interface/approach. FillPump(reducer, ledger, ownership: OwnershipPolicy) with pump(fills: list[Fill]) -> list[PositionDelta]: filter ownership.owns(f.ownership_id) (FOREIGN fills dropped — ownership fill filter, quirk #2), dedupe by (venue_order_id, event_seq), apply each via PASS-5 fill_reducer.apply_fill, push the PositionDelta into the ledger (event_seq-ordered). Idempotent: re-pumping the same fills produces no double-count. Pass criteria. Own fills applied; foreign fills dropped (ledger unchanged); duplicate fills deduped; out-of-order event_seq handled per ledger rules; idempotency proven. Cite the PINK ownership filter. No edits outside the 2 files.

TASK 27 — Reconcile loop (zero-wb guard + reseed-on-update + settle ordering) [quirks #1, #5]

Why. Periodically reconcile local position/capital vs venue frames; the zero-wb guard and reseed-on-update are the documented PINK fixes that stop a transient walletBalance=0 frame from zeroing capital. Affected files (NEW): prod/clean_arch/violet/exec/reconcile.py, prod/clean_arch/violet/exec/test_violet_reconcile.py. Interface/approach. Reconciler(ownership) with reconcile(local: dict[Symbol, OpenPositionView], account: AccountFrame, positions: list[PositionFrame]) -> list[ReconcileCorrection]:

  • Ignore foreign frames (ownership).
  • Zero-wb guard (quirk #1): if account.wallet_balance == 0.0 (poison frame), DO NOT adopt it — skip the capital update, log, keep prior (cite PINK).
  • Reseed-on-update: adopt venue position state only on a genuine update (newer event_seq), not on a stale/duplicate frame.
  • Settle/funding desync (quirk #5): process frames strictly in event_seq order; a settle event arriving out of fill order is reordered, never applied ahead of its fills.
  • Emit ReconcileCorrections for genuine drift (ADOPT_VENUE / FLATTEN), NOOP otherwise. Pass criteria. A zero-wb frame does NOT change capital; a stale frame is ignored; out-of-order settle is reordered; genuine drift yields the right correction. Hypothesis: capital never set to 0 by a zero-wb frame. Cite PINK lines. No edits outside the 2 files.

TASK 28 — TTL requote / cancel-replace (+ setLeverage ordering) [quirk #7]

Why. Maker orders that don't fill within their TTL must be canceled and re-submitted at a new price; setLeverage is a separate ackable op that must be ordered before the order it applies to. Composes PASS-5 MockBingxVenue + V2 ExecDeadlineDriver. Affected files (NEW): prod/clean_arch/violet/exec/requote.py, prod/clean_arch/violet/exec/test_violet_requote.py. Interface/approach. RequoteController(venue, driver) with on_deadline(order, new_price) -> OrderAck: cancel the stale maker order (assert it reaches CANCELED in the FSM), submit a fresh one at new_price. setLeverage ordering (quirk #7): if leverage must change, issue set_leverage and await its ack BEFORE submitting the order (no race). Reuse the V2 driver's TTL/deadline logic (READ exec_driver.py); do not fork it. Pass criteria. TTL expiry → cancel + requote at new price; the old order is CANCELED, the new one ACKed; setLeverage ack precedes the dependent order; requote count tracked. Determinism. No edits outside the 2 files.

TASK 29 — Orphan detection + handling [the PINK orphan fixes]

Why. Orders/positions can exist on the venue but not locally (or vice versa) — orphans. PINK's orphan-reconcile is the reference; misget handling caused real incidents. Affected files (NEW): prod/clean_arch/violet/exec/orphans.py, prod/clean_arch/violet/exec/test_violet_orphans.py. Interface/approach. detect_orphans(local: dict, venue_positions: list[PositionFrame], ownership) -> list[ReconcileCorrection]:

  • ORPHAN_VENUE: venue has an owned position we have no local record of → action ADOPT_VENUE (or FLATTEN per policy; default ADOPT_VENUE with a quarantine flag).
  • ORPHAN_LOCAL: we have a local position the venue doesn't report → action QUARANTINE (do not silently delete; the zombie-trade lesson — never resurrect/erase by guesswork).
  • Match by chain token / client_order_id where available; ambiguous → QUARANTINE. Pass criteria. Each orphan class yields the correct ReconcileCorrection; foreign positions ignored; ambiguous → quarantine, never silent flatten/resurrect. Cite the PINK orphan fix. No edits outside the 2 files.

TASK 30 — Quirk-injection suite + gate (flip every QuirkProfile flag ON) [all quirks]

Why. Prove the PASS-6 handlers actually survive each quirk by INJECTING it through PASS-5's seams and asserting the outcome. This is the payoff of the seam discipline. Affected files (NEW): prod/clean_arch/violet/exec/quirk_injection.py (the injecting fill/frame models that plug into PASS-5's MockBingxVenue(fill_model=…, quirks=…) and a QuirkAccountStream emitting injected AccountFrame/PositionFrames); prod/clean_arch/violet/exec/test_violet_quirk_injection_gate.py; gate report → prod/VIOLET_dev/reports/violet_quirk_injection_<UTC>.json. Interface/approach. For each QuirkProfile flag, build the injecting model and assert the matching handler neutralizes it:

  • zero_wb=True → Reconciler keeps capital (Task 27).
  • foreign_fill=True → FillPump drops the foreign fill (Task 26).
  • bound_price_poison=True → fill_reducer PnL uses fill_price not bound_price (PASS-5 Task 23, re-asserted).
  • settle_desync=True → Reconciler reorders by event_seq (Task 27).
  • setlev_race=True → RequoteController orders setLeverage first (Task 28).
  • reduce_only_increase=True → OrderFSM rejects (PASS-5 Task 21, re-asserted).
  • (×leverage notional, dead .pro TLS) → documented; ×leverage asserted in fill_reducer, .pro TLS noted as connection-layer/real-client-only. Pass criteria (@pytest.mark.gate). Every quirk flag flipped ON in at least one scenario, each neutralized by its handler; a combined "all quirks on" storm still reaches consistent ledger/position state; report archived. No edits outside the new files.

Composition map

PASS-5 mock (order_fsm, mock_bingx_venue[seams], fill_reducer, QuirkProfile)
   25 contracts(AccountFrame/PositionFrame/ReconcileCorrection) + ownership
   26 fill_pump(ownership filter, dedup) ───► PositionDelta ──► PASS-4 EconomicsLedger
   27 reconcile(zero-wb guard, reseed, settle-order) ─► ReconcileCorrection
   28 requote(TTL cancel/replace, setLeverage order) ─► uses PASS-5 venue + V2 driver
   29 orphans(detect) ─► ReconcileCorrection (quarantine, never silent flatten)
   30 quirk_injection + GATE: flip every QuirkProfile flag ON, assert each handler neutralizes it

Integration (running reconcile/pump/requote on the live reactor against the REAL BingX client) is the OWNER's job later. The real-key boundary smoke remains MANDATORY before V4-live — quirk injection proves the LOGIC, never the undocumented venue reality.

25 (contracts+ownership) → 26 (fill-pump) → 27 (reconcile) → 29 (orphans) → 28 (requote) → 30 (quirk-injection gate).

Still NOT in scope (operator/owner only, or a later pass)

  • Real BingX client + real-key smoke — mandatory before V4-live; operator-gated.
  • Live reactor wiring of reconcile/pump/requote — owner's integration job.
  • DARK soak start; HZ-bridge refactor; VIBRISS; the V3.4c parity root-cause (CRITICAL #1).
  • Any edit to PASS-5 files, in-flight V3.4 files, or shared files in §0.