docs: HANDOFF rev 6 + gotcha 17 (delayed-fill incident, disarm state)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jaxperro
2026-07-10 11:25:52 -04:00
parent 56fe0dee92
commit a938febdc1
2 changed files with 40 additions and 1 deletions
+29 -1
View File
@@ -1,4 +1,32 @@
# Session handoff — 2026-07-10 (rev 5.1: live app on PUSH mode — awaiting first organic fill)
# Session handoff — 2026-07-10 (rev 6: delayed-fill incident fixed — DISARMED, re-arm is the user's call)
## INCIDENT 2026-07-10 (resolved in code; bot left DISARMED)
Push mode worked (~6s detection) — and exposed an executor bug: **in-play
markets accept orders with status `delayed` and ZERO matched at response
time**. The port read matched==0 as a rejection, logged an honest miss, and
forgot the order — six such orders then **filled at the exchange untracked**
(09:1013:14Z, $30 real spend vs $5 booked; caps stopped binding; exits never
mirrored; CASH≠CHAIN alarmed exactly as designed). Damage net **$2.7**
(G2 g2/g3 and PlayTime g1 lost; Rune Eaters +$7.50 and Aurora g1 +$3.77 won,
platform auto-redeemed; Aurora g2 won, redeem pending). First diagnosis
wrongly blamed a second bot instance — the missed ledger's
"order rejected: {'order_id': …}" rows matching on-chain fills to the second
settled it: it was THIS bot.
**Fix (copybot.py, unit-tested with a stub client, 5 paths):** the executor
invariant is now *no order outlives `_order()` untracked* — zero-matched
acceptances poll `get_order`, cancel the remainder, and measure truth via the
exchange's CONDITIONAL balance diff; exception paths sweep all open orders on
the token the same way. State surgery done disarmed: cash = chain ($10.03),
six fills adopted as bets, spend tracker = the real $35 today (over cap → a
re-arm today places nothing new), false miss rows purged, $0.59 adjustment
documented.
**DISARMED via `flyctl secrets unset LIVE_CONFIRM` (survives restarts —
note `machine stop` does NOT: http_service auto-restarts the machine).
Re-arm = user types `flyctl secrets set LIVE_CONFIRM="TRADE LIVE"` after
reviewing the fix. Recommended: re-run a probe on an IN-PLAY market first.**
Self-contained pickup for a fresh session (human or AI). Read
[README.md](README.md) gotchas 116 (16 is the new-stack one),
+11
View File
@@ -381,6 +381,17 @@ runner is retired (GitHub throttled `*/5` to ~2h in practice — it copied 1 of
— revoke in-process before exit (`atexit` in the probe) or they pile up
unrevocable-but-inert on the account (9 accumulated during bring-up;
the UI at polymarket.com/settings?tab=builder can clean those).
17. **In-play order acceptance reports ZERO matched — it is NOT a rejection.**
In-play (esports/live-game) markets return `AcceptedOrder` with status
`delayed` and `making/taking = 0`; the match happens seconds-to-minutes
later at the exchange. Treating zero-matched as a miss caused the
2026-07-10 incident: six $5 copies were logged as misses and filled
untracked — the book went blind, caps stopped binding, exits were never
mirrored (net $2.7; the CASH≠CHAIN alarm caught it). The executor's
invariant since: **no order outlives the placing call untracked**
poll `get_order`, cancel the remainder, then measure the fill as the
exchange's CONDITIONAL-balance diff (chain truth beats response parsing).
Same sweep runs on exception paths (a timed-out POST may still fill).
---