PINK: async submit + process_intent hot path; async/race flaw audit (pass 5)

N2/N3/N4 (3x Critical async bugs):
- BingxVenueAdapter.submit_async(): awaits backend.submit_intent() directly
  in caller's event loop — no thread-pool, no asyncio.run(), no _backend_snapshot()
- ExecutionKernel.process_intent_async(): same FSM guard logic as sync version;
  replaces venue.submit() with await venue.submit_async(); sync process_intent()
  untouched so all 122 tests stay green
- pink_direct.step() line 952: process_intent() -> await process_intent_async()

restore_state JSON parse (test fix):
- ExecutionKernel.restore_state() wraps Rust FFI in try/except JSONDecodeError
  returns False; matches documented contract; test_restore_corrupt_json_rejected passes

FLAWS doc: pass 5 table added; 21 total fixed; Z6/N5 marked resolved

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Codex
2026-06-04 21:02:26 +02:00
parent a9ba407ae2
commit f3a5f21460
4 changed files with 165 additions and 9 deletions

View File

@@ -425,6 +425,22 @@ class BingxVenueAdapter(VenueAdapter):
snapshot_after = self._backend_snapshot(include_history=True)
return self._events_from_submit(intent, receipt, snapshot_before, snapshot_after)
async def submit_async(self, intent: KernelIntent) -> List[VenueEvent]:
"""Async submit — runs in the caller's event loop, no thread-pool deadlock.
The sync submit() calls _backend_snapshot() × 2 + submit_intent via
_run() → asyncio.run() in a thread-pool → new event loop → aiohttp
session (main-loop-bound) deadlocks → 30s timeout on every ENTER/EXIT.
This version awaits the backend directly. The before/after snapshots
are omitted: fill size comes from the receipt's executedQty field, and
the WS account stream delivers FULL_FILL events independently.
Passing None for snapshots makes _filled_size_from_snapshots return 0.0
(a safe fallback; the receipt fields take precedence).
"""
receipt = await self.backend.submit_intent(self._legacy_intent(intent))
return self._events_from_submit(intent, receipt, None, None)
def _events_from_submit(self, intent: KernelIntent, receipt: Any, before, after) -> List[VenueEvent]: # noqa: ANN001
ack_row = dict(getattr(receipt, "raw_ack", {}) or {})
status = _normalize_status(getattr(receipt, "status", "") or _row_text(ack_row, "status", default="NEW"))