Files
jaxperro f9a4b51a4d docs: sweep everything stale after the 2026-07-10 go-live day
README system-today gains the REAL-MONEY worker row (armed, push mode,
pending registry, Discord pings); /live marked live; cheat-sheet covers
both apps + disarm/re-arm + the stale-clone verify rule; guard bullet and
safety paragraph reflect the absolute +0.05 guard and retired caps; file
map adds fly.live.toml + the bridge/probe/flatten utilities. copybot/
copytrade module docstrings drop the archived py-clob-client story.
LIVE_ROLLOUT gets a status banner (executed through Phase 4; rule 0.6
superseded; Phase 5 matrix still worth running).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 17:31:51 -04:00

220 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Real-money rollout plan — executable by a fresh agent session
> **STATUS 2026-07-10: executed through Phase 4** (bot ARMED, push mode,
> first fills verified end-to-end — see [HANDOFF.md](HANDOFF.md) for current
> state). **Rule 0.6's hard caps were RETIRED 2026-07-10 by user decision**:
> sizing is paper-parity 4%-of-equity, floored at the venue $1 minimum. The
> new-stack realities discovered during execution live in README gotchas
> 1617 (pUSD collateral / bridge conversion; in-play `delayed` acceptances
> and the pending-order registry). This document stays as the historical
> plan + the Phase 5 edge-case matrix, which is still worth running.
Written 2026-07-07 after the pre-money audit. This document is **self-contained**:
a session with no prior context (Sonnet/Opus) should be able to execute every
phase from here + the repo. Read [README.md](README.md) gotchas 115 and
[LIVE_TEST.md](LIVE_TEST.md) first. Project memory (`polymarket-resolution-truth`,
`polymarket-us-expansion`) has deeper background; the repo is authoritative.
**The goal:** a supervised minimum-size live test — deposit → copy a followed
bet → mirror a sell before resolution → hold to resolution (win, loss, AND
50/50 refund) → withdraw — with worst-case loss bounded by the ~$30 exposure
cap. Only after every edge case in Phase 5 passes does sizing-up get discussed.
---
## Phase 0 — invariants (read, don't skip)
These were each earned the hard way. Violating any of them voids the test.
| # | invariant | why |
|---|-----------|-----|
| 0.1 | **Live orders only from a geo-TRADABLE box.** `python3 host/geocheck.py` must print `VERDICT: TRADABLE`. The Fly worker (`wwf-copybot`, Stockholm) passes; a Mac in the US gets 403-geo-blocked on the order POST. | Polymarket IP-blocks order placement (README gotcha 10). |
| 0.2 | **The user decides WHEN to arm** (jurisdictional posture: they trade live from Colombia months, paper from US months). The agent prepares everything; the arming step and its timing are user-only. | The machine's location doesn't relocate the person. |
| 0.3 | **One writer per book.** The live book uses its own state (`copybot_state.live.json`) and must use its own feed file (Phase 1.1 — today both modes write `live/copybot_live.json`, which would clobber the July paper test's dashboard). The paper worker keeps running untouched throughout. | The paper July test is the control group; don't contaminate it. |
| 0.4 | **Settlement truth is the chain.** CLOB `winner` flags never populate for operator-resolved in-play markets, and 50/50 refunds (28% of follow-set markets!) pay $0.50/share. `resolution_price()` in copybot.py already does CLOB→chain-payout tiering; live mode requires `ALCHEMY_RPC_URL` set or refunded/operator-resolved positions never settle. | 2026-07-06 audit: 4 positions sat locked for hours. |
| 0.5 | **The ledger must reconcile to the cent.** `ledger_drift` in the feed must stay 0.00; in live mode it additionally must match the on-chain USDC balance (Phase 1.4). Any drift >1¢ = stop, diagnose, fix before the next order. | Real money makes bookkeeping bugs unrecoverable. |
| 0.6 | **Hard caps stay at test size until Phase 6.** `config.live.json` risk block: $5/trade, $5/position, $25/day, $30 exposure, 6 open, prices 0.050.95, FAK orders. Never raise mid-test. | Worst case ≈ the $30 exposure cap. |
| 0.7 | **Paper-mode safety interlocks stay intact**: live needs `mode:"live"` + `--live` + typed phrase. Never bypass, never automate the phrase. | The phrase is the human checkpoint. |
---
## Phase 1 — code prep (agent; all verifiable in paper mode, no money)
Each item is a small PR-sized change with its own verification. Do them in order.
### 1.1 Feed separation
`copybot.py` `FEED` is a module constant (`live/copybot_live.json`) — a live run
overwrites the paper feed. Make it config-driven: `cfg.get("feed_path",
"live/copybot_live.json")`, and set `"feed_path": "live/copybot_live_real.json"`
in `config.live.json`. Same for `FILL_LOG``copybot_fills.live.jsonl`.
**Verify:** run paper poll-once with a scratch config setting `feed_path`; confirm
the alternate file is written and `live/copybot_live.json` is untouched.
### 1.2 Secrets via env for the worker path
`config.live.json` is gitignored — the Fly worker clones the repo and won't have
it. Extend `load_cfg`/`normalize_follow_config` so `LIVE_PRIVATE_KEY`,
`LIVE_FUNDER_ADDRESS`, `LIVE_SIGNATURE_TYPE` env vars override `cfg["live"]`.
**Verify:** unit-style: `LIVE_PRIVATE_KEY=0xdead python3 -c "…load_cfg…"` shows
the override; no secret ever printed or committed.
### 1.3 Live deps in the worker image
`fly.Dockerfile` is stdlib-only. Add `pip install py-clob-client web3` (pin
versions) — needed for `LedgerLiveExecutor` + `redeem.py`. Keep the paper path
import-lazy (it already is: LiveExecutor imports inside `__init__`).
**Verify:** `flyctl deploy --remote-only`, then
`flyctl ssh console -C "python3 -c 'import py_clob_client, web3'"` exits 0, and
the paper bot still boots clean (banner + heartbeat, drift 0.00).
### 1.4 Live-mode ledger anchor: on-chain balance check
In live mode, `summary()` should each heartbeat compare `state["cash"]` against
the CLOB collateral balance (`client.get_balance_allowance`, cached ≥60s) and
log `⚠ CASH≠CHAIN` when they diverge by more than pending-order float. This is
the real-money version of `ledger_drift`.
**Verify:** code-review level only until Phase 4 (needs a funded account);
assert the check is skipped cleanly in paper mode.
### 1.5 Geocheck fatal in live mode
`host/start.sh` warns on geo-block but continues (fine for paper). In
`copybot.py` main(): if `want_live` and a fresh `geocheck` probe (import the
3-probe logic or shell out) is not TRADABLE → `sys.exit`. Never place a live
order from a blocked box.
**Verify:** from the (US) Mac, `--live` with a filled dummy key must refuse with
the geo message before the confirmation phrase.
### 1.6 Exit-order resilience
Mirror-sells use marketable FAK. On a thin in-play book a sell may fill 0 and
the engine just logs ORDER FAILED — the position then silently rides to
resolution. Add bounded retry: on a failed/partial SELL, retry next heartbeat
(up to N=10 attempts, logged each time), then alert `⚠ EXIT STUCK` to Discord.
**Verify:** paper-mode unit: monkeypatch executor.sell to fail twice then
succeed; position exits on third heartbeat, sold-leg accounting intact.
### 1.7 Sync `config.live.json` follow set
It still lists `shisan888` (long gone). Copy the current `wallets` list +
pinned floors from `live/copybot.paper.json` (post-demotion: all volume class,
Stavenson/0x4bFb/ArbTrader pinned at $4,760/$1,090/$168), keep the $5 caps and
`bankroll_pct` small (0.02 ≈ $5 stakes on a $250 book — recompute so
`bankroll_pct × bankroll_usd ≈ $5`).
**Verify:** `./live/deploy_bot.sh`-style preview: `python3 -c` validation prints
8 wallets and the caps unchanged.
### 1.8 Dashboard: live-money section
The dashboard reads `live/copybot_live_real.json` (from 1.1) into a fourth
board chip + tables, clearly labeled REAL MONEY, reusing the standardized
columns (Their/Our Entry, Stake, Result W/L/R/S, Placed/Settled). Statuses and
the `ledger_drift`/`adjustments` fields render like the paper section.
**Verify:** point it at a copy of the paper feed with `"mode":"live"`; preview
renders; then empty-state renders when the file 404s (live test not started).
**Phase-1 exit criteria:** all verifications green; paper bot untouched and
heartbeating (`flyctl logs`); `git push` after each item; the July paper feed
never skipped a beat.
---
## Phase 2 — account + funding (USER ONLY — agent stops here until told)
Follow [LIVE_TEST.md](LIVE_TEST.md) §12, with these updates:
1. Dedicated Polymarket account (email login recommended → `signature_type: 1`).
**Deposit through the Polymarket UI** (~$50 USDC) so the collateral token is
whatever the venue expects — do not send tokens directly on-chain (USDC vs
USDC.e confusion is a real loss mode).
2. Export the private key (Settings → Export private key). Fill
`config.live.json` `live.private_key` + `live.funder_address` (profile
deposit address). The file is gitignored; keep it that way.
3. Send ~1 POL to the **signing EOA address** (preflight prints it) for redeem
gas. Skipping this = set `live.auto_redeem: false` and redeem manually.
4. Decide the venue box (0.1/0.2): **recommended = the Fly worker** (geo-clean,
push-speed). Running on the Mac requires being in an unrestricted country.
---
## Phase 3 — preflight + dry runs (agent, with user watching)
1. `python3 preflight_live.py` — all green (auth, USDC balance, book access,
POL gas). Fix reds before proceeding.
2. `python3 copybot.py --config config.live.json --state /tmp/dry.json
--test-wallet <an active follow-set wallet>` — paper-mode routing of a real
trade through filter+engine. Confirms config/filters without orders.
3. If running on Fly: `flyctl secrets set LIVE_PRIVATE_KEY=… LIVE_FUNDER_ADDRESS=…`
(values piped, never echoed), plus a **separate Fly app or process group**
decision: do NOT convert the paper worker — run a second app `wwf-copybot-live`
from the same image with `mode:"live"` env config, so paper and live books
never share a process. Update the Alchemy webhook? No — live app runs
**poll mode (60s)** for the test; push webhooks stay pointed at the paper app.
Poll lag ≈ 39s avg was measured fine.
4. Baseline sanity: live app boots, logs `on-chain settle fallback: ON`,
`geocheck TRADABLE`, prints caps, **waits at the confirmation phrase** —
which only the user types (via `flyctl ssh console` attach or Mac run).
**Abort at any point:** Ctrl-C / `flyctl apps stop wwf-copybot-live`. Nothing
is armed until the phrase is typed; nothing is at risk beyond the deposit.
---
## Phase 4 — first fill (supervised)
Arm during a liquid window (evening EU/US — ITF + esports flow). Then wait for
the first conviction signal. For the FIRST fill, verify every link end to end:
- [ ] console `FOLLOW …` line → `OPEN … [LIVE] buy` with order id
- [ ] fill appears in `copybot_fills.live.jsonl` with lag + slippage
- [ ] position visible in the Polymarket UI (the account actually holds it)
- [ ] feed `live/copybot_live_real.json` row status `open`; dashboard renders it
- [ ] `state.cash` dropped by cost+fee; 1.4's CASH≠CHAIN check silent
- [ ] Polygonscan: the fill's on-chain transfer matches shares/price ±rounding
If any link fails: stop (Ctrl-C), snapshot state + logs, diagnose before re-arming.
---
## Phase 5 — edge-case matrix (the actual test)
Run days-to-weeks until every row has a ✓ with evidence (log line + feed row +
chain tx where applicable). Most trigger naturally from the follow set's flow;
patience beats forcing.
| case | trigger | acceptance evidence |
|------|---------|---------------------|
| BUY copy | any conviction signal | Phase-4 checklist |
| ADD (signal scales in) | whale re-buys same market | position grows ≤ stake rule; feed cost/shares aggregate |
| **SELL before resolution** | signal fully exits (they exit most positions — hours, not days) | `EXIT … [LIVE] sell` + proceeds land in balance + status `closed`/SOLD + 1.6 retry logs if book thin |
| hold → WIN | resolution | `SETTLE … WON` + **auto-redeem tx hash on Polygonscan** + USDC balance up by shares×$1 |
| hold → LOSS | resolution | `SETTLE … LOST`, no redeem attempted, book realizes cost |
| hold → **REFUND 50/50** | ITF walkover/abandonment (≈28% of markets — will happen) | `SETTLE … REFUND ↩` + redeem tx at $0.50/share + cash up by shares×0.5 |
| missed (price guard) | price runs >5% in the copy lag | `skip … price moved` + missed row on dashboard, later settled hypothetically |
| missed (cash gate) | exposure cap hit | `capital fully deployed` skip + missed row |
| downtime reconcile | stop the live app 30+ min during flow, restart | boot logs `reconcile:` lines; downtime entries → missed("bot offline"); exits mirrored or settled |
| FAK no-fill | thin book (observe, don't force) | order killed cleanly, no resting order in the UI, retry per 1.6 |
| neg-risk market | if one gets copied | `⚠ NEG-RISK … redeem manually` warning; manual UI redeem works |
| **WITHDRAW** | user, at the end | USDC withdrawn via UI back to source; final book cash ≈ withdrawal ± dust; screenshot + final feed snapshot committed |
Throughout: `ledger_drift` 0.00 every heartbeat, CASH≠CHAIN silent, and the
paper book running in parallel as control.
---
## Phase 6 — wind-down + verdict (user + agent)
1. Stop the live app; final feed + state snapshot committed (tagged `live-test-1`).
2. Write `FINDINGS.md` addendum: measured live slippage vs paper's 3.6%/65s,
fee accuracy, fill rate (FAK kill rate), any stuck exits, refund handling.
3. **Graduation gate (all required):** every Phase-5 row ✓ · ledger drift 0
throughout · live slippage within 2× of paper model · no manual state
surgery needed. Then — and only then — a sizing discussion (separate
decision, separate doc; the their-bet ceiling and depth-aware fills from
README "Scale carefully" apply).
## Standing notes for the executing agent
- Never print/commit the private key; `git status` before every commit.
- The daily 08:00 Mac pipeline and the bot's self-commits keep pushing to this
repo — always `git pull --rebase --autostash` before pushing.
- The paper worker's webhook/feed/state are load-bearing for the July test:
if a change might touch them, it's a Phase-1-style isolated change with its
own verification first.
- When something looks wrong, the debugging order that worked in the audit:
feed → state → fills ledger → data-api → CLOB → **chain payouts** (the chain
is always right).