For Agents
Reverse-chronological session log. Newest entries at top, grouped by date (
## YYYY-MM-DD). Each bullet: one piece of work, short summary, wikilinks to docs touched. Updated byobsidian-documenteron every project doc write. Read byhistorianat bootstrap.
2026-07-09
- Live caps are advisory, not blocking — and the regression test couldn’t compile. On the LIVE entry path,
fast_reserve(execute.rs:125) does a bare INSERT and evaluates NO caps;evaluate_caps_post(execute.rs:133) runs AFTER the reserve inside atokio::spawn, concurrent withplace_buy, and on breach only callsagg.record(Category::CapSkip, ...)— a Telegram digest counter. Nothing cancels/unwinds/blocks; signing + submission proceed. Sototal_exposure_cap_usd/daily_spend_cap_usd/max_open_positions/max_copies_per_eventare ALARMS not BRAKES on live. They ARE enforced synchronously — but only inreserve(), the DRY path where no money moves (exactly backwards). Root cause:fast_reservereplacedreserve()on live for latency; caps went with it into a spawned post-check. Why it hid:tests/mode_control_ledger.rs::live_cap_event_breach(assertsSkipped("cap:event")) is precisely the regression test, but that file hadn’t compiled sincePositionManager::newgained anaggparam (consumer_ack.rs/ops_commands.rsdead same way — latter refs removedCommand::Enable/Disable);cargo test --features db-tests= 7 compile errors on main. Durable lesson: a test that cannot compile is not protecting you, and nothing tells you — 213 unit tests green the whole time, only the money-path db-tests were dead, and CI doesn’t run them (need Docker/testcontainers) → green CI meant nothing. Current exposure LOW (20k caps vs $100–235 wallets = missing guardrail, not active loss). Fix (proposed, babylon #1172): fold cap conditions into the guarded reserve-INSERT CTE (already gates on DBmode/breakerat zero extra round-trips); explicitly do NOT revert toreserve()(pays BEGIN+SELECT FOR UPDATE+serial SUM over ~183ms Zurich→Dublin hop). — pmv2-live-caps-are-advisory-not-blocking-2026-07-09 - Entry-path config cache — design corrections that mattered. PM on pmv2-zurich (GCP europe-west6-a), Supabase mgmt eu-west-1 (Dublin) = ~183ms/round-trip. Measured (N=196, 3h):
gates_msmedian 733/p90 1179/p99 1914/max 2750ms;transit_ms220;book_ms212 — config reads the LARGEST controllable stage (bigger than transit+book combined). Path did 7 config SELECTs not 4 (place_entryexecute.rs:99re-resolves mode AFTER book fetch, outside gates_ms) → true ~1.28s/entry. levi/gyula (12k behind) loggedgates_ms==0on 28% (stale-gate early returns); backlog is self-reinforcing. Eight pre-build adversarial-review corrections recorded: (1) v1’s “300s blind live trading on DB outage” accepted-risk was UNREACHABLE (INSERT+mark_submittedneed DB → dead DB fails closed); real risk = healthy DB + stale cache. (2) v1 ArcSwap racy (3 writers → in-flight load restoreslivepost-/halt); fix = ONE writer owns solewatch::Sender(replaced generation-counter+CAS). (3) boot fail-open: empty first snapshot reads FRESH → allOff→SkipAck→Ack(consumer.rs:64) → live entries dropped, never redelivered on every restart; fix = hard-fail if firstload()fails. (4)decide3(None,..)returnsbreaker_tripped:true→ stale guard must returneff:Off+breaker_tripped:false(else breaker alert spam). (5)decide3hardcodessize_scale:ONE/priority:1→ resolver must copy realWalletCfgvalues (else silent 1× while tests pass). (6)clamp_size_scalewas read-time → must clamp at load (else rawsize_scale=100→ 100× order). (7) torn 4-queryload()errs towardlive→ one REPEATABLE READ txn. (8) alerts poller-driven not read-driven (read-driven ladder silent during lulls). Key reusable idea: when you cache authority, move the authority check to a round-trip you already pay —fast_reserveINSERT became a CTE conditional on DBmode/breaker_tripped, distinguishing guard-reject from ON CONFLICT dedup via returned gate columns; zero extra round-trips, stale cache can never place an unauthorized live order → collapsed design (no generation counter, no breaker snapshot-patch, noRETURNING breaker_tripped). Constraints:arc_swap/mokaabsent from Cargo.lock +Dockerfile:24cargo build --locked→ usetokio::sync::watch(borrow()cheap sync read); sqlxPgPoolOptionsdefaultstest_before_acquire=true(ping/acquire) +min_connections=0(~150ms/query cross-region, taxes INSERT/mark_submitted/ledger — cache doesn’t remove). Branchfeat/entry-config-cache@ec0b666(10 commits), 226 lib + 295 db-tests green. — pmv2-entry-config-cache-design-2026-07-09 - Polymarket funding gotcha: deposit to the EOA, not the proxy. Each
pmv2_walletsrow hasmaker_proxy(holds pUSD collateral, bot trades from here) +signer_eoa(always 10 test withdraw to the PROXY: landed as native USDC, proxy showed 31.34 pUSD + 10.00 native USDC; only depositing via the EOA wrapped it (proxy → 41.34 pUSD, native USDC → 0). Rules: collateral = pUSD0xC011a7E1…6dp (only token CLOB//walletscount); verify by watching pUSD rise at the proxy, not “USDC left my wallet”; always small-test first. Public RPC:polygon-rpc.com=403,polygon.llamarpc.com=empty → usehttps://polygon-bor-rpc.publicnode.com; balance =eth_call balanceOf0x70a08231on pUSD. Addresses recorded (public, no keys). — pmv2-polymarket-funding-deposit-address-gotcha-2026-07-09 - Measure before mitigating — the priority-tiers miss (retro). Shipped “priority tiers” (staggered fire, tier-2 sleeps
(priority-1)*400msbefore fetching book) to fix a 4-wallet slowdown I attributed to depth cannibalization (fan-out → race → FAK exhaustion). Wrong hypothesis. Thecryptoagent MEASURED: 90.5% of failures areprice-moved-past-limit(best_ask > limit at fire), NOT no-liquidity; 88 four-of-four all-fill instances disprove a depth cap; real cost is PRE-BUILD latency (emit→signal p50 2.31s); ~50% fail rate predates wallets 3+4. The 400ms stagger measured actively harmful (levi/gyula ROI −7.3%/−7.6%, below breakeven) → neutralized toTIER_DELAY_MS=0; tier machinery inert (tier_delay(1)==Duration::ZERO, gatedif !delay.is_zero()→ tier-1 byte-identical). Real fix R2/R3 left UNSTARTED pending 24h of R1 latency-decomposition telemetry. Lessons: measure before mitigating; instrument first (R0-R6 harness exists to answer “where does the time go”); watch for a mitigation that makes things worse. Deploy hazard: R0-R6 branchfeat/fanout-latency-r-itemswas cut beforeTIER_DELAY_MS=0landed on main → still carried 400, would silently regress the fix; check whether main moved under you on a const you don’t touch, and say “don’t ship this” in the SAME message as a handoff. — pmv2-measure-before-mitigating-priority-tiers-2026-07-09 - OPEN brainstorm: multi-wallet liquidity ceiling for copy-trading. Captured a strategic/design ceiling as a future brainstorm topic. PM fan-out (one durable NATS consumer per wallet, pmv2-multi-signer-wallet-2026-07-01) has every wallet fire a FAK into the SAME thin CLOB book simultaneously → they cannibalize each other: first wallet Placed, later 2-3 no-match, grind 2 re-priced retries, fail
RecordedFailure("other")(FAK no-match string falls throughclassify_error→“other”). Deploy CONFIRMED (2026-07-08): 4.5× fan-out, 0×429s (NOT rate-limit/latency), “first Placed later fail” adverse-selection FAK. Shipped v1 mitigation on branchfeat/wallet-priority-tiers(spec/plan2026-07-09-multi-wallet-priority-tiers): per-walletpriorityhot-read column, fire delayed(priority-1)*~400ms; Tier1=bandi/balu fire immediately (zero-delay short-circuit, latency unchanged), Tier2=levi/gyula ~400ms later to take leftover book. This ORDERS contention but does NOT beat the ceiling — book depth caps profitable fills at ~1-2 wallets/signal regardless of wallet count, so onboarding more client wallets ≠ proportional fills. Directly constrains the fee/MLM plan (2026-07-08-pmv2-fee-accounting-engine+ per-walletfee_rate): later/lower-priority wallets under-fill → different PnL → different 20% fee. Open agenda left for fresh brainstorm (coordinate fills / partition sources / diversify markets / fairness / fee capacity). Known v1 gaps: tier-2 still no-match if tier-1 empties book (pre-fire depth-skip TODO); tier delay is blocking sleep → serializes under 5m-boundary bursts (non-blocking spawn TODO). — pmv2-open-multi-wallet-liquidity-ceiling-brainstorm
2026-07-03
- resolution-PnL booking false alarm (#1031) — PM cleared. Crypto reported 7/246 settled rows in
resolution_sweep.rsbooked as losses (-cost_basis) that “should have won” (order ids 3721/3814/3872/3875/3876/3958/4043), alleging PM premature-reads CLOBtokens[].winnerbefore UMA finalization. Investigation: CLOBMarketResponsehas NO dedicated resolved/UMA field (MarketStatus.resolvedis a fake copy ofclosed—status_fromsetsresolved: m.closed); only signal is per-tokenwinner: bool. The live path (c444c86) ALREADY skips on empty winner set (“resolved but no winner token; skipping this pass”) → no-flag markets are deferred, not booked. Deploy re-checked all 7 against CLOB authoritatively → we GENUINELY LOST all 7; PM booking was CORRECT. Root cause = crypto’s OWN Data Streams near-tie cross-ref artifact, not a PM read error / token flip. #1031 closed zero-delta. A “settlement grace period” gate was designed+approved-then-withdrawn, rejected YAGNI once premise collapsed; empty-winners skip guard remains the minimal defense, no grace constant added. — pmv2-resolution-pnl-booking-verified-false-alarm-2026-07-03 - dry_run verification signals gotcha. Documented what
Mode::Dryactually exercises when verifying a new wallet (e.g. wallet2/Balu).dry_run→place_entry→reserve()→dry_rehearse()(execute.rs:145-205) BUILDS AND SIGNS the order (signer.build_order), runsassert_amounts, logsautotradeDRY-SIGNED maker/taker/hash/contract— proving arm+signer+routing end-to-end. It also recordsCategory::DryRehearse→ digest rendersdry Nper wallet (tg_digest.rs:88). It does NOT reachlog_fak_attempt(execute.rs:404-419, LIVEplace_buyonly) sopm_fak_attemptsstays empty and FAK-retry v2 is NOT exercised untilmode=live. Valid dry verification signals = DRY-SIGNED logs +dry Ndigest count, NOT pm_fak_attempts rows. — pmv2-dry-run-verification-signals-2026-07-03
2026-07-02
- PM is LIVE real money on multi-wallet + FAK. Global
pmv2_autotrade_config.mode=live(crypto go-live, babylon 896), backfill migration20260701000200copiedconfig.mode→wallet1.mode=live, all 10 crypto sources live → 3-way clamp effectiveliveend-to-end; real ledger entries3451/3452/3457. Deploy chatter “mode stays OFF/current” meant UNCHANGED=live, NOT off. FAK-retry v2 (ceiling=directive.limit_pricevs hardcoded0.88) backlogged, gated on crypto producerdf6f500+ Andras go. Telegram 30-min aggregate shipped (maina5f3604, spec indocs/superpowers/). — pmv2-pm-live-real-money-status-2026-07-02 - sqlx runtime type-decode bug + db-tests remediation.
wallet_handles_source(consumer.rs) usedquery_scalar::<_, i64>("SELECT 1 …"); PG types1asint4, sqlx fails thei64decode at runtime,.ok().flatten()swallowed it tofalse→ multi-wallet consumer SILENTLY SKIPPED EVERY ENTRY fleet-wide (~40-min real-money routing halt). Compiles fine + invisible to offline tests — a “verified by reading” blind spot. Fix (c9bbffb):query(...).fetch_optional().is_some()+ log DB errors. Remediation:feature=db-teststestcontainers suitetests/wallet_fak_db.rs(69a1cb8), anchorwallet_handles_source_regressionfails on old / passes on fix; recommend wiring into CI (full--features db-testsbuild blocked by pre-existingops_commands.rsEnable/Disabledrift → use--test). — pmv2-sqlx-runtime-type-decode-bug-db-tests-2026-07-02
2026-07-01
- Built multi-signer-wallet (“wallet axis”): ONE PM process trades from N signer wallets concurrently, parallel to the source axis. New tables
pmv2_wallets(registry, AES-256-GCM encrypted keys, per-wallet caps +size_scale) +pmv2_source_wallets(many-to-many routing);pmv2_autotrade_ordersgainedwallet_id(dedup index now(wallet_id, dedup_key)). One durable NATS consumer per wallet; 3-way mode clampmin(global, wallet, source); per-wallet cap breach ADVISORY (breaker only on real faults). Two bugs caught + fixed mid-build: wallet-blind money gateplace_entry(SAFETY GAP,b2faddf) andNonce::from_slicepanic on 1-byte placeholder nonce crashing the whole fleet at boot (c6bd9d8). Branchfeat/multi-signer-walletHEADc6bd9d8, LOCAL/UNPUSHED, mode OFF, 179 tests green, NOT deployed. — pmv2-multi-signer-wallet-2026-07-01
2026-06-30
- FAK (“fill-and-kill”) no-match BUY rejects (transient lateness, e.g. “no orders found to match with FAK order”) now collapse to ONE telegram line instead of REJECTED + latency-breakdown. New pure fns
is_fak_no_match(err)+fak_reject_line(id, req, url)inexecute.rs(commit4cac2cf); FAK branches first in theOk(Err(e))arm.mark_failed/bump_breakerunchanged. The FAKifblock is a deliberate structural SEAM for a future retry path (NOT yet implemented). — pmv2-fak-no-match-reject-single-line-2026-06-30
2026-06-28
- Polymarket data-api
/positionsreports resolved-but-unredeemed losses incashPnlwithrealizedPnl: 0until redeemed, breaking naive PnL split;/walletsshowed uPnL −0. Fixedaggregate()(self_portfolio.rs, commitb26b53b) to classify realized vs unrealized on theredeemableflag → uPnL −130.89. Also confirmed pUSD balance contract0xC011a7…viaeth_call balanceOf. — pmv2-data-api-positions-pnl-classification-2026-06-28
2026-06-26
- Solved Polymarket CLOB live trading from PM: deposit-wallet / Poly1271 flow — authenticate with the deposit wallet’s
owner()EOA, DERIVE (not supply) the L2 key, set maker=signer=funder=deposit wallet. Order accepted on live. Config:maker_proxy=deposit wallet,PM_SIGNATURE_TYPE=poly1271, signer=owner EOA, remove captured L2 creds. — pmv2-polymarket-deposit-wallet-live-trading-solution-2026-06-26
2026-06-25
- Signer pre-arm
proxycheck silently failed for Polymarket Deposit Wallet type (3rd factory, non-standard CREATE2); fixed by verifying on-chainmaker_proxy.owner()instead of deriving. Also swapped flapping DRPC WSS to Alchemy (env-only). — pmv2-signer-prearm-proxy-owner-gotcha-2026-06-25
2026-06-24
- crypto pmv2 v2 migration renamed wire
sourcetocrypto; body-driven source resolution silently fail-closed PM. Fixed with idempotent seed migration (mode='off'). — pmv2-crypto-source-rename-gotcha-2026-06-24 - Initialized position_manager project folder + service overview. — position_manager