Branch #1 Delivery — fix/halt-reason-lifecycle¶
To: Katja CC: Vesper From: Orion Re: Branch #1 of the 7-branch audit plan — ready for your terminal
What's in this branch¶
One commit, three files touched, four passing unit tests.
neo_engine/main_loop.py — halt taxonomy + lifecycle
- New module-level constants: HALT_REASON_* tokens for every halt path (duration, engine-request, 7 risk flavors, kill switch, reconciler, replay, 4 interrupt flavors, startup failure, unhandled exception, keyboard interrupt, config_mismatch — reserved for Branch #2, unexpected_halt fallback).
- _startup() — fresh sessions (parent_session_id is None) now clear halt.reason and halt.detail. Recovery restarts preserve them (per Atlas 2E).
- _shutdown() — != gate removed. Precedence: explicit halt_reason > existing engine_state value > HALT_REASON_UNEXPECTED. Always writes.
- Every internal halt path rewritten to use taxonomy tokens + halt.detail free-form numeric context: risk halts (kill_switch, xrp_exposure, rlusd_exposure, rpc_failure, stale_ledger, gateway_unhealthy, generic), reconciler halt, replay exhausted, 3 startup-failure paths, run() entry point's KeyboardInterrupt / unhandled-exception / engine-requested-halt handlers.
run_paper_session.py — explicit halt_reason at all 5 _shutdown() call sites
- Signal handler: "interrupted_sigint" / "interrupted_sigterm" / "interrupted_sigbreak" / "interrupted_other" chosen per signal.
- Engine-requested halt: "engine_requested_halt".
- Duration elapsed: "duration_elapsed".
- Both failure paths (sqlite3.OperationalError, generic Exception): "unhandled_exception".
tests/test_halt_reason_lifecycle.py — 4 unit tests
- test_explicit_halt_reason_overrides_stale_ghost — pre-seed halt.reason with an S39-style ghost string, call _shutdown with duration_elapsed, assert the ghost is overwritten.
- test_no_explicit_reason_with_no_existing_writes_unexpected — no explicit kwarg, no existing value, assert fallback to unexpected_halt.
- test_startup_clears_halt_reason_on_fresh_session — pre-seed stale halt, call _startup(parent_session_id=None), assert cleared.
- test_startup_preserves_halt_reason_on_recovery_restart — pre-seed halt, call _startup(parent_session_id=37), assert preserved.
All four pass. Local verification also ran test_shutdown_ended_at.py — still green.
Patch location¶
02 Projects\NEO Trading Engine\orion-patches-2026-04-18-halt-reason-lifecycle\
0001-fix-halt-reason-lifecycle-explicit-halt_reason-at-al.patch
Copy-paste for your VS Code terminal (PowerShell)¶
From inside your neo_engine repo root, on a clean main:
# 1. Confirm you're on main and clean
git status
git branch --show-current # should print: main
# 2. Create the branch
git checkout -b fix/halt-reason-lifecycle
# 3. Apply the patch (adjust the workspace path if yours differs)
git am "C:\path\to\Claude Homebase Neo\02 Projects\NEO Trading Engine\orion-patches-2026-04-18-halt-reason-lifecycle\0001-fix-halt-reason-lifecycle-explicit-halt_reason-at-al.patch"
# 4. Verify the commit landed
git log --oneline -1
# expected: e38103b fix(halt-reason-lifecycle): explicit halt_reason at all call sites + taxonomy
# 5. Run the new tests + the adjacent regression suite
python -m pytest tests/test_halt_reason_lifecycle.py tests/test_shutdown_ended_at.py -v
# Expected: 8 passed
If git am rejects anything (it shouldn't — I built the patch on top of your current merged main), bail out with git am --abort and ping me before pushing anything.
Review checklist for Vesper¶
Before merge, please confirm:
- Taxonomy constants look complete — any halt path I missed?
-
_startup()clear-on-fresh block is guarded byif parent_session_id is None:(line ~378 of main_loop.py). -
_shutdown()halt.reason write is unconditional — the oldexisting_reason != _hrcheck is gone (line ~719). - The 5
run_paper_session.pycall sites all pass explicithalt_reason. - Tests exercise all four invariants from Atlas 2E.
Scope notes — read before cutting Branch #2¶
Branch #2 re-scoping needed. My pre-7.3 audit memo's Item 2 flagged summarize_paper_run._get_inventory_balance as missing the capital_events overlay, but FLAG-034 (commit 58ad919 on your main) already added that overlay. So the Item 2 root-cause analysis in the original memo is stale.
The S39 inventory anomaly (terminal showed 112.56 XRP + 176.03 RLUSD = 337 RLUSD, expected ~197 RLUSD) is still real and still unexplained — but the cause isn't the overlay. Branch #2 (fix/summarize-paper-run-capital-overlay) should be renamed or re-scoped to whatever the actual root cause turns out to be, most likely the InventoryManager.get_snapshot() behavior at shutdown (open-order reserves? a rebuild-at-shutdown miss?). I'll do the re-investigation before I cut Branch #2 and return findings before writing any code.
This does not affect Branch #1 — halt-reason lifecycle is orthogonal.
The three-source inventory invariant Atlas specified in 2A is still the correct scope expansion for Branch #2; it just needs the actual underlying bug identified first.
Next step after merge¶
Once Branch #1 is in your main, ping me and I'll begin Branch #2 investigation. Per Atlas's ruling: individual PRs, reviewed between each, no bundling.
— Orion