← Back to Dashboard

Agent handoff — Polymarket Earnings Trading Bot

Purpose: Single entry document for a new AI or human operator. It maps all project documentation, runtime layout, rules, and where to look first. Update this file when onboarding expectations change.

Repository: https://github.com/eumarius/earnings-trading-bot-.git (default branch: master)

Finding #479 DB activity logging DEPLOYED Sep 26, 2026 20:55 UTC (deploy #8 614a29d; the post-deploy evaluator defect (security:db_client_activity could not evaluate, so the foreign-client page was inactive 20:58–21:33 UTC) is fixed DEPLOYED Sep 26, 2026 21:30 UTC (deploy #10 f28c0da); the rule reads ok): code-derived DB entrypoint identities, a query-free 15-second all-database pg_stat_activity sampler, rotated host evidence, a persistent foreign-client latch, and one CRITICAL page per new foreign address. DigitalOcean extension and off-cluster sink steps are in the Project-store reliability runbook.


1. Read order (bootstrap)

Order Document Why
1 .cursor/memory.md (repo root; may require git show if .cursor/ is gitignored locally) Mandatory fix checklist, deployment rule, trade prohibition, findings rule, cross-interface rules (§M volume/gates), user preferences.
2 docs/CONTEXT.md Architecture, key file paths, directives (IBKR vs DB truth, no hardcoded capital, dashboard parity).
3 docs/STRATEGY.md Trading rules, schedules, position sizing, volume filter (§2.4), stops, exchanges.
4 docs/OPERATIONS.md Server access, systemd services, deploy, logs, troubleshooting.
5 docs/FINDINGS.md Canonical narrative for every finding/fix (per memory §J). Dashboard “Testing Findings” tab reads this via API.
6 docs/DATA_ARCHITECTURE.md Tables (markets, earnings_markets, price_snapshots, etc.), collectors, freshness expectations.
7 docs/DATABASE_DIRECTIVES.md Schema/migration discipline, earnings sync rules.

2. Full documentation index (docs/)

Core (active)

File Contents
STRATEGY.md Strategy, BMO/AMC schedule, conviction bands, caps, volume (effective DB-or-Gamma, SIGNAL_MIN_VOLUME_TOTAL), risk.
OPERATIONS.md Droplet [hidden], SSH, [hidden], services, logs, backfill, env.
CONTEXT.md Long-form context: modules, IBKR cache, reconciliation, config overview.
FINDINGS.md All numbered findings; only place for canonical finding text (§J).
DATA_ARCHITECTURE.md DB tables, collector sources (Gamma, CLOB, Dome), hypertables.
DATABASE_DIRECTIVES.md DB authority rules, migrations, earnings_time (Finding #220).
DATA_INTEGRITY.md Integrity checks, reconciliation expectations.
API_REFERENCE.md Polymarket/Dome API fields (verify against live Gamma when debugging).
EDGE_CASES.md Known failure modes (volume fields, shorts filter, etc.).
IBKR_CONTINUITY.md Weekly 2FA, ibgateway-ibc.service, IBC config, restart procedure. (May 31 text is partly stale — recycle 2FA and #426–#428 are missing; use the Sep 2 briefing below.)
IBKR_SETUP.md Gateway ports, VNC, connectivity. (May 10: paper described as ready-to-wire; paper is active. Capital figures drift.)
packs/2026-09-02-ibkr-vps-briefing/ IBKR-on-VPS operating manual (PDF/HTML/MD): process chain, secrets locations, client IDs, watchdogs, 2FA, recycle, greenfield replica. Probed 2 Sep 2026.
STATE_MACHINES.md Position/order state machines.
POSTGRES_OPERATIONS.md DB ops, retention, memory.
PAPER_TRADING_TEST_PLAN.md Paper testing checklist.
BMO_AMC_TIMING_DEEP_DIVE.md Timing edge cases.
OPS_TIMING_VERIFICATION.md Ops timing checks.
HOLISTIC_REVIEW_PROMPT.md Large review prompt template.
CODE_AUDIT_RECONCILIATION_2026_08.md Aug 22 code-audit reconciliation SSOT (TIER 0/1/2 queue; #393–#410). TIER 2 do-wave #401–#405; deferred T2.2 T2.6 T2.8 T2.11 T2.13 T2.15 ON-HOLD (#406). T2 leftover HTTP quota closed as #407. T2 leftover orchestrator helpers closed as #408. DONE-CLAIM/C10/C11 is #409. Aug 21–22 ship letter-audit is #410.
POSITION_SIZING_ANALYSIS.md / FINANCIAL_PROJECTIONS*.md Analysis (may be dated; verify against code).
STRATEGY_IMPROVEMENTS.md Improvement backlog / notes.
SYSTEM_REVIEW_2026_01_06.md Point-in-time review.
HYPERLIQUID_API.md If touching Hyperliquid (sniper path).

Deprecated / redirects (do not add new findings here)

File Note
AUDIT_FIX_QUEUE.md Deprecated per FINDINGS.md / memory §J.
TESTING_FINDINGS_PENDING.md Deprecated redirect.

docs/archive/

Historical specs, old Cloud deployment, superseded strategy docs. Do not treat as current unless cross-checking STRATEGY.md / code. Start at docs/archive/INDEX.md — the SSOT index of every archived/frozen doc (what it was, why historical, what superseded it; drift-audit C9 keeps it complete). It also points to the historical-DATA counterpart (the OPS-1 orderbook archive runbook in OPERATIONS.md).


3. Production & deploy

Item Value
Server root@[hidden] (DigitalOcean)
App root [hidden]
Python venv [hidden]/venv/bin/python
Env [hidden]/.env (secrets; not in git)
Deploy script [hidden]/deploy/deploy_from_github.sh — fast-forward to ONE pinned commit (DEPLOY_TARGET_SHA, HEAD asserted; #476 Round 4) + restart listed systemd units if present; launched detached by bash deploy/remote_deploy.sh [--sha <sha>] (F-8)
Dashboard URL http://[hidden]:8080

Default workflow after code changes: commit → git push origin master → from the operator machine: bash deploy/remote_deploy.sh (detached systemd-run deploy, polled to DEPLOY_EXIT; OPERATIONS §8.A1) → verify earnings-trader + polymarket-dashboard active (see memory §H / §I).

Services commonly involved: earnings-trader, polymarket-dashboard, polymarket-collector (continuous_collector.py), ibgateway-ibc, ibkr-monitor, polymarket-earnings-sync.timer (hourly), polymarket-resolver.timer (daily 06:00 ET — outcome / counterfactual / regime, Finding #227), plus backfill units in deploy_from_github.sh.


4. Codebase map (where things live)

Area Path Notes
Earnings trader src/earnings/ Orchestrator, signal generator, risk, orders, config.py, volume_policy.py (shared volume gate), market_regime_provider.py (shared regime/VIX, Finding #227), opportunity log.
Outcome resolver src/data_tracker/collectors/outcome_resolver.py Daily learning-loop fill (Finding #227): actual_beat, eps_actual, stock_price_*, hypothetical_pnl, prediction_correct. Run by polymarket-resolver.timer.
IBKR src/ibkr/ Client, gateway monitor, fees.
Data collection src/data_tracker/collectors/ continuous_collector.py — orderbooks + Gamma market sync (~15m for markets.volume_total bulk sync).
Earnings ↔ DB sync src/data_tracker/collectors/earnings_market_fetcher.py, src/db/ Hourly live writer + supported backfills; legacy manual entry points retired by #480.
Dashboard earnings_dashboard/app.py, templates/dashboard.html FastAPI + Jinja; some metrics duplicated in inline JS — grep both when changing gates (Finding #225).
Tests tests/unit/ Pytest; run with project venv / Python 3.11+ where zoneinfo exists.
Migrations src/data_tracker/migrations/ SQL migrations.

5. Rules the other agent must follow

  1. .cursor/memory.md — Mandatory checklist (§A–§M), trade execution prohibition (no orders without CONFIRMED), FINDINGS only in docs/FINDINGS.md, deploy after push unless user opts out.
  2. Cross-interface changes (volume, conviction, timing): §§E + M — grep Python + Jinja + JS + API keys; one shared module where possible.
  3. Database authority — earnings_time / trusted timing: Finding #220, DATABASE_DIRECTIVES.md; do not persist yfinance as authoritative.
  4. Volume semantics (2026): src/earnings/volume_policy.py + SignalGenerator.get_market_volume — both go through the SAME helper (memory §M consolidation per Finding #282, Apr 30 2026). Effective cumulative = max(markets.volume_total, Gamma cumulative) since cumulative is monotonically non-decreasing. Earlier "DB else Gamma" semantic per Findings #222–#226 was changed because once-per-15-min DB sync became stale within seconds for active markets (silently rejected tradeable MRNA #2026-05-01 at $6.28 < $2k while live Gamma showed $3,734).

6. Operational quick checks

# SSH
ssh root@[hidden]

# Trader + dashboard + gateway
systemctl status earnings-trader polymarket-dashboard ibgateway-ibc

# IBKR API port (live)
ss -tlnp | grep 4001

# Collector
systemctl status polymarket-collector
journalctl -u polymarket-collector -f

IBKR 2FA: systemctl restart ibgateway-ibc.service — approve on mobile within timeout; see IBKR_CONTINUITY.md.


7. Configuration highlights (src/earnings/config.py + .env)


8. Tests & CI


9. When to add documentation


File Purpose
README.md Quick start, repo link, high-level structure.
archive/PROJECT_STATUS.md Historical snapshot (archived; may be stale — verify live).
.env.example Non-secret env template.
deploy/deploy_from_github.sh Production deploy + service restarts.
requirements.txt Python deps.

11. Current state (last updated: Oct 4, 2026 — Findings #486 and #487 DEPLOYED (unit polymarket-deploy-20261004T120553Z-812717079, SHA 7c9d6b0, 12:05 UTC: NASDAQ calendar GET timeout=8 and a 45s budget; orderbook size FAIL names a finished drain; trader 12:06 UTC, integrity monitor 12:08 UTC; Gateway not restarted). Sep 26, 2026 — Sep 27, 2026 — Findings #481/#482 DEPLOYED (deploy #14 28b572b 00:33 UTC: #481 quarantines a permanently failing live-trade row once and marks it seen, market-data poison is WARN only; deploy #15 860af84 02:43 UTC: #482 migration 042 widens trades.token_label to varchar(64), the typmod probe is scoped to 042, and the three quarantined payloads — two distinct trades — were released, spool empty; the deploy-#14 dashboard restart paged once again, fix PR #9 pending; no trading change). Reliability Batch B3 — Findings #454/#455 and migration 039 — DEPLOYED Sep 26, 2026 23:29 UTC (deploy #13 00607c9, the exact approved SHA, pure fast-forward: paced IBKR in-process recovery with reset/latch truth; a durable DB-write spool for entry/exit/execution/reconciliation/live-trade writes; 039 idempotency keys; the live-trades poison-row loop (trades.token_label varchar(20) vs a 22-character outcome; 26 CRITICAL alerts 23:32:47–23:37:46 UTC; polymarket-live-trades stopped 23:39:08 by operator decision) was fixed by #481 (deploy #14 28b572b, Sep 27 00:33 UTC: poison rows quarantined once and marked seen, market-data poison WARN only, collector restarted) and #482 (deploy #15 860af84, 02:43 UTC: migration 042 widened token_label to varchar(64); the quarantined trades were released and the spool was empty at 02:51); the #454 soak (next 04:13 reset and 06:00 AutoRestart) is pending; no strategy change). The #479 sampler-grace race hardening DEPLOYED Sep 26, 2026 22:39 UTC (deploy #12 2ecb02c: one systemctl show snapshot per evaluation, a restart-loop FAIL that overrides the grace, the untrusted-address WARN checked before the stale grace; the deploy's own dashboard restart still raced the new monitor's first cycle and paged once, resolved five minutes later — follow-up OPEN; no trading change). Finding #467 and the #479 sampler restart-grace fix DEPLOYED Sep 26, 2026 22:19 UTC (deploy #11 3138aca: dashboard response secret-redaction filter; FastAPI auto-docs off and the three inert write endpoints removed; the IBKR account, wallet and server identifiers masked on public pages, docs included (one open gap: /api/btc5m/docs/doc_map still shows the raw WALLET_NAGI value, the six-character prefix of the third-party benchmark maker's wallet (not a full address, not Marius's wallet); a code follow-up is OPEN); the sampler rules tolerate a deploy restart for 120 s; no trading change). Findings #477/#480 DEPLOYED Sep 26, 2026 21:56 UTC (deploy #9 0e985dd: guarded data migrations 040 then 041 applied — #477 one P&L definition for closes and corrections plus a reconciler P&L-consistency check, BBW 274/307 −121.52 → −282.10 and SNOW 75 net −9.96 → −1.16; #480 pg8000 covered-channel lookup typed, the unaudited legacy sync retired, the four false leak rows relabelled timing_channel_mismatch; the Phase-1 net from the DB is now −$922.28 (n=13), matching the record; no strategy or admission change). Findings #457/#478/#479 DEPLOYED Sep 26, 2026 20:55 UTC (deploy #8 614a29d: #457 committed secrets scrubbed from HEAD + hash tripwire, the doadmin reset DONE 20:49:21 UTC before the deploy; #478 C-Q2/G-RS count trigger, 2/12; #479 per-service application_name + DB activity sampler — the post-deploy evaluator defect (security:db_client_activity could not evaluate, so the foreign-client page was inactive 20:58–21:33 UTC) is fixed DEPLOYED Sep 26, 2026 21:30 UTC (deploy #10 f28c0da); the rule reads ok; no live trading behaviour change). Findings #473/#474/#475 reliability follow-ups + docs sweep DEPLOYED Sep 26, 2026 19:05 UTC (deploy #7 a695050: #473 requirements pinned to the prod venv — the deploy never runs pip, prod pip freeze equals constraints.txt; #474 migration-baseline check — prod --check-baseline 5/5, the deploy ran no DDL; #475 C-Q2 detection replay, offline research tooling; docs sweep — Phase-1 P&L record, dashboard findings parser, drift-audit C10/C12, comment-only code edits; no live trading behaviour change). Finding #468 prod checkout hygiene DEPLOYED Sep 26, 2026 15:15 UTC (deploy #5 f130113; .gitignore + OPERATIONS §8.A2 + test only, no runtime change; prod git status --porcelain empty; prod moves DONE 14:59 UTC). Reliability Batch A DEPLOYED Sep 26, 2026 (deploy #1 884e528 10:18 UTC; #445 fix + F-7/F-8 deploy #2 3d7f1b1 11:48 UTC; apt-daily-upgrade hold ended 11:27 UTC): Finding #453 Monday 28 Sep stall stopgap (300 s dated watchdog budget, self-expiring Tue 00:00 ET); Finding #445 dashboard hypertable SQL bounded + Sep 25 read-only incident record (7-day coverage cached 6 h; per-statement transactions; 60 s statement timeout); Finding #444 deploy migration ledger (029 never re-runs; DDL under lock_timeout; output logged); Finding #443 needrestart covers the live xvfb.service/x11vnc.service (deploy-time coverage check; apt timer hold ended operator-attended at 11:27 UTC). Previous production baseline was #442 (DEPLOYED Sep 13, 2026). Finding #442 earnings-date join + 21-day verifier lookahead + timeline DATE SPLIT DEPLOYED Sep 13, 2026 (CBRL Wed 23 no longer wears Sep 14; verify 5→21 days; votes persist on confirm-in-place; #349/#424 leftover). Finding #441 1102 no nested util.run; attach-bounce after IB() reset DEPLOYED Sep 9, 2026 (04:14 1102 then 5h reset loop; farm sticky blocked bounce; #440 leftover). Finding #440 same-thread req after 2FA DEPLOYED Sep 7, 2026 (heartbeat/reconnect must not util.run on another loop; #87 leftover; 03:57 RESTORED then 40s Mode B). Finding #439 weekly last-resort 2FA budget DEPLOYED Sep 5, 2026 (one Python recycle per Sunday week; 7d cooldown; Sunday 06:00–18:00 UTC quiet; #438 leftover). Finding #438 last-resort recycle DEPLOYED Sep 5, 2026 (P1 req first; P2 2FA only Sunday or after IB() reset failed; #435 age/cooldown; #437 leftover). Finding #437 Saturday stay-connected DEPLOYED Sep 5, 2026 (recycle-off periodic IB() reset; heartbeat keeps probing; weekday AutoRestart reattach no bounce; attach down_since continuous; #436 leftover). Finding #436 weekly-only Sunday 2FA DEPLOYED Sep 3, 2026 (recycle-off default; Mode A/B keep probing; Mon–Sat session preserve; Sunday/crash attach-only; #434 leftover). Finding #435 recycle-once DEPLOYED Sep 3 (Mode B in-place first; no 2FA while Gateway young / cooldown / late-watch; 06:27/06:48/07:14 leftover). Finding #434 stay connected first DEPLOYED Sep 3 (recycle-on even with a live book; farm window still no-2FA; #430 leftover). Finding #433 keep probing when weekly 2FA holds recycle off DEPLOYED Sep 3 (Mode B exhaust-and-stop has no next step; header follows request_path_ok; 3 Sep 05:16 leftover). Finding #432 Mode A daily reconnect without 2FA DEPLOYED Sep 3 (1100 sticky farm probe until durable success; 900s timer no longer exhausts; Mode B leftover AMENDED by #433; recycle stays off). Finding #431 channel-window already-evaluated DEPLOYED Sep 2 (AEO C-1 generate_signal covers; any non-timing row; true timing-only still FAIL). Finding #430 weekly 2FA contract DEPLOYED Sep 2 (standing Gateway recycle off; midweek ibgateway-ibc restart was the extra 2FA; Sunday 06:00 stays; true needs GUARD-OVERRIDE-UNTIL). Finding #429 late-login quiet + Mode B window DEPLOYED Sep 2 (2FA resets 1800s quiet; 8 heartbeat fails/600s exhaust). Finding #428 late-login attach DEPLOYED Aug 30 (2FA abort keeps watching IBC+:4001; systemd-run bounce; flat-book re-check in that call). Finding #427 farm-recovery + standing recycle DEPLOYED Aug 29 (1100 keeps 900s probe window — no 61s zombie-exhaust; no-1100 TCP-up still #426 exhaust; flat-book auto-recycle Gateway then trader; IBC 06:00 is not recycle). Finding #426 Gateway zombie DEPLOYED Aug 28 (durable-only healthy; TCP-up trip exhausts not 300s loop; no client 99 on dead path; recycle CONFIRMED 11:22 UTC). Finding #425 etfstack VIX fallbacks DEPLOYED Aug 28 (Yahoo chart KeyError → history → Cboe → last-good cache). Finding #424 ASO official IR DEPLOYED (Sep 1 disputed → Sep 9 BMO confirmed). Finding #423 trade-reconcile BBW#274 DEPLOYED (live log + LMT fill + IBKR ingest). Finding #422 Trader Session READY DEPLOYED (Connected, not Unknown). ** Finding #421 IBKR one session machine DEPLOYED (TCP-up always in-place; connect/1102 clears REAL_DROP; bind errorEvent before connect; 1102 probes in place; STILL DISCONNECTED only if TCP down; PATH_DEAD/EXHAUSTED win the header; empty live book no ghost last-good stops; #420 1100-misclassify leftovers AMENDED; trader bounce — book flat). Finding #420 IBKR nightly-1100 leftovers DEPLOYED (CancelledError swallow; 1100 skips in-place exhaust; skip VIX/snapshot flood; UNREADABLE not on live book; last-good stops kept; recon-guard 1800 after exhaust; #419 AMENDED; trader bounce — BBW/DG hardware stops stay). Finding #419 IBKR in-place reconnect escape hatch DEPLOYED (quiet heartbeat while TCP up; one IB() reset then stop; no infinite 60s; leftover timeout cancel; UNREADABLE not on live book; #418/#416 AMENDED; trader bounce — HPQ hardware stop stays). Finding #418 IBKR reconnect amplifier DEPLOYED (keep TCP session on durable-health fail; last-good book; skip 412 reload; exit still attempts bounded send; #415 AMENDED; trader bounce — BBWI hardware stop stays). Finding #417 needrestart + overnight trades freshness DEPLOYED (live-trades + integrity-monitor excluded; overnight max_age 180 min / RTH 60 min). Finding #416 unverifiable IBKR ≠ missing DEPLOYED (IBKR UNREADABLE vs IBKR MISSING; overlay skip). Finding #415 IBKR latch leftover DEPLOYED (re-trip only inside durable probe; 300s reconnect while dead; get_quote skip; #413 AMENDED; trader bounce — BBWI hardware stop stays). Finding #414 channel-window leak already-admitted suppress DEPLOYED (ignore timing_channel_leak when same condition_id already signal_emitted/admitted; DKS Aug 25 false #384; trader + integrity bounced). Finding #413 IBKR request-path latch hysteresis DEPLOYED (3 successes incl. qualify to clear; 5 min reconnect cooldown; no ibkr_disconnect on #412 trips; re-trip leftover AMENDED by #415). Finding #412 IBKR request-path latch DEPLOYED (3 consecutive bounded_request failures → trading block 412 + Telegram ibkr_request_path + dashboard request_path_dead + integrity trader:ibkr_request_path; clear/reconnect/alert AMENDED by #413). Finding #411 orderbook-archive dual-limit pacing DEPLOYED (18/run, TimeoutStartSec=21600, 00:35+06:35 UTC). Trader not bounced (mid-session; no trading-path change). Finding #410 ship-verification letter-audit (record only; no trading-logic change). Finding #409 DONE-CLAIM + C10/C11 DEPLOYED Aug 22, 2026 15:23:13 UTC SHA defe78f. Finding #408 leftover-orchestrator twins DEPLOYED Aug 22, 2026 14:49:44 UTC SHA 4e4ffa1. One persist helper + one missed-exit/scheduled-exit helper. Trader process is defe78f (ActiveEnterTimestamp 2026-08-22 15:23:13 UTC); #410 did not bounce the trader. This docs-stamp is a later SHA. Previous: Finding #407 HTTP quota + five lean memory.md §F/§H clauses DEPLOYED Aug 22, 2026 13:39:29 UTC SHA 7fbfe05. T2 leftover HTTP quota closed. Previous TIER 2 do-wave Findings #401 #402 #403 #404 #405 #406 DEPLOYED Aug 22, 2026 07:53:22 UTC SHA 88014f4. Reservation credits 10:32 exits; CapitalManager gone; INTRADAY skip; Telegram log + fake schedule gone; one PDT constant; deferred T2.2 T2.6 T2.8 T2.11 T2.13 T2.15 ON-HOLD on the Action Queue. See FINDINGS for status markers. Previous: Aug 22, 2026 night — #400 get_positions fail-closed DEPLOYED Aug 21, 2026 23:00:26 UTC in the trader process at SHA e857f5e (systemd ActiveEnterTimestamp, NRestarts=0). Do not write “Aug 22 ~23:00 UTC”. This docs-stamp commit is a later SHA than the trader process — origin/master after this stamp is NOT e857f5e. #393–#399 remain live from the earlier 4292bdb restart (2026-08-21 21:43:10 UTC). T1.6 CLOSED on the wire Aug 22 ~22:38 UTC (POLYGON_KEY rotated + REST probe 200; burned default is only in rotate bak files). Prior implementer WRONG on UTC (wrote Aug 22 ~21:43 UTC), WRONG on 'must ROTATE' / 'prod .env still holds the burned default', and WRONG on 'fully memory.md'. Previous: Aug 21, 2026 15:17 UTC — THE QUEUED MARKET-SAFE TRADER RESTART EXECUTED (operator-approved, Friday ~11:15 ET inside the safe corridor): the trader now runs origin/master 7340dd5 — #378 + #379 + #380-code + #382-detector + #384 are LIVE in the trader process. Pre-check all green (0 open positions DB+IBKR, 0 breakers, 0 stops today, no signals in flight; snapshot /root/deploy_snapshots/pre_restart_378-384_20260821T151542Z); pull was a no-op (server already at origin/master — today's earlier targeted deploys had synced code, only the running process was stale); full deploy_from_github.sh run (contract check PASS, dashboard HTTP 200); post-restart NRestarts=0, reconciliation "DB and IBKR in sync", scheduler armed (10:30 AMC / 15:50 BMO / 15:55 exits), startup shows prnewswire (corroboration-only, #379) + zero RuleB lines, GUARD_REGISTRY clean (16 entries), integrity 72 rules / same 2 known FAILs (storage drain + FIVE disputed date) / rule set identical pre-post, §G probe proved the #384 closed=true fallback live on DE's closed market; #378/#384 epochs (Aug 23) unchanged — conservative enforcement start; inert RULEB_* prod-.env lines removed with a dated comment per the #380 plan. Earlier — Class-A queue day: A-1 DONE (#372 deep dive — operator REDIRECTED A-1 to "always use the market's own benchmark", census 1,307 markets: TRUE mislabels 141/1,239 = 11.4% + 12 poisoned trade flags + 34 free backfills — then APPROVED IN FULL → #382 IMPLEMENTED + §F backfill LANDED the same day: actual_beat now written by the 3-tier system (the market's own UMA resolution, Gamma degenerate-only, write-once-LOCKED pm_resolution_v1 > basis-matched benchmark EPS vs the market's stated strike parsed at discovery into benchmark_basis/benchmark_comparator (migration 034; strict >, tie→NO, abstain-on-unparseable, NEW shared src/earnings/benchmark_contract.py) > w357 sustained-crowd proxy, dashboard-marked ~provisional); pre-event conviction cascade + single-snapshot 0.95/0.05 RETIRED as label sources; resolver Pass 4 corrective; backfill verified on prod: 1,273 resolutions stamped / 141 relabels / 29 fills (5 of the census 34 self-filled between census and run, all agreeing) / 85 trade corrections / 2 condition_id repairs (CME #29 + XOM #19) / 0 conflicts / a1_resolution_compare probe 0 mislabels + 0 poisoned + 0 fillable / second run plans ZERO; displayed prediction accuracy 87.3% vs predicted ~88 (flips nearly cancel); audit CSVs scripts/analysis_357/backfill_372_audit{,_v2}.csv; the NEW labels:benchmark_parse drift rule caught REAL drift on its FIRST cycle — 6 whole-dollar-strike markets (no pt fraction, invisible to the legacy eps parser too) → slug parser v2 (PARSE_VERSION v2); resolver/dashboard/integrity DEPLOYED (migration 034 applied, targeted restarts, no positions held); the earnings_detector basis guard (GAAP: Yahoo street claims → UNKNOWN; wire claims refused persistence-alone) is TRADER-side — DEPLOYED Aug 21, 2026 15:17 UTC at the market-safe trader restart (with #378/#379/#380); commits 79122bd (implementation; jointly carried the in-flight A-9 detector change to keep the shared suite green) + 7d622a5 (parser v2) + the records commit; FINDINGS #372 (design) + #382 (implementation)); A-2 DONE (#371 — resolver 6-hour lockout fixed structurally: retry guard re-keyed from the shared updated_at the hourly sync kept touching to a resolver-OWNED resolver_attempted_at, migration 033; decayed actual_beat backlog self-heals on normal timer fires); A-3 DONE (#375 — the 5-part volume-integrity bundle: monotonic GREATEST guard on all 3 markets.volume_total writers; closed-market Gamma fetches fixed via a closed=true RETRY — the param is an EXCLUSIVE filter, verified empirically, the literal "add closed=true" would have blinded every open-market fetch; real events-sync pagination (the API silently caps at 100/page — limit=500 was a no-op beyond page 1); entered/past dashboard rows show ADMISSION-TIME volume (@adm) with ⛔ below-gate-AT-DECISION vs ⓘ below-gate-NOW icons; 24h~ marker on the rolling fallback; stored finals re-baseline FIX-FORWARD per #365; deploy = collector + dashboard restarts only, trader untouched); A-5 DONE (#378 — admitted-stage opp-log rows restored: they NEVER flowed live, all 369 historical admitted rows were the May-29 synthetic backfill of pre-Apr-22 markets and the census misread the backfill's Apr-8 coverage edge as a live stop; NEW _log_admitted_signal at batch scheduling in both cycles + consistency:opp_log_admitted_coverage HIGH integrity rule; DEPLOYED Aug 21, 2026 15:17 UTC at the market-safe trader restart — the rule epoch Aug 23 had assumed a weekend restart and stays as a conservative enforcement start); A-9 DONE (#379 — PRNewswire demoted to corroboration-only: NEW frozen CORROBORATION_ONLY_SOURCES = {'prnewswire'} makes the #342 arbiter REFUSE persistence-alone admission of a solo PRNewswire contra claim (the residual solo path on non-GAAP markets; crowd-agree + second-source corroboration stay open, pending stays armed, veto reset unchanged) + the BusinessWire poller DELETED (_check_businesswire gone — 842k failed/error log lines, ZERO detections ever, truth-audit §5); changelog #379 per §O; DEPLOYED Aug 21, 2026 15:17 UTC at the same trader restart as A-5); A-12 DONE (#380 — operator chose REMOVE: RuleB apparatus deleted entirely (module + orchestrator hooks + RULEB_* knobs + GUARD_REGISTRY entry + tests; phase3 refutation evidence KEPT); trader-log contradiction resolved live — the 2.33 GB unrotated log is REAL at [hidden]/logs/ (unit append: target, outside #274 logrotate), the 0-B file was the legacy /var/log twin, journald was 3.9 GB unbounded; logrotate extended + journald bounded 2G/14d INSTALLED LIVE, code deletion DEPLOYED Aug 21, 2026 15:17 UTC at the same trader restart); A-13 DONE (#381 — counterfactual window-truncation fixed: the CF cron had priced trades MID-FLIGHT (1,100/3,355 stored prices computed before the simulated exit; DELL +32.1% stored as +2.2%) — the pricing library now REFUSES unsettled (+30 min buffer) or bar-gapped (>5 min, stop-hit exempt) windows via never-persisted cf_refused_* versions, the cron SELECT excludes unsettled exits, resolver Pass 2 gains both guards, NEW HIGH rule consistency:cf_window_truncation, and --requeue-truncated re-queued the backlog through the fixed job; deployed LIVE, collector-side, no trader restart); A-14 DONE (#384 — the FINAL Class-A item, queue COMPLETE 14/14: silent coverage bugs fixed — (1) conviction-fetch failures (TMUS/AXP/PYPL class): Polymarket CLOSES earnings markets ~9:00 ET on earnings day BEFORE the 10:29 ET AMC conviction check and the #333 live-Gamma fallback lacked closed=true (the #375 exclusive-filter trap) → [] for exactly the closed markets it existed to save; fixed with a closed=true retry on empty + a 2-attempt transient-HTTP retry + WARNING-level failure logs (the silent debug swallow hid the class for months); (2) channel-window leaks (MA/WWD class): inconsistent timing resolution made BOTH channels say "not mine" until the right channel's entry moment passed; NEW classify_timing_mismatch stamps skip_reason='timing_channel_leak' + ERROR when the resolved channel's entry moment (AMC 10:39 ET; BMO midnight ET) has passed; TWO HIGH integrity rules coverage:conviction_fetch + coverage:channel_window_leak (explicit marker + timing-only structural sweep), epoch Aug 23; DEPLOYED Aug 21, 2026 15:17 UTC at the market-safe trader restart (conviction_calculator + signal_generator in-process, with #378/#379/#380/#382-detector) — §G probe proved the closed=true fallback live on DE's closed market). Earlier 07:10 UTC — OPS-4/OPS-5 CLOSED (#369/#370, deployed 06:56 UTC); earlier same day: DEPLOYED: PKG-357 (#366) + Phase-1 (#367) are LIVE. Operator "deploy all" executed in the overnight market-safe window (00:34 ET, market closed, 0 open positions, 0 breakers): server at 3465087; prod .env PDT_RESERVE_CAPITAL=17450 + stale SIGNAL_LONG_MAX_THRESHOLD 0.95→0.90 fixed; migrations 031/032 applied BEFORE the restart; close-persist contract check PASS; trading capital $10,054.38 live (PDT floor intact); integrity 0 FAIL incl. the new b5_pilot_auto_revert + open_position_release_window rules; Phase-1 pill + "Trades: 0" + frozen testing count 193 verified; monitors M-C1/K-S2C/K-B4/K-RES/K-B5 ARMED, D-6/D-7/D-9 RETIRED. Earlier — Finding #367: Phase-1 transition ENCODED — "TESTING IN PRODUCTION" → Phase 1 with a P&L fresh start and a from-zero trade counter, all derived from the single Config.PHASE1_START_ET boundary (default 2026-08-21; deployer sets/verifies at deploy): blue "Phase 1" header pill, "Trades: N" counter (Phase-1 only), Test Progress cell removed, History Trading Capital ROI re-cut to three stacked windows (All-time / Testing in Production frozen at 193 trades / Phase 1), "Levrage"→"Leverage" typo fixed; changelog #367 + chart marker; NOT DEPLOYED — rides the PKG-357 deploy, checklist extended. Earlier — Finding #366: PKG-GO given and the FULL PKG-357 package IMPLEMENTED in code — 9 implementation commits + ship mechanics (A-7 and B-5 own commits): $10k capital step (repo lockstep; prod .env line at deploy) + S2c equal-split sizing with $9k activation gate + B-4 3-tier priority table + C-1 LONG band re-closed 0.90 + RES M1+M5 tonight-only reservation/A-10 + WAVE-RT + CLB-1/A-6 (consecutive-loss breaker retired, daily-loss 5% sole brake, persisted) + B-5 small-cap pilot (auto-revert ENCODED) + M-VOL V-BLEND shadow; migrations 031/032; full unit suite green py3.11. NOT DEPLOYED — the live bot runs pre-#366 code until the market-safe window; DEPLOY CHECKLIST in IMPLEMENTATION_PLAN_2026_08.md. Earlier — Finding #365: the BABA apparent volume-gate breach ($930 shown vs the $1,000 gate) deep-dived: DISPLAY ARTIFACT, zero true breaches (admission volume $2,145.74, gate PASS legitimate); A-3 EXPANDED into a 5-part volume-integrity bundle — records only, NO code shipped. Earlier — Finding #364.b: FUND-1 re-specified as a pure CONFIG/CODE change (verified live: equity $27,465.52 ALREADY in the account; PDT_RESERVE_CAPITAL 25000→17450 → trading capital $10,015.52; NO money movement; PDT floor margin $2,465.52) + the P1_SURFACES census-grade re-audit of the #363/#364 package additions (census 148→161, flags N14–N23, §11a addendum replaced by full inventories) + the binding memory.md execution-discipline note — records only. Earlier same day — Findings #363 + #364: circuit-breaker audit + operator decisions RECORDED (records only; NO trading-code changes, NO deploys, production DB untouched). #363: the commissioned breaker audit found the consecutive-loss breaker's count semantics BREAK under S2c (70% of 3-loss runs same-wave; 49 firings/47wk package replay, 28 blocking nothing; money ≈ $0 outlier-noise) → verdict RETIRE the counter, keep the daily-loss 5% brake; 33-control inventory 24 SAFE · 6 NEEDS-RETUNE · 3 REDESIGN; D-9 armed, D-4 extended. #364 (later same day): operator APPROVED CLB-1 exactly as recommended (counter display-only; daily-loss 5% = SOLE loss brake, restart-proof with true-ET resume + DB-anchored day P&L; hidden ≥3/≥5-loss sizing halving re-keyed to daily-loss tiers; A-6 elevated into the ship set), CONFIRMED the ONE-PACKAGE PKG-357 manifest (A-7 → C-Q1 capital step → S2c+activation gate → B-4 12-surface lockstep incl. the P1_SURFACES 148-surface inventory → C-1 → RES M1+M5 breaker-independent + A-10 → WAVE-RT BATCH-1/EQ-1/MSP-1 → CLB-1+A-6 → B-5 pilot → M-VOL shadow → arm M-C1/K-S2C/K-B4/K-RES/K-B5, retire D-6/D-7/D-9 → ONE changelog entry + supersession flips; 4 missed items flagged and added: the C-Q1 capital step, MSP-1, D-6/D-7 retirement, ship mechanics) and RECORDED the pending-decisions register (FUND-1 gates S2c activation; PKG-GO = the ONLY decision missing for the build to start; Class A queue A-1/A-2/A-3/A-5/A-9/A-11/A-12/A-13/A-14 = per-commit NOW work, corrected from "post-package"; A-12 fix-or-REMOVE flagged; OPS-1 CLOSED (#383 Aug 21 — option C lossless archive pipeline live, backlog drains overnight); OPS-2 CLOSED (#373); OPS-3 CLOSED (#377); C-Q2 ~Sep 13; C-Q3 before ~Oct 12). The consecutive-loss breaker STAYS LIVE and D-9 stays armed until CLB-1 ships. Records: FINDINGS #363/#364, IMPLEMENTATION_PLAN_2026_08.md (manifest + register sections), IMPLEMENTATION_PLAN_P1_SURFACES.md §11 addendum, STRATEGY_REVALIDATION Part I/§II.7/§II.8/App §15, dashboard rows breaker-retire-363 + wave-scale-retunes-363 + CLB-1 decision row → APPROVED-AWAITING-IMPLEMENTATION (all four template encodings already carry the #361 vocabulary), package row scope = the full manifest, both canvases. Previous: Aug 18, 2026 — Finding #362: VIK entered a DAY EARLY (Mon Aug-17 for a Wed Aug-19 BMO release) — root cause chain: #333 end_time OR-branch selection → #349 resolved a Tuesday entry → NO entry-DATE guard (#359 only rejects LATE) → run_bmo_cycle's "All BMO have same entry time" batch assumption. Fix package SHIPPED: entry-date invariant wrong_entry_date at all 4 admission loops + batch scheduling + execution moment; post-resolution window re-check schedule_outside_window in the signal generator (closes #333↔#349); integrity rule consistency:open_position_release_window (HIGH; VIK Aug-17 allowlisted as known incident); multi-day BMO reservation dedup; dashboard ET timestamps + WRONG-ENTRY-DAY red highlight; ON/NET date-drift recorded as a measurement caveat (PLAYBOOK §5), NO historical rows repaired. VIK LEFT OPEN per operator decision (stop live, exits Wed Aug-19 10:32 ET; tonight's cycle cannot re-enter it — already-have-position check). 1058 unit tests green. Previous: Aug 17, 2026 — Finding #361 + #361.b (corrective sweep + DEPLOY: the #361 records are now LIVE on the dashboard; stale-pending text on 8 surfaces fixed; status-vs-description tripwire added): operator APPROVED all three open Part II decisions — C-1 (re-close the LONG cap at 0.90), RES (the M1+M5 reservation mechanism, multi-day component dropped), B-5 (the small-cap LONG 0.80–0.95 pre-registered pilot). RECORDS ONLY: nothing implemented, nothing deployed — the three carry the NEW status APPROVED-AWAITING-IMPLEMENTATION (distinct from open AND from implemented-pending-deploy), wired into build_revalidation_357() / build_action_queue() (rows reservation-m1m5-357, smallcap-long-b5-357; relax-202-9095 annotated) and ALL FOUR dashboard template encodings (meta/filter/summary-chip/sort + decStatusColor, the #334 tripwire). The #360 action plan is FINALIZED (every #357-program decision is made): decisions_done → implementation queue (capital/reserve step → the package awaiting the operator's EXPLICIT go — S2c+B-4+M1/M5+C-1 re-close, B-5 rides or ships separately → Class A fixes, A-7 first). Operator-requested plain-language reservation explainer added: doc §II.2.1 + plain_language field on the RES queue row. The #361 records ride the NEXT operator-triggered deploy. See FINDINGS #361. Prior: Aug 16, 2026 late — Finding #360: the dashboard Strategy tab RECONCILED to the re-issued decision record (records/dashboard only, NO trading behavior): the #357 section now renders the Part I/II decision states (C-Q1/S2c/B-4 DECIDED-NOT-IMPLEMENTED with the HARD-PDT red callout — equity ≥$25k, reserve ≈$17,450 NOT withdrawn, no day-trade counting in code; B-3/A-8 implemented as #359/#358 — DEPLOYED Aug 16, 2026 20:22 UTC; C-2 SUPERSEDED by the decided B-4 table; C-1/RES/B-5/C-Q2/C-Q3 open with recommendations), the ORDERED ACTION PLAN (decide C-1 + M1/M5 + B-5 now → deploy #358+#359+#360 → capital/reserve step → the S2c+B-4+M1/M5 package with A-7 prerequisite → Class A fixes) and the consolidated 16-row §II.7 monitor table. Action Queue reconciled: Audit re-validation → IMPLEMENTED (executed as #357); vix-2025-truesim-357 → D-2 DISCHARGED (shadow monitor); drop-5k-volume-bump-357 → RETRACTED (folded into B-4); #314.e SKIP candidates RETIRED; v3 D folded into B-4 / v3 E refreshed to D-5/B-008 criteria; NEW rows package-10k-s2c-b4-357 (rank 1, new status DECIDED-NOT-IMPLEMENTED), class-a-fixes-357 (rank 2), reservation-m1m5-357, smallcap-long-b5-357. Tests 25/25 + full unit suite green. DEPLOYED Aug 16, 2026 20:22 UTC — #360 shipped with #358+#359 in a single deploy_from_github.sh run at efebe45; the live tab renders the decision record (verified: PDT callout + 16-row monitor table + rank-1 package row served). See FINDINGS #360. Earlier same day: #357 RE-ISSUED (same filename): docs/STRATEGY_REVALIDATION_2026_08.md restructured into Part I decision record (DECIDED Aug 16: C-Q1 capital → $10,000 — inside a ≥$25k-equity account, the PDT constraint callout §I.1; reserve ≈$17,450, NOT withdrawn; S2c sizing; the B-4 3-tier table as fill-ordering/safety infrastructure, $0 profit claim; B-3 = #359; A-8 = #358) · Part II open decisions awaiting operator sign-off (C-1 close the 90–95 band — recommended CLOSE + band shadow monitor; M1+M5 reservation mechanism; B-5 small-cap LONG pilot; 12 remaining Class A fixes — A-7 is now an S2c prerequisite; ALL armed monitors/triggers/contracts consolidated in ONE table, §II.7) · Part III updated verdict register (51 HOLDS · 9 WEAKENED · 5 REFUTED · 10 INSUFFICIENT-DATA · 5 DORMANT; B-006 HOLDS→WEAKENED per §12; superseded rows retired explicitly) · Part IV study library — appendices §0–§3 and §7–§14 preserved verbatim with ORIGINAL section numbers (old §4 → Part III, §5 → Parts I–II, §6 → §I.4; every existing "§N" citation still resolves). Canvas rebuilt to match. #358 + #359 DEPLOYED Aug 16, 2026 20:22 UTC (single operator-triggered deploy with #360). Same day: Finding #359: stale-signal guard at ADMISSION shipped (operator-approved B-3 of the #357 queue): signals whose intended entry time is >5 min past are rejected as born_dead_schedule BEFORE consuming a slot/capital (new Config.STALE_SIGNAL_MAX_ENTRY_LATE_MINUTES, all 4 admission loops, W6 Bug-B _log_exposure_skip date-field fix rode along = A-4 done); 995 unit tests green; DEPLOYED Aug 16, 2026 20:22 UTC together with #358. Same day: Finding #358: the A-8 print-density collapse is ROOT-CAUSED and FIXED in code (three collector bugs: a markets-table lifecycle leak throttling the poller to ~1 print/5–9 min per market, a WebSocket collector with a frozen subscription list living outside the deploy pipeline, and a WS writer storing order-book LEVEL prices of EITHER token — NO uninverted — as yes_price; plus the #351 stale-median geometry still live on the PRIMARY detection path, now fixed with a fresh-window median + live-Gamma fallback). 976 unit tests green. DEPLOYED Aug 16, 2026 20:22 UTC (deploy/deploy_from_github.sh from the server; first reaper run closed exactly 1,288 stale rows). Historical WS-signature prints (volume_24h/liquidity NULL) are order-level prints, NOT market prices — exclude them from analyses; the #336/#339 "noisy stream" phenomena were largely this bug. See FINDINGS #358 + docs/STRATEGY_REVALIDATION_2026_08.md §10. Same day, earlier: Finding #357: FULL STRATEGY RE-VALIDATION at n=183 COMPLETE — ZERO trading changes shipped; all candidates queued for operator decision. The 9-workstream program (80-row assumption register under scripts/analysis_357/, reports w1–w9 + Phase-3 synthesis) delivered docs/STRATEGY_REVALIDATION_2026_08.md: 52 HOLDS · 8 WEAKENED · 5 REFUTED · 10 INSUFFICIENT-DATA · 5 DORMANT. Headlines: (1) core premise REFUTED as stated — the +$120.80 net book = stop-mechanics (+$123/+$321 vs no-stop) + named-5 outliers (+$400.92 > total net) + selection cells (70–75 band +$408/18 gauntlet-PASS); pre-release drift is generic (non-traded markets drift too), ~73% survivorship-conditioned, ≈0 post-Jul — falsifiable KILL monitor armed (Sep+Oct universe drift ≤0 AND book net ≤0 ⇒ regime edge dead); (2) resolver truth bug (W7 §0, 64/648 mislabeled, GAAP-vs-adjusted one-sided): corrected calibration Brier 0.1760→0.1432, overconfidence gaps ~halve, the "post-June high-band deterioration" REVERSES; 14 canonical trades carry poisoned prediction_correct (§F repair queued Class A-1); W9's no-conviction-gradient conclusion survives; (3) 0.90–0.95 reopened band FAILED its contractual re-gauntlet on real fills: −$1.90/trade vs +$6.19 promised (18 fills, ex-top-2 −$163) → OPERATOR DECISION C-1 pending: re-close the LONG cap at 0.90 (action-queue row relax-202-9095 → NEW status FAILED-REGAUNTLET); priority REFUTED as P&L ranker (ρ=−0.06), #313 volume gradient GONE (≥$5k tier worst) → C-2 pending: drop the +2@$5k bump; (4) volume-collapse narrative re-based: markets.volume_total rewritten DOWNWARD on 44% of markets — entry-time pass rates 51–83% all year (Jul/Aug best); May–Jun was the real (seasonal) trough; Oct season: slots bind, not universe; (5) all defensive/mechanical rules survived (stops 3/4/5 ~optimal, RuleB still FAILED-GAUNTLET at n=186, flat-25 OVER-delivered +$17.4/wk vs +$7.4, instant-exit apparatus net-NEGATIVE −$157 lifetime — kept as tail insurance). Queue: 12 Class A data/code fixes (resolver EPS-tier fix + §F backfill, volume monotonic guard, breaker persistence −$58, LLY admission backfill, PM snapshot density, PRN demotion, …) + 3 Class B operator decisions (C-1, C-2, stale-signal guard) + 3 Class C strategic questions (≥$10k capital, detection funding, Oct capacity) + 8 Class D armed triggers ($2k-gate re-price at n≈240, VIX 20–25 true-sim replay, monthly drift-decay monitor, …). Dashboard: NEW Strategy-tab section "2026-08 Full Re-validation (#357)" (build_revalidation_357()) + FAILED-REGAUNTLET status + 3 new queue rows. NO strategy_changelog entry (nothing strategy-affecting shipped). Prior entry — Finding #356 SHIPPED: guard-override tripwire — disabled live defenses police their own restoration. The #355 session-breaker override was restored Aug-9 instead of the planned Aug-5 night (the restore died with an interrupted agent session; zero behavioral impact verified — Aug-6 peaked at 3 stops, Aug-7 at 0). Structural fix: any .env guard-weakening line now requires # GUARD-OVERRIDE-UNTIL: YYYY-MM-DD (inclusive through end-of-day UTC); NEW integrity rule consistency:guard_overrides (HIGH, 55th rule) is OK while an override is future-dated (listed in detail), FAILS the moment the expiry passes or immediately when undated. GUARD_REGISTRY in integrity_monitor.py: SESSION_STOP_BREAKER_ENABLED (true), SESSION_STOP_BREAKER_THRESHOLD (≤10 — catches neutering), MAIN_LOOP_STALL_TIMEOUT_SEC (≤600), RULEB_MODE (known values); absent=safe default; unparseable=violation. Pure core evaluate_guard_overrides unit-tested against the literal Aug-5 override shape (correctly FAILS: undated). 17 NEW tests (947 green); OPERATIONS.md convention + memory.md §F line. Also Aug-9: Sunday IBKR 2FA rotation handled (gateway restart → operator push → reconnected, net liq $27,345); #355 commit landed (487dbac) + deployed. Prior entry — Finding #355: operator override of the session stop breaker (⚠️ RE-ENABLE PENDING) + breaker-alert display fix + COMPLETE-STATE readiness rule. Aug-5 morning: 4 of last night's 5 BMO entries stopped out (UBER/CRCL/CVS/KHC, −$83.76; DIS +$17.04 scheduled) → consecutive-loss circuit breaker (08:57:48 ET, 4h) + session stop breaker (4 stops ≥ threshold 4) both tripped; the 10:30 AMC wave was fully skipped (not recoverable). Operator explicitly ordered the block overridden for tonight's 15:58 ET BMO-for-Aug-6 wave: SESSION_STOP_BREAKER_ENABLED=false in prod .env (dated comment, .env.bak-355) + trader restart; the circuit breaker self-expired at 12:57:48 ET. ⚠️ FOLLOW-UP REQUIRED: re-enable the session stop breaker after tonight's session (remove the .env override) — it is a gauntlet-validated defense (#312/#335.b) and must not stay off silently. Also: breaker alert showed "4h pause / pause_hours: 0" (trigger path passes resume_at, alert rendered pause_hours or 0) → hours now derived from resume_at (+1 test); NEW memory.md "COMPLETE-STATE CHECKS" section after the agent answered "ready to trade" from connectivity alone while both brakes were active (operator directive: "always check everything! no shortcuts!"). Prior entry — Finding #354 SHIPPED: 15:58 ET batch freeze — hard deadlines on ALL IBKR requests + batch-prep deadline. Operator asked "is IBKR connected? why no trades Monday?" — IBKR was healthy all day (live probe; the Sunday-2FA scenario did NOT occur), but the 19:58 UTC batch of 6 BMO signals (PFE/CAT/MRK/FIS/MPC/APTV, ~$3,566) froze on symbol 3: get_quote's ib.qualifyContracts() runs the ib_insync event loop with NO timeout and blocked forever on a half-dead gateway socket. The 120s main-loop watchdog forced a restart at 20:00:03; the re-derived signals hit the (correct) market-hours gate at 16:00:47 ET — zero entries, no orders leaked. Second same-class stall that day (06:06 UTC, gateway daily auto-restart window, stage=run_scheduled_cycles). FIXES: (1) NEW IBKRClient.bounded_request(awaitable, what, timeout_sec) — every ib_insync request-response call runs under util.run(..., timeout=IBKR_REQUEST_TIMEOUT_SEC=10) (asyncio.wait_for; cancels on expiry; verified on prod ib_insync 0.9.86); timeout → None + honest ⏱️ log; callers DEGRADE (skip symbol / abort order), never hang. qualify_contract_bounded() wraps the worst offender. (2) §E sweep — ALL 15 request-response sites bounded: 7 qualifyContracts in client.py (incl. the 4 order paths — unqualifiable contract now ABORTS the order), reqExecutions (async, post-sleep removed), 2 qualifyContracts + 4 sync reqAllOpenOrders in order_executor (stop management + #352 zombie checks); tripwires assert no unbounded site returns. (3) NEW BATCH_PREP_DEADLINE_SEC=45: execute_batch_entries' prep loop stops when the deadline expires and SUBMITS WHATEVER IS PREPARED — partial entry beats zero entry; worst case per-request timeouts keep 6 symbols under it. (4) get_account_info's connection guard passed the literal "place_market_order" (copy-paste) — routine account polls during disconnects logged as blocked ORDER attempts and misled this incident's review; fixed + tripwired. DELIBERATELY unchanged: the 120s watchdog (true backstop now) and the market-hours gate that refused the post-restart retry (16:00:47 is after the close — the fix is to never lose the window, not to enter after it). 14 NEW tests (test_finding_354_ibkr_request_timeouts.py — bounded_request paths, get_quote skip-on-timeout = the literal Aug-3 site, batch-deadline matrix against the REAL execute_batch_entries with a fake clock, §E tripwires); 929 green; changelog #354. Prior entry — Finding #353 SHIPPED: History-tab Error Log window + error-ledger backfill + SBUX #349 reclassification + twin repair + 5 doc corrections (all operator-approved; accounting/display only, NO strategy-behavior change). A research pass inventoried ALL past operational error losses; operator decisions: backfill V #83 (−$8.08) + XOM #89 (−$2.84) into trading_errors; MOVE the SBUX #349 born-dead trade out of strategy stats into the ledger (−$0.78 net = −$0.06 gross + $0.7169 commissions — the documented −$0.67 was wrong); add UNQUANTIFIED placeholders (realized_loss 0.00) for the ON #178 recovery legs + PFE #109 unwind legs (never in the DB; quantifiable from IBKR Flex on demand); KEEP all false-detection exits + conviction-bug entries counted; LEAVE the VZ +$32.64 reconciliation gain where it is. Ledger now 6 rows, −$248.79 all-time. Mechanics: NEW error:% exclusion class in TRADE_METRICS_FILTER_SQL/_T (+ the strategy-analysis script's twin filter + the 2 frozen-JSON deep-dive scripts, §M); SBUX relabeled error:born_dead_schedule_349 in BOTH tables (originals preserved in the audit note). Verified stats delta: 162 → 161 closed trades, net P&L +$114.77 → +$115.55 (+$0.78), gross +$373.82 → +$373.88 (+$0.06). Twin repair: trading_positions id 45 (SBUX Jan-28) said reconciliation:not_in_ibkr/$0.00 while trade_audit_log id 11 (the stats source) says stop_loss_software_backup/−$15.07 — id 45 aligned to the audit row (STAYS counted; stats unchanged). UI: get_trade_history() now fetches the ROW-LEVEL ledger in ONE query (totals derived in Python; #352 single-FROM trading_errors tripwire green) → NEW collapsed "⚠ Error Log" <details> section on the History tab (between P&L Summary and the Progression chart; Jinja-only, all-time, fail-soft). Doc corrections: the "stats derive from trading_positions" wording was WRONG everywhere it appeared (7 surfaces — FINDINGS #352, migration 030, app.py, dashboard tooltip, tripwire comment, CONTEXT, this file): stats derive from trade_audit_log via TRADE_METRICS_FILTER_SQL; the invariant is "no stats code reads trading_errors". Also corrected: FINDINGS #178 "recorded in DB" claim (ON legs never were); #179B HOOD −11.2% was the unrealized trough (realized −$5.26 = −3.0% design). NO strategy_changelog entry (accounting/display change, not strategy behavior — noted in FINDINGS #353). 8 NEW tests (test_finding_353_error_log.py); 915 green (was 907). Prior entry — Finding #352.b SHIPPED: the #352 dedup guard made MARKETABILITY-AWARE (operator caught the flaw the same day #352 shipped). Operator asked "are we risking not being able to exit if SL hits outside regular hours?" — hardware stops are fine (STP-LMT outsideRth=True GTC), but the instant-exit falling-market scenario was NOT: pre-market miss exit → stop cancelled → SELL LIMIT at quote−1% → price keeps falling BELOW the limit → the limit sits above the market unfilled, nothing protects the position (stop deliberately not restored per #352), and the #352 guard — a dumb counter — deferred to that stale limit FOREVER, including in RTH where it no-op'd both the re-fired instant exit AND the 10:32 ET scheduled exit (pre-#352 the accidental "guarantee" was the queued MARKET filling at the open; #352 removed it without a deliberate replacement). FIX: NEW _working_exit_orders_marketable(symbol, side, tolerance_pct) (+ _get_working_exit_order_details carrying limit prices) — SELL exit LIMIT counts only if limit_price ≤ bid×(1+tol), BUY exit if ≥ ask×(1−tol), tol = NEW Config EXIT_ORDER_MARKETABILITY_TOL_PCT (default 0.002, env-tunable); quote unavailable → FAIL-SAFE defer (never churn blind); working MARKET in RTH inherently marketable, found OUTSIDE RTH (must not exist post-#352) → cancelled belt-and-braces with honest CRITICAL log. Both guards (place_instant_exit + place_scheduled_exit): marketable → defer as before (exit_orders_already_working, anti-stacking preserved); STALE → _cancel_working_exit_orders + PROCEED (fresh marketable limit at the current quote / place_guaranteed_fill's RTH limit→market ladder). Outside RTH this is a bounded ~5-min limit chase (#141 re-arm): each cycle cancels-and-replaces ONE order, never stacking; all #352 invariants intact (never MARKET outside RTH, ≤1 working order, cancel-before-restore, no stop beside the working limit — the chase is what makes that safe). 17 NEW tests (test_finding_352b_exit_marketability.py: rule matrix + tolerance edge + quote fail-safe + the operator scenario end-to-end + marketable-defer + queued-MKT-outside-RTH cancel); 2 #352 guard tests pinned marketable; 907 green (was 890); changelog #352.b. Prior entry — Finding #352 SHIPPED: VIRT −77 NAKED SHORT (worst operational loss to date, −$237.09) + working-order-aware exit engine + error-loss ledger. Operator spotted an untracked VIRT SHORT 77 @ $54.46 on the live IBKR panel. Chain (all verified): the (correct) VIRT pre-market instant exit at 10:46Z placed SELL 11 LIMIT → IBKR Error 399 "will not be placed until 09:30 ET" (a WARNING — the order QUEUES, it does not die) → 5s timeout placed SELL 11 MARKET (also queues; IBKR MKT never fills extended-hours) → verification saw 11 shares → "restore stop" but left both queued sells alive → #141's 5-min stale-clear re-armed → 8 cycles, ~15 queued sells (#199's duplicate check is POSITION-based, blind to working orders) → one filled pre-market 11:01:50 ($55.60, recorded; DB closed; trader forgot VIRT) → 13:30:00.0Z: the seven surviving MARKET orders filled in the same second @ $54.49 → naked SHORT −77, no stop, invisible to accounting → reconciler screamed CRITICAL every 5 min from 13:34 but only the operator's eyeball caught it → flattened on operator instruction 15:47Z (BUY 77 MKT @ $57.53, clientId 87, verified flat, zero working orders). Error loss −$237.09 recorded in the NEW trading_errors ledger (migration 030) — visible on the History tab "⚠ Error Losses (excluded from stats)" card, EXCLUDED from P&L/win-rate/ROI/PF/DD by construction (corrected #353: all stats derive from trade_audit_log via TRADE_METRICS_FILTER_SQL — not trading_positions as originally written; the holding invariant is "no stats code reads trading_errors", and the tripwire asserts app.py has exactly one trading_errors read). The legit VIRT LONG (−$26.73, correct miss exit per #351) stays a normal trade. FIXES (4 prevention + 1 detection): (1) working-order dedup — NEW _get_working_exit_orders (ACTIVE non-stop exit-side orders via openTrades); both exit paths refuse to stack (exit_orders_already_working, unverified → exit_manager keeps position open + waits), top up only the uncovered difference; (2) entry clamp: exit size ≤ actual signed IBKR position; already-flat → NO order + zombie exit orders AND stops cancelled; (3) NO MARKET outside RTH (extends #90C to extended hours; BOTH fallback sites) — the marketable GTC LIMIT stays working as THE exit, stop deliberately NOT restored beside it (double-exposure); (4) cancel-before-restore on RTH verification failure; (5) NEW integrity rule consistency:external_ibkr_position (HIGH, 54th rule; reads ibkr_snapshot portfolio vs DB open rows, skips when snapshot stale). Auto-flatten of external positions considered and REJECTED (reconciler false positive would close real positions) — documented in FINDINGS. Deploy hook generalized to a migrations list (029+030). Lineage honestly recorded: #58/#83 (entry dups) → #199 (GPRO exit dups, direction-aware but position-based) → #352 (working-order-aware — the piece every prior defense lacked). 17 NEW tests (test_finding_352_exit_order_stacking.py, incl. the literal 7×11 stack + both extended-hours sites); 890 green; changelog #352. Prior entry — Finding #351 SHIPPED: Hyatt false "earnings miss" exit — sparse-market crowd-witness fix + consensus-fallback fix + §F repair. Operator asked whether the two Jul-30 BMO "Instant Exit (Earnings Miss)" rows (H, VIRT) were correct. VIRT: CORRECT (Virtu pre-announced Jul-14 and delivered exactly in-line $1.82 Normalized Adjusted EPS vs $1.82 estimate → the market's beat criterion resolves NO; crowd pinned 0.0005 sustained; #341 contra path exited by design). H: FALSE DETECTION — Hyatt BEAT ($1.12 adjusted vs $0.91; crowd pinned 0.9995 from 11:04:45Z) but PRNewswire parsed the GAAP $0.38 from Hyatt's dual-format headline ("Diluted EPS was $0.38 and Adjusted Diluted EPS was $1.12") → false MISS claim, the VZ/#264 GAAP-vs-adjusted class. The #342 arbiter refused the immediate exit and started its 300s persistence clock, but its crowd witness (_live_polymarket_median) took the median of the last 5 prints REGARDLESS of time spread — on H's sparse market (~11 snapshots/hr vs VIRT's 712) the ONE fresh 0.9995 was outvoted by four 23-40-min-old pre-release 0.77-0.795 prints → "neutral 0.79" → the veto that was factually earned 3 minutes before the clock expired never fired → false MISS admitted at 11:07:47, exit filled 11:09:56 (−$18.63; H sold the news anyway, so P&L impact vs the scheduled exit is likely small — the poisoned labels were the real damage). SECONDARY BUG in the same log: exit_manager/orchestrator periodic checks pass the in-memory position's consensus_eps, which loads 0.0 when the DB row was NULL (open-persist never writes it) → _determine_outcome($0.38, $0)=BEAT flip-flop ("beat admitted (informational)" vs the monitor loop's MISS) — harmless for LONGs but fabricates contra BEAT claims for SHORTs. FIXES: (1) NEW pure select_fresh_median — crowd median counts only prints ≤CONTENT_CROWD_MAX_AGE_SEC (900s), requires ≥CONTENT_CROWD_MIN_FRESH_PRINTS (NEW knob, default 3); (2) NEW _live_gamma_yes_price fallback when the DB stream is too sparse (the #325 bake-off's best source; ACCEPTS resolved 0/1 prints unlike the #333 conviction path — resolution IS the signal here); with these, H would have been VETOED at 11:04:45; (3) check_position_for_exit recovers zero/None consensus from the monitored registry (#339-recovered); (4) §F repair (both tables, #344 taxonomy): H rows → instant_exit:false_detection, outcome beat, actual_eps 1.12, prediction flags corrected, originals in audit note; earnings_markets.actual_beat was never touched (#342 held) and the #344 exit_label_vs_resolution rule would have self-detected on resolution. Deliberately NOT changed: _check_polymarket_price's own median (its sparse-market failure mode is a MISSED exit — fail-safe; widening without replay evidence = #330-class risk). 12 NEW tests (test_finding_351_sparse_crowd_witness.py, incl. the literal H print pattern); 873 green; changelog #351 (execution). Prior entry — Finding #350 SHIPPED: Polygon plan-downgrade incident + #348 root-cause correction. The Polygon/Massive subscription silently dropped to the FREE tier (5 req/min) between Jul-27 10:07 and Jul-28 ~10:00 UTC: etfstack-signal.service hard-failed (exit 3 — SPY 429 after 3 retries; its (2,5,12)s ≈ 19s backoff never escaped the 60s quota window) and the whole account throttled (371 resolver + 52 equity-bars 429s on Jul-29). Empirical proof: 10-request probe = exactly 5× 200 then 429 "upgrade your subscription". #348's root cause was CORRECTED — the schedule collision was real but latent-and-harmless for months (Jul-26/27: zero 429s); the plan downgrade was the trigger (correction note in FINDINGS #348). Operator restored the subscription (verified 12/12 rapid 200s). Hardening shipped (operator points 2+3): (1) NEW src/utils/polygon_rate_limiter.py — cross-process flock'd token bucket (POLYGON_MAX_RPS default 10 combined, POLYGON_BURST 10, fail-open ALWAYS) wired into all scheduled/live consumers: counterfactual_pricing shared fetch (both resolvers + equity-bars + compute), etfstack data_provider (guarded import, subtree stays standalone), resolver financials, dashboard trade-chart ×2, caching.py mcap, weekly strategy-analysis; manual one-off scripts + dormant resolution_sniper deliberately NOT wired. (2) Plan-limit 429 bodies (classify_quota_downgrade) write logs/polygon_quota_downgrade.json; NEW integrity rule consistency:polygon_plan_downgrade (HIGH, 53rd rule) alarms BY NAME; flag self-clears on next successful request. (3) etfstack backoff (2,5,12)→(15,30,65)s — escapes an exhausted minute. (4) etfstack live run_daily now passes allow_etf_yfinance_fallback=True (operator point 3): signal continuity > single-source purity on the paper account; WARNING-logged, whole-series fallback, no splicing. NEW tests/unit/test_finding_350_polygon_limiter.py (21 tests: bucket math, exact-prod-body classifier, flag round-trip, integrity evaluator, tripwires incl. wired-consumers list). Jul-29 etfstack signal row is missing (release passed) — the Jul-30 21:03 UTC run generates fresh; paper account held risk_off regardless. Prior entry — Finding #349 SHIPPED: earnings-date consensus verification + admission guards (SBUX instant-exit war fix). SBUX was entered Jul-28 19:58:11 UTC and closed 20 SECONDS later labeled "Scheduled Exit" (−$0.67 — corrected #353: −$0.06 gross / −$0.78 net; since #353 an error-ledger entry, not a strategy trade): the Feb-9 single-source verifier (NASDAQ, which was RIGHT: Jul-29) and the hourly Polymarket-derived sync (WRONG: Jul-28, from the market's own end_time/slug) fought over earnings_markets.earnings_date for two days — the verifier corrected Jul-28→Jul-29 FOUR times (incl. 19:50:02 Jul-28, seconds before the entry wave), the sync clobbered it back every hour. The trade was SELECTED via the corrected Jul-29 date but SCHEDULED from the wrong Jul-28 end_time (_get_signals_for_timing derived earnings_dt from end_time) → scheduled exit 10:32 ET Jul-28 = 5.5h in the PAST at entry → exit manager sold on its next tick; the intended Jul-29 AMC trade was missed. Fixes (Layer 1 root cause + Layer 2 guards, all shipped together): (1) multi-source consensus verifier (NEW earnings_date_verifier.py + rewritten verify_earnings_dates): DB row + NASDAQ + Finnhub (+ yfinance tiebreak) each get one vote; unique ≥2-vote winner ⇒ date_confidence='confirmed' (+ timing corrected on consensus, amending #220); no majority ⇒ 'disputed'; single-source auto-correct REMOVED. (2) Confirmed dates LOCKED against all 3 Polymarket-derived writers (fetcher/scraper/earnings_sync CASE guards — this ends the war). (3) NEW earnings_date_audit + 2 integrity rules: consistency:earnings_dates_disputed (WARN) + consistency:earnings_date_churn (HIGH — ≥3 changes/7d = writer fighting the verifier). (4) Layer-2 admission guards in signal_generator: disputed rows skip (earnings_date_disputed); unconfirmed date-vs-end_time mismatch skips (earnings_schedule_mismatch); confirmed mismatch trades on the VERIFIED day; entry-time invariant refuses any signal whose computed exit is <30 min in the future (scheduled_exit_not_future). (5) Dashboard ⚠ DATE? badge on disputed rows (Jinja + mobile JS + API, §M). (6) Migration 029 (date_confidence, date_verified_at, audit table); §F: SBUX corrected to Jul-29 AMC confirmed per official IR. 26 NEW tests (test_finding_349_earnings_date_war.py; 840 green — 5 older fixtures updated whose stubbed "exit=now" the new invariant correctly refuses). Prior entry — Finding #348 SHIPPED: Polygon 429-storm fix. equity-bars collector failed (30/35 jobs 429'd, first failure since ≥Jun 1): its fixed 10:00 UTC slot collided with the daily resolver's 06:00 ET (= 10:00 UTC in EDT) on the shared Polygon key at 20 req/s each; no retry/backoff existed; 429s were mislabeled "likely delisted/OTC". Fixes (operator-approved Tier A): (1) fetch_polygon_minute_bars retries 429/5xx with 2s/8s/30s backoff + NEW raise_on_transient/PolygonTransientError (§M — all consumers inherit; legacy default unchanged); (2) collector classifies honestly (delisted only on clean-200-zero-bars; transient= counted/reported separately; >10% threshold still exits 2); (3) timer 10:00 → 11:35 UTC; (4) --polygon-sleep 0.05 → 0.25 in equity-bars + both resolver units. 5 NEW tests (815 total green); rerun backfilled the 30 missed jobs; integrity green. Tier B (cross-process token-bucket limiter) explicitly deferred unless more Polygon consumers are added. Same day — Finding #347 SHIPPED: 150-trade TESTING_PROD auto-pause removed (operator passed 150 DB trades and chose to continue; the pause was latent anyway — in-memory counter reset on every restart; guard test added). Jul 22 — Finding #346: BMO-reservation override threshold was 0.90 on the LIVE orchestrator path (#333 lockstep missed the twin) → 0.95 + twin tripwire test. Jul 22 — Finding #345: backup service wedged 7 days (cleanup ran AFTER pg_dump → disk-full death spiral, 27 GB stale local backups on a 48 GB disk) → cleanup-first + 2-day local retention; Spaces off-site remains operator-choice-absent. Prior entry — Finding #344 SHIPPED: honest exit labels — operator-caught discipline failure in the #342 repair. The #342 §F repair deliberately kept PNC's instant_exit:earnings_miss label ("historical fact of why we exited" + audit note) — WRONG call: notes never enter SQL aggregations, so every "how many PM predictions missed" query would count the parse-bug exit as a real miss, violating the #328 (KR) honest-labels precedent; operator: "we also dealt with this before and you did not fix it... no shortcuts ever." Full §E audit of ALL 13 outcome-labeled exits, each cross-checked against resolver actual_beat AND the market's own RESOLVED price (last snapshot ≥12h post-earnings — actual_beat itself was detector-writable pre-#342) AND external EPS where ambiguous: (1) 5 FALSE-DETECTION exits relabeled instant_exit:false_detection in BOTH tables with audit notes preserving the original label — ON Feb-10 (PRNewswire $0.06 vs $0.64, #264 class), ZM Feb-25 (beat-exit 10:59 ET = hours PRE-release AMC), IONQ Feb-25 (miss-exit 15:51 ET pre-release; IONQ actually beat −$0.20 vs −$0.48 adj), VZ Apr-27 (#264 Yahoo GAAP), PNC Jul-15 (#342 dividend parse); 6 genuine outcome exits confirmed unchanged (TXN/COP/AMZN/MAR/SNOW/KR). (2) 2 poisoned actual_beat corrected to the market's RESOLVED outcome (the market's criterion is what we trade): AXP Jan-30 True→False (resolved NO 0.0005; resolver's EPS-tie 3.53>=3.53 heuristic said beat), XOM Jan-30 False→True (resolved YES 0.9995; GAAP 1.54-vs-1.69 said miss). (3) Prediction flags corrected on 6 rows in both tables (ON/ZM/IONQ/VZ → correct; AXP/XOM → wrong) — resolver Pass 4 only fills NULL flags, stale values never self-heal. (4) NEW self-detecting integrity rule consistency:exit_label_vs_resolution (HIGH): FAILS whenever an earnings_miss/beat exit label contradicts the market's resolution, naming offenders — the class no longer waits for an operator to eyeball a history row. Taxonomy: instant_exit:false_detection renders "Instant Exit (False Detection)" via the existing generic branch; excluded from %stop% and %earnings_miss% predicates, still counted as an instant exit (honest). 4 NEW tests (807 green); rule verified OK on prod post-repair. Prior entry — Finding #343 SHIPPED: intraday outcome-resolver cadence + display-only crowd read (completes #342's single-writer model). Operator caught JNJ/CTAS beats (PM pinned 0.9995, aligned with our LONGs) showing NO dashboard badge. Root cause (3 stacked): (1) #341 silenced same-side PM detections BY DESIGN — they were the position-row badge feed AND the actual_beat poison class; (2) PRNewswire chronically timing out + Yahoo slow → no content detection either; (3) the EPS resolver — #342's SOLE actual_beat writer — ran once daily at 06:00 ET, BEFORE same-day BMO releases (Jul-15: ran 06:01, JNJ released ~06:45) → same-day outcomes settled ~24h late. Fix A: NEW polymarket-resolver-intraday.{service,timer} light pass (--earnings-limit 50 --skipped-limit 0 --opportunity-limit 0 --lookback-days 3, 10-min timeout) on 06..10:15,45:00 + 16..18:15,45:00 America/New_York (:15/:45 offsets clear the 06:00 daily full pass which remains the completeness backstop; calendar strings systemd-analyze-validated on prod; wired into deploy_from_github.sh). Fix B: positions API computes pm_outcome_display/pm_outcome_yes_price for open positions with NULL earnings_outcome via the SAME _compute_outcome_status helper the Upcoming table uses (§M single definition — fresh ≤12h post-release PM extreme ≥0.99/≤0.01); rendered in Jinja desktop + JS mobile as "📊 BEAT/MISS · PM" with an explicit crowd-read-not-settled-truth tooltip. NOT done: re-enabling same-side detection writes (would reopen #337/#341 corruption). Side note: JNJ stopped out 9:03 ET (−$15.22, stop_loss:hardware) on a correct BEAT — sell-the-news, same as JPM Jul-14; flags correct. Prior entry — Finding #342 SHIPPED: UNIFIED EXIT ARBITER — no detection source may exit a position uncorroborated, settle actual_beat, or stop monitoring. Trigger: PNC beat clearly ($4.81/$4.85 adj vs $4.41) but PRNewswire's EPS parser extracted the DIVIDEND ("Increased quarterly common stock dividend... to $2.00 per share") as EPS → $2.00 vs $4.41 = false MISS → instant exit of a CORRECT LONG at 6:34:31 ET (−$11.14, ~$14-15 vs holding; PNC pre-market ~$253 an hour later) while the live crowd median sat at 0.91 BEAT — then wrote actual_beat=False and stopped monitoring. Root cause is the AUTHORITY MODEL: check_earnings was first-to-detect-wins, giving whichever source spoke first unilateral power (same defect as ON $0.06-parse, VZ #264 GAAP scrape, JPM #337 pre-release pin, WFC #341 pins — one bug in five costumes). Operator directive: "single integrated mechanism... bulletproof," whole package in one. SHIPPED: (1) _arbitrate_content_claim — every content-source (PR/BW/Yahoo) CONTRA claim is cross-examined against the live PM crowd median (_live_polymarket_median, median-of-5, fresh ≤900s): agrees (≤0.25/≥0.90) → admit immediately (two witnesses, FASTER than the 5-min PM-only path); neutral/stale → 2nd independent source or 300s persistence (CONTENT_EXIT_CORROBORATION_SEC) required; contradicts (≥0.90 vs MISS claim / ≤0.10 vs BEAT) → ARBITER VETO + persistence-clock reset (a recurring bad parse can never time in). Same-side claims informational. Feb-10 _confirmed_polymarket sticky cache + #264 (a)/(b) shields replaced by the arbiter. PM source unchanged from #341. (2) NO detection signal writes earnings_markets.actual_beat — removed from BOTH writers (trading_orchestrator + position_manager _persist_earnings_detection, §E); the EPS resolver (#227) is the sole settler. (3) Monitoring is immortal to signals: _check_all_positions never removes; callbacks dedup per outcome (last_reported_outcome, survives the reconciler's 5-min re-registrations + restarts via already_reported_outcome seed) and RE-FIRE on flips; BOTH reconciler registration sites now register even when an outcome exists (closes the #337 restart gap that left JPM unprotected). (4) _extract_eps_from_text rebuilt 2-tier: number-first "$4.81 Diluted EPS" parses; bare "$X per share" excluded within 80 chars of dividend/buyback/repurchase/book-value/offering context; negatives (−$0.25 / ($0.12)) parse. 4 new Config knobs. NEW tests/unit/test_finding_342_exit_arbiter.py (22 tests: PNC/VZ/ON incident replays, arbiter matrix, monitoring lifecycle, parser corpus, SET-actual_beat source tripwire); 2 #341 tests re-pinned; 803 unit tests green. §F: PNC rows repaired (actual_beat False→True, actual_eps 2.00→4.81, outcome miss→beat, prediction flags corrected; exit_reason string kept as historical fact + note). Monitor: first live ARBITER VETO/WAIT/admitted lines; actual_beat stays NULL until the resolver runs. Prior entry — Finding #341 SHIPPED: Polymarket price detection becomes EXIT-ONLY (contra-position signals, 5-min sustained confirmation + 60s grace, never settles the record). The pre-deploy acceptance replay of #339 (scripts/detection_latency_replay.py --truth market --positions, all 13 resolved markets with ≥200 prints incl. the WFC/JPM day) found: (a) 30s confirmation lets 0.1-4 min post-release whipsaws (IMAX/OKTA class) exit CORRECT positions — 3 false exits; (b) same-side "resolution" pins are NOT truth — WFC pinned ≥0.98 for ~1.5h before flipping to its real MISS, 5 more markets did the same; under the pre-#341 design those settled actual_beat + STOPPED monitoring (the #337 poisoning class) from signals that exit nothing. Shipped: _check_polymarket_price emits ONLY contra-position signals (LONG→MISS side / SHORT→BEAT side, both grades; nothing without a direction — live callers all pass it); every PM signal requires 300s SUSTAINED consensus (POLYMARKET_DECISION_CONFIRMATION_SEC=300) with 60s grace for brief median pops (POLYMARKET_CONFIRMATION_GRACE_SEC=60 — without grace, print noise kept resetting the clock and median save latency ballooned 22m→73m); PM signals carry EarningsResult.grade ('resolution'/'decision'), never write earnings_markets.actual_beat (orchestrator gates on source — EPS resolver #227 owns ground truth), never remove the position from monitoring (the instant-exit flow does that on close), and never arm _confirmed_polymarket — the PR/BW/Yahoo contradiction shield now lazily uses _live_polymarket_extreme_outcome (self-correcting after a crowd flip). Replay scorecard: saves 2→4, false exits 3→2 (both remaining had the crowd decisively wrong 5+ sustained minutes — now cost only an early exit, not corrupted truth), median save latency +22m from crowd flip (vs +104m pre-#339). 19 detector tests re-pinned to contra-only semantics (4 NEW: same-side pin suppressed / no-direction emits nothing / grace clock / PM keeps monitoring); 781 unit tests green. Prior entry — Finding #340 SHIPPED: #339 blast-radius cleanup + structural prevention. The 2 failing integrity rules (conviction_at_exit_post_fix 6/23 NULL, hold_duration_post_fix 3/23) were the #263-era detection net firing exactly as designed on #339's fallback-path closes (5× Jul-14 + VZ Apr-27). Fixes: (1) §F data repair — conviction_at_exit backfilled point-in-time on all 6 rows (median ±30min of exit; last-60min-pre-exit median for WFC/GS/BAC whose PM markets closed at 9:00 ET before their exits: VZ 0.9995 / JPM 0.8400 / C 0.9995 / WFC 0.9870 / GS 0.9400 / BAC 0.9490); (2) fallback close-persist WIDENED — now carries exit_commission, total_commission (numeric-cast), conviction_at_exit, hold_duration_hours, prediction_correct (previously dropped despite being in-scope; only margin-ledger aggregates remain primary-only); (3) NEW scripts/close_persist_contract_check.py + deploy hook — runs the REAL PositionManager close-persist end-to-end against prod PG on every deploy (synthetic __SQLCHK__ row, fractional commission = the #339 22P02 trigger shape, asserts primary-path success via reconciliation_status, deletes residue) so the #263/#339 class of PG-type-resolution bugs is caught at deploy time instead of on the first live close. NEW test_position_manager_fallback.py (3 tests, 795 pass). Prior entry — Finding #339 SHIPPED: WFC miss-detection lag (48 min → stop bleed) + Jul-14 fee-capture loss — 3 compounding bugs fixed. WFC: release ~7:00 ET, PM collapsed 0.99→~0.12 at 8:39 ET (decisive MISS) but NO detection → hardware stop 9:27 (-$20.58, ~$7-10 avoidable). Bugs: (1) #336's all-of-5-prints unsatisfiable on real mixed bid/ask/trade streams → decision statistic now MEDIAN of last 5; (2) resolution thresholds 0.98/0.02 unreachable on a dying book → NEW decision-grade thresholds post-typical-release (BMO 7:00 ET / AMC 16:05 ET; median ≤0.25 MISS / ≥0.90 BEAT; env-tunable; #337 gate + 30s confirmation still in front; same-direction signals benign for held positions); (3) consensus_eps silently $0.00 on 4/5 positions (yfinance calendar failed, no DB fallback; _determine_outcome(actual, 0) = Yahoo could never exit a LONG) → add_monitored_position now falls back to earnings_markets.eps_estimate. FEES: COALESCE(:param, 0) bare-0 typed the param INTEGER → first fractional Tiered commission ($0.3555) crashed the primary close persist (22P02) → fallback path dropped commissions/gross_pnl/hold_duration; fixed with 0::numeric + CAST in BOTH writers (position_manager + orchestrator reconciliation, §E); §F backfill of JPM/WFC rows from logged commissionReport values (0.3555/0.3652). JPM was NOT a bug: real BEAT detected 6:56 ET, prediction correct, 7:07 stop = sell-the-news (by design). PRNewswire RSS chronic timeouts noted, not actioned. 6 NEW incident-replay tests. Prior entry — Finding #338 SHIPPED: canonical chronological Strategy Changelog (operator-directed). Operator: July's changes were scattered / "extremely difficult to follow chronologically" / "use a standardized format... also in the future". NEW src/earnings/strategy_changelog.py = single source of truth (26 entries launch→#337; schema: date/finding/category/title/change/why/expected_impact/verification/monitor/status/superseded_by/marker_label; supersession chains encoded) → feeds the NEW Strategy-tab 📜 Strategy Evolution timeline (top of tab, newest-first, month-grouped, status filters) AND app.py's History-chart markers (hardcoded list replaced by get_history_chart_markers(), verified byte-identical to the prior 7 markers incl. the Jul-6 merged-label convention). Enforced by NEW tests/unit/test_strategy_changelog.py (9 tests incl. content-depth minimums + marker continuity) + NEW memory.md §O (mandatory same-commit entry for every strategy change) + STRATEGY_ANALYSIS_ARCHITECTURE.md §0.14. Also fixed in-audit: stale phase-status banner (Last shipped: May 28 → points at the changelog). Prior entry — Finding #337 SHIPPED: release-window gate on the price-based earnings detector (JPM false-BEAT incident). JPM's Polymarket book spiked to 0.997 for ~3 minutes at 8:26 PM ET Jul-13 (~10.5h before its BMO release; 6,974 prints ≥0.98 overnight) — sustained enough to LEGITIMATELY pass #336's all-of-5-prints check + the 30s confirmation → detector confirmed BEAT, wrote earnings_markets.actual_beat=True + trading_positions.earnings_outcome='beat', stopped monitoring JPM; after 2 overnight trader crashes (asyncio "cannot enter context" on IBKR reconnect ~04:14Z — pre-existing, needs own RCA, systemd auto-recovered) the reconciler re-registered only outcome-less positions → JPM left WITHOUT instant-exit protection into its real release. FIX: _release_window_open gates _check_polymarket_price — BMO window opens 4:00 AM ET on the earnings date (preserves KR 6:52 AM early-release class), AMC 3:30 PM ET, derived from earnings_markets (cached 10 min), FAIL-OPEN on gaps; content-based sources (PR/BW/Yahoo) deliberately ungated. Neither signal.earnings_datetime (BMO = midnight-UTC = 8 PM ET prior evening — would NOT have blocked this) nor MonitoredPosition.expected_release_time (reconciler degrades it to now() on restart) was usable as the gate. DB §F-corrected (actual_beat NULL + position detection fields NULL, 1+1 rows, audit event kept) → JPM re-registers on the deploy restart. Flagged not actioned: asyncio reconnect crash RCA; reconciler's expected_release_time=now(); consensus_eps=$0.00 at registration. 4 NEW tests (779 pass). Prior entry — Finding #336 SHIPPED: false pre-event "BEAT - PM" outcome badges + earnings-detector single-print hardening (eve of the Jul-14 bank wave). Operator caught JPM/GS badged BEAT the night before earnings while conviction showed 93-94%. Root cause: price_snapshots thin-book noise (JPM 621/11,137 prints ≥0.99 in 12h, ask-side prints interleaved with 0.75 bid-side in the same second) + both _compute_outcome_status (#280) and the trader detector's PM reads (#264) trusting the SINGLE latest print. Entry path unaffected (no outcome input; median conviction robust) — the banks trade normally tonight. Latent trader risk closed: a noise-sustained false BEAT would have poisoned _confirmed_polymarket (blocks real MISS detection) and PERSISTED wrong earnings_markets.actual_beat via _persist_earnings_detection. Fixes: dashboard PM-extreme branch gated on _is_past_release_time; _check_polymarket_price + _live_polymarket_extreme_outcome now require ALL of the last POLYMARKET_EXTREME_MIN_PRINTS=5 prints at the extreme (env-tunable; 30s confirmation window unchanged on top). NEW tests/unit/test_outcome_status.py + 4 detector tests incl. the exact JPM print pattern (774 pass). Data-quality note flagged, not actioned: the snapshot stream mixes bid/ask/trade prints within the same second. Prior entry — Finding #335 SHIPPED: busy-week capital bundle (operator-directed, ahead of the Jul-14 bank-earnings wave — busiest week on record expected: 7-14 entries vs ~2/wk avg). (1) Exposure cap MAX_DAILY_EXPOSURE 1.40→1.50: flat-25% sizing (#314.f) fit only 5 concurrent positions (125%) under 1.40 — 6th always rejected at 150%>140%, stranding ~$406/night; 1.50 fits exactly 6 (targeted granularity fix; #315's declined 2.0 raise was a different, broad question; margin-safe vs enforced 2.0× Reg-T gate ≈$5.4k). (2) ADMISSION freed-capital fix: can_open_position(capital_freed_from_exits=) nets out about-to-exit positions (10:32 BMO exits before 10:34 AMC entry; 15:57 AMC exits before 15:58 BMO entry) — pre-fix a full overnight book auto-rejected EVERY next-morning AMC signal (Jan-28-2026 fix covered batch SIZING only, not admission; would have silently killed Wed UAL / Thu NFLX after 5-position nights). Wired: orchestrator check_amc (300s helper) + check_bmo (600s helper) + capital_manager lockstep twins. (3) BMO-reservation admission re-based: get_actual_available_capital returned RAW IBKR day-trade buying power (~4×net_liq ≈$110k on $2.7k trading capital) minus reservation → never bound; now returns the strategy budget (MAX_DAILY_EXPOSURE × total_capital − effective exposure, freed-aware) bounded above by BP; dashboard available_capital now shows ~$4k not $110k. Also: build_phase3_sim_capacity cap resolved from Config at build time (was hardcoded 1.40); replay_exposure_cap_sweep DEFAULT_CAPS += 1.50 with LIVE marker; stale 30/22/15 + "70% exposure" + 2/3/4%-stop docstrings in risk_manager corrected; STRATEGY.md ×10; Action Queue row busy-week-capital-335; tests re-pinned + NEW TestBusyWeekCapitalFixesFinding335 (764 pass). SESSION BREAKER (#312) — threshold RAISED 3→4 (#335.b, operator choice): counterfactual on all 9 historical ≥3-stop days — only 4 would-be-blocked entries, net −$15.78 (breaker SAVED money; 7/9 days the 3rd stop fired post-20:00Z blocking nothing); agent recommended keep-as-is, operator chose 4 for busy-week correlated-stop tolerance (SESSION_STOP_BREAKER_THRESHOLD=4; skip-reason string session_3_stop_breaker kept for analytics continuity; only 4/9 historical breaker days reached 4+ stops). NOTE: TESTING_PROD 150-trade pause counter is in-memory only (resets each restart; currently 0/150) — never fires as documented; flagged, not fixed. Prior entry — Finding #334 SHIPPED: priority re-tiered 0-10 for the reopened 0.90-0.95 band (operator-directed; completes #333, which had MISSED priority.py — a 0.92 LONG would have passed the relaxed config gate but scored 0 and been silently dropped at priority_filter). New base table: Large 70-80/80-90/90-95 = 8/7/6 · Mid = 5/4/3 · Small 70-80 = 2 (90-95 LOWEST per mcap group; small caps stay 70-80 LONGs only; SHORT miss-conf ≥90% stays SKIP) + #313 volume bump → 0-10 scale (MAX_PRIORITY_SCORE=10, new LONG_MAX_CONVICTION=0.95). All consumers audited (orchestrator /6 + JS mobile /6 leftovers fixed); dashboard legend rebuilt (+2 NEW rows, all /8→/10, stale "LONG 90%+"/"70-89%" texts fixed); STRATEGY.md §3 rewrite; Action Queue + FINDINGS + PLAYBOOK + CONTEXT; tests re-pinned + new #334 band test. §M lesson: the LONG cap is encoded in BOTH Config.SIGNAL_LONG_MAX_THRESHOLD AND priority.py — change together. Prior entry — Finding #333 part 2 SHIPPED: #202 LONG cap RELAXED ≥0.90 → ≥0.95 (operator-confirmed outright relax). SIGNAL_LONG_MAX_THRESHOLD default + prod .env → 0.95; §M lockstep: is_high_conviction, simulator DirectionPolicySpec (prob_threshold_70_30_with_95_skip), capital_manager._bmo_reservation_threshold 0.90→0.95 (would otherwise have been silently overridden by every reopened-band signal; this threshold IS the mechanism behind the "AMC needs 90%+ if BMO tomorrow" rule), dashboard app.py+Jinja+JS gating (badges ✗95+/70-94%; JS stale $2k fallback → $1k per #311), Live Config Direction row, History P&L marker, STRATEGY.md ×8, Action Queue rows (relax-202-9095 IMPLEMENTED), 2 guard tests (741 pass). ≥0.95 skip REMAINS. Monitor: first ~20 real fills in the 0.90-0.95 band vs +$6.19/trade counterfactual; re-gauntlet on REAL fills at trade #150 or 20 band-fills. Prior entry — Finding #333 exclusion-funnel coverage leaks FIXED + gap-aware 0.90-0.95 band replay (relax decision PENDING-OPERATOR). Leak fixes shipped: (1) get_earnings_markets end_time::date OR-window with ±1-day guard vs earnings_date (PXLW channel-window hole — stale earnings dates made markets invisible to both channels); (2) NEW ConvictionCalculator._get_conviction_from_gamma_live last-resort fallback, ≤24h-target-gated to keep point-in-time replays look-ahead-free (GME Jun-9 $22.8k conviction_unavailable class); (3) timing PRIORITY 4.5 — infer BMO/AMC from market end_time hour ≤14Z/≥20Z before the web fallback (GD/UBSI timing_unknown class). NEW scripts/analyze_90_95_bucket.py (gap-aware stops per #330 + Fixed/Tiered commissions + flat-25% sizing): 0.90-0.95 band n=78 → +$482.94 net, ex-top-2 +$135.39 PASSES pooled (sub-slices outlier-carried — large/mid/pre-May fail ex-top-2; ~2/3 of convictions pre-date the #325/#327 estimator fixes; no slippage; instant-exit rescue unmodeled = conservative); 0.95+ n=34 → −$6, ex-top-2 −$228 → KEEP skipping. Action Queue row relax-202-9095 = PENDING-OPERATOR (relax cap to 0.95 / half-size 12.5% pilot until ~20 real fills / keep + re-run at trade #150). 8 NEW tests, 741 pass. OPEN operator decisions: relax-202-9095 + #314.e SKIP rules at 30+ trades. Prior entry — Finding #314.f SHIPPED: FLAT 25/25/25 sizing (operator-confirmed real-money change; was 30/22/15 per #211) — basis +$7.40/wk gauntlet-PASS ×2 at the live 1.40 cap; lockstep config + simulator rules.py (jul6_flat25 + production_strategy_jul6()) + dashboard + STRATEGY.md + memory.md + 3 guard tests; monitor small-cap stop-outs (25% in the 5%-stop tier) + re-validate at trade #150+. Same day — Finding #332 Tiered-commission alignment audit + History-tab P&L upgrade. Operator switched IBKR Fixed → Tiered (change processes by Friday per IBKR); no-shortcuts audit of the commission chain fixed: exit-commission INT-ROUNDING before persist (would corrupt Tiered legs $0.42→$0 — the critical one), $0.00-commission falsy checks in client.py, gross_pnl writer inconsistency (+97-row DB correction; realized_pnl IS gross price P&L). Verified aligned: fill-summed commissionReport capture (partial-fill-correct), #329 Flex weekly reconcile, fee_calculator (already models Tiered), #331 net displays, PROJECTIONS $1/side (frozen benchmark). History tab: NEW Gross P&L + Commissions Paid cards + launch-to-date cumulative gross/NET progression chart (LightweightCharts) with strategy-change markers (app.py:STRATEGY_CHANGE_MARKERS, sync with PLAYBOOK §2) + per-rule-era performance table. OPEN operator decisions: #314.f sizing-up variant (flat 25/25/25 +$7.40/wk vs 35/25/18 +$6.08/wk vs KEEP — both gauntlet-PASS). Prior entry — Finding #330 RuleB FAILED-GAUNTLET: the simulator's window arm was gap-fill OPTIMISTIC → gap-aware fix flips RuleB from +$224/100 to −$209/100; Phase-2 active flip CANCELLED (NO live trading change). Operator-directed full strategy review found simulate_window_only filled window-blocked stops at the STOP PRICE even when the price was still through the stop at rearm — unobtainable under the as-shipped #305 cancel-and-rearm mechanism (re-placed STP-LMT triggers instantly, fills near market; = FA_hard's rearm_gap_open, the arm that retracted FA_hard in #228). Gap-aware fix (first post-window bar OPEN fill; signature unchanged so the phase3_simulator reference-adapter contract holds; RuleG inherits) + prod re-run n=129: RuleB −$209/100 headline / −$194/100 ex-top-2 / −$227/100 ex-repeat — FAIL every §K check (optimism bias ≈ $535); RuleG −$30/100. Live corroboration: all 4 June in-window stops (LULU/ADBE/ACN/KR) were CORRECT stops — RuleB-active would have worsened each (ACN −$26 actual vs ~−$134 held). NO stop-loss rule variant currently survives the gauntlet. Consequences: Action Queue Sim RuleB → FAILED-GAUNTLET; v3 D/E RuleB dependencies REMOVED (pure selection rules at trade #150+ / SHORT n≥30); #323.b "0.92-0.99 conditional on RuleB" lever dead; May-28 PR-1 live_baseline_gauntlet PASS/SHIP marked SUPERSEDED. Phase-1 dry-run stays deployed (logging only) + its log spam fixed (8,026 duplicate lines → once per position). 6 NEW unit tests. Same review also produced the ranked improvement list: (1) idle-cash yield on the $25k PDT reserve — operator: reserve becomes trading capital post-testing, NO action; (2) IBKR Fixed→Tiered commission plan (~55% of gross P&L currently goes to commissions at test-size positions; 106/127 legs = the $1 Fixed minimum) — informational, operator decides; (4) capacity-aware sizing re-run for the parked #314.c candidates — DONE (Finding #314.f: flat 25/25/25 Δ+$7.40/wk + 35/25/18 Δ+$6.08/wk BOTH gauntlet-PASS at the live 1.40 cap on 578 signals/39wks incl. June; STRENGTHENED vs May 31; Action Queue row sizing-up-2026-07 = PENDING-OPERATOR-APPROVAL — historical: resolved Jul 6, 2026, #314.f shipped flat 25/25/25; later E-001 contractual PASS with S2c as decided successor per #360); (5) LONG 0.85-0.90 skip candidate (n=31, −$92 net, ex-top-2 −$210 §K-robust) — WAIT for fresh post-#327 data per operator; (6) P&L display fix (net-of-commissions) — SHIPPED same session as Finding #331 (all Total P&L displays net via shared PNL_EFFECTIVE_SQL definition; Strategy-tab analysis stays gross with a NET companion line; labels/tooltips renamed). Remaining open items from the review: Fixed→Tiered commission plan (operator decides, account setting), idle-cash yield (operator declined — reserve becomes trading capital post-testing), LONG ≥0.85 skip candidate (WAIT for fresh post-#327 data), sizing-up ship decision (#314.f PENDING-OPERATOR-APPROVAL — historical: resolved Jul 6, 2026, flat 25/25/25 shipped). Audit-process lesson #7 (STRATEGY_AUDIT_v3 §16): a TRUE simulator must model the REARM path of any cancel-and-rearm rule, not just the silence. Prior entry — Finding #329 Commission-capture completeness + IBKR Flex ground-truth reconcile (data-integrity + ops). Follow-up to the P&L accounting RCA. The live commissionReport path (ibkr_executions source=live/live_stop_fill, 205 rows) IS ground truth for most trades, but the IBKR_FLEX_TOKEN had EXPIRED (err 1012) — Flex ran once at setup (Jan 7-29) then lapsed and was never scheduled. NULL-commission trades (31 exit + 5 entry) = early no-exec trades + commission-less recovered fills (log_backfill/manual_recovery). statement_csv rows store commission NEGATIVE (sign convention; 2 XOM dupes) → backfill uses ABS(). Fixes: (1) backfilled the 7 recoverable NULL sides (+$7) + recomputed total_commission (staleness $206.37→$229.44); (2) refreshed Flex token (→2027-06-06) + new Activity Flex Query id 1563851 (Trades, Last 365 Calendar Days, XML); (3) NEW weekly FAIL-SOFT polymarket-flex-reconcile.{service,timer} (Sun 13:30 UTC) + scripts/run_flex_reconcile.sh → pulls official IBKR statement, gap-fills exact-charged commissions (ABS), never cry-wolfs (#297/#326). Full-history backfill PENDING IBKR new-query propagation (transient 1001); detached retry probe + weekly timer complete it. P&L DISPLAY fix (Total P&L omits commissions) is separate + pending operator go-ahead. Prior entry — Finding #328 KR exit MISLABELED "Scheduled Exit" → crash + honest reconciliation labels + durable crash logs FIXED (PRODUCTION FIX). Operator: KR recorded as a Scheduled Exit but exited 6:52 AM ET (scheduled 10:32) — RCA whether scheduled or stop. It was NEITHER: KR reported a pre-market earnings MISS → the detector fired an instant_exit:earnings_miss (SELL 1122823 filled 9@$60.02, 10:52:23 UTC, −$16.83); 13 s later the trader crashed persisting the close → DB never updated → orphaned → on restart reconciliation recovered the fill but blanket-labeled it scheduled_exit:reconciled. Bug 1 (root cause = the recurring exit 1): _persist_position_close_with_exit_details used self.position_manager; the real attribute is self.position_mgr → AttributeError on EVERY instant-exit persist (fixed). Why CI missed it: test_close_path_routing built its mock with the same wrong name position_manager, passing against the bug → corrected to position_mgr (now guards it). Bug 2: order_executor.py imported get_engine from non-existent src.db.database → src.db.connection. Bug 3 (honest labels): BOTH reconcilers (orchestrator _reconcile_positions_with_ibkr + reconciliation_manager, §E) now timing-aware — fill >5 min before scheduled_exit_time → instant_exit:reconciled, else scheduled_exit:reconciled (added scheduled_exit_time to both reconcile queries; dashboard already renders instant_exit:*; stop-rate %stop% analyses unaffected). Bug 4 (durable logs): tracebacks WERE captured but buried in 362 MB of ib_insync INFO → quieted ib_insync→WARNING + added sys/threading excepthooks (full flushed traceback for any uncaught main/thread exception) + fatal handler now logger.exception. DB corrected: KR trade_audit_log id=137 + trading_positions (§F) → instant_exit:earnings_miss, exit_time → 10:52:23 (actual fill), note appended; P&L/exit price were already correct. 8 unit tests pass; committed + deployed + §G. Prior entry — Finding #327 a #325 REGRESSION fixed (PRODUCTION FIX — trader conviction path). Operator asked to check the open KR position's entry conviction + ensure the calculator works. KR logged 0.885 (traded Mid LONG 80-90%) but EVERY source at entry = 0.92 (→ #202 ≥0.90 skip). Root cause: #325 raised the SHARED PRICE_SNAPSHOT_ROBUST_LIMIT 9→10000, which broke the no-time-window _get_latest_from_price_snapshots "latest conviction" path (live BMO/AMC entries via _get_latest_conviction): limit 10000 medianed over the market's WHOLE history (KR 4021 snapshots, 0.50-0.95 → 0.885) instead of recent. Affected 2 of 3 post-#325 trades — KR + ACN (both Jun 17, both should've been #202-skipped); SWBI (time-windowed path) fine. Fix: window the latest path to the last 60 min of available data (anchored to max(time)). Verified prod: KR 0.885→0.92, ACN 0.895→0.905, both → #202 SKIP. KR + ACN positions OPEN (operator decision per ADBE #325). 3 unit tests pass. Prior entry — Finding #326 api-health cry-wolf FIXED (ops/integrity; NO trading change). Operator flagged systemd_last_run:polymarket-api-health.service failing >1 day. Ran clean Jun 3-12, first failed Jun 13 08:03 UTC; ONLY the Polymarket website check errored ("Could not find buildId") — Gamma/CLOB/data-api/price-accuracy all HEALTHY (ZERO trading impact). Root cause: Polymarket migrated Next.js Pages Router → App Router (buildId + /_next/data gone; data now inline via self.__next_f RSC). No self-recovery because: persistent upstream change + daily timer no-retry + an INFORMATIONAL-ONLY check escalating to exit(1) (cry-wolf, #297). Fix (operator A+C): rewrote check_polymarket_website_api App-Router-aware (GET /finance, verify finance-markets+dehydratedState markers) + demoted all website failures to 'warning' (exit 0 per #297); removed unused re. Verified healthy (finance_markets=151), deployed, service re-run → integrity green. Prior entry — Finding #325 Conviction under-sampling bug FIXED — production trader conviction path (committed + deployed). Operator flagged an open ADBE LONG the dashboard marked SKIP (97.5% ≥90% → #202). The trader's ConvictionCalculator price-snapshot fallback computed 0.789 vs true ~0.95 because (1) the 0.02<yes_price<0.99 filter (3 sites) deleted the genuine high-conviction prints (ADBE's most-common reading 0.999) and (2) PRICE_SNAPSHOT_ROBUST_LIMIT=9 sampled 9 of ~1,700-per-±5min → noise-dominated median. 0.789 fell in the 70-90% band → #202 never fired. The dashboard was correct via a SEPARATE live-Gamma latest-yes_price path (app.py:2074). Scope audit (127 trades): ≥2 #202 violations — ADBE (open) + LULU (−$25.54, stopped); SNOW direction-INVERTED. Bake-off (408 markets vs actual_beat): Gamma is the BEST source (78% acc), not faulty — the estimator was. Fix (conviction_calculator.py, 5 edits): drop filter (0<v<1), LIMIT 9→10000 so the median spans the full ±30min window, reliability gate min-max RANGE>0.35 → robust IQR>0.25 (recover the real conviction, do NOT skip flickering markets — operator constraint). Before/after (378 markets): is_reliable-skips 24→19, 0 new tradeable-band skips. §G prod probe caught a fix-to-the-fix: first deploy used LIMIT 600 → ADBE still 0.863 (600-closest collapses to ~±2min on the high-freq Gamma stream); raised to 10000 → ADBE 0.912, reliable → #202 SKIP, is_reliable count (1) + ≥0.90 band membership (27/82) identical at 600 vs 10000 (surgical). 3 unit tests pass (+1 new). Refines #197; interacts with #283/#319 gate. ADBE held per operator; dashboard display path left as-is (correct). NEW scripts/conviction_source_bakeoff.py. Prior entry — Finding #323.b RuleB re-test AMENDS #323 — the LONG ≥0.90 skip (#202) flips POSITIVE under RuleB (offline; NO production change). Operator's insight confirmed: ~62-67% of stop-hits fire in the ±90min earnings window (vol-driven, conviction-independent), so #323's baseline-stop "keep ≥0.90" was confounded. NEW scripts/phase3_simulator/replay_deadzone_ruleb.py (3 regimes via canonical analyze_finding_a_full sims): LONG ≥0.90 under RuleB = +$853 / ex-top-2 +$614 / 47.7% win (n=65), RuleB > no-stop > baseline. Dead-zone 0.60-0.70 LONG ~breakeven even unstopped (weak edge, KEEP); small-cap/short KEEP. Action Queue conviction-band-revalidation. ⚠️ CORRECTED (replay_202_ruleb.py): the ≥0.90+RuleB "flip" does NOT hold on the real #202 trades — the 26 REAL closed ≥0.90 LONG trades (real fills) are ACTUAL −$112/15.4% WR → RuleB −$88/15.4% WR → NO-STOP −$154; RuleB can't fix the 85%-wrong direction. The +$853 was post-#202 SKIPPED signals (counterfactual, later period, ~$33/26 bar-pricing optimism). #202 HOLDS even with RuleB; Action Queue row back to KEPT; ≥0.90-relax lead downgraded. RuleB's value (Phase 2, #305) is the vol-spike-stop cases generally, independent of ≥0.90. Prior entry — Finding #323 conviction-band exclusion re-validation → ALL HOLD + Finding #324 counterfactual-% units cleanup (offline/dashboard; NO production change). #323: operator asked if the bot excludes profitable trades; comprehensive §K-gauntlet check (stop-aware opp_log counterfactual + universe proxy, de-duped + ex-top-2 + real per-tier scale + per-mcap) → raising the ≥0.90 cap, trading the 0.30-0.70 dead zone (0.60-0.70-as-LONG = −$54.65/FAIL once stops modeled, the #228 trap), and loosening small-caps/shorts ALL fail — production bands UNCHANGED; KEPT row in Action Queue. NEW scripts/phase3_simulator/replay_deadzone.py (+ 3 analysis scripts). #324: standardized counterfactual P&L on % (canonical hypothetical_pnl_pct, PERCENT); fixed a 100× "Avg %" dashboard display bug + honestly labeled the $10k-notional dollars; flagged a separate suspected regime/VIX realized_pnl_pct fraction-display bug for follow-up. Prior entry — Finding #322.f Pre-Monday trading-readiness audit → GO (test-only fixes; NO prod behavior change). After the week's #311-#322.e changes, full systems+code audit per memory.md before Monday markets. Live prod ALL GREEN: config effective values all match ground truth; 6/6 services active; integrity 47/47 OK; IBKR connected net_liq $27,548, 0 open positions, no breakers, 0 trader errors; prod HEAD==origin/master. Full unit suite caught 7 stale-test failures (verified NOT prod bugs per INFERENCE DISCIPLINE): 5× from #322.b scale 245→550, 1× exposure test stale vs #317 uniform-140%, 1× pre-existing dashboard cache flake — all fixed (test-only) → 726 passed. ⏭️ REMINDER (Sat Jun 6, 2026): review RuleB dry-run logs (RuleB[dry_run] in trader journal / Strategy Analysis tab) → decide Phase-2 RULEB_MODE=active flip (#322.e). Prior entry — Finding #322.e RuleB Phase 1 DRY-RUN actually ENABLED in prod (logging only; NO live order change) — pre-flight for an operator "push RuleB to prod" request found RuleB had NEVER run (RULEB_ENABLED default False + no prod .env override → main-loop hook never fired, 0 log lines in 7d; the dashboard "IMPLEMENTED-DRY-RUN" was code-only). Operator chose dry-run-first + tracked config default: RULEB_ENABLED False→True, RULEB_MODE stays dry_run (cancel/rearm only LOGGED, no IBKR side effects). NOT the Phase-2 active flip (separate confirmed ship after reviewing dry-run logs; known edge cases: restored-position earnings_dt, clock-skew on cancel→rearm). py_compile + 68 tests pass; deployed (trader restart). Phase 2 (RULEB_MODE=active) pending operator review of dry-run logs. Prior entry — Finding #322.d Phase 5: ROI re-baseline (analysis/dashboard/docs; NO trading change) — completes the final roadmap item. Re-baselines strategy ROI as a 3-epoch ladder (frozen pre-launch PROJECTIONS → frozen 87-trade → current ~117-trade + simulator) + net-new metrics (5-KPI, capacity lift, §K outlier dependence) + a hybrid forward projection (live vs sim side-by-side). Caution-first (operator choice): leads with the outlier alarm (top-5 +$427 vs ex-top-5 −$205 = the whole realized edge is ~5 outliers), tiny/frozen sample (117 trades, drought since ~Mar 2026), and live +$12.32/wk < sim +$18.11/wk; forward ~+25.6%/+37.7% on $2,500 shown but LOW CONFIDENCE. NEW PROJECTIONS_87TRADE + build_roi_rebaseline() (reuses KPI/capacity; no recompute) → report roi_rebaseline; NEW dashboard "📊 ROI Re-baseline" + renderRoiRebaseline(); NEW docs/FINANCIAL_PROJECTIONS_2026_05.md (old 87-trade doc frozen as Epoch 2). py_compile + jinja + node + builder unit-test clean; deployed (report regen + dashboard restart). Phase-3 strategy-simulator project COMPLETE end-to-end (PR 1-5 + Phase 4 + Phase 5). Prior entry — Finding #322.c Audit follow-ups (dashboard + config; NO trading behavior change): wired the dashboard risk panel via the trader's ibkr_snapshot.json risk_state (read-only serialization of risk_manager.get_risk_state(); single source of truth, no §M duplication); added a data-api.polymarket.com dependency probe + System-tab row (#289); confirmed MAX_POSITION_SIZE_PCT vestigial (no consumer; Large sizes 30% unclamped) → re-aligned 0.15→0.30 + documented, mirroring #315. py_compile + test_config (0.30) + test_risk_manager 27 PASS. Deployed; trader restarted (operator-CONFIRMED, Sunday) → snapshot now carries live risk_state, reconnected clean (net_liq $27,548.36). Prior entry — Finding #322.b Full repo-wide staleness RE-AUDIT — all 29 files (3 dashboard render sources + 26 docs, ~47k lines) read line-by-line; ~110 stale current-state claims fixed (docs + dashboard render code; NO trading change). Operator caught #322 as a shortcut — it claimed "every word and line" but only covered dashboard.html static HTML, skipping the Python that generates the dashboard (strategy_performance_analysis.py 4,815 LoC, app.py 6,252 LoC) + all 26 docs/*.md. The complete re-do: universe gate $2K→$1K (SQL+labels+sweep), P&L→+$212/~117, cockpit capital/sizing $2,450/10%/$245→$2,500/22%/$550, PR2-banner→RESOLVED, priority 0-6→0-8, dead watchdog removed, max_trades 100→150, position-sizing→30/22/15% by mcap; 18 docs synced (Phase 3 PR1-5 + Phase 4 COMPLETE; service topology; equity-bars 47k→1.9M; rules→~47; watchdog→integrity-monitor; stops/sizing/exposure; $2k→$1k; IBKR Xvfb:99/IBC; FINANCIAL_PROJECTIONS superseded banner). History preserved; no numbers fabricated. py_compile + jinja clean; deployed. Prior entry — Finding #322 Full dashboard staleness audit (dashboard-only; NO trading change)) = operator-directed exhaustive line-by-line audit of every dashboard tab (Trading/System/Database/Findings/History/Strategy + header) vs current production config; ~30 stale static facts fixed. Trading: priority 0-6→0-8 (legend/badges/clamps/labels, #313), volume gate $2,000→$1,000 (tooltip + 3 fallbacks, #311), stops STOP-LIMIT, AMC exit comment 3:58→3:57. System: exposure "rare 60%/140%"→uniform 140% (#317), backfill 4→6-pass, "Phase 0/1/2"→Phase 0-4 COMPLETE, long-weekend 75%→50% (matches live signal_generator.py:1364), 2FA "VNC"→IBKR app push. Strategy: gate-retune "current $2,000"→$1,000 post-#311 + cockpit sizing exposure→uniform + #316 panel "superseded by #317". Database +#307 citation; History 5-outlier net "$159"→+$212. Verified node --check (both <script> blocks) + jinja parse + 0 mojibake + global residual-token sweep clean. Frontend-only (dashboard.html); deployed (dashboard restart). Also earlier this session — Finding #321 follow-up SHIPPED: self-healing counterfactual retry in outcome_resolver (daily timer reprices transient price_data_unavailable gaps once bars exist; bounded by bars-exist gate + 3-day cooldown). Earlier — Finding #321 Strategy-analysis data completeness: Polygon backfill + full simulator re-validation (data backfill; NO code/strategy change)) = operator directive to ensure COMPLETE Polygon-sourced data before trusting the simulator. Audit found 472 PAST counterfactual-P&L gaps in polymarket_opportunity_log (price_data_unavailable rows whose equity_bars_1m already existed — original fetch failed transiently + compute_counterfactual_pnl.py never retries computed rows) + ~48 earnings_markets outcome/return gaps. Backfilled via compute_counterfactual_pnl.py (counterfactual P&L 1,288→1,755 priced; 30 remain = 25 future + 5 delisted) + outcome_resolver.py --force (stock_return 99.8% of past markets; actual_beat 948/961, 13 awaiting EPS publication). Validation batch repriced 20/20 via Polygon first. Full re-run on COMPLETE data — every conclusion HOLDS: capacity #314.b $1k gate KEEP +$18.11/wk (was +$16.40); regime #318 KEEP compound_v1 (identical); depth #319 KEEP gate (identical, 22 binding kills, net +$22.54). Backfill VALIDATED the shipped KEEP decisions rather than overturning them. Key insight: the _CF_SQL simulator universe is filter-limited (volume≥$1k drops 713 + conv≥0.90/small-cap drops 369 → 590 raw → 441 post-dedup), NOT data-limited. Corrected #320's Universe View label (regenerates on every report build via analyze_universe_hit_rate, not a 4h manual job). NO code/strategy change (DB backfill via existing resolver + compute script). Follow-up flagged: add a periodic retry of price_data_unavailable rows whose bars now exist. Earlier same day — Finding #320 Phase 4 (remainder): Unified Simulator dashboard consolidation (dashboard-only; NO trading change)) = completes the Phase-3-dependent half of Phase 4 (un-gated now Phase 3 PR 1-5 is done). NEW 🧪 Unified Simulator umbrella on the Strategy tab (#unified-simulator-section) framing ONE canonical engine over its arms: live stop-loss (Finding-A, relabeled), capacity-aware (Phase 3 Capacity-Aware Sweeps + Gate Retune), selection-rule research (static regime #318 + depth #319 KEEP cards, #phase3-selection-research-section), universe signal-quality (Universe View, folded + honest Apr-25-baseline label). Archived Q3-2025 Backtest Scenario Comparison + BMO/AMC Timing Matrix off the tab (producers+JSON retained; dispatcher calls removed). Consolidated 5 overlapping breakdowns → 2 canonical (removed Live Performance Snapshot; kept Direction+Timing Matrix + Performance Breakdowns). Operator AskQuestion: unify-and-preserve (not delete-all-3) + static regime/depth cards + include breakdowns. Frontend-only; verified node --check (both <script> blocks) + Jinja parse + div balance preserved + removed-DOM-ids only referenced by 3 now-dead (never-called) render fns (renderScorecard/renderBacktestTable/renderCohortTimingTable — documented dead, safe to delete in follow-up). Deployed (dashboard restart only; NO trader restart). Completes Phase 3 + Phase 4 (history at May 31 ship: Phase 5 ROI re-baseline was still outstanding; Phase 5 SHIPPED same day — Finding #322.d). Earlier same day — Finding #319 PR 5: Depth-gate v2 widened reconstruction — break-even low-information-signal filter → KEEP (offline-only; closes Finding #283; Phase 3 simulator PR 1-5 COMPLETE)) = NEW scripts/phase3_simulator/depth_gate_variants.py (pure decomposition + remove_all / carveout_real_trade / spread_gate_only / depth_gate_only variants) + replay_depth_gate_v2.py (replays the REAL ConvictionCalculator.get_conviction_at_entry_time point-in-time over the full 915-market universe → 22 BINDING depth kills [gate fired AND clears volume + #202/#207 skips], recovers raw orderbook + reliability_reason, prices counterfactual equity P&L via run_counterfactual_for_row). Operator's question "has the gate ever saved us from a bad trade?" → YES: 13 losers BLOCKED (−$142.56), 9 winners KILLED (+$165.09), net removal +$22.54 over ~7 months (≈ +$1/trade = noise). Kill cause 18/22 snapshot_reliability (hollow book → sparse/unstable price-snapshot conviction), 1 spread, 3 no-orderbook → low-information-signal filter, not the pure spread gate #283 implied. The n=4 "only costs us / blocked 0 losers" was a small-sample mirage; widening (operator-requested) reversed it. Variants: remove_all +$22.54 / carveout +$22.78 (≈ identical) → adds variance for ~$0 EV. Operator-confirmed KEEP; production conviction_calculator/signal_generator depth check UNCHANGED. 395 tests PASS (+20). NO PRODUCTION CHANGE, NO deploy. This was the last Phase 3 deferred-bundle item — Phase 3 strategy-simulator (PR 1-5) is COMPLETE. Earlier same day — Finding #318 PR 4: Regime classifier v2 sweep — faster detectors add no robust ROI → KEEP compound_v1 (offline-only; closes Finding #278)) = NEW scripts/phase3_simulator/regime_classifiers.py (pure-Python point-in-time SpyDailyHistory prior-completed-bar lookup + 6 classifiers: compound_v1 baseline + compound_or_dd5 [#278 option B] + below_ema20 + below_ma50 + dd_3pct + compound_symmetric; VIX deferred to Phase 4) + replay_regime_v2.py (re-classify regime point-in-time → re-apply Rule #1 [#304] → recompute P&L via run_sizing_sweep/apply_sizing_gauntlet vs compound_v1; sizing held at production so credited == raw realized P&L). Operator AskQuestion: classifier set = equity family of 6; universe = BOTH (live-only primary + capacity-aware 1.40 cross-check); point-in-time = prior-close (agent); gauntlet-floor = full-pop spillover primary (agent). Prod result (568 signals = 127 live + 441 counterfactual): below_ema20 (fastest; skips ~40% of LONGs vs compound's 17%) gains +$70.31 PASS on live-only but −$17.02 FAIL-EX-TOP-2 on the capacity-aware universe at the live 1.40 cap — the live-only win is selection luck concentrated in actually-traded outcomes (whipsaws out winners on the broader universe; same reversal as #315 "don't look in isolation"). compound_or_dd5 + below_ma50 inert; dd_3pct + compound_symmetric FAIL. Only effect surviving both universes = better worst-week tail (5p −$98.60→−$57.08) at ~breakeven P&L (downside trade-off, not a money-maker). Methodology validated: PIT compound_v1 reproduces the live recorded regime 95.3%. Operator-confirmed KEEP compound_v1; production src/earnings/market_regime_provider.py UNCHANGED. 375 tests PASS (+22 new). NO PRODUCTION CHANGE, NO deploy (offline analysis). Earlier same day — Finding #314.e PR 3.b.3: 4 deferred SKIP-rule candidates — real-scaled + bias gauntlet (ANALYSIS-ONLY, ship deferred to operator) = NEW scripts/phase3_simulator/replay_skip_rules.py re-evaluates the 4 Finding #313 deferred SKIP cells REAL-SCALED (not hypothetical $10k-notional) + capacity-aware. Real-scaling corrected 2 cells (Large+SHORT+$2k-$5k only n=3 not 12; Large+LONG[.80,.90)+$1k-$2k net-POSITIVE +$0.93 avg, hyp said −$20). Gauntlet: skip Large+LONG+≥$10k +$2.04/wk PASS; skip Mid+LONG+$1k-$2k +$1.12/wk PASS; skip Large+SHORT+$2k-$5k FAIL (n=3); skip Large+LONG+$1k-$2k −$0.82/wk FAIL (cell positive); combined skip-all-4 +$5.42/wk PASS (maxDD −$220, WR 39.1%). Operator ship-bar=C analysis-only → NOTHING shipped; per-candidate ship pending operator. 313/313 tests PASS (+10). NO PRODUCTION CHANGE. Earlier same day — Finding #314.d PR 3.b.5 SHIPPED: Hybrid seasonal gate variant — KEEP uniform $1k) = evaluated operator's "$2k peak-weeks / $1k off-peak" gate. Verified the academic peak-week classifier tracks PM data first (peak windows hold 57.7% of 977 markets / 60% of vol≥$1k). NEW scripts/phase3_simulator/replay_gate_hybrid.py (hybrid = uniform $1k gate + peak-week sub-$2k skip; reuses run_sizing_sweep). Prod run (568 signals): hybrid removes 144 peak-week $1k-$2k signals → +$8.08/wk vs uniform $1k +$16.86/wk (Δ −$8.78/wk, FAIL-SPILLOVER), nearly reverting to uniform $2k (+$6.70/wk). Operator premise "$1k is noise in peak weeks" REFUTED (those markets are net-positive per #314.b; removing them gives back most of #311's lift; cap-reallocation doesn't help — cap-skips 58→5 but freed slots not refilled). Honest note: pre-build diagnostic under-counted footprint (31 via cumulative volume vs 144 in simulator). DECISION: KEEP uniform $1k (#311). 303/303 tests PASS (+7). NO PRODUCTION CHANGE. Earlier same day — Finding #314.c PR 3.b.4 SHIPPED: Sizing variants sweep — KEEP 30/22/15) = first sweep on the size-aware capital simulator. simulate_one_variant() gained scale_pnl_with_size flag (default OFF → #314.b + SKIP sweeps byte-identical; design doc §12 PR3) + realized_pnl_credited on AdmissionDecision; NEW gauntlet.apply_sizing_gauntlet() + NEW scripts/phase3_simulator/replay_sizing_variants.py (8 variants, per-period + per-bucket + gauntlet vs baseline + Pareto). Prod run (568 signals / 36 wk / $2.5k × 2.0 = $5k max gross): operator's "lower sizing → more positions → more P&L" hypothesis REFUTED — lowering sizing admits more (20/15/10 → 437 vs baseline 409; capacity-skips 58→30) but total P&L DROPS −$6.80/wk (FAIL-SPILLOVER — marginal capacity-freed trades are low-EV; shrinking good positions costs more). Only positive-Δ variants (flat 25/25/25 +$1.41/wk, 35/25/18 +$0.75/wk) FAIL the gauntlet (single-name / outlier dominated) + worsen maxDD/5p. DECISION: KEEP production 30/22/15 (Finding #211 unchanged). 296/296 phase3_simulator PASS (+14). NO PRODUCTION BEHAVIOR CHANGE. Earlier (May 30 LATE LATE evening — Finding #314.b PR 3.b.2 SHIPPED: Capacity-aware re-validation of Finding #311) = NEW scripts/phase3_simulator/replay_capacity_aware.py (~410 lines) — first sweep on capital simulator. Prod run on 568-signal universe (127 live + 441 counterfactual, 36 weeks): post-#311 ($1k gate) admits 409 / total +$607 / +$16.86/wk; pre-#311 ($2k gate) admits 295 / total +$241 / +$6.70/wk; Δ +$10.16/wk (HALF the optimistic +$21/wk Phase γ+δ projection — capacity contention is the gap: 58 capacity-rejects at $1k vs 5 at $2k). $1k-$2k admitted bucket POSITIVE (n=132, avg +$3.20, total +$423). max_capital_used=$4,949 (99% of cap; binding). DECISION: KEEP $1k gate. Earlier same day (LATE LATE evening) — Finding #314 PR 3.b.1 SHIPPED: Capital simulator foundation) = NEW scripts/phase3_simulator/capital_simulator.py (~365 lines, all offline-only): CapitalSimSignal (live+counterfactual) + SizingVariant (mcap_pct + extra_skip_predicates + gate_volume_min) + AdmissionDecision + CapacitySimResult + simulate_one_variant() per-event heap-based simulator + signal_from_live_trade() + signal_from_counterfactual_market() (latter applies per-tier empirical scaling). 30 NEW unit tests; 282/282 phase3_simulator PASS local. NO PRODUCTION BEHAVIOR CHANGE. PR 3.b.2 NEXT: re-validate Finding #311 ($1k gate) capacity-aware; PR 3.b.3 → 4 deferred SKIP rule candidates with bias gauntlet; PR 3.b.4 → sizing variants sweep. Earlier same day (LATE LATE evening) — Finding #311 Re-validation Note (doc-only): operator-requested re-run; methodology audit caught my PM-volume / IBKR-slippage conflation; live n=10 within 1σ of simulator projection — sampling noise. KEEP $1k gate; revisit after 30+ live trades. Earlier same day (LATE LATE evening) — Finding #313 SHIPPED: PR 3.a — Volume-tier priority bump + capital cap raise + NYSE dead-code cleanup) = src/earnings/priority.py rewrite (scale 0-6 → 0-8, base 0-6 mcap×conv ordering preserved per Mar 9 + new volume bump +1 at $2k +2 at $5k); src/earnings/config.py MAX_TOTAL_EXPOSURE_PCT 1.40 → 2.00 (Reg T overnight ceiling); src/earnings/order_executor.py is_nyse dead branch dropped (post Finding #185 unification); signal_generator.py passes volume_total to priority; dashboard legend + clamp updated. 617/618 unit tests PASS. NEW MAX_PRIORITY_SCORE=8, VOLUME_BUMP_TIER_1_USD=2000, VOLUME_BUMP_TIER_2_USD=5000. Operator follow-up next: re-validate Finding #311 ($1k gate) against new volume data — $1k-$2k bucket was historically -$0.77 avg / WR 30% (n=10) → may need gate adjustment if simulator + new data agree. 4 candidate SKIP rules deferred. Earlier same day (LATE evening) — Finding #312 SHIPPED: v2 #6 Daily 3-stop session breaker + DirectionPolicySpec doc-bug fix = NEW src/earnings/session_stop_breaker.py pure-decision module (~155 lines: SessionBreakerAction enum + SessionBreakerDecision frozen dataclass + decide_session_stop_breaker() pure function + COUNT_STOPS_TODAY_SQL constant). UPDATED src/earnings/signal_generator.py: NEW _count_todays_stops() helper (fail-soft on DB error → 0) + skip block in _build_trading_signal after Queue Rule #1 long_downtrend → returns (None, "session_3_stop_breaker") when ≥ Config.SESSION_STOP_BREAKER_THRESHOLD (default 3) stops fired today (ET-day). UPDATED src/earnings/config.py: 2 new env vars (SESSION_STOP_BREAKER_ENABLED default True, SESSION_STOP_BREAKER_THRESHOLD default 3). Plus production_strategy_may30() direction-policy correction: long_threshold 0.55 → 0.70, short_threshold 0.45 → 0.30, NEW long_max_threshold=0.90, conviction_fn "abs_distance_x2" → "raw_prob". Dashboard Live Config "Direction" + "Selection" rows updated. 79 tests PASS (22+8+38+11). Earlier same day (LATE evening) — Finding #311 SHIPPED: gate_uniform_$1000 production change (first real-money strategy ship since Mar 9, 2026)) = src/earnings/config.py SIGNAL_MIN_VOLUME_TOTAL default lowered $2,000 → $1,000. production_strategy_may30() factory created. Earlier (May 30) — Phase α+β+γ+δ+ζ shipped (10 commits): unified Strategy framework with 9-component composable object + 5-KPI engine + replay-based candidate sweep + Pareto frontier + simulator-driven Top-5 in cockpit. Earlier (May 30 morning) — Phase ε.1-5: Operator Cockpit (4 layers) + Section Index + Universe View staleness banner + auto-regen on deploy. NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved). Earlier (May 29 — Finding #310.c shipped: Phase 3 simulator PR 2.c FINAL) = NEW scripts/phase3_simulator/gate_retune_sweep.py (~280 lines) with ShipEligibility / VariantSweepResult / SweepResult frozen dataclasses + evaluate_conservative_ship_eligibility() 3-condition Q3 (C) check + run_gate_retune_sweep_on_universe() deterministic orchestrator + run_gate_retune_sweep() SQL-loading runner. NEW gauntlet.apply_admission_gauntlet(variant_id, admission_results, period_filter) entry point reusing existing 5-check helpers via duck-typed AdmissionResult (added @property symbol + @property entry_time). NEW output.write_sweep_results_json() schema = phase3_sim_gate_retune.v1 (deterministic, sort_keys + trailing newline). Extended runner.py with --run-gate-retune-sweep flag + --sweep-output arg + _run_gate_retune_sweep_mode() (~80 lines). Added strategy_performance_analysis.build_phase3_sim_gate_retune() helper that loads scripts/phase3_simulator_gate_retune_sweep.json + falls back to status='not_run' placeholder. Added new dashboard 🧪 Phase 3 — Gate Retune Sweep section (per design doc §7.4) below the existing 🧪 Phase 3 Simulator Status section: status header (boundary date / universe / eligible-count) + per-variant table (sorted: ship-eligible first, then per-100 desc) + per-variant collapsible details with ship-eligibility reasons + per-period gauntlet table + eligible-variants summary + renderPhase3SimGateRetune() JS function (~150 lines). 25 NEW unit tests across 6 classes (TestAdmissionResultDuckTypeProperties + TestApplyAdmissionGauntlet incl. synthetic H-rule shape + TestConservativeShipEligibility incl. each FAIL path + TestRunGateRetuneSweepOnUniverse + TestWriteSweepResultsJson incl. 0-byte-diff reproducibility + TestSerialization). 250/250 PASS local Python 3.11 (208 phase3_simulator + 42 prior signal-test files); no regressions. NO production behavior change. PR 2 COMPLETE — operator AskQuestion per qualifying variant is the next step. PR 3 (sizing + priority + exposure-cap) begins next session. Earlier (May 29) — Finding #310.b shipped: Phase 3 simulator PR 2.b — counterfactual_admission.py (load opp_log low-volume markets + per-variant admission filter) = NEW scripts/phase3_simulator/counterfactual_admission.py (~230 lines) with CounterfactualMarket frozen dataclass + load_counterfactual_markets() SQL loader + evaluate_admission(variant, market) per-variant filter + evaluate_variant_admissions() per-variant scoring + AdmissionResult dataclass + summary_dict() quick aggregate. §A pre-coding INFERENCE DISCIPLINE catch (6th this session-arc): original PR 2.b design assumed counterfactual replay would re-fetch bars + re-compute stops + re-execute the stop-loss simulator (substantial new code). Pre-coding schema inspection revealed polymarket_opportunity_log already has hypothetical_entry_price + hypothetical_exit_price + hypothetical_stop_hit + hypothetical_pnl + hypothetical_pnl_pct + intended_* fields pre-populated by compute_counterfactual_pnl.py (Finding #308 re-run). Massive scope reduction. Critical filter: USABLE_PRICES_SOURCES=('polygon.io_1m_close',) excludes 278/540 rows with source='none' ($0 placeholder; Polygon fetch failed). Pre-coding evidence: 262 usable counterfactual markets (all in collapse period; max volume $1,953 → no production-gate admission); $1k admits 96, $500 admits 146, $250 admits 181, $100 admits 196, $0 admits 262; aggregate +$3,180 if all admitted (avg +$12.14/trade). Strong signal pending PR 2.c gauntlet vetting. 20 NEW unit tests in 6 classes; 225/225 PASS local Python 3.11. NO production behavior change. PR 2.c FINAL (next): gauntlet per-variant×per-segment + output schema + runner --variant-set + 3-condition conservative-ship check + phase3_sim_gate_retune dashboard section + run on prod + AskQuestion ship decisions. Earlier (May 28-29) — Finding #310 shipped: Seasonality preflight + structural-collapse discovery + PR 2.a declarative scaffolding (gate_variants.py + 11 variants + period segmenter + week-of-month classifier) = OPERATOR-REQUESTED preflight before PR 2 coding REFUTED the pure-seasonality hypothesis (Dec 2025 off-season was healthy at 98.1% gate-pass + $14,522 median volume; Mar+2026 off-season collapsed to 27.8% + $0 median → real STRUCTURAL collapse starting March 2026, NOT seasonality). Operator AskQuestion 3-question pattern expanded PR 2 scope: Q1 = (B) SEGMENTED (3 segments: healthy < 2026-03-01 / collapse / full); Q2 = (B) THRESHOLD + SEASONAL AXIS (11 unique variants = 6 thresholds × 2 modes uniform/seasonal_week with off-peak factor 0.5, dedupe $0/$0); Q3 = (C) SHIP CONSERVATIVE (ship only if gauntlet PASS on full-pop + ≥20 admitted markets in collapse + ex-top-2 stays positive). PR 2 split into 3 sub-commits: PR 2.a SHIPPED (this; declarative scaffolding + 51 unit tests + doc updates; 205/205 PASS local); PR 2.b NEXT (counterfactual_admission.py — synthesize Trade from polymarket_opportunity_log skip_reason='low_volume' rows + delegate to replay_trade); PR 2.c FINAL (gauntlet aggregator extension + output schema + runner --variant-set + 3-condition ship check + phase3_sim_gate_retune dashboard section + run on prod + AskQuestion ship decisions). NO production behavior change. Earlier (May 28 LATE evening): Finding #306.c shipped: Phase 3 simulator gauntlet.py + output.py + runner.py --run-gauntlet (fifth PR-1 ship of the day; THIRD of 3 sub-commits for #306 — PR 1 COMPLETE) = NEW scripts/phase3_simulator/gauntlet.py (5-check bias gauntlet per memory §K: n_adequate + ex_top_2 + ex_repeat_tickers + full_population_spillover + five_outlier; verdict dispatcher in priority order FAIL-N-TOO-LOW → FAIL-EX-TOP-2 → FAIL-EX-REPEAT-TICKERS → FAIL-SPILLOVER → WARN-OUTLIER-DOMINATED → PASS; true_simulator_compliant=True baked in; VariantStats + CheckResult + GauntletResult frozen dataclasses) + NEW scripts/phase3_simulator/output.py (deterministic JSON schema per design doc §7.2: DataSnapshot + RulesetEvaluation + ShipRecommendation + RunOutput; write_results_json with sort_keys + trailing newline; mask_volatile_fields for reproducibility diffs; derive_ship_recommendation verdict→SHIP/DEFER/DROP mapping; get_code_git_sha defensive subprocess wrapper) + UPDATED scripts/phase3_simulator/runner.py (added --run-gauntlet flag for full pipeline + --baseline-rule + --variant-rules + --output + --no-replay-detail + --polygon-sleep; removed "#306.a stops here" footer staleness). 33 NEW tests across 2 new files; 154/154 PASS local Python 3.11 (112 phase3_simulator + 42 prior signal-test regression). §10.3 H-rule self-validation per design doc PR 1 acceptance gate: PASS (synthetic v2 H-rule shape returns FAIL-EX-TOP-2 as required; if it ever flips to PASS, gauntlet is broken — block PR 2). §10.2 reproducibility per design doc PR 1 acceptance gate: PASS (2 runs same input produce byte-identical JSON after masking generated_at). PR 1 IS NOW COMPLETE: foundation (#306.a) + replay+cost-model+cross-check (#306.b) + gauntlet+H-rule+reproducibility+JSON (#306.c) + Finding #273 conditional-label fix (#307) + Bug #6 hyp_pnl re-run (#308) all shipped. All 4 PR 1 acceptance gates pass (§10.1 cross-check PASS per #306.b §G probe = 100% match within $1 on all 7 rules; §10.2 reproducibility; §10.3 H-rule self-validation; Phase 0 §0.6 corrected per #308). NO production behavior change. PR 2 (volume-gate retune sweep, highest-leverage per May-28 PM volume-collapse evidence) can begin next session per design doc §12. Earlier (LATE evening): Finding #306.b shipped: Phase 3 simulator cost_model + replay engine + reference_adapter + cross_check_baseline script (fourth PR-1 ship of the day; second of 3 sub-commits for #306) = NEW scripts/phase3_simulator/cost_model.py (CostEstimate dataclass + estimate_cost with proxy-flat-1pct default per Q2 + latent pm_v2_orderbook path for PR 2+ counterfactual admission) + NEW scripts/phase3_simulator/reference_adapter.py (re-exports fetch_bars/run_trade/simulate_/TradeSim from scripts.analyze_finding_a_full per Q1 HYBRID + _assert_reference_module_stable() signature-drift guard fired at import time with memory.md §M-pointing diagnostic) + NEW scripts/phase3_simulator/replay.py (ReplayContext + ReplayResult dataclasses + replay_trade(trade, ruleset, ctx) delegating to reference's run_trade + replay_population progress-logging loop) + NEW scripts/phase3_simulator/cross_check_baseline.py (operator-runnable script implementing design doc §10.1 acceptance gate: load N trades, run replay_population, INNER JOIN against scripts/finding_a_full.csv, per-rule drift breakdown, PASS if match_pct >= 95% within $1 on every rule, writes JSON evidence file). §A pre-coding INFERENCE DISCIPLINE catch (fifth this session-arc): Trade.scheduled_exit_dt(cal) was MISSING from population.py — the reference's run_trade calls it, so the delegation in #306.b would AttributeError immediately. My #306.a TestReferenceParity only checked dataclass FIELDS (via dataclasses.fields()), not METHODS. Fixed both as part of #306.b: added scheduled_exit_dt to population.py + new TestReferenceParity.test_method_names_match_expected + test_scheduled_exit_dt_consistency_amc tests. Caught BEFORE writing #306.b code. Code shipped: 4 new package files + 1 updated (population.py) + 3 new test files + 2 tests added to test_population.py. 37 NEW tests; 121/121 PASS local Python 3.11 (79 phase3_simulator + 42 prior signal-test regression). NO production behavior change (simulator package remains offline-only). The §10.1 cross-check is now RUNNABLE — execution is part of #306.b §G probe runway (post-deploy on prod, expect verdict PASS within $1 on every rule since Q1 HYBRID delegation calls the same simulate_ functions; any drift would indicate (a) reference CSV stale vs current reference code or (b) Polygon bar cache-vs-fresh divergence). Earlier (LATE evening): Finding #306.a shipped: Phase 3 simulator foundation skeleton (third PR-1 ship of the day; first of 3 sub-commits for #306) = NEW scripts/phase3_simulator/ package skeleton (__init__.py + population.py Trade+load_trades duplicated per Q1 HYBRID + rules.py declarative RuleSet dataclasses + baseline_ruleset factory + runner.py CLI stub) + NEW tests/unit/phase3_simulator/ test scaffold (42 NEW tests: 18 population including 4 CHANGE-CONTRACT cross-check tests vs analyze_finding_a_full.Trade + 24 rules including serialization round-trip). 84/84 PASS (42 new + 42 prior signal-test regression, no regressions). NO production behavior change (offline-only simulator package). 3 operator AskQuestion decisions resolved BEFORE coding: Q1 = (C) HYBRID (duplicate Trade/load_trades, delegate stop-loss simulate_ via future reference_adapter.py in #306.b); Q2 = (A) FLAT 1% proxy spread (lands in #306.b cost_model.py); Q3 = (B) THREE INCREMENTAL MILESTONES (this sub-commit + #306.b cost_model+replay+cross-check + #306.c gauntlet+H-rule self-validation+reproducibility+output+dashboard). Earlier (May 28 LATE evening): Finding #308 shipped: Phase 3 simulator PR 1 second commit (ops-only) = compute_counterfactual_pnl.py re-run on prod (78 truly-pending rows processed; generate_signal coverage 67.5% → 69.1%) + structural-ceiling discovery documented in design doc §3 Gate 4. INFERENCE DISCIPLINE catch #4 this session: the 480 generate_signal + 39 depth rows with NULL hyp_pnl are STRUCTURALLY un-simulatable (all direction_would_be='NONE' = conviction-deadband; no direction means no counterfactual P&L). The Phase 0 audit's "75% coverage target" was a framing artifact — actual achievable ceiling is ~69% / ~28% on those stages, not 75%. The signal_emitted subset (100% coverage) is sufficient for Phase 3 simulator's primary input; no Phase 3 work blocked. Earlier (May 28 LATE evening): Finding #307 shipped: Phase 3 simulator PR 1 first commit = snapshot_schema='pm_v2' conditional-label fix at signal_generator.py:1041 + integrity rule update in lockstep (integrity_monitor.py:541 null_rate:opp_log:pm_v2_post_deploy col changed from snapshot_schema → method to handle the new conditional-label semantic) + 6 new unit tests + design doc updates reflecting INFERENCE DISCIPLINE catch (Bug #2/#3 ALREADY FIXED Apr 26 — Findings #234/#237 — so revised PR 1 scope drops their forward-fix work; only Bug #6 hyp_pnl re-run remains in #308). 42/42 tests PASS pre-commit (6 new + 36 prior). This is the LOCKSTEP fix for the original Finding #273 (deferred Apr 27 + re-confirmed May 28 afternoon). Closes #273 Phase 1. The 1,055 pre-fix mislabeled rows remain as historical baseline (not retroactively unlabeled per memory.md §C surgical-changes + audit-trail discipline); cost model in Phase 3 simulator handles both pre-fix and post-fix rows uniformly via proxy fallback. Earlier (May 28 evening): Phase 3 unified simulator design doc shipped at docs/PHASE_3_SIMULATOR_DESIGN.md (867 lines). Operator chose all-4-bundle scope via AskQuestion LATE afternoon → design doc concretizes 5-PR ship plan (~3-5 weeks): PR 1 foundation + Finding #273 fix lockstep + Bug #2/#3/#6 backfill; PR 2 volume-gate retune sweep (highest leverage per Finding #279 May-28 amendment); PR 3 sizing + priority + exposure-cap variants; PR 4 regime classifier v2; PR 5 depth-gate v2 decomposition. Phase 3 gates from Phase 0 doc (STRATEGY_ANALYSIS_ARCHITECTURE.md §0.6) VERIFIED MET as of May 28 (equity_bars_1m 185 days/418 symbols, actual_beat 94.9%, opportunity_log hyp_pnl 100% on signal_emitted subset, Bug #5 stop_triggered fixed; remaining sub-gaps on sp500/conviction_at_exit/depth-stage-hyp_pnl addressed in PR 1 scope). 8 open operator-decision points in design doc §15 must resolve before PR 1 starts. Earlier (May 28 LATE afternoon): Finding #305 shipped: RuleB (±90 min no-stop window) Phase 1 DRY-RUN — SECOND Phase-3 strategy rule landed ~3 hours after Rule #1 ship. Hardware cancel-and-rearm pattern (NOT soft-gate per Finding #228 lesson). Phase 1 RULEB_MODE='dry_run' default + RULEB_ENABLED=false default (no production behavior change until operator explicitly enables); Phase 2 active-mode flip pending operator's review of dry-run logs over ~few days. 25 new unit tests in tests/unit/test_ruleb_window_gate.py, 36/36 PASS pre-commit (25 RuleB + 7 Rule #1 + 4 prior signal-test regression). Bias-gauntlet PASS robustly on May-28 fresh simulator n=121: ex-top-2 +$127/100 STRENGTHENED from Apr-25 +$123/100 baseline. Structural inconsistency caught during pre-coding (INFERENCE DISCIPLINE + NO SHORTCUTS): the original Playbook §6.7 spec described a soft-gate/bookkeeping-defer approach that would have delivered $0 EV (same failure mode as FA_soft retracted in Finding #228); operator chose Path (A) hardware cancel-and-rearm via AskQuestion after agent surfaced the inconsistency with comparison matrix + recommendation. State is DERIVED per-cycle (no DB column, no migration) per memory.md "IBKR is source of truth for live state" + §C surgical-changes. Phase 1 known limitation flagged: restored positions (signal=None per position_manager:1155) cannot derive earnings_release_dt → graceful SKIP_NO_EARNINGS_DT; Phase 2 may add restart-safe lookup if dry-run logs show this happens in practice. Earlier (May 28 afternoon): Finding #304 shipped: Queue Rule #1 (Skip LONGs in downtrend regime) — FIRST Phase-3 strategy rule landed ~50h after the May-26 un-gate. Per memory.md NO SHORTCUTS + INFERENCE DISCIPLINE: 7 new unit tests added in lockstep (tests/unit/test_signal_regime_gate.py), all PASS pre-commit on prod's Python 3.12 venv along with 4 existing signal-test regressions (11/11). Rule fires on direction==LONG AND _safe_get_regime() == "downtrend"; fail-soft on every regime-fetch failure mode. Bias-gauntlet PASS robustly on fresh May-28 data: 22 LONG-downtrend trades / 5W / −$136.30 net (ex-top-2 −$175, more negative than headline → robust); full-population spillover positive (uptrend +$133/41tr, sideways +$89/39tr, downtrend SHORT +$172/3tr preserved). Sequence-of-work choice on Finding #273: operator AskQuestion option (E) DEFER per the original FINDINGS.md lockstep approach (with Phase 3 simulator), after evidence surfaced #273's bug surface is 16× bigger than originally documented + the fix scope is non-trivial without simulator schema knowledge. Earlier (May 28 afternoon): Finding #279 Phase B amendment shipped (PM earnings-market volume collapse evidence folded in). First piece of Phase 3 strategy-review work landed today. Doc-only change — no code, no integrity rule changes, no live-trading change. Triggered by operator's "next session we will deal with phase 3 strategy review" direction + AskQuestion option (B) "fold #304 evidence into #279" (vs standalone Finding #304 / both / defer). Sequence-of-work plan: (D then A) — ship Finding #273 (pm_v2 NULL bid/spread data-capture fix) next, then IN-QUEUE Rule #1 (Skip LONGs in downtrend regime), then re-evaluate remaining Phase 3 options. Today's pre-amendment data-gathering pass: re-ran scripts/strategy_performance_analysis.py (116 clean trades, +$212.21 net, WR 37.07 %, stop rate 51.72 %, 0 new clean closures since May 7 = 21-day strategy-execution drought May 6→27); re-ran scripts/analyze_finding_a_full.py (n=121 fresh, RuleB strengthened to ex-top-2 +$127/100, RuleG headline higher but ex-top-2 +$105 weaker, FA_hard retraction reasoning still holds via rearm_gap_open arm at -$1,545/100); direct SQL on earnings_markets LEFT JOIN markets USING (condition_id) quantified the PM volume collapse: Jan 2026 100.0 % gate-pass at avg $33,475 → May 2026 1.9 % gate-pass at avg $306, ~109× avg drop / ~1,847× median drop / 52× gate-pass-rate drop, NO Mid-cap market in all of May crossed the $2k gate (0/74), the 3 May gate-passers were AMPL Small (vol $2,441 / 12 % conv / Finding #207 SHORT skip), NVDA Large (vol $9,358 / 97 % conv / Finding #202 LONG skip), LOW Large (vol $2,253 / 89 % conv / no entry — sub-question for Phase B planning). HPQ entry yesterday crossed the gate via Finding #282 Gamma-cumulative fallback path (DB volume_total $23.84 < $2k but volume_threshold_met = True recorded; data-capture gap flagged not actioned per memory §C). Earlier: Finding #303 shipped (deploy_from_github.sh pre-pull hygiene for recurring working-tree drift, RESOLVED). Closes the operational-hygiene issue surfaced by Finding #302's deploy: 3 pull-abort rounds had to fall back to memory.md §H scp single-file hotfix. New git_hygiene_cleanup() function before the git pull --ff-only line: removes untracked files byte-identical to origin/master (case a, ~12 files today), resets modified files byte-identical to origin/master (case b, ~5 files today), stashes modified files that DIFFER from both HEAD and origin/master with timestamped message (case c, ~2 files today; operator reviews via git stash list). Operator-approved divergent-handling = stash-preserve (not abort-deploy or auto-drop). Scope = script-only (no docs/integrity-rule additions). Earlier (May 28 LATE morning): Finding #302 shipped (polymarket-backup.timer collision with apt-daily-upgrade.timer, RESOLVED). Operator-approved fix-path (A) "move backup timer to 05:00 UTC" via AskQuestion after I traced the full root-cause chain per the new INFERENCE DISCIPLINE rule. Today's missing May-28 backup filled via manual systemctl start polymarket-backup.service post-deploy; backup file successfully created, integrity rule flipped FAIL → OK. The IBKR Gateway disconnect from earlier this morning had the same root cause (network restart killed its TCP connection); Gateway auto-recovered separately and doesn't need a fix. Earlier (May 28 morning): memory.md 🚨 INFERENCE DISCIPLINE rule codified as companion to NO SHORTCUTS. Triggered by today's HPQ-scheduling-bug-that-wasn't false alarm (agent declared a +24h scheduling bug on a correctly-running AMC position; operator caught it instantly because the code's _calculate_scheduled_exit_time_from_db literally calls get_next_trading_day(entry_time) and the file-level docstring says "AMC Exits: Execute at 3:57 PM (next day)" — agent had never traced the exit-scheduling code, only the entry-sizing path). Rule + the May-17 2FA-inference + May-28 AMC-scheduling anchoring incidents now live in .cursor/memory.md between NO SHORTCUTS and TRADE EXECUTION PROHIBITION; §A pre-coding checklist updated with single-line cross-reference. No code changes; doc-only commit. Also today's IBKR live disconnect at 04:12 UTC — auto-recovered without manual intervention (port 4001 came back via systemd auto-restart at 04:17:16; trader reconnected at 04:18:17). Total downtime 6m 17s. Earlier (May 27 morning): factual correction shipped to the May-17 and May-24 reconnect entries below: I had incorrectly inferred "IBKR allowed session resume without re-2FA" on both Sundays from the fast (~16-20s) port-up time, but the operator clarified that every Sunday reconnect DOES require operator 2FA approval on phone — the IBC server-side logs are blind to the human-side push approval, and fast port-up is consistent with quick mobile approval, NOT with session resume. Both reconnect entries now reflect the actual workflow (operator-approves-push). The downstream reasoning chain that claimed "live Sunday automation may be feasible as fully unattended" was also unfounded and has been removed. Earlier (May 24 morning): IBKR Sunday 2FA reconnect handled manually (09:18 UTC restart → operator approved 2FA push on mobile → port 4001 open in ~20s → trader reconnected at 09:19:56 with account U2…13 / net_liq $27,537.50). Integrity-monitor: 41 / 45 OK + 4 FAIL — 1 transient (systemd:earnings-trader "activating" cache lag, self-resolves next cycle), 2 etfstack-subtree-scope (freshness:etfstack_equity_curve ~8.8d stale; systemd_last_run:etfstack-execute.service exit code 1) deferred to etfstack handoff, 1 main-project (artifact:strategy_analysis_report 350h stale vs 336h threshold, 14h past — would regenerate via cd [hidden] && source .env && export DATABASE_URL && venv/bin/python3 scripts/strategy_performance_analysis.py per memory.md §K). Surfaced for operator decision; not silently regenerated per NO SHORTCUTS. Earlier (May 17 morning): Finding #297 shipped (api_health_checker WARN-but-exit-0 cry-wolf fix) — closes the pre-existing systemd_last_run:polymarket-api-health.service FAIL surfaced during the §G probe of #296. Operator approved option (b) WARN-but-exit-0 via AskQuestion (vs widen-threshold / leave-as-is / build-age-aware-fix). Today's prior op also completed: IBKR Sunday 2FA reconnect handled manually (06:48 UTC restart → port 4001 open in ~16s — faster than May-10's 90s; IBKR allowed session resume without re-2FA → trader reconnected at 06:49:01 with account U2…13 / net_liq $27,534.56). Integrity-monitor: 45 / 45 OK (expected back to baseline after #297 §G probe). Earlier (May 11 evening): Finding #296 (_check_systemd_unit_last_result branch reorder) shipped as the lowest-risk close-out of the 3 May-10 "observed-but-not-actioned" maintenance items; the other 2 (Sunday-2FA partial-automation + Sunday-aware ibkr_snapshot threshold) remain explicitly deferred per AskQuestion. Today's 2FA reconnect data point (operator was awake-and-available; an automation would have put the trader back online ~43 min earlier) recorded for future operator decision on item #1. Project remains in steady-state observation mode for ~9 days until May 26, 2026* when the Phase 3 Strategy Revamp un-gates (Finding #240's 30-day equity-bars window completes). Two new modular subtrees (strategies/etfstack/ LIVE on paper account DU…91 since May 11 14:13 UTC; strategies/pm_earnings_arb/ research-only) are isolated by design and have dedicated handoff prompts.)

Deployed — Sep 27, 2026 (Findings #481/#482: deploy #14 28b572b DEPLOYED Sep 27, 2026 00:33 UTC; deploy #15 860af84 DEPLOYED Sep 27, 2026 02:43 UTC)

Deployed — Sep 26, 2026 (reliability Batch B3 #454/#455 + migration 039 DEPLOYED Sep 26, 2026 23:29 UTC: deploy #13 00607c9)

Deployed — Sep 26, 2026 (#479 sampler-grace race hardening DEPLOYED Sep 26, 2026 22:39 UTC: deploy #12 2ecb02c)

Deployed — Sep 26, 2026 (Finding #467 dashboard defense in depth + #479 sampler restart-grace fix DEPLOYED Sep 26, 2026 22:19 UTC: deploy #11 3138aca)

Deployed — Sep 26, 2026 (Findings #477/#480 DEPLOYED Sep 26, 2026 21:56 UTC: deploy #9 0e985dd)

SELECT count(*) FROM polymarket_opportunity_log WHERE skip_stage='timing' AND skip_reason='timing_channel_leak' AND (
 (id=9381 AND evaluated_at='2026-09-23 14:30:11.403726+00' AND condition_id='0x2ffe6e26095d2a088f6674ce4cfd54d3f862937f07ef069be6908e123d95f92c' AND symbol='GIS'  AND timing_channel='AMC' AND earnings_timing_resolved='BMO' AND earnings_datetime='2026-09-23 20:00:00+00') OR
 (id=9382 AND evaluated_at='2026-09-23 14:30:11.701571+00' AND condition_id='0x43deba5d45a469c11be88dbf6769ce958d107f2b7f5dfe73403603e00b4aac1c' AND symbol='CTAS' AND timing_channel='AMC' AND earnings_timing_resolved='BMO' AND earnings_datetime='2026-09-23 13:00:00+00') OR
 (id=9383 AND evaluated_at='2026-09-23 14:30:11.915809+00' AND condition_id='0x63e296422fbc8aa3cd7b57cc8147c3acd6668bd0804367cd50706411420771bf' AND symbol='PAYX' AND timing_channel='AMC' AND earnings_timing_resolved='BMO' AND earnings_datetime='2026-09-23 13:00:00+00') OR
 (id=9387 AND evaluated_at='2026-09-23 19:50:09.672826+00' AND condition_id='0x2ffe6e26095d2a088f6674ce4cfd54d3f862937f07ef069be6908e123d95f92c' AND symbol='GIS'  AND timing_channel='AMC' AND earnings_timing_resolved='BMO' AND earnings_datetime='2026-09-23 20:00:00+00'));

Deployed — Sep 26, 2026 (Finding #457 credentials, OD8 option (a): repo scrub incl. API secrets DEPLOYED Sep 26, 2026 20:55 UTC, deploy #8 614a29d; doadmin reset DONE Sep 26, 2026 20:49 UTC (#457); trusted sources DONE Sep 26, 2026 16:06 UTC (#457); prod Finnhub key and the other rotations OPEN)

Deployed — Sep 26, 2026 (Finding #478 C-Q2/G-RS count trigger DEPLOYED Sep 26, 2026 20:55 UTC: deploy #8 614a29d)

Deployed — Sep 26, 2026 (reliability follow-ups F-1, F-5, C-Q2 replay: Findings #473/#474/#475 DEPLOYED Sep 26, 2026 19:05 UTC: deploy #7 a695050, with the docs sweep)

Deployed — Sep 26, 2026 (Finding #468 prod checkout hygiene DEPLOYED Sep 26, 2026 15:15 UTC: deploy #5 f130113; prod moves DONE Sep 26, 2026 14:59 UTC (#468))

Deployed — Sep 26, 2026 (reliability Batch A DEPLOYED Sep 26, 2026: deploy #1 884e528 10:18 UTC; deploy #2 3d7f1b1 11:48 UTC)

Finding #476 managed root crontab — IMPLEMENTED-PENDING-DEPLOY

Pre-flight: deploy/remote_deploy.sh runs the fetched installer's --dry-run before the pull and stops on refusal (OPERATIONS §8.F). Refusal is shell-aware (shlex), the install holds an fcntl lock and re-reads the crontab before committing. Retired: gateway_restart_manager.sh (Gateway PID killer, OD5), ibkr_health_monitor.sh, ibkr_restart_warning.sh, the 05:55 duplicate, --critical, and the dead check_trader_health.sh / exit_reminder.sh lines. cron:schedule_audit (dashboard) runs the audit each cycle. - Verify: tests/unit/test_finding_476_managed_crontab.py. After deploy: venv/bin/python3 deploy/install_crontab.py --check prints OK; bash scripts/ibkr_schedule_audit.sh PASS.

Finding #448 senders deliver and prove it — IMPLEMENTED-PENDING-DEPLOY

Finding #446 cluster DB-health — IMPLEMENTED-PENDING-DEPLOY

Finding #447 backup pg_dump full stderr (no truncation) — IMPLEMENTED-PENDING-DEPLOY

Finding #471 high-conviction LATERAL LIMIT 1 (F-10) — IMPLEMENTED-PENDING-DEPLOY

Finding #472 ibkr_reality snapshot cadence — IMPLEMENTED-PENDING-DEPLOY

Finding #456 AutoRestart login watchdog — IMPLEMENTED-PENDING-DEPLOY

Finding #449 integrity alert policy + Telegram last-send — IMPLEMENTED-PENDING-DEPLOY

Reliability Batch B2 (trader alerts, stall, F-6, #450) — IMPLEMENTED-PENDING-DEPLOY on branch reliability/b2

Recently completed — Sep 13, 2026 (Finding #442 earnings-date join + 21-day verifier + DATE SPLIT DEPLOYED Sep 13, 2026)

Recently completed — Aug 22, 2026 (Finding #410 ship-verification letter-audit)

Recently completed — Aug 22, 2026 (Finding #409 DONE-CLAIM + C10/C11 DEPLOYED Aug 22, 2026 15:23:13 UTC SHA defe78f; #408 DEPLOYED Aug 22, 2026 14:49:44 UTC SHA 4e4ffa1)

Recently completed — Aug 22, 2026 (Finding #407 HTTP quota + memory.md §F/§H DEPLOYED Aug 22, 2026 13:39:29 UTC SHA 7fbfe05)

Recently completed — Aug 22, 2026 (Findings #401 #402 #403 #404 #405 #406 DEPLOYED Aug 22, 2026 07:53:22 UTC SHA 88014f4)

Recently completed — Aug 22, 2026 (Findings #393–#400: TIER 0+1 + get_positions fail-closed — #400 DEPLOYED Aug 21, 2026 23:00:26 UTC)

Recently completed — Aug 21, 2026 04:28–04:39 UTC (THE #366/#367 DEPLOY — operator "deploy all")

The full PKG-357 package + Phase-1 transition went LIVE per the deploy checklist (IMPLEMENTATION_PLAN_2026_08.md, now marked EXECUTED): - Window: overnight, 00:28 ET Friday; market closed; trading_positions open = 0 rows; IBKR connected; 0 active breakers; integrity 0 FAIL pre-deploy. Pre-deploy snapshot: /root/deploy_snapshots/pre_366_20260821T042834Z. - Sequence: migrations 031 (pilot_tag) + 032 (v_blend_score) applied BEFORE any restart (an in-flight read-only analytics query held the trading_positions lock ~4 min and was cancelled to let the ALTER through) → prod .env: PDT_RESERVE_CAPITAL 25000→17450, stale SIGNAL_LONG_MAX_THRESHOLD=0.95 fixed to 0.90 (the checklist's flagged hazard, real), PHASE1_START_ET verified absent (deploy day = default) → deploy_from_github.sh pulled bf16b1c → 3465087, cleared pycache, restarted trader/dashboard/collector/live-trades/websocket, ran the close-persist contract check (PASS) and regenerated the report. - Verified live: capital $10,054.38 (NetLiq $27,504.38 − 17,450); A-6 day-P&L anchor from DB at startup; no consecutive-loss init; consecutive_losses key gone from /api/state; integrity 57 rules, 0 FAIL (only the pre-existing storage:orderbook_snapshots WARN); b5_pilot_auto_revert OK; DB↔IBKR reconciliation in sync; Phase-1 pill, "Trades: 0", no Test Progress/Levrage, testing frozen at 193, testing+phase1 ≡ alltime; B-4 ranks + 0.90 cap + reservation card rendering; 25000//10/consecutive-loss greps clean on the prod checkout. - Watch next session: 10:30 ET reservation summary line; 15:50 ET priority=N/3 admission logs; 15:58 equal-split sizing; v_blend_score on new opportunity-log rows; first pilot entry carries pilot_tag + 🧪 badge. - ~~Outstanding~~ → BOTH CLOSED same day (Aug 21, 2026 06:56 UTC deploy — Findings #369/#370): (1) OPS-4 CLOSED (#369) — the nightly ~04:15 UTC (and ~06:06 UTC) trader deaths were self-inflicted watchdog kills: unbounded ib_insync request-response calls (11 reqAllOpenOrders + 3 qualifyContracts + 1 reqContractDetails sites the #354 sweep missed) hung the main loop on the half-dead socket left by the nightly IBKR server reset (~00:13 ET) / IBC 06:00 UTC Gateway AutoRestart; NOT OOM (148 MB peak), NOT the 04:15 apt neighbor (aggravator only — needrestart bounced the Gateway on library updates; now excluded via /etc/needrestart/conf.d/polymarket.conf). All 15 sites bounded with explicit degrade paths; trader:restart_tripwire integrity rule armed (NRestarts>0 = death outside a deploy → Telegram within one 5-min cycle; baseline 0 as of the 06:50 UTC restart — tonight's 04:13/06:00 windows are the live proof and cannot pass silently). (2) OPS-5 CLOSED (#370) — the failure was three stacked holes: the exit-2 was CORRECT (#251b; 16 real issues: 14 unrecoverable pre-March legacy exit-exec gaps + 2 side false positives from 3 legacy SLD rows, since normalized — documented DB repair), but the unit had no EnvironmentFile (Telegram silently disabled) and was never in BATCH_SERVICES (nobody watched it). Fixed end-to-end: EnvironmentFile on both reconcile units + deploy-managed, exec-linkage epoch (2026-03-07) reports legacy gaps as non-fatal, services:systemd_unit_health dynamic failed-unit sweep (which immediately caught + cleared a 6.5-month ghost polymarket-ibc.service failed state on its first cycle), reconcile-full/flex-reconcile last-run + timer rules. First clean full-history run 06:58 UTC (exit 0, 0 mismatches, 14 named legacy gaps); monthly timer re-armed: next fire Sep 1. Integrity after: 63 rules / 62 OK / 0 FAIL / 1 pre-existing WARN.

Recently completed — Aug 21, 2026 (Finding #367: Phase-1 transition encoded — records/dashboard only, NO trading-behavior change; DEPLOYED later the same day, see above)

The operator closed the "TESTING IN PRODUCTION" era (193 trades) and moved the project to Phase 1 trading with a P&L fresh start, effective at the PKG-357 deploy moment. - Single-source boundary (#334 lesson): NEW Config.PHASE1_START_ET (env-overridable, default 2026-08-21 ET; Config.phase1_start() the only parser). Every phase surface derives from it; trades split on entry_time, timezone-correct. Deploy-time step: the deployer SETS/VERIFIES PHASE1_START_ET in prod .env if deploying on any day other than Aug 21, 2026 — checklist in IMPLEMENTATION_PLAN_2026_08.md extended (env step + #367 post-deploy probes). - Dashboard: header pill "TESTING IN PRODUCTION" (yellow) → "Phase 1" (blue badge-phase1); "193/150 trades + progress bar" → "Trades: N" (Phase-1 closed trades from zero, DB COUNT(*) FILTER); Trading Stats "Test Progress" cell REMOVED; System-tab card reworked to "🚀 Phase 1 Trading" (shows the frozen testing-era count); History Trading Capital ROI re-cut to three stacked filter-independent windows — All-time / Testing in Production (start – cutover) / Phase 1 (cutover – ) — via the pure compute_phase_roi_windows + module-level compute_roi_triplet; the meaningless PERIOD day-span ("2,423 days") is gone. "Levrage" typo → "Leverage". - Records: FINDINGS #367; changelog entry #367 (reporting) with chart marker "TESTING IN PRODUCTION (193 trades) → Phase 1"; STRATEGY.md §4.0 retitled (transition CLOSED-historical, §4.1 naming note); OPERATIONS.md mode/services tables; CONTEXT.md; P1_SURFACES census-drift note; risk_manager.py alert-string fossils updated. Testing-era history (milestones, #347 narrative) stays historical; NO data deleted. - Tests: NEW tests/unit/test_finding_367_phase1_transition.py — phase-split ROI (synthetic rows both sides of the boundary incl. the ET/UTC edge), counter-from-zero, frozen-count immutability, testing+phase1≡alltime invariant, single-source tripwire, stale-string tripwires (no "Test Progress"/"Levrage"/x-of-150). Full unit suite green (py3.11).

Recently completed — Aug 20, 2026 late (Finding #366: PKG-GO — the FULL PKG-357 package IMPLEMENTED in code; NOT deployed; production untouched, DB read-only, market open throughout)

The operator gave PKG-GO and the entire #364 ONE-PACKAGE manifest went in the same day: 9 implementation commits in the planned order + this ship-mechanics commit, each implementation commit carrying its own test_finding_366_* file. - What changed (headline per item): A-7 affordability admission + greedy backfill (own commit, shared src/earnings/affordability.py helper in all 4 loops) · C-Q1 capital step (PDT_RESERVE_CAPITAL 25000→17450 across every repo encoding incl. the N14 History-ROI hardcode; prod .env at deploy) · S2c equal-split shrink-to-fit ≤25%/name with the buffer-aware $9k activation gate (S2C_MIN_ACTIVATION_CAPITAL; flat-25 below it) · B-4 3-tier table (3 = LONG 70–75 any mcap, 2 = LONG 80–85 large/mid, 1 = rest, 0 = skip; volume bumps + mcap spine deleted; batch multiplier flattened; /10 surfaces swept) · C-1 SIGNAL_LONG_MAX_THRESHOLD → 0.90 + M-C1 shadow tagging · RES M1+M5 tonight-only observed-set reservation (10:30 ET count, breaker-independent per #363) + rank-3 AMC carve-out replacing the deleted ≥0.95 override twins (A-10) · WAVE-RT (deadline min(45+5×(n−6),90)s, per-batch slippage counting, MSP-1 record-only) · CLB-1+A-6 (3/5/7 pauses + sizing-halving + counter DELETED; daily-loss 5% sole brake, DB-derived, persisted, true-ET resume; tiers −2.5%/−4%) · B-5 pilot (own commit; B5_PILOT_ENABLED; ≤2 concurrent; pilot_tag migration 031; auto-revert ENCODED as integrity rule consistency:b5_pilot_auto_revert; K-B5 re-score 25 fills / Dec 15) · M-VOL V-BLEND shadow (src/earnings/v_blend.py, frozen Aug-16 reference populations, migration 032, zero trading impact). - Ship mechanics: changelog package entry #366 (chart marker) + B-5 entry #366.b5 + supersession flips (#313/#333/#334/#346 SUPERSEDED, #207→#366.b5, #314.f/#335 AMENDED); all dashboard/queue/action-plan/monitor encodings flipped to IMPLEMENTED-PENDING-DEPLOY; FINDINGS #366 + supersession notes on the six affected entries; STRATEGY.md/OPERATIONS.md rewritten in the per-item commits; DEPLOY CHECKLIST added to IMPLEMENTATION_PLAN_2026_08.md (manual prod .env line, migrations 031+032 BEFORE restart, close_persist_contract_check.py on the server, §G probes, market-safe window). - Monitors: M-C1/K-S2C/K-B4/K-RES/K-B5 IMPLEMENTED — arm at the deploy restart; D-6/D-7/D-9 retire at the deploy restart (the live consecutive-loss breaker keeps running pre-#366 code until then).

Recently completed — Aug 20, 2026 (Finding #365: BABA apparent volume-gate breach = display artifact, zero true breaches; A-3 expanded to a 5-part bundle — records only, NO code shipped, production DB read-only)

The operator saw BABA (entered/traded) displaying $930 against the $1,000 volume gate. Deep-dive verdict: display artifact — BABA's admission volume was $2,145.74 at Tue Aug-19 15:50 ET (opportunity log; gate PASS legitimate); today's $930 is a post-close 24h-ROLLING snapshot substituted into the cumulative slot by the dashboard's last-resort fallback (app.py:2260) after BOTH cumulative sources failed. - Root causes (3): (1) sync_markets_from_gamma's limit=500 is silently capped at 100 by the API with no pagination (continuous_collector.py:225–238) — BABA fell off the top-100 in the heavy week, volume_total stale at its Aug-16 value; (2) all three Gamma cumulative fetches omit closed=true → closed markets return EMPTY (volume_policy.fetch_gamma_cumulative_single + dashboard batch + per-market); (3) the price-snapshot fallback substitutes 24h-rolling volume into the cumulative display slot with no marker. - Class audit: 97 trades have admission-time log rows; 93 provably passed the gate; all 43 below-gate-today displays are artifacts (100%); 80% store less volume today than at admission (vs W1's 44% — traded markets evaluate up to resolution). No bypass branches, no unit confusion; PNC/CFG (Apr 14–15) entered under a config-DISABLED gate (by design then). - Plan change: A-3 EXPANDED into a 5-part bundle (the monotonic guard alone would NOT have prevented this): monotonic GREATEST guard on both writers + closed=true on the three Gamma fetches + sync pagination + admission-time volume display semantics (below-gate-NOW info vs below-gate-AT-DECISION alarm) + mandatory stale/rolling marker on the 24h fallback. Priority stays P2 Class A; the class has cost operator attention twice. Recorded across IMPLEMENTATION_PLAN_2026_08.md (P1_SURFACES has no A-3 reference — verified), the revalidation §II.6/A-002 rows, both dashboard encodings, CONTEXT, this handoff, and the implementation-master-plan canvas. Tests green (python3.11).

Recently completed — Aug 19, 2026, latest (Finding #364.b: FUND-1 re-specified as config/code + census-grade re-audit of the package additions + execution-discipline note — records only)

Operator clarification verified correct: the account ALREADY holds the money; the "PDT reserve" is blocked in code by us. - FUND-1 verified + re-specified: IBKR snapshot 19:03:41 UTC / dashboard /api/state 15:05:43 ET both show NetLiquidation $27,465.52, trading capital $2,465.52, reserve 25,000 (prod .env:62, the only operative encoding). Trading capital is computed LIVE as net_liquidation − PDT_RESERVE_CAPITAL at every read (no hardcoded trading-capital constant; nothing counts day trades; nothing moves money). The step: PDT_RESERVE_CAPITAL 25000→17450 → trading capital $10,015.52; PDT floor $25k holds with $2,465.52 margin (equity unaffected by the parameter); S2c gate re-specified buffer-aware ~$9k (satisfied at ship, ≈$1,000 margin). Lockstep: 5 env-default "25000" strings, the app.py:3690 PDT_RESERVE_USD History-ROI hardcode (N14 — env change alone would leave History ROI wrong), 3 fallback literals, 2 Jinja defaults, .env.example, GUARD_REGISTRY add. - Census-grade re-audit (P1_SURFACES): census 148→161 (§1b NEW: 13 alert/notification surfaces — the original was dashboard-only); §11a thin addendum replaced by full §11 (CLB-1+A-6: 5 dashboard + 2 alert surfaces) and §12 (WAVE-RT: 0 dashboard + 2 alert) inventories; §3 FUND-1 rewritten (10 dashboard + 1 alert + 1 code hardcode); MSP-1 confirmed record-only (0 everywhere). Flags N14–N23; material catches beyond N14/N15: NO consecutive-loss counter renders anywhere today (N17 — §11a's "keeps rendering" was wrong), the Edge-Cases second breaker table (N16), dead CONSECUTIVE_LOSS_LIMIT knob (N19), DAILY_LOSS_LIMIT+tiers must join GUARD_REGISTRY (N20), STRATEGY.md L2050 stale "3 stop-exits" (N21). Prod .env carries NO breaker keys (verified — CLB-1 needs no GUARD-OVERRIDE dance). Manifest ↔ surfaces cross-check clean after the FUND-1/C-Q1 rewrites. - Execution discipline (operator instruction): binding note added under the manifest — every package item and every Class A commit follows the full memory.md checklist, no exceptions.

Recently completed — Aug 19, 2026 (Findings #363 + #364: circuit-breaker audit + CLB-1 approved + the ONE-PACKAGE manifest confirmed + the pending-decisions register recorded — records only)

Triggered by the morning's 10:03 ET "3 consecutive losses — 4h pause" firing (TGT/LOW/DY — all three from the same Aug-18 evening entry wave; the pause blocked nothing; the day closed +$37.70). The operator commissioned a full breaker/risk-control audit under the P1 package (#363), then approved its verdict and packed everything into one implementation package (#364). - #363 (analysis + records): package replay (47 wk, §14 harness): the consecutive-loss rule fires 49× with 28 blocking nothing; 70% of 3-loss runs contain ≥2 same-wave losses; money effect is a sign-flip on two outliers (−$823/+$312). The daily-loss 5% brake fires ONCE (the worst day in history), $0 foregone. Full 33-control inventory: 24 SAFE · 6 NEEDS-RETUNE (→ WAVE-RT + CLB-1 riders) · 3 REDESIGN (the counter, its hidden sizing escalation, the reservation-breaker interaction → RES design requirement). New decision CLB-1 (§II.8), monitor D-9 armed, D-4 extended. - #364 (operator decisions recorded): CLB-1 APPROVED — retire the counter (display-only), daily-loss 5% sole brake (true-ET resume + DB-anchored day P&L), sizing halving re-keyed to daily-loss tiers (−2.5%/−4% proposal), A-6 rides; ships inside PKG-357; breaker LIVE + D-9 armed until then. ONE-PACKAGE manifest confirmed (recorded in IMPLEMENTATION_PLAN_2026_08.md; 4 additions the communicated manifest missed: the C-Q1 capital step, MSP-1, D-6/D-7 retirement-at-ship, changelog/supersession ship mechanics). Pending-decisions register recorded with one correction (Class A queue = per-commit NOW work, not post-package). FUND-1 + the explicit PKG-GO are all that remain before the build starts. - Sweep per the #361.b lesson: statuses AND description texts flipped together across FINDINGS, both plan docs, the revalidation doc (Part I re-headed Aug 16–19; §II.8 → APPROVED; App §15 annotated), CONTEXT, this handoff, rb_breaker_audit_report.md, build_revalidation_357()/build_action_queue() rows and both canvases; zero remaining "awaiting operator" markers for CLB-1. Tripwire tests updated (+#364 pins); full unit suite green (python3.11).

Recently completed — Aug 18, 2026 (Finding #362: VIK wrong-day entry — entry-date invariant + #333↔#349 seam closed + integrity rule + reservation dedup + dashboard ET/divergence fixes; VIK left open per operator decision)

The operator reported VIK's Active-Trades row (entered "Aug 17, 7:58 PM") contradicting its own timeline plan (Tuesday entry, Wednesday exit). Investigation verdict: yes, a day early — VIK reports Wed Aug-19 BMO (confirmed #349 consensus); the intended entry was Tue Aug-18 15:58 ET; it filled Mon Aug-17 15:58 ET in HD's batch. ("7:58 PM" was a second bug: raw naive-UTC strings re-read as browser-local.) - Root cause (4 links): stale Polymarket end_time (Tue) pulled VIK into Monday's window via the #333 OR-branch → resolve_schedule_basis correctly built a TUESDAY-entry signal → no guard checks the entry DATE (#359 passes future entries trivially) → run_bmo_cycle scheduled the whole batch at signals[0] (HD's Monday) time. - Class frequency (193 trades): 4 genuine wrong-date entries — CRSP Feb-10 (−$6.04), SBUX Jul-27 (+$8.91 by luck), SBUX Jul-28 (−$0.06, the #349 born-dead), VIK (open). Net realized +$2.81 = luck, not design. ON Feb-9 / NET Feb-10 are NOT wrong entries — post-hoc DB date drift (measurement caveat added to PLAYBOOK §5; rows deliberately NOT repaired). - Shipped (all six fixes): (1) entry_is_for_today guard (wrong_entry_date) at all 4 admission loops before can_open_position + _filter_batch_signals_for_today at batch scheduling (the shared-entry-time assumption is DEAD) + last-line refusal in both batch executors; (2) schedule_within_window re-check after resolve_schedule_basis (schedule_outside_window) — a market resolved outside its selecting window is re-picked on its true day; (3) integrity rule consistency:open_position_release_window (HIGH): open positions' entry day + implied release day re-checked daily against the CURRENT confirmed date; KNOWN_WRONG_ENTRY_POSITIONS allowlists ("VIK", Aug-17) as OK-with-annotation; (4) dedupe_reservation_signals in both multi-day BMO reservation builders (VIK was double-counted under Aug-18 AND Aug-19); (5) dashboard: positions API sends ET times (_format_trade_time_et, §M), JS renders verbatim + "ET" suffix (no browser-local reparse), red WRONG-ENTRY-DAY badge + highlighted entry cell when actual entry day ≠ plan; (6) date-drift assessment only, no data repair. - VIK guard statement: tonight's Tue cycle re-selects VIK (in-window, correct entry day → new guards pass it) and can_open_position rejects it as an existing position — NOT re-entered, NOT force-exited; Wednesday 10:32 ET exit + live stop untouched. - Verification: NEW tests/unit/test_finding_362_wrong_entry_date.py (44 tests: invariant matrix, window semantics, dedup, admission/scheduling/execution tripwires, integrity shapes incl. VIK/HD, dashboard pins); 1058 unit tests green (python3.11; was 1014).

Recently completed — Aug 17, 2026 (Finding #361.b: corrective amendment to #361 — the sweep was INCOMPLETE and the operator caught stale PENDING rows on the LIVE dashboard; full stale-state re-sweep + status-vs-description tripwire + DEPLOY)

Operator: "why is this not updated? what else did you miss?" (the #334 missed-surfaces lesson repeating). Two root causes, both fixed: - #361 was never deployed — the live server still ran the pre-#361 build, so its regenerated strategy_analysis_report.json still carried C-1/RES/B-5 as PENDING-OPERATOR-APPROVAL and the strategy tab rendered the stale [PENDING-OPERATOR-APPROVAL] badges the operator screenshotted. Fixed: deployed (per §H) and verified the live report/tab renders the APPROVED state. - #361's records sweep missed surfaces (its own repo-side text was mostly correct, but not everywhere): docs/CONTEXT.md had NO #359/#360/#361 entries at all (bootstrap doc — a future agent would bootstrap into the pre-decision world); docs/STRATEGY_PLAYBOOK.md's READ-THIS-FIRST banner still listed C-1/RES/B-5 as "OPEN operator decisions"; docs/STRATEGY_REVALIDATION_2026_08.md §10 + the canvas still said "pending deploy / the deploy is yours" for #358 (deployed Aug 16 20:22 UTC — a b7a162c miss); the canvas §11 table labeled C-1 "pending"; STRATEGY.md carried no pointer that the live 0.95 cap has an approved re-close; stale sizing-up/relax-202-9095 PENDING markers in the CONTEXT/HANDOFF historical accretions lacked resolution pointers. All fixed in this commit; every remaining PENDING-OPERATOR-APPROVAL match is either live code vocabulary, the genuinely-open C-Q2/C-Q3, or explicitly-framed historical narrative (full ledger in FINDINGS #361.b). - Tripwire (can't recur): test_finding_357_revalidation.py gained a status-vs-description coupling test class — for EVERY build_revalidation_357() decision row and EVERY build_action_queue() item, a pending/approval-needed marker in the row's TEXT is only allowed when the row's canonical status IS PENDING-OPERATOR-APPROVAL; plus template-source assertions that no line ties C-1/RES/B-5 identifiers to a pending marker. Statuses and descriptions can no longer diverge silently.

Recently completed — Aug 17, 2026 (Finding #361: C-1 + RES + B-5 APPROVED — records only, awaiting implementation; deployed later the same day by #361.b)

Operator decision (Aug 17): APPROVE all three open Part II decisions from the #357 program — C-1 (re-close the LONG conviction cap at 0.90; M-C1 arms at ship), RES (the M1+M5 reservation mechanism: observed-set reservation + rank-3 AMC carve-out, multi-day component dropped; K-RES arms at ship), B-5 (the small-cap LONG 0.80–0.95 pre-registered pilot; K-B5 contract, §12.5 auto-revert terms unchanged). Implementation explicitly does NOT start yet. - New status vocabulary (#360 extension): APPROVED-AWAITING-IMPLEMENTATION — distinct from PENDING-OPERATOR-APPROVAL (no decision), DECIDED-NOT-IMPLEMENTED (the Aug-16 Part I decisions) and IMPLEMENTED (shipped). Wired into all four dashboard.html parallel encodings (AQ_STATUS_META / filter checkbox / summary chip s.approved_awaiting_implementation / sort order) + decStatusColor (checked BEFORE the IMPLEMENTED substring so it can't render green). - docs/STRATEGY_REVALIDATION_2026_08.md: revision table + reading guide, Part I §I.2 decided-table (3 new rows), §I.3, Part II heading + §II.1/§II.2/§II.3 headings and queue rows → APPROVED Aug 17; §II.7 M-C1/K-B5/K-RES parentheticals; Part III B-005/B-006/E-008 action cells + retired-table rows; §13 header note. NEW §II.2.1 — the operator-requested plain-language reservation explainer (verbatim-substance adaptation of the approved text). - build_revalidation_357(): C-1/RES/B-5 decision-record entries → APPROVED-AWAITING-IMPLEMENTATION (Aug 17, 2026); updated/ship_note extended; action_plan FINALIZED — finalized banner + decide_now → decisions_done (all three approvals recorded); implementation queue re-sequenced (deploy step DONE → capital/reserve step → the package on the operator's EXPLICIT go, arming K-S2C/K-B4/K-RES/M-C1(+K-B5) → Class A fixes, A-7 first). - build_action_queue(): reservation-m1m5-357 + smallcap-long-b5-357 → APPROVED-AWAITING-IMPLEMENTATION (+ summary count approved_awaiting_implementation); relax-202-9095 title/next_action → C-1 APPROVED, re-close awaiting implementation; package-10k-s2c-b4-357 SEQUENCE updated (decision set COMPLETE). NEW optional plain_language field on the RES row, rendered as its own block by _renderAQItem. - No strategy_changelog entries (nothing ships — #338 convention). No deploy: the records ride the next operator-triggered deploy. - Tests: tests/unit/test_finding_357_revalidation.py extended for the new status/plan/plain-language encodings; full unit suite green (python3.11).

Recently completed — Aug 16, 2026 late (Finding #360: Strategy-tab decision-state reconciliation — dashboard/records only, NO trading behavior; DEPLOYED Aug 16, 2026 20:22 UTC with #358/#359)

Operator instruction: fully incorporate today's work into the dashboard's Strategy Analysis tab in the established disciplined format, reconciling every old open/postponed item so nothing is lost or duplicated. - build_revalidation_357() now carries the Part I/II decision record (statuses DECIDED-NOT-IMPLEMENTED / APPROVED-IMPLEMENTED (#359…) / IMPLEMENTED (#358…) / PENDING-OPERATOR-APPROVAL / SUPERSEDED), the pdt_constraint red callout, the action_plan (decide_now / implementation_queue / future_work) and the 16-row monitors table mirroring §II.7. Verdict chips updated to 51/9/5/10/5. - build_action_queue() reconciled (every disposition explicit — see FINDINGS #360 item 4 for the full ledger) + 4 new rows: the rank-1 package-10k-s2c-b4-357 (new status DECIDED-NOT-IMPLEMENTED — decisions vs implementations distinct per the #338 convention; changelog entries at SHIP time), rank-2 class-a-fixes-357 (A-7 first), reservation-m1m5-357 + smallcap-long-b5-357 (pending sign-off). - dashboard.html: new status wired into all four parallel encodings (meta/filter/summary-chip/sort — the #334 tripwire); renderer extended with PDT callout + plan lists + monitor table. - Tests: tests/unit/test_finding_357_revalidation.py 17→25, all green; full unit suite green. - DEPLOYED Aug 16, 2026 20:22 UTC — shipped with #358+#359 in a single deploy_from_github.sh run; the deploy's Phase ε.1 regen rebuilt strategy_analysis_report.json in the same run (no timer wait). Verified live: pdt_constraint + action_plan + 16-row monitors in the served report; package-10k-s2c-b4-357 rank-1 DECIDED-NOT-IMPLEMENTED row present.

Recently completed — Aug 16, 2026 late (#357 RE-ISSUED: STRATEGY_REVALIDATION_2026_08.md restructured into Parts I–IV; docs + canvas only, NO code change)

Operator instruction: "re-do the strategy revalidation 357 considering all above points; also structure all findings and proposed fixes cleanly." The document (grown by accretion: original §0–§6 + eight appended deep dives §7–§14) was re-issued under the same filename; no analysis content deleted or compressed. - New structure: Part I decision record (the decided items with evidence one-liners + implementation status; the PDT ≥$25k-equity constraint as the READ-THIS-FIRST callout §I.1; the #358/#359 items (deployed Aug 16, 2026 20:22 UTC); the former §6 discipline notes as §I.4) · Part II open decisions (C-1 recommended CLOSE, M1+M5 reservation, B-5 pilot, dispositions, C-Q2/C-Q3, the 14-row Class A queue with A-4/A-8 marked done, and §II.7 = every armed monitor/trigger/contract in ONE table: KILL, D-1…D-8, M-C1, M-VOL, K-S2C, K-B4, K-B5, K-RES, G-RS) · Part III the register, updated through every deep dive (B-006 HOLDS→WEAKENED per §12 → totals 51/9/5/10/5) + an explicit "retired and superseded" table · Part IV appendices §0–§3 and §7–§14 verbatim, original section numbers deliberately preserved so existing citations ("§14.6", "§5 Class D-1", "§4 B-005") still resolve (header carries the old→new map). - Open decision list for the operator: C-1 (close the 0.90–0.95 LONG band) · the M1+M5 reservation mechanism · B-5 (small-cap LONG 0.80–0.95 pilot) · the volatility/VIX/small-cap-SHORT standing dispositions · C-Q2/C-Q3 · the 12 remaining Class A fixes (A-7 first: S2c prerequisite). - Canvas strategy-revalidation-357.canvas.tsx rebuilt to mirror the parts (PDT callout on top; all existing charts/tables preserved, reordered into the appendix library).

Recently completed — Aug 16, 2026 (Finding #359: stale-signal guard at ADMISSION — operator-approved B-3 shipped; DEPLOYED Aug 16, 2026 20:22 UTC)

The operator approved B-3 from the #357 revalidation queue (docs/STRATEGY_REVALIDATION_2026_08.md, originally §5 — in the re-issued edition the decided B-3 row lives in Part I §I.2). Implemented as Finding #359. - The gap (W6 §5.2): the #349 born-dead class was guarded at signal BUILD/entry (exit_schedule_is_actionable) but not at ADMISSION — the stale SBUX leftover (Jul-28) consumed an admission slot (displacing HUM), entered, and died 15s later. ~1 per 4 weeks observed. - Fix: NEW pure guard entry_schedule_is_admissible (earnings_date_verifier.py) rejects signals whose intended entry_datetime is MORE than Config.STALE_SIGNAL_MAX_ENTRY_LATE_MINUTES (=5) minutes past, wired into all 4 admission loops (orchestrator check_bmo_signals/check_amc_signals + capital_manager §E twins) BEFORE can_open_position. Live-path rejections land in polymarket_opportunity_log (skip_stage admission, reason born_dead_schedule). Knob GUARD_REGISTRY-policed (#356). Aware-ET comparison; prior-evening BMO admissions (entries hours ahead) pass untouched. - Rode along (same code path): W6 Bug B / #357 queue A-4 — _log_exposure_skip passed entry_datetime where earnings_datetime belongs (skip rows back-shifted a day on BMO). Fixed in both spots; the A-4 queue row was deliberately left to its owner (sibling-concurrency rule), FINDINGS #359 is the canonical record. - Verification: NEW tests/unit/test_finding_359_stale_signal_guard.py (19 tests); 995 unit tests green (python3.11). - Deploy: DEPLOYED Aug 16, 2026 20:22 UTC — single operator-triggered deploy_from_github.sh run at efebe45 with #358 + #360. Verified: trader restarted cleanly (NRestarts=0), knob live at 5.0, in GUARD_REGISTRY, integrity clean, zero guard firings post-deploy (market closed — no misfires).

Recently completed — Aug 16, 2026 (Finding #358: A-8 print-density collapse root-caused — 3 collector bugs + 1 detector bug fixed; DEPLOYED Aug 16, 2026 20:22 UTC)

Operator follow-up on #357/W7's "cannot evaluate G-002/3/4, fix A-8 first" verdicts: did we examine the WHOLE detection logic? Answer: W7's framing was a shortcut — beneath the real density collapse sat a data-corruption bug and a detector-side staleness bug. - Root causes: (1) sync_markets_from_gamma never closes markets that leave Gamma's unarchived window → 2,093 'open' rows, 1,288 dead → collection cycle 5–9 min → per-market cadence = cycle time (W7's 49–117 prints/day). (2) WS collector froze its token list at process start (new markets never subscribed) and lived OUTSIDE the deploy pipeline (unit server-only, not in the restart list). (3) WS writer stored raw order-book LEVEL prices of EITHER token (NO uninverted) as yes_price — only 0.7% of Jul-14's 86,671 WS rows match the true bid/ask mid; plus a dedent bug dropped all but the last entry per batch. (4) Detector: _check_polymarket_price used an unwindowed median-of-5 (the #351 Hyatt geometry on the PRIMARY path). - Fixes: stale-open reaper (3-day cutoff, STALE_OPEN_REAP_DAYS); WS token refresh on every (re)connect + 15-min set-change watcher; extract_yes_quotes (YES-oriented bid/ask midpoints, NO books inverted, full batch, dedup); YES-only book persistence; NEW deploy/systemd/polymarket-websocket.service + deploy install/restart; detector fresh-window median (select_fresh_median, 900s/3-print knobs shared with the #351 witness) + live-Gamma fallback with persist=True (fallback reads land in price_snapshots, signature: all four extra columns NULL). - Historical caveat (IMPORTANT for analyses): WS-signature rows (volume_24h/liquidity NULL) are order-level prints, not market prices — unfixable post-hoc (orientation unrecorded). Exclude them; re-check dense-market (JPM/WFC Jul-14 class) adjudications as part of A-1. #336/#339's "noise" characterizations were largely this bug. - Verification: NEW tests/unit/test_finding_358_print_density.py (14 tests); 976 unit tests green (python3.11). - Deploy: DEPLOYED Aug 16, 2026 20:22 UTC. First reaper run closed exactly 1,288 stale rows (open markets 2,093 → 805); WS service got its first repo-managed restart, resubscribed 1,386 tokens and connected; per-market print cadence ~291s pre → ~105s in the first 10 min post (trending down as the 3-day grace window drains).

Recently completed — May 31, 2026 (Finding #320 Phase 4 remainder: Unified Simulator dashboard consolidation — dashboard-only, NO trading change)

Completes the Phase-3-dependent half of Phase 4 (STRATEGY_ANALYSIS_ARCHITECTURE.md §0.7/§0.11.2), un-gated now that Phase 3 (PR 1-5) is complete. Pure frontend (earnings_dashboard/templates/dashboard.html) — zero trading/capital impact. - Why: the Strategy tab was fragmented across 23 sections on 3 different populations (live-traded / universe / Q3-2025-archive), none canonical → operator confusion. Phase 3's simulator is now the canonical engine. - Shipped: NEW 🧪 Unified Simulator umbrella over its arms (live stop-loss = Finding-A; capacity-aware = Phase 3 Capacity/Gate-Retune sweeps; selection-rule research = static regime #318 + depth #319 KEEP cards; universe signal-quality = Universe View folded). Archived Q3-2025 Backtest + BMO/AMC off the tab. Consolidated 5 breakdowns → 2 canonical (removed Live Performance Snapshot). Section Index regrouped; phase banner → "Phase 3 + Phase 4 COMPLETE". - Operator AskQuestion: unify-and-preserve (not literal delete-all-3, which loses unique live-stop-loss + universe-PM-accuracy evidence); static regime/depth cards (not live sweeps — KEEP decisions, depth needs Polygon ~40s); include the breakdowns consolidation. - Honest note: a true Universe View data-refresh is a separate ~4h Polygon job (analyze_universe_view.py) — folded + labeled as the Apr-25 accuracy baseline rather than silently burning quota; current-universe P&L is already covered live by the Capacity-Aware arm. - Verification: both inline <script> blocks pass node --check; Jinja parses; div balance preserved (HEAD −1 / working −1, pre-existing); removed DOM ids referenced only by 3 now-dead never-called render fns (documented; safe follow-up delete). - Deploy: dashboard service restart only; NO trader restart, NO migration, NO integrity change. - Phase status: Phase 3 + Phase 4 COMPLETE. Remaining: Phase 5 (re-baseline STRATEGY.md §9 ROI projections vs simulator-derived) + monitor shipped changes (#311 $1k gate, sizing-up #314.c, SKIP-rule #314.e) at 30+ live trades.

Recently completed — May 31, 2026 (Finding #319 PR 5: Depth-gate v2 widened reconstruction — KEEP; closes Finding #283; Phase 3 simulator PR 1-5 COMPLETE; offline-only, NO production change)

PR 5 of the Phase 3 simulator (design §4.3 / §12). Answers the operator's first-principles question: the depth/spread gate has no execution role (we trade equities on IBKR; Polymarket is signal-only) and is ~98% redundant — has it ever saved us from a bad trade? - Why widen: depth-stage logging only began 2026-04-14 (collapse period) → ~4 unique tradeable kills. The first look (n=4) showed it had blocked 0 losers and only killed winners → looked removable. Small-sample mirage. 659 healthy-period (Oct 2025–Mar 2026) markets were never depth-logged. - What was built (offline-only): scripts/phase3_simulator/depth_gate_variants.py (pure-Python decompose into min-depth/spread/asymmetry factors + remove_all/carveout_real_trade [#283 hyp-#3]/spread_gate_only/depth_gate_only variants; thresholds imported from ConvictionCalculator per §M) + replay_depth_gate_v2.py (replays the REAL conviction code point-in-time over the full 915-market universe; binding-kill filter = gate fired AND clears volume ≥$1k + #202 ≥0.90/small-≥0.80 + #207 small-cap-SHORT [depth fires BEFORE the volume gate]; recovers raw orderbook from orderbook_snapshots + reliability_reason; counterfactual equity P&L via run_counterfactual_for_row, Polygon 1m, mcap stop; per-market, no repeat-eval inflation). - Result (22 binding kills, all usable): 13 losers BLOCKED (−$142.56 avoided), 9 winners KILLED (+$165.09), net removal +$22.54 over ~7 months (≈ +$1/trade = noise). So YES it has saved us from bad trades (reversing n=4). Kill cause: 18/22 snapshot_reliability (hollow 1¢/99¢ book → conviction fell back to price-snapshots that were too thin/volatile: insufficient_samples:1<3 / unstable_window:range>0.35), 1 pure spread, 3 no-orderbook → a low-information-signal filter, not the pure spread gate #283 implied. Variants: remove_all +$22.54 / carveout_real_trade +$22.78 (≈ identical — 21/22 price-snapshot-derived). - DECISION: operator-confirmed KEEP. Removal/carve-out is +$22.54 over 7 months (noise, one trade swings it) while ADDING variance by admitting single-sample / 40–90-pt-unstable convictions — the low-EV/high-variance proliferation the strategy avoids. Production conviction_calculator/signal_generator depth check UNCHANGED. Closes Finding #283 / depth-gate-v2 Action Queue row as KEPT. - Process note: flipped twice (n=4 "remove, only costs us" → conviction-source "the big win was a lucky hollow-midpoint guess, lean carve-out" → widened n=22 "break-even low-info filter, KEEP"). The widening was decisive; the small-sample answer would have been wrong. n=22 still below §K ideal → research-grade KEEP, not gauntlet-PASS (design §12 PR 5 acceptance). - Tests: 368/368 phase3_simulator (+20) + 22 risk_manager + 5 config = 395 PASS. NO integrity change, NO migration, NO deploy. - Phase 3 milestone: #283 was the last open deferred-bundle item (#278 closed by #318, #279 by #311-#317, #283 by #319). The Phase 3 strategy-simulator project (PR 1 foundation → PR 5 depth-gate) is COMPLETE. Remaining forward work is monitoring shipped changes (#311 gate, sizing-up defer-monitor, SKIP-rule candidates) at 30+ live trades.

Recently completed — May 31, 2026 (Finding #318 PR 4: Regime classifier v2 sweep — KEEP compound_v1; closes Finding #278; offline-only, NO production change)

PR 4 of the Phase 3 simulator (design §4.1 / §12). Tests whether a FASTER downtrend detector improves Rule #1 (Skip LONGs in downtrend, #304) vs the production compound classifier (median 31-trading-day lag from SPY peak — Finding #278). - What was built (offline-only): scripts/phase3_simulator/regime_classifiers.py — pure-Python (no pandas/yfinance/DB) point-in-time SpyDailyHistory (MA20/50/200 + EMA20 + 60d-high; prior-completed-bar lookup enforcing the design §11 forward-look-bias mandate) + 6 classifiers (compound_v1 baseline mirroring market_regime_provider._classify; compound_or_dd5 = #278 option B; below_ema20; below_ma50; dd_3pct; compound_symmetric = adds close<MA200 fixing the #278 false-positive asymmetry) + build_regime_variants (Rule #1 skip predicate per classifier; sizing/gate held at production). replay_regime_v2.py — fetches SPY daily bars (yfinance), runs both universes via the existing run_sizing_sweep/apply_sizing_gauntlet engines (candidate-vs-compound_v1 union-delta; scale_pnl_with_size off-equivalent since sizing unchanged). - Design tensions (operator AskQuestion): classifier set = equity family of 6 (VIX-term-structure deferred to Phase 4 per design §15-Q5); replay universe = BOTH (live-only primary + capacity-aware 1.40 cross-check); point-in-time = prior completed daily close (agent decision); gauntlet-floor = full-population spillover is the primary decider (agent decision). - Result (prod, 568-signal universe; SPY 450 daily bars): the fastest detector below_ema20 gains +$70.31 / PASS on live-only (maxDD −$157.66→−$114.92, 5p −$68.47→−$31.51, WR 38.2%→41.0%, downtrend-LONG cell n=44 / −$159 / ex-top-2 −$277) but −$17.02 / FAIL-EX-TOP-2 on the capacity-aware universe at the live 1.40 cap (headline −$5.6/100, ex-top-2 flips +$35; maxDD identical). It labels ~40% of LONGs "downtrend" (vs compound's 17%) → whipsaws out winners on the broader universe, so the live-only win doesn't generalize. compound_or_dd5 (+$0 / +$13.58) + below_ma50 inert/non-robust; dd_3pct + compound_symmetric FAIL. The only durable benefit across both universes is a better worst-week tail (5p −$98.60→−$57.08) at ~breakeven P&L. - Methodology validation: point-in-time (prior-close) compound_v1 reproduces the LIVE recorded regime_at_entry on 95.3% of 106 LONG trades (17 PIT downtrends ⊂ 22 recorded; the gap is the live intraday 3:58 PM read vs the prior completed close — conservative, look-ahead-free). - DECISION: operator-confirmed KEEP compound_v1. Rule #1 optimizes for ROI and faster detection adds no robust ROI (it's a wash on the full opportunity set + whipsaws good LONGs). Also #278 re-test trigger (≥30 live Rule #1 firings) is unmet (~0 since May 28 in the low-volume regime). Closes Finding #278 / regime-detection-v2 Action Queue row as KEPT. Production market_regime_provider.py UNCHANGED. - Tests: 348/348 phase3_simulator (+22: 18 classifier + 4 replay incl. forward-look-bias + #278-lag-reproduction synthetic) + 22 risk_manager + 5 config = 375 PASS. NO integrity change, NO migration, NO deploy.

Recently completed — May 31, 2026 (Finding #317 SHIPPED: simplify daily exposure tiering to uniform 140% cap — operator-CONFIRMED production change)

Operator high-level review: "do we need rare vs busy? are we overcomplicating? maximize ROI not complex rules." Answer + ship: - Change: RiskManager.OPPORTUNITY_EXPOSURE_LIMITS rare tier 60% → 140% (= MAX_DAILY_EXPOSURE); all tiers now UNIFORM; removed _RARE_EXPOSURE_RATIO; get_adjusted_max_exposure docstring updated. 3 buckets kept only for observability logging labels. - Why: Finding #316 showed the rare 60% cap was near-inert (bound 2% of rare days, ~1 trade/36wk); normal==busy already. Simplification, ~zero ROI cost. - Strategic framing (the deeper answer): "more opportunities" ≠ "more ROI" — looser caps / lower gate / smaller sizing all DILUTE ROI (admit low-EV marginal trades, per #314.c/#315/#314.d). The ROI lever is CONCENTRATION (sizing-up #314.c gauntlet-PASS +$4.6/wk at the live cap), not proliferation. #317 is cleanup, not a ROI move; sizing-up stays defer-and-monitor. - Production behavior: rare-day (1-2 opps) cap rises 60% → 140% (affects ~2% of rare days). Normal/busy unchanged. Operator-CONFIRMED + deployed (earnings-trader restarted — class attr is import-time). - Encoding incident: StrReplace corrupted risk_manager.py UTF-8 mid-edit (re-wrote em-dashes/arrows as cp1252; caught when the new test imported the module). Reverted via git checkout + re-applied with a UTF-8-safe Python patch (ASCII-only text). Verified clean before commit. Lesson: avoid StrReplace on files with existing non-ASCII; use a utf-8 read/replace/write script. - Tests: NEW TestUniformExposureCapFinding317; 22/22 test_risk_manager.py PASS.

Recently completed — May 31, 2026 (Finding #316: exposure daily-tiering re-eval — KEEP 60/140/140 + #315 resolution config cleanup)

⏭️ PENDING NEXT SESSION — Priority 5 commit 2 (capacity-aware dashboard section + Top-5 re-rank)

Priority 5 commit 1 (action-queue rows #314.b-e + #315 + Live Config exposure→1.40) SHIPPED + verified live (commit 76dc553). Commit 2 is DEFERRED to a focused next session per operator decision (May 31). Plan locked: - Approach (operator chose A): scripts/strategy_performance_analysis.py should RUN the capacity sweeps at exposure_cap=1.40 during report build (deterministic, always live-accurate; ~10-15s added). The sweep JSONs are regenerated-per-run + not committed, so this avoids stale pinned artifacts. - Scope: new dashboard.html section "🧪 Phase 3 — Capacity-Aware Sweeps" (renders #314.b/c/d/e + #315 at the live 1.40 cap) + JS render fn (mirror renderPhase3SimGateRetune pattern) + a build_phase3_sim_capacity() helper in strategy_performance_analysis.py; mark the existing Phase γ+δ section "superseded (pre-capacity, optimistic +$21/wk)"; re-rank the Top-5 cockpit on the corrected live-cap lift (NOT the +$21/wk projection — the $1k-gate lift is +$6.61/wk at the live cap per #314.b re-run). - Live-cap numbers to surface (all re-run May 31 at exposure_cap=1.40): #314.b $1k gate +$15.61/wk (Δ +$6.61 vs $2k); #314.c flat 25/25/25 +$4.18/wk PASS + 35/25/18 +$4.63/wk PASS (sizing-UP ship-eligible, defer-monitor); #314.d hybrid −$5.24/wk FAIL; #314.e skip-≥$10k +$2.53/wk PASS + combined +$4.28/wk PASS.

🎉 End-of-day session summary — May 10, 2026 (12 commits + IBKR Sunday 2FA)

Morning baseline be60109 carried 4 operator-approved follow-up findings (#289.a, #289.b, #290, #291) queued in docs/FINDINGS.md from the previous session's Dome→Polymarket migration handoff. By end of day:

Time (UTC) Commit Finding Summary
~07:00 785b5f1 — .cursor/memory.md NO-SHORTCUTS cross-link in §A and §G checklists (dangling diff from session-start git_status)
~07:30 a27df6e #292 + #288.v3 Cold-bootstrap of polymarket-trades-backfill.service (operator-decision-D) ran 29-min wall, 152,101 trades saved with new polymarket_data_api_backfill source label. §G probe surfaced two real findings: #292 Polymarket data-api /trades offset hard-cap at 3000 (_OffsetCapReached sentinel + truncated flag in backfill_market's 5-tuple return — graceful end-of-pagination); #288.v3 5-label trade_dedupe.source doc correction (previously-undocumented 'backfill' label with 2.17M rows from Jan 31 bulk import). Timer enabled.
~08:00 2053086 #290 Watchdog dead-code deletion — 5 files (watchdog{,_v3}.py + 3 deploy/.service files; 5th caught by §E re-grep) + 7 stale-reference fixes + 4 historical-context forward-pointer annotations. Decommission loop in deploy_from_github.sh:127-128 still idempotent.
~08:30 eb11136 #291 (RE-SCOPED) Long-running-no-op service defense. Original spec recommended 3 progress:* rules, but live-system analysis showed the named daemons are SOLE writers covered by existing freshness rules. Surfaced via AskQuestion (NO-SHORTCUTS rule); operator chose option (B) state-file mtime defense. New artifact:trades_backfill_state rule + 40-line context comment + deploy/deploy_from_github.sh services-array CHECKLIST + new DATA_INTEGRITY.md §2.5.
~08:45 ebbd953 #291.v2 Doc-count drift correction (34/35 → 37 rules across 6 surfaces — systemd count was stale at 17 vs actual 19 from #269's trades-backfill timer + last_run additions).
~09:00 abf544d #289.a api_health_checker silent-failure → systemd-timer migration (operator option B via AskQuestion). Root cause: /bin/sh is dash on Ubuntu, source is bashism; chained && short-circuited; bug existed silently for 14 days. New polymarket-api-health.{service,timer} with bash -c 'source .env && ...' pattern + 3 new integrity rules. §E bonus: same dash-bug also affected sync_earnings_markets.py cron — fixed via SHELL=/bin/bash at top of root crontab (covers both bugs going forward).
~09:30 6d634b8 #289.b live_trades_collector pg8000 InterfaceError cascade (~118k errors over 10 days in log file, NOT journal as original Finding entry incorrectly claimed). Root cause: try/except INSIDE with engine.begin() block → inner SQL exception swallowed at DEBUG → connection in failed-tx state → COMMIT fails → cascade. PLUS failed trade_id never added to _seen_trade_ids so SAME bad trade re-attempted every 5s. Fix: try/with INVERSION + cascade closure via _seen_trade_ids.add on both success AND error paths + WARNING-level visibility. §G probe: 1 InterfaceError post-restart (was ~4/min pre-fix); 3 distinct WARNING entries surfaced the underlying VARCHAR(10) data-quality issue → became #294.
~09:45 c717f53 #289.b §G Post-deploy §G probe close-out + new follow-up findings surfaced for next batch (#293 Polymarket Website 404, #294 VARCHAR(10) overflow, #295 backfill_trades.py same-pattern).
~12:00 4da2a1a #294 trades.outcome widened VARCHAR(10) → VARCHAR(64), trades.side 10 → 20 (operator chose option B "defense-in-depth" via AskQuestion). Migration 028 + ORM database.py lockstep. Hypertable-safe (PostgreSQL VARCHAR widening is non-rewriting; 907 chunks; ~3min total ALTER time on prod). §G probe clean.
~12:30 e62f10a #293 Polymarket Website URL renamed /finance/earnings → /finance (cached 404 with age 18h+ confirmed long-term). Operator chose option (C) via AskQuestion: fixed api_health_checker.py URL + deleted dead EarningsMarketSyncer class (12 KB; zero callers besides retry test) + removed silently-broken cron entry on prod. Wrapper scripts/sync_earnings_markets.py preserved per Finding #220 manual-sync workflow (uses healthy run_full_sync Gamma path). §G probe: api_health Result=success, polymarket_website status=healthy, 94 finance_markets — daily Telegram noise eliminated.
~13:00 9eee672 #295 backfill_trades.py:save_trades_batch SAVEPOINT-per-trade fix. Operator chose option (A) via AskQuestion: with conn.begin_nested(): per trade matches existing convention in continuous_collector.py:534 + trading_orchestrator.py:1641. Failed trades isolated; outer batch COMMIT succeeds for all valid trades (pre-fix: single bad trade silently rolled back ENTIRE batch). Plus per-trade WARNING logging (was: only first error per batch at ERROR) + failed counter + summary line. Return signature unchanged so all 4 callers work. §G probe: 432/432 markets succeeded, +7 trades, 0 SAVEPOINT-rollback WARNINGs (consistent with #294 already removing today's trigger).
~14:00 (manual) — IBKR Sunday 2FA reconnect. Initial state: ibgateway-ibc.service shown as active but Java window had exited (IBC returned exit status 143 + Gateway finished); port 4001 not listening. systemctl restart ibgateway-ibc spawned fresh Gateway → 2FA prompt appeared → operator approved (likely via IBKR mobile push — "Login has completed" 11s after prompt). Port 4001 came up at 10:00:32 UTC; trader auto-reconnected at 10:01:33 UTC (90s after gateway restart); snapshot refreshed at 10:01:42 with connected: True, error: None. Account U2…13, no positions (correct for Sunday). Integrity-monitor flipped 2 last non-OK rules → all 40/40 OK.

🎯 NO operator-approved follow-ups remaining

All 4 morning-baseline items shipped + 3 newly-discovered side-effect items shipped + IBKR Sunday 2FA handled. Project is in steady-state observation mode.

📋 NO-SHORTCUTS rule was exercised heavily today (4 design surfaces via AskQuestion)

In line with the morning's NO-SHORTCUTS codification, three design tensions were surfaced explicitly to the operator via the AskQuestion tool rather than silently interpreted: 1. #291 RE-SCOPE: original spec recommended progress:* rules; live analysis showed those would duplicate existing freshness coverage. Operator chose (B) state-file mtime defense. 2. #289.a fix path: crontab patch (A) vs full systemd-timer migration (B) vs hybrid (C). Operator chose (B); my recommendation came with 5-point reasoning weighted by long-term observability. 3. #293 fix path: URL fix only (A) vs check-flip-to-warn (B) vs URL fix + delete dead code (C). Operator chose (C). 4. #294 fix path: outcome only (A) vs widen both columns (B) vs input truncation (C). Operator chose (B). 5. #295 fix path: SAVEPOINT-per-trade (A) vs one-tx-per-trade (B) vs defer (C). Operator chose (A).

In every case, the operator picked the deeper / more-thorough option. Pattern: when in doubt, ASK — operator overwhelmingly prefers thoroughness over surgical-minimum.

🔮 Next operator-decision items (per docs/STRATEGY_PLAYBOOK.md §7 deferral bundle)

🏗️ Architectural improvements established today

🔄 Maintenance items observed today (not actioned)

Recently completed — May 31, 2026 (Finding #315: MAX_TOTAL_EXPOSURE_PCT has no live consumer + exposure-cap re-validation)

Surfaced during the Priority 4 docs audit; operator then directed "understand WHY the max-exposure rule exists + re-run the analysis." NO production change.

Recently completed — May 31, 2026 (Finding #314.e PR 3.b.3: 4 deferred SKIP-rule candidates — real-scaled + bias gauntlet — ANALYSIS-ONLY)

Operator Priority 3: evaluate the 4 SKIP-rule candidates deferred in Finding #313 under the capital simulator + bias gauntlet. Offline-only; NOTHING shipped (operator ship-bar = C analysis-only). Originally sized on hypothetical $10k-notional; re-evaluated REAL-SCALED + capacity-aware per the operator's correction.

Recently completed — May 31, 2026 (Finding #314.d PR 3.b.5 SHIPPED: Hybrid seasonal gate variant — KEEP uniform $1k)

Operator Priority 2: evaluate "$2k in peak earnings-weeks / $1k off-peak" vs uniform $1k (Finding #311). Offline-only; NO production change.

Recently completed — May 31, 2026 (Finding #314.c PR 3.b.4 SHIPPED: Sizing variants sweep — KEEP 30/22/15)

Operator Priority 1. Capacity binds at 99% of the $5,000 max-gross cap (Finding #314.b), so the operator hypothesized the 30/22/15 sizing itself is the constraint and lowering it would fit more concurrent positions + lift P&L. This sweep tests that with size-aware P&L + the bias gauntlet.

Recently completed — May 30, 2026 LATE LATE evening (Finding #314.b PR 3.b.2 SHIPPED: Capacity-aware re-validation of Finding #311)

First sweep landing on the capital simulator foundation (PR 3.b.1). Re-validates Finding #311 ($1k gate ship) under realistic capacity constraints.

Recently completed — May 30, 2026 LATE LATE evening (Finding #314 PR 3.b.1 SHIPPED: Capital simulator foundation)

PR 3.b foundation. Per-event timestep simulator that models capacity-constrained admission across overlapping positions — the missing piece for evaluating sizing/SKIP-rule variants under realistic capital constraints (~$2.5k trading capital × 2.0× exposure cap = $5k gross max post Finding #313).

Recently completed — May 30, 2026 LATE LATE evening (Finding #311 Re-validation Note — doc-only, no code change)

Operator-requested re-validation of PR 2 ($1k gate ship) given the new volume-tier evidence in Finding #313.

Recently completed — May 30, 2026 LATE LATE evening (Finding #313 SHIPPED: PR 3.a — Volume-tier priority bump + capital cap raise + NYSE dead-code cleanup)

THIRD real-money strategy ship of the day (after #311 gate ship, #312 v2 #6 + DirectionPolicySpec).

Recently completed — May 30, 2026 LATE evening (Finding #312 SHIPPED: v2 #6 Daily 3-stop session breaker + DirectionPolicySpec doc-bug fix)

SECOND REAL-MONEY STRATEGY SHIP of the day (after #311 gate_uniform_$1000 evening ship).

Recently completed — May 30, 2026 LATE evening (Finding #311 SHIPPED: gate_uniform_$1000 production change)

FIRST REAL-MONEY STRATEGY SHIP since Mar 9, 2026 (Mar 9 stops + LONG-90% skip per Findings #202 + #211).

Recently completed — May 29, 2026 (Finding #310.c — Phase 3 simulator PR 2.c FINAL: gate-retune sweep machinery + dashboard section + admission gauntlet; PR 2 COMPLETE)

Third (FINAL) sub-commit of PR 2 of the Phase 3 simulator project. Per operator AskQuestion incremental-milestones pattern (from #306). NO production behavior change; sweep is offline-only. After this commit, PR 2 is COMPLETE.

Recently completed — May 29, 2026 (Finding #310.b — Phase 3 simulator PR 2.b: counterfactual_admission.py with opp_log loader + per-variant admission filter)

Second sub-commit of PR 2 of the Phase 3 simulator project. Per operator AskQuestion incremental-milestones pattern (from #306). NO production behavior change; offline-only loader + filter.

Recently completed — May 28-29, 2026 (Finding #310 — Seasonality preflight + structural-collapse discovery + PR 2.a declarative scaffolding)

OPERATOR-REQUESTED preflight before any PR 2 code change: "start PR 2 now but you need to consider something very important (check it): volume seasonality with the main earnings seasons vs 'off seasons' periods, that might play a part in the current volume drought, consider and check this as well before drawing any conclusions". Per memory.md NO SHORTCUTS + INFERENCE DISCIPLINE 2× for trading-related observations.

Recently completed — May 28, 2026 LATE evening (Finding #306.c — Phase 3 simulator gauntlet + output + runner.py --run-gauntlet; FIFTH PR-1 ship of the day, THIRD/FINAL sub-commit for #306; PR 1 COMPLETE)

Fifth PR-1 commit today after #307 + #308 + #306.a + #306.b. Closes the multi-week Phase 3 simulator foundation. Adds the bias-gauntlet + deterministic JSON output schema + end-to-end CLI pipeline. All 4 PR 1 acceptance gates now PASS.

Recently completed — May 28, 2026 LATE evening (Finding #306.b — Phase 3 simulator cost_model + replay engine + reference_adapter + cross_check_baseline script; fourth PR-1 ship of the day, second of 3 sub-commits for #306)

Fourth PR-1 commit today after #307 (snapshot_schema conditional-label fix) + #308 (hyp_pnl re-run + structural ceiling) + #306.a (foundation skeleton). Adds the baseline replay path end-to-end via Q1 HYBRID delegation; #306.c will add gauntlet + H-rule self-validation + reproducibility + JSON output + dashboard hook. NO replay/gauntlet variants, NO production behavior change.

Recently completed — May 28, 2026 LATE evening (Finding #306.a — Phase 3 simulator foundation skeleton: package skeleton + RuleSet dataclasses + CLI stub; third PR-1 ship of the day, first of 3 sub-commits for #306)

Third PR-1 commit today after #307 (snapshot_schema conditional-label fix) + #308 (hyp_pnl re-run + structural ceiling). Foundation skeleton intervention only — NO replay, NO gauntlet, NO production behavior change. First of 3 incremental sub-commits per operator AskQuestion Q3 = option B INCREMENTAL MILESTONES.

Recently completed — May 28, 2026 LATE evening (Finding #308 — Phase 3 simulator PR 1 second commit, ops-only: Bug #6 hyp_pnl re-run + structural-ceiling discovery)

Ops-only commit (no code change). Second of three PR 1 ships. Closes the Bug #6 framing from Phase 0 §0.4 with a corrected understanding: the "coverage gap" was structural, not a real gap.

Recently completed — May 28, 2026 LATE evening (Finding #307 — Phase 3 simulator PR 1 first commit: pm_v2 conditional-label fix + integrity rule update in lockstep)

First code-bearing commit of the Phase 3 simulator project (PR 1). Ships the Option A MINIMAL conditional-label fix per design doc §9 + the integrity rule update that the fix necessitates (caught via memory.md §M cross-interface check). Doc-only design-doc updates also folded in to reflect the INFERENCE DISCIPLINE catch from PR 1 §A pre-coding (Bug #2 + #3 are ALREADY FIXED in code since Apr 26 — Findings #234/#237 — so PR 1 active scope is reduced ~30%).

Recently completed — May 28, 2026 evening (Phase 3 unified simulator design doc — docs/PHASE_3_SIMULATOR_DESIGN.md, 867 lines)

Fifth Phase-3-related artifact today (after #279 amendment, JSON refresh, Rule #1 ship, RuleB ship). Doc-only intervention; no code changes. Companion to STRATEGY_ANALYSIS_ARCHITECTURE.md Phase 0 baseline (Apr 26).

Recently completed — May 28, 2026 LATE afternoon (Finding #305 — RuleB ship Phase 1 DRY-RUN: ±90 min no-stop window around earnings release; SECOND Phase-3 strategy rule)

Third Phase-3 commit today after Finding #279 amendment (dc13ff3), strategy report refresh (27501b8), Finding #304 Rule #1 (3f8654d), refresh-post-#304 (6a4cfa2). Code-bearing intervention; hardware-side execution change (cancel + re-place hardware STP-LMT during ±90min earnings window) — but Phase 1 ships in DRY-RUN mode + RULEB_ENABLED=false default, so production behavior is UNCHANGED until operator explicitly enables.

Recently completed — May 28, 2026 afternoon (Finding #304 — Queue Rule #1 ship: Skip LONGs in downtrend regime; FIRST Phase-3 strategy rule)

Second piece of Phase 3 Strategy Review work landed today, on top of the Finding #279 Phase B amendment commit (dc13ff3) + dashboard-refresh commit (27501b8). Code-bearing intervention; bias-gauntlet PASS; first selection-rule change in the Action Queue since Mar 9, 2026 (Finding #202 LONG-≥90 % skip). No live-trading change beyond the new skip semantics; no integrity-monitor rule change; no DB migration.

Recently completed — May 28, 2026 afternoon (Finding #279 Phase B amendment — PM earnings-market volume collapse evidence folded in; data-gathering pass for Phase 3 strategy review)

First piece of Phase 3 Strategy Review work landed today, ~50 hours after the May 26 Phase 3 un-gate. Doc-only intervention; no code, no live-trading change, no integrity-rule change.

Recently completed — May 28, 2026 LATE-LATE morning (Finding #303 — deploy_from_github.sh pre-pull hygiene)

Operator-directed fix for the recurring prod-git operational-hygiene problem surfaced in Finding #302's deploy (where 3 successive git pull --ff-only aborts forced fallback to memory.md §H scp single-file hotfix). Operator-approved via AskQuestion: divergent-handling = stash_continue (preserve operator work, don't block deploy, no Telegram alert), scope = script_only.

Recently completed — May 28, 2026 LATE morning (Finding #302 — polymarket-backup.timer collision with apt-daily-upgrade.timer)

Operator-directed root-cause investigation of the systemd_last_run:polymarket-backup.service STATUS_FAIL surfaced via the dashboard banner earlier today. Resolved same session via operator-approved fix-path (A) "move backup timer to 05:00 UTC" through AskQuestion.

Recently completed — May 28, 2026 morning (memory.md 🚨 INFERENCE DISCIPLINE rule codified + IBKR auto-recovery from mid-week disconnect)

Two distinct things landed today:

1. New .cursor/memory.md rule: 🚨 INFERENCE DISCIPLINE — VERIFY MODEL BEFORE DECLARING

Triggered by an agent false-alarm earlier today. Operator asked the agent to do an end-to-end readiness check on yesterday's AMC trades (May 27); agent traced position-sizing + priority + exposure-cap code thoroughly but did NOT trace the exit-scheduling code path. As a result, the agent reported AMC as a "~3.5h same-day hold" when the actual strategy is a ~29h next-trading-day-exit hold (Day-1 morning entry → Day-2 afternoon exit, spanning the after-close earnings announcement).

When the agent investigated today's open HPQ position (entered yesterday morning, scheduled to exit today 19:57 UTC per the correct AMC scheme), it declared the scheduled_exit_time = 2026-05-28 19:57 UTC to be a "+24h scheduling bug" and surfaced it as URGENT capital-risk to the operator — proposing "manually flat HPQ now" as a remediation option. Operator caught the error instantly: "why do you think AMC trades are not overnight? this is a major thinking error on your side, AMC trades are opened on day 1 early in the session and closed on day 2 late in the session as per our trading scheme, so there is no error!"

Code confirms operator: src/earnings/trading_orchestrator.py:17 file-level docstring literally says "AMC Exits: Execute at 3:57 PM (next day)"; _calculate_scheduled_exit_time_from_db at lines 1358-1360 explicitly calls self._market_calendar.get_next_trading_day(entry_time). The agent had every opportunity to see this and missed it on the first pass.

Root cause: agent built conclusions on an unverified mental model and propagated to URGENT escalation without ever reading the relevant code. Same logical-trap shape as the May-17 2FA inference error (treating absence of evidence as evidence of absence), but applied to investigation instead of inference about hidden human action. Second instance in 12 days of this category of error → operator approved codifying as a permanent project rule.

What shipped: - .cursor/memory.md: new 🚨 INFERENCE DISCIPLINE — VERIFY MODEL BEFORE DECLARING section between NO SHORTCUTS and TRADE EXECUTION PROHIBITION. Includes: (a) the rule statement + 3-step verification process, (b) both anchoring incidents in narrative detail (May 17 + May 28), (c) common-shape analysis identifying the unverified-mental-model pattern, (d) 6-item self-audit checklist to run before any anomaly declaration, (e) why-it-matters-for-this-project rationale. - .cursor/memory.md: §A pre-coding checklist gets a single-line cross-reference to the new section (peer to the existing NO SHORTCUTS pointer). - Scope decided via AskQuestion: operator chose medium scope (memory.md + this handoff entry, NOT full-cascade to CONTEXT.md + STRATEGY_PLAYBOOK.md like NO SHORTCUTS received) and single-banner visual weight (🚨 prefix, not the triple-banner gravitas of NO SHORTCUTS). - Doc-only commit; no prod deploy needed.

2. IBKR live mid-week auto-recovery (separate from rule codification)

At 04:12:00 UTC today, IBKR live Gateway dropped the trader's session (Peer closed connection); Gateway exited cleanly (status=0/SUCCESS). Two notable aspects: (a) timing did not match the documented patterns — neither Sunday weekly auth (today is Thursday) nor the daily 06:00 UTC internal-restart window (this was ~1h 50min earlier than that). May indicate an IBKR-side off-schedule push or a network blip. (b) systemd auto-restart attempt #2 (counter=1) brought port 4001 back at 04:17:16 UTC, and the trader auto-reconnected at 04:18:17 UTC without any operator intervention — total downtime 6m 17s. Trader correctly blocked all trading operations during the disconnect window (hardware stops remained active per design). Post-recovery: position reconciliation completed, "✅ IBKR RECONNECTED and verified healthy" alert fired. HPQ position (22 shares @ $24.65 avgCost, stop-loss order 1044546 GTC) survived intact through the disconnect with no exposure change.

Observability gap surfaced (NOT actioned today — for future operator decision): the integrity-monitor's artifact:ibkr_snapshot rule reports STATUS_OK during the disconnect because the trader keeps writing the snapshot file (just with connected: false). The rule checks file FRESHNESS, not file CONTENT. A future rule reading snapshot.connected == True would have caught the disconnect proactively — possible Finding #299 if scoped.

Files (this commit): - .cursor/memory.md (new INFERENCE DISCIPLINE section + §A cross-reference) - docs/AGENT_HANDOFF.md §11 (this block + last-updated header)

Recently completed — May 24, 2026 morning (IBKR Sunday 2FA reconnect — operator-directed)

Second-week-in-a-row standard Sunday 2FA workflow per IBKR_CONTINUITY.md + handoff Decision 13. Operator-requested via "reconnect ibkr now".

Recently completed — May 17, 2026 morning (Finding #297 — api_health_checker WARN-but-exit-0 cry-wolf fix)

Operator-directed standalone close-out of the pre-existing systemd_last_run:polymarket-api-health.service FAIL that surfaced during the §G post-deploy probe of Finding #296 on May 11. Operator-approved fix-path (b) "WARN-but-exit-0" via AskQuestion (vs (a) widen threshold / (c) leave-as-is / (d) build age-aware structural fix).

Recently completed — May 17, 2026 morning (IBKR Sunday 2FA reconnect — operator-directed)

Operator-requested manual reconnect via the standard weekly Sunday 2FA workflow (per IBKR_CONTINUITY.md + original handoff Decision 13).

Recently completed — May 11, 2026 evening (Finding #296 — _check_systemd_unit_last_result branch reorder — closes 1/3 of the May-10 maintenance items)

Standalone close-out of the lowest-risk item from the 3-item "observed-but-not-actioned" list at the bottom of the May-10 end-of-day session summary. Operator approved option (A) "ship #2 only" via AskQuestion (vs B: ship #1+#2 / C: ship all three / D: defer all three / E: different).

Recently completed — May 10, 2026 LATE morning (Finding #289.b — live_trades_collector pg8000 InterfaceError cascade FIXED via try/with inversion)

Final item in operator-decided plan-(C) sequence (#290 → #291 → #289.a → #289.b).

🎉 Plan-(C) sequence COMPLETE

All 4 operator-approved follow-up findings from session-start handoff (#289.a + #289.b + #290 + #291) shipped today in this session. Plus the bonus cold-bootstrap close-out work (#292 + #288.v3 + #291.v2). Total 7 commits today; net delta of significant cleanup + structural defense + 5 new integrity rules.

Next operator-decision items remaining on the project (per docs/STRATEGY_PLAYBOOK.md §7 deferral bundle): - Phase 3 Strategy Revamp (gated to ~May 26, 2026 when Finding #240 30-day equity-bars window completes) - May 26 deferral bundle: #278 (regime detection v2), #279 + #285 Phase B (sizing + priority + exposure-cap re-eval), #283 (depth-gate v2), #273 (pm_v2 NULL bid/spread on signal_emitted rows — fix in lockstep with simulator ship) - NEW (surfaced today as side-effect findings — not in original handoff scope): - ✅ Polymarket Website API earnings JSON 404 → RESOLVED via Finding #293 — operator-approved option (C) "URL fix + delete dead code". Investigation surfaced the route was renamed /finance/earnings → /finance (cached 404 with age 18h+ confirmed long-term, not transient); JSON URL /finance.json (without /earnings) returns 200 with same shape. Fixed api_health_checker.py URL + deleted dead EarningsMarketSyncer class (zero callers besides retry test) + removed silently-broken cron entry on prod. Wrapper scripts/sync_earnings_markets.py preserved per Finding #220 manual-sync workflow. §G probe verified polymarket_website check now STATUS_OK; systemd_last_run:polymarket-api-health.service flipped FAIL → OK; daily Telegram noise eliminated. - ✅ trades.outcome / trades.side VARCHAR(10) too narrow → RESOLVED via Finding #294 — operator-approved option (B) widen BOTH columns. Migration 028 shipped: outcome 10→64, side 10→20. Hypertable-safe (PostgreSQL VARCHAR widening is non-rewriting; 907 chunks, no chunk rewrite needed). ORM database.py updated in lockstep. §G probe verified no service disruption + 60 live_poll writes/10min continuing healthy + 0 new InterfaceErrors. - ✅ backfill_trades.py:save_trades_batch SAVEPOINT-per-trade fix → RESOLVED via Finding #295 — operator-approved option (A). Restructured save loop to use with conn.begin_nested(): SAVEPOINT per trade (matches existing convention in continuous_collector.py:534 + trading_orchestrator.py:1641). Failed trades isolated via SAVEPOINT rollback; outer transaction stays healthy → end-of-batch COMMIT succeeds for all valid trades (pre-fix: single bad trade silently rolled back ENTIRE batch). Plus per-trade WARNING-level logging (was: only first error per batch at ERROR) + failed counter + summary log. Return signature unchanged so all 4 callers work without changes. §G probe: AST clean, manual systemctl start polymarket-trades-backfill.service runs cleanly post-deploy.

Recently completed — May 10, 2026 LATE morning (Finding #289.a — api_health_checker cron-silent-failure → systemd-timer migration)

Third item in operator-decided plan-(C) sequence. Operator approved fix-path (B) systemd-timer migration after AskQuestion surfaced trade-offs vs (A) crontab patch.

Recently completed — May 10, 2026 LATE morning (Finding #291 — Long-running-no-op service defense, RE-SCOPED)

Second item in operator-decided plan-(C) sequence. Originally documented spec recommended 3 new progress:* rules (polymarket-live-trades, polymarket-collector, polymarket-websocket) attributing writes to specific services via DB-query patterns. After reading integrity_monitor.py end-to-end and verifying the live system, all 3 named daemons are the SOLE writers of their respective tables in the current architecture (CLOB trades disabled per ENABLE_CLOB_TRADES=false; the Dome-era redundant-writer pattern doesn't apply). So the existing freshness:* rules already alarm 10-60 min after any of these would silently die. The proposed progress:* rules would have duplicated coverage with attribution noise on illiquid days.

Per memory.md NO SHORTCUTS rule, the design tension was surfaced explicitly to the operator (via AskQuestion) rather than silently interpreted. Operator chose option (B): re-scope to actual blind-spot closure via state-file mtime defense + documentation + deploy-checklist, skip the progress:* rules.

Recently completed — May 10, 2026 LATE morning (Finding #290 — Watchdog dead-code deletion + same-pattern stale-reference cleanup)

First item in operator-decided plan-(C) sequence (#290 → #291 → #289.a → #289.b) following the cold-bootstrap close-out earlier this morning. Pure cleanup; closes the §C "left for future deletion" footnote that originated in #270 May 9 evening and was reaffirmed in #289.v2.

Recently completed — May 10, 2026 LATE morning (cold-bootstrap §G probe close-out: Finding #292 + #288.v3)

Operator chose Plan D from session-start (cold bootstrap before any new code), with go-decision (B) on the offset-cap finding mid-session (fix-now rather than defer). Both shipped in this commit chain.

🎯 Next-session pending work (4 items, operator-approved May 10 at handoff)

These were surfaced in the May 10 self-audit at end of session and explicitly approved by the operator for next-session ship. Order of recommended execution (smallest-blast-radius first):

# Finding Scope Est. effort
1 ✅ #290 — Watchdog dead-code deletion (RESOLVED May 10, 2026 LATE morning) Deleted 5 files (watchdog.py 22 KB, watchdog_v3.py 31 KB, 3 deploy/.service files — deploy/systemd/polymarket-watchdog.service was a 5th duplicate caught by §E re-grep); fixed 7 stale references (setup-digitalocean.sh, install_services.sh, 2 test files, DATA_INTEGRITY.md §6.1+§7.1+Appendix); added forward-pointers in 4 historical-context refs (FINDINGS #270 + #289.v2 + AGENT_HANDOFF §11 + this row). Decommission loop in deploy_from_github.sh:127-128 still idempotent post-deletion. Out-of-scope per §C: polymarket_sniper/watchdog.py (different sub-project process-monitor — left alone). DONE
2 ✅ #291 — Long-running-no-op service integrity defense (RESOLVED May 10, 2026 LATE morning — RE-SCOPED from original spec) Re-scoped from "3 progress:* rules + helper" to "1 state-file mtime rule + deploy-checklist + documentation" after live-system analysis showed the named daemons are SOLE writers (existing freshness rules already cover them). New artifact:trades_backfill_state rule (9-day threshold, HIGH severity) + 40-line context comment in _build_rules() + CHECKLIST in deploy/deploy_from_github.sh:services + new §2.5 in DATA_INTEGRITY.md. Total integrity rules now 35 (was 34). DONE
3 ✅ #289.a — api_health_checker silent-failure → systemd-timer migration (RESOLVED May 10, 2026 LATE morning — operator-approved option (B)) Root cause confirmed via reproduction: /bin/sh: source: not found (Ubuntu's /bin/sh is dash, source is bashism, chained && short-circuits, >> log redirect never runs). Migrated to polymarket-api-health.{service,timer} with bash -c 'source .env && python -m' ExecStart. Daily 08:00 UTC + 5-min RandomizedDelay. New 3 integrity rules (systemd_timer: + systemd_last_run: + artifact:api_health_latest_json). §E bonus: same dash-bug also affected sync_earnings_markets.py cron — fixed via SHELL=/bin/bash at top of crontab. Old cron line removed manually post-deploy. Pre-existing Polymarket Website API 404 surfaced for operator awareness — was hidden by the cron silence. DONE
4 ✅ #289.b — live_trades_collector pg8000 failed-tx cascade (RESOLVED May 10, 2026 LATE morning) Root cause confirmed via code review: inner try/except was INSIDE with engine.begin() block; on inner SQL exception the catch swallowed it at DEBUG level (invisible), connection went into failed-tx state, engine.begin's exit tried to COMMIT, pg8000 raised InterfaceError on the COMMIT, outer Cycle error caught it. PLUS the failed trade_id was never added to _seen_trade_ids so the SAME bad trade re-fetched + re-attempted every 5s — 12-13k InterfaceErrors/day for 10+ days. (Original Finding entry's claim "errors in journalctl" was wrong — they're in /var/log/polymarket/live_trades.log because StandardOutput=append:.) Fix: try/with INVERSION (try wraps with) + WARNING-level visibility + cascade closure via _seen_trade_ids.add(trade_id) on error path. DONE

Total estimated effort: ~3-4 hours for all 4 items. Smaller subset: ~1 hour for items 1-2 (the cleanups).

Each finding has its own canonical entry in docs/FINDINGS.md per memory.md §J, including §B 5-step plan + §E same-pattern check guidance + §G acceptance probes for whoever picks them up.

🚨 NO SHORTCUTS — EVER (top-of-doc reminder for any agent picking up this project)

If you're a new agent reading this section to onboard, the most important thing to know about this project is: NO SHORTCUTS, EVER. This is a live trading bot with real capital; shortcuts in docs/code leak into operator confusion, runbook drift, silent tech debt, and — at the limit — into trading-logic mistakes that cost real money.

Anchoring example (May 10, 2026 — Dome→Polymarket migration Tier C): during the migration's doc cleanup phase, the agent took a "banner-only + surgical update of highest-impact lines" shortcut on DATA_ARCHITECTURE.md (45 inline Dome refs) and OPERATIONS.md (24 refs) instead of doing the full pass the operator had explicitly asked for. The operator caught it and said: "the shortcuts are extremely disturbing and make me question everything you did before! we can never take shortcuts for this project!!!! huge risk to capital. ensure this is clearly saved in all handovers and never repeated! if there is one thing that you must always know about this project is this: no shortcuts! ever!"

The shortcut was fixed same-day in Finding #289.v2, but the rule is now codified in: - .cursor/memory.md dedicated "🚨🚨🚨 NO SHORTCUTS — EVER 🚨🚨🚨" section (gravity equivalent to TRADE EXECUTION PROHIBITION) + §A pre-coding checklist + §G post-fix verification checklist - docs/CONTEXT.md "MAIN DIRECTIVES (NON-NEGOTIABLE)" §0 (ABOVE every other directive) - This section (docs/AGENT_HANDOFF.md §11) - docs/STRATEGY_PLAYBOOK.md §0 (above the verdict)

Read the full rule in .cursor/memory.md before any work. The TL;DR is: no banner-only doc updates, no "several refs" without an exact grep count, no §E same-pattern check that isn't exhaustive across active code + docs, no "I'll fix it in a follow-up" without explicit operator approval, no "highest-impact only" interpretation when the user said "all." Train the discipline on the small stuff (doc cleanup, comment updates) so it's automatic on the big stuff (trading logic, sizing, stops, signals).

If tempted: STOP and ASK the operator.

What changed in the last working session. Update this section every time a major piece of work lands so the next agent / operator can self-onboard from this doc alone.

Recently completed — May 10, 2026 morning (Finding #287 — Dome EOL Apr 28, 2026 Tier A response)

Operator screenshot of docs.polymarket.com revealed: "Dome has been acquired by Polymarket. As such, all Dome APIs will reach end of life on April 28th, 2026." Yesterday's HTTP 000 / 504 timeouts on api.domeapi.io (Findings #269.v2 + #269.v3) weren't a transient outage — Dome has been permanently dead for 12 days.

Operator commissioned a comprehensive investigation (Q1-Q7 covering live state of every Dome consumer, replacement-endpoint inventory, data-loss assessment, hidden-bug audit) followed by a Tier A → B → C migration plan.

Tier A shipped this commit (Finding #287): - Stopped + disabled 3 dead services on prod that had been silently failing for 12 days: polymarket-orderbook-backfill (no-op cycling with frozen state file), polymarket-prices-backfill (Gamma endpoint not in new docs, "saves 0 prices"), polymarket-dome-prices-backfill (OOM-killed daily — disabled but Restart=on-failure cycling). - Archived 3 state files on prod (preserved for forensic / Phase 3 simulator review). Deleted 3 systemd unit files from repo. Removed those services from deploy_from_github.sh:services array with a multi-line comment block citing #287 so the next agent knows what was removed and why. - Manual rm of unit files from /etc/systemd/system/ per §H fallback (deploy script doesn't auto-clean removed units). - Zero data loss. Verified: trades / orderbook_snapshots / price_snapshots all written within 0-2 min of probe. The 4 dead services produced no useful data; redundant Polymarket-native consumers (polymarket-collector, polymarket-live-trades, polymarket-websocket) cover all the surfaces. - New §E lesson logged: "long-running no-op service" blind spot — service can be enabled+active+running but doing zero useful work without any alert firing. Existing freshness rules (Finding #258) catch real data impact but not wasted-compute / silent-API-death scenarios. Future long-running daemon collectors should add forward-progress assertions.

Tier B (Finding #288) — SHIPPED same session (May 10 morning): - Migrated backfill_trades.py from Dome /polymarket/orders → Polymarket data-api/trades?market=<cid>. Replaced DomeAPIClient with requests.Session + new _fetch_trades_page helper. Rate-limit gap dropped 1.2s → 0.05s (free tier 200 req/10s = 20 q/s sustained, 20× faster than Dome). - Cold bootstrap estimate ~3h → ~8 min wall time. Warm weekly delta ~18 min → ~1-2 min. - Preserved all smart Option A logic verbatim (T1 delta — now client-side filter on MAX(time) - 1h overlap since Polymarket /trades has no server-side start_time; T2 smart re-fetch; T3 earnings-only; circuit breaker; CLI flags; success-flag handling; exit non-zero on circuit-break). - Field mapping reworked: tx_hash→transactionHash, user→proxyWallet, condition_id→conditionId, shares_normalized→size, token_label →outcome. New asset (token_id) field included in trade_id for uniqueness on multi-fill same-tx orders. trade_dedupe.source label changed from 'dome_backfill' → 'polymarket_data_api_backfill' for forensic distinguishing of pre/post-migration data. - Removed dome_client._make_request + prices_backfill_service._get_with_retry retry tests from verify_retry_backoff_and_block.py CI script (modules to be deleted in Tier C). - Operator decision pending (deferred to after Tier C): when to trigger cold bootstrap. Likely after Tier C ships to avoid mid-migration noise.

Tier C (Finding #289) — SHIPPED same session (May 10 morning): - Deleted 8 Dome-dependent files (1 more than originally planned — prices_backfill_service.py added to deletion list because Q4 investigation showed its Gamma /markets/{cid}/prices endpoint is not in the new Polymarket API docs and was logging "Saved: 0 prices" cycling): dome_client.py, main_collector.py, backfill_earnings_trades.py, backfill_candlesticks.py, backfill_prices.py, dome_prices_backfill.py, orderbook_backfill_service.py, prices_backfill_service.py. - Cleaned src/data_tracker/collectors/__init__.py — removed imports of DomeAPIClient + DataCollector. __all__ now ['PolymarketClient']. - Cosmetic cleanup of live_trades_collector.py: DOME_URL → POLYMARKET_DATA_API, fetch_trades_from_dome() → fetch_trades_from_polymarket(), source='dome' label → 'live_poll', create_trade_id prefix dome_ → lp_. Header docstring rewritten. No functional change — service was always hitting data-api.polymarket.com, just with misleading naming. - Cleaned earnings_dashboard/app.py — removed dead prices_backfill_service and orderbook_backfill_service process-running checks. Stats keys still set to defaults so dashboard template doesn't KeyError. - Cleaned gap_monitor.py:check_backfill_processes() — removed backfill_candlesticks check (file deleted); backfill_trades check kept but description updated to note it's now timer-driven. - Updated 6 docs: env.example.txt (removed DOME_API_KEY), README.md (3 inline refs), docs/DATA_INTEGRITY.md (§3.1 + §3.2), docs/API_REFERENCE.md (TOC + §4 fully rewritten + §5 + §6), docs/OPERATIONS.md (services diagram + trades-backfill runbook + data sources table + backfill status + appendix + curl health checks), docs/DATA_ARCHITECTURE.md (top-of-file banner with full migration table — body left intact as historical context). - (CI test cleanup verify_retry_backoff_and_block.py was already done preemptively in #288 — removed dome_client._make_request and prices_backfill_service._get_with_retry retry tests.)

Open follow-ups from #289 (separate sub-findings, NOT blocking): - #289.a — api_health_checker cron silent-failure: log file 0 bytes since Apr 27; cron fires daily but produces no output. Likely cron-env / .env-not-sourced / module-import issue. Manual run produces output. - #289.b — live_trades_collector pg8000 failed-tx cascade: InterfaceError: in failed transaction block every 5s. Service still functional (3,474 trades/24h written) but emits constant error noise. Likely a DB error in a prior cycle leaves connection in failed state without explicit ROLLBACK.

Final command (post-deploy): DOME_API_KEY removed from prod [hidden]/.env — done as a separate single-command operation after Tier C deploy verifies clean.

Recently completed — May 9, 2026 evening (pre-Phase-3 housekeeping batch — Finding #270 RESOLVED across 5 files via §E expansion)

Operator commissioned a housekeeping batch to use the ~17 days until Phase 3 (May 26) productively. Plan: address the 4 deferred operator-decision findings (#269, #270, #273, #274) one at a time per memory.md §A-§N, with each finding shipped as its own commit + push + deploy + §G live probe. Finding #270 was the first one shipped.

Recently completed — May 7-9, 2026 (Finding #285 Phase A: greedy-fill + opportunity_log emission)

Operator pushback May 7 on TTD + MCHP being silently rejected at the exposure cap. Investigation surfaced 3 distinct problems and the resulting fix took 2 commits over the day with full memory.md compliance audit at the end.

Earlier May 6, 2026 — P&L Summary enrichment (Finding #284)

Earlier Apr 28-30, 2026 — Multiple deferrals + UX improvements

Earlier Apr 27 LATE evening (rigorous §B re-audit + Finding #272 RCA)

After the 6-phase Database Integrity Audit + dashboard 500 chain, operator requested a rigorous §B 5-step audit of every code commit in the last 4 days, then asked for the volume_24h_at_entry regression to be fixed. Both delivered in one session.

Five options scoped (see chat transcript for full evidence + trade-off matrix). Operator chose Option A (status quo) for reliability preservation — the current yfinance → Polygon → Finnhub chain is reliability-conservative (yfinance free first, Polygon paid only when yfinance fails, Finnhub last-resort tail). Ship-readiness rationale: - The widget is already visible at T+10s via DB-bootstrap (Finding #249 fix B). The 9-min full-enrichment doesn't block operator visibility. - Reliability has multiple independent fallbacks today — Polygon outage degrades to yfinance, which degrades to Finnhub. Flipping the chain order or making Polygon hot-path-required would reduce that depth. - 98.4% Polygon hit rate is already excellent; further speedups would deliver minutes of saved time per restart but add concurrency or DB schema surface area.

Deferred options for future review (do not re-propose without re-running the live tier-split probe to check if the picture has changed): - Option B (low-risk speedup): extend _polygon_get_market_cap to also cache primary_exchange + name from the same response → eliminates ~250 separate yfinance round-trips per cold start. Estimated saving 30-90s. Zero reliability impact. - Option D (high-value, schema change): persist Polygon-resolved market_cap_billions to a new column on earnings_markets so DB-bootstrap reads it instantly — cold-start drops to T+30s for full enrichment. Requires migration + writer-placement decision. - Option C (chain-order flip — Polygon first): NOT recommended without Option D's DB safety net. Makes Polygon a hot-path dependency without offline cache fallback. - Option E (parallelize Polygon calls): NOT recommended — adds concurrency surface area for ~5 min of restart-only savings.

Lesson logged for the agent: §B Step 5 (acceptance test) and §G (post-fix verification) failed on my original #265 fix — I never opened the actual VZ row in the API response post-deploy. Going forward, every fix MUST end with a probe of the changed output (curl /api/..., journalctl tail, psql SELECT) — not just import/syntax/pattern checks.

Earlier completed (same Apr 27 session) — Database Integrity Audit + Dashboard Rebuild (6 phases, 12 Findings)

Comprehensive audit and rebuild of the data-recording surface area shipped in one session Apr 27, 2026. Operator decisions: Q1=fresh build (Phase 3), Q2=defer retention (Phase 5), Q3=threshold-based (Phase 1).

Phase 0 — Audit baseline (Finding #250): comprehensive read-only audit via four parallel subagents produced docs/DATABASE_AUDIT_2026_04_27.md (9 sections, all SQL evidence). Headline findings cataloged below.

Phase 1 — Silent process-failure fixes (Finding #251 a-f): six services now exit non-zero on per-script failure thresholds so systemd correctly surfaces failures. Fixed: equity_bars_collector.py (10% threshold), trade_reconciler.py (status='error' check), outcome_resolver.py (50% per-tier infrastructure-failure threshold), backup_database.py (Spaces upload failure), polymarket-trades-backfill.service (Type=oneshot), auto_login_gateway.py broken send_telegram_message import (added wrapper to alerts.py).

Phase 2 — Active data-loss patterns (Findings #252-#257): investigation revealed FIVE of six audited "data-loss" patterns were NOT current bugs (forward-only fixes hadn't fired yet because no trades closed since deploy + no signal-evaluation cycles ran since the weekend). One real bug fixed: trade_audit_log.hold_duration_hours 99% NULL — column existed in schema since migration 005 but no code wrote it. Added to position_manager.py close-path UPDATE; backfilled 113 historical rows (1/116 → 114/116 = 98% coverage). Three "no current bug" findings (#252, #254, #255) get integrity-monitor coverage in Phase 3 (alert if expected post-deploy rows still NULL after a weekday cycle).

Phase 3 — Unified integrity monitor (Finding #258): new src/data_tracker/integrity_monitor.py (~700 LOC) replaces v2/v3 watchdogs. Declarative rule config: 28 rules across 6 categories (5 freshness + 5 NULL-rate + 11 systemd + 2 artifact + 4 storage + 1 meta). Migration 027 created integrity_status table. New AlertCategory.DATA_INTEGRITY plugged into existing AlertManager (rate-limit + dedup + circuit breakers + retry inherited free). New systemd unit polymarket-integrity-monitor.service. Decommissioned v2 + v3 watchdogs (deploy hook idempotent). Telegram alerts on HIGH/CRITICAL transitions only (per operator policy). Pre-deploy live test on prod: 28 rules / 6.2s cycle / 0 transitions / clean.

Phase 4 — Database tab redesign (Finding #259): new /api/integrity endpoint (JSON-snapshot-first with DB fallback). Database tab fully redesigned around 4 panes (Freshness / Coverage / Services & Pipelines / Storage & Retention). Removed all hardcoded misleading sections (WebSocket: Connected literal, Watchdog auto-restart HTML literals, Market Info: 100% always-green, frozen Reliability Timeline prose, Database Tables heuristic-size column, Active Earnings Markets via tags). Live 30-second JS polling. Net dashboard.html size: 6562 → 6253 lines.

Phase 5 — Storage hygiene (Finding #260 a-f): investigation surfaced 4 production-critical issues beyond original scope. #260a (CRITICAL): polymarket-backup was OOM-killed every run since DB grew past ~6 GB (loaded entire pg_dump output into memory via process.communicate()). Fixed via streaming pg_dump pipe in 1MB chunks. #260b: Spaces creds not configured (operator's local-only choice — documented). #260c: watchdog v2 + v3 leftover unit files; v3 had been crash-looping with ModuleNotFoundError, generating 291MB of tracebacks. Idempotent cleanup loop in deploy hook. #260d: log rotation — new deploy/logrotate/polymarket (daily, 14d retention, copytruncate). /var/log/polymarket/* was 11GB at audit start. #260e: Timescale job 3 (166 failures) is an Apache-edition limitation — even alter_job() requires community license. Documented as known limitation. #260f: trade_dedupe 4.1GB documented as acceptable bloat.

Phase 6 — Doc refresh (Finding #261, this update): consolidated changelog above + DATA_ARCHITECTURE.md v1.6 (current numbers, undocumented tables added, gap_tracking phantom removed, retention/compression limitations documented) + DATA_INTEGRITY.md §2 rewritten (integrity monitor replaces watchdog) + OPERATIONS.md §8.A integrity-monitor runbook + §8.B service exit-code semantics + §8.C derived-artifact freshness expectations.

Production state post-audit

Open known limitations

Reference


Earlier completed (prior to Database Integrity Audit)

Outstanding work — Action Queue (live state, Apr 25 LATE evening — post Finding #232)

Aug 19, 2026: the complete, criticality-ordered implementation plan for everything open after the #357–#362 revalidation cycle (the approved C-1/S2c/B-4/RES/B-5 package, the Class A queue, all 16 monitors/contracts with dates, operational items, and the items found in no queue) lives in docs/IMPLEMENTATION_PLAN_2026_08.md. Read that first; the table below is a historical Apr-25 snapshot.

Single source of truth: scripts/strategy_performance_analysis.py:build_action_queue() regenerates this list on every report run. The dashboard 🎯 Action Queue tab and docs/STRATEGY_PLAYBOOK.md §2 are programmatic mirrors. Edit the function → both surfaces refresh.

# Rule Status Source Est. lift / 100 Risk
1 Skip LONGs in downtrend regime 🟢 IN-QUEUE — ship next v3 Finding B / v2 #1 · Apr 25 re-val (Finding #232) +$140 to +$200 (revised UP) LOW — STRONGER bias check (22/22 distinct tickers; ex-top-2 = −$175, more negative because SWBI was masking; 8+ distinct losers)
2 RuleB — ±90 min no-stop window around release 🟢 IN-QUEUE — PROMOTED rank 4 → 2 Apr 25 simulator (Finding #228 §10.5) · Apr 25 re-val (Finding #232) +$123 LOW — only stop-loss rule that survived; stop bleed is strategy's biggest leak (−$676/57 stops) per Finding #233
3 Daily 3-stop session breaker 🟢 IN-QUEUE v2 #6 · Apr 25 re-val (8 historical ≥3-stop sessions confirmed) tail-risk + ~$50 historical LOW (defensive; real fire frequency confirmed)
4 Re-validate full audit at trade #150+ via the 4-check gauntlet 🟢 IN-QUEUE memory §K meta meta — currently at 116, trigger at 150+
⏸️ v3 D — Skip Mid-cap LONGs at conviction ≥ 80 % ⏸️ ON-HOLD pending RuleB v3 audit + Apr 25 universe view + Apr 25 re-val (REINFORCED — Mid 90-95 % has 87 % stop rate) +$110 n/a — stops-eat-winners hypothesis vindicated; RuleB resolution highly likely. Note: Large 80-90 % degraded from +$22 → −$3, possible future widening to "ALL LONGs ≥ 80 %"
⏸️ v3 E — Skip non-downtrend SHORTs ⏸️ ON-HOLD pending RuleB + ≥30 SHORT trades v3 audit + Apr 25 operator decision + Apr 25 re-val (REINFORCED — sideways SHORT 83 % stop rate) +$60 n/a — total SHORT n=18 too small; KEEP set GPRO+STUB-dominated
🛑 v3 F — BMO + VIX-bucket sizing 🛑 RETRACTED Apr 25 LATE evening (Finding #232) v3 audit (claim) + Apr 25 deep bias gauntlet (refutation) n/a — claimed +$60, modeled +$36, scale-UP arm fails ex-top-3 n/a
🛑 v3 C — drift filter 🛑 RETRACTED Apr 25 (option 3 — drop and rely on B+D+E) v3 audit (claim) + universe view (refutation) n/a n/a — universe view at 5-day baseline showed skip-bucket net positive; live n=13 too small to override
🛑 v3 A — Block stops outside RTH 🛑 RETRACTED Apr 25 (Finding #228) v3 audit (claim) + simulator (refutation) n/a simulator: −$418/100 (hard) or $0 (soft, inert)

Combined realistic lift if all IN-QUEUE rules ship: +$190–$310 / 100 trades. Slightly UP from the prior +$160-$280 estimate (Finding B's bias check came out stronger; v3 F drop offset by RuleB promotion + 3-stop breaker confirmation). Flips the current run rate of +$1.44/trade to +$3.30 to +$4.50/trade average.

Implementation order matters: ship #1 first (Skip LONGs in downtrend) — cleanest mechanism, no execution dependencies, ~10 lines in signal_generator using existing MarketRegimeProvider. Then ship RuleB (#2) — addresses the stop bleed at root and unblocks v3 D + v3 E re-evaluation (both ON-HOLD pending RuleB; bias_check fields explicitly state "if RuleB closes the live stop bleed → close as RESOLVED-BY-OTHER-RULE").

Per-rule implementation traces (5-step verification per memory §B): docs/STRATEGY_PLAYBOOK.md §6.

Audit-process learnings (must-read before adding any new rule)

docs/STRATEGY_AUDIT_v3.md §6 has 5 lessons from the v3 audit; §10 added the true-simulator rule (Finding #228 lesson — proxies and observational tables are NOT counterfactual evidence); §13.6 added the single-source-of-truth rule (live production tables are canonical, not derived artifacts); §14 added the canonical-field-validity rule (Finding #233 lesson — never trust convenience flags like stop_triggered without cross-checking against the source-of-truth field like exit_reason).

The most important inherited rule (renamed Apr 25 — was 3-outlier through Apr 25 morning): the GPRO + DELL + SWBI + TXN + STUB quintet carries ~$430 of the strategy's $167 net P&L on 116 trades. Ex-top-5 the strategy is −$263 across 111 trades. Any slice that includes them looks great; any slice that excludes them is structurally negative. Always report numbers ex-top-2 alongside the headline; for any slice containing 2+ of the named 5 outliers, also report ex-them. The 4-check bias gauntlet is mandatory for every new finding (memory §K).

Data infrastructure state (Apr 25 LATE evening)

Source State Self-healing
markets.volume_total 5,622+ markets with positive volume; in-band 90.4 % covered (546 / 604) ✅ polymarket-data-backfill.timer (Sun 04:00 ET) re-queries Gamma with closed=true for newly-resolved markets
earnings_markets.prob_*_before 698+ of 900 populated; 54 past markets still NULL (genuinely no PM activity) ✅ Same timer chains conviction_tracker --backfill + data-api /trades fallback
earnings_markets.actual_beat 70 % past markets resolved (528 / 752) ✅ polymarket-resolver.timer (daily 06:00 ET) — 4-tier source cascade
Universe view (LONG > 60 % + SHORT < 30 %, resolved) 604 markets (was 568 in last handoff, 279 in morning) ✅ Auto-refreshes every report run
trade_audit_log.stop_triggered ✅ FIXED Apr 26, 2026 (Finding #236) — derived from exit_reason at write time; 57/57 closed stop-exits match canonical ✅ Both writer-side + analysis-side now agree
trade_audit_log.exit_reason ✅ Canonical stop indicator. 49 % real stop frequency on 116 trades. ✅ Always populated by order_executor on exit
trade_audit_log.regime_at_entry / vix_at_entry / sp500_change_at_entry ✅ FIXED Apr 26, 2026 (Finding #234) — orchestrator INSERT path now mirrors PositionManager; 100% / 98% / forward-only coverage post-backfill ✅ Forward-only enrichment via regime_provider snapshot at entry
trade_audit_log.conviction_at_exit ✅ FIXED Apr 26, 2026 (Finding #237) — close path now reads live Polymarket conviction. Forward-only (cannot reconstruct historical) ✅ Will populate on every close starting Apr 26
polymarket_opportunity_log.hypothetical_pnl ✅ Triaged Apr 26 (Finding #238) — 39% completion is correct (61% legitimately non-simulatable); weekly self-heal added to backfill timer ✅ polymarket-data-backfill.timer now includes compute_counterfactual_pnl.py
polymarket_opportunity_log.snapshot ✅ Verified Apr 26 morning (Finding #241) — 1064/1064 (100%) populated. ✅ ENRICHED Apr 26 evening (Finding #243) — pm_v2 schema captures full PM book + conviction-window on every new signal; IBKR L1 still deferred to Phase 3 ✅ Captured live by signal_generator._emit_pm_opportunity_log
earnings_markets.actual_beat (past-settled coverage) ✅ 71.6% → 84.1% (Finding #244 6-hour-guard fix) → 98.1% (Finding #246 post-event snapshot backfill). 14 remaining unresolved are genuinely irreducible (6 with zero post-event PM trades + 8 PM-ambiguous in 0.20-0.80 range). ✅ Daily resolver + weekly post-event-snapshot backfill in polymarket-data-backfill.timer
earnings_markets.actual_earnings_time (SEC 8-K precision) ✅ NEW Apr 26 (Finding #247) — 0% → 90.9% (667 / 734 past-settled). Precise to within minutes from SEC's 8-K acceptedDate. Refines simulator's ±90-min release-window heuristics on non-traded markets. ✅ Weekly SEC EDGAR backfill in polymarket-data-backfill.timer (5th pass; 6 total passes now)
equity_bars_1m (NEW Apr 26 — Finding #240) 🟢 21 symbols / 47,510 bars / 0 failures on Day 0 backfill. Daily timer accumulates; 30-day window completes ~May 26 ✅ polymarket-equity-bars.timer (daily 10:00 UTC ≈ 06:00 ET, before market open)

Next-pass to-do (DEFER queue)

11 hypotheses deferred to next audit pass — need better data capture or larger n. Full list with re-test triggers: docs/STRATEGY_AUDIT_v3.md §9. Re-trigger the audit after the next 50 closed trades (currently 109; trigger at 150+, or earlier if Action Queue items #1–#4 ship and we want to measure impact).

One follow-on observation worth tracking (not in queue, see Finding #230 §13.3): LONG 95%+ bucket emerged at universe scale as the strongest LONG segment (n=27, +$192/trade). Currently skipped by Mar 9 Finding #202 (skips ≥ 90 %). Live n at 95%+ is just 2 trades — far below gauntlet floor. Re-test at trade #150+ if signal persists.

Apr 28-30, 2026 — three larger items parked for post-Phase-3 review (operator decisions Apr 28-30 — all saved in docs/FINDINGS.md + docs/STRATEGY_PLAYBOOK.md §7 "DEFERRED-POST-PHASE-3" + Action Queue under DEFERRED-POST-PHASE-3 filter on the dashboard):

Live-system verification (run on any fresh session)

# All 7 services should be active (added polymarket-equity-bars.timer in Phase 2)
ssh root@[hidden] 'systemctl is-active \
    earnings-trader polymarket-dashboard polymarket-collector \
    polymarket-resolver.timer polymarket-data-backfill.timer \
    polymarket-equity-bars.timer ibgateway-ibc'

# Timers' next-fire times (3 timers post Phase 2)
ssh root@[hidden] 'systemctl list-timers \
    polymarket-resolver.timer polymarket-data-backfill.timer \
    polymarket-equity-bars.timer --no-pager'

# Equity 1m bar cache size (Phase 2 infrastructure — Finding #240)
ssh root@[hidden] 'source [hidden]/.env && export DATABASE_URL && \
  psql "$(echo $DATABASE_URL | sed s/+pg8000//)" -c \
  "SELECT count(DISTINCT symbol) AS symbols, count(*) AS bars, \
          pg_size_pretty(pg_total_relation_size(\\\"equity_bars_1m\\\")) AS size \
   FROM equity_bars_1m"'
# Expected (Day 0): 21+ symbols, 47k+ bars; should grow daily as the timer fires.

# Universe + actual_beat coverage (post Findings #229 + #230 — should be ≥600 in-band)
psql "$(ssh root@[hidden] 'grep DATABASE_URL [hidden]/.env | cut -d= -f2-' | sed s/+pg8000//)" \
  -c "SELECT
        count(*) FILTER (WHERE prob_24h_before > 0.60 OR prob_24h_before < 0.30) AS in_band,
        count(*) FILTER (WHERE earnings_date < CURRENT_DATE AND actual_beat IS NOT NULL) AS resolved_beat,
        count(*) FILTER (WHERE earnings_date < CURRENT_DATE) AS past_total
      FROM earnings_markets"

# Stop-rate sanity check (Finding #233 — confirms exit_reason path, not stop_triggered)
ssh root@[hidden] 'source [hidden]/.env && export DATABASE_URL && \
  psql "$(echo $DATABASE_URL | sed s/+pg8000//)" -c \
  "SELECT count(*) FILTER (WHERE stop_triggered) AS broken_flag_count, \
          count(*) FILTER (WHERE exit_reason ILIKE \\\"%stop%\\\") AS canonical_count, \
          round(100.0 * count(*) FILTER (WHERE exit_reason ILIKE \\\"%stop%\\\") / count(*), 1) AS real_stop_rate \
   FROM trade_audit_log WHERE status = \\\"closed\\\" AND realized_pnl IS NOT NULL"'
# Expected: broken_flag_count ~2, canonical_count ~57, real_stop_rate ~49%

# Backfill timer last-run log
ssh root@[hidden] 'tail -30 /var/log/polymarket/data_backfill.log'

# Dashboard universe chips (manual check)
open http://[hidden]:8080/#strategy
# Expect: 🎯 In conviction band: ~600 · ✅ Resolved: ~570 · 💰 With positive volume: ~550 (≥90 %)

This handoff document is maintained for operator/agent continuity. Last structural pass (Sep 27, 2026 — #481/#482 DEPLOYED, deploys #14 28b572b / #15 860af84; B3 #454/#455, deploy #13 00607c9; #479 grace hardening, deploy #12 2ecb02c; #467 + #479 restart grace, deploy #11 3138aca; #477/#480, deploy #9 0e985dd; #457/#478/#479, deploy #8 614a29d; earlier docs sweep + #473–#475, deploy #7 a695050) aligns with .cursor/memory.md (through §DOC R1–R7 + C12), docs/FINDINGS.md (through #482), docs/STRATEGY_AUDIT_v3.md (through §14), and docs/STRATEGY_PLAYBOOK.md §2 (generated from build_action_queue(); do not hand-edit the Action Queue). History: Apr 27, 2026 LATE evening pass documented through #274 logrotate size and then-current Playbook §2.