Purpose: This document captures ALL key directives, architecture, configuration, and implementation details. Use this to bootstrap new AI conversations without losing context.
Last Updated: 2026-09-26 (Sep 27, 2026 — Findings #481/#482 DEPLOYED (deploy #14
28b572b00:33 UTC: #481 quarantines a permanently failing live-trade row once and marks it seen, market-data poison is WARN only; deploy #15860af8402:43 UTC: #482 migration 042 widenstrades.token_labeltovarchar(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 #1300607c9, 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-characteroutcome; 26 CRITICAL alerts 23:32:47–23:37:46 UTC;polymarket-live-tradesstopped 23:39:08 by operator decision) was fixed by #481 (deploy #1428b572b, Sep 27 00:33 UTC: poison rows quarantined once and marked seen, market-data poison WARN only, collector restarted) and #482 (deploy #15860af84, 02:43 UTC: migration 042 widenedtoken_labeltovarchar(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 #122ecb02c: onesystemctl showsnapshot 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-upOPEN; no trading change). Finding #467 and the #479 sampler restart-grace fix DEPLOYED Sep 26, 2026 22:19 UTC (deploy #113138aca: 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_mapstill shows the rawWALLET_NAGIvalue, the six-character prefix of the third-party benchmark maker's wallet (not a full address, not Marius's wallet); a code follow-up isOPEN); the sampler rules tolerate a deploy restart for 120 s; no trading change). Findings #477/#480 DEPLOYED Sep 26, 2026 21:56 UTC (deploy #90e985dd: 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 relabelledtiming_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 #8614a29d: #457 committed secrets scrubbed from HEAD + hash tripwire, thedoadminreset DONE 20:49:21 UTC before the deploy; #478 C-Q2/G-RS count trigger, 2/12; #479 per-serviceapplication_name+ DB activity sampler — the post-deploy evaluator defect (security:db_client_activitycould not evaluate, so the foreign-client page was inactive 20:58–21:33 UTC) is fixedDEPLOYED Sep 26, 2026 21:30 UTC(deploy #10f28c0da); 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 #7a695050: #473 requirements pinned to the prod venv — the deploy never runs pip, prodpip freezeequalsconstraints.txt; #474 migration-baseline check — prod--check-baseline5/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 #5f130113; .gitignore + OPERATIONS §8.A2 + test only, no runtime change; prodgit status --porcelainempty; prod moves DONE 14:59 UTC). Reliability Batch A DEPLOYED Sep 26, 2026 (deploy #1884e52810:18 UTC; #445 fix + F-7/F-8 deploy #23d7f1b111: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 offDEPLOYEDSep 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 2FADEPLOYEDSep 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-evaluatedDEPLOYEDSep 2 (AEO C-1 generate_signal covers; any non-timing row; true timing-only still FAIL). Finding #430 weekly 2FA contractDEPLOYEDSep 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 windowDEPLOYEDSep 2 (2FA resets 1800s quiet; 8 heartbeat fails/600s exhaust). Finding #428 late-login attachDEPLOYEDAug 30 (2FA abort keeps watching IBC+:4001; systemd-run bounce; flat-book re-check in that call). Finding #427 farm-recovery + standing recycleDEPLOYEDAug 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 zombieDEPLOYEDAug 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 fallbacksDEPLOYEDAug 28 (Yahoo chart KeyError → history → Cboe → last-good cache). Finding #424 ASO official IRDEPLOYED(Sep 1 disputed → Sep 9 BMO confirmed). Finding #423 trade-reconcile BBW#274DEPLOYED(live log + LMT fill + IBKR ingest). Finding #422 Trader Session READYDEPLOYED(Connected, not Unknown). ** Finding #421 IBKR one session machineDEPLOYED— 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. Finding #420 IBKR nightly-1100 leftoversDEPLOYED— 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. Finding #419 IBKR in-place reconnect escape hatchDEPLOYED— 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. Finding #418 IBKR reconnect amplifierDEPLOYED— keep TCP session on durable-health fail; last-good book; skip 412 reload; #415 AMENDED; trader bounce. Finding #417 needrestart + overnight trades freshnessDEPLOYED— live-trades + integrity-monitor excluded; overnight 180 min / RTH 60 min. Finding #416 unverifiable IBKR ≠ missingDEPLOYED— IBKR UNREADABLE vs MISSING. Finding #415 IBKR latch leftoverDEPLOYED— re-trip only inside durable probe; 300s reconnect while dead; #413 AMENDED; trader bounce. Finding #414 channel-window leak already-admitted suppressDEPLOYED— ignoretiming_channel_leakwhen same condition_id already signal_emitted/admitted; DKS Aug 25 false #384; trader + integrity bounced. Finding #413 IBKR request-path latch hysteresisDEPLOYED— 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 latchDEPLOYED— 3 consecutive bounded_request failures block trading + page; clear/reconnect/alert AMENDED by #413. Finding #411 orderbook-archive dual-limit pacingDEPLOYED— 18/run, 6h kill-clock, 00:35+06:35 UTC. Trader not bounced.) Finding #410 ship-verification letter-audit (record only). Finding #409 DONE-CLAIM + C10/C11DEPLOYED Aug 22, 2026 15:23:13 UTCSHAdefe78f. Finding #408 leftover-orchestrator twinsDEPLOYED Aug 22, 2026 14:49:44 UTCSHA4e4ffa1. One persist helper + one missed-exit/scheduled-exit helper. This docs-stamp is a later SHA than the trader process. Finding #407 HTTP quota + five lean memory.md §F/§H clausesDEPLOYED Aug 22, 2026 13:39:29 UTCSHA7fbfe05. T2 leftover HTTP quota closed.#401–#406DEPLOYED Aug 22, 2026 07:53:22 UTCSHA88014f4.#393–#400remainDEPLOYEDfrom the Aug 21 weekend restarts. See FINDINGS for status markers. Prior 2026-08-21 — Finding #374 doc-drift framework: the ⚙️ CONFIGURATION VALUES section below now cites SSOT (src/earnings/config.py/ STRATEGY.md) instead of copying numbers — seedocs/DOCUMENTATION_DISCIPLINE.md. Previous full staleness re-audit: 2026-05-31, Finding #322.b — 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 only covered dashboard.html static HTML, skipping the Python that generates the dashboard + all docs); this is the complete re-do per "absolutely everything, not a single line skipped." See the "What changed this week" bullet + FINDINGS #322.b for the full per-file breakdown. Earlier same day — Finding #321 Strategy-analysis data completeness: Polygon backfill + full simulator re-validation (data backfill; NO code/strategy change) — operator directive: Polygon was bought for COMPLETE data; audit + backfill gaps, THEN full simulator run. Audit found 472 PAST counterfactual-P&L gaps inpolymarket_opportunity_log(price_data_unavailablebutequity_bars_1mexisted → original fetch failed transiently + never retried) + ~48earnings_marketsoutcome/return gaps. Backfilled viacompute_counterfactual_pnl.py(counterfactual P&L 1,288→1,755 priced; 30 remain = 25 future + 5 delisted) +outcome_resolver.py(stock_return 99.8% of past markets; actual_beat 948/961). Validation batch repriced 20/20 via Polygon before the full run. Full re-run on COMPLETE data — every conclusion HOLDS: capacity $1k gate KEEP +$18.11/wk (was +$16.40); regime #318 KEEP compound_v1 (identical); depth #319 KEEP gate (identical). The backfill VALIDATED the shipped KEEP decisions rather than overturning them. Key insight: the_CF_SQLuniverse is filter-limited (volume≥$1k drops 713, conv≥0.90/small-cap drops 369 → 590 raw → 441 post-dedup), NOT data-limited — so completeness improved power/accuracy without changing conclusions. Corrected the #320 Universe View label (it regenerates on every report build viaanalyze_universe_hit_rate, not a 4h manual job). 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 (now un-gated since Phase 3 PR 1-5 is complete). NEW 🧪 Unified Simulator umbrella on the Strategy tab framing one canonical engine over its arms: live stop-loss (Finding-A), capacity-aware (Phase 3 Capacity-Aware Sweeps + Gate Retune), selection-rule research (static regime #318 + depth #319 KEEP cards), universe signal-quality (Universe View, folded + honestly Apr-25-baseline-labeled). Archived the Q3-2025 Backtest Scenario Comparison + BMO/AMC Timing Matrix off the canonical tab (a third historical population that fragmented "which number do I trust?"; producers + JSON retained). Consolidated the 5 overlapping breakdown widgets → 2 canonical (removed Live Performance Snapshot; kept Direction+Timing Matrix + Performance Breakdowns). Operator chose unify-and-preserve (not literal delete-all-3, which would lose unique live-stop-loss + universe-PM-accuracy evidence) + static regime/depth cards + include-the-breakdowns-task. Verified: both inline JS blocks pass node --check, Jinja parses, div balance preserved, removed-DOM-ids only referenced by 3 now-dead (never-called) render fns. Dashboard-only; deployed (dashboard restart). Earlier same day — Finding #319 PR 5: Depth-gate v2 widened reconstruction — the "unreliable orderbook" skip is a roughly break-even low-information-signal filter → KEEP (offline-only; closes Finding #283; Phase 3 simulator PR 1-5 COMPLETE) — NEWscripts/phase3_simulator/depth_gate_variants.py(decompose score into min-depth/spread/asymmetry + variants: remove_all / carveout_real_trade [#283 hyp-#3] / spread_gate_only / depth_gate_only) +replay_depth_gate_v2.py(replays the REAL ConvictionCalculator 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). Answer to operator's "has it 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 1¢/99¢ book → sparse/unstable price-snapshot conviction), 1 spread, 3 no-orderbook → it's a low-information-signal filter, not the pure spread gate #283 implied. The earlier n=4 "only costs us / blocked 0 losers" was a small-sample mirage — widening (operator-requested) reversed it. Removal/carve-out (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. 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) — NEWscripts/phase3_simulator/regime_classifiers.py(point-in-timeSpyDailyHistory+ 6 classifiers: compound_v1 baseline + compound_or_dd5 + below_ema20 + below_ma50 + dd_3pct + compound_symmetric; VIX deferred to Phase 4) +replay_regime_v2.py(re-classify regime point-in-time prior-close → re-apply Rule #1 → recompute P&L via run_sizing_sweep + apply_sizing_gauntlet vs compound_v1, on BOTH live-only primary + capacity-aware 1.40 cross-check). Prod (568-signal universe): 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 — the live-only win is selection luck that doesn't generalize (#315 "don't look in isolation"); its only durable benefit is a better worst-week tail at ~breakeven P&L (a downside trade-off, not a money-maker). All other candidates inert or FAIL. PIT compound_v1 reproduces the live recorded regime 95.3%. Operator-confirmed KEEP compound_v1; no classifier swap; production market_regime_provider.py UNCHANGED. 375 tests PASS (+22). 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) — re-evaluated the 4 Finding #313 deferred SKIP cells REAL-SCALED (not hypothetical $10k-notional) + capacity-aware. NEWscripts/phase3_simulator/replay_skip_rules.py. Real-scaling corrected 2 cells: Large+SHORT+$2k-$5k is only n=3 (not 12); Large+LONG[.80,.90)+$1k-$2k is net-POSITIVE (+$0.93 avg, hyp said −$20). Gauntlet verdicts: 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%). ANALYSIS-ONLY (operator ship-bar=C): nothing shipped; per-candidate ship decision 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" idea. Verified first (operator directive): academic peak-week classifier TRACKS PM data (peak windows hold 57.7% of 977 earnings markets / 60% of vol≥$1k). NEWscripts/phase3_simulator/replay_gate_hybrid.py— hybrid = uniform $1k gate + peak-week sub-$2k skip predicate; 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), landing almost back at uniform $2k (+$6.70/wk). Operator premise "$1k is noise in peak weeks" REFUTED — those markets are net-positive (#314.b $1k-$2k bucket +$423), removing them gives back most of #311's lift; capacity-reallocation doesn't rescue it (cap-skips 58→5 but freed slots not usefully refilled). Honest note: pre-build earnings_markets diagnostic under-counted footprint (31 via cumulative volume vs 144 in simulator universe — different volume fields). DECISION: KEEP uniform $1k (Finding #311 unchanged). 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.capital_simulator.simulate_one_variant()gainedscale_pnl_with_sizeflag (default OFF — #314.b + SKIP sweeps byte-identical; design doc §12 PR3 "P&L = realized_pnl_pct × new_position_value") +realized_pnl_creditedon AdmissionDecision; NEWgauntlet.apply_sizing_gauntlet()(per-trade delta over union of admitted trades); NEWscripts/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 while 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) AND worsen maxDD/5p tail risk. DECISION: KEEP production 30/22/15 (Finding #211 unchanged). 296/296 phase3_simulator tests PASS (+14). NO PRODUCTION BEHAVIOR CHANGE (offline analysis). Earlier (LATE LATE evening May 30) — Finding #314.b PR 3.b.2 SHIPPED: Capacity-aware re-validation of Finding #311 — first sweep landing on the capital simulator foundation. NEWscripts/phase3_simulator/replay_capacity_aware.pyorchestration script. Run on prod (568 signals = 127 live + 441 counterfactual over 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 (about HALF the optimistic +$21/wk Phase γ+δ projection — gap is capacity contention: 58 capacity-rejected at $1k vs 5 at $2k). $1k-$2k bucket admitted (n=132): avg +$3.20, WR 31.8%, total +$423 → POSITIVE contribution; operator's earlier concern (from live n=10 = -$0.77 avg) not confirmed at scale. max_capital_used=$4,949 (99% of $5,000 cap) — capacity binding. Decision UNCHANGED: KEEP $1k gate as shipped; real lift confirmed at +$10/wk (~+$520/yr ≈ 21% return-on-capital). Earlier same day (LATE LATE evening) — Finding #314 PR 3.b.1 SHIPPED: Capital simulator foundation — NEWscripts/phase3_simulator/capital_simulator.py(~365 lines). Per-event timestep simulator that models capacity-constrained admission across overlapping positions.CapitalSimSignal(live + counterfactual signal input) +SizingVariant(declarative variant spec with extra_skip_predicates + mcap_pct + gate_volume_min) +AdmissionDecision(audit trail) +CapacitySimResult(aggregate).simulate_one_variant()walks chronologically-sorted signals through a heap of (exit_dt, capital_reserved) tuples; per signal: free expired positions → check variant skip rules → compute position size (variant_pct/prod_pct ratio scaling) → capacity check (current + new ≤ trading_capital × exposure_cap) → admit or skip with reason. Convenience constructorssignal_from_live_trade()+signal_from_counterfactual_market()(latter applies per-tier empirical scaling per Audit Fix v3, lazy-imports EMPIRICAL_POSITION_BY_TIER from replay_production_and_candidates). 30 NEW unit tests; 282/282 phase3_simulator PASS local. NO PRODUCTION BEHAVIOR CHANGE (offline-only foundation). Subsequent commits: PR 3.b.2 (re-validate #311 capacity-aware) + PR 3.b.3 (4 SKIP rule candidates with bias gauntlet) + PR 3.b.4 (sizing variants sweep). Earlier same day (LATE LATE evening) — Finding #311 Re-validation Note — THIRD real-money strategy ship of the day. After data-driven first-principles analysis (operator-pushed back on initial proposal that misused $10k-notional hypothetical_pnl): kept Mar 9 mcap × conviction ordering (live LONG WR Large 45.7% n=35 > Mid 34.3% n=70 still validates), added volume-tier bump (live LONG WR jumps 36% NULL → 64% $2-5k → 83% $5k+) — new scale 0-8 (was 0-6). Capital cap raised 1.40 → 2.00 (matches Reg T overnight ceiling, +43% gross capacity; trading_capital base unchanged ~$2.5k). NYSE/NASDAQ branching dropped (verified 100% dead post Finding #185 Feb 15 unification — 126/127 trades had primary_exchange=NULL anyway). 617/618 tests PASS. Operator-flagged follow-up: re-validate $1k gate (Finding #311) against new volume-tier data — $1k-$2k bucket has historically been the only LOSING bucket (n=10, avg -$0.77, WR 30%). DEFERRED to Finding #313-followup once simulator framework is fully ready: 4 candidate SKIP rules (Mid+L80-90+$1-2k, Large+L80-90+$1-2k, Large+L80-90+$10k+, Large+SHORT+$2-5k). Earlier same day (LATE evening) — Finding #312 SHIPPED: v2 #6 Daily 3-stop session breaker + DirectionPolicySpec doc-bug fix — SECOND real-money strategy ship of the day. NEWsrc/earnings/session_stop_breaker.py(pure-decision module: SessionBreakerAction enum + decide_session_stop_breaker() + COUNT_STOPS_TODAY_SQL constant). signal_generator._build_trading_signal now calls_count_todays_stops()after Queue Rule #1 long_downtrend; if ≥ Config.SESSION_STOP_BREAKER_THRESHOLD (default 3) stops fired today (ET calendar day) → return ('session_3_stop_breaker'). Defaults SESSION_STOP_BREAKER_ENABLED=True. Historical evidence: 9 sessions in production with ≥3 stop-exits in a single ET-day (2026-02-03 (3), -02-05 (3), -02-10 (4), -02-19 (5), -02-26 (5), -03-03 (3), -03-05 (4), -04-22 (3), -04-29 (3)). Stop classification uses exit_reason ILIKE '%stop%' per Finding #233 corrected stop-rate (covers stop_loss:hardware + stop_loss:hardware_reconciled + instant_exit:stop_loss_backup; avoids broken stop_triggered boolean). Lift: +$1/wk + ~-$50 Max DD (heuristic; defensive rule). Plus DirectionPolicySpec correction inscripts/phase3_simulator/rules.py: production_strategy_may30() factory now accurately models prod thresholds. Was: long_threshold=0.55, short_threshold=0.45, conviction_fn="abs_distance_x2". Now: long_threshold=0.70, short_threshold=0.30, NEW long_max_threshold=0.90 (Finding #202 over-confident skip), conviction_fn="raw_prob" (production: ConvictionCalculator returns BEAT-prob directly, NOT |prob-0.5|×2). Dashboard Live Config Direction row + Selection row updated to match. 79 tests PASS local (22 session_stop_breaker pure + 8 integration + 38 phase3_simulator + 11 regression). Earlier (May 30 evening) — Finding #311 SHIPPED: gate_uniform_$1000 production change — first real-money strategy ship since Mar 9, 2026. SIGNAL_MIN_VOLUME_TOTAL default lowered $2,000 → $1,000 in src/earnings/config.py. Operator-confirmed after thorough correctness audit found 6 errors in earlier simulator outputs (E1-E6), all fixed via per-tier empirical scaling (Large $537, Mid $339, Small $205) replacing hardcoded $245 + memory K outliers standardized across production+candidates. Phase γ+δ replay shows: Δ E[wk] +$21/wk (~+$1,092/yr), max DD impact -$46 (still within -$735 target = 30% of $2,450 capital), outlier alarm fixed (production ex-top-5 -$152 → +$582 post-admit). All 9 strategy components formalized inproduction_strategy_may30()factory (renamed from may29; backward-compat alias preserved). Earlier (May 30) — Phase β + γ + δ + ζ shipped: full unified strategy framework (5-KPI engine + per-tier scaling audit fix + Pareto frontier verified + simulator-driven Top-5 in cockpit). Earlier (May 30 morning) — Phase ε.1 through ε.5 + α: Operator Cockpit (4 layers: target strip + 5 KPIs + live config + Top-5) + Section Index navigation + Strategy formalized as 9-component object. Earlier (May 29 early morning — Finding #310.c shipped: Phase 3 simulator PR 2.c FINAL — gate-retune sweep machinery + dashboard section + admission gauntlet (PR 2 COMPLETE) = NEWscripts/phase3_simulator/gate_retune_sweep.py(~280 lines) withShipEligibility/VariantSweepResult/SweepResultdataclasses +evaluate_conservative_ship_eligibility()Q3 (C) 3-condition check +run_gate_retune_sweep_on_universe()deterministic +run_gate_retune_sweep()SQL-loading. NEWgauntlet.apply_admission_gauntlet()entry point reusing existing 5-check helpers via duck-typed AdmissionResult (added @property symbol + @property entry_time). NEWoutput.write_sweep_results_json()schema=phase3_sim_gate_retune.v1. Extendedrunner.py --run-gate-retune-sweep. Addedstrategy_performance_analysis.build_phase3_sim_gate_retune()helper. Added new dashboard 🧪 Phase 3 — Gate Retune Sweep section +renderPhase3SimGateRetune()JS. 25 NEW unit tests; 250/250 PASS local. PR 2 COMPLETE. NO production behavior change. Operator AskQuestion per qualifying variant is 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) = NEWscripts/phase3_simulator/counterfactual_admission.py(~230 lines) with CounterfactualMarket frozen dataclass + load_counterfactual_markets() SQL loader (262 usable rows out of 540 — USABLE_PRICES_SOURCES filter excludes 278 with 'none' source = Polygon fetch failed = $0 placeholder) + 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 simulator. Pre-coding schema inspection revealedpolymarket_opportunity_logalready hashypothetical_entry_price + hypothetical_exit_price + hypothetical_stop_hit + hypothetical_pnl + hypothetical_pnl_pct + intended_*fields pre-populated bycompute_counterfactual_pnl.py(Finding #308 re-run). Massive scope reduction. Pre-coding evidence: 262 usable 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 98.1% gate-pass + $14,522 median; 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 SEGMENTED (healthy < 2026-03-01 / collapse / full), Q2 THRESHOLD + SEASONAL AXIS (11 unique variants = 6 thresholds × 2 modes uniform/seasonal_week with off-peak factor 0.5), Q3 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: 2.a SHIPPED (declarative scaffolding + 51 unit tests; 205/205 PASS); 2.b NEXT (counterfactual_admission.py — synthesize Trade from opp_log skip_reason='low_volume' rows + delegate to replay_trade); 2.c FINAL (gauntlet aggregator + 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/FINAL of 3 sub-commits for #306 — PR 1 COMPLETE) = NEWgauntlet.py(5-check bias gauntlet per memory §K with verdict dispatcher in priority order + VariantStats/CheckResult/GauntletResult frozen dataclasses +true_simulator_compliant=True) + NEWoutput.py(deterministic JSON schema per design doc §7.2 with sort_keys +mask_volatile_fieldsfor reproducibility diffs +derive_ship_recommendationverdict→SHIP/DEFER/DROP mapping) + UPDATEDrunner.py(--run-gauntlet flag for full pipeline + footer cleanup, removed "#306.a stops here" staleness). 33 NEW tests; 154/154 PASS local Python 3.11. §10.3 H-rule self-validation acceptance gate: PASS (synthetic H-rule shape returns FAIL-EX-TOP-2). §10.2 reproducibility acceptance gate: PASS (2 runs same input = 0-byte diff after masking generated_at). PR 1 IS COMPLETE: all 5 PR 1 commits shipped (#306.a + #306.b + #306.c + #307 + #308); all 4 PR 1 acceptance gates PASS (§10.1 cross-check 100% match per #306.b §G probe + §10.2 reproducibility + §10.3 H-rule + Phase 0 §0.6 corrected per #308). PR 2 (volume-gate retune sweep) can begin next session. NO production behavior change (offline-only). 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) = NEWcost_model.py(CostEstimate + estimate_cost with proxy-flat-1pct default per Q2; latent pm_v2_orderbook path for PR 2+) + NEWreference_adapter.py(re-exports per Q1 HYBRID +_assert_reference_module_stable()signature-drift guard fired at import time with memory.md §M-pointing diagnostic) + NEWreplay.py(ReplayContext + ReplayResult dataclasses + replay_trade + replay_population) + NEWcross_check_baseline.py(operator-runnable script implementing design doc §10.1 acceptance gate). §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 would AttributeError. My #306.a TestReferenceParity only checked dataclass FIELDS, not METHODS. Fixed both: added scheduled_exit_dt + new TestReferenceParity.test_method_names_match_expected + test_scheduled_exit_dt_consistency_amc. 37 NEW tests; 121/121 PASS local Python 3.11 (79 phase3_simulator + 42 prior regression). NO production behavior change. §10.1 cross-check execution is part of #306.b §G probe runway. 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) = NEWscripts/phase3_simulator/package (__init__.py+population.pyTrade+load_trades duplicated per Q1 HYBRID +rules.pydeclarative RuleSet dataclasses +baseline_rulesetfactory +runner.pyCLI stub) + NEWtests/unit/phase3_simulator/(42 NEW tests: 18 population including 4 CHANGE-CONTRACT cross-check tests vsanalyze_finding_a_full.Trade+ 24 rules). 84/84 PASS local Python 3.11. NO production behavior change (offline-only simulator package; nothing insrc/earnings/orsrc/data_tracker/references it). 3 operator AskQuestion decisions resolved BEFORE coding: Q1 = (C) HYBRID, Q2 = (A) FLAT 1% proxy spread (lands in #306.b cost_model.py), Q3 = (B) THREE INCREMENTAL MILESTONES. #306.b next = cost_model+replay+reference_adapter+baseline cross-check; #306.c final = gauntlet+H-rule self-validation+reproducibility+output+dashboard. After #306.c, PR 1 COMPLETE and PR 2 (volume-gate retune sweep) begins. Earlier (LATE evening) — Finding #308 shipped: Phase 3 simulator PR 1 second commit (ops-only) =compute_counterfactual_pnl.pyre-run (78 pending rows processed, generate_signal 67.5%→69.1%, integrity 47/47 OK preserved) + structural-ceiling discovery (480+39 rows with NULL hyp_pnl are alldirection_would_be='NONE'conviction-deadband signals → STRUCTURALLY un-simulatable, not a coverage gap; Phase 0 75% target was incomplete framing). Phase 3 simulator'ssignal_emitted100% subset is sufficient for all 4-bundle analyses; no Phase 3 work blocked. Earlier (LATE evening) — Finding #307 shipped: Phase 3 simulator PR 1 first commit =snapshot_schema='pm_v2'conditional-label fix atsignal_generator.py:1041(Option A MINIMAL per design doc §9) + integrity rulenull_rate:opp_log:pm_v2_post_deployupdated in lockstep (integrity_monitor.py:541col changedsnapshot_schema→method, same intent unaffected by new conditional semantic — memory.md §M cross-interface catch). Lockstep fix for original Finding #273 (deferred Apr 27 + re-confirmed May 28 afternoon). 6 new unit tests + 42/42 pre-commit regression (no regressions). PR 1 §A pre-coding revealed Bug #2 + #3 are ALREADY FIXED Apr 26 (Findings #234/#237 — third INFERENCE DISCIPLINE catch this session); operator AskQuestion approved revised smaller PR 1 scope = #306 foundation + #307 #273 fix (this commit) + #308 Bug #6 hyp_pnl re-run only (~3-5 days vs original 5-8). Design doc §3 + §12 + §15 updated to reflect corrected gate-1 understanding. The 1,055 pre-fix mislabeled rows remain as historical baseline (not retroactively unlabeled per §C surgical-changes + audit-trail discipline). Earlier (2026-05-28 evening) — Phase 3 unified simulator design doc shipped atdocs/PHASE_3_SIMULATOR_DESIGN.md(867 lines, 16 sections); companion todocs/STRATEGY_ANALYSIS_ARCHITECTURE.mdPhase 0 baseline. Operator chose all-4-bundle scope viaAskQuestion→ 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 May-28 PM volume-collapse evidence); 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 §0.6 VERIFIED MET (equity_bars_1m 185 days/418 symbols/1.9M bars; actual_beat 94.9%; opportunity_log hyp_pnl 100% signal_emitted; Bug #5 stop_triggered fixed; remaining sp500/conv_at_exit/depth-stage gaps addressed in PR 1). 8 open operator-decision points in design doc §15 must resolve before PR 1 starts (Finding #273 fix scope, sp500 backfill vs forward-only, etc.). Earlier (2026-05-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). Code: newsrc/earnings/ruleb_window_gate.pymodule (pure decision function, 8-valueRuleBActionenum, fail-softderive_earnings_release_dthelper) + new orchestrator methods_process_ruleb_window_gate/_ruleb_fire_cancel/_ruleb_fire_rearm/_ruleb_log_once+ main-loop hook + 4 new env vars in Config (RULEB_ENABLEDdefault False,RULEB_MODEdefault 'dry_run',RULEB_WINDOW_MINUTESdefault 90,RULEB_HARD_FALLBACK_MIN_BEFORE_EXITdefault 30). Tests: 25 new intests/unit/test_ruleb_window_gate.py, 36/36 PASS pre-commit on prod Python 3.12 venv (25 RuleB + 7 Rule #1 + 4 prior signal-test regression). Phase 1 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. Structural inconsistency caught during pre-coding (INFERENCE DISCIPLINE + NO SHORTCUTS): original Playbook §6.7 spec described a soft-gate that would deliver $0 EV (same as FA_soft retraction Finding #228); operator chose Path (A) hardware cancel-and-rearm via AskQuestion after agent surfaced 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. Bias-gauntlet PASS robustly on May-28 fresh sim n=121: ex-top-2 +$127/100 STRENGTHENED from Apr-25 +$123/100 baseline. 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 shows this happens in practice. Earlier (2026-05-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. Code change: new_safe_get_regime()helper + skip block in_build_trading_signalatsrc/earnings/signal_generator.py(~40 lines). Tests: 7 new intests/unit/test_signal_regime_gate.py(LONG×downtrend skip + 6 fail-soft/direction-asymmetry/regime-variation negative controls). 11/11 PASS (4 existing signal-test + 7 new) on prod's Python 3.12 venv pre-commit, no regressions. Bias-gauntlet PASS on May-28 fresh data: 22 LONG-downtrend trades / 5W (23 %) / −$136.30 net, ex-top-2 −$175 (MORE negative than headline → robust), 22/22 distinct tickers, full-population spillover positive. Sequence of work today: Finding #273 (originally next per "D then A") was reconsidered via AskQuestion to (E) DEFER per its original lockstep narrative — evidence surfaced #273's bug surface is 16× bigger than originally documented (1,055 NULL pm_v2 rows across 3 stages vs FINDINGS.md's "8 rows on 1 stage") AND fix scope is non-trivial without Phase 3 simulator schema knowledge. Net: Rule #1 ships first (this entry), Finding #273 stays deferred to lockstep with Phase 3 simulator.scripts/strategy_performance_analysis.py:build_action_queuequeue rank-1 row flipped IN-QUEUE → IMPLEMENTED withimplemented_date="May 28, 2026"+implemented_findings=["#304"]. Operational note: at current 1.9 % PM volume-gate pass rate (Finding #279 amendment), Rule #1's natural firing rate is ~0.4 % of all earnings markets — likely zero firings per month at current signal density; the rule's value is bias-gauntlet-locking + correct behavior in the next high-volume regime, not fast live evidence. Earlier (2026-05-28 afternoon) — Finding #279 Phase B amendment shipped (PM earnings-market volume collapse evidence folded into Phase B scope) as first piece of Phase 3 Strategy Review work; doc-only commit, no code/live-trading change. Triggered by operator'sAskQuestionoption (B) "fold #304 into #279" over a standalone Finding #304 entry. Pre-amendment data-gathering pass: re-ranscripts/strategy_performance_analysis.py(fresh 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; HPQ entered yesterday May 27 is the only post-drought entry, crossing the gate via Finding #282 Gamma-cumulative fallback path); re-ranscripts/analyze_finding_a_full.py(n=121 fresh, RuleB ex-top-2 strengthened +$123/100 → +$127/100, RuleG headline higher but ex-top-2 +$105 weaker, FA_hard retraction still holds —rearm_gap_openarm n=24 at -$1,545/100); direct SQL onearnings_markets LEFT JOIN markets USING (condition_id)confirmed Jan→May volume collapse at much greater severity than the May-19 chat-dump suggested — avg ~109× drop ($33,475 → $306), median ~1,847× drop, $2k-gate pass rate 52× collapse (100.0 % → 1.9 % = 3 of 154 May markets); NO Mid-cap market crossed the $2k gate in all of May (0/74). Phase 3 first-ship sequence per operator AskQuestion option "D then A, then re-evaluate": Finding #273 (pm_v2 NULL bid/spread data-capture fix) NEXT, then IN-QUEUE Rule #1 (Skip LONGs in downtrend), then re-evaluate. Earlier (2026-05-28 LATE-LATE morning) — Finding #303 shipped (deploy_from_github.shpre-pull hygiene for recurring working-tree drift). Earlier (2026-05-28 LATE morning) — Finding #302 shipped (polymarket-backup.timer collision with apt-daily-upgrade.timer, RESOLVED). Earlier (2026-05-28 morning) — memory.md🚨 INFERENCE DISCIPLINErule codified as companion to NO SHORTCUTS. Earlier (2026-05-27 morning) — factual correction to the May-17 reconnect entry: I had incorrectly inferred "IBKR allowed session resume without re-2FA" from the fast (~16s) port-up time; operator clarified May 27 that every Sunday reconnect DOES require operator 2FA approval on phone — IBC server-side logs are blind to the human-side push approval, and fast port-up is consistent with quick mobile approval, NOT session resume. The downstream speculation that "live Sunday automation may be feasible as fully unattended" was unfounded and has been removed fromdocs/AGENT_HANDOFF.md§11. Paper Gateway (port 4002, etfstack subtree) genuinely may not require 2FA — different IBKR security tier — but that property is paper-specific percf56485and does NOT generalise to live. Earlier (2026-05-17 morning) — Finding #297 (api_health_checkerWARN-but-exit-0 cry-wolf fix) shipped closing the pre-existingsystemd_last_run:polymarket-api-health.serviceFAIL surfaced during #296's §G probe; operator approved option (b) viaAskQuestionvs widen-threshold / leave-as-is / build-age-aware-structural-fix. 1-digit behavior change (exit(2)→exit(0)for warning branch) + 16-line context comment +logger.warningfor visibility.error → exit 1andhealthy → exit 0paths UNCHANGED — real failures still alert. §E exhaustive grep: 1 otherexit(2)in repo (backup_database.py:287for real S3 upload failure — correct, NOT same antipattern). Today's prior op: IBKR Sunday 2FA reconnect handled manually (06:48 UTC restart → operator approved 2FA push on mobile → port 4001 open ~16s after restart → trader reconnected at 06:49:01 with account U2…13 / net_liq $27,534.56). NOTE: the original May-17 entry incorrectly inferred "session resume / no 2FA needed" from the ~16s port-up time; operator clarified May 27 that every Sunday reconnect DOES require operator 2FA approval on phone — the IBC server-side logs are blind to the human-side approval step and rapid port-up is consistent with quick approval, not session-resume. Corrected May 27, 2026. Integrity-monitor returned to 45/45 OK baseline. Earlier — 2026-05-11 evening — Finding #296 (_check_systemd_unit_last_resultbranch reorder) shipped as the lowest-risk close-out of the 3 May-10 "observed-but-not-actioned" maintenance items perAskQuestionoperator option (A) "ship #2 only". The other two (Sunday-2FA partial-automation timer + Sunday-awareartifact:ibkr_snapshotthreshold) explicitly deferred — they change operator workflow and alert semantics and need explicit approval beyond "close the maintenance backlog." Integrity-monitor still 45/45 OK. Project remains in steady-state observation mode for ~15 days until May 26, 2026 Phase 3 un-gate. Seedocs/AGENT_HANDOFF.md§11 May-11 evening block +docs/FINDINGS.md#296 for full details. Earlier — 2026-05-10 early afternoon — 🎉 ALL operator-approved follow-ups + 3 newly-discovered side-effect findings SHIPPED in 12 commits today. Plus IBKR Sunday 2FA reconnect handled (gateway exited at 06:00 UTC;systemctl restart ibgateway-ibcspawned fresh Gateway; 2FA approved via mobile push; trader auto-reconnected at 10:01 UTC). Final state: 8/8 critical services active, integrity-monitor 40/40 OK, account U2…13 reconnected with no positions. NO operator-approved follow-ups remaining. Project is in steady-state observation mode for ~16 days until May 26, 2026 when Phase 3 Strategy Revamp un-gates (Finding #240's 30-day equity-bars window completes). NO-SHORTCUTS rule was exercised heavily today — 5 design tensions surfaced viaAskQuestioninstead of silent interpretation, operator overwhelmingly chose deeper / more-thorough options. Architectural patterns established:with conn.begin_nested():SAVEPOINT for batch-writes (3 active code paths now); try-wraps-with-engine.begin for single-row writes (2 active code paths now); never trust stale-doc rule counts — alwayscurl /api/integrityfor live count when adding rules. #293: Polymarket Website URL renamed/finance/earnings→/finance(cached 404 confirmed long-term); fixedapi_health_checker.pyURL + deleted deadEarningsMarketSyncerclass + removed silently-broken cron entry; wrapperscripts/sync_earnings_markets.pypreserved per Finding #220. #294:trades.outcomewidened VARCHAR(10) → VARCHAR(64),trades.side10 → 20 (operator-approved option B "defense-in-depth"). Migration 028 + ORM lockstep. Hypertable-safe (PostgreSQL VARCHAR widening is non-rewriting; 907 chunks). §G probe: no disruption + 60 live_poll writes/10min healthy + 0 new InterfaceErrors. Earlier today: 🎉 ALL 4 plan-(C) operator-approved follow-ups shipped: #290 + #291 + #289.a + #289.b, plus cold-bootstrap close-out #292 + #288.v3. Project is squeaky clean for May 26 Phase 3 commission. #289.b root cause:save_tradehad innertry/exceptinsidewith 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. PLUS the failed trade_id was never added to_seen_trade_idsso the SAME bad trade re-attempted every 5s — ~12-13k InterfaceErrors/day for 10+ days (~118k total). Fix: try/with INVERSION + WARNING-level visibility + cascade closure via_seen_trade_ids.addon error path. #289.a was operator-approved option (B) full systemd-timer migration after AskQuestion surfaced the trade-offs vs (A) crontab patch. Root cause: Ubuntu /bin/sh is dash,sourceis bashism, cron's chainedsource venv && python && >> logshort-circuited at the dash failure for 14 days. §E found same bug in sync_earnings_markets cron — fixed viaSHELL=/bin/bashat top of crontab; api_health migrated to dedicated systemd timer with bash-c source pattern. #291 was RE-SCOPED from original spec (3progress:*rules) to a state-file mtime defense after live-system analysis showed the proposed rules would duplicate existing freshness coverage — operator-approved viaAskQuestionper NO SHORTCUTS rule. #290: 5 files deleted (watchdog{,_v3}.py+ 3 deploy/.service files), 7 stale-reference surgical edits, 4 historical forward-pointer annotations. Earlier same morning: cold-bootstrap ofpolymarket-trades-backfill.serviceCOMPLETE; #288 Tier B fully validated end-to-end; Finding #292 caught by §G probe and shipped same session — graceful handling of Polymarket data-api/tradesoffset hard-cap at 3000 (TSLA Q3 2025 was 1/929 affected; 144 high-volume markets all have pre-#288 Dome coverage so zero historical loss); Finding #288.v3 doc-completeness gap closed — 5trade_dedupe.sourcelabels coexist not 4 (backfill2.17M rows from one-off Jan 31 bulk import was previously undocumented). Earlier morning: Dome EOL Apr 28, 2026 migration COMPLETE; "NO SHORTCUTS — EVER" codified as the #1 project rule after operator-flagged Tier C v1 shortcut; Tier A (#287) + Tier B (#288) + Tier C (#289) + Tier C v2 (#289.v2) all shipped same session. 8 dead Dome-dependent files deleted;live_trades_collector.pycosmetic rename; 6 docs surgically updated;check_dome_apireplaced withcheck_polymarket_data_api. 4 next-session items remain (#289.a + #289.b + #290 + #291). Earlier May 9: pre-Phase-3 housekeeping batch COMPLETE: Findings #270 + #286 + #274 + #273 + #269 all addressed. #270StartLimit*section moved across 5 live unit files via §E expansion; #286 dashboard cold-start race fixed as forced detour from #270 §G probe (_empty_summarywas missing 13 TC-ROI fields from #284); #274 logrotatemaxsize 500Mwith size-vs-maxsize semantics correction; #273 formally deferred to Phase 3 lockstep ship (joins May 26 bundle alongside #278/#279/#283); #269 RESOLVED via smart Option A** — newpolymarket-trades-backfill.timer(weekly Sun 06:00 ET) + delta backfill via Domestart_time(T1) + smart re-fetch policy for active and recently-resolved markets (T2) + earnings-only scope by default (T3), CLI flags for ops control, cold-bootstrap-then-enable runbook in OPERATIONS.md, integrity-monitor rule with 8-day freshness. Earlier May 9: Finding #285 Phase A greedy-fill + exposure-cap logging shipped; Phase B rule re-evaluation deferred-post-Phase-3 alongside #279. Seedocs/AGENT_HANDOFF.md§11 for full Apr 21 → May 9 changelog. Rule rankings indocs/STRATEGY_PLAYBOOK.md§2 still unchanged since Apr 25.)What changed this week (Apr 21 → Aug 22, 2026)
- Oct 14 — Finding #488: dormant sniper refuses a missing or retired funder,
IMPLEMENTED-PENDING-DEPLOY(no unit restart). Both sniper copies drop the hard-codedFUNDER_ADDRESSdefault and refuse a missing, malformed, or retired funder, and a retired signer. Collector, conviction tracker, data tracker, cron, anddeploy_from_github.shdo not importresolution_sniper. Canonical: FINDINGS #488.- Oct 3 — Findings #486 and #487: NASDAQ calendar deadline and an honest orderbook size page,
DEPLOYED Oct 4, 2026 12:05 UTC(unitpolymarket-deploy-20261004T120553Z-812717079, SHA7c9d6b0; trader 12:06 UTC, integrity monitor 12:08 UTC; Gateway not restarted). #486:finance-calendarsrequests.gethad no timeout; five watchdog deaths on 2026-10-01 sat in that GET during the 21-day lookahead. Our GET usestimeout=8and the multi-day loop stops at 45s (worst case 53s, under the 120s watchdog) and does not retry; a failure stays source-silent and writes noearnings_time. #487: after grace expiry,storage:orderbook_snapshotsstays FAIL at the 12 GB line. When the archive backlog is clear, the message says the drain finished and the 90-day working set is over the line — it does not say the drain failed. A stalled backlog still says stalled.trader:restart_tripwirewas ok after that restart (NRestarts=0). Canonical: FINDINGS #486 and #487.- Sep 27 — Finding #484: IBC Command Server
RESTARTbefore last-resort 2FA,IMPLEMENTED-PENDING-DEPLOY(flag default off; do not enable, do not deploy, do not restart Gateway/trader). heal-1234 paper P4 + live L7 proved IBCRESTARTreuses the session token (OK RESTART in progressthenLogin has completedwithinitialTokenType=TST/pwd=0 soft=256, no IB Key). New loopback client (src/ibkr/ibc_command.py, 127.0.0.1:7462, neverSTOP); when the flag is later dated-enabled,recycle_gateway_if_flattriesRESTARTinside the B1 lock beforesystemctl restart ibgateway-ibc, does not spend the #439 weekly budget, and refuses Monday 13:30–20:15 UTC plus weekday entry 14:25–14:40 / exit 19:45–20:05 UTC. NoRESTARTfrom Sunday 01:00 ET until launcher.log or this week'slauncher.YYYYMMDD.logshowsinitialTokenType=PWD(heal-1234 L5; Monday AutoRestart rotates the live file) and IBCLogin has completed; TST does not count; that guard does not block last-resort. Sat 20:00 UTC is not a weekday wave. Canonical: FINDINGS #484.- Sep 27 — Finding #483: post-deploy #467 docs review gap,
WALLET_NAGIleak,IMPLEMENTED-PENDING-DEPLOY. After #467 deployed (master3138aca, deploy #11), a post-deploy scan found/api/btc5m/docs/doc_mapstill serving the rawWALLET_NAGIvalue (facts.py's benchmark-maker wallet prefix[REDACTED]) in doc prose, even though/api/btc5m/statealready masks it per-route. Root cause: doc routes render PROSE with no per-field call site to mask at, so #467.D relied entirely onsecret_redaction._collect_known_identifier_literals()— which collected btc5m's wallet/host constants from a hand-maintained tuple of attribute NAMES that nobody updated whenWALLET_NAGIwas added tofacts.py. Fixed centrally (operator direction: mask identifiers centrally for every public response, JSON APIs included, rather than per route) — that function now DISCOVERS everyWALLET_*/EXECUTION_HOST*/EXECUTION_TREE_PATHconstant by naming convention, so a future addition needs zero other code changes. The fix's own comprehensive route+identifier sweep (tests/unit/test_finding_483_identifier_leak_gap.py, 26 tests — every parameter-free GET route, both doc-serving routes at every valid slug, AND the routes needing a fixture parameter to reach —/api/trade-chart-data, etfstack's/backtest/{run_id}/...) found a SECOND hit the same way:DU…91(etfstack's earlier paper account id) on five main-dashboard docs, fixed by adding it toidentifier_masking.KNOWN_ACCOUNT_IDS. Also folded in (B3 review): a #467 test that hung on a fresh cloud VM (planted a fakeDATABASE_URLat a private-looking address and hit every route; on a VM whose network silently drops rather than rejects that address, the real DB connect attempt hung with noPGCONNECT_TIMEOUTset) — fixed hermetically with ano_db_networkfixture that patches the one shared engine factory so no DB call site can ever open a real socket, and by replacing the fixture's real-looking host with the RFC 2606-reserveddb.invalid. Full suite and drift audit clean (counts below).- Sep 27 — Finding #482:
trades.token_labelwidened tovarchar(64)DEPLOYED Sep 27, 2026 02:43 UTC(deploy #15860af84). Migration 042 (catalog-only on the Timescale hypertable, typmod-probed) lets the deploy-#13 incident trade class store; the quarantined payloads were released after the deploy and the DB-write spool is empty. Canonical: FINDINGS #482.- Sep 27 — Finding #481: live-trades poison rows quarantined once, no alert loop
DEPLOYED Sep 27, 2026 00:33 UTC(deploy #1428b572b). Deploy #13's #455 writer looped a 22001 market-data row through spool → quarantine → API re-fetch and sent 26 CRITICAL alerts before the collector was stopped; now a permanent error is quarantined once and marked seen, market-data poison is WARN only, and the outage alert clears only after a successful replay. Canonical: FINDINGS #481.- Sep 26 — Finding #470: main-loop stall root cause fixed
IMPLEMENTED-PENDING-DEPLOY(retires the #453 stopgap). The one-sided-book fallback's latest-snapshot CTE joined every historical row (287k rows, 35.5 s cold on prod) on the IB owner thread — the Sep 21–22 kills. Now two bounded statements (same rows, ~10 ms) under a 15 s statement timeout; latest-book read bounded; progress marked per ticker with IB owner pumping; the thread dump is written tologs/stall_dumps/(the old StringIO dump never landed). Budget back to 120 s every day. Batch B2.- Sep 26 — Finding #452: leverage alerts validity + latch
IMPLEMENTED-PENDING-DEPLOY. 78 false pages (Aug 26: 2.50× over the $1,000 startup placeholder,internal_estimate; Sep 8: 0% buffer on a flat book with margin fields not yet populated after the 06:00 reattach) paged once a minute. Only broker-reported, non-stale state is evaluated; zeroed margin fields skip the margin checks; one page per incident + one resolve (persisted). Alert-only. Batch B2.- Sep 26 — Finding #451: trader IBKR alert policy
IMPLEMENTED-PENDING-DEPLOY. Pages follow IBKR's schedule: an incident opened inside the nightly reset (23:30–01:15 ET) or the 06:00 UTC AutoRestart window pages only if still open after the window SLA (30 / 10 / Sunday 5 min); outside, after the grace. One CRITICAL page with the correct lever (systemctl restart ibgateway-ibc+ 2FA when:4001is down), one WARNING resolve only after a delivered page; incidents persist across the #441 bounce (logs/alert_incidents.json); bounce/deploy lifecycle pages silent; recon guard one page after 30 min (5 with an open position). OD6: integritytrader:ibkr_request_pathstill evaluates; its page is skipped only when the trader's own Telegram page for the same incident generation was delivered. Review round 2: oneibkrincident per outage; operator actions page through it (≤1 per kind); delivered = Telegram HTTP 200. Round 3: on B1's sharedIncidentLedger, sent and retried by the trader's alert dispatcher;:4001closed + liveibkr-monitor→ the monitor pages. Batch B2 (ships after B1).- Sep 26 — Finding #450: dashboard TRADE?/API gate = the trader's gate
IMPLEMENTED-PENDING-DEPLOY.src/earnings/signal_gates.pyis the one conviction-band + depth definition (Config edges read at call time) for the builder,is_valid, priority,ConvictionCalculatorand the dashboard (APIconviction_ok/conviction_reason/depth_quality_ok/depth_source; Jinja/JS read only those). Missing or one-sided orderbook fails closed on the dashboard (PM FALLBACK); 0% conviction renders; the priority filter's SHORT miss-confidence SKIP shows. Trader identical at prod values; LONG/SHORT env edges now honoured end to end and guarded (≥0.70 / ≤0.30). Batch B2.- Sep 26 — Finding #469 (reliability F-6): PR Newswire check runs right after a host boot
IMPLEMENTED-PENDING-DEPLOY._check_prnewswireread its failure stamp with.get(key, 0); on the monotonic clock 0 is boot, so a feed that never failed was skipped for the first 300 s after every boot with no log (instant-exit detection gap, register row 33). An absent key now means never failed; a real failure still backs off 300 s. Same class fixed inIBKRPositionCache(_last_refresh == 0.0always refreshes). Batch B2.- Sep 26 — Findings #477/#480
DEPLOYED Sep 26, 2026 21:56 UTC(deploy #90e985dd). #480 explicitly types the pg8000 covered-channel lookup, retires the unaudited legacy sync entry points, scans SQL fragments plus ambiguousdate_trunc/EXTRACT, and migration 041 relabels only the four proven false leak rows. #477 makes exit-price corrections update every P&L field and the position twin; migration 040 corrects BBW 274/307 and SNOW 75.DATA_MANIFESTis numeric 040 then 041; both guarded migrations ship together. The pre-deploy gate passed at 21:56 UTC (041 count 4; each 040 guard matched 1 row; no ledger rows); 040 then 041 applied at 21:56:30, the rows now read −282.10 / −1.16 andtiming_channel_mismatch, and the #477 P&L check finds 0 mismatches. No trading-rule change; canonical narratives: FINDINGS #477/#480.- Sep 26 — Finding #467: public dashboard defense in depth
DEPLOYED Sep 26, 2026 22:19 UTC(deploy #113138aca; GPT round-3 approved; one open gap:/api/btc5m/docs/doc_mapstill shows the rawWALLET_NAGIvalue, the six-character prefix of the third-party benchmark maker's wallet (not a full address, not Marius's wallet); a code follow-up isOPEN— FINDINGS #467). Sep 14 an internet scanner harvested the liveDATABASE_URL— password included — because/docs/operationsrendereddocs/OPERATIONS.mdverbatim and that doc had the connection string pasted into it (scrubbed separately on#457/reliability/cred-scrub). Three parts, same finding: (1)earnings_dashboard/secret_redaction.py— a global ASGI response filter redacts every response (HTML/JSON/plain text/404/error/unhandled-exception, via a catch-all handler that closes the one real Starlette bypass) by VALUE from env-collected secrets first, shape patterns (postgres creds, DigitalOceanAVNS_, Telegram tokens) as a backstop; (2) FastAPI's own/openapi.json//docs//docs/oauth2-redirect//redocdisabled, and 3 always-inert unauthenticated write endpoints (/api/testing-prod/confirm,/api/testing-prod/reset,/api/set-trading-mode) deleted; (3)earnings_dashboard/identifier_masking.pymasks IBKR account/order ids, wallet addresses, and server hostnames/IPs/paths at each payload's source — found across the main dashboard AND the btc5m/etfstack modular subtrees (both had real wallet/host/account exposures), fixed with local dependency-free helpers in each subtree per their "modular, separately-extractable" guarantee. Cross-family review (71dd002) found 4 blockers, all fixed same finding: a chunk-boundary split-before-redact leak (now redacts the whole window first, margin sized from the actual literals); public/docs/*pages still showed the real account number/server IP/path (now masked on the rendered copy, source.mduntouched); short/URL-encodedDATABASE_URLpasswords weren't collected (now no minimum length, token-boundary-anchored); the full-route sweep wasn't mutation-sensitive (now instrumentsredact_bytesdirectly + an explicit mutation test). Round 2 (05c02ed) found 1 more blocker: a multibyte/Unicode secret (e.g. 1500🔐= 6000 UTF-8 bytes) still leaked across a streaming chunk boundary — the carry margin was sized from Python character count (undersizing it 4x for a 4-byte character) ANDredact_bytesdecoded each window tostrwitherrors="replace"first, which permanently corrupts an incomplete trailing multi-byte sequence before the rest of it ever arrives. Fixed by matching secrets as UTF-8 bytes throughout the streaming path (never decoding a window) and sizing the margin from each literal's encoded byte length; 10 new tests (offset sweep across 2-/3-/4-byte characters + 2 targeted mutation checks), mutation-confirmed against the actual pre-round-2 file. 135 tests total intests/unit/test_finding_467_dashboard_hardening.py, full suite 2619/2619, drift audit clean.- Sep 26 — Finding #479: DB activity logging
DEPLOYED Sep 26, 2026 20:55 UTC(deploy #8614a29d); the post-deploy evaluator defect (security:db_client_activitycould not evaluate, so the foreign-client page was inactive 20:58–21:33 UTC) is fixedDEPLOYED Sep 26, 2026 21:30 UTC(deploy #10f28c0da); the rule reads ok. Restart graceDEPLOYED Sep 26, 2026 22:19 UTC(deploy #11) and its race hardeningDEPLOYED Sep 26, 2026 22:39 UTC(deploy #122ecb02c). Every DB-capable entrypoint derives a unique startupapplication_namethroughsrc/db/connection.py(unit env is an override, not a deploy dependency). A read-only 15-second all-databasepg_stat_activitysampler records only client/user/app/backend-start/state/query-class to a 14-rotation local log. Foreign global addresses are latched until successfully CRITICAL-paged once per address; all non-droplet/non-internal addresses WARN in the integrity panel. No query text or parameters are emitted. The sampler has run since 20:58 UTC, after the OD8 reset; every app session now carries its service name.- Sep 26 — Finding #478: C-Q2/G-RS decided KEEP; the Sep 13 date reminder becomes a count trigger
DEPLOYED Sep 26, 2026 20:55 UTC(deploy #8614a29d). Operator decisions #8/#9 (17:34 UTC) on the clean-print re-judgment (69 resolved markets, 23 real trades, #358-parity replay): 0.98/0.02, 0.25/0.90, 300 s, 60 s and the Polymarket price source all stay (0 false exits, no priced benefit, underpowered).contract:cq2_grs_detection_rejudgmentnow WARNs at 12 wrong-held positions (canonical, resolved, open at the release gate, contra) or on 2026-12-15 — dashboard-only, no Telegram (src/data_tracker/contract_cq2_count.py). Count today: 2 (BOX, BBW).- Sep 26 — Finding #476: root crontab managed from the repo
IMPLEMENTED-PENDING-DEPLOY. Four cron scripts had been100644and dead (Permission denied) since Feb 15.deploy/cron/root.crontab+deploy/install_crontab.py(backup; refuses an unknown host line); one Sunday reminder and the audit, both via/bin/bashand100755;gateway_restart_manager.sh(Gateway PID killer),ibkr_health_monitor.sh,ibkr_restart_warning.shand the deadcheck_trader_health.shline retired;cron:schedule_auditon the dashboard.- Sep 26 — Finding #475 (reliability P1-cq2 follow-up 1): the C-Q2 detection replay is a true simulator again
DEPLOYED Sep 26, 2026 19:05 UTC(deploy #7a695050).scripts/detection_latency_replay.pystill used the pre-#358 median of the last 5 prints of any age, per print.simulatenow replays the live_check_polymarket_priceon the poll grid (#337 gate, #358 fresh window ≤ 900 s / ≥ 3 prints / upper median viaselect_fresh_median, Gamma fallback proxy, #341 contra grades and 300 s / 60 s confirmation), with every number read from live config;w7_arbiter_sweep.pyfollows. After review:--truth marketreadspm_resolution(not the last print, which can be an empty-book 0.50), and--positionsapplies the liveTradingSignal.is_validwith the policy in force at the scheduled entry (pre-#366 0.95 / small-cap 0.80 caps; small-cap SHORTs never). A parity test drives the live method on the same series (identical fires), and it matches the C-Q2 v2 simulator on the v2 cohort (crowd lens 69/69, live-gate lens 26/26). The entry-conviction window is half-open (no print at the entry instant counts). Research tooling; no trading change. Branchreliability/followups.- Sep 26 — Finding #474 (reliability F-5): the migration bootstrap baseline verifies full schema effects
DEPLOYED Sep 26, 2026 19:05 UTC(deploy #7a695050). #444 recorded an unrecorded migration as bootstrapped when its objects existed by name, its column data types matched and twoid = 1rows existed, so a partially applied file could pass.deploy/migration_baseline.json(captured read-only from prod) now pins types with length/precision, nullability, defaults, PK/UNIQUE/FK/CHECK constraints, index definitions and seed rows by key with their post-UPDATEvalues;deploy/migrate.pycompares them with structured catalog queries and fails closed. Read-only--check-baseline(prod: 5/5 match) and--capture-baseline. Prod deploys are unaffected (all 5 IDs are already in the ledger). Branchreliability/followups.- Sep 26 — Finding #473 (reliability F-1): requirements pinned to the prod venv
DEPLOYED Sep 26, 2026 19:05 UTC(deploy #7a695050).requirements.txtlacked pg8000 and SQLAlchemy and left FastAPI / Starlette / pytest-asyncio floating, so a fresh venv did not match prod. Every direct dependency is now an exact pin at prod's version (read-onlypip freeze, Python 3.12.3),constraints.txtis the full freeze, the two sub-files redirect to the one list. Host and developer install commands use-c constraints.txt; CI reaches the exact direct pins through the redirects, and its transitive constraints wait on aworkflow-scoped update of the workflow file. numpy 2.4.0 mirrors prod but is yanked on PyPI; its upgrade is a pending operator item. The deploy never runs pip, so prod is unchanged. Branchreliability/followups.- Sep 26 — Finding #457: live DB credential and committed API secrets scrubbed from HEAD
DEPLOYED Sep 26, 2026 20:55 UTC(deploy #8614a29d);doadminresetDONE Sep 26, 2026 20:49 UTC (#457); trusted sourcesDONE Sep 26, 2026 16:06 UTC (#457). Thedoadminpassword sat in 20 tracked files and the cluster accepted any IP. The 4 prodscripts/now usesrc.db.connection.get_database_url(); the 15archive/files and the OPERATIONS snippet reados.environ["DATABASE_URL"]. Trusted sources allow only the droplet (ip_addr:[hidden]); workstations and agents reach the DB over SSH. Thedoadminreset (OD8 step 2) isDONE Sep 26, 2026 20:49 UTC (#457)(RESET COMPLETE20:49:21 UTC; all 7 services reconnected; the old password is rejected). Plan fix (2) strips the sniper's Telegram token, chat id and 8 vendor-key defaults, the timing study's Alpha Vantage fallback, the live prod Finnhub key that 2fill_earnings_*scripts used as a default (rotationOPEN, operator), and the burned Polygon literal (now its sha256 prefix; the #399 test compares by hash).tests/unit/test_finding_457_no_live_db_url.pyfails on any DigitalOcean DB password shape, Telegram bot token, credential-namedgetenvdefault or the burned Polygon key in a tracked file, and a sha256-prefix denylist fails on any of the 13 known-leaked values in any file type. Re-rated High: the public dashboard's/docs/operationsserved the live URL and was harvested twice (Sep 14 and Sep 26; DB access can be neither proven nor excluded; see #457 root cause (4) and #467). Review P2-B1 found six vendor keys still indocs/archive/POLYMARKET_KNOWLEDGE.md; they are redacted now.- Sep 26 — Finding #449: integrity alert policy + Telegram last-send
IMPLEMENTED-PENDING-DEPLOY. Per-rule limiter (data_integrity:<rule>); one page + one resolve through the shared incident ledger, with failed sends retried; 2-cycle hysteresis for restart-prone rules; openings deferred (never dropped) in the deploy window and Sun 01:30-03:00 UTC; paging no longer needs a DB write (RO still pages).scripts/send_telegram.shexit 0 only on HTTP 200; the last-send artifact is best-effort (review Major 4).- Sep 26 — Finding #456: AutoRestart login watchdog
IMPLEMENTED-PENDING-DEPLOY.ibkr-monitor's pager, every day, one page per outage: a hang or crash pages after 180 s; only the weekly Sun 06:00–06:30 UTC restart gets 20 min, and only on IBC evidence (the scheduledRe-starting sessionplus a real 2FA prompt after it; a genuine crash in that window pages at 180 s). Latched, so the page says "not approved". Pages say what was detected, what runs automatically and the fallback lever. The monitor is page-only; the trader's #438 automatic last-resort recycle stays on (operator decision Sat 26 Sep 20:39 UTC: trader always on, then no dependence on him) behind the #435/#439 guards, with no DB I/O on the IB owner thread. Every Gateway restart needs a 2FA approval on his phone (#430). The Saturday-onlyibkr:weekend_autorestartrule is removed.- Sep 26 — Finding #448: senders deliver and prove it
IMPLEMENTED-PENDING-DEPLOY.ibkr-monitor.servicein git withEnvironmentFile(80/80 pages had failed), installed + restarted by the deploy; live:4001SLA pager (src/ibkr/gateway_pager.py), paper tracked not paged, no API probe; delivered = Telegram HTTP 200 (DeliveryResult); breaker OPEN WARNING, delivery INFO;alerting:ibkr_monitor. Healthchecks.io dead-man (OD7): one secretHEALTHCHECKS_PING_KEY, slugsearnings-trader/integrity-monitor/prod-host/db-backup, silent when unset.- Sep 26 — Finding #446: cluster DB-health integrity
IMPLEMENTED-PENDING-DEPLOY.db:read_onlyCRITICAL;db:sessions_killed(first sample = baseline);db:disk_plan= all databases + WAL reserve vs 120 GiB (Sep 25 = 92.5% FAIL);db:temp_spill,db:long_transaction,storage:btc5m,dashboard:api_reachable,dashboard:db_errors, intraday resolver timer/run; monitor-blind page when the DB is unreachable (review blockers 5-6).- Sep 26 — Finding #447: backup pg_dump stderr sidecar
IMPLEMENTED-PENDING-DEPLOY. Sep 25 05:12 UTC the dump failed aspg_dump: er. Concurrent stderr drain + fsync/var/log/polymarket/backup-last-pgdump.err; one retry on read-only/session-kill. The sidecar is best-effort: a write failure warns and never fails a good dump (review Major 5). Stdout still streamed (#260).- Sep 26 — Finding #471: high-conviction query LATERAL LIMIT 1
IMPLEMENTED-PENDING-DEPLOY.get_high_conviction_marketsno longerDISTINCT ONthe 7-day open-Earnings snapshot set (one ~11.3 MB sort / pass). Same panel filters; same #445 time bound.- Sep 26 — Finding #472:
ibkr_realitysnapshot stale uses trader cadenceIMPLEMENTED-PENDING-DEPLOY. Overlay compared age toIBKR_SNAPSHOT_INTERVAL_SEC=10 s while the trader rewrites ~60 s on weekends (loop idle) / off-hours. Sat 15:18 UTC #468 probe: file 43.4 s old,connected: True, overlay disconnected. Sharedsrc/earnings/ibkr_snapshot.py: stale = cadence × 2; the trading day is the ET date, as in the trader's loop (review Major 3). Not a widened 10→60 constant (weekday RTH still 20 s).- Sep 26 — Finding #454: IBKR in-process session recovery
DEPLOYED Sep 26, 2026 23:29 UTC(deploy #1300607c9; soak pending). Paper 4002 reproduced the live 0/37 signature: connect-timereqCurrentTimepassed and the immediate durable probe timed out at 10.013 s. Owner-thread probes now use a 2.0 s margin; 1100/1102 preserve the reset budget; late owner jobs cancel; owner-pump misses page as main-loop stalls without changing IBKR state; completed 2FA logins use the attach path; failed-reset bounces leave a 10-minute restart-tripwire marker while request-path follows the recovered process. No Gateway restart or live 4001 attach.- Sep 26 — Finding #455: post-fill DB-write resilience
DEPLOYED Sep 26, 2026 23:29 UTC(deploy #1300607c9; the live-trades poison-row loop (trades.token_label varchar(20)vs a 22-characteroutcome; 26 CRITICAL alerts 23:32:47–23:37:46 UTC;polymarket-live-tradesstopped 23:39:08 by operator decision) was fixed by #481 (deploy #1428b572b, Sep 27 00:33 UTC: poison rows quarantined once and marked seen, market-data poison WARN only, collector restarted) and #482 (deploy #15860af84, 02:43 UTC: migration 042 widenedtoken_labeltovarchar(64); the quarantined trades were released and the spool was empty at 02:51)). Entry/exit/execution/reconciliation/live-trade writes use a durable per-kind spool with trader/collector lock separation, migration-039 unique keys and advisory DB locks. Pending fills hydrate position/risk/earnings state and hold both entry gates closed; committed closes and stopout-before-restart clear idempotently. Per-kind capacity plus sticky/emergency entry gate protects fills from collector backlog without blocking exits. Transients back off indefinitely; only poison/corrupt records quarantine, with a tested audited operator release. The final nine-case real-Postgres suite—including pg8000 execution writes and over-long-value 22001 preservation—awaits cloud-VM rerun under the no-Mac-installs rule; the earlier four cases passed locally. Prod duplicate-index preflight returned 0 rows; any failed pre-restart deploy gate restores the previous checkout.- Sep 26 — Finding #468: prod checkout hygiene
DEPLOYED Sep 26, 2026 15:15 UTC(deploy #5f130113; prod movesDONE Sep 26, 2026 14:59 UTC (#468)). The server writes state and outputs inside[hidden], and 254 untracked files had piled up..gitignorenow coversdata/api_health/, the three IBKR state files,logs/*.log.*, the generatedscripts/phase3_*outputs, the SEC cache (its rule never matched because the comment sat on the pattern line) and.env.bak.*. The btc5m umpire tree and*.py.testare deliberately not ignored. OPERATIONS §8.A2: never rungit cleanon prod; move, never clean. Moves: 42 legacy logs to a verified.tar.zstunder/var/lib/polymarket/archive/,.env.bak.*to/root/secret-backups/(deleted after OD8), the umpire tree to/root/btc5m-umpire-2026-09-26/plus a Mac copy, and the dumps and scratch copies archived. Untracked 254 → 187, all now ignored: prodgit status --porcelainis empty since the deploy.- Sep 26 — Finding #453 (stopgap): main-loop stall budget 300 s on Mon 28 Sep 2026 ET only
DEPLOYED Sep 26, 2026 10:18 UTC. Operator OD3. The 120 s watchdog killed the trader 13× Sep 21–22 at 14:32/19:52 UTC (Monday's entry slots) during slow legitimate signal work.Config.MAIN_LOOP_STALL_TIMEOUT_DATED_OVERRIDES={2026-09-28: 300}, read every watchdog poll viaConfig.main_loop_stall_timeout_sec(); expires by itself at Tue 00:00 ET; never lowers the base. The correctness fix (#453 proper, Batch B2) deletes it.- Sep 26 — Finding #443: needrestart covers the LIVE
xvfb.service/x11vnc.serviceDEPLOYED Sep 26, 2026 10:18 UTC.#369'sxvfb-*/x11vnc-*patterns matched only the -paper twins; apt's needrestart bounced live Xvfb andRequires=took the Gateway down 6× Sep 1–25 (weekday 2FA each). Patternsxvfb(-.*)?/x11vnc(-.*)?;deploy/needrestart/gate.shproves every live unit before migrations/restarts; live xvfb/x11vnc/ibgateway-ibc units + apt drop-ins committed compare-only (deploy/host-reference/, drift stops the deploy).apt-daily-upgrade.timerwas held from Sep 25 19:25:51 UTC; the hold ended operator-attended at Sep 26 11:27 UTC (DONE, #443).- Sep 26 — Finding #445: dashboard hypertable SQL bounded + Sep 25 read-only incident record
DEPLOYED Sep 26, 2026 11:48 UTC. The unbounded "Data availability" CTE (5.2 GiB temp spill, one ~14 min transaction per pass) fed the managed-disk read-only guard 03:29–19:44 UTC Sep 25. Coverage is now 7-day pre-aggregated and cached 6 h; every dashboard hypertable read is time-bounded; hypertable counts useapproximate_row_count; sessions carry a 60 s statement timeout; the stats pass / earnings calendar / positions view run one short transaction per statement (no connection across Gamma); DB errors no longer log the earnings_sync hint.- Sep 26 — Finding #444: deploy migrations through a ledger
DEPLOYED Sep 26, 2026 10:18 UTC.deploy/migrate.py+schema_migrations(038): already-applied migrations are recorded as bootstrap without DDL; 029's ACCESS EXCLUSIVE ALTER never re-runs; new DDL underlock_timeout=5swith a lock-holder preflight; output tee'd to/var/log/polymarket/deploy-migrations.log; every failure (sibling trees included) and any ledger checksum drift stops before restarts; bootstrap needs the reviewed baselinedeploy/migration_baseline.json(column types + seed rows). §E: conviction-tracker start no longer ALTERsconviction_history.- Sep 13 — Finding #442: earnings-date join + 21-day verifier + DATE SPLIT
DEPLOYED. Upcoming PM fields join on(ticker, date), never ticker alone. Verify window 5→21 days so an official print can vote; confirm-in-place writesvotes=; NASDAQ+Yahoo pair may rewrite a locked PM date before the last-resort IR map (CBRL Sep 14→23). Timeline flags DATE SPLIT when the scheduled-exit plan ≠ calendar. Complements#349/#424. Does not move the live CBRL exit.- Sep 3 — Finding #436: weekly-only Sunday 2FA
DEPLOYED. Recycle-off default; Mode A/B keep probing; Mon–Sat session preserve; Sunday/crash attach-only.#434leftover AMENDED.- Sep 3 — Finding #435: recycle-once
DEPLOYED. Mode B in-place first; no 2FA while Gateway young / cooldown / late-watch. 06:27/06:48/07:14 leftover.#434leftover AMENDED. Recycle-on leftover AMENDED by #436.- Sep 3 — Finding #434: stay connected first
DEPLOYED. Recycle-on even with a live book. Farm window still no-2FA.#430leftover AMENDED.- Sep 3 — Finding #433: weekly-2FA keep probing
DEPLOYED. Recycle off refuses exhaust-and-stop. Header followsrequest_path_ok. 3 Sep 05:16 leftover.#432/#429/#421leftovers AMENDED. Recycle-off default AMENDED by #434.- Sep 3 — Finding #432: Mode A daily reconnect without 2FA
DEPLOYED. 1100 sticky farm probe until durable success. 900s timer no longer exhausts. Mode B leftover AMENDED by #433. Recycle stays off (#430).#427window-expiry leftover AMENDED.- Sep 2 — Finding #431: channel-window already-evaluated
DEPLOYED.#414emit/admit-only leftover AMENDED. AEO BMO C-1 skip (generate_signal/ 0.915) is covered; leftover leak row stops FAILing. True timing-only leaks still FAIL.- Sep 2 — Finding #430: weekly IBKR 2FA contract
DEPLOYED. Standing Gateway recycle off. Midweeksystemctl restart ibgateway-ibcwas the extra 2FA (24 IBC initiations 28 Aug–2 Sep). Sunday 06:00 AutoRestart still logs in without a new 2FA.#427recycle-default leftover AMENDED;#427stays ACTIVE (farm window).- Sep 2 — Finding #429: IBKR late-login quiet + Mode B window
DEPLOYED. 2FA resets the 1800s quiet timer; 8 heartbeat fails in 600s exhaust even when luckyreqCurrentTimeresets consecutive.#428leftover AMENDED;#428stays ACTIVE.- Aug 30 — Finding #428: IBKR late-login attach
DEPLOYED. Recycle 2FA abort no longer leaves the trader exhausted after IBC later logs in. 900s watcher; systemd-run bounce; flat-book re-check in that call.#427leftover AMENDED;#427stays ACTIVE.- Aug 29 — Finding #427: IBKR farm-recovery + standing recycle
DEPLOYED. Real 1100 keeps a 900s probe window (Aug 29 exhausted in 61s because #426 treated TCP-up as zombie). No-1100 wedge still #426-exhausts. Flat-book standing recycle ofibgateway-ibcthenearnings-trader; IBC 06:00 is not recycle.#426leftover AMENDED;#426stays ACTIVE.- Aug 28 — Finding #426: IBKR Gateway zombie
DEPLOYED. TCP-up + req* dead is a wedged Gateway, not a reconnect. Durable-only healthy; TCP-up trip exhausts (no 300s loop); reconcile skips client 99 on a dead snapshot. Recycle CONFIRMED 11:22 UTC restored the path.#421/#419/#413leftovers AMENDED.- Aug 28 — Findings #425/#424/#423/#422: integrity satellites + READY header
DEPLOYED. etfstack VIX Yahoo KeyError → history/Cboe/cache; ASO IR Sep 1→Sep 9 BMO; BBW#274 live trader log + LMT + ingest; Trader Session READY is Connected not Unknown.- Aug 28 — Finding #421: IBKR one session machine
DEPLOYED.#420_real_disconnectskipped in-place after a missed handshake 1102 and self-reset every ~300s while TCP stayed up; dashboard hid PATH_DEAD behind Reconnecting; empty live book inherited ghost MRVL stop1741024. OneSessionPhase; TCP-up always in-place; connect/1102 clears REAL_DROP; bind before connect; STILL DISCONNECTED only if TCP down; PATH_DEAD/EXHAUSTED win the header; empty live book remembers empty. Trader bounce (book flat).- Aug 27 — Finding #420: IBKR nightly-1100 leftovers
DEPLOYED.#419leftover-future cancel killed the heartbeat (CancelledError); a real 1100 with TCP flicker exhausted in-place; VIX/snapshot flooded the dead path; last-good stops wiped; UNREADABLE on a live BBW+DG book. Swallow CancelledError; 1100 keepsextended_60s; skip noncritical reqs; keep last-good stops; live book is readable. Trader bounce (BBW/DG hardware stops stay).- Aug 26 — Finding #419: IBKR in-place reconnect escape hatch
DEPLOYED.#418kept TCP but left no escape: heartbeat + durable probe wedged the loop, thenextended_60sforever. Quiet heartbeat while TCP is up; light in-place probe; one IB() reset at N=3 then stop; leftover timeout cancel; UNREADABLE not on a live book. Trader bounce (HPQ hardware stop stays).- Aug 26 — Finding #418: IBKR reconnect amplifier
DEPLOYED.#415300s floor still tore the session down after each failed durable health (187 self-disconnects vs ~12 Error-1100s). Keep TCP + last-good book; skip 412 reload; exit still attempts a bounded send. Infinite in-place leftover AMENDED by#419. Trader bounce (BBWI hardware stop stays).- Aug 26 — Finding #417: needrestart + overnight trades freshness
DEPLOYED.#369missedpolymarket-live-trades+polymarket-integrity-monitor; apt clean-restarted the poller at 04:29 UTC andfreshness:tradesFAILed on a 91-min overnight gap. Both units excluded; overnight threshold 180 min, RTH 60 min.- Aug 26 — Finding #416: unverifiable IBKR ≠ missing
DEPLOYED. Empty snapshot during request-path dead showed IBKR MISSING on BBWI while 142 LONG + hardware stop were live. Badge is IBKR UNREADABLE; overlay skipsmissing_in_ibkr.- Aug 26 — Finding #415: IBKR latch leftover
DEPLOYED.#413RESTORED then leftover heartbeat/VIX/SPY re-tripped in 21s; 1100 reconnect ignored the 300s floor. Re-trip only inside the durable probe; reconnect ≥300s while dead;get_quoteskips while dead.- Aug 25 — Finding #414: channel-window leak already-admitted suppress
DEPLOYED.#384stampedtiming_channel_leakon DKS after BMO had already admitted it (AMC probe 07:11 UTC). Stamp site + integrity explicit SQL now ignore a leak whosecondition_id(or symbol+earnings day) already hassignal_emitted/admitted. True MA/WWD leaks still FAIL. Trader + integrity bounced.- Aug 25 — Finding #413: IBKR request-path latch hysteresis
DEPLOYED.#412cleared on onereqCurrentTimeand reconnected every trip → ~330 Telegrams after the 04:13 UTC 1100. Clear now requires 3 consecutive successes including qualify*; one reconnect / 5 min; noibkr_disconnecton#412trips. Complements#412/#354/#369. Trader bounced (0 open).- Aug 25 — Finding #412: IBKR request-path latch
DEPLOYED. After 3 consecutivebounded_requestfailures the send-dead socket can no longer look "connected": trading block 412, Telegramibkr_request_path, dashboardrequest_path_dead, integritytrader:ibkr_request_path. Heartbeat usesprobe_request_path(), notaccountValues()cache. Clear/reconnect/alert AMENDED by #413. Complements#354/#369. DKS 15:58 Aug 24 batch 0/0 was the incident.- Aug 24 — Finding #411: orderbook-archive dual-limit pacing
DEPLOYED.TimeoutStartSec=21600,ARCHIVE_MAX_CHUNKS_PER_RUN=18, timer 00:35+06:35 UTC after systemd SIGTERM'd a healthy 40-chunk / 3h run. Proof gate unchanged. Extra drain waits for the 00:35 UTC window (not mid-session). Trader not bounced.- Aug 22 — Finding #410: Aug 21–22 ship verification letter-audit (record only; no trading-logic change). Official
DEPLOYED/§B:completed on #367–#408; 13 missing live-behavior changelog keys backfilled (#368 #369 #370 #371 #373 #375 #378 #383 #388 #389 #390 #402 #404). Dashboard restart after pull; trader not bounced.- Aug 22 — Finding #409: DONE-CLAIM + C10/C11
DEPLOYED Aug 22, 2026 15:23:13 UTC(SHAdefe78f). Closer must post a DONE-CLAIM table before saying a memory.md-scoped task is done. C10 now includesbuild_revalidation_357+ live canvases. C11 requires§B:+test_finding_N_*.py. The #408 R2 builder/canvas miss is the reason.- Aug 22 — Finding #408: leftover-orchestrator "one helper, two copies" twins collapsed
DEPLOYED Aug 22, 2026 14:49:44 UTC(SHA4e4ffa1). Twin A: IBKR fill ledger isPositionManager.persist_ibkr_executiononly. Twin B: missed-exit startup + scheduled-exit body isExitManager. Not a trading-rule change. Leftovers cited: Flex backfill + archive paper_trade_test. Trader restarted (0 open positions, weekend). This docs-stamp is a later SHA than the trader process.- Aug 22 — Finding #407: quota-limited HTTP protection generalized (Finnhub shared limiter + Yahoo live wiring + PRN tripwire) + five lean memory.md §F/§H clauses
DEPLOYED Aug 22, 2026 13:39:29 UTC(SHA7fbfe05). T2 leftover HTTP quota closed. Not a trading-rule change. Trader restarted (0 open positions, weekend).- Aug 22 — Findings #401 #402 #403 #404 #405 #406
DEPLOYED Aug 22, 2026 07:53:22 UTC(SHA88014f4). One cost-basis helper (src/earnings/exposure.py); 10:30 reservation credits 10:32 exits (#401). CapitalManager deleted after lockstep tests retargetedTradingOrchestrator.check_*(#402). INTRADAY/unknown skip — never AMC (#403). Recon Telegram logs, fakesetup_schedulegone (#404). OneConfig.PDT_RESERVE_CAPITAL; integrity always-on includes websocket + live-trades; unread Config knobs deleted — OrderExecutor class attrs kept,HEARTBEAT_INTERVAL_SECkept (#405). Deferred T2.2 T2.6 T2.8 T2.11 T2.13 T2.15ON-HOLDon the Action Queue (#406). Operator premises that were wrong at HEAD: F16 deploy already restarted WS+live-trades (#358); T2.5 fallback timeouts are OrderExecutor class attrs not unread Config. See FINDINGS for status markers.#393–#400remain live from the Aug 21 weekend restarts.- Aug 22 night — Finding #400:
get_positionsfail-closedDEPLOYED Aug 21, 2026 23:00:26 UTC(trader-process SHAe857f5e).PositionsLookupErroron not-connected / exception; honest empty book is still[]. The#393F26 abort only ran ifget_positionsraised; the callee used to swallow and return[]. Prior implementer was WRONG on three stamps: UTC (wrote “Aug 22 ~21:43 UTC”; systemd is 2026-08-21 21:43:10 UTC), burned-key (wrote “must still ROTATE”; T1.6 closed on the wire Aug 22 ~22:38 UTC, REST 200), and “fully memory.md” (Rev/plan/canvas/STRATEGY banner missed#393–#399). TIER 2 not implemented.- Aug 22 — Findings #393–#399: TIER 0+1 of the code-audit reconciliation (operator: implement both in full; TIER 2 out of scope). Exit fail-open cluster closed (#393: batch restore-on-fail,
#352lookup fail-closed, MARKET fallback abort). Live persist writespilot_tag(#394). PRN RSS once/30s + one poller (#395). Cheapget_positions+#369exit-path tripwire (#396). One_exit_in_progressset (#397). Batch sizing uses admissions2c_split_pctand resizes after the#362day-filter (#398). Recon gate on the live batch + Polygon default stripped + Jinja TRADE?timingKnown+ priority cap reads Config (#399). SSOT:docs/CODE_AUDIT_RECONCILIATION_2026_08.md. Status:DEPLOYED Aug 21, 2026 21:43:10 UTC(4292bdb). T1.6 CLOSED on the wire (POLYGON_KEY rotated + REST 200). Leftover#400closed the[]swallow.- Aug 21 later evening — Finding #392: K-B5 lost its special autonomous status (operator: "we treat everything the same"). Examined
consistency:b5_pilot_auto_revert(#366): it ONLY ever alerted — no code path flipsB5_PILOT_ENABLEDor blocks pilot admissions (the flag is env-read-only) — so the change is semantic: renamed into the #389 contract-reminder pattern ascontract:k_b5_revert_decision(categorycontract, WARNING, 📋 telegram once-then-rearm). The pre-registered revert conditions are unchanged verbatim (net ≤$0 ex-top-2 at 25-fills/Dec-15 re-score · >3 gap-throughs · breaker firing with ≥2 pilot stops); a trip means the operator decides. Every "auto-revert"/"only K-B5 acts autonomously" claim swept (code comments, changelog monitor field, builder texts, dashboard tooltips, Rev/plan/STRATEGY/PLAYBOOK, #366/#389 amendment notes). No trader-side deletion needed — the trader never carried an autonomous path. Integrity-monitor targeted restart + report regen only.- Aug 21 evening — Finding #389: the §II.7 monitor table went SELF-ALERTING. Ten new
contract:*integrity rules compute every K-contract / D-trigger / scheduled re-judgment condition LIVE (K-S2C fills≥40-or-A-7>2/mo · K-B4 ordering-proxy≥25-or-Nov-30 · K-RES res-skips≥50-or-Nov-30 · D-2 VIX≥20-LONGs≥25 · D-4 shared-wave-or-2nd-firing · D-5 CF-SHORTs≥40 · D-8 downtrend≥30 · D-1 canonical≥240 · C-Q2/G-RS Sep-13 · C-Q3 Oct-12) and send a 📋 Telegram reminder once when due (WARNING severity, re-arms per the table). Alert-only — every outcome returns to the operator; only K-B5's encoded auto-revert acts autonomously (stale by the same evening — #392 removed K-B5's special status, see the bullet above). Same pass: the operator's C-Q3 REFRAME recorded on all four surfaces (no hard slot limit under S2c+shrink-to-fit+A-7 → "accept per-trade dilution on peak October nights, or add capital before the season", ~Oct 12, §14 evidence kept). Non-trader service.- Aug 21 evening — Finding #388: all four P4 tails closed. SCRIPT-1 (
check_earnings_sources.pyimport fixed + proven live) · F-324 (regime/VIX ×100 unit bug VERIFIED REAL — DB 0.90% rendered "0.01%" — fixed at the producer + template tripwire test) · F-298 (effective_cumulative_volume_at_entrypersisted on both entry-persist twins, migration 036; backfill evaluated and SKIPPED with evidence — 48% coverage, wrong-time semantics; trader-side write activates at the next safe-window restart, QUEUED) · F-321 (CFprice_data_unavailablerows now retry Polygon DIRECTLY, bounded bycf_retry_countmigration 037 — 5 attempts / 3-day cooldown / terminalprice_data_unavailable_final_v1; 85 backlog rows enter the budget).- Aug 21 — Finding #391: dashboard clean-up phase 2 — the operator APPROVED the #387 proposal IN FULL; all 7 CONSOLIDATE + 3 REMOVE candidates shipped (dashboard-only, trader untouched). Gone: the phase-status banner (red rules-of-engagement box kept, top of tab), the 🧪 Phase 3 Simulator Status tracker (HTML + renderer + its 385-line builder), Performance-vs-Projections (headline rows verified covered by the 📊 ROI Re-baseline ladder), the selection-research static cards (→ Unified Simulator bullet with #318/#319 verdicts + replay-script links), the ROI-target model-baseline card, and the 3 stop-reversal summary cards (aggregates moved into the stopped-trades table title). Consolidated: Gate Retune Sweep → collapsed 📦 archive block (earnings-calendar table kept inside); regime snapshot → 1-row strip under the Cockpit (legend in tooltip; killed the template's one pre-existing stray
</div>with it); Stop Loss + Stop Reversal → one "Stops (descriptive)" section; the Total-P&L-(NET) tooltip definition single-sourced (Jinjapnl_net_def). Strategy tab 32 → 28 section divs; census counts in the proposal §0 + FINDINGS #391. Dashboard-only targeted restart outside the corridor.- Aug 21 — Finding #390: the two live integrity FAILs dealt with (data write + integrity-monitor deploy only; trader untouched). (1) FIVE's #349 dispute resolved per official IR: BOTH machine sources were wrong (DB/Polymarket Aug 26, Finnhub Aug 25) — Five Below's own Aug-19 press release says Q2 FY2026 reports AMC Wed Sep 2, 2026; row corrected to Sep 2 AMC
confirmed(write-locked) with anoperator_ir_checkaudit row;consistency:earnings_dates_disputedback to ok. (2)storage:orderbook_snapshotsmade DRAIN-AWARE: whilearchive:orderbook_backlogis OK and today ≤ORDERBOOK_DRAIN_GRACE_UNTIL(2026-08-29, #356-style dated expiry), a size FAIL downgrades to WARN (message says why); self-reverts to FAIL if the drain stalls or the date passes — the 12 GB threshold itself unchanged (no silent bump; the #355 alarm-fatigue lesson).- Aug 21 15:17 UTC — THE QUEUED MARKET-SAFE TRADER RESTART (operator-approved): #378 + #379 + #380-code + #382-detector + #384 are LIVE in the trader. Friday ~11:15 ET, inside the safe corridor. Pre-check all green (0 open positions DB+IBKR, 0 breakers, 0 stops today, IBKR connected, no signals in flight; snapshot
/root/deploy_snapshots/pre_restart_378-384_20260821T151542Z); server was already at origin/master7340dd5so the pull was a no-op — the restart itself activated the trader-side code.deploy_from_github.shfull run (pycache cleared per #262; close-persist 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 showsprnewswire (corroboration-only, #379)with no BusinessWire and ZERO RuleB lines; GUARD_REGISTRY 16 entries (no RULEB knob),consistency:guard_overridesok; integrity 72 rules, 70 ok, same 2 known FAILs (storage drain-until-~Aug-27 + FIVE@2026-08-26 disputed date), rule set identical pre/post; §G probe proved the #384 closed=true fallback on DE's closed market. The #378/#384 epochs (Aug 23) are unchanged — restart landed early, enforcement starts Sunday midnight UTC. InertRULEB_*prod-.env lines removed with a dated comment (#380 plan).- Aug 21 — #387 whole-dashboard census clean-up (dashboard-only): 210 surfaces classified; 8 stale-text FIXes shipped (phase chips, "PR 2 begins next session", hardcoded 116/~109 trade counts, stale ROI-target tooltip, May-28 P&L footnote) + the #320 tail formally closed (3 never-called render functions + 7 dead DOM-id lookups deleted). CONSOLIDATE/REMOVE judgment candidates →
docs/DASHBOARD_CLEANUP_PROPOSAL_2026_08.md(operator review; nothing judgment-class shipped). NO trading behavior change.- Aug 21 — LEV-1 DECIDED (operator): leverage question CLOSED — keep 2× Reg-T margin + the 1.50× total-exposure cap unchanged. Code-verified: both gates compute against TRADING CAPITAL (
risk_manager.py:601/:1162), so the 1.50× cap binds strictly before the 2.0× margin gate. Nothing ships; no live behavior changes. SSOT: revalidation §I.2 row LEV-1; record: FINDINGS #366 update note.- Aug 21 — Finding #386: doc tree closed top-down — drift-audit C8 (orphan check) + C9 (archive index). Every live
docs/*.mdmust now be REACHABLE from.cursor/memory.mdvia doc references (BFS memory → tier-2 → tier-3; an unlinked doc fails the unit suite). NEWdocs/archive/INDEX.md= SSOT for historical docs: all 20 archived files + 7 banner-frozen docs still indocs/indexed (what/why/froze-date/superseded-by), 3 missing R4 frozen banners added (DATABASE_AUDIT_2026_04_27/PAPER_TRADING_TEST_PLAN/OPS_TIMING_VERIFICATION), and the OPS-1 orderbook data-archive runbook linked as the DATA counterpart. Repo-side only.- Aug 21 — Finding #385:
.cursor/memory.mdleaned 695 → 316 lines (−55%) per the operator-approved proposal (scripts/analysis_357/memory_lean_proposal.md). Every binding rule preserved (verbatim, merged, or as a pointer): §K+§N merged, §L volume definition folded into §M, MAIN DIRECTIVES dissolved into §F/Principle 2 (the old "NEVER remove docs content" directive superseded by §DOC/R4 — operator decision), incident narratives compressed to anchors + FINDINGS cites, the ~150-line ops/API reference tail replaced by a Quick Reference (module API snippets moved to this doc's "Module quick reference" section; server/services/conviction-tracker were already in OPERATIONS.md; Hyperliquid →docs/HYPERLIQUID_API.mdpointer). Drift fixed in the same pass: stale Apr-25 §K numbers → Playbook §1 lookups, the archivedSTRATEGY_IMPROVEMENTS.mdclaim removed, §F #354 line extended per #369. Repo-side only — the server's memory.md copy synced at the Aug 21 15:17 UTC trader-restart deploy (rode that window as planned).- Aug 21 — Finding #384 (A-14, the FINAL Class-A item — queue complete 14/14): silent coverage bugs fixed (DEPLOYED Aug 21, 2026 — the 15:17 UTC market-safe trader restart). Two classes from the i1 missed-outliers census (+$465 demonstrated): (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; the collector had stopped snapshotting and the #333 live-Gamma fallback queried without
closed=true(the #375 exclusive-filter trap) → [] for exactly the closed markets it existed to save →conviction_unavailableat DEBUG/WARNING. Fixed: closed=true retry on empty (resolved degenerate prints stay rejected) + 2-attempt transient-HTTP retry (the scan runs once/day — one 5s blip was permanent) + WARNING-level failure logs. (2) channel-window leaks (MA/WWD class) — inconsistent timing resolution made BOTH channels say "not mine" (MA: AMC on the Jul-29 scan, BMO on the Jul-30 scan; WWD: only ever surfaced by the wrong channel) until the right channel's entry moment had passed. Fixed: NEWclassify_timing_mismatchstamps the distinctskip_reason='timing_channel_leak'+ ERROR log when the resolved channel's entry moment (AMC 10:39 ET earnings day; BMO midnight ET earnings day) has passed. TWO HIGH integrity rules:coverage:conviction_fetch(any post-epoch conviction-stage skip) +coverage:channel_window_leak(explicit marker + a timing-only structural sweep that works with any trader version); epoch Aug 23 (set assuming a weekend restart — the restart landed Friday Aug 21 15:17 UTC, epoch kept as conservative enforcement start).- Aug 21 — Finding #381 (A-13): counterfactual window-truncation fixed + layered never-again guard (deployed LIVE, collector-side, no trader restart). The CF cron priced trades MID-FLIGHT — 1,100/3,355 stored prices carried
counterfactual_computed_at < exit_datetime_scheduled(DELL's +32.1% winner stored as +2.2%): the selection had no exit-time filter and_pick_exit_barsilently returned the last available bar; resolver Pass 2 had the same hole for same-day AMC exits. Fixed in layers:run_counterfactual_for_rowREFUSES unsettled (exit +30 min buffer in the future, checked before any API call) or bar-gapped (>5 min at entry/exit, stop-hit-before-gap exempt) windows via never-persistedcf_refused_*versions (is_cf_refusal()= the caller contract); the cron SELECT excludes unsettled exits; resolver Pass 2 gains both guards; NEW HIGH integrity ruleconsistency:cf_window_truncationnames any post-epoch signature row;--requeue-truncatedre-queued the ~1,100-row backlog through the fixed job (permanent bar-gap markets now stay honestly unpriced). Truncation bias hides WINNERS (trades cut mid-flight), so analyses reading stored CF P&L under-counted outliers.- Aug 21 — Finding #382 (A-1 IMPLEMENTED — #372 approved in full): resolution-as-truth LIVE + §F backfill landed.
actual_beatis now written by a 3-tier system: tier 1 = the market's OWN UMA resolution (Gamma resolved + degenerate outcomePrices,closed=true) — terminal, write-once-LOCKED (actual_beat_source='pm_resolution_v1', CASE-guard, tripwire-tested; non-degenerate never auto-written); tier 2 = the market's stated benchmark parsed at discovery intobenchmark_basis/benchmark_comparator(migration 034; NEW sharedsrc/earnings/benchmark_contract.py): basis-matched actuals ONLY (non-GAAP → Yahoo/Finnhub street, GAAP → Polygon SEC), cent-rounded, strict>, tie→NO, abstain-on-unparseable; tier 3 = the w357 sustained-crowd verdict,~provisional-marked on the dashboard. RETIRED as label sources: the pre-event conviction cascade + the single-snapshot 0.95/0.05 read. §F backfill landed + verified on prod (1,273 resolutions stamped / 141 relabels / 29 fills / 85 trade corrections / 2 condition_id repairs; probe: 0 mislabels, 0 poisoned flags; idempotent re-run plans zero; audit CSVs with the finding). The NEWlabels:benchmark_parsedrift rule caught 6 whole-dollar-strike markets on its FIRST cycle → slug parser v2. Resolver/dashboard/integrity deployed (targeted restarts, no positions); the detector's GAAP basis guard is trader-side — DEPLOYED Aug 21, 2026 at the 15:17 UTC market-safe trader restart (with #378/#379/#380).- Aug 21 — Finding #383 (OPS-1 CLOSED via option C): lossless proof-gated archive pipeline for
orderbook_snapshotsLIVE (no trader restart). Operator approved option C after the #376 platform blocker (DO ships Apache TimescaleDB, no native compression): chunks >90 d are canonically dumped (all 24 columns), zstd-9 compressed (~50×), uploaded to the privatepolymarket-orderbook-archiveSpaces bucket (nyc3), download-back hash-checked, byte-identity proven via scratch restore, ledgered inorderbook_archive_log(migration 035), and only then dropped. Acceptance: the TTD/RKLB/SG counterfactual baseline byte-matched through a full archive→drop→restore→replay cycle.ConvictionCalculatorraisesArchivedRangeError(with the exact restore command) instead of silently degrading on archived ranges;scripts/orderbook_archive_restore.pyrestores by date range with hash verification. History: nightly 06:35 UTC timer at ≤40/night — pacing AMENDED #411 (18/run, 00:35+06:35 UTC, 6h kill-clock). 4archive:*integrity rules armed;storage:orderbook_snapshotsre-armed 20→12 GB (the Jul-27 WARN clears honestly as the backlog drains). Runbook (THE home): OPERATIONS.md §6 "Hypertable retention & archive".- Aug 21 — Finding #380 (A-12): RuleB apparatus REMOVED entirely (operator option (b)) + trader-log contradiction resolved — module/hooks/knobs/registry-entry/tests deleted (twice-refuted #330/#357-D-004; harness blind since mid-June, ~8.3k lines validating nothing); the "2.3 GB unrotated trader log" claim was TRUE at
[hidden]/logs/(unitappend:target, outside logrotate coverage — 2.33 GB + 536 MB error log) while the "0 B since May 9" refutation was the legacy/var/logtwin; logrotate extended + journald bounded 2G/14d (installed live); code deletion DEPLOYED Aug 21, 2026 at the 15:17 UTC trader restart (zero RuleB startup lines; GUARD_REGISTRY clean).- Aug 21 — Finding #379 (A-9): PRNewswire demoted to corroboration-only + BusinessWire poller REMOVED (DEPLOYED Aug 21, 2026 — the 15:17 UTC market-safe trader restart). Truth-audit §5: PRNewswire is ~2% available and its only 2 solo exit fires were BOTH false (PNC −$13.71 dividend-parse, H); BusinessWire logged 842k errors with ZERO detections ever. The #342 arbiter still let a solo PRNewswire contra claim in via the persistence path on non-GAAP markets — NEW frozen
EarningsDetector.CORROBORATION_ONLY_SOURCES = {'prnewswire'}refuses persistence-alone admission (crowd-agree and second-source corroboration stay open; pending stays armed; veto reset unchanged);_check_businesswiredeleted outright. Changelog #379 per §O.- Aug 21 — Finding #378 (A-5): admitted-stage opportunity-log rows restored (DEPLOYED Aug 21, 2026 — the 15:17 UTC market-safe trader restart). Root cause of the census flag "admitted rows silently stopped April 2026": they NEVER flowed live. All 369 historical
skip_stage='admitted'rows are the May-29 synthetic backfill of pre-Apr-22 markets (backfill_opp_log_pre_apr22.py,timing_channel='backfill'; last covered market Apr 8, 2026) — the opportunity log itself only went live Apr 22 (#227), and its funnel ended atsignal_emitted+ negative admission outcomes. Fixed: NEWTradingOrchestrator._log_admitted_signalwrites the positive admitted row at batch scheduling in BOTH cycles (after all guards + the #362 filter; admitted ≠ filled stays atrade_audit_logjoin by design), failure path is a WARNING naming the newconsistency:opp_log_admitted_coverage(HIGH) integrity rule. Analysis note: pre-#378 admitted rows are synthetic — filtertiming_channel='backfill'; the trade-census §6.1 flag is corrected in place.- Aug 21 — Finding #375 (A-3): the 5-part volume-integrity bundle SHIPPED (#365's expansion; collector + dashboard deploy only, trader untouched). (1) Monotonic
GREATESTguard on all threemarkets.volume_totalwriters (continuous_collectorupsert,gamma_collector.sync_earnings_markets,backfill_markets_volume.py) —volume_24hdeliberately stays unguarded (rolling window must fall). (2) Closed-market Gamma fetches fixed via a closed=true RETRY on empty across all four fetch sites — empirically verified theclosedparam is an EXCLUSIVE filter (open market +closed=true→[]), so #365's literal "add closed=true" would have blinded every open-market fetch. (3) Real pagination for the events sync: the API silently caps at 100 events/page (limit=500was a no-op beyond page 1);hasMore/offset loop + 50-page bound + endDate-DESC 60-day early-stop on the closed pass. (4) Entered/past dashboard rows display ADMISSION-TIME volume from the opportunity log (@admmarker) with ⛔ below-gate-AT-DECISION (real breach) vs ⓘ below-gate-NOW (display artifact) icons, in both Jinja + JS twins (new pure helpercompute_volume_display). (5) The 24h-rolling fallback carries a visible24h~marker. Re-baseline: stored finals trustworthy FIX-FORWARD only; pre-#375 finals stay under the W1/A-3 measurement ban.- Aug 21 — Finding #371 (A-2): resolver 6-hour lockout fixed structurally. The Pass-1 retry guard keyed on the SHARED
earnings_markets.updated_at, which the hourly earnings-sync timer touches on every sync — unresolved rows were re-locked every hour andactual_beatcoverage silently decayed (76.0% measured Aug 16 vs the documented ~95-99.8%). Guard re-keyed to a resolver-OWNEDresolver_attempted_at(migration 033);--force+ the #343 same-day exemption preserved; the decayed backlog self-heals on normal timer fires. No trader restart (resolver = oneshot timer).- Aug 21 06:56 UTC — Findings #369/#370: OPS-4 + OPS-5 both root-caused, fixed, CLOSED and DEPLOYED (second deploy of the day, same safe window). #369 (OPS-4): the nightly trader deaths (~04:15 UTC and ~06:06 UTC, 2/night since ≥Aug 15) were self-inflicted watchdog kills — unbounded ib_insync request-response calls (11
reqAllOpenOrders+ 3qualifyContracts+ 1reqContractDetailssites the #354 sweep missed) hung the main loop on the half-dead socket after the nightly IBKR server reset (~00:13 ET) / IBC 06:00 UTC Gateway AutoRestart; NOT OOM, NOT the apt neighbor (aggravator only — needrestart bounced the Gateway on library-update nights, now excluded via deploy-managed conf). All 15 sites bounded with explicit degrade paths (skip enrichment / never cancel from a stale view / yfinance fallback; the reconciler RAISES so stop-autofix can't act on a stale book) +trader:restart_tripwireintegrity rule (NRestarts>0 → Telegram; baseline 0). #370 (OPS-5): reconcile-full failed since Aug 1 with zero alerts for 3 weeks = three stacked holes — the exit-2 was CORRECT (16 issues: 14 unrecoverable pre-March legacy exit-exec gaps + 2 side false positives from 3 legacySLDrows, since normalized in a documented 3-row DB repair), but the unit had noEnvironmentFile(Telegram silently disabled) and was never inBATCH_SERVICES(unwatched). Fixed end-to-end: EnvironmentFile on both reconcile units + deploy-managed installs, exec-linkage epoch2026-03-07(pre-epoch gaps = named non-fatal legacy),services:systemd_unit_healthdynamic failed-unit sweep (first cycle immediately caught + cleared a 6.5-month ghostpolymarket-ibc.service), 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 Sep 1. Integrity: 63 rules / 0 FAIL. New teststest_finding_369_*+test_finding_370_*(31), full suite 1371 green.- Aug 21 04:28–04:39 UTC — THE #366/#367 DEPLOY (operator "deploy all"): PKG-357 + Phase 1 are LIVE. Overnight market-safe window (market closed, 0 open positions, 0 breakers). Executed per the checklist: migrations 031/032 BEFORE the restart; prod
.envPDT_RESERVE_CAPITAL=17450+ the staleSIGNAL_LONG_MAX_THRESHOLD=0.95override fixed to 0.90; serverbf16b1c → 3465087; contract check PASS; all §G/#367 probes PASS (capital $10,054.38, integrity 0 FAIL, Phase-1 pill, "Trades: 0", testing frozen at 193). Monitors M-C1/K-S2C/K-B4/K-RES/K-B5 ARMED; D-6/D-7/D-9 RETIRED. The live bot now runs S2c equal-split sizing, the B-4 3-tier table, the 0.90 LONG cap, tonight-only reservation, daily-loss sole brake, and the B-5 pilot.- Aug 21 — Finding #367: Phase-1 transition encoded (records/dashboard only; rides the pending #366 deploy — NO deploy, NO trading-behavior change). The operator moved the project from "TESTING IN PRODUCTION" to Phase 1 trading with a P&L fresh start and a new trade counter, effective at the PKG-357/#366 deploy. Single-source boundary:
Config.PHASE1_START_ET(default2026-08-21, env-overridable; the deployer sets/verifies it at deploy — checklist extended). Everything derives from it (#334 lesson): header pill "TESTING IN PRODUCTION" (yellow) → "Phase 1" (blue, non-warning); header counter "193/150 trades + progress bar" → "Trades: N" counting Phase-1 trades from zero (split onentry_time, timezone-correct UTC↔ET); the Trading Stats "Test Progress" cell REMOVED; the History-tab Trading Capital ROI panel re-cut from PERIOD+ALL-TIME (the PERIOD window showed a meaningless "ROI over 2,423 days" span anchored at the 2020 filter floor) to three stacked windows: All-time / Testing in Production (start–cutover, frozen; 193 trades at cutover) / Phase 1 (cutover–, fresh start); the System-tab testing-progress card reworked to a Phase-1 card with the frozen testing-era count. Pre-existing header typo "Levrage" → "Leverage" fixed. Testing-era data NOT deleted — the era count + P&L are derived (entry_time < boundary) and frozen by construction. Docs/records: STRATEGY.md §4.0 (transition process CLOSED as historical), OPERATIONS.md mode table, changelog entry #367 (chart marker "Phase 1 start"), deploy checklist extended with PHASE1_START_ET verify + post-deploy greps. Newtest_finding_367_phase1_transition.py(phase-split windows, counter-from-zero, frozen testing count, single-source + stale-string tripwires).- Aug 20 (late) — Finding #366: PKG-GO given — the FULL PKG-357 package IMPLEMENTED in code (NOT deployed; production untouched, market open throughout). 9 implementation commits + ship mechanics, in the planned order: A-7 affordability admission (own commit) → C-Q1 capital step (
PDT_RESERVE_CAPITAL25000→17450 repo lockstep; prod.envat deploy) → S2c equal-split shrink-to-fit ≤25%/name + $9k activation gate → B-4 3-tier priority table (volume bumps + mcap spine deleted; batch multiplier flattened) → C-1 LONG band re-closed at 0.90 + M-C1 shadow → RES M1+M5 tonight-only observed-set reservation + rank-3 carve-out + A-10 twin deletion (breaker-independent per #363) → WAVE-RT (scaled prep deadline, per-batch slippage, MSP-1 record-only) → CLB-1 + A-6 (consecutive-loss breaker + counter DELETED; daily-loss 5% sole brake, DB-derived, persisted, true-ET resume; sizing re-keyed to daily-loss tiers) → B-5 small-cap LONG 0.80–0.95 pilot (own commit,B5_PILOT_ENABLED, ≤2 concurrent, migration 031, auto-revert ENCODED) → M-VOL V-BLEND shadow (migration 032). Changelog package + B-5 entries + supersession flips written at ship; FULL unit suite green py3.11. Remaining = DEPLOY ONLY (checklist inIMPLEMENTATION_PLAN_2026_08.md: manual prod.envline, migrations before restart, market-safe window). Monitors M-C1/K-S2C/K-B4/K-RES/K-B5 arm and D-6/D-7/D-9 retire at the deploy restart.- Aug 20 — Finding #365: apparent volume-gate breach (BABA $930 shown vs the $1,000 gate) deep-dived — DISPLAY ARTIFACT, zero true breaches; A-3 EXPANDED into a 5-part volume-integrity bundle (records only; NO code shipped, NO deploys, production DB read-only). 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 display slot by the dashboard's last-resort fallback (
app.py:2260) after BOTH cumulative sources failed. Root causes: (1)sync_markets_from_gamma'slimit=500silently capped at 100 by the API, no pagination (continuous_collector.py:225–238) — BABA went stale at its Aug-16 value; (2) all three Gamma cumulative fetches omitclosed=trueso closed markets return EMPTY (volume_policy.fetch_gamma_cumulative_single+ dashboard batch + per-market); (3) the 24h-rolling fallback wears the cumulative label with no marker. Class audit: 97 trades with 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%); no bypass branches, no unit confusion; PNC/CFG entered under a config-DISABLED gate (by design at the time). Plan change: A-3 (was: monotonic write guard, which would NOT have prevented this) expanded to monotonic GREATEST guard on both writers +closed=truefetches + sync pagination + admission-time display semantics (below-gate-NOW info vs below-gate-AT-DECISION alarm) + mandatory stale/rolling marker. Priority stays P2 Class A; the class has now cost operator attention twice (W1 re-baseline, today's false alarm).- Aug 19 — Findings #363 + #364: circuit-breaker audit + operator decisions recorded (records only; NO trading-code changes, NO deploys). #363 (commissioned after the 10:03 ET "3 consecutive losses — 4h pause" firing): under S2c the consecutive-loss count measures position count, not decisions (70% of 3-loss runs same-wave; 49 firings/47wk in 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; monitor D-9 armed, D-4 extended. #364 (later same day): the operator APPROVED CLB-1 (counter display-only; daily-loss 5% = sole loss brake, restart-proof; hidden sizing-halving re-keyed to daily-loss tiers; A-6 elevated to ride), confirmed the ONE-PACKAGE PKG-357 manifest (A-7 → C-Q1 capital step → S2c+gate → B-4 lockstep → C-1 → RES+A-10 → WAVE-RT → CLB-1+A-6 → B-5 → M-VOL → monitors; 4 missed items flagged+added: C-Q1 step, MSP-1, D-6/D-7 retirement, ship mechanics) and recorded the pending-decisions register (FUND-1, PKG-GO — the only decision missing for the build to start, Class A queue = per-commit NOW work, A-12 fix-or-REMOVE, OPS-1 due, OPS-2/OPS-3 need ok, C-Q2 ~Sep 13, C-Q3 before ~Oct 12). The breaker stays LIVE and D-9 armed until CLB-1 ships. #364.b (latest same day): FUND-1 re-specified as a pure CONFIG/CODE change — verified live (IBKR snapshot 19:03 UTC): NetLiquidation $27,465.52 is ALREADY in the account; the "reserve" is just
PDT_RESERVE_CAPITAL=25000in prod.env; the step is 25000→17450 (trading capital = 27,465.52 − 17,450 = $10,015.52, live-recomputed; PDT floor holds with $2,465.52 margin; NO money movement) + lockstep sites (5 env-default strings,app.py:3690History-ROI hardcode N14, 3 fallbacks, 2 Jinja defaults,.env.example). Also #364.b: the P1_SURFACES census-grade re-audit of the package additions (census 148→161 — 13 alert/notification surfaces added; §11a addendum replaced by full §11/§12 inventories; flags N14–N23 incl. "no consecutive-loss counter renders anywhere today" N17 and the deadCONSECUTIVE_LOSS_LIMITknob N19) + the binding memory.md execution-discipline note under the manifest. See FINDINGS #363/#364/#364.b; manifest + register live indocs/IMPLEMENTATION_PLAN_2026_08.md.- Aug 18 — Finding #362: VIK entered a DAY EARLY (Mon Aug-17 for a Wed Aug-19 BMO release) — fix package SHIPPED + DEPLOYED; VIK left OPEN per operator decision. Root cause chain: stale Polymarket
end_timepulled VIK into Monday's window via the #333 OR-branch →resolve_schedule_basis(#349) correctly built a TUESDAY-entry signal → NO guard checked the entry DATE (#359 only rejects LATE entries) →run_bmo_cycle's "All BMO have same entry time" assumption executed it at Monday's batch time. Shipped: entry-date invariantwrong_entry_date(all 4 admission loops + batch scheduling + execution moment), post-resolution window re-checkschedule_outside_window(closes the #333↔#349 seam), integrity ruleconsistency:open_position_release_window(HIGH; VIK Aug-17 allowlisted as the known incident), multi-day BMO reservation dedup, dashboard ET timestamps + WRONG-ENTRY-DAY red highlight. Class audit (193 trades): 4 genuine wrong-date entries (CRSP −$6.04, SBUX +$8.91 luck, SBUX −$0.06, VIK open), net +$2.81 by luck; ON/NET are post-hoc DB date drift, NOT wrong entries — recorded as the PLAYBOOK §5 date-drift measurement rule (rows deliberately NOT repaired). 44 new tests; 1058 unit tests green.- Aug 17 — Finding #361: operator APPROVED all three open Part II decisions of the #357 program (records only; NOTHING implemented) + #361.b corrective sweep + deploy. C-1 (re-close the LONG conviction cap at 0.90, reverting #333/#334; M-C1 shadow monitor arms at ship) + RES (the M1+M5 reservation mechanism — observed-set BMO 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). New status
APPROVED-AWAITING-IMPLEMENTATIONwired intobuild_revalidation_357()/build_action_queue()+ all four dashboard template encodings; the #360 action plan FINALIZED (every #357-program decision is made; nothing ships without the operator's explicit go); operator-requested plain-language reservation explainer added (doc §II.2.1 + RES queue row). #361.b (same day): the #361 sweep was INCOMPLETE — the operator caught stale PENDING rows on the live dashboard. Root causes: #361 was never deployed (the live server still served the #360-era report) AND #361 missed surfaces (this doc had NO #359/#360/#361 entries; STRATEGY_PLAYBOOK.md banner still said "OPEN operator decisions"; REVALIDATION §10 + canvas still said "pending deploy" for #358). All fixed; new status-vs-description tripwire tests added (approved/decided/implemented rows must never carry a pending marker in their text); DEPLOYED to prod (see FINDINGS #361.b for the full ledger).- Aug 16 (late) — Finding #360: Strategy-tab decision-state reconciliation (records/dashboard only; DEPLOYED Aug 16, 2026 20:22 UTC with #358/#359).
build_revalidation_357()gained the Part I/II decision record (C-Q1/S2c/B-4DECIDED-NOT-IMPLEMENTEDwith the HARD ≥$25k PDT callout; C-2 SUPERSEDED by B-4; D-2 discharged to a shadow monitor), the ordered action plan and the consolidated 16-row §II.7 monitor table; Action Queue reconciled with nothing silently dropped (new rank-1package-10k-s2c-b4-357+class-a-fixes-357+reservation-m1m5-357+smallcap-long-b5-357rows); newDECIDED-NOT-IMPLEMENTEDstatus wired into all four template encodings (the #334 tripwire).- Aug 16 (late) — Finding #359: stale-signal guard at ADMISSION (operator-approved B-3; DEPLOYED Aug 16, 2026 20:22 UTC). Signals whose intended entry time is >5 min past are rejected as
born_dead_scheduleBEFORE consuming an admission slot (all 4 admission loops; config knobSTALE_SIGNAL_MAX_ENTRY_LATE_MINUTESinGUARD_REGISTRY; W6 Bug-B_log_exposure_skipdate-field fix rode along, closing queue item A-4). 19 new tests.- Aug 16 (late) — Finding #358: A-8 print-density collapse ROOT-CAUSED + FIXED (code+tests committed; DEPLOYED Aug 16, 2026 20:22 UTC — first reaper run closed 1,288 stale rows, open markets 2,093 → 805). W7's "fix A-8 first" was a deferral: the collapse is three of our own collector bugs, not API decay. (1)
sync_markets_from_gammanever closes markets that leave Gamma's unarchived window → 2,093 'open' rows / 1,288 dead → the poller's sweep takes 5–9 min → per-market cadence = cycle time (W7's 49–117 prints/day). (2) The WebSocket collector froze its subscription list at process start (new markets never covered) and its unit lived only on the server, outside the deploy restart list. (3) Its writer stored raw order-book LEVEL prices of EITHER token (NO uninverted) asyes_price— only 0.7% of Jul-14's 86,671 WS rows match the true bid/ask mid; a dedent bug also dropped all but the last entry per batch. Historical WS-signature prints (volume_24h/liquidityNULL) are NOT market prices — exclude from analyses; #336/#339's "noisy stream" was largely this bug, so #341's 300s/60s tuning is tainted. Detector side: the PRIMARY path still used the unwindowed median-of-5 (#351's Hyatt geometry) — now usesselect_fresh_median(900s/3-print) + live-Gamma fallback that persists its reads. Fixes: stale-open reaper (3-day), WS resubscribe-on-reconnect + 15-min watcher, YES-oriented midpoint writer, unit in repo + deploy list. 14 new tests (976 green). See FINDINGS #358 +docs/STRATEGY_REVALIDATION_2026_08.md§10 (G-002/3/4, G-008, A-8 rewritten).- Aug 16 — Finding #357: FULL STRATEGY RE-VALIDATION at n=183 COMPLETE (9 workstreams + synthesis; ZERO trading changes shipped — all candidates queued for operator decision). Verdicts over the 80-row assumption register: 52 HOLDS · 8 WEAKENED · 5 REFUTED (core premise as stated; the 0.90–0.95 reopened band — FAILED its contractual re-gauntlet on real fills at −$1.90/trade vs +$6.19 promised; priority as P&L ranker; the #313 volume→WR gradient; instant-exits-beat-stops) · 10 INSUFFICIENT-DATA · 5 DORMANT. The net book decomposes into stop-mechanics + the named-5 outlier tail (+$400.92 > total net +$120.80) + selection cells; pre-release drift is generic and ≈0 post-Jul (falsifiable KILL monitor armed). Resolver truth bug corrected the calibration story (Brier 0.176→0.143; overconfidence attenuates; post-June "deterioration" REVERSES). Volume-collapse narrative re-based (44% of markets store deflated final volumes; May–Jun was the real, seasonal trough). Operator decisions pending: C-1 re-close LONG cap at 0.90, C-2 drop +2@$5k bump, ≥$10k capital question, detection-apparatus funding, Oct capacity plan. (All since resolved: $10k DECIDED + C-2 SUPERSEDED by B-4 — #360, Aug 16; C-1 APPROVED Aug 17 — #361; C-Q2 detection funding + C-Q3 Oct capacity remain open by design.) Full report:
docs/STRATEGY_REVALIDATION_2026_08.md; dashboard Strategy tab gained the "2026-08 Full Re-validation (#357)" section + FAILED-REGAUNTLET action-queue status.- Aug 3 — Finding #354 SHIPPED: trader froze mid-batch at 15:58 ET on an un-timed-out IBKR request — 6 BMO entries (~$3,566) lost while IBKR was healthy.
ib.qualifyContracts()(and every other ib_insync request-response call) runs the event loop until the gateway answers with NO timeout; a half-dead socket froze the batch on symbol 3 of 6, the 120s watchdog restart landed after the close, and the whole wave was lost (second same-class stall that day at 06:06 UTC during the gateway auto-restart). Fixes: NEWIBKRClient.bounded_request(util.run timeout,IBKR_REQUEST_TIMEOUT_SEC=10) wired into ALL 15 request-response call sites (§E sweep: 9× qualifyContracts, reqExecutions, 4× reqAllOpenOrders) — timeout → degrade (skip symbol / abort order), never hang; NEWBATCH_PREP_DEADLINE_SEC=45— the batch prep loop submits whatever is prepared when its deadline expires (partial entry beats zero entry);get_account_info's mislabeled "place_market_order" connection guard fixed (it misled the incident review). Watchdog (120s) deliberately unchanged — now a true backstop. 14 new tests (929 green).- Jul 31 — Finding #353 SHIPPED: History-tab Error Log window + error-ledger backfill + SBUX #349 reclassification + 5 doc corrections (operator-approved; accounting/display only, NO strategy-behavior change). All past operational error losses inventoried into
trading_errors(now 6 rows, −$248.79 all-time): VIRT #352 −$237.09 · V #83 −$8.08 · XOM #89 −$2.84 (both already stats-excluded; backfilled for one honest ledger) · SBUX #349 −$0.78 net (MOVED out of strategy stats — relabelederror:born_dead_schedule_349in both tables; NEWerror:%exclusion inTRADE_METRICS_FILTER_SQL+ twin filters §M) · ON #178 + PFE #109 unquantified placeholders (legs never in the DB). Stats delta verified: 162→161 closed, net +$114.77→+$115.55, gross +$373.82→+$373.88. Kept counted: false-detection exits + conviction-bug entries; VZ +$32.64 stays put. Twin repair:trading_positionsid 45 aligned to audit id 11 (stop_loss_software_backup−$15.07; stays counted). NEW collapsed "⚠ Error Log" section on the History tab (row-level ledger, ONE query, Jinja-only). Doc corrections: SBUX was −$0.06 gross not −$0.67; "stats derive from trading_positions" was wrong on 7 surfaces (truth:trade_audit_logviaTRADE_METRICS_FILTER_SQL; invariant = "no stats code reads trading_errors"); #178 ON legs never "recorded in DB"; #179B −11.2% was the unrealized trough (realized −3.0%). 8 NEW tests (915 green).- Jul 30 (evening) — Finding #352.b SHIPPED: the #352 dedup guard made marketability-aware (operator-caught same-day flaw). Scenario: 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, and the #352 guard (a dumb counter) deferred to that stale limit forever — including in RTH, where it would no-op both the re-fired instant exit AND the 10:32 ET scheduled exit. Fix: NEW
_working_exit_orders_marketable(SELL limit ≤ bid×(1+tol), BUY ≥ ask×(1−tol), tol=EXIT_ORDER_MARKETABILITY_TOL_PCTdefault 0.002; quote unavailable → fail-safe defer; queued MARKET outside RTH → cancelled belt-and-braces); both guards cancel-and-reprice stale orders and proceed (bounded ~5-min limit chase outside RTH, ≤1 working order, never MARKET outside RTH; hardware stops were never affected — STP-LMT outsideRth=True GTC). 17 NEW tests; 907 unit green.- Jul 30 (afternoon) — Finding #352 SHIPPED: VIRT −77 NAKED SHORT (worst operational loss to date, −$237) + working-order-aware exit engine. The (correct, #351-verified) VIRT pre-market instant exit looped 8 cycles (re-armed every 5 min by the #141 stale-clear); each cycle placed a LIMIT+MARKET sell pair that NYSE queued until 09:30 ET (Error 399 is a warning — orders stay alive); the #199 duplicate check was position-based and blind to working orders. One order filled pre-market → DB closed → trader forgot VIRT → at 09:30:00.0 the seven surviving MARKET orders sold 77 shares against a flat book → unprotected naked short, flattened ~2.2h later on operator instruction (BUY 77 @ $57.53). Fixes: working-order dedup guard in both exit paths (
exit_orders_already_working), entry clamp to actual IBKR position + zombie cleanup when already flat, NO MARKET orders outside RTH (extends #90C), cancel-zombies-before-stop-restore, NEW integrity ruleconsistency:external_ibkr_position(54th), NEWtrading_errorsledger (migration 030) + dashboard "⚠ Error Losses (excluded from stats)" card — the −$237.09 is visible but excluded from P&L/win-rate/all stats BY CONSTRUCTION (corrected #353: stats derive from trade_audit_log via TRADE_METRICS_FILTER_SQL, not trading_positions as originally written; the invariant is "no stats code reads trading_errors"). Lineage honestly recorded: #58/#83 → #199 all checked positions, never working orders. 17 new tests (890 green).- Jul 30 — Finding #351 SHIPPED: Hyatt false "earnings miss" exit — the #342 arbiter's crowd witness was blind on sparse markets. Hyatt BEAT (Adjusted Diluted EPS $1.12 vs $0.91; crowd pinned 0.9995) but PRNewswire parsed the GAAP $0.38 from the dual-format headline → false MISS claim (VZ/#264 class). The arbiter demanded corroboration, but its unwindowed median-of-5 blended the ONE fresh 0.9995 BEAT pin with four 25-min-old pre-release 0.77-0.79 prints on H's sparse market (~11 prints/hr) into "neutral 0.79" — the veto earned 3 min before the persistence clock expired never fired; correct LONG exited −$18.63, labels poisoned. Same day: the VIRT miss exit verified CORRECT (Virtu in-line $1.82 = market resolves NO; crowd 0.0005). Fixes: fresh-window median (≤900s, ≥3 in-window; new
CONTENT_CROWD_MIN_FRESH_PRINTS), LIVE Gamma fallback for sparse streams (accepts resolved prints),check_position_for_exitconsensus recovery (zero consensus was fabricating BEAT reads on the periodic path — a SHORT false-exit risk), §F repair of H rows (instant_exit:false_detection/beat/$1.12). 12 new tests (873 green).- Jul 29 (very late) — Finding #350 SHIPPED: Polygon plan-downgrade incident — subscription silently dropped to the free tier (5 req/min) ~Jul-28; #348's root cause CORRECTED. etfstack-signal hard-failed (exit 3, SPY 429 x3 retries) and the whole account was throttled (371 resolver + 52 equity-bars 429s that day); an empirical probe proved exactly 5 requests/min succeed then 429 "upgrade your subscription". #348's schedule-collision diagnosis was wrong as TRIGGER (collision was latent-and-harmless for months; Jul-26/27 logs show zero 429s) — correction note added to #348. Operator restored the subscription (verified 12/12 rapid 200s); hardening shipped: NEW
src/utils/polygon_rate_limiter.pycross-process token bucket (flock'd state file,POLYGON_MAX_RPS=10 combined default, fail-open) wired into all 6 scheduled/live consumers; plan-limit 429 bodies write a flag artifact + NEW integrity ruleconsistency:polygon_plan_downgrade(HIGH, 53rd rule, self-clears on next success); etfstack backoff (2,5,12)→(15,30,65)s so retries escape the 60s quota window; etfstack live runs may fall back to yfinance for ETF dailies (operator-approved, WARNING-logged, whole-series). 21 new tests (861 green).- Jul 29 (late) — Finding #349 SHIPPED: earnings-date consensus verification + admission guards (SBUX instant-exit war fix). SBUX was entered and closed 20s later ("Scheduled Exit", −$0.67 — corrected #353: −$0.06 gross / −$0.78 net; since #353 an error-ledger entry, not a strategy trade): the single-source date verifier (NASDAQ, correct: Jul-29) and the hourly Polymarket-derived sync (wrong: Jul-28) fought over
earnings_datefor 2 days — 4 corrections, each clobbered back. The trade entered seconds after a correction, was SELECTED on Jul-29 but SCHEDULED from the Jul-28end_time→ exit 5.5h in the past at entry. Fixes: multi-source consensus verifier (DB/NASDAQ/Finnhub/yfinance voting, ≥2-vote majority; single-source auto-correct removed) stampingdate_confidenceconfirmed/disputed; confirmed dates LOCKED against all 3 Polymarket-derived sync writers; 3 new admission guards (earnings_date_disputed,earnings_schedule_mismatch,scheduled_exit_not_future);earnings_date_audittable + 2 integrity rules (disputed WARN, churn-war HIGH); dashboard ⚠ DATE? badge; migration 029; SBUX corrected to Jul-29 AMC per official IR. 26 new tests (840 green).- Jul 29 — Finding #348 SHIPPED: Polygon 429 storm fix (equity-bars collector). ⚠️ Root cause CORRECTED by #350: the schedule collision (equity-bars @ 10:00 UTC + daily resolver @ 06:00 ET, both 20 req/s) was real but NOT the trigger — the actual trigger was the plan downgrade (#350); the collision had run daily for months with zero 429s. The #348 fixes remain valid hardening: shared fetch helper retries 429/5xx with 2s/8s/30s backoff (+
PolygonTransientErrorfor honest classification); collector separates transient vs no-data failures; timer moved to 11:35 UTC; pacing 0.05→0.25s in all three scheduled units. 5 new tests; rerun backfilled the missed jobs; integrity green.- Jul 29 — Finding #347 SHIPPED: 150-trade checkpoint passed, auto-pause removed (operator decision). The TESTING_PROD auto-pause at 150 round-trips (which zeroed all position sizing) was latent — in-memory counter reset on every restart — and the operator chose to continue trading; milestone is now log/alert only. Also Finding #346 (Jul 22): BMO-reservation override threshold was still 0.90 on the LIVE orchestrator path (#333 lockstep miss) → 0.95 + twin-divergence tripwire test; and Finding #345 (Jul 22): backup service wedged 7 days by disk-full death spiral (cleanup ran after pg_dump; 27 GB of stale local backups) → cleanup-first + 2-day retention.
- Jul 15 (evening) — Finding #344 SHIPPED: honest exit labels (operator-caught discipline failure in the #342 repair). The #342 §F repair had deliberately KEPT PNC's
instant_exit:earnings_misslabel ("historical fact" + note) — but notes never enter SQL aggregations, so miss/beat statistics would forever count the parse-bug exit as a real miss (violating the #328 honest-labels precedent). Full §E audit of ALL 13 outcome-labeled exits (cross-checked against resolver actual_beat + the market's own RESOLVED price + external EPS): 5 false-detection exits relabeledinstant_exit:false_detection(ON Feb-10 #264-class, ZM + IONQ Feb-25 pre-release, VZ Apr-27 #264, PNC Jul-15 #342), 2 poisoned actual_beat corrected to the market's resolution (AXP True→False resolved 0.0005 — EPS-tie heuristic; XOM False→True resolved 0.9995 — GAAP heuristic), prediction flags fixed on 6 rows in both tables (resolver Pass 4 only fills NULLs). NEW self-detecting integrity ruleconsistency:exit_label_vs_resolution(HIGH) fails on any future label-vs-resolution contradiction. 4 new tests; verified green on prod.- Jul 15 (afternoon) — Finding #343 SHIPPED: intraday resolver cadence + crowd-read badges (completes #342's single-writer model). JNJ/CTAS beat (PM pinned 0.9995, aligned with our LONGs) but the dashboard showed nothing: #341/#342 deliberately silenced same-side detection signals (the actual_beat poison class — which had also been the position-row badge feed), and the EPS resolver — now the SOLE actual_beat writer — ran once daily at 06:00 ET, BEFORE same-day BMO releases → outcomes settled ~24h late. Fixes: (A) NEW
polymarket-resolver-intraday.timer— light pass (earnings stage, 3-day lookback) every 30 min in release windows (06:15-10:45 + 16:15-18:45 ET; daily 06:00 full pass stays as backstop) → same-day actual_beat ≤~30 min after release; (B) position rows without a detector outcome show a display-only "📊 BEAT/MISS · PM" crowd-read badge via the SAME_compute_outcome_statushelper as the Upcoming table (§M, honestly labeled, superseded by the resolver verdict). Same-side detection writes NOT re-enabled. Also noted: JNJ stopped out 9:03 ET (−$15.22) on a correct BEAT — sell-the-news, JPM pattern.- Jul 15 — Finding #342 SHIPPED: unified exit arbiter — the last unilateral-authority source class closed. PNC beat clearly ($4.81 vs $4.41) but PRNewswire's parser extracted the DIVIDEND ("to $2.00 per share") as EPS → false MISS → instant exit of a CORRECT LONG at 6:34 ET (−$11.14) while the live crowd said 0.91 BEAT; actual_beat poisoned, monitoring killed. Root cause = the AUTHORITY MODEL (first-to-detect-wins), same defect as ON/VZ/JPM/WFC in different clothes. Shipped: (1) content-source CONTRA claims pass ONE arbiter — crowd-agree → exit immediately; crowd-neutral → 2nd source or 300s persistence; crowd-contradict (≥0.90 vs MISS claim) → VETO + persistence-clock reset; (2) NO detection signal writes actual_beat (EPS resolver #227 = sole writer, removed from BOTH persist writers per §E); (3) NO detection signal stops monitoring — callbacks dedup per outcome, RE-FIRE on flips, reconciler re-registers positions even with an outcome (closes the #337 restart gap); (4) parser hardened (number-first "$4.81 Diluted EPS" shape, dividend/buyback/book-value exclusion). NEW test_finding_342_exit_arbiter.py (22 tests incl. PNC/VZ/ON incident replays + SET-actual_beat tripwire); 803 green. §F: PNC rows repaired (actual_beat→True, eps→4.81, outcome→beat).
- Jul 14 (night) — Finding #341 SHIPPED: Polymarket price detection becomes EXIT-ONLY (contra-position signals, 5-min sustained confirmation + 60s grace, never settles the record). Pre-deploy acceptance replay of #339's decision-grade thresholds over ALL 13 high-liquidity resolved markets exposed two problems: (1) 30s confirmation let 0.1-4 min post-release whipsaws (IMAX/OKTA class) exit CORRECT positions (3 false exits); (2) same-side "resolution" pins are untrustworthy — WFC sat ≥0.98 for ~1.5h before flipping to its true MISS, and 5 more markets did the same — under the old design those settled
actual_beatand STOPPED monitoring (the #337 poisoning class) from signals that exit nothing. Shipped: PM source emits ONLY contra-position signals (both grades; nothing without a direction), every PM signal needs 300s sustained consensus (POLYMARKET_DECISION_CONFIRMATION_SEC) with 60s grace for brief median pops (POLYMARKET_CONFIRMATION_GRACE_SEC— without it save latency ballooned 22m→73m), PM signals never writeactual_beatand never stop monitoring (EPS resolver #227 owns ground truth), and the wire-source contradiction shield now uses the LIVE median extreme instead of a sticky cache. Replay scorecard: saves 2→4, false exits 3→2 (both remaining = crowd decisively wrong 5+ min, cost is only an early exit now), median save latency +22m from crowd flip vs +104m on the pre-#339 stack. 19 detector tests re-pinned to contra-only (4 new); 781 unit tests green.- Jul 14 (evening) — Finding #340 SHIPPED: integrity rules caught #339's blast radius (6 rows NULL conviction_at_exit/hold_duration from fallback closes) → data repaired + fallback path widened + deploy-time close-persist SQL contract check. The #263-era null-rate rules fired exactly as designed after #339 routed all 5 Jul-14 closes through the minimal fallback path (6th row = VZ Apr-27, the original #263 incident). Systemic fixes: (1) fallback now persists exit/total commissions, conviction_at_exit, hold_duration, prediction_correct (they need no fragile SQL — only margin-ledger aggregates stay primary-only); (2) NEW
scripts/close_persist_contract_check.pyruns the REAL close-persist against production PG on EVERY deploy with a synthetic row + fractional commission (the #339 22P02 trigger shape mock-DB tests can't catch), asserting the PRIMARY path succeeded — PG-type-resolution bugs (#263, #339 class) no longer wait for the first live close. §F: conviction_at_exit backfilled point-in-time on all 6 rows (±30min median; last-60min pre-exit median for the 3 whose markets closed early). Integrity verified green.- Jul 14 (post-open) — Finding #339 SHIPPED: WFC 48-min miss-detection lag + Jul-14 fee loss (3 bugs). WFC's PM market collapsed 0.99→~0.12 at 8:39 ET after its miss but (1) the #336 all-of-5-prints rule was unsatisfiable on the mixed bid/ask stream, (2) the dying book never pinned ≤0.02, (3) consensus_eps was silently $0.00 (Yahoo disabled) → no detection; LONG bled to the stop at 9:27. Fixes: median-of-5 statistic + decision-grade post-typical-release thresholds (≤0.25 MISS / ≥0.90 BEAT, BMO 7:00 ET / AMC 16:05 ET) + consensus_eps DB fallback. Fees: COALESCE bare-0 typed the commission param INTEGER → first fractional Tiered commission crashed the primary close persist → fallback dropped fees; fixed with explicit numeric casts (both writers) + §F backfill of JPM/WFC from logged commissionReport values. JPM itself was a correct BEAT + sell-the-news stop (by design).
- Jul 14 — Finding #338 SHIPPED: canonical chronological Strategy Changelog (operator-directed discipline). NEW
src/earnings/strategy_changelog.py(26 standardized entries, launch→#337, fixed schema with supersession chains) feeds BOTH the new Strategy-tab 📜 Strategy Evolution timeline (newest-first, month-grouped, status filters) AND the History-chart markers (derivation verified byte-identical to the old hardcoded list). Schema test-enforced (9 tests incl. content-depth minimums); NEW memory.md §O makes an entry MANDATORY for every future strategy change in the same commit. Stale phase-status banner ("Last shipped: May 28") fixed in the same audit.- Jul 14 (pre-market) — Finding #337 SHIPPED: release-window gate on the price-based earnings detector. JPM's book spiked to 0.997 for ~3 min at 8:26 PM ET the night before its BMO release — sustained enough to legitimately clear #336's all-of-5-prints check AND the 30s confirmation → detector confirmed a false BEAT, wrote
earnings_markets.actual_beat=True, stopped monitoring, and (after 2 overnight asyncio-reconnect crashes, separate issue) the reconciler skipped re-registering JPM (if not earnings_outcome) — leaving it without instant-exit protection into its real release. Fix:_check_polymarket_priceis now gated by_release_window_open(BMO ≥ 4:00 AM ET on earnings date / AMC ≥ 3:30 PM ET, DB-derived, cached, FAIL-OPEN; content-based sources ungated so true early releases still detect). DB corrected: actual_beat + position detection fields NULL-ed → JPM re-registered on restart. 4 NEW tests (779 pass).- Jul 13 — Finding #336 SHIPPED: false pre-event "BEAT - PM" badges (JPM/GS eve-of-earnings) + earnings-detector single-print hardening. Operator caught JPM/GS showing BEAT outcome badges the night BEFORE their Jul-14 BMO earnings. Root cause: thin pre-event books intermittently print ≥0.99 in
price_snapshots(JPM 5.6% of prints, interleaved with 0.75s in the same second) and both the #280 dashboard helper AND the #264 trader detector read the SINGLE latest print. Trading impact: NONE on entry (qualification = volume×conviction×depth; conviction path uses the #325 windowed median → robust), but latent post-entry risk: a noise-sustained false BEAT would poison the Yahoo contradiction shield AND permanently write a wrongearnings_markets.actual_beat. Fixes: dashboard PM-extreme branch now requires the release window to have passed; detector requires ALL of the lastPOLYMARKET_EXTREME_MIN_PRINTS=5prints at the extreme (30s confirmation stays on top). 9 NEW tests (774 pass).- Jul 11 — Finding #335 SHIPPED: busy-week capital bundle (operator-directed, pre-Jul-14 bank wave). (1) Exposure cap 1.40 → 1.50 (
MAX_DAILY_EXPOSURE; flat-25% sizing fit only 5 positions = 125% under 1.40, stranding 15%/night — 1.50 fits exactly 6; targeted granularity fix consistent with #315's declined broad 2.0 raise; margin-safe vs the enforced 2.0× Reg-T gate). (2) Admission freed-capital fix:can_open_positionnow nets out positions exiting before the new entry (10:32 BMO exits vs 10:34 AMC entry; 15:57 AMC exits vs 15:58 BMO entry) — pre-fix a full overnight book auto-rejected EVERY next-morning AMC (would have killed UAL/NFLX in the Jul-14 week); the Jan-28 fix covered batch sizing only. (3) BMO-reservation re-based:get_actual_available_capitalreturned raw day-trade BP (~$110k) so the reservation never bound; now strategy budget bounded by BP. Session breaker (#312): 9-day counterfactual shows it would have blocked only 4 entries netting −$15.78 (SAVED money); agent recommended keep-as-is, operator chose threshold 3→4 (#335.b) — tolerates one more correlated stop on 6-slot days (skip-reason string kept). Deep-dive context: Jul-14 week expects 7-14 entries (record 6/day, ~2/wk avg); TESTING_PROD 150-trade counter noted as in-memory-only (resets each restart, never fires as documented).- Jul 11 — 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 entirely).
priority.pystill carried its own independent ≥0.90 zero-score encoding of the #202 cap + no 90-95 rows inPRIORITY_TABLE→ a 0.92 LONG would pass the relaxed config gate but score 0 and be silently dropped atpriority_filter(the #333 relax was inert for live entries). Operator caught the dashboard gap and specified the tiers: base 0-8 = Large 70-80/80-90/90-95 = 8/7/6 · Mid = 5/4/3 · Small 70-80 = 2 (90-95 LOWEST within each mcap group — counterfactual-only evidence; small caps stay 70-80 LONGs only; SHORT miss-conf ≥90% stays SKIP) + #313 volume bump → 0-10 scale (MAX_PRIORITY_SCORE=10, newLONG_MAX_CONVICTION=0.95constant). Consumers audited: signal_generator/batch_order_manager scale-agnostic; orchestrator log/6leftover fixed; app.py label bands rescaled; JS mobile/6leftover fixed; simulator auto-lockstep (imports production fn). Dashboard: legend table rebuilt (+2 NEW rows), all clamps//8→/10, stale "LONG 90%+"/"70-89%" texts fixed. Docs: STRATEGY.md §3 rewrite, FINDINGS #334, PLAYBOOK, Action Queue. Tests: re-pinned + new #334 band test; legacy health-check expectations corrected. Lesson (§M): the LONG cap is encoded in BOTHConfig.SIGNAL_LONG_MAX_THRESHOLDANDpriority.py— change together.- Jul 11 — Finding #333 (part 2) SHIPPED: #202 LONG conviction cap RELAXED ≥0.90 → ≥0.95 (real-money strategy change, operator-confirmed outright relax).
SIGNAL_LONG_MAX_THRESHOLDdefault + prod.env0.90→0.95. Lockstep per §M across every surface:conviction_calculator.is_high_conviction; simulatorrules.pyDirectionPolicySpecdefault + baseline spec idprob_threshold_70_30_with_95_skip;capital_manager._bmo_reservation_threshold0.90→0.95 (INFERENCE-DISCIPLINE catch: at 0.90 the reopened band would have silently overridden every BMO capital reservation — and this threshold turned out to be the real mechanism behind the "AMC needs 90%+ if BMO tomorrow" doc line previously suspected stale); dashboard triple-surface (app.pyconviction_ok<95 + Jinja badges✗95+/70-94%+ JS mobilemaxConv 95; also fixed the JS's stale $2k volume-threshold fallback → $1k per #311); Live Config Direction row; History-tab P&L marker (Jul-11); STRATEGY.md ×8 (rules table, priority buckets, busy-day note, AMC-reservation rows, quick-ref card); Action Queuerelax-202-9095→ IMPLEMENTED +Mar 9 #202row +conviction-band-revalidationsupersession note; 2 simulator guard tests re-pinned (741 pass). ≥0.95 skip REMAINS (fails ex-top-2 −$228). Monitor: first ~20 real fills in the reopened band vs +$6.19/trade counterfactual; re-gauntlet on real fills at trade #150 or 20 band-fills.- Jul 10 — Finding #333 exclusion-funnel coverage leaks FIXED + 0.90-0.95 band replay (leak fixes SHIPPED; relax decision PENDING-OPERATOR). (1) Channel-window hole:
get_earnings_marketsnow also matchesmarkets.end_time::date(±1-day guard vsearnings_date) so stale/sliding earnings dates can't make a market invisible to both channels (PXLW May-12 class; $1k+ market lost). (2) NEWConvictionCalculator._get_conviction_from_gamma_live— last-resort live-Gamma outcomePrices fallback (bake-off-best source per #325) when DB snapshots are missing, gated to ≤24h-old targets so point-in-time replays stay look-ahead-free (GME Jun-9 class; $22.8k market lost). (3) NEW timing PRIORITY 4.5 — infer BMO/AMC from the market's end_time hour (≤14Z/≥20Z; 1042/1058 convention) before the web fallback (GD/UBSI/HAS class). 8 NEW tests (741 pass). (4) NEWscripts/analyze_90_95_bucket.py— gap-aware (#330), commission-aware, flat-25%-sized 1m-bar replay of all 112 #202-skipped markets: 0.90-0.95 (n=78) +$482.94 net / ex-top-2 +$135.39 PASSES pooled (sub-slices mostly fail ex-top-2; convictions pre-Jun-11 from the buggy estimator; no slippage; instant-exit rescue unmodeled) vs 0.95+ (n=34) −$6 / ex-top-2 −$228 KEEP skipping. Action Queue rowrelax-202-9095= PENDING-OPERATOR-APPROVAL (relax / half-size pilot / keep). (Historical: decided next day — relax SHIPPED Jul 11 as #333 part 2; the band later FAILED its contractual re-gauntlet — #357 — and the C-1 re-close was APPROVED Aug 17, 2026 — #361, awaiting implementation.)- Jul 6 — Finding #314.f SHIPPED: position sizing FLAT 25/25/25 (real-money strategy change, operator-confirmed via AskQuestion). Replaces the Mar-9 30/22/15 tiers (#211). Basis: capacity-aware simulator at the live 1.40 cap, 578 signals / 39 wks — +$7.40/wk vs baseline, worst-week 5p unchanged (−$98.60), gauntlet PASS on two consecutive runs (May 31 + Jul 6); runner-up 35/25/18 (+$6.08/wk, worse tail) declined. Lockstep per §M:
config.pyPOSITION_SIZE_PCT_* + simulatorrules.py(jul6_flat25spec +production_strategy_jul6()factory) + dashboard Live Config/labels + STRATEGY.md §4 + memory.md + 3 sizing guard tests updated (733 pass). Monitor: small-caps now size 25% in the 5%-stop tier (#207 LONG-70-80-only mitigates); re-validate at trade #150+.- Jul 6 — Finding #332 Tiered-plan commission alignment + History-tab P&L upgrade. Operator switched IBKR Fixed → Tiered; no-shortcuts audit of the commission chain found and fixed: (1)
position_managerINT-ROUNDED exit_commission before persist (invisible at Fixed $1.00/leg, would corrupt Tiered $0.42→$0); (2) two falsy checks inclient.pymapping legitimate $0.00 commissions to NULL; (3)gross_pnlwriter inconsistency (realized_pnl + exit_commissionvsrealized_pnl; realized_pnl IS gross price P&L → standardized + corrected 97 DB rows). Verified aligned: fill-summing commissionReport capture, Flex weekly reconcile,fee_calculator(already Tiered), all displays, #331 net SQL. History tab gained Gross P&L + Commissions Paid cards and a launch-to-date cumulative gross/NET progression chart with strategy-change markers + per-rule-era performance table (STRATEGY_CHANGE_MARKERS).- Jul 5 (late) — Finding #331 P&L display now NET of commissions (dashboard/display; NO trading change). Every "Total P&L" surface showed gross price P&L while IBKR-charged commissions (#329 ground truth) consume ~55% of it at test-size positions (+$421 gross vs +$190 net). Fix per §M single-definition:
PNL_EFFECTIVE_SQL/_Tnow subtracttotal_commission(2 inline duplicates replaced with the constants);get_trade_history()period summary subtracts commissions (NOT financing again — per-trade pnl is already financing-adjusted); header quick-stat / History card / TC-ROI triplets / Database-tab stats all net automatically; labels + tooltips renamed "(NET)" with commission components; Strategy tab keeps analysis GROSS by design (per #324) butestimate_roi()gainedcurrent_pnl_net/current_roi_netrendered as a companion line. Same day — Finding #314.f sizing-up re-validation at the live 1.40 cap (analysis-only): flat 25/25/25 Δ+$7.40/wk + 35/25/18 Δ+$6.08/wk BOTH gauntlet-PASS and STRENGTHENED vs May 31 (578 signals / 39 wks incl. June); Action Queue rowsizing-up-2026-07= PENDING-OPERATOR-APPROVAL (ship flat25 vs 35/25/18 vs KEEP). (Historical: resolved next day — flat 25/25/25 SHIPPED Jul 6 as #314.f; later E-001 contractual PASS with S2c decided as successor — #357/#360.)- Jul 5 (late) — Finding #330 RuleB FAILED-GAUNTLET — Phase-2 active flip CANCELLED (simulator fix + decision reversal; NO live trading change). The operator-directed full strategy review found the RuleB simulator arm (
analyze_finding_a_full.py:simulate_window_only) was structurally OPTIMISTIC: stops crossed during the silent ±90min window were "filled" 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 (a re-placed STP-LMT triggers instantly and fills near market;simulate_finding_a_hardmodels exactly this asrearm_gap_open, the arm that retracted FA_hard at −$1,545/100 in #228). Gap-aware fix + prod re-run (n=129): RuleB −$209/100 headline / −$194/100 ex-top-2 / −$227/100 ex-repeat (was +$224/+$122/+$228) — FAIL on every §K check; optimism bias ≈ $535/129 trades; RuleG also flips negative (−$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). Consequences: Action QueueSim RuleBrow → FAILED-GAUNTLET; v3 D + v3 E RuleB dependencies REMOVED (pure selection rules at trade #150+ / SHORT n≥30); #323.b's "0.92-0.99 conditional on RuleB" lever dead; May-28 PR-1live_baseline_gauntletPASS/SHIP record marked SUPERSEDED (the §10.1 cross-check itself remains valid — both sides shared the pre-#330 rule math). NO stop-loss rule variant currently survives. Phase-1 dry-run code remains (logging only); its log spam fixed (8,026 duplicate "would CANCEL" lines for 3 positions → once per position via_ruleb_log_once). 6 NEW unit tests (gap-through/recovery/pre-window/intra-bar/SHORT/RuleG). Process lesson (audit lesson #7, STRATEGY_AUDIT_v3 §16): a TRUE simulator must model the REARM path of any cancel-and-rearm rule, not just the silence.- Jul 5 — Finding #329 Commission-capture completeness + IBKR Flex ground-truth reconcile (data-integrity + ops). Follow-up to the P&L accounting RCA. Operator: capture every commission exactly as IBKR charged, in the DB, moving forward. Findings: the live
commissionReportpath (ibkr_executionssource=live/live_stop_fill, 205 rows) IS ground truth for most trades, but theIBKR_FLEX_TOKENhad EXPIRED (err 1012) — Flex ran once at setup (Jan 7-29) then lapsed and was never scheduled, so no ground-truth reconcile since January; the NULL-commission trades (31 exit + 5 entry) are early no-exec trades + commission-less recovered fills (log_backfill/manual_recovery). Also foundstatement_csvrows store commission NEGATIVE (sign convention; 2 XOM dupes) → backfill usesABS(). Fixes: (1) backfilled the 7 recoverable NULL sides (+$7) + recomputedtotal_commission(fixed staleness $206.37→$229.44); (2) refreshed the Flex token (valid → 2027-06-06) + new Activity Flex Query id 1563851 (Trades, Last 365 Calendar Days, XML); (3) added a weekly FAIL-SOFTpolymarket-flex-reconciletimer (Sun 13:30 UTC) +scripts/run_flex_reconcile.shthat pulls the official IBKR statement and gap-fills exact-charged commissions (never cry-wolfs, per #297/#326). Full-history backfill pending IBKR new-query propagation (transient1001); a detached retry probe + the timer complete it. The P&L DISPLAY fix (Total P&L omits commissions) is separate + still pending operator go-ahead.- Jun 18 — Finding #328 KR exit MISLABELED "Scheduled Exit": crash + honest labels + durable logs FIXED (PRODUCTION FIX). Operator: KR recorded as a Scheduled Exit but exited 6:52 AM ET (scheduled 10:32) — RCA. It was NEITHER scheduled NOR a stop: KR reported a pre-market earnings MISS → the detector fired an
instant_exit:earnings_miss(filled 9@$60.02, 10:52:23 UTC, −$16.83); 13 s later the trader crashed persisting the close, so the DB never updated → orphaned → on restart reconciliation recovered the fill but blanket-labeled itscheduled_exit:reconciled. Bug 1 (root cause, the recurringexit 1):_persist_position_close_with_exit_detailsreferencedself.position_manager, but the attribute isself.position_mgr→AttributeErroron EVERY instant-exit persist. Why CI missed it:test_close_path_routingbuilt its mock with the same wrong nameposition_manager, so it passed against the bug — corrected toposition_mgr. Bug 2:order_executorimportedget_enginefrom non-existentsrc.db.database(→src.db.connection). Bug 3 (honest labels): BOTH reconcilers (orchestrator +reconciliation_manager, §E) now timing-aware — fill >5 min beforescheduled_exit_time→instant_exit:reconciled, elsescheduled_exit:reconciled(dashboard already renders it; stop-rate%stop%analyses unaffected). Bug 4 (durable logs): tracebacks WERE captured but buried in 362 MB of ib_insync INFO — quietedib_insync→WARNING, addedsys/threadingexcepthooks (full flushed traceback for any uncaught main/thread exception), fatal handler nowlogger.exception. DB corrected: KRtrade_audit_logid=137 +trading_positions(§F) →instant_exit:earnings_miss, exit_time → 10:52:23 (actual fill). Lesson: a single typo + a mock that didn't mirror__init__+ a lossy default label compounded to silently orphan & mislabel every instant-exit.- Jun 18 — 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
conviction_at_entry=0.885(traded Mid LONG 80-90%) but EVERY source at entry = 0.92 (→ #202 ≥0.90 skip). Root cause: a #325 regression — raising the SHAREDPRICE_SNAPSHOT_ROBUST_LIMIT9→10000 broke the no-time-window_get_latest_from_price_snapshots"latest conviction" path (used by live BMO/AMC entries via_get_latest_convictionon one-sided-orderbook/future-target). With limit 10000 it medianed over the market's WHOLE history (KR: 4021 snapshots spanning 0.50-0.95 → 0.885) instead of recent data. Affected 2 of 3 post-#325 trades — KR + ACN (both Jun 17, both should've been #202-skipped); SWBI (time-windowed path) was fine. Fix: window the latest path to the last 60 min of available data (anchored tomax(time)). Verified prod: KR 0.885→0.92, ACN 0.895→0.905, both → #202 SKIP. Lesson: a shared tuning constant served two paths with different bounding invariants — raising it fixed one, broke the other; enumerate all consumers (§B/§E). KR + ACN positions OPEN (operator decision, per ADBE #325). 3 unit tests pass.- Jun 14 — Finding #326 api-health cry-wolf FIXED (ops/integrity; NO trading change). Operator flagged
Integrity Monitor: systemd_last_run:polymarket-api-health.servicefailing for >1 day. Investigation: 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/datagone; finance data now streamed inline viaself.__next_fRSC). Why no self-recovery: real persistent upstream change + daily timer with no retry + an INFORMATIONAL-ONLY check escalating toexit(1)(cry-wolf — #297 antipattern recurring). Fix (operator chose A+C): rewrotecheck_polymarket_website_apiApp-Router-aware (GET/finance, verifyfinance-markets+dehydratedStatemarkers) AND demoted ALL website failures to 'warning' (exit 0 per #297, so it never fails the service again). Removed unusedreimport. Verified healthy (finance_markets=151); deployed +systemctl start→ integrity green.- Jun 11 — Finding #325 Conviction under-sampling bug FIXED (PRODUCTION FIX — trader conviction path). Operator flagged an open ADBE LONG the dashboard itself marked SKIP (97.5% ≥90% → #202). Root cause: the trader's
ConvictionCalculatorprice-snapshot fallback computed 0.789 vs true ~0.95 — (1) the0.02<yes_price<0.99filter (3 sites) DELETED the genuine high-conviction prints (ADBE's most-common reading was 0.999), and (2)PRICE_SNAPSHOT_ROBUST_LIMIT=9sampled 9 of ~1,700-per-±5min → noise-dominated median. 0.789 fell in the 70-90% band → #202 never fired. The dashboard was right because it reads the latest live-Gammayes_price(0.975) — a DIFFERENT path (app.py:2074). Scope audit (127 trades): ≥2 #202 violations — ADBE (open) + LULU (−$25.54, stopped); SNOW was direction-INVERTED (0.05 vs true 0.90). 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 the 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 (flickering-but-stable markets keep their correct median, NOT skipped — operator's 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 = ~±2min on the high-freq stream); raised to 10000 → ADBE 0.912 → #202 SKIP, is_reliable count + ≥0.90 membership identical at 600 vs 10000. 3 unit tests pass (+1 newtest_price_snapshot_flicker_tails_stay_reliable). Refines #197; interacts with the #283/#319is_reliablegate (now IQR+dense → fires less). ADBE held per operator; dashboard display path left as-is (correct). NEWscripts/conviction_source_bakeoff.py.- Jun 1 — Finding #323.b RuleB re-test AMENDS #323 (offline; NO production change). Operator pushed back on #323's "stops eat the dead-zone edge" — argued stop-hits are earnings-VOLATILITY-driven (the ±90min window RuleB neutralizes), NOT conviction-driven, so the conclusion was confounded by not modeling RuleB. Operator was RIGHT. NEW
scripts/phase3_simulator/replay_deadzone_ruleb.pyre-prices the excluded bands under 3 stop regimes (baseline/RuleB/no-stop) via the canonicalanalyze_finding_a_fullsimulators. ~62-67% of all stop-hits fire INSIDE the ±90min release window, flat across conviction bands (vol-driven, conviction-independent). Key correction: the LONG ≥0.90 over-confident skip (#202) — which #323 said "keep" on baseline-stop data — flips strongly POSITIVE under RuleB: +$853 total / ex-top-2 +$614 / 47.7% win (n=65), RuleB > no-stop > baseline. So #202 is confounded by the always-on stop eating in-window vol-spikes; the ≥0.90 skip may leave money on the table once RuleB is active. Dead-zone 0.60-0.70 LONG: RuleB helps (−$55→−$23) but ~breakeven even unstopped → genuinely weak edge (KEEP, correct reason). Small-cap/short: KEEP. The Action Queueconviction-band-revalidationrow was moved KEPT→IN-QUEUE, coupled to RuleB Phase 2 (re-eval ≥0.90 per-mcap + capacity once RuleB active). Note: also debugged a 30-min SSH hang (foreground multi-day Polygon fetches) → switched to detached nohup + JSON polling. ⚠️ CORRECTED same day (replay_202_ruleb.py): added a RuleB column to the EXACT original #202 dataset (the 26 REAL closed ≥0.90 LONG trades, real fills) → ACTUAL −$112/15.4% WR → RuleB −$88/15.4% WR → NO-STOP −$154. RuleB does NOT rescue the real ≥0.90 trades (directionally wrong ~85%; RuleB can't fix direction). The +$853 "flip" was a DIFFERENT population (post-#202 SKIPPED signals, later period, counterfactual idealized pricing ~$33/26-trades optimistic) and is contradicted by the ground-truth fills. #202 HOLDS even with RuleB; the ≥0.90-relax lead is downgraded (Action Queue row back to KEPT). Lesson: validate relax-the-cap leads on REAL fills + current regime, not idealized counterfactuals. #323.c (Jun 2) applied the same method to small-cap LONG 80-90% (#207): original basis = Q3 2025 backtest (10 trades / 30% WR / +$0.40 EV, near-zero); reproduced on 364-market universe (reproduce_207_backtest.py) = 49 names / 38.8% WR / +0.5% raw return (weakest band). Simulator counterfactual looked great (+$137/trade) but de-dupes to 3 names (CBT +63% outlier, MBWM ×5 identical, IART wash), 0 stops, ZERO real fills. KEEP #207 — a 3-name §K mirage; RuleB irrelevant (no stops to rescue). #323.d (Jun 11) — entry-aligned dataset: rebuilt the analysis universe on the REAL strategy clock (BMO 15:58→10:32, AMC 10:34→15:57) — conviction = windowed-medianyes_priceat entry (NOT 24h-before), stock return + per-tier stop model fromequity_bars_1m. NEWscripts/build_entry_aligned_dataset.py+analyze_entry_aligned.py+entry_aligned_dataset.json(649 markets, 351 clean 2026+). Findings:prob_24h_beforeis ~unbiased vs entry (mean diff ~0, 3.8% direction flips) BUT 36% of markets change conviction band. Re-validated with stops: small-cap LONG flat-to-neg, LONG≥0.90 ~breakeven, dead-zone negative → every exclusion HOLDS. Leads (not actions): Large LONG 70-80 = best (+$16.6/trade); small-SHORT looks +$11.9 but squeeze tail-risk + n=12. Caveat: bar-based stop = −stop% (understates gap-through); for publication, re-run via the full tick-accurate #323 simulator.- Jun 1 — Finding #323 Conviction-band exclusion re-validation → ALL exclusions HOLD (offline; NO production change). Operator asked whether the bot excludes profitable trades (raise the 90% LONG cap? trade the 0.30-0.70 dead zone? loosen small-caps/shorts?). Comprehensive check using the integrated simulator + universe data, full §K gauntlet (de-dupe tickers + ex-top-2 + real per-tier $-scale + per-mcap, stop-aware via opp_log.hypothetical_pnl). Every excluded band fails the gauntlet. Raise ≥0.90 cap: Large/Mid 0.90-0.95 ex-top-2 NEGATIVE, ≥0.95 NEGATIVE + 47% stop (first-glance positive was $10k-notional inflation + repeat-ticker concentration). Dead-zone 0.60-0.70-as-LONG: universe proxy +$307 was NO-STOPS; priced WITH stops (NEW
scripts/phase3_simulator/replay_deadzone.py) = −$54.65 / 40% win / FAIL-EX-TOP-2 (stops eat it, #228). Small-cap LONG ≥0.80 (#207) + small-cap SHORT (squeeze): KEEP. No ship; production conviction bands UNCHANGED. KEPT row added to Action Queue.- Jun 1 — Finding #324 Counterfactual P&L units cleanup (dashboard/docs; NO trading change). Operator: "the $10k-notional hypothetical_pnl is stupid — change everything to %." Chose SAFE standardization (no DB migration):
hypothetical_pnl_pctalready exists + is the scale-invariant truth (PERCENT units; pricerpnl=notional×pct/100), and the simulator already scales $10k→real correctly. Fixed a real 100× display bug — the learning-loop "Avg %" columns didavg_ret_pct*100while the value is already percent (showed 79% vs 0.79%); fixed both renderers. Honestly relabeled "Total $" → "Total $ (@$10k notional)" with tooltip (comparability-only, NOT real bot-sized). Documented units inanalyze_skipped_cost. §E grep flagged a SEPARATE suspected unit bug in the regime/VIX bucket renderer (usesrealized_pnl_pct= FRACTION, renders without ×100 → ~0.00%) — NOT flipped (out of scope + needs own verification). py_compile + jinja + node clean; deployed.- May 31 — Finding #322.f Pre-Monday trading-readiness audit → GO (test-only fixes; NO prod behavior change). After the week's heavy changes (#311-#322.e), operator asked for a full systems+code audit per memory.md before Monday markets. Live prod all green: config effective values all match ground truth (gate $1k, priority 0-8, exposure 1.40, MAX_TOTAL/MAX_POSITION vestigial, sizing 30/22/15, stops 3/4/5, breaker 3, RuleB dry-run, TESTING_PROD 1.0); 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 (8d15c7f). Full unit suite caught 7 FAILURES, all stale tests not prod bugs (verified each vs code per INFERENCE DISCIPLINE): 5× test_top_5_decisions (my #322.b scale 245→550 change; tests asserted old 0.0245/$1.84), 1× exposure test (asserted rare 0.60 but #317 made it uniform 1.40), 1× pre-existing dashboard cache-pollution flake. All fixed (test-only): updated expectations + added an autouse cache-reset fixture → 726 passed / 0 failed. Process lesson: #322.b shipped without a full-suite run; the readiness audit caught it. ⏭️ Reminder Sat Jun 6: review RuleB dry-run logs → Phase-2 active decision.
- May 31 — Finding #322.e RuleB Phase 1 DRY-RUN actually ENABLED in prod (logging only; NO live order change). Operator asked to "push RuleB to production"; pre-flight check found RuleB had never run —
RULEB_ENABLEDdefault wasFalse+ prod.envhad noRULEB_*, so the main-loop hook never fired (0 RuleB log lines in 7 days). The dashboard "IMPLEMENTED-DRY-RUN" reflected shipped code, not an observed dry-run, so the Phase-2 "review dry-run logs first" gate could not have been met. Operator chose (AskQuestion) dry-run-first + tracked config default: flippedRULEB_ENABLEDdefault False→True,RULEB_MODEstaysdry_run(_ruleb_fire_*only LOG the cancel/rearm decisions — no IBKR side effects, no live stop-order changes). Did NOT go straight to active (that's a 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, Sunday). Phase 2 (RULEB_MODE=active) pending operator review of dry-run logs.- May 31 — Finding #322.d Phase 5: ROI re-baseline (analysis/dashboard/docs; NO trading change). Final roadmap item after Phase 3 PR 1-5 + Phase 4. Re-baselines strategy ROI as a 3-epoch ladder (frozen pre-launch
PROJECTIONS→ frozen 87-trade → current ~117-trade + simulator), side-by-side per metric, + net-new metrics (5-KPI, capacity lift, §K outlier dependence, per-tier) + a hybrid forward projection (live vs simulator anchors shown side-by-side, not blended). Operator chose caution-first framing: leads with the outlier alarm (top-5 +$427 vs ex-top-5 −$205 → the entire +$222 realized edge is ~5 outliers), the tiny/frozen sample (117 trades, drought since ~Mar 2026, WR CI 29.4–46.7%), and that live +$12.32/wk < simulator +$18.11/wk — forward annualized (live ~+25.6% / sim ~+37.7% on $2,500) shown but LOW CONFIDENCE. NEWPROJECTIONS_87TRADEconstant +build_roi_rebaseline()(reuses KPI/capacity results, no expensive recompute) → reportroi_rebaseline; NEW dashboard "📊 ROI Re-baseline" section +renderRoiRebaseline(); NEW docdocs/FINANCIAL_PROJECTIONS_2026_05.md(old 87-trade doc frozen as Epoch 2; pre-launchPROJECTIONSfrozen as Epoch 1). py_compile + jinja + node + builder unit-test clean. Deployed (report regen + dashboard restart). This completes the Phase-3 strategy-simulator project end to end (PR 1-5 + Phase 4 + Phase 5).- May 31 — Finding #322.c Audit follow-ups (dashboard + config; NO trading behavior change; trader restarted to activate). Three operator-directed follow-ups from #322.b: (1) wired the dashboard risk panel —
risk_level/active_breakers/consecutive_losses/testing_prod_paused/opportunity_countwere hardcoded placeholders; now the trader attachesrisk_state(fromrisk_manager.get_risk_state().to_dict()) toibkr_snapshot.jsonnear the top (present even when IBKR down) andget_dashboard_state()reads it — single source of truth, no breaker-logic duplication (§M); read-only serialization, no trading change. (2) data-api dependency probe incheck_dependencies()+ System-tab row (replaced Dome, #289). (3)MAX_POSITION_SIZE_PCTconfirmed vestigial (no consumer; Large already sizes 30% unclamped) → re-aligned0.15→0.30+ documented vestigial, mirroring #315. Verified: py_compile + test_config (asserts 0.30) + test_risk_manager 27 passed. Deployed; trader restarted (operator-CONFIRMED, Sunday) → snapshot now carries liverisk_state; reconnected clean (net_liq $27,548.36).- May 31 — Finding #322.b Full repo-wide staleness RE-AUDIT (docs + dashboard render code; NO trading change). Operator caught that #322 took a shortcut — it claimed "every word and line" but only covered
dashboard.htmlstatic HTML, skipping the Python that generates the dashboard + all project docs. This is the honest complete re-do: all 29 files read line-by-line (strategy_performance_analysis.py4,815 +app.py6,252 +dashboard.html+ all 26docs/*.md≈ 47k lines) against one ground-truth config; ~110 stale current-state claims fixed (append-only history preserved; no fabricated numbers). Dashboard code: universe gate $2K→$1K (SQL value + labels + sweep), P&L $167/116→+$212/~117, cockpit capital/sizing $2,450/10%/$245→$2,500/22%/$550 (#211), PR2-refinement banner "🚨 IN-FLIGHT"→RESOLVED, priority labels 0-6→0-8 (removed deadps>=11), deadwatchdog_activeremoved (#290),max_trades 100→150, position-sizing 10%→30/22/15% by mcap, pricesbackfillable→False(#289). Docs (18 changed): STRATEGY §3.2 tiered→uniform-140%, STOP→STP-LMT, +data-api; PLAYBOOK bundle CLOSED + PR2-5 done; ARCHITECTURE/PHASE_3/AUDIT_v3 Phase 3 PR1-5 + Phase 4 COMPLETE; OPERATIONS/DATA_ARCHITECTURE/STATE_MACHINES service-topology + equity-bars 47k→1.9M + rules→~47 + watchdog→integrity-monitor + stops/sizing/exposure; DATA_INTEGRITY/API_REFERENCE/DATABASE_DIRECTIVES gate/rules/coverage/timers; IBKR docs Xvfb:99/IBC gatewaystart + credentials redacted; HOLISTIC/PAPER_TRADING/BMO/EDGE_CASES; FINANCIAL_PROJECTIONS SUPERSEDED banner. py_compile + jinja parse clean; deployed. Lesson (NO-SHORTCUTS): "every line" = every render surface + every doc, not just the template.- May 31 — Finding #322 Full dashboard staleness audit (dashboard-only; NO trading change). Operator: "lots of outdated info in all tabs — priority logic in trading, system tab, strategy tab … check every single word and line." Exhaustive line-by-line audit of all tabs + header (3 parallel sub-audits + global residual-token sweep) vs current production config; ~30 stale static facts fixed. Trading: priority scale 0-6→0-8 everywhere (legend/badges/clamps/labels/comments, #313); volume gate $2,000→$1,000 (tooltip + 3 fallbacks, #311); stops note STOP-LIMIT; AMC exit comment 3:58→3:57. System: exposure "rare-day 60%/140%"→uniform 140% (#317); backfill "4-pass"→6-pass; "Phase 0/1/2"→Phase 0-4 COMPLETE; long-weekend 75%→50% (matches live code); 2FA "VNC"→IBKR app push. Strategy: gate-retune "current $2,000"→$1,000 (post-#311); cockpit sizing exposure tiered→uniform; #316 panel notes superseded by #317. Database: +#307 citation. History: 5-outlier net P&L "$159"→+$212. Live code/service files verified before each change. Verified node --check + jinja parse + 0 mojibake + residual sweep clean. Frontend-only; deployed.
- May 31 — Finding #321 Strategy-analysis data completeness: Polygon backfill + full simulator re-validation (data backfill; NO code/strategy change). Operator directive: "it's paramount we have the best/most complete data possible … only after we do this we do a full run on the simulator." Audit found 472 PAST counterfactual-P&L gaps (
price_data_unavailablerows whoseequity_bars_1malready existed — original Polygon fetch failed transiently + the script never retries computed rows) + ~48earnings_marketsoutcome/return gaps. Backfilled:compute_counterfactual_pnl.py→ counterfactual P&L 1,288→1,755 priced (30 remain = 25 future + 5 delisted);outcome_resolver.py→stock_return99.8% of past markets,actual_beat948/961. Full re-run on complete data — ALL conclusions HOLD: capacity $1k gate KEEP +$18.11/wk (was +$16.40); regime #318 KEEP; depth #319 KEEP — the backfill VALIDATED the shipped KEEP decisions. Insight: the simulator universe is filter-limited (volume≥$1k / conv≥0.90 / small-cap skips → 441 post-dedup), NOT data-limited — completeness improved power without changing conclusions. Corrected the #320 Universe View label (regenerates every build viaanalyze_universe_hit_rate, not a 4h manual job). Follow-up flagged:compute_counterfactual_pnl.pyshould periodically retryprice_data_unavailablerows so transient Polygon failures don't become permanent gaps.- May 31 — Finding #320 Phase 4 (remainder): Unified Simulator dashboard consolidation (dashboard-only; NO trading/capital change). Completes the Phase-3-dependent half of Phase 4 (
STRATEGY_ANALYSIS_ARCHITECTURE.md §0.7/§0.11), un-gated now that Phase 3 (PR 1-5) is complete. The Strategy tab was fragmented across 23 sections answering different questions on different populations (live-traded / universe / Q3-2025-archive), none canonical — the operator-confusion root cause (§0.0). NEW 🧪 Unified Simulator umbrella presents the Phase 3 engine as the single canonical SoT 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 the Q3-2025 Backtest Scenario Comparison + BMO/AMC Timing Matrix off the tab (historical-only third population; producers+JSON retained). Consolidated the 5 overlapping breakdown widgets → 2 canonical (removed Live Performance Snapshot; kept Direction+Timing Matrix + Performance Breakdowns). Operator AskQuestion: unify-and-preserve (not literal delete-all-3 — keeps unique live-stop-loss + universe-PM-accuracy evidence); static regime/depth cards (not live sweeps — they're KEEP + depth needs Polygon); include the breakdowns consolidation. Frontend-only (dashboard.html); verified node --check + Jinja parse + div balance + no orphaned live DOM refs. Deployed (dashboard restart). This + #318/#319 complete Phase 3 AND Phase 4; only Phase 5 (re-baseline ROI projections) + live-monitoring remain.- May 31 — Finding #319 PR 5: Depth-gate v2 widened reconstruction → KEEP (offline-only; NO production change; closes Finding #283; Phase 3 simulator PR 1-5 COMPLETE). Answers the operator's "has the depth/spread gate ever saved us from a bad trade?" at proper sample size. The live depth-skip log only began 2026-04-14 (collapse period, ~4 unique kills); widened by replaying the REAL
ConvictionCalculator.get_conviction_at_entry_timepoint-in-time across the full 915-market universe (incl. the healthy Oct 2025–Mar 2026 window). NEWscripts/phase3_simulator/depth_gate_variants.py(pure decomposition + remove/carve-out/threshold variants) +replay_depth_gate_v2.py(binding-kill filter + raw-orderbook recovery + counterfactual equity P&L viarun_counterfactual_for_row). Result (22 binding kills): 13 losers BLOCKED (−$142.56 avoided), 9 winners KILLED (+$165.09), net removal +$22.54 over ~7 months (≈ noise). So YES it has saved us from bad trades — reversing the n=4 read where it had blocked 0 losers (small-sample mirage). Kill cause: 18/22snapshot_reliability(hollow 1¢/99¢ book → sparse/unstable price-snapshot conviction:insufficient_samples:1<3/unstable_window:range>0.35), only 1 pure spread → it's a low-information-signal filter, NOT the pure spread gate #283 implied. remove_all +$22.54 / carveout_real_trade +$22.78 (≈ identical) → removal adds variance for ~$0 EV (low-EV/high-variance proliferation the strategy avoids). DECISION: operator-confirmed KEEP. Productionconviction_calculator/signal_generatordepth check UNCHANGED. 395 tests PASS (+20). This closes the last Phase 3 deferred-bundle item (#278/#279/#283) — the Phase 3 strategy-simulator project (PR 1-5) is COMPLETE.- May 31 — Finding #318 PR 4: Regime classifier v2 sweep → KEEP compound_v1 (offline-only; NO production change; closes Finding #278). Tests whether a FASTER downtrend detector improves Rule #1 (Skip LONGs in downtrend, #304) vs the production compound rule (which lags SPY peaks by a median 31 trading days). NEW
scripts/phase3_simulator/regime_classifiers.py(pure-Python point-in-timeSpyDailyHistory— prior-completed-bar lookup, design §11 forward-look mandate — + 6 classifiers) +replay_regime_v2.py(re-classify → re-apply Rule #1 → recompute P&L viarun_sizing_sweep/apply_sizing_gauntletvs compound_v1; sizing held at production so credited == raw realized P&L). Operator AskQuestion: classifier set = equity family of 6 (VIX deferred to Phase 4 per design §15-Q5); universe = BOTH (live-only primary + capacity-aware 1.40 cross-check); point-in-time = prior-close (agent); gauntlet-floor = full-pop spillover primary decider (agent). Result (prod 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; it whipsaws out winners on the broader universe so the gain evaporates (same "don't look in isolation" reversal as #315).compound_or_dd5(#278 option B) +below_ma50inert;dd_3pct+compound_symmetricFAIL. The only effect surviving both views is a better worst-week tail (5p −$98.60→−$57.08) at ~breakeven P&L — a downside trade-off, not a money-maker. Methodology validated: PIT compound_v1 reproduces the live recorded regime 95.3%. DECISION: operator-confirmed KEEP compound_v1 (Rule #1 optimizes for ROI; faster detection adds no robust ROI). Productionsrc/earnings/market_regime_provider.pyUNCHANGED. 375 tests PASS (+22). NO deploy.- May 31 — Finding #317 SHIPPED (operator-CONFIRMED): Simplify daily exposure tiering to a single uniform 140% cap. Operator high-level review ("do we need rare vs busy? are we overcomplicating? maximize ROI not complex rules"). Collapsed
RiskManager.OPPORTUNITY_EXPOSURE_LIMITS— rare tier 60% → 140% (=MAX_DAILY_EXPOSURE); all tiers now uniform; removed_RARE_EXPOSURE_RATIO; 3 buckets kept only for rare/normal/busy logging labels. Per #316 the rare 60% cap was near-inert (bound 2% of rare days), so live impact ~1 marginal entry/36wk. Strategic framing: "more opportunities" ≠ "more ROI" — looser caps/lower gates/smaller sizing all dilute ROI; the ROI lever is concentration (sizing-up #314.c, defer-monitor), not proliferation. This is a simplification, not a ROI lever. Caught + reverted a StrReplace UTF-8 corruption of risk_manager.py mid-edit (re-applied UTF-8-safe; verified clean). 22/22 risk_manager tests PASS. Deployed (trader restarted). NO integrity change (47/47).- May 31 — Finding #316: Exposure daily-tiering re-eval (#285 Phase B) → KEEP 60/140/140 (offline-only; NO production change). Operator-requested re-eval of
OPPORTUNITY_EXPOSURE_LIMITS(rare 60% / normal·busy 140%). NEWscripts/phase3_simulator/replay_exposure_tiering.py— per-day entry-budget model (faithful to #285 greedy-fill). Prod (117 trading days): rare-day 60% cap is NON-binding (binds only 1/49 days = 2.0%, $151 foregone) → operator's "most days rare → 60% over-constrains" premise REFUTED (rare days have 1-2 small positions that rarely reach $1,500). Real binding is busy-day 140% (48.4%, $41k foregone — risk ceiling working as designed). Loosening rare cap (uniform-140 / raised-rare-100) admits +1 net-loser, total drops +$926→+$907, both FAIL-EX-TOP-2. DECISION: KEEP 60/140/140; #285 Phase B exposure-cap sub-item closed. 326/326 tests PASS (+9).- May 31 — Finding #315 RESOLVED (operator-confirmed):
MAX_TOTAL_EXPOSURE_PCTre-aligned 2.0→1.40 (unwired var, KEEP live 1.40); #313 cap-raise re-scoped as inert. Plus exposure-cap re-validation (docs audit discovery; NO production change). During the Priority 4 docs audit: the var Finding #313 raised to 2.0 is referenced only in config/tests/docs — the live exposure gate isMAX_DAILY_EXPOSURE=1.40viaOPPORTUNITY_EXPOSURE_LIMITS(rare 0.60 / normal·busy 1.40). So #313's capital-cap raise is inert, and the #314.b/c/d/e sweeps usedexposure_cap=2.0(live is 1.40 → capacity even more binding; directional conclusions hold). Traced the cap history (none P&L-tuned: v10.0 70% design → #191 2x-rollout 140% → #313 unwired 200%) and ran NEWreplay_exposure_cap_sweep.pyon prod: lift peaks near 1.0× (+$18.28/wk), flat-to-declining above 1.4×; the 1.40→2.0 raise buys only +$1.25/wk for −$41 worse maxDD. Recommendation: KEEP live 1.40, do NOT wire 2.0, re-scope #313's cap claim. Awaiting operator confirmation. 317/317 tests PASS (+4).- May 31 — Finding #314.e PR 3.b.3: 4 deferred SKIP-rule candidates — real-scaled + bias gauntlet (ANALYSIS-ONLY; nothing shipped). Re-evaluated the 4 Finding #313 deferred SKIP cells REAL-SCALED (not hypothetical $10k-notional) + capacity-aware via NEW
scripts/phase3_simulator/replay_skip_rules.py. Real-scaling corrected 2 cells (Large+SHORT+$2k-$5k is only n=3 not 12; Large+LONG[.80,.90)+$1k-$2k is 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 (best maxDD −$220, WR 39.1%). Operator ship-bar=C analysis-only → per-candidate ship decision deferred to operator AskQuestion. 313/313 tests PASS (+10). NO PRODUCTION CHANGE.- May 31 — Finding #314.d PR 3.b.5 SHIPPED: Hybrid seasonal gate variant (offline-only; KEEP uniform $1k). Evaluated operator's "$2k peak-weeks / $1k off-peak" idea. 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 (#314.b), removing them gives back most of #311's lift; capacity-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 universe). DECISION: KEEP uniform $1k (#311). 303/303 tests PASS (+7). NO PRODUCTION CHANGE.- May 31 — Finding #314.c PR 3.b.4 SHIPPED: Sizing variants sweep (offline-only; KEEP 30/22/15). First sweep on the size-aware capital simulator. Added
scale_pnl_with_sizeflag tosimulate_one_variant()(default OFF so #314.b + SKIP sweeps stay byte-identical; design doc §12 PR3 "P&L = realized_pnl_pct × new_position_value") +realized_pnl_creditedon AdmissionDecision +gauntlet.apply_sizing_gauntlet()+ NEWscripts/phase3_simulator/replay_sizing_variants.py(8 variants per operator AskQuestion option C). Prod run (568 signals / 36 wk): operator's "lower sizing for more concurrent positions" hypothesis REFUTED — lowering sizing admits more (20/15/10 → 437 vs 409 baseline) but total P&L drops −$6.80/wk (all four lower-sizing variants FAIL-SPILLOVER: marginal capacity-freed trades are low-EV). Raise/flat variants give only tiny non-robust lift (flat 25/25/25 +$1.41/wk FAIL-EX-REPEAT-TICKERS; 35/25/18 +$0.75/wk WARN-OUTLIER-DOMINATED + worse maxDD/5p). DECISION: KEEP 30/22/15 vs LOWERING. 🔁 CORRECTION (Finding #315): the run above usedexposure_cap=2.0, but the live cap isMAX_DAILY_EXPOSURE=1.40. Re-run at 1.40 — lowering still FAILS, but flat 25/25/25 (+$4.18/wk) and 35/25/18 (+$4.63/wk) now PASS the gauntlet → RAISING/concentrating is ship-eligible at the real cap (operator decision pending; caveats re: stale MAX_POSITION_SIZE_PCT + small-cap risk). 296/296 tests PASS (+14). NO PRODUCTION BEHAVIOR CHANGE.- May 30 LATE LATE evening — Finding #314.b PR 3.b.2 SHIPPED: Capacity-aware re-validation of Finding #311. First sweep on the capital simulator foundation. NEW
scripts/phase3_simulator/replay_capacity_aware.py(~410 lines) — loads full universe (127 live + 441 counterfactual = 568 signals over 36 weeks), de-dupes against live, runs simulator on production_post_313 + pre_311_baseline_2k variants, writes JSON. Prod run: post-#311 admits 409 / +$607 / +$16.86/wk; pre-#311 admits 295 / +$241 / +$6.70/wk; Δ +$10.16/wk (HALF the optimistic +$21/wk Phase γ+δ projection — gap is capacity contention: 58 capacity-rejected at $1k vs 5 at $2k). $1k-$2k bucket admitted (n=132): avg +$3.20, WR 31.8%, total +$423 → POSITIVE contribution. max_capital_used=$4,949 (99% of $5,000 cap) — capacity binding. Decision UNCHANGED: KEEP $1k gate as shipped (Finding #311 re-validation note updated). NO PRODUCTION BEHAVIOR CHANGE (offline analysis run).- May 30 LATE LATE evening — Finding #314 PR 3.b.1 SHIPPED: Capital simulator foundation. NEW
scripts/phase3_simulator/capital_simulator.py(~365 lines) — per-event timestep simulator that models capacity-constrained admission across overlapping positions.CapitalSimSignal(live + counterfactual signal input) +SizingVariant(declarative variant spec withextra_skip_predicates/mcap_pct/gate_volume_min) +AdmissionDecision(audit trail) +CapacitySimResult(aggregate).simulate_one_variant()walks chronologically-sorted signals through a heap of (exit_dt, capital_reserved); per signal: free expired → variant skip rules → variant_pct/prod_pct ratio sizing → capacity check (current + new ≤ trading_capital × exposure_cap) → admit/skip with reason. Convenience constructorssignal_from_live_trade()+signal_from_counterfactual_market()(latter applies per-tier empirical scaling per Audit Fix v3 via lazy import of EMPIRICAL_POSITION_BY_TIER). 30 NEW unit tests; 282/282 phase3_simulator PASS local. NO PRODUCTION BEHAVIOR CHANGE (offline-only foundation). Subsequent commits: PR 3.b.2 (re-validate #311 capacity-aware) + PR 3.b.3 (4 SKIP rule candidates with bias gauntlet) + PR 3.b.4 (sizing variants sweep).- May 30 LATE LATE evening — Finding #311 Re-validation Note (post Finding #313 ship). Operator-requested re-run of PR 2 analysis. Methodology audit caught my conflation of PM platform volume vs IBKR equity-execution slippage (operator correction). Hypothetical_pnl already captures prediction-quality via per-bucket WR; equity slippage depends on stock liquidity not PM volume. Live n=10 evidence on $1k-$2k bucket (-$0.77 avg) is within 1σ of simulator-projected +$1.87/trade — sampling noise, not signal. DECISION: KEEP $1k uniform gate as shipped; revisit after 30+ live trades accumulate. NO CODE CHANGE; doc-only commit.
- May 30 LATE LATE evening — Finding #313 SHIPPED: PR 3.a — Volume-tier priority bump + capital cap 1.40→2.00 + NYSE dead-code cleanup. THIRD real-money strategy ship of the day. After data-driven analysis: priority scale 0-6 → 0-8 (kept Mar 9 mcap×conv ordering since live LONG WR Large 45.7% > Mid 34.3% still validates; ADDED volume-tier bump since live LONG WR jumps 36%→64%→83% across NULL→$2-5k→$5k+). MAX_TOTAL_EXPOSURE_PCT 1.40→2.00 (Reg T overnight ceiling). Verified IBKR-side NYSE/NASDAQ behavior fully unified post Finding #185 (Feb 15 unification) → dropped is_nyse dead branch + updated dashboard legend. 617/618 unit tests PASS. NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47). NO DB MIGRATION. Production behavior CHANGES (priority now incorporates volume; up to 2× gross exposure allowed). Operator follow-up: re-validate $1k gate (#311) against new volume data — $1k-$2k bucket has been the only LOSING bucket historically (n=10, -$0.77 avg). 4 SKIP rule candidates DEFERRED until simulator framework is fully ready.
- May 30 LATE evening — Finding #312 SHIPPED: v2 #6 Daily 3-stop session breaker + DirectionPolicySpec doc-bug fix. Second real-money strategy ship of the day. NEW
src/earnings/session_stop_breaker.pypure-decision module +_count_todays_stops()helper in signal_generator + skip block in_build_trading_signal(after Queue Rule #1, before scheduled-time computation). 2 new env vars:SESSION_STOP_BREAKER_ENABLED(default True),SESSION_STOP_BREAKER_THRESHOLD(default 3). Skip propagates to opp_log via existingskip_stage='generate_signal'infrastructure. Historical evidence: 9 sessions in production with ≥3 stop-exits (verified May 30 fresh prod query: Feb 3/5/10/19/26 + Mar 3/5 + Apr 22/29). Stop classification usesexit_reason ILIKE '%stop%'per Finding #233 corrected stop-rate. Plusproduction_strategy_may30()factory direction-policy correction: was wrongly using long_threshold=0.55, short_threshold=0.45, conviction_fn="abs_distance_x2"; corrected to long_threshold=0.70, short_threshold=0.30, NEW long_max_threshold=0.90 field, conviction_fn="raw_prob" — matches production signal_generator.py:1184-1196 + Finding #202 over-confident skip. Phase α modeling error caught by operator via dashboard screenshot (INFERENCE DISCIPLINE catch). Dashboard Live Config "Direction" row updated to accurate text + Selection row includes "Daily 3-stop session breaker (v2 #6)". 79 tests PASS local (22 session_stop_breaker pure-decision + 8 integration + 38 phase3_simulator/test_rules.py + 11 prior signal-test regression). NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved). NO DB MIGRATION. Production behavior CHANGES (rule fires on ≥3-stop sessions; first historical fire would have been Feb 3 — looking at historical data, the rule would have prevented additional entries on those 9 days).- May 30 evening — Finding #311 SHIPPED: gate_uniform_$1000 production change. First real-money strategy ship since Mar 9.
src/earnings/config.pySIGNAL_MIN_VOLUME_TOTAL default lowered $2,000 → $1,000 (one-line change).production_strategy_may30()factory created;production_strategy_may29()backward-compat alias kept. Earlier same day — full correctness audit found 6 errors (E1 population mismatch 117 vs 127; E2 CRITICAL scaling factor wrong $245 vs actual avg $400; E3 outlier set inconsistent; E4 backfill rows hardcoded $10k position; E5 sweep dashboard missing notional labels; E6 max DD identical for both populations — verified coincidence). All 6 fixed. Per-tier empirical scaling (Large $537 / Mid $339 / Small $205) replacing global hardcoded $245. Memory K outliers (GPRO/DELL/SWBI/TXN/STUB) standardized across production+candidates so deltas apples-to-apples. Phase γ+δ corrected replay: gate_uniform_$1000 yields Δ E[wk] +$21/wk (~+$1,092/yr at $2,450 capital), max DD impact -$46 (within -$735 target = 30% capital), outlier alarm fixed (production ex-top-5 -$152 → +$582 post-admit), Pareto-optimal among 12 candidates. Other 2 ship-eligible variants (gate_quarterly_$2000peak_$1000other,gate_quarterly_$1000peak_$500other) RETRACTED-IN-FAVOR-OF gate_uniform_$1000 (operator picked simplest single-axis change; quarterly variants would need day-of-year branch in signal_generator.py — extra code surface for ~$0/wk vs $20/wk extra lift not worth it). 38/38 phase3_simulator tests PASS (was 37;test_volume_gate_is_2000renamed totest_volume_gate_is_1k+test_production_gate_is_1kreferences new factory). NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved). NO DB MIGRATION. Production behavior CHANGES (admits markets with $1k-$2k volume that previously gated). Monitor over Jul 2026 Q2 earnings season for actual fill slippage vs simulator estimate.- May 30 — Phase α + β + γ + δ + ζ shipped: full unified strategy framework (10 commits today). Phase α formalized Strategy as 9-component composable object (
production_strategy_may29factory +DirectionPolicySpec+TimingPolicySpec+ExitPolicySpec). Phase β KPI engine (scripts/phase3_simulator/strategy_kpis_engine.py) withcompute_kpis_for_population()working on live + counterfactual + combined populations + Pareto frontier helpers + 18 unit tests. Phase γ+δreplay_production_and_candidates.pyscript: replays production strategy through simulator + sweeps 11 gate variants + computes Δ KPIs per candidate + identifies Pareto frontier. Phase ζ wires real simulator deltas into Cockpit Top-5 (replaces ε.3 heuristic ranking). Plus deploy hook auto-refreshes candidate KPIs on every deploy.- May 30 morning — Phase ε.1 through ε.5: Operator Cockpit (4 layers) + Section Index + cleanup. Layer 0 target strip + Layer 1 5-KPI cards (E[wkPL], 5p worst, max DD, outlier dep, capital eff) with time-horizon selector + Layer 2 live production config table + Layer 3 DD-aware Top-5 ranked decisions. Section Index navigation card after cockpit. Universe View staleness banner. ε.1 fix: deploy_from_github.sh now auto-runs strategy_performance_analysis.py at end of deploy + schema sanity check (catches missing top-level keys) — fixes "Phase 3 Gate Retune Sweep shows not_run" bug. NO production behavior change.
- May 29 — Finding #310.c shipped: 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. NEW
scripts/phase3_simulator/gate_retune_sweep.py(~280 lines, all offline-only):ShipEligibilityfrozen dataclass (eligible / full_pop_verdict / full_pop_ex_top_2_per_100 / n_admitted_collapse / reasons[]) +VariantSweepResult(per-variant aggregate + per-period gauntlet + ship eligibility) +SweepResult(full sweep + universe-by-period counts + eligible_variants list).evaluate_conservative_ship_eligibility(full_pop_gauntlet, n_admitted_collapse) -> ShipEligibilityimplements Finding #310 Q3 (C) 3-condition check (verdict==PASS + n_collapse≥20 + ex_top_2_per_100≥0).run_gate_retune_sweep_on_universe(variants, markets) -> SweepResultdeterministic in-memory orchestrator (used by tests).run_gate_retune_sweep(engine, limit, variants) -> SweepResultloads universe + delegates. NEWscripts/phase3_simulator/gauntlet.pyextension:ADMISSION_BASELINE_RULE_ID = "gate_status_quo_$2k"constant +apply_admission_gauntlet(variant_id, admission_results, period_filter) -> GauntletResultnew entry point reusing existing_aggregate / _check_*helpers via duck-typed AdmissionResult. UPDATEDscripts/phase3_simulator/counterfactual_admission.py: added@property symbol+@property entry_timeto AdmissionResult so gauntlet helpers work via duck typing. UPDATEDscripts/phase3_simulator/output.py:write_sweep_results_json(sweep_result, path, code_git_sha)— schema =phase3_sim_gate_retune.v1(deterministic: sort_keys + indent=2 + trailing newline). UPDATEDscripts/phase3_simulator/runner.py:--run-gate-retune-sweepflag +--sweep-outputarg + new_run_gate_retune_sweep_mode()orchestration (~80 lines). Wall time ~1-3s on 262-row prod universe. UPDATEDscripts/strategy_performance_analysis.py:build_phase3_sim_gate_retune()helper that loadsscripts/phase3_simulator_gate_retune_sweep.json+ falls back tostatus='not_run'placeholder if missing — integrated intorun_analysis()report dict asphase3_sim_gate_retunealongsidephase3_simulator_status. UPDATEDearnings_dashboard/templates/dashboard.html: new 🧪 Phase 3 — Gate Retune Sweep section under Strategy Analysis tab (placed below 🧪 Phase 3 Simulator Status section per design doc §7.4): status header (boundary date / universe / eligible-count) + empty-state with runbook for "not_run" path + per-variant table (sorted: ship-eligible first, then by per-100 desc) with verdict color-coding + per-variant collapsible details with ship-eligibility reasons + per-period gauntlet table + eligible-variants summary. NEWrenderPhase3SimGateRetune(s)JS function (~150 lines) + render-loop integration. 25 NEW unit tests in 6 classes; 250/250 PASS local Python 3.11 (208 phase3_simulator + 42 prior signal-test files); no regressions. NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved), NO DB MIGRATION, NO PRODUCTION-CODE PATH CHANGE — simulator package remains offline-only. PR 2 COMPLETE: all 3 PR 2 sub-commits shipped (#310 + #310.b + #310.c). The gate-retune sweep is now operator-runnable + dashboard-visible. Operator AskQuestion per qualifying variant is the next step. PR 3 (sizing + priority + exposure-cap) begins next session per design doc §12 (~5-7 days):sizing_policies.py+capital_simulator.py+ per-direction SKIP rule revalidation + position×regime interaction +phase3_sim_sizingdashboard section. PR 4 (regime classifier v2) → PR 5 (depth-gate v2) follow per design doc §12 sequencing.- May 29 — Finding #310.b shipped: Phase 3 simulator PR 2.b — counterfactual_admission.py with opp_log loader + per-variant admission filter (second sub-commit of PR 2; first sub-commit was Finding #310 / PR 2.a). NEW
scripts/phase3_simulator/counterfactual_admission.py(~230 lines, all offline-only):CounterfactualMarketfrozen dataclass (22 fields — identity / trader-context / pre-computed outcome / intended sizing / regime) +load_counterfactual_markets(engine, limit, usable_sources)SQL loader filteringskip_reason='low_volume' AND hypothetical_prices_source='polygon.io_1m_close' AND direction_would_be IN ('LONG', 'SHORT')+USABLE_PRICES_SOURCES=('polygon.io_1m_close',)constant regression-locked +evaluate_admission(variant, market) -> boolper-variant filter usingvariant.admits(earnings_date, volume_total_usd)+AdmissionResultfrozen dataclass (variant_id / market / admitted / delta_vs_status_quo_dollars;.periodproperty delegating togate_variants.classify_period) +evaluate_variant_admissions(variant, markets) -> List[AdmissionResult]driver +admitted_only(results)convenience filter +summary_dict(variant, results)quick aggregate (n_universe / n_admitted / n_admitted_by_period / total_pnl / per_100 / n_winners / n_losers). §A pre-coding INFERENCE DISCIPLINE catch (6th this session-arc, 4th self-caught on my own PR 2 planning): original PR 2.b design assumed counterfactual replay would have to re-fetch Polygon bars + re-compute stop prices + re-execute the stop-loss simulator (substantial new code ~300+ lines + bar fetcher + stop-tier mapper + replay-engine extension). Pre-coding schema inspection revealedpolymarket_opportunity_logalready hashypothetical_entry_price + hypothetical_exit_price + hypothetical_stop_hit + hypothetical_pnl + hypothetical_pnl_pct + intended_position_value + intended_shares + intended_stop_pricefields pre-populated byscripts/compute_counterfactual_pnl.py(Finding #308 re-run). Massive scope reduction — PR 2.b becomes a ~230-line loader + 20-test suite instead of multi-module replay extension. Critical filter discovered too: 278 of 540skip_reason='low_volume'rows havehypothetical_prices_source='none'($0 placeholder; Polygon fetch failed; NOT a real outcome) — without filtering them out the gauntlet would bias downward by counting "ghost markets" as observed neutral outcomes. Pre-coding evidence (262 usable counterfactual universe): 262 usable rows (all in collapse period Mar+2026; opp_log instrumentation coverage); max volume $1,953 →gate_uniform_$2000admits 0 (confirms current production never admits these); per-variant admission preview $1k=96 / $500=146 / $250=181 / $100=196 / $0=262; aggregate counterfactual P&L 224/262 non-zero / avg +$12.14/trade / total +$3,180 if all 262 admitted; per-mcap (collapse) Large 36 avg +$4.49 / Mid 121 avg +$12.82 / Small 105 avg +$13.98 — strong directional signal but needs PR 2.c gauntlet vetting (ex-top-2 / ex-repeat-tickers / spillover / 5-outlier per memory §K). 20 NEW unit tests in 6 classes (TestCounterfactualMarket + TestEvaluateAdmission + TestEvaluateVariantAdmissions + TestAdmittedOnly + TestAdmissionResultPeriod + TestSummaryDict); 225/225 PASS local Python 3.11 (183 phase3_simulator including this commit + 42 prior signal-test files); no regressions. NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved), NO DB MIGRATION, NO DASHBOARD TEMPLATE CHANGE, NO PRODUCTION-CODE PATH CHANGE — simulator package remains offline-only. PR 2.c (FINAL) NEXT: gauntlet per-variant × per-segment aggregation + output.py schema extension + runner.py--variant-set gate_retune_sweepflag + 3-condition conservative-ship check per Finding #310 Q3 (C) + newphase3_sim_gate_retunedashboard section instrategy_performance_analysis.py+ render indashboard.html+ run on prod + operator AskQuestion per qualifying variant.- 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 INFERENCE DISCIPLINE preflight before any PR 2 code change. SQL preflight on
earnings_markets LEFT JOIN markets(Sep 17, 2025 → Jun 4, 2026; 977 markets; 3 reporting seasons + 2 off-season troughs observed) REFUTED the pure-seasonality hypothesis: Dec 2025 off-season was healthy at 98.1% gate-pass + $14,522 median volume, while Mar+2026 off-season collapsed to 27.8% + $0 median. This is a REAL STRUCTURAL collapse starting March 2026, NOT seasonality. Per-mcap evidence (May 2026): Large 9.5% / Mid 0% / Small 1.7% (across-the-board). Per-week evidence (May 2026): wk1 1.0% / wk2 0% / wk3 15.4% / wk4+ 0% (even heaviest reporting weeks failed). Hypotheses for the structural cause (deferred, not investigated): regulatory action on PM in late Feb / early Mar; loss of key market-maker; migration to Kalshi; PM UI/feature change. Operator AskQuestion 3-question pattern AFTER preflight findings + BEFORE any code change: Q1 (B) SEGMENTED = run sweep on 3 segments (healthy< 2026-03-01 /collapse≥ 2026-03-01 /full); Q2 (B) THRESHOLD + SEASONAL AXIS = 11 unique variants (6 thresholds × 2 modes − dedupe $0/$0): thresholds 2000/1000/500/250/100/0, modesuniform+seasonal_weekwith off-peak factor 0.5 (approximates healthy-regime wk1+wk2 vs wk3+ median-volume ratio); Q3 (C) SHIP CONSERVATIVE = ship a candidate only if ALL 3: gauntlet PASS on full-pop + ≥20 admitted markets in collapse segment + ex-top-2 per-100 stays positive on full-pop, otherwise DEFER until Jul 2026 (next earnings season) provides natural re-evaluation. PR 2 split into 3 sub-commits per #306 incremental-milestones pattern: PR 2.a SHIPPED (this — declarative scaffolding only:scripts/phase3_simulator/gate_variants.py~205 lines withGateVariantSpecfrozen dataclass + 11 variants + period segmenter + week-of-month classifier + constants all operator-AskQuestion-locked + 51 NEW unit tests; 205/205 PASS local Python 3.11 across phase3_simulator + 5 prior signal-test files); PR 2.b NEXT (counterfactual_admission.py— synthesize Trade from polymarket_opportunity_log skip_reason='low_volume' rows + fetch entry bar from equity_bars_1m cache + compute stop_price from mcap-tier + delegate to existing replay.replay_trade + unit tests with mocked bars; the technically-novel piece — first time simulator extends beyond replaying existing closed trades); PR 2.c FINAL (gauntlet aggregator extension for per-variant × per-segment scoring +output.pyschema extension +runner.py --variant-set gate_retune_sweepflag + 3-condition conservative-ship check + newphase3_sim_gate_retunedashboard section instrategy_performance_analysis.py+ render indashboard.html+ run on prod + operator AskQuestion per qualifying variant). NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved), NO DB MIGRATION, NO DASHBOARD TEMPLATE CHANGE, NO PRODUCTION-CODE PATH CHANGE — simulator package remains offline-only.- 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/FINAL sub-commit for #306 — PR 1 COMPLETE). NEW
scripts/phase3_simulator/gauntlet.py(~340 lines): all 5 checks 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) +apply_gauntlet(results, baseline_rule, variant_rule)public entry +compute_per_trade_deltahelper + VariantStats/CheckResult/GauntletResult frozen dataclasses withto_dictserializers +FIVE_OUTLIER_SYMBOLS = (GPRO, DELL, SWBI, TXN, STUB)regression-locked + constantsN_FLOOR=12 / N_IDEAL=20 / SPILLOVER_FAIL_THRESHOLD_PER_100=-5.0. Diverges from Q1 HYBRID delegation deliberately (gauntlet AGGREGATION must support arbitrary variant ids for PR 2-5; stop-loss MATH still delegated via reference_adapter). NEWscripts/phase3_simulator/output.py(~210 lines):DataSnapshot + RulesetEvaluation + ShipRecommendation + RunOutputdataclasses per design doc §7.2 +write_results_jsonwith sort_keys + indent=2 + trailing newline (POSIX) +mask_volatile_fieldszeros generated_at for reproducibility diffs +derive_ship_recommendationverdict→SHIP/DEFER/DROP/WARN mapping with human-readable rationale per memory §K +get_code_git_sha()defensive subprocess (timeout=5s, returns 'unknown' if git unavailable). UPDATEDscripts/phase3_simulator/runner.py(~80 lines change): added--run-gauntletflag for full pipeline +--baseline-rule(default A_base) +--variant-rules(nargs+, default RuleB) +--output+--no-replay-detail+--polygon-sleep. New_run_full_pipeline()orchestrates baseline replay → per-variant gauntlet → write JSON → print ship recommendations. Removed "#306.a stops here" footer staleness; updated module docstring to reflect post-#306.c capabilities. 33 NEW tests across 2 files:test_gauntlet.py(~340 lines, 17 tests in 7 classes incl. TestHRuleSelfValidation = design doc §10.3 PR 1 acceptance gate — 12-trade synthetic H-rule shape with headline +$30 / ex-top-2 -$10 sign flip; asserts verdict==FAIL-EX-TOP-2 with DO-NOT-SHIP-PR-2 diagnostic) +test_output.py(~210 lines, 16 tests in 5 classes incl. TestReproducibility = design doc §10.2 PR 1 acceptance gate —test_two_runs_produce_zero_byte_diffbyte-identical output after masking generated_at + sorted-keys + trailing-newline + mask_volatile_fields + no-replay-detail strip). 154/154 PASS local Python 3.11 (112 phase3_simulator including this commit + 42 prior signal-test files); no regressions. PR 1 IS NOW COMPLETE: all 5 PR 1 commits shipped (#306.a foundation skeleton + #306.b replay/cost-model/cross-check + #306.c gauntlet/output/H-rule/reproducibility + #307 #273 conditional-label fix + #308 Bug #6 hyp_pnl re-run). All 4 PR 1 acceptance gates PASS: §10.1 cross-check vs analyze_finding_a_full.py = 100% match within $1 on all 7 rules / max drift $0.00 (verified on prod May 28 per #306.b §G probe); §10.2 reproducibility = 0-byte diff after masking generated_at; §10.3 H-rule self-validation = FAIL-EX-TOP-2; Phase 0 §0.6 Gate 1/4 = corrected per #308 structural-ceiling discovery. NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved), NO DB MIGRATION, NO DASHBOARD TEMPLATE CHANGE, NO PRODUCTION-CODE PATH CHANGE — simulator package remains offline-only. PR 2 (volume-gate retune sweep) can begin next session per design doc §12 — highest-leverage per May-28 PM volume-collapse evidence. PR 2 will addgate_variants.py($2k→$0 sweep) +replay.pyextension for counterfactual admission onskip_reason='low_volume'rows (activates the latentopp_log_pm_fields_by_audit_idplumbing for pm_v2_orderbook cost-model on newly-admitted markets) + firstphase3_sim_gate_retunedashboard section instrategy_performance_analysis.py+ operator AskQuestion ship decisions per variant.- May 28 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(~140 lines,CostEstimatefrozen dataclass +estimate_cost(trade, opp_log_pm_fields=None)per Q2 FLAT 1% default;pm_v2_orderbookpath fires when ALL 3 fields non-None matching Finding #307 conditional-label semantic, elseproxy_flat_1pct; half-spread per leg withabs(shares)defense;cost_estimate_to_dictfor JSON; cost is NOT folded into baselinepnl_dper design doc §10.1 — held separately for #306.c gauntlet aggregation). NEWscripts/phase3_simulator/reference_adapter.py(~120 lines, re-exportsTradeSim/fetch_bars/run_trade/simulate_*per Q1 HYBRID;REFERENCE_RULE_IDS = (A_base, FA_soft, FA_hard, RuleB, RuleD, RuleG, RuleF)lockstep withTradeSim.run_all;_assert_reference_module_stable()fires at import + checksinspect.signatureagainst_EXPECTED_PARAM_COUNTS+ raisesRuntimeErrorwithCHANGE-CONTRACT+ memory §M diagnostic on drift — caught my own off-by-one forsimulate_combined_window_atrduring test collection). NEWscripts/phase3_simulator/replay.py(~210 lines,ReplayContext+ReplayResultdataclasses +replay_trade(trade, ruleset, ctx)delegating to reference'srun_trade+replay_populationprogress-logging loop with WARNING-skip on None results; per-rule pnl_d kept separate from aux to avoid duplication;rulesetarg accepted-but-unconsumed for baseline preserves signature for #306.c + PR 2-5 variant branching). NEWscripts/phase3_simulator/cross_check_baseline.py(~280 lines, OPERATOR-RUNNABLE SCRIPT implementing design doc §10.1 acceptance gate: load N trades → run replay_population → INNER JOIN againstscripts/finding_a_full.csvreference → per-rule drift breakdown → PASS ifmatch_pct >= 95%within $1 on every rule, else FAIL with outlier dump first-10-per-rule; writes JSON evidence toscripts/phase3_simulator_baseline_cross_check.json; CLI args for population/limit/tolerance/min_match_pct/output; standalone — NOT a unit test). UPDATEDscripts/phase3_simulator/population.py: addedTrade.scheduled_exit_dt(cal)method matchinganalyze_finding_a_full.Trade(mirrors BMO/AMC calendar lookup via_scheduled_times_from_timing+ fallback toexit_time.astimezone(ET)) +sys.pathbump + lockstep imports of_scheduled_times_from_timing+MarketCalendar. §A pre-coding INFERENCE DISCIPLINE catch (fifth this session-arc, third self-caught on my own #306.a code):Trade.scheduled_exit_dtwas MISSING from population.py — the reference'srun_tradecalls it atanalyze_finding_a_full.py:621, so the delegation in #306.b would AttributeError immediately when called with our Trade. The #306.aTestReferenceParityonly validated dataclass FIELDS (viadataclasses.fields()), not METHODS. Fixed both: addedscheduled_exit_dtto population.py + newTestReferenceParity.test_method_names_match_expectedtest asserting{release_dt, scheduled_exit_dt, position_value, stop_distance_pct}present on BOTH dataclasses + newTestReferenceParity.test_scheduled_exit_dt_consistency_amcoutput-parity guard. Caught BEFORE writing any #306.b code. Same shape as the 4 prior INFERENCE DISCIPLINE catches earlier in the session arc (#273 16× bug surface; Playbook §6.7 RuleB soft-gate broken; Bug #2/#3 already-fixed; #308 deadband-conviction structural ceiling). 37 NEW tests across 3 new test files + 2 added to test_population.py:test_cost_model.py(13 tests in 4 classes incl. constants regression-lock + 3-way partial-field parametrize),test_reference_adapter.py(13 tests in 3 classes incl. monkeypatch-based signature-drift simulation + missing-function simulation),test_replay.py(9 tests in 3 classes incl. stub-mocked adapter so NO real Polygon/DB calls + JSON round-trip viareplay_result_to_dict + json.dumps(default=str)). 121/121 PASS local Python 3.11 (79 phase3_simulator including this commit's additions + 42 prior signal-test files); no regressions. NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved), NO DB MIGRATION, NO DASHBOARD TEMPLATE CHANGE, NO PRODUCTION-CODE PATH CHANGE — simulator package remains offline-only and nothing insrc/earnings/orsrc/data_tracker/references it. §10.1 cross-check execution is part of #306.b §G probe (post-deploy on prod:python3 scripts/phase3_simulator/cross_check_baseline.py --limit 200 --log-level INFO→ expect verdict PASS withmatch_pct >= 95.0%on every rule +max_drift <= $1.00; wall time ~1-7 min depending on Finding #240 cache hit rate; expected to PASS trivially since Q1 HYBRID delegation calls the same simulate_* functions as the reference).- May 28 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:__init__.py(~40 lines, package marker + sub-commit roadmap),population.py(~200 lines,Tradedataclass +load_trades(engine, population, limit)duplicated fromanalyze_finding_a_full.pyper Q1 HYBRID with module-level CHANGE-CONTRACT docstring),rules.py(~230 lines, declarativeRegimeClassifierSpec/SizingPolicySpec/GateThresholdSpec/DepthGateSpec/RuleSetdataclasses allfrozen=Trueper design doc §5.4 +baseline_ruleset(id_suffix='may28')factory regression-locking production config +spec_to_dict/spec_from_dictround-trip via_kinddiscriminator for §10.2 reproducibility),runner.py(~150 lines, CLI stub with--variant-id/--population/--limit/--skip-db/--log-level; explicit exit 2 ifvariant_id≠baseline_may28; NO replay/NO gauntlet — those land in #306.b/c). NEWtests/unit/phase3_simulator/:__init__.py+test_population.py(~250 lines, 18 tests: TestPositionValue/TestStopDistancePct/TestReleaseDt + TestReferenceParity = 4 CHANGE-CONTRACT tests that fail loudly with memory.md §M-pointing diagnostic if either side drifts vsanalyze_finding_a_full.Trade, including a cross-source-grep thatanalyze_finding_a_full.load_tradessource contains the same 4 population filter keys) +test_rules.py(~210 lines, 24 tests: per-spec construction+immutability + baseline_ruleset regression-lock 7 tests + serialization round-trip on every spec class + full RuleSet). 42/42 NEW tests PASS local Python 3.11; 84/84 PASS combined regression with prior 42 signal-test tests; no regressions. 3 operator AskQuestion decisions surfaced BEFORE any code change per NO SHORTCUTS rule + answered: Q1 cross-check approach = (C) HYBRID (duplicate Trade+load_trades, delegate stop-loss simulate_* via futurereference_adapter.pyin #306.b); Q2 cost-model proxy = (A) FLAT 1% spread documented ascost_model_method='proxy_flat_1pct'(lands in #306.bcost_model.py); Q3 commit granularity = (B) THREE INCREMENTAL MILESTONES (this sub-commit + #306.b cost_model+replay+cross-check + #306.c gauntlet+H-rule self-validation+reproducibility+output+dashboard). After #306.c, PR 1 COMPLETE; PR 2 (volume-gate retune sweep, highest-leverage per May-28 PM volume-collapse evidence) begins. NO INTEGRITY-MONITOR RULE COUNT CHANGE (47/47 preserved), NO DB MIGRATION, NO DASHBOARD TEMPLATE CHANGE, NO PRODUCTION-CODE PATH CHANGE — the simulator package is offline-only and nothing insrc/earnings/orsrc/data_tracker/references it. Memory §E exhaustive grep confirmed: 6 new source files + 3 doc references = no stale phase3_simulator references; only intentionalfrom scripts.analyze_finding_a_fullimport in theTestReferenceParitycross-check.- May 28 LATE evening — Finding #308 shipped: Phase 3 simulator PR 1 second commit (ops-only) =
compute_counterfactual_pnl.pyre-run on prod (78 truly-pending rows processed in ~90s; generate_signal coverage 67.5% → 69.1%; depth + signal_emitted unchanged; integrity 47/47 OK preserved) + structural-ceiling discovery documented (the 480+39 NULL-hyp_pnl rows that Phase 0 doc treated as "coverage gap" are actually alldirection_would_be='NONE'conviction-deadband signals where counterfactual P&L is structurally impossible; ~69% / ~28% on generate_signal/depth is the achievable ceiling, not 75%). INFERENCE DISCIPLINE catch #4 this session (after #273 16× bug surface + Playbook §6.7 RuleB soft-gate spec broken + Bug #2/#3 already-fixed). Phase 3 simulator'ssignal_emitted100% subset is sufficient for all 4-bundle analyses; no Phase 3 work blocked. Doc-only commit (no code change).- May 28 LATE evening — Finding #307 shipped: Phase 3 simulator PR 1 first commit (lockstep fix for original Finding #273).
_emit_pm_opportunity_logatsignal_generator.py:1041now setssnapshot_schema='pm_v2'ONLY whenbest_bid + best_ask + spreadare ALL non-None (Option A MINIMAL conditional-label per design doc §9). Conservative gate matches the simulator's cost-model expectation thatpm_v2label means "complete orderbook snapshot present"; partial-data rows fall back to implicit v1 (nosnapshot_schemakey emitted) but otherpm_*fields stay populated (preserves JSONB shape for forward-compat readers). Pre-fix 95.6% mislabel rate (1,055 of 1,104pm_v2-labeled rows had NULL bid/ask/spread becauseConvictionCalculator._get_latest_from_price_snapshotsfallback fires for one-sided orderbooks common in high-conviction markets like NVDA at 97%). Cross-interface (memory §M): integrity rulenull_rate:opp_log:pm_v2_post_deployatintegrity_monitor.py:541updated in lockstep —colchanged from(snapshot->>'snapshot_schema')→(snapshot->>'method')so the rule's intent ("verify pm enrichment block ran for rows with ConvictionResult") is preserved unaffected by the new conditional label semantic;methodis set unconditionally inside the enrichment block. 17 + 18 = 35 lines code (with 27 lines of context comments cross-referencing Findings #228/#273/#307). 6 new unit tests intests/unit/test_pm_v2_conditional_label.py; 42/42 PASS pre-commit on prod Python 3.12 venv. PR 1 §A pre-coding INFERENCE DISCIPLINE catch (third this session): Bug #2 + #3 are ALREADY FIXED in code since Apr 26 — Findings #234/#237 — verified via per-era SQL on closed trades (post-Apr-26: sp500 9/9, conviction_at_exit 9/10; pre-Apr-26: 111 + 110 NULL rows are HISTORICAL BASELINE, not current bugs). Operator AskQuestion approved revised smaller PR 1 scope = #306 foundation + #307 (this commit) + #308 Bug #6 hyp_pnl re-run only (~3-5 days vs original 5-8 estimate; ~30% scope reduction). Design docdocs/PHASE_3_SIMULATOR_DESIGN.md§3 (Gate 1) + §12 (PR 1 scope) + §15 (Decisions 2/3) updated to reflect corrected understanding. The 1,055 pre-fix mislabeled rows remain in DB as historical baseline (NOT retroactively unlabeled — would lose audit trail per §C surgical-changes); Phase 3 simulator's cost model handles both pre-fix (label-but-NULL) and post-fix (label-iff-populated) rows uniformly via proxy fallback. Closes Finding #273 Phase 1 (the deferred-to-Phase-3-lockstep ship from Apr 27).- May 28 evening — Phase 3 unified simulator design doc shipped at
docs/PHASE_3_SIMULATOR_DESIGN.md(867 lines, 16 sections). Doc-only intervention; companion todocs/STRATEGY_ANALYSIS_ARCHITECTURE.mdPhase 0 baseline (Apr 26). Operator chose all-4-bundle scope viaAskQuestionLATE afternoon → design doc concretizes 5-PR ship plan (~3-5 weeks total): PR 1 foundation (5-8d) —scripts/phase3_simulator/package skeleton + baseline replay matchinganalyze_finding_a_full.pycross-check + cost model V1 with proxy fallback + Finding #273 fix (Option A conditional-label per §9) + Bug #2 sp500 + #3 conviction_at_exit + #6 hyp_pnl backfills; PR 2 volume-gate retune sweep (3-5d) — HIGHEST-LEVERAGE per May-28 PM volume-collapse; PR 3 sizing + priority + exposure-cap variants (5-7d); PR 4 regime classifier v2 (3-5d) — compound/EMA20/MA50/drawdown baseline; PR 5 depth-gate v2 decomposition (3-5d). Phase 3 gates from Phase 0 §0.6 VERIFIED MET: equity_bars_1m 185 days × 418 symbols × 1.9M bars (gate 2 6× exceeded); actual_beat 94.9% (gate 3 exceeded); opportunity_log hyp_pnl 100% on signal_emitted subset (gate 4 met for critical subset); Bug #5 stop_triggered FIXED (50.8% via both stop_triggered=TRUE and exit_reason~stop). Remaining gaps (Bug #2 sp500 NULL 111/120, Bug #3 conviction_at_exit NULL 111/120, depth-stage hyp_pnl 27.8%) addressed in PR 1 scope. Key architectural decisions captured: declarative rule format (dataclasses not Python code), state derived from data (no migration needed), cost model with proxy fallback documented in output, bias-gauntlet integration with verdict-dispatch render, true-simulator self-validation (run H-rule → must FAIL), reproducibility via diff-clean repeated runs, operator-approval-required for every ship. 8 open operator-decision points in §15 must resolve before PR 1 starts (Finding #273 fix scope default Option A, Bug #2 backfill vs forward-only default backfill, PR ordering default PR2-first, etc.).- 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. Hardware cancel-and-rearm pattern (matches simulator's
simulate_window_onlymechanism, NOT the soft-gate originally specified in Playbook §6.7 — that would deliver $0 EV like FA_soft did per Finding #228 retraction). Code: NEWsrc/earnings/ruleb_window_gate.py(pure decision function, 8-valueRuleBActionenum,RuleBDecisionContextdataclass, fail-softderive_earnings_release_dthelper) + new orchestrator methods (_process_ruleb_window_gateper-cycle dispatcher +_ruleb_fire_cancel+_ruleb_fire_rearm+_ruleb_log_onceskip-log dedupe) + main-loop hook + 4 new Config env vars (RULEB_ENABLEDdefault False,RULEB_MODEdefault 'dry_run',RULEB_WINDOW_MINUTESdefault 90 matching simulator,RULEB_HARD_FALLBACK_MIN_BEFORE_EXITdefault 30 safety boundary). Tests: 25 new intests/unit/test_ruleb_window_gate.py, 36/36 PASS pre-commit (25 RuleB + 7 Rule #1 + 4 prior signal-test regression). Phase 1 RULEB_ENABLED=false default = NO production behavior change until operator enables. Phase 2 active-mode flip pending operator dry-run-log review (separate session). Operator AskQuestion: chose Path (A) hardware cancel-and-rearm with Phase 1 dry-run-first rollout, apply-only-to-new-positions policy. Agent flagged in pre-coding (not asked):ruleb_stateDB column unnecessary per memory.md "IBKR is source of truth for live state" + §C surgical-changes — state DERIVED per-cycle from(now, hardware_stop_active, release_dt, scheduled_exit). Bias-gauntlet PASS robustly on fresh May-28 sim n=121: headline +$236/100, ex-top-2 +$127/100 (STRENGTHENED from Apr-25 baseline +$123/100 on smaller n=51), ex-repeat-tickers +$239/100, full-population spillover 74 better/33 worse/14 unchanged. Phase 1 known limitation: restored positions (signal=None per position_manager:1155) can't derive earnings_release_dt → graceful SKIP_NO_EARNINGS_DT log; Phase 2 may add restart-safe lookup if dry-run shows this matters.scripts/strategy_performance_analysis.py:build_action_queuequeue rank-2 row flipped IN-QUEUE → IMPLEMENTED-DRY-RUN.STRATEGY_PLAYBOOK.md §2IN-QUEUE renumbered (3-stop breaker 3→1, trade-#150+ revalidation 4→2).STRATEGY_PLAYBOOK.md §6.7implementation notes REWRITTEN: original soft-gate spec preserved as "DO NOT IMPLEMENT" historical context with the corrected caveat explaining WHY the soft-gate would fail.- May 28 afternoon — Finding #304 shipped: Queue Rule #1 (Skip LONGs in downtrend regime) — FIRST Phase-3 strategy rule. Landed ~50 h after the May-26 un-gate; second Phase-3 commit today (after Finding #279 amendment + dashboard-refresh commit). Code: new
_safe_get_regime()helper + skip block in_build_trading_signalatsrc/earnings/signal_generator.py(~40 lines). Tests: 7 new intests/unit/test_signal_regime_gate.py(LONG×downtrend skip + 6 fail-soft / direction-asymmetry / regime-variation negative controls). 11/11 PASS (4 existing + 7 new) on prod Python 3.12 venv pre-commit, no regressions. Bias-gauntlet PASS robustly: 22 LONG-downtrend trades / 5W (23 %) / −$136.30 net, ex-top-2 −$175 (MORE negative than headline → robust), 22/22 distinct tickers, full-population spillover positive (uptrend +$133/41tr, sideways +$89/39tr, downtrend-SHORT +$172/3tr all preserved). Operator AskQuestion sequence: (1) "Phase 3 entry path" → "D then A, then re-evaluate" (originally #273 first then Rule #1); (2) "Fix #273 scope" → "(E) defer per the original FINDINGS.md lockstep approach" after I surfaced that #273's bug surface is 16× bigger than originally documented (1,055 NULL pm_v2 rows across 3 stages vs FINDINGS.md's "8 rows on 1 stage") AND fix scope is non-trivial without Phase 3 simulator schema knowledge. Net updated sequence: Rule #1 ships first (this entry), Finding #273 stays deferred to lockstep with Phase 3 simulator.scripts/strategy_performance_analysis.py:build_action_queuequeue rank-1 row flipped IN-QUEUE → IMPLEMENTED. Operational note: at current 1.9 % PM volume-gate pass rate, Rule #1's natural firing rate is ~0.4 % of all earnings markets — the rule's immediate value is in bias-gauntlet-locking the policy on the books + correct behavior in the eventual next high-volume regime, NOT fast live evidence; "≥30 live trades" gauntlet trigger is a DEFERRED-bundle re-test condition, not a Rule #1 ship-precondition.- 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 ~50 hours after the May 26 Phase 3 un-gate. Doc-only intervention. Operator-approved 6-step plan executed: stash inspection on prod (May-25 strategy report 271 KB found in
stash@{0}, fresher than the on-disk Apr-21 committed version since the newpolymarket-strategy-analysis.timerhasn't fired yet — first natural fire Sun 2026-05-31 13:00 UTC) → strategy report regen (scripts/strategy_performance_analysis.py, ~15 s — 116 clean trades / +$212.21 net / WR 37.07 % / stop rate 51.72 % / 0 new clean closures since 2026-05-07 = 21-day execution drought, HPQ entered yesterday May 27 is the only post-drought entry) → Finding-A simulator regen (scripts/analyze_finding_a_full.py, 6m 50s — RuleB ex-top-2 strengthened to +$127/100 / gauntlet PASS robustly; RuleG headline +$242 but ex-top-2 only +$105 / less robust; FA_hard retraction still holds via rearm_gap_open arm n=24 at -$1,545/100) → direct SQL onearnings_markets LEFT JOIN markets USING (condition_id)(Jan→May avg ~109× drop, median ~1,847× drop, $2k-gate pass rate 100.0 % → 1.9 %, NO Mid-cap market crossed gate in all May, 3 May gate-passers each had specific reasons they produced no entries) → Finding #279 amendment shipped via operator AskQuestion option (B) "fold into #279" (vs standalone #304 / both / defer). Phase 3 first-ship sequence per operator AskQuestion option "D then A, then re-evaluate": Finding #273 (pm_v2 NULL bid/spread data-capture fix) ships NEXT, then IN-QUEUE Rule #1 (Skip LONGs in downtrend), then re-evaluate the remaining options. Files changed:docs/FINDINGS.md(#279 status line + new "Phase B framing premise — PM earnings-market volume collapse" subsection);docs/STRATEGY_PLAYBOOK.md(§1 verdict-banner May-28 refresh block + §0/§1 "Verified still current" updated to May 28 + intervening-work rollup expanded + §7 deferred-bundle row forsizing-priority-v2rewritten);docs/AGENT_HANDOFF.md§11 (header + new afternoon block); this file (docs/CONTEXT.md). NO code changes, NO integrity-monitor changes, NObuild_action_queue()changes — the existingsizing-priority-v2row staysDEFERRED-POST-PHASE-3because the amendment is informational scope-expansion, not a status change.- May 28 LATE-LATE morning — Finding #303 (deploy_from_github.sh pre-pull hygiene) shipped. Operator-directed fix for the recurring prod-git operational-hygiene problem surfaced in Finding #302's deploy (3 pull-abort rounds forced fallback to memory.md §H scp single-file hotfix). New
git_hygiene_cleanup()function indeploy/deploy_from_github.shbeforegit pull --ff-only: case (a) untracked file identical to origin/master → rm; case (b) modified file identical to origin/master →git checkout HEAD --; case (c) modified file differs from both HEAD and origin/master →git stash push -m "auto-stash before deploy <ISO> (Finding #303)". Operator-approved divergent-handling = stash-preserve. Script-only scope.- May 28 LATE morning — Finding #302 (polymarket-backup.timer collision with apt-daily-upgrade.timer) shipped. Operator-approved fix-path (A) "move backup timer to 05:00 UTC" via
AskQuestionafter root-cause chain traced per the new INFERENCE DISCIPLINE rule. apt-daily-upgrade ran 04:11:35 UTC and restarted systemd-resolved/networkd at 04:11:54 UTC; polymarket-backup.timer fired 04:11:53 UTC into the dead DNS. New scheduleOnCalendar=*-*-* 05:00:00(kept 1800s jitter) → fully clear of apt-daily-upgrade's 04:30 UTC latest end. Today's missing backup filled via manualsystemctl start polymarket-backup.service. Same root cause as today's IBKR Gateway disconnect (collateral network-restart damage); Gateway auto-recovered 6m 17s, no fix needed.- May 28 morning —
🚨 INFERENCE DISCIPLINE — VERIFY MODEL BEFORE DECLARINGrule codified in.cursor/memory.mdas companion to NO SHORTCUTS. Triggered by agent false-alarm on AMC scheduling (declared "+24h scheduling bug" on a correctly-running AMC position; operator caught instantly because the code's_calculate_scheduled_exit_time_from_dbatsrc/earnings/trading_orchestrator.py:1358-1360explicitly callsget_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). Apply 2× harder for trading-related observations. 6-item self-audit checklist mandatory before declaring any anomaly. Codified.cursor/memory.md(new section between NO SHORTCUTS and TRADE EXECUTION PROHIBITION) +docs/AGENT_HANDOFF.md§11.- May 27 morning — factual correction shipped to the May-17 and May-24 reconnect entries: 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 — IBC server-side logs are blind to the human-side push approval step, and fast port-up is consistent with quick mobile approval, NOT session resume. Both reconnect entries corrected.
- May 24 morning — IBKR Sunday 2FA reconnect handled manually (09:18 UTC restart → operator approved push → port 4001 open ~20s → trader reconnected 09:19:56). Account U2…13 / net_liq $27,537.50.
- May 17 morning — Finding #297 (
api_health_checkerWARN-but-exit-0 cry-wolf fix) shipped. Closes the pre-existingsystemd_last_run:polymarket-api-health.serviceFAIL that surfaced during #296's §G probe on May 11 (the rule had been continuously FAIL since the May-16 08:00 UTC daily fire flaggedprice_accuracy: 1 prices differ - may be normal market movement→ script exit 2 → rule correctly STATUS_FAIL → HIGH Telegram alert). Operator-approved fix-path (b) WARN-but-exit-0 viaAskQuestion(vs widen-threshold / leave-as-is / build-age-aware-structural-fix). 1-digit behavior change (exit(2)→exit(0)for warning branch inapi_health_checker.py:main) + 16-line context comment explaining the cry-wolf antipattern + memory.md Principle 5 rationale + a newlogger.warning("Overall WARNING result — exiting 0 by design (Finding #297)")for forward observability.error → exit 1andhealthy → exit 0paths UNCHANGED — real failures (HTTP/network, API contract violation, >50% drift) still alert viasystemd_last_run. §E exhaustive: only 1 otherexit(2)in repo (backup_database.py:287for real S3 upload failure — correct, NOT same antipattern). Today's also: IBKR Sunday 2FA reconnect manual operator op (06:48 UTC restart → 06:49:01 trader reconnect; faster than May-10's 90s due to IBKR session-resume). Integrity-monitor back to 45/45 OK baseline.- May 11 evening — Finding #296 (
_check_systemd_unit_last_resultbranch reorder) shipped. Closes 1/3 of the May-10 "observed-but-not-actioned" maintenance items per operator-approvedAskQuestionoption (A) "ship #2 only". Live-verified bug shape on prod via a probe oneshot unit (Result=success+ExecMainExitTimestamp=empty for never-fired units; same shape forLoadState=not-foundunits). Branch reorder puts the never-ran check (exit_ts in ("", "n/a", "0")) BEFORE the success check, so never-fired units (and bonus: not-found units) now correctly return STATUS_UNKNOWN instead of falsely STATUS_OK with text "last run succeeded (dead)". 5-case smoke test (fired / never-ran / failed / not-found / n/a-timestamp) all pass; §G post-deploy probe confirms 8/8 existingsystemd_last_run:*rules continue STATUS_OK on real data — no regression. Bonus §E observation deferred as possible #297 candidate:LoadState=not-foundcould arguably return STATUS_FAIL (more diagnostic) but currently nosystemd_last_run:*rule references a misnamed unit (verified live). Other two May-10 maintenance items (Sunday-2FA partial-automation timer; Sunday-awareibkr_snapshotthreshold) explicitly deferred — both change operator workflow + alert semantics, need explicit operator approval beyond "close the backlog." 45/45 integrity rules OK (40 main-project + 5 etfstack subtree).- May 10 LATE morning — Finding #289.b (live_trades_collector pg8000 InterfaceError cascade) shipped. Final item in plan-(C) sequence. Reality-check correction to original Finding entry: errors were in the LOG FILE not journalctl (StandardOutput=append: writes to file, not journal). Across 10 daily-rotated logs: ~118k InterfaceErrors total in 10 days. Root cause: inner try/except INSIDE
with engine.begin() as conn:swallowed inner SQL exception at DEBUG level (invisible), connection went into failed-tx state, engine.begin's exit tried to COMMIT, pg8000 raised InterfaceError on the COMMIT, outerCycle errorhandler caught it. Cascade closure was missing:_seen_trade_ids.add(trade_id)was called only on success path, so the failed trade was re-fetched + re-attempted every 5s forever. Fix: structuraltrywrapswith engine.begin()(not vice versa) +_seen_trade_ids.addon BOTH success and error paths + DEBUG → WARNING level for visibility. §G probe post-deploy: 0 InterfaceError in 5-min observation window (was ~60 in same window pre-fix);live_pollwrites continue at baseline; no throughput regression. §E future-deferred sub-finding:backfill_trades.py:save_trades_batchhas same structural pattern (timer-driven so cascade bounded) — should be refactored when next touched.- May 10 LATE morning — Finding #289.a (api_health_checker silent-failure → systemd-timer migration) shipped. Third item in plan-(C). Root cause confirmed via reproduction:
/bin/sh: source: not found(Ubuntu's /bin/sh is dash,sourceis bashism, chained&&short-circuits, redirect never executes). Bug existed for 14 days (Apr 27 → May 10) — log file 0 bytes since the day after #260 logrotate rotated it. Mail spool empty (no MAILTO) so dash error went nowhere. §E same-pattern check found IDENTICAL bug insync_earnings_markets.pycron — both fixed in same commit. Operator-approved option (B) full systemd-timer migration (after AskQuestion surfaced trade-offs vs (A) crontab patch): newpolymarket-api-health.{service,timer}withbash -c 'source .env && export DATABASE_URL POLYGON_KEY FINNHUB_API_KEY && python -m api_health_checker'ExecStart, daily 08:00 UTC + 300s RandomizedDelaySec. 3 new integrity rules:systemd_timer:polymarket-api-health.timer+systemd_last_run:polymarket-api-health.service+ newartifact:api_health_latest_json(26h threshold).SHELL=/bin/bashadded to top of root crontab to fix sync_earnings_markets latent bug + future-proof any cron entries that use bashisms. Old api_health cron line removed manually post-deploy. Total integrity rules now 40 (was 37: +3 from this finding). Pre-existing Polymarket Website API 404 surfaced (build-ID changes break the_next/data/<buildID>/...URL) — was hidden by the cron silence; now alarms daily viasystemd_last_runSTATUS_FAIL; needs separate fix (build-ID auto-discovery OR change Website check from fail to warn).- May 10 LATE morning — Finding #291 (long-running-no-op service defense, RE-SCOPED) shipped. Second item in plan-(C). Originally documented spec recommended 3
progress:*rules attributing writes to specific services — but live-system analysis showed all 3 named daemons are SOLE writers in the current architecture (CLOB trades disabled perENABLE_CLOB_TRADES=false); existing freshness rules already alarm 10-60 min after they'd silently die. Per memory.md NO SHORTCUTS rule, the design tension was surfaced explicitly to the operator (viaAskQuestion) rather than silently interpreted. Operator chose option (B): re-scope to actual blind-spot closure via state-file mtime defense — 1 newartifact:trades_backfill_staterule (9-day threshold, HIGH severity) + 40-line context comment in_build_rules()+ CHECKLIST indeploy/deploy_from_github.sh:servicesarray + new §2.5 inDATA_INTEGRITY.md. Total integrity rules now 35 (was 34);artifactcount 3 (was 2). §E inventory of other state files:login_attempt_state.jsonnot no-op-relevant;gap_monitor.pydead code per #289 (no service, out of scope);*.archived_*preserved archives.- May 10 LATE morning — Finding #290 (watchdog dead-code deletion) shipped. First item in operator-decided plan-(C) sequence (#290 → #291 → #289.a → #289.b). Deleted 5 files (
src/data_tracker/watchdog.py22 KB,src/data_tracker/watchdog_v3.py31 KB,deploy/polymarket-watchdog.service,deploy/polymarket-watchdog-v3.service,deploy/systemd/polymarket-watchdog.service— 5th file caught by §E re-grep after first delete sweep, was a duplicate copy in subdir); fixed 7 stale references (deploy/setup-digitalocean.sh,deploy/install_services.sh,tests/system_health_check.py,tests/comprehensive_system_test.py,docs/DATA_INTEGRITY.md§6.1+§7.1+Appendix); added forward-pointer annotations in 4 historical-context refs (FINDINGS.md #270 + #289.v2 + AGENT_HANDOFF.md #290 row + line 373 footnote). Decommission loop indeploy_from_github.sh:127-128still idempotent post-deletion. §E exhaustive grep covered 22 references — 7 stale (fixed), 14 historical (kept with citations to #258 / #260c / #290), 1 out-of-scope (polymarket_sniper/watchdog.py— different sub-project process-monitor).- May 10 LATE morning — Cold bootstrap of
polymarket-trades-backfill.service(operator-decision-D) COMPLETE; Finding #292 (Polymarket /trades offset cap) caught by §G probe and shipped same session; Finding #288.v3 (5-label vs 4-labeltrade_dedupe.sourcedoc correction) shipped. Cold bootstrap exit clean (Result=success, 968 API calls / 929 markets / 152,101 trades saved with newpolymarket_data_api_backfillsource label, ~29 min wall time). Surfaced two §G-probe discoveries: (1) Polymarket data-api/tradeshard-capsoffsetat 3000 with body{"error":"max historical activity offset of 3000 exceeded"}— direct probes confirmed there is NO alternative pagination param (?before=,?after=,?since=,?until=,?startTime=,?endTime=, etc. all silently ignored;?asset=<token>returns unrelated trades;/openapi.jsonis 404). Bounded blast radius: only 1 of 929 earnings markets vulnerable (TSLA Q3 2025; the other 144 markets in DB with >3500 trades all have substantial pre-#288 Domebackfillcoverage so historical depth is preserved). Fix shipped via new_OffsetCapReachedsentinel inbackfill_trades.py— graceful end-of-pagination handling marks market completed-truncated (NOT failed) so weekly timer doesn't re-attempt forever. (2) 5trade_dedupe.sourcelabels coexist, not 4 —'backfill'(2.17M rows) was previously undocumented; came from a one-off bulk import on 2026-01-31 10:13:24 UTC (script no longer in repo); raw_data shape varies (oldest Dome / newest Polymarket-native);'dome_backfill'literal in pre-#288 source code has 0 rows in DB (all ON CONFLICT-skipped against the existing'backfill'rows). All 6 doc surfaces updated (memory.md, CONTEXT.md, AGENT_HANDOFF.md, FINDINGS.md #288 + #289, DATA_ARCHITECTURE.md, verify_schema.py). Cold-bootstrap-then-enable-timer runbook from #269 now safe to complete:systemctl enable --now polymarket-trades-backfill.timerafter deploy.What changed earlier (Apr 21 → May 9, 2026)
- Apr 21-22 — Finding #227 broken learning loop fixed (4-tier EPS resolver,
MarketRegimeProvider, daily resolver timer;actual_beat25% → 73%).- Apr 22-23 — Strategy ROI Audit v3 (
docs/STRATEGY_AUDIT_v3.md) ran 4-check bias gauntlet on every hypothesis; verified 6 v3 findings (A retracted Apr 25 via simulator).- Apr 25 — Finding #228 Finding-A retraction via comprehensive simulator. Findings #229 (volume_total backfill +319 markets), #230 (conviction backfill +384 markets), #231 (weekly self-heal timer), #232 (queue re-rank — RuleB promoted, v3 F dropped), #233 (stop_triggered field broken — analysis-side fixed).
- Apr 26 — Strategy Analysis Revamp Phases 0/1/2/2.1/2.2/2.x: Findings #234-#247 (data-quality + infrastructure: equity 1m bars, opp_log snapshot enrichment,
actual_beat71.6% → 98.1%, SEC EDGAR press-release timestamps 0% → 90.9%). Plus Finding #249 (Polygon mcap fallback + DB-first dashboard cold-path) and Phase 4 partial (dashboard simplification, −5.7k LOC).- Apr 27 — Database Integrity Audit + Dashboard Rebuild (Findings #250-#261, all 6 phases): unified integrity monitor replaces v2/v3 watchdogs, Database tab redesign, storage hygiene + backup OOM streaming fix, doc refresh. Then #262 dashboard 500 chain (6 incident commits — SIGPIPE, pycache, Jinja cache, schema mismatch). Then evening rigorous §B re-audit + #263 (
pg8000 :: castsyntax error broke close path) + #264 (Yahoo override of Polymarket extreme — VZ regression) + #265 (volume display) + #265.v2 (audit-of-audit caught my own incomplete fix) + #267-#274 (audit ancillary).- Apr 28 — Operator-decision parking (Findings #278 + #279): market regime detection v2 (current classifier lags SPY peaks by median 31 trading days) and position sizing + 0-6 priority tier system v2 (Mar 9 percentages never re-validated against current data) both formally deferred to post-Phase-3 review. New Action Queue status
DEFERRED-POST-PHASE-3plumbed throughbuild_action_queue()and dashboard JS (filter chip + summary count). New committed empirical baselinescripts/analyze_regime_detection_lag.py.- Apr 29 — Trading + History tab UX (Findings #280 + #281): Upcoming Earnings widget now shows post-event outcome badges (✅ BEAT · EPS / ❌ MISS · PM / ⏳ PENDING) so released BMO rows are visually distinct from real upcoming opportunities; row dimmed to 55% opacity when settled. New shared trade-chart modal opens from a 📈 button on every Active Trades + History trade-log row, rendering 1m candlesticks (or daily Polygon fallback for pre-Apr-26 trades) with entry / exit / stop / earnings-release markers via TradingView Lightweight Charts. New endpoint
GET /api/trade-chart-data?source={position|audit}&id=N&resolution={auto|1m|15m|1h|1d}. Finding #281.v2 (same-day) added auto-refresh every 10 min for live charts + hidden anchor line series so price scale always includes off-bar markers (caught SOFI exit price line being below visible range). Finding #281.v3 added resolution selector (auto / 1m / 15m / 1h / 1d) so older trades aren't locked to daily-only.- Apr 30 — Volume policy correctness + UX renaming (Findings #282 + #283): CRITICAL —
effective_cumulative_volume_usdnow returnsmax(DB, Gamma)instead of "DB-when-positive else Gamma" because once-per-15-min DB sync became stale within seconds (MRNA #2026-05-01 was being silently gated out at $6.28 < $2k while live Gamma was $3,734). Trader's_fetch_gamma_cumulative_volumeconsolidated to share the SAME helper as the dashboard except-fallback (memory §M cross-interface). Finding #283 renamed dashboard "LOW OB" badge → "WIDE SPREAD" since 37/38 historical kills were spread-dominated, not depth-deficient; gate-tuning deferred-post-Phase-3.- May 3 — IBKR weekly 2FA reconnect (Sunday window). Gateway port 4001 came up at 06:52 UTC; trader reconnected to account U2…13 within 60s; position reconciliation completed; Telegram alert fired. Operational only — no code changes.
- May 6 — P&L Summary enrichment (Finding #284): every existing tile now has hover-tooltips explaining the formula + caveat. New 🎯 Trading Capital ROI panel below the 6 tiles with two columns (PERIOD + ALL-TIME, each with Total / Avg Monthly / Annualized). Trading capital computed dynamically as
net_liq − $25k PDT reserve. Annualization uses compound CAGR(1 + return)^(365/days) − 1. Sub-finding under #279: SHORT ≤10% conviction is symmetrically SKIPPED via("size","90+")PRIORITY_TABLE omission despite the rule never being data-validated on SHORTs (operator caught on COIN #2026-05-07 yes_price=0.0755). Deferred-post-Phase-3 with the rest of the priority/sizing v2 work.- May 7-9 — Exposure-cap rule (Finding #285): operator caught TTD + MCHP signals being silently rejected on rare-day cap (60% on 1-2 opportunity days vs 140% normal). Phase A shipped May 9: greedy-fill in BMO + AMC loops (top-priority signal enters first; subsequent signals see running pending-exposure tally) + opportunity_log emission for
skip_stage='exposure_cap'(was previously stdout-only — zero historical record). 9 new unit tests intests/unit/test_risk_manager.py:TestPhase285GreedyFillExposureMath. Phase B (rule re-evaluation) deferred to Phase 3 Strategy Revamp May 26 alongside #279 — operator's structural argument: "most days become rare days as our bottleneck is markets with volume above $2k usd"; 22/34 last days = "rare" by classifier (rule applies to MAJORITY of trading days). By May 26 we should have ~10-30 logged exposure_cap cases for §K bias gauntlet.- May 10 morning — Dome EOL Apr 28, 2026 migration COMPLETE in one session: Tier A (#287) + Tier B (#288) + Tier C (#289). Tier C (Finding #289 RESOLVED) — codebase + doc cleanup. Deleted 8 dead Dome-dependent files:
dome_client.py,main_collector.py(broken imports, never invoked),backfill_earnings_trades.py(superseded by #269),backfill_candlesticks.py(no readers, table empty),backfill_prices.py(Dome-candlestick consumer, no readers),dome_prices_backfill.py(service stopped Tier A),orderbook_backfill_service.py(service stopped Tier A; no Polymarket replacement for historical orderbook),prices_backfill_service.py(Gamma/markets/{cid}/pricesnot in new docs). Cleanedsrc/data_tracker/collectors/__init__.py(__all__=['PolymarketClient']). Cosmetic rename oflive_trades_collector.py(Q4 investigation verdict: KEEP — service is working, writes ~3,474 trades/24h):DOME_URL→POLYMARKET_DATA_API,fetch_trades_from_dome()→fetch_trades_from_polymarket(),source='dome'→'live_poll',create_trade_idprefixdome_→lp_. No functional change — service was always hittingdata-api.polymarket.com, just had misleading naming. Cleanedearnings_dashboard/app.py(removed dead backfill-process checks; verified no template references) andgap_monitor.py(removedbackfill_candlestickscheck). Updated 6 docs:env.example.txt(removedDOME_API_KEY),README.md,docs/DATA_INTEGRITY.md(§3.1 + §3.2),docs/API_REFERENCE.md(TOC + §4 fully rewritten + §5 + §6),docs/OPERATIONS.md(services + runbook + data sources + appendix + curl health checks),docs/DATA_ARCHITECTURE.md(top-of-file banner with full migration table). Two sub-findings deferred: #289.a (api_health_checker cron silent-failure since Apr 27 — manual run works, cron-env issue) + #289.b (live_trades_collector pg8000 failed-tx cascade — InterfaceError every 5s but trades still being saved). Final cleanup:DOME_API_KEYremoved from prod[hidden]/.envas separate single-command operation post-deploy.- May 10 LATE morning — "NO SHORTCUTS — EVER" rule codified. Operator caught a banner-only shortcut on Tier C v1 (#289) where the agent had updated only the highest-impact lines of DATA_ARCHITECTURE.md (45 Dome refs) instead of doing a full surgical pass. Operator framing: "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!" Codified across 4 surfaces with same gravity as TRADE EXECUTION PROHIBITION:
.cursor/memory.md"🚨🚨🚨 NO SHORTCUTS — EVER 🚨🚨🚨" section + §A pre-coding + §G post-fix checklist additions;docs/CONTEXT.mdMAIN DIRECTIVES §0 (above every other directive);docs/AGENT_HANDOFF.md§11 top-of-section reminder;docs/STRATEGY_PLAYBOOK.md§0 (above the verdict). Self-audit at end-of-session surfaced 4 operator-approved follow-up findings now documented in FINDINGS.md as canonical entries: #290 (delete watchdog dead code per #260c — ~15 min), #291 (long-running-no-op service integrity rule — closes the §E hidden-cascade blind spot from #287 — ~45 min), #289.a (api_health_checker cron silent-failure since Apr 27 — ~30-60 min), #289.b (live_trades_collector pg8000 failed-tx cascade — ~60-120 min). Next-session estimated work: ~1h cleanups (#290+#291) or ~3-4h all 4 items.- May 10 morning — Dome EOL Apr 28, 2026 migration: Tier A (#287) + Tier B (#288) shipped same session. Tier B (Finding #288 RESOLVED): migrated
backfill_trades.pyfrom Dome/polymarket/orders→ Polymarketdata-api/trades?market=<cid>. ReplacedDomeAPIClientwithrequests.Session+ new_fetch_trades_pagehelper. Rate-limit gap 1.2s→0.05s (free tier 200 req/10s = 20 q/s sustained, 20× faster). Cold bootstrap estimate drops ~3h→~8min, warm weekly ~18min→~1-2min. Preserved all smart Option A logic verbatim (T1 delta — now client-side filter onMAX(time)-1hoverlap since Polymarket /trades has no server-sidestart_time; T2 smart re-fetch; T3 earnings-only; circuit breaker; CLI flags). Field mapping:tx_hash→transactionHash,user→proxyWallet,condition_id→conditionId,shares_normalized→size,token_label→outcome. Newasset(token_id) included intrade_idfor multi-fill uniqueness.trade_dedupe.sourcelabel'polymarket_data_api_backfill'for new rows (old rows stay'dome_backfill'for forensic distinguishing). Removeddome_client+prices_backfill_serviceretry tests fromverify_retry_backoff_and_block.pyCI script. Tier C (#289) ships next: delete 7 Dome-dependent files, cosmetic cleanup oflive_trades_collector.py(renameDOME_URL, update docstring, changesource='dome'→'live_poll'), update API_REFERENCE.md/DATA_ARCHITECTURE.md/OPERATIONS.md/README.md/env.example.txt, fixapi_health_checkercron silent-failure, investigatelive_trades_collectorpg8000 failed-tx, remove DOME_API_KEY from[hidden]/.env.- May 10 morning — Dome EOL Apr 28, 2026 discovered + Tier A response (Finding #287). Operator screenshot of docs.polymarket.com revealed Dome was acquired by Polymarket; all Dome APIs reached EOL Apr 28, 2026 — 12 days ago. Yesterday's HTTP 000 / 504 timeouts on
api.domeapi.ioweren't outages, they were the EOL. Comprehensive investigation (Q1-Q7) revealed: 3 Dome-era services have been silently failing for 12 days (polymarket-orderbook-backfillcycling no-ops with frozen state file;polymarket-prices-backfill"saves 0 prices" using Gamma endpoint not in new docs;polymarket-dome-prices-backfillOOM-killed daily). Zero data loss —trades/orderbook_snapshots/price_snapshotsall 0-2 min fresh, populated by Polymarket-native consumers (polymarket-collector,polymarket-live-trades,polymarket-websocket). Tier A shipped: stopped + disabled 3 dead services, archived 3 state files (preserved for Phase 3 forensics), deleted 3 systemd unit files from repo, removed fromdeploy_from_github.sh:servicesarray, manualrmfrom/etc/systemd/system/per §H. New §E lesson: "long-running no-op service" blind spot — service can be active+running but doing zero useful work without alerts. Existing freshness rules catch data impact but not wasted-compute / silent-API-death. Tier B (#288 —backfill_trades.pymigration to Polymarketdata-api/trades, 20× faster rate limit, drops cold bootstrap ~3h → ~8min) and Tier C (#289 — delete 7 Dome-dependent files, cleanuplive_trades_collectorcosmetic, update docs, remove DOME_API_KEY from.env) ship next.- May 9 LATE evening — Pre-Phase-3 housekeeping batch COMPLETE. Operator commissioned a 4-finding housekeeping pass (#269 / #270 / #273 / #274) to use the ~17 days until Phase 3 (May 26) productively. All 4 + 1 forced-detour bug shipped in this session. Finding #270 RESOLVED —
StartLimitIntervalSec/Burstdirectives moved from[Service]to[Unit]across 5 live systemd unit files (earnings-trader,polymarket-integrity-monitor,polymarket-trades-backfill,polymarket-collector,earnings-conviction-tracker) plus theinstall_trader_service.shheredoc template. Original Apr 27 documentation only flagged trades-backfill; memory.md §E same-pattern check surfaced the additional 4 live + 1 helper instances. Decommissioned-watchdog files left untouched per §C. §G probe verified zeroUnknown key namewarnings post-reload. Forced detour: Finding #286 RESOLVED same session — #270's §G probe caught a separate pre-existing bug (May 6, latent since Finding #284 ship):_empty_summarycold-start fallback dict inapp.py:4359was missing the 13 TC-ROI fields added by #284 to the populated path, causing a ~2-minute 503 window after every dashboard restart withUndefinedError: 'roi_tc_total_pct'. Fixed by mirroring the populated dict's full key set + adding|default(0)defense to 6 Jinja conditionals. Lesson reinforced: §E "data-shape contracts" rule — when adding fields to a dict consumed across multiple producer paths, enumerate every producer in lockstep (same structural lesson as §M cross-interface from Finding #225; same §G "probe the changed output" lesson as #265.v2 + #284 v1→v2). Finding #274 RESOLVED — logrotate configdailycadence had no size early-trigger. Pre-flightlogrotate -ddry-run on prod confirmedsizeREPLACES the time-based count (small logs would never rotate) whilemaxsizeADDS a size early-trigger on top of daily cadence — only the latter is correct. Operator approvedsize 500Mfirst based on my incorrect explanation; per memory.md §1 I flagged the correction in pre-flight and re-confirmedmaxsize 500Mbefore shipping. Finding #273 formally deferred to Phase 3 lockstep ship — pm_v2 schema label fires butpm_best_bid/spreadNULL on 8/8signal_emittedrows since Apr 26. Zero live impact (Phase 3 simulator consumer hasn't shipped). Status(DOCUMENTED — deferred to post-Phase-3 — medium)matches parser convention used by #278/#279/#283. Finding #269 RESOLVED via smart Option A — operator chose smart Option A over Option B (manual+document) after weighing free-tier Dome rate budget. Three targeting changes: T1 delta backfill via Dome/ordersstart_timeparameter (per-marketMAX(time)lookup with 1-hour overlap;trade_dedupehandles duplicates), T2 smart re-fetch policy (active markets + closed-within-7-days of earnings_date re-fetched even if previously "completed"; old closed markets stable-skipped), T3 earnings-only scope by default (~5,735 → ~929 markets, 6× reduction). CLI flags:--all-markets,--full-resync,--dry-run. Newpolymarket-trades-backfill.timer(Sun 06:00 ET, Persistent, RandomizedDelay 600s) — runs 2h after data-backfill timer to avoid Dome burst. Cold-bootstrap-then-enable policy:deploy_from_github.shINSTALLS but does NOT enable the timer (operator runs cold bootstrap manually first viasystemctl start polymarket-trades-backfill.service, ~3h on free tier; then enables withsystemctl enable --now polymarket-trades-backfill.timerso subsequent fires are warm-delta ~18 min). Integrity-monitor rulesystemd_timer:polymarket-trades-backfill.timerwith 8-day freshness; returns STATUS_UNKNOWN until enabled (no false alarms). Cold-bootstrap → enable-timer runbook documented indocs/OPERATIONS.md. May 26 Phase 3 deferral bundle is now 4 items (was 3): #278, #279+#285 Phase B, #283, #273.For everything in detail, see
docs/AGENT_HANDOFF.md§11.
This is the single most important rule on this project. It supersedes everything else when there's tension.
A shortcut is any deviation from full, surgical completion of a task to save agent effort, even when rationalized as "pragmatic" or "good enough." Specifically forbidden patterns include: - "Add a banner / TL;DR / summary instead of editing the body" - "Surgically update only the highest-impact lines" when the user asked for "all" - "Several refs updated" without an exact count from grep - "§E same-pattern check" without an exhaustive grep across the entire active surface - "I'll fix it in a follow-up" without explicit operator approval - "This is dead code per §C, leave it" used to skip a same-pattern sweep
Anchoring example (May 10, 2026 — Dome→Polymarket migration Tier C v1):
agent took a banner-only shortcut on DATA_ARCHITECTURE.md (45 inline Dome refs) and said "several updates" on OPERATIONS.md (24 refs) without exhaustive audit. Operator caught it: "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." Fixed in #289.v2; codified as a permanent rule in .cursor/memory.md "NO SHORTCUTS — EVER" section + here + docs/AGENT_HANDOFF.md §11 + docs/STRATEGY_PLAYBOOK.md §1.
Why on this project specifically: live trading bot with real capital. Shortcuts in docs leak into operator confusion when alerts fire, future-agent confusion picking up the project, silent technical-debt that masks real bugs. The same "I'll just take this small shortcut" pattern applied to trading logic (sizing, stops, signals, exposure, exits) absolutely could cost capital. Train the discipline on the small stuff so it's automatic on the big.
If tempted to shortcut: STOP and ASK the operator explicitly. They will almost always say "no, do it properly." Memory.md §1 "Don't assume. Surface tradeoffs." applies — surface the temptation, don't act on it silently.
See .cursor/memory.md "NO SHORTCUTS — EVER" section for the full rule + self-audit prompts.
docs/DATABASE_DIRECTIVES.mdnet_liquidation at runtimeDATA_ARCHITECTURE.md, DATA_INTEGRITY.md, STRATEGY.mdIBKRPositionCache (default 5s TTL, configurable) provides thread-safe position datadocs/STATE_MACHINES.md Section 3.5src/earnings/
├── trading_orchestrator.py # Thin coordinator - creates and wires managers
├── trading_mode_persistence.py # Shared mode persistence + self-healing default
├── position_manager.py # Position CRUD, DB persistence, stop order tracking
├── exit_manager.py # All exit paths: scheduled, instant, batch, stop backup
├── exposure.py # Freed-capital + reservation free-book (one helper, #401)
├── reconciliation_manager.py # IBKR/DB reconciliation, table sync
├── trading_utils.py # Shared utilities: price, time, scheduling
├── position_state.py # Position state machine (PENDING/OPEN/CLOSING/CLOSED/FAILED)
├── ibkr_position_cache.py # Thread-safe IBKR position cache (5s TTL, source of truth)
├── event_bus.py # Publish/subscribe event system (real-time stop fill detection)
├── risk_manager.py # Position sizing, circuit breakers, stop-loss
├── signal_generator.py # Signal generation, prioritization
├── polymarket_opportunity_log.py # Research DB log (all PM candidates; Finding #221)
├── counterfactual_pricing.py # Polygon/yfinance counterfactual sim for opportunity log
├── order_executor.py # IBKR order execution
├── batch_order_manager.py # Concurrent batch order execution
├── earnings_detector.py # Earnings release detection
├── conviction_calculator.py # Orderbook → conviction
├── earnings_timing.py # SINGLE SOURCE OF TRUTH for BMO/AMC timing
├── timing_data/overrides.json # Persistent timing overrides
├── config.py # Centralized configuration with validation
├── types.py # Shared types (ActivePosition, MarketStatus, Result)
├── audit_logger.py # Database audit trail
├── alerts.py # Telegram notifications
└── market_calendar.py # NYSE calendar (2024-2028 holidays)
Architecture Note (Phase 4A - Feb 7, 2026): The TradingOrchestrator was split from a 4878-line monolith into 6 focused modules using dependency injection. Each manager receives only its specific dependencies via __init__. The orchestrator creates and wires all managers, then delegates to them.
src/ibkr/
├── client.py # IBKRClient for account/order operations
├── gateway_monitor.py # Background health monitoring
└── fee_calculator.py # Trade cost calculations
earnings_dashboard/
├── app.py # FastAPI application
├── secret_redaction.py # Finding #467: response filter (defense in depth)
├── identifier_masking.py # Finding #467.D: account/order/wallet/path masking
└── templates/dashboard.html # Jinja2 template
Dashboard Reality Overlay: ibkr_reality panel compares DB positions vs live IBKR and verifies stop orders. Live IBKR data is read from the trading service snapshot file. Freshness is src/earnings/ibkr_snapshot.is_stale (Finding #472) — not IBKR_SNAPSHOT_INTERVAL_SEC.
Public dashboard defense in depth (Finding #467): the dashboard is intentionally public. install_secret_redaction(app) (one line, right after app = FastAPI(...)) redacts secrets from every response; identifier_masking.py masks IBKR/wallet/host identifiers at each payload's source. FastAPI's auto-docs are disabled; three inert unauthenticated write endpoints were removed. See docs/FINDINGS.md #467.
src/db/
├── earnings_sync.py # EarningsMarketSync: earnings_markets + MCAP backfill (Finding #219)
└── __init__.py
market_cap_category: Filled via sync enrichment, backfill_upcoming_null_mcaps(), and yfinance_limiter (Yahoo + Finnhub with FINNHUB_API_KEY). Hourly Gamma sync (src/data_tracker/collectors/earnings_market_fetcher.py) runs the backfill after each run. See docs/DATABASE_DIRECTIVES.md and Finding #219 in docs/FINDINGS.md.
run_earnings_trader.py # Main trading service entry point
run_dashboard.py # Earnings dashboard entry point
run_gateway_monitor.py # IBKR gateway monitor service entry point
deploy/
├── earnings-trader.service # systemd service for trader
├── install_trader_service.sh # Installation script
.github/workflows/verify_retry_backoff.yml runs scripts/verify_retry_backoff_and_block.py 1100 transient (no persistent block), 1300 critical (persistent block + trading blocked)bash deploy/remote_deploy.sh from the operator machine — runs [hidden]/deploy/deploy_from_github.sh (pull + restart listed services) detached as a systemd-run unit and polls it (F-8; OPERATIONS §8.A1); for dashboard-only changes you may git pull and systemctl restart polymarket-dashboard.service only.get_db_stats() uses a light _fetch_db_stats_from_db(full=False) on cache miss and a background thread upgrades to full=True (Finding #215, docs/FINDINGS.md).max_locks_per_transaction; mitigations in docs/POSTGRES_OPERATIONS.md (Finding #216). docs/OPERATIONS.md references this.docs/OPS_TIMING_VERIFICATION.md (Finding #214).scripts/strategy_analysis_report.json. Regenerate after material DB changes:cd [hidden] && source .env && export DATABASE_URL && venv/bin/python3 scripts/strategy_performance_analysis.pydocs/STRATEGY_PLAYBOOK.md §2. The 🛡️ Bias Gauntlet + 🔬 Finding-A Simulator sections capture process learnings + the Apr 25 retraction. Includes position-sizing quintiles and other checks (Finding #218; consolidate analyses per agent memory.md §K in Cursor).scripts/analyze_finding_a_full.py — read-only counterfactual replay of v3 Finding A and the alternative stop-loss rules (B/D/G/F) on Polygon 1m bars. Re-run before any new stop-loss rule proposal. Output scripts/finding_a_full.csv is consumed by analyze_finding_a_simulator() in strategy_performance_analysis.py and rendered in the dashboard. Canonical narrative: docs/FINDINGS.md Finding #228.src/earnings/market_cap_tier.py, the trader's own resolver (KNOWN_MCAPS → shared 24 h cache → yfinance; unknown = Small; #450 review). Other rows: database market_cap_category first, then in-process fallbacks (KNOWN_MCAPS, limiter). Backfill + limiter behavior: Finding #219 (docs/FINDINGS.md); agent checklist **memory.md §L`.earnings_time): the hourly earnings_market_fetcher writes NASDAQ-explicit timing or earnings_timing.py defaults/overrides; no web timing is persisted. Optional one-ticker NULL/TBD backfill: scripts/backfill_ticker_earnings_timing.py. Legacy run_full_sync / markets-table sync is retired before DB access (#480: it lacked the mandatory date-audit write). Dashboard NEEDS TIMING vs ✓ TRADE gating. Finding #220 (docs/FINDINGS.md); details docs/DATABASE_DIRECTIVES.md, docs/OPERATIONS.md.src/earnings/config.py + STRATEGY.md §4; updated Aug 21, 2026)S2c equal-split (Finding #366, DEPLOYED Aug 21, 2026): with trading capital ≥ S2C_MIN_ACTIVATION_CAPITAL ($9k), enter every qualifying signal, split the free exposure budget equally, shrink to fit, never above S2C_MAX_POSITION_PCT (25%) per name. Below the gate: FLAT 25% all mcap tiers (#314.f; history: 30/22/15 per #211 retired Jul 6, 2026). Stop-losses by tier (Finding #202): Large 3% · Mid 4% · Small 5%.
Fractional Shares: ✅ Enabled - System uses fractional shares for NYSE/NASDAQ/AMEX stocks (4 decimal precision). Falls back to whole shares for ineligible stocks.
Exit Verification: ✅ Enabled (Jan 11, 2026) - Every exit order verifies IBKR position is flat before marking DB closed. Alerts on verification failures.
Position Reconciliation: ✅ Enabled (Jan 11, 2026) - On startup, reconciles DB positions with IBKR. Detects orphaned positions and external trades.
src/earnings/risk_manager.py class docstring; updated Aug 21, 2026 — CLB-1/#366)| Trigger | Action |
|---|---|
Daily loss 5% (DAILY_LOSS_LIMIT) |
Sole loss brake — stop for the day; DB-anchored; resumes at true next ET open (A-6/#366) |
| Day P&L ≤ −2.5% / ≤ −4% | Sizes ×0.50 / ×0.25 (re-keyed from the old loss-streak tiers) |
| Drawdown 30% / 40% / 50% | Reduce sizes 50% / 75% / full pause |
| VIX > 30 / > 35 | Reduce sizes 50% / no new positions |
| S&P 500 < -5% | Pause all entries |
| Session-stop breaker: 4 stop-exits per ET day | Pause signal generation for the session (#312; threshold 4 per #335.b) |
(The consecutive-loss breaker — 3/5/7-loss pauses — was RETIRED by CLB-1, Finding #366, deployed Aug 21, 2026.)
SIGNAL_LONG_THRESHOLD, SIGNAL_SHORT_THRESHOLD (read live by signal_gates, the one band definition for trader + dashboard; GUARD ≥0.70 / ≤0.30; Finding #450)SIGNAL_MIN_VOLUME_TOTAL (default 1000 USD; lowered $2k→$1k May 30, 2026 per Finding #311 SHIP after Phase 3 unified-strategy framework + 5-KPI cockpit + per-tier empirical scaling. Effective cumulative = max(markets.volume_total, Gamma volume) per Finding #282, Apr 30, 2026; previously "positive DB else Gamma" per Findings #222-224 but stale-DB-low could silently gate out tradeable markets)POLYMARKET_DECISION_CONFIRMATION_SEC (live detector; unread POLYMARKET_CONFIRMATION_SEC Config knob deleted #405)RECONCILIATION_AUTO_CLOSE (default false)IBKR_POSITION_CACHE_TTL_SEC (default 5s)IBKR_SNAPSHOT_PATH, IBKR_SNAPSHOT_INTERVAL_SEC (write floor; cadence + overlay stale live in src/earnings/ibkr_snapshot.py, Finding #472)IBKR_REQUEST_PATH_FAIL_THRESHOLD (default 3; GUARD 1–10; Finding #412)IBKR_REQUEST_PATH_CLEAR_THRESHOLD (default 3; GUARD 1–10; Finding #413)IBKR_REQUEST_PATH_RECONNECT_COOLDOWN_SEC (default 300; GUARD 60–3600; Finding #413)IBKR_ALERT_CATEGORY_COOLDOWN_SEC (default 300; GUARD 30–3600; Finding #413)EVENTBUS_MAX_FILE_BYTES (default 50MB)| Time | Action |
|---|---|
| 10:32 AM | BMO Exit (1h after open - captures first-hour gains) |
| 10:34 AM | AMC Entry (1h 4 min after open) |
| 3:57 PM | AMC Exit (1 min buffer before BMO entry) |
| 3:58 PM | BMO Entry (2 min before close) |
src/earnings/priority.py + STRATEGY.md §3.1)Rank 3 = LONG 70-75% any mcap · rank 2 = LONG 80-85% large/mid · rank 1 = every other qualifier (incl. qualifying SHORTs and B-5 pilot small-cap LONGs) · 0 = SKIP. No volume dimension, no market-cap spine — both refuted by #357 (the #313 volume→win-rate gradient inverted). History: scale 0-6 (#211) → 0-8 (#313) → 0-10 (#334) → 0-3 (B-4/#366).
| Table | Purpose |
|---|---|
markets |
All Polymarket markets (4,719 rows) - volume only, no yes_price |
earnings_markets |
Curated earnings markets (414 rows) |
trading_positions |
Active/closed positions |
trade_audit_log |
Detailed trade records |
audit_events |
System event logging |
price_snapshots |
Live price history - CONVICTION SOURCE (yes_price) |
orderbook_snapshots |
Orderbook depth history |
trades |
Historical trade data |
⚠️ CRITICAL: Conviction (beat probability) comes from
price_snapshots.yes_price, NOTmarketstable. When querying for future entry times (BMO tomorrow), use the LATEST available data.
systemctl status earnings-trader # Trading bot
systemctl status polymarket-dashboard # Dashboard
systemctl status ibkr-monitor # Gateway health
SHELL=/bin/bash # Required so chained `source venv/bin/activate` works (Finding #289.a)
# Note: api_health_checker MIGRATED to systemd timer per Finding #289.a (May 10, 2026):
# polymarket-api-health.timer + polymarket-api-health.service. Daily 08:00 UTC.
# Note: sync_earnings_markets.py cron REMOVED per Finding #293 (May 10, 2026) —
# was silently broken 14 days due to /bin/sh dash bashism, AND the
# EarningsMarketSyncer it invoked used a broken Polymarket Website URL. Hourly
# `polymarket-earnings-sync.timer` (EarningsMarketFetcher, Gamma-based) is the
# canonical earnings_markets writer. Finding #480 later retired the wrapper
# and src.db.earnings_sync manual entry points before DB access.
[hidden]/ # Application root
[hidden]/venv/ # Python virtual environment
[hidden]/logs/ # Log files
[hidden]/trading_mode.txt # Persisted mode (testing_prod)
[hidden]/trading_mode.txtsrc/earnings/trading_mode_persistence.pyrun_earnings_trader.pyearnings_dashboard/app.pysrc/data_tracker/collectors/trade_reconciler.pytesting_prod (not paper)testing_proddocs/OPERATIONS.md, docs/IBKR_SETUP.md)docs/IBKR_SETUP.md §8; status: UNVERIFIED from this doc — check ibgateway-ibc-paper / port 4002 on host):5901 is GUI fallback only (docs/IBKR_CONTINUITY.md, .cursor/memory.md IBKR connect contract)ibkr-monitor.service pages live :4001 down >180 s, page-only (#448/#456). gateway_restart_manager.sh is retired (#476)ibgateway-ibc.service (IBC manages Gateway lifecycle).cursor/memory.md, Finding #385)IBKRClient — correct connection parameters for scripts:
from src.ibkr.client import IBKRClient
client = IBKRClient(
paper=False, # Live trading (NOT paper)
use_gateway=True, # IB Gateway (NOT TWS)
client_id=99 # trading service owns client_id=1
)
# Auto-selects port 4001 (GATEWAY_LIVE_PORT)
| Mode | Port | Use |
|---|---|---|
| TWS Paper | 7497 | Local paper trading |
| TWS Live | 7496 | Local live trading |
| Gateway Paper | 4002 | Server paper |
| Gateway Live | 4001 | Server live (USE THIS) |
get_account_info() (NOT get_account_summary — returns AccountInfo), get_positions() (cheap — List[PositionInfo] with .symbol/.shares/.avg_cost only; market_value/unrealized_pnl are 0.0 — #396; do not use it for marks; raises PositionsLookupError when not connected or ib.positions() fails — #400; a successful empty book is []), get_quote(symbol) (expensive; dict with bid/ask/last), connect() / disconnect()PositionInfo: .symbol, .shares (NOT .position), .avg_cost, .market_value, .unrealized_pnlAccountInfo: .account_id (e.g. "U2…13"), .net_liquidation, .available_funds, .buying_powerRiskManager — correct parameters:
from src.earnings.risk_manager import RiskManager, TradingMode
rm = RiskManager(
total_capital=capital,
trading_mode=TradingMode.TESTING_PROD # NOT mode=, use trading_mode=
)
position_size = rm.calculate_position_size(signal, stock_price=price)
TradingMode values: PAPER, TESTING_PROD (100% sizing, TESTING_PROD_SIZE_MULTIPLIER=1.0), LIVESignalGenerator:
from src.earnings.signal_generator import SignalGenerator
sg = SignalGenerator(os.environ["DATABASE_URL"])
signals = sg.get_bmo_signals_for_tomorrow() # List[TradingSignal]
TradingSignal: .symbol, .direction (SignalDirection.LONG/SHORT), .conviction (beat probability 0-1), .market_cap (MarketCap enum), .entry_datetime / .exit_datetime (timezone-aware), .earnings_timing (EarningsTiming.BMO/.AMC)Server-side script templates (SSH + venv + DATABASE_URL) and the conviction-tracker runbook live in docs/OPERATIONS.md.
docs/AGENT_HANDOFF.md §11, last updated Sep 26, 2026 Reliability Batch A)TESTING_PROD (phase: Phase 1 since Aug 21, 2026 — #367)Config.PHASE1_START_ET, set at the PKG-357 deploy). The testing era closed at 193 trades — its 150-trade target was milestone-only since #347 (auto-pause removed); the dashboard counter now counts Phase-1 trades from zeroPDT_RESERVE_CAPITAL, FUND-1 #366) → ~$10,000 trading capital (was $25k reserve / ~$2,450 capital until Aug 20, 2026)| File | Purpose |
|---|---|
docs/AGENT_HANDOFF.md |
Onboarding bootstrap (read first); §11 = current state + ranked outstanding work |
docs/STRATEGY_PLAYBOOK.md |
Forward-looking source of truth. Ranked Action Queue (mirrored in dashboard) + verified findings + retracted findings + implementation notes per rule |
docs/STRATEGY_AUDIT_v3.md |
Audit trail — every hypothesis tested + audit-process learnings; §10 (Apr 25) = Finding A retraction |
docs/FINDINGS.md |
Canonical narrative for every numbered finding (per memory.md §J). Append-only — the latest finding is at the insertion point noted in its header (never cite "latest #N" in prose; it rots) |
docs/STRATEGY.md |
Long-form trading strategy (rules, schedules, sizing, exchanges) |
docs/OPERATIONS.md |
Deployment & operations; runbooks for resolver + simulator + report regeneration |
docs/EDGE_CASES.md |
Holiday/early close handling + extended-hours stop fill mechanics (Apr 25) |
docs/FINANCIAL_PROJECTIONS.md / docs/FINANCIAL_PROJECTIONS_REVISED_87_TRADES.md |
Historical ROI projections (superseded by Playbook §1 for current numbers) |
docs/IBKR_SETUP.md |
Gateway configuration |
docs/IBKR_CONTINUITY.md |
Weekly 2FA & session management |
docs/DATA_ARCHITECTURE.md |
Database schema & data sources + outcome resolver + Finding-A simulator |
docs/API_REFERENCE.md |
Polymarket API docs + Dashboard endpoints |
docs/DATABASE_DIRECTIVES.md |
No tech debt policy |
docs/PAPER_TRADING_TEST_PLAN.md |
Testing checklist |
docs/SYSTEM_REVIEW_2026_01_06.md |
Earlier health audit |
docs/STRATEGY_IMPROVEMENTS.md |
DEPRECATED Feb 8 doc; redirects to STRATEGY_PLAYBOOK |
GET /api/state - Full dashboard stateGET /api/health - Health checkGET /api/signals - BMO/AMC signalsGET /api/positions - Active positions (post Finding #281: response includes id field for chart-link wiring)GET /api/earnings-calendar - Upcoming earnings (post Finding #280: response includes outcome_status / outcome_label / outcome_source / outcome_yes_price / outcome_settled for the post-event status badge column)GET /api/trade-chart-data?source={position|audit}&id=N&resolution={auto|1m|15m|1h|1d} - NEW Apr 29 (Finding #281 + #281.v2 + #281.v3) Bars + entry/exit/stop/earnings markers for the trade-chart modal. Source position reads trading_positions, audit reads trade_audit_log. Resolution auto (default) routes by trade age: live OR closed-within-24h → fresh Polygon 1m; older closed → equity_bars_1m cache; pre-Apr-26 (cache start) → polygon_daily fallback. Explicit resolutions (1m / 15m / 1h / 1d) bypass the routing and fetch directly from Polygon at the requested interval with a resolution-appropriate window pad. Live charts include meta.live_refresh_seconds=600 for the JS auto-refresh interval.GET /api/ibkr-status - Gateway connectionGET /api/trader-status - Trading service statusGET /api/dependencies - External service healthGET /api/testing-prod-status - Progress trackingPOST /api/set-trading-mode, /api/testing-prod/confirm, /api/testing-prod/reset — unauthenticated writes that were already inert; change mode via docs/OPERATIONS.md.)GET /docs/{doc-name} - Rendered markdown docsssh root@[hidden] "systemctl status earnings-trader polymarket-dashboard ibkr-monitor"
ssh root@[hidden] "tail -50 [hidden]/logs/earnings_trader.log"
ssh root@[hidden] "systemctl restart earnings-trader polymarket-dashboard"
ssh root@[hidden] "curl -s http://127.0.0.1:8080/api/state | python3 -m json.tool"
ssh root@[hidden] "cd [hidden] && source venv/bin/activate && python3 -c \"
from sqlalchemy import create_engine, text
import os
engine = create_engine(os.environ['DATABASE_URL'])
with engine.connect() as c:
r = c.execute(text('SELECT COUNT(*) FROM earnings_markets WHERE earnings_date >= CURRENT_DATE'))
print(f'Upcoming earnings: {r.fetchone()[0]}')
\""
docs/IBKR_CONTINUITY.md — history: docs once claimed VNC was the primary path)docs/IBKR_SETUP.md (history: "intentionally down for TESTING_PROD" was stale; live earnings uses :4001 only)When starting a new conversation:
docs/CONTEXT.md first"Example prompt:
Please read docs/CONTEXT.md to understand the project context.
I want to [specific task]. Remember the main directives:
- Read ALL files before acting
- No tech debt in database
- Capital from IBKR, not hardcoded
This document is the single source of truth for AI context bootstrapping.