Orion Delivery — fix/startup-mode-reset (FLAG-041 follow-up)¶
To: Vesper (she/her)
From: Orion (he/him)
CC: Katja (Captain), Atlas (he/him)
Date: 2026-04-21
Branch: fix/startup-mode-reset
Base: main (does NOT include FLAG-041 — independent bug, independent branch)
Flag: FLAG-041 follow-up — BLOCKING before S43
Type: Delivery — patches + apply instructions
TL;DR¶
S43 pre-flight fix landed per your tasking. Single-file change in _startup(): the fresh-session clear block now also resets inventory_truth.mode → MODE_OK, inventory_truth.degraded_since → "", and inventory_truth.degraded_reason → "" alongside the existing halt.reason / halt.detail clear. Recovery restarts (parent_session_id is not None) deliberately preserve all four fields — the resuming engine still inherits its parent's degraded/halt semantics.
2 commits, 3 new tests pinned in a new TestStartupModeReset class, 250/250 regression green. No new imports (the truth-checker constants were already imported at top of main_loop.py).
Root Cause Recap (from Vesper's audit)¶
S42 HALTed and left inventory_truth.mode=halt persisted in engine_state. S43 startup cleared halt.reason / halt.detail (the FLAG-041-fixed clear block) but NOT inventory_truth.mode. Startup truth check returned ok, but _apply_truth_check_result only exits DEGRADED, not HALT. Tick 1 saw MODE_HALT, returned False from _tick() without writing halt.reason, and _shutdown's precedence halt_reason or existing_reason or HALT_REASON_UNEXPECTED fell through to unexpected_halt — the FLAG-041 default, now visible because the old engine_requested_halt clobber is gone.
This branch is independent of fix/halt-reason-taxonomy-leak. The bug would surface without FLAG-041 too — it'd just produce a different clobbered token. Both branches can apply in either order; no conflict.
Implementation¶
Commit 1 — 010de6d fix(main_loop): clear inventory_truth.mode on fresh session startup¶
Single edit in neo_engine/main_loop.py, _startup()'s fresh-session clear block (~lines 578–586). Before:
if parent_session_id is None:
try:
self._state.set_engine_state("halt.reason", "")
self._state.set_engine_state("halt.detail", "")
except Exception:
log.debug(
"Failed to clear halt.reason on fresh session startup",
exc_info=True,
)
After:
if parent_session_id is None:
try:
self._state.set_engine_state("halt.reason", "")
self._state.set_engine_state("halt.detail", "")
# FLAG-041 follow-up (S43 pre-flight): also reset stale
# inventory_truth mode / degraded bookkeeping. Without this,
# a prior session's MODE_HALT persists in engine_state, and
# tick 1 of the fresh session sees MODE_HALT -> returns
# False without writing halt.reason -> shutdown falls back
# to unexpected_halt. Recovery restarts (parent_session_id
# != None) deliberately preserve these so the resuming
# engine can finish degraded/halt semantics.
self._state.set_engine_state(KEY_MODE, MODE_OK)
self._state.set_engine_state(KEY_DEGRADED_SINCE, "")
self._state.set_engine_state(KEY_DEGRADED_REASON, "")
except Exception:
log.debug(
"Failed to clear halt.reason / truth-mode on fresh session startup",
exc_info=True,
)
Diff: 12 insertions, 1 deletion in main_loop.py. No new imports — KEY_MODE, KEY_DEGRADED_SINCE, KEY_DEGRADED_REASON, MODE_OK are all already pulled from neo_engine.inventory_truth_checker at the top of main_loop.py.
Recovery path (parent_session_id is not None) is untouched — it never enters this block, and the resuming engine inherits parent state.
Commit 2 — d25e300 test(startup): inventory_truth mode reset assertions¶
New TestStartupModeReset class appended to tests/test_halt_reason_lifecycle.py with 3 cases. All exercise engine._startup() against a real StateManager(":memory:").
| Test | What it proves |
|---|---|
test_startup_clears_mode_halt_on_fresh_session |
Canonical S43 regression. Seeds KEY_MODE=MODE_HALT + KEY_DEGRADED_SINCE + KEY_DEGRADED_REASON; fresh startup (parent_session_id=None) flips all three back to their clean defaults (MODE_OK, empty, empty). |
test_startup_clears_mode_degraded_on_fresh_session |
Same contract for MODE_DEGRADED — covers the mid-DEGRADED-crash path. |
test_startup_preserves_mode_on_recovery_restart |
Recovery restart (parent_session_id=37) must NOT reset — all three fields preserved. Pairs with the existing test_startup_preserves_halt_reason_on_recovery_restart contract. |
Pre-existing cases in the file (stale-ghost override, unexpected fallback, fresh halt.reason clear, recovery halt.reason preserve) unchanged and still pass. File total: 7 cases (4 pre-existing + 3 new).
Added imports at the top of the test file: KEY_DEGRADED_REASON, KEY_DEGRADED_SINCE, KEY_MODE, MODE_DEGRADED, MODE_HALT, MODE_OK from neo_engine.inventory_truth_checker.
Commits¶
| # | Hash | Subject |
|---|---|---|
| 1 | 010de6d |
fix(main_loop): clear inventory_truth.mode on fresh session startup (FLAG-041 follow-up) |
| 2 | d25e300 |
test(startup): assert inventory_truth mode reset on fresh session (FLAG-041 follow-up) |
Regression¶
Run on fix/startup-mode-reset (Linux sandbox, Python 3.10):
python -m pytest tests/test_halt_reason_lifecycle.py -v
→ 7 passed in 0.24s
python -m pytest tests/test_halt_reason_lifecycle.py tests/test_anchor_saturation_guard.py \
tests/test_directional_drift_guard.py tests/test_inventory_corridor_guard.py \
tests/test_anchor_error_stat.py tests/test_flag_036_wallet_truth_reconciliation.py \
tests/test_reconciler_anomaly_log.py tests/test_reconciler_conservative.py \
tests/test_state_manager.py tests/test_summarize_paper_run_overlay_baseline.py \
tests/test_inventory_invariant.py tests/test_wal_checkpoint_hardening.py \
tests/test_distance_to_touch_summary.py tests/test_config_invariants.py \
tests/test_ledger_reconciler.py
→ 250 passed in 10.01s
3 new tests + 247 pre-existing Phase-7.3-relevant tests — all green.
Note: tests/test_summarize_paper_run.py::TestSummarizePaperRun::test_summary_uses_existing_persisted_state continues to fail with KeyError: 'total_paper_orders_created' on main and on this branch — pre-existing, unrelated, out of scope. Same caveat as the FLAG-041 delivery.
Patches¶
Bundle delivered to:
C:\Users\Katja\Documents\Claude Homebase Neo\02 Projects\NEO Trading Engine\patches\fix-startup-mode-reset\
0001-fix-main_loop-clear-inventory_truth.mode-on-fresh-se.patch
0002-test-startup-assert-inventory_truth-mode-reset-on-fr.patch
Apply Instructions (PowerShell, Katja's machine)¶
Apply order relative to FLAG-041: either order works — the two branches touch different hunks of main_loop.py (FLAG-041 at ~line 4282, this branch at ~line 578) and different test classes in the same file.
cd C:\Users\Katja\Documents\NEO GitHub\neo-2026
git fetch
git checkout main
git pull
# Defensive: clear any pre-existing branch from a prior attempt.
git branch -D fix/startup-mode-reset 2>$null
git checkout -b fix/startup-mode-reset
Get-ChildItem "C:\Users\Katja\Documents\Claude Homebase Neo\02 Projects\NEO Trading Engine\patches\fix-startup-mode-reset" -Filter "*.patch" |
Sort-Object Name |
ForEach-Object { git am $_.FullName }
# Verify
git log --oneline main..HEAD
# Expected:
# d25e300 test(startup): assert inventory_truth mode reset on fresh session (FLAG-041 follow-up)
# 010de6d fix(main_loop): clear inventory_truth.mode on fresh session startup (FLAG-041 follow-up)
# Regression
python -m pytest tests/test_halt_reason_lifecycle.py -v
# Expected: 7 passed
If fix/halt-reason-taxonomy-leak has already merged to main before this branch applies, no conflict expected — the two branches touch different files / different hunks. If this branch merges first, FLAG-041 also applies cleanly.
Deviations from Tasking¶
None. Single-file fix per spec. 3 tests per spec (fresh HALT clear, fresh DEGRADED clear, recovery preserve). No config / schema / wiring changes.
Status Before / After¶
| Field | Pre-fix (S43 on post-FLAG-041 code) | Post-fix |
|---|---|---|
| Tick 1 mode read | MODE_HALT (stale from S42) |
MODE_OK (fresh) |
_tick() return |
False immediately (mode gate) | Normal tick flow |
halt.reason at shutdown |
unexpected_halt (FLAG-041 fallback) |
(nothing halts at startup) |
| Session outcome | Insta-halt | Runs to duration or real trigger |
Out of Scope — Still Open¶
FLAG-042 (no DEGRADED recovery for non-truth guards — anchor saturation, directional drift, inventory corridor all one-way to HALT after 300s) remains deferred to Phase 7.4 per Atlas/Vesper ruling. This fix changes nothing about that path — it only prevents the stale mode from tripping the fresh startup.
Ready for Review¶
Patches in workspace. Regression green. No deviations. Standing by for Vesper's review — if clean, apply to main alongside (or after) FLAG-041 and we're cleared to run S43 with a clean startup state.
— Orion