dita_v2(exec): INDETERMINATE submit is not REJECTED — unknown is never flat

A BingX read-timeout/reset/5xx after send means the answer was lost, not that
the order failed. Classify every submit failure by what it PROVES:
NOT_ATTEMPTED / REFUSED -> rollback sound; INDETERMINATE -> point-lookup our
own clientOrderId (read-only, bounded, never a reconcile); unresolved stays
UNKNOWN — no synthetic REJECT, no slot rollback, E-feed FILL settles truth.

- prod/bingx/http.py: BingxHttpError.effect + order_may_exist, 9 raise sites tagged
- adapters/bingx_direct.py: _lookup_own_order_by_client_id (never POSTs)
- dita_v2/venue.py: VenueIndeterminateError(VenuePostAckError) — existing fences catch it
- dita_v2/bingx_venue.py: both submit paths escalate INDETERMINATE receipts
- 14 tests incl. kernel no-rollback invariant + genuine-REFUSED contrast

Suite: 3416 passed, 19 skipped, 3 xfailed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-07-13 15:43:55 +02:00
parent d9b7e05531
commit bdc54fbeaa
5 changed files with 529 additions and 19 deletions

View File

@@ -40,6 +40,23 @@ class VenuePostAckError(Exception):
self.events = events or []
class VenueIndeterminateError(VenuePostAckError):
"""The submit outcome is UNKNOWN: the request was sent, the answer was lost.
A read timeout / connection reset / 5xx means BingX may have matched the order.
The adapter has already asked the venue about our clientOrderId and could not
establish the truth, so we are left with genuine uncertainty.
Deliberately a subclass of VenuePostAckError, because it demands the SAME
response: the effect may exist, therefore DO NOT roll the slot back to flat and
DO NOT synthesise a REJECT. Leave the slot working and let the E-feed FILL /
the account stream settle it. Callers that already fence VenuePostAckError get
this behaviour for free.
Unknown is not flat. It is not failed either. It is unknown.
"""
class VenueAdapter(Protocol):
"""Abstract venue adapter used by the kernel."""