Gamma's default /markets listing HIDES closed markets — the resolved
Odyssey market returns [] without &closed=true. Closed markets are
exactly the population repair_market_meta exists for (resolved-but-stuck
positions), so the gamma-only lookup left the paper book's own Odyssey
copy (178.57sh, resolved won) and its France-WC position permanently
cond-less; both verified resolving via the closed view. The fetch edge
is factored into _gamma_markets so the variant order, caching, and
error-then-fallback paths are unit-tested.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root cause of the 07-20 CASH≠CHAIN -$1.75: some markets stream RTDS
trades with empty title/outcome/conditionId; the seed stored them
verbatim, on_wallet_activity's 'tok not in self.conds' guard made the
empty value sticky, and settle_resolved's 'if not cond' skip left the
position silently unsettleable until the venue auto-redeemed it.
Three-layer fix:
- RTDS seed: when the payload lacks conditionId/title, enrich from the
token id via _token_market (gamma, cached) before dispatch; the copy
still goes through on a miss.
- conds write: falsy-overwrite instead of key-presence, so a later,
richer source repairs an empty entry.
- repair_market_meta (called at the top of every settle pass, holding
the lock): re-resolves empty conds for my_pos AND open missed entries,
backfills position/bet/missed records, 300s per-token lookup backoff,
and a once-per-token alarm when a position has sat unsettleable >1h.
A repaired token settles in the SAME pass — the Odyssey bet would have
self-healed the moment the market resolved.
The paper book currently carries 4 empty conds and both books have
cond-less open missed entries — the repair pass fixes those on the first
cycle after deploy. tests/test_meta_repair.py covers backfill, backoff,
the single alarm, recovery, and the end-to-end repair->settle un-stick;
full suite 8/8.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>