Audit 2026-07-06 found the live paper book stuck: 4 of 5 open positions were
50/50 refunds the bot could never settle, and the 5th was a position the
signal had exited a day earlier.
- resolution_price tier 2: CTF payoutNumerators/Denominator via RPC
(ALCHEMY_RPC_URL env or config alchemy_key). CLOB winner flags never
populate for operator-resolved in-play markets and never express 50/50
refunds; the chain records both. Token-id match now precedes outcome-label
match in tier 1. New bet status: 'refund' (settles at $0.50/share,
redeems in live mode).
- reconcile_exits(): at boot + every backstop poll, verify the signal still
holds each copied token (market-filtered data-api queries — no pagination
cap). Exit requires three AFFIRMATIVE facts (open-positions fetched+empty,
closed-positions fetched+contains token, market fetched+still trading) —
an API failure must never read as an exit.
- ledger_drift(): cash-vs-ledger invariant checked every heartbeat, warned
in the log, published in the feed (ledger_drift / adjustments fields).
Accumulators no longer round per fill (that drifted cents per trim);
the feed rounds at render. SELL fills now logged to copybot_fills.jsonl.
- their_positions seed cap 500 -> 2000 (whale books exceed 500).
Dry-run vs a copy of the real stuck state: 4 refunds settled at 0.50,
McCormick mirror-exited at 0.69, drift stable at the known +15.45 residue.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Polymarket geoblocks order placement by IP and Railway ran the worker in a
US region — fine for the paper bot (reads only) but a dead end for going
live. Fly arn (Sweden, unrestricted, ~25ms from the CLOB's eu-west-2
primaries) passes: geocheck VERDICT TRADABLE, pm geoblock blocked:false.
- host/geocheck.py: 3-probe geo-gate verdict (ipinfo, pm /api/geoblock,
unauth CLOB order POST); --idle keeps a test machine up
- host/start.sh: geocheck at boot (GEOCHECK_ONLY=1 = probe-and-idle test
mode, no second book-writer); warns loudly if a region is blocked
- fly.toml/fly.Dockerfile: wwf-copybot, single machine, http_service :8080
for the Alchemy push webhook (now wh_blf4qjjvfdbqs9mc -> fly.dev domain)
- deploy_bot.sh: railway redeploy -> flyctl apps restart + banner wait
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Run from a mktemp snapshot so committing daily.sh while a run is in flight
can't shift bash's read offset (the 2026-07-06 phantom-syntax-error crash),
and let the publish-step rebase autostash so a dirty tree can't wedge the
auto-push behind the copybot's feed commits.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One plain line when the cache refresh starts, so the end-of-run digest
isn't the only sign of life. Non-fatal if the webhook is missing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>