Branch Status — feat/anchor-dual-signal-calibration¶
C1 — Schema + Config ✅ COMPLETE¶
Verified (Vesper): All spec items landed. No issues.
state_manager.py— three new REAL columns (structural_basis_bps,rolling_basis_baseline_bps,residual_distortion_bps) onsystem_metricsCREATE TABLE; three matching_ensure_columnALTER calls for legacy DBs;record_system_metricextended with threeOptional[float] = Nonekwargs; INSERT updated.config.py—AnchorDualSignalConfigfrozen dataclass with Atlas Option 3 ruling reference in docstring; registered onConfig; YAML parse block added;_validate_anchor_dual_signalwith five invariants (includeswarmup_ticks <= basis_ema_window_ticks); wired intoload_config.config_live_stage1.yaml,config.example.yaml,config.yaml—anchor_dual_signal:block added with correct defaults:enabled: true,basis_ema_window_ticks: 150,warmup_ticks: 50,residual_hysteresis_lookback_ticks: 20,baseline_staleness_hours: 24.0.- Backward compat confirmed: legacy-shape
record_system_metriccall leaves new columns NULL. - Backup DB ALTER migration path confirmed.
- 99 tests passed (
test_config.py+test_state_manager.py).
Branch not yet cut (per standing rule #1 — correct).
C2 — Signal Computation ✅ COMPLETE¶
Verified (Vesper): All spec items landed. Zero new failures.
dual_signal_calculator.py— new module.DualSignalObservationfrozen dataclass +AnchorDualSignalCalculator.observe()gates baseline/residual toNoneduring warm-up (EMA advances internally — warm-ready at tick 51).seed_baseline()/reset()stubs in place for C4.strategy_engine.py— three new attrs (last_structural_basis_bps,last_rolling_basis_baseline_bps,last_residual_distortion_bps). Atlas-literal formula((mid - amm) / mid) * 10000.0— NOT reusingraw_divergence_bps. Sign invariant confirmed:structural_basis_bpsandlast_anchor_divergence_bpscarry opposite signs.main_loop.py— four touch points: import, instantiate, observe, persist._persist_tick_telemetryuses samesnapshot.is_valid()gate as legacyanchor_err_bps.test_anchor_error_telemetry.py— helper fixed (MagicMock → None for new attrs).- 212 FLAG-048-adjacent tests green. Zero new failures on regression (325/639 pre-existing split unchanged).
- Legacy anchor saturation guard untouched — reads
last_anchor_divergence_bps/ capped window unchanged until C3.
C3 — Guard Rewire ✅ COMPLETE¶
Verified (Vesper): All spec items landed. Rename confirmed. Zero stale references.
_select_anchor_guard_window()— returns(residual_window, "residual_distortion_bps")when dual-signal enabled AND warm; falls back to(anchor_error_window, "last_anchor_divergence_bps")otherwise.- Both entry and exit evaluators read through the same selector — hysteresis coherence preserved.
- Source label threaded into WARNING logs and circuit_breaker context dict for forensic visibility.
_anchor_residual_windowfed fromdual_obs.residual_distortion_bpswhen warm. NULL-during-warmup honored — no partial-window saturation risk.- Rename:
_evaluate_anchor_saturation_guard→_evaluate_anchor_residual_guard.main_loop.py(5 replacements),test_anchor_idle_state.py(4 replacements),test_anchor_saturation_guard.py(13 replacements).grep -rn "_evaluate_anchor_saturation_guard"returns zero hits. _evaluate_anchor_idle_exitintentionally NOT renamed — FLAG-046 scope, different semantic layer.anchor_saturation_guardYAML key intentionally NOT renamed — backward-compat surface, separate deliberate decision if ever revisited.- Test fixtures:
_select_anchor_guard_windowstubbed on MagicMock engines in three suites to exercise legacy capped-window path. Residual-window coverage reserved for C5. - 120/120 FLAG-048 adjacent tests green. Full regression 691 passed / 378 failed — baseline-identical. Pre-existing failures unrelated to FLAG-048.
C4 — Cross-Session Persistence ✅ COMPLETE¶
Verified (Vesper): All spec items landed. Branch coverage correct. Standing note answered.
dump_state()— returns(baseline, effective_sample_count)orNoneif pre-seed. Does NOT gate onis_warm— correct. Pre-warm EMA is a legitimate partial sample worth preserving across sessions.- Three
engine_statekey constants:KEY_ANCHOR_DS_BASIS_BASELINE_BPS,KEY_ANCHOR_DS_BASIS_BASELINE_COUNT,KEY_ANCHOR_DS_BASIS_BASELINE_CLOSED_AT. Namespaceanchor_dual_signal.*. _restore_anchor_dual_signal_baseline()— 7 behavioral branches: disabled→noop, missing→cold-start silent, DB read exception→cold-start log, malformed float→cold-start+clear, malformed ISO→cold-start+clear, stale (>24h)→cold-start+clear+WARN with forensic detail, seed rejects count<1→cold-start+clear+ERROR, success→seed+INFO withis_warm_on_resume. All correct._persist_anchor_dual_signal_baseline()— 3 branches: disabled→noop, no observation→noop (preserves prior persisted state — critical: engine that halts pre-tick-1 must not cost next run its warm-up), normal→3-key atomic write. No-observation path is the right call._clear_anchor_dual_signal_persistence()helper — best-effort triple clear, exceptions swallowed.- Wired into
_startup()and_shutdown()correctly. - Standing Vesper note answered:
fix/startup-mode-resetconfirmed does NOT touchanchor_dual_signal.*keys (commits401d006+502be63touchhalt.*,inventory_truth.*,KEY_ANCHOR_IDLE_*, FLAG-044 cooldown only). Documented inline in reset block. - 188/188 FLAG-048 adjacent tests green. Full regression 691/378 — baseline-identical.
- 9 behavioral branches verified end-to-end via direct unit exercise.
VPS note (flag for setup checklist): KEY_ANCHOR_DS_BASIS_BASELINE_CLOSED_AT is an ISO timestamp — staleness check is timezone-sensitive. Atlas's hardening baseline requires NTP + correct timezone on VPS before engine runs. Confirm this is in the setup runbook.
C5 — 12 Tests ✅ COMPLETE¶
Verified (Vesper): All spec items landed. 17 tests, 12 plan items, 0 new failures. Branch accepted.
tests/test_flag_048_dual_signal.py— 874 lines, 17 tests, 5 classes.- T1: Structural basis uncapped ✅ | T2: Sign convention (±basis) ✅ | T3: EMA convergence (stable + stepped) ✅
- T4: Rail-lock proof ✅ (non-negotiable per Atlas) | T5: Residual entry fires (fire + below-threshold) ✅ | T6: Exit reachability proof ✅ (non-negotiable per Atlas)
- T7: Hysteresis preserved ✅ | T8/T9/T10: Cross-session persistence DB round-trips via real
StateManager(":memory:")✅ — T10 additionally asserts three persistence keys cleared after stale discard - T11: Warm-up suppression (calculator None + selector fallback) ✅ | T12: Dashboard no hidden substitution (3-column distinctness + NULL-not-zero) ✅
- 150-tick benchmark row: N=150, alpha=0.0132, baseline@t150=13.46, residual_stdev=1.14 bps. 86% convergence in one window width — correct default. ✅
- Full regression: 708 passed (+17 vs C4 baseline), 378 pre-existing failures unchanged. Zero new failures. ✅
- NTP/UTC VPS prerequisite documented by Orion, traceable to Vesper's C4 note. ✅
⚠️ Pre-live gate open item: T4 rail-lock proof is synthetic at session-52 shape — live DB was not accessible from sandbox. Atlas's pre-code ruling requires replay on S48/S49/S50 showing exit condition reachable in afternoon ET. This is NOT a branch acceptance blocker but IS required before session hold lifts and S51 runs. Orion to complete once live DB is accessible (VPS migration or direct copy to working tree).
Pre-Live Replay — ✅ COMPLETE — SESSION HOLD LIFTED¶
Vesper ruling (2026-04-22): Option A — replay accepted as sufficient. Session hold lifted.
- Replay executed against
neo_live_stage1.db.bak.20260421T165223Z(integrity_check=ok) via realStateManager(":memory:")round-trips. No mocks. Read-only (mode=ro&immutable=1). - Sessions 50/51/52 (S47/S48/S49) replayed in order. Session 53 (S50) unrecoverable — live DB malformed (Atlas's exact SMB/WAL failure mode confirmed).
- Exit reachability (Q4) ✅ PASS: Session 51 tick 171 — structural +10→+4.58, residual = +0.08 bps. Sub-threshold, single-tick reaction.
- Rail-lock (Q5) ✅ DIRECTIONAL PASS: Session 52 (38 ticks @ +10) — residual 5.42→3.31 monotone toward 0. Right sign, right slope. Full convergence unambiguous from 150-tick window math.
- Cross-session persistence ✅ PASS: dump→write→read→seed round-trip verified end-to-end at both session boundaries.
- Warm-up gating ✅ PASS: Session 50 (32 ticks, never warm) emits None throughout. Session 51 seeded at 32, crossed warm at tick 18.
- Uncapped structural not exercised (data gap, not calculator gap) — queued for post-merge replay after first clean session.
- FLAG-049 urgency confirmed: backup cadence was the only reason this replay was possible.
Replay report: [C] Orion Pre-Live Replay Report — FLAG-048.md
Branch Disposition — ✅ MERGED — SESSION HOLD LIFTED¶
All five checkpoints (C1–C5) verified by Vesper. Branch merged to main (commit a8033e5). Pre-live replay complete. Katja cleared to run next session.
— Vesper (COO) 2026-04-22