Skip to content

Orion Delivery — feat/anchor-idle-state (FLAG-046)

Vesper — branch complete per your APPROVED pre-code ruling and Atlas's S49 post-mortem addendum. ANCHOR_IDLE is now a first-class regime-pause state, distinct from the DEGRADED safety-pause state. Anchor saturation no longer consumes the FLAG-044 episode budget. Drift / corridor / truth events that fire while ANCHOR_IDLE still escalate to DEGRADED — but they do so through a single explicit transition point that cannot be silently bypassed. Ready for review + merge. S49 unblocked once merged.

Branch

feat/anchor-idle-state (off main), 4 commits, +1181 / −444 across 8 files.

Commits

# Hash Subject
C1 c3eba41 feat(state): FLAG-046 C1 — ANCHOR_IDLE mode + engine_state keys + stability counter
C2 502be63 feat(engine,execution): FLAG-046 C2 — ANCHOR_IDLE entry/exit methods + truth gate + startup reset
C3 7e0ba1c feat(engine,config,tests): FLAG-046 C3 — retire FLAG-044 for anchor + rewire guard to ANCHOR_IDLE
C4 664212d test(anchor_idle): FLAG-046 C4 — 5 Atlas-locked behavioral tests

Order on the branch is C1 → C2 → C3 → C4 — matches the pre-code ruling exactly. Each intermediate commit imports cleanly and its own test slice runs green (pytest --collect-only + the adjacent-FLAG suite at each step).


Atlas binding delivery requirements — one-pass confirmation

Atlas's addendum (2026-04-22) listed four non-negotiable deliverables. Each is addressed below, in order, with code anchors.

Requirement 1 — Exact state transitions

Four transitions. No others are legal. Anchor-based escalation-to-DEGRADED and direct ANCHOR_IDLE → HALT paths are structurally impossible in this branch (Requirement 4 below proves it).

Transition Trigger Entry method Episode budget? Cool-down?
ACTIVE → ANCHOR_IDLE _evaluate_anchor_saturation_guard fires: window full AND abs(mean) ≥ bias_threshold_bps (default 6.0) AND %(|x| > prevalence_threshold_bps)prevalence_pct (default 40%) over lookback_ticks (default 30) _enter_anchor_idle_mode("anchor_saturation_guard_exceeded") at main_loop.py:2287 No — does not call _bump_episode_count (explicit comment at main_loop.py:2026) No — does not call _arm_recovery_cooldown
ANCHOR_IDLE → ACTIVE _evaluate_anchor_idle_exit observes abs(mean) < recovery_exit_bias_threshold_bps (default 4.0) AND %(|x| > prevalence_threshold_bps) < recovery_exit_prevalence_pct (default 30%) for recovery_stability_ticks (default 30) consecutive ticks _exit_anchor_idle_mode() at main_loop.py:2388, called only from the exit evaluator N/A (leaving idle, no budget event) N/A
ANCHOR_IDLE → DEGRADED _evaluate_directional_drift_guard / _evaluate_inventory_corridor_guard / truth-check delta / reconciler fires, invoking _enter_degraded_mode(source=SOURCE_DRIFT \| SOURCE_CORRIDOR \| SOURCE_WALLET_TRUTH \| SOURCE_RECONCILER, reason=…) _exit_anchor_idle_on_escalation(to_source=…, to_reason=…) at main_loop.py:2139 is called from inside _enter_degraded_mode at main_loop.py:1645, before MODE_DEGRADED is written Yes — the safety source bumps its own counter via _bump_episode_count(source) at main_loop.py:1671 (only drift / corridor are in RECOVERY_CAPPED_SOURCES; wallet_truth / reconciler keep pre-FLAG-042 semantics) Yes — if the drift/corridor cap is exceeded, _enter_degraded_mode escalates directly to HALT_REASON_DEGRADED_EPISODE_LIMIT
ANCHOR_IDLE → HALT No such transition exists

Key properties:

  1. Anchor saturation does not enter DEGRADED, ever. The only mode it can write is MODE_ANCHOR_IDLE.
  2. ANCHOR_IDLE is not on a budget clock. A hostile regime that persists for the entire session keeps the engine idle for the entire session; the engine simply stops quoting and waits. Atlas ruling: "A bad regime is not, by itself, a reason to terminate the session."
  3. Escalation from ANCHOR_IDLE to DEGRADED is single-pointed: it can only happen from inside _enter_degraded_mode, and only via _exit_anchor_idle_on_escalation. This is the code-level expression of Requirement 4.

Requirement 2 — Entry/exit criteria (config field values + evaluation logic)

Entry evaluator — _evaluate_anchor_saturation_guard (main_loop.py:2188), runs at Step 8.5 of _tick.

All three conditions from AnchorSaturationGuardConfig (config.py:252) must hold on the same tick:

Field Default Role
enabled True Master kill switch. False → evaluator no-ops; engine never enters ANCHOR_IDLE from anchor saturation.
lookback_ticks 30 Rolling window size (deque(maxlen=lookback_ticks)). Condition 1: window must be fully populated before any evaluation runs.
bias_threshold_bps 6.0 Condition 2: abs(mean(window)) >= bias_threshold_bps.
prevalence_threshold_bps 5.0 Per-sample threshold for the prevalence count.
prevalence_pct 40.0 Condition 3: %(|x| > prevalence_threshold_bps for x in window) >= prevalence_pct.

On a trigger tick (all three hold), the evaluator:

  1. Calls _enter_anchor_idle_mode("anchor_saturation_guard_exceeded") — idempotent; no-op on re-fire.
  2. Sets self._anchor_guard_triggered_this_session = True — one-shot latch so subsequent entries this session do not re-emit record_circuit_breaker_event.
  3. Returns intents=[] so the Step 9 submit loop no-ops (belt-and-braces with the execution pre-trade gate at execution_engine.py:1079).

Exit evaluator — _evaluate_anchor_idle_exit (main_loop.py:2294), runs at Step 8.4 of _tick (before the entry evaluator at 8.5).

Uses the same rolling self._anchor_error_window as entry — no separate buffer, so exit cannot get ahead of or behind the data the entry evaluator is reading.

Field Default Role
recovery_enabled True Secondary kill switch. False → exit evaluator no-ops; ANCHOR_IDLE becomes one-way for the session.
recovery_exit_bias_threshold_bps 4.0 Condition 1: abs(mean(window)) < recovery_exit_bias_threshold_bps. Validated in _validate_anchor_saturation_guard (config.py:1297) to be strictly less than bias_threshold_bps — hysteresis is enforced at config load, not just by convention.
recovery_exit_prevalence_pct 30.0 Condition 2: %(|x| > prevalence_threshold_bps) < recovery_exit_prevalence_pct. Validated (config.py:1309) to be strictly less than prevalence_pct — same hysteresis guarantee.
recovery_stability_ticks 30 Number of consecutive ticks on which both conditions hold before exit fires. Tracked in self._anchor_idle_stability_ticks. Any tick that fails either condition resets the counter to 0.

On a trigger tick (both exit conditions hold for the full stability window), the evaluator calls _exit_anchor_idle_mode() (main_loop.py:2093), which:

  1. No-ops if current mode isn't MODE_ANCHOR_IDLE (defensive — matches the enter guard).
  2. Writes KEY_MODE=MODE_OK, clears KEY_ANCHOR_IDLE_SINCE + KEY_ANCHOR_IDLE_REASON.
  3. Resets self._anchor_idle_stability_ticks = 0.
  4. Resets self._anchor_guard_triggered_this_session = False — clears the one-shot latch so the entry evaluator can re-fire cleanly if the regime turns hostile again later in the same session.
  5. Emits [ANCHOR_IDLE] exit conditions stable — resuming ACTIVE WARNING log with mean / prevalence / ticks context.

Startup reset (fresh session, not recovery restart) — _startup() at main_loop.py:711-712 writes KEY_ANCHOR_IDLE_SINCE = "" and KEY_ANCHOR_IDLE_REASON = "" alongside the existing KEY_MODE = MODE_OK / degraded key resets. Recovery restarts (parent_session_id != None) deliberately preserve these so the resuming engine can finish the idle cycle it was mid-way through (matches FLAG-041 follow-up pattern).

Requirement 3 — Test coverage summary

65 tests passing across the FLAG-046 adjacent suite. The five Atlas-locked contract tests (T1–T5) live in the new test_anchor_idle_state.py file; supporting hysteresis / no-op / escalation tests live across three sibling files that were restructured in C3.

Atlas-locked contracts — tests/test_anchor_idle_state.py (NEW, 7 tests in 5 classes):

Atlas contract Test class Test method Path covered
T1 — Anchor saturation routes to ANCHOR_IDLE (no DEGRADED bookkeeping) TestAnchorSaturationRoutesToAnchorIdle test_saturation_enters_anchor_idle_without_degraded_bookkeeping _evaluate_anchor_saturation_guard_enter_anchor_idle_mode; verifies _bump_episode_count / _arm_recovery_cooldown / _enter_degraded_mode all untouched
T2 — Persistent hostile regime does not halt TestPersistentHostilityStaysIdle test_hostile_regime_does_not_halt_over_many_ticks 50-tick loop through the guard evaluator; asserts record_circuit_breaker_event.call_count == 1 (one-shot), _enter_degraded_mode / _escalate_degraded_to_halt never called
T3 — Regime normalization exits ANCHOR_IDLE TestRegimeNormalizationExitsAnchorIdle test_calm_window_advances_counter_to_stability_and_exits Exit evaluator counter advances 0→1→2→3, then _exit_anchor_idle_mode called on the stability-reached tick; drift/corridor exit paths untouched
T4 — Safety event escalates ANCHOR_IDLE → DEGRADED TestSafetyEventEscalatesAnchorIdleToDegraded test_drift_fires_while_anchor_idle_escalates_and_counts_episode Drift-sourced _enter_degraded_mode call from ANCHOR_IDLE: _exit_anchor_idle_on_escalation(to_source=SOURCE_DRIFT) called first, then _bump_episode_count(SOURCE_DRIFT), then MODE_DEGRADED written
T4 (continued) TestSafetyEventEscalatesAnchorIdleToDegraded test_corridor_fires_while_anchor_idle_also_escalates Same pattern, corridor-sourced — confirms the escalation hook is source-agnostic across all RECOVERY_CAPPED_SOURCES
T5 — No direct ANCHOR_IDLE → HALT path TestNoDirectAnchorIdleToHalt test_source_anchor_not_in_flag_044_cap_table Static invariant: SOURCE_ANCHOR not in RECOVERY_CAPPED_SOURCES; RECOVERY_CAPPED_SOURCES == (SOURCE_DRIFT, SOURCE_CORRIDOR)
T5 (continued) TestNoDirectAnchorIdleToHalt test_persistent_anchor_idle_never_reaches_halt_or_degraded 50-tick hostile loop through both guard evaluator and exit evaluator: _enter_degraded_mode / _escalate_degraded_to_halt never called, _anchor_idle_stability_ticks pins at 0, MODE_HALT never written

Supporting coverage — three sibling files restructured in C3:

  • tests/test_anchor_saturation_guard.py (16 tests, updated): entry-side guard coverage. All 16 tests updated to assert MODE_ANCHOR_IDLE / _enter_anchor_idle_mode / _anchor_guard_triggered_this_session instead of the pre-C3 DEGRADED semantics. Regression anchor for Step 8.5 wiring — persist-failure tolerance, circuit-breaker row emission, one-shot behavior.
  • tests/test_flag_042_degraded_recovery.py (15 tests, restructured): split into anchor-idle gate/hysteresis coverage (Parts A–C — 10 tests — TestAnchorIdleExitNoOpGates / TestAnchorIdleExitHysteresis / TestAnchorIdleExitPath) + drift/corridor recovery tests (Parts D–E — 5 tests — TestDriftRecovery / TestCorridorRecovery). Module docstring updated to explain the FLAG-046 split. Retired 2 tests that exercised the pre-FLAG-046 degraded_reason=anchor_saturation_guard_exceeded gate (no longer a reachable code path).
  • tests/test_flag_044_recovery_cooldown.py (27 tests, updated): TestCappedSourcesInvariant + TestBumpEpisodeCount::test_bump_anchor_source_is_unmapped_post_flag_046 assert anchor's retirement from the FLAG-044 cap table at the lowest level. Drift / corridor cool-down end-to-end tests preserved — those guards still use the capped path.

Adjacent-suite run (sandbox reproducible):

python -m pytest tests/test_anchor_idle_state.py \
  tests/test_anchor_saturation_guard.py \
  tests/test_flag_042_degraded_recovery.py \
  tests/test_flag_044_recovery_cooldown.py -q
# Expected: 65 passed, 2 subtests passed in ~0.3s

Full regression (sandbox): 378 failed / 691 passed. All 378 failures are the pre-existing OrderSizeConfig.__init__() missing 1 required positional argument: 'max_size_pct_of_portfolio', ModuleNotFoundError: No module named 'plotly', and test_xrpl_gateway signatures — same failure set as on main (verified by stash + git checkout main dry-run during branch development). Net-new passes on this branch: +7 (the new test_anchor_idle_state.py tests). No net-new failures vs. baseline.

Requirement 4 — ANCHOR_IDLE cannot silently bypass truth / drift / corridor

Two guarantees, both code-referenced, both tested:

(a) The entry path for DEGRADED is a single function.

Every DEGRADED entry — from drift, corridor, wallet_truth, or reconciler — flows through _enter_degraded_mode(reason: str, *, source: str) at main_loop.py:1613. There is no other call path that writes MODE_DEGRADED. Grep-verifiable:

$ grep -rn "set_engine_state(KEY_MODE, MODE_DEGRADED" neo_engine/
neo_engine/main_loop.py:1697:            self._state.set_engine_state(KEY_MODE, MODE_DEGRADED)

One write site. Inside that method. The single-entry-point property is preserved exactly as it was pre-FLAG-046.

(b) That one function escalates ANCHOR_IDLE explicitly, before writing MODE_DEGRADED.

_enter_degraded_mode runs this block at the top, before any FLAG-044 bookkeeping or state write (main_loop.py:1635-1648):

now = time.time()
prior_mode = self._current_truth_mode()
already_degraded = prior_mode == MODE_DEGRADED

# FLAG-046 — if the prior mode was ANCHOR_IDLE, drift / corridor /
# truth just escalated a regime pause to a safety pause. Log it
# (for the operator timeline) and clear the anchor-idle
# bookkeeping BEFORE writing MODE_DEGRADED below. Anchor itself
# is not a legal escalation target — it retired from
# RECOVERY_CAPPED_SOURCES in this commit, so `_enter_degraded_mode`
# is no longer reachable with source=SOURCE_ANCHOR.
if prior_mode == MODE_ANCHOR_IDLE:
    self._exit_anchor_idle_on_escalation(
        to_source=source, to_reason=reason
    )

_exit_anchor_idle_on_escalation (main_loop.py:2139) emits a [ANCHOR_IDLE_ESCALATED_TO_DEGRADED] WARNING log with to_source + to_reason, resets _anchor_idle_stability_ticks, and clears KEY_ANCHOR_IDLE_SINCE / KEY_ANCHOR_IDLE_REASON — but does not touch KEY_MODE or the DEGRADED keys. That stays exclusively with _enter_degraded_mode, which then runs its normal FLAG-044 bookkeeping path (_bump_episode_count + cool-down arm + cap check) and writes MODE_DEGRADED.

Operator-visible consequence: every safety escalation from an idle regime produces two log lines in order — the [ANCHOR_IDLE_ESCALATED_TO_DEGRADED] transition marker first, the [DEGRADED_ENTER] state-write second. The timeline is unambiguous.

Test coverage for this invariant is T4 (both subtests) in test_anchor_idle_state.py. The tests assert call order: _exit_anchor_idle_on_escalation(to_source=…, to_reason=…) is called before _bump_episode_count(source), which is called before the MODE_DEGRADED write. Bypass is behaviorally impossible; a future edit that writes MODE_DEGRADED from outside _enter_degraded_mode would break the invariant, but would also violate the existing single-entry-point property flagged in (a).


Architecture summary

New truth mode (C1, inventory_truth_checker.py:122): MODE_ANCHOR_IDLE = "anchor_idle". Added to VALID_MODES = (MODE_OK, MODE_ANCHOR_IDLE, MODE_DEGRADED, MODE_HALT). All downstream mode readers that previously collapsed non-OK modes into a 3-value tuple have been expanded to 4 values — notably _current_truth_mode (main_loop.py:1579), the execution pre-trade gate (execution_engine.py:1041), and the runtime truth-check branching in inventory_truth_checker.py.

New engine_state keys (C1, inventory_truth_checker.py:99-100):

  • KEY_ANCHOR_IDLE_SINCE = "inventory_truth.anchor_idle_since" — ISO-8601 timestamp written on fresh entry only, preserved on re-entry.
  • KEY_ANCHOR_IDLE_REASON = "inventory_truth.anchor_idle_reason" — short free-form token (currently always "anchor_saturation_guard_exceeded"; reserved for future regime sources).

Both keys are cleared by _exit_anchor_idle_mode on normal exit and by _exit_anchor_idle_on_escalation on safety escalation. The DEGRADED keys (KEY_DEGRADED_SINCE / KEY_DEGRADED_REASON) remain exclusively owned by the DEGRADED state machine — reviewer can grep for either key set to confirm neither state pollutes the other.

New process-cache counter (C1, main_loop.py:323): self._anchor_idle_stability_ticks: int = 0. Initialised in __init__. Advanced by _evaluate_anchor_idle_exit, zeroed by _exit_anchor_idle_mode / _exit_anchor_idle_on_escalation, and reset to 0 on any tick where either exit condition fails (hysteresis). Not persisted — stability state is recomputed from the rolling error window on every fresh session, same treatment as _anchor_error_window itself.

FLAG-044 retirement for SOURCE_ANCHOR (C3, main_loop.py:198): RECOVERY_CAPPED_SOURCES = (SOURCE_DRIFT, SOURCE_CORRIDOR) — anchor removed. Concrete consequences:

  • _bump_episode_count(SOURCE_ANCHOR) → logs a [SOURCE_UNMAPPED_POST_FLAG_046] WARNING and returns 0; cannot increment the cap (covered by test_flag_044_recovery_cooldown::TestBumpEpisodeCount::test_bump_anchor_source_is_unmapped_post_flag_046).
  • _enter_degraded_mode(source=SOURCE_ANCHOR, …) is no longer reachable from any evaluator — the only anchor-side entry is _enter_anchor_idle_mode, which writes MODE_ANCHOR_IDLE and returns.
  • Drift and corridor retain the full FLAG-044 contract: episode counter + cap + cool-down + HALT_REASON_DEGRADED_EPISODE_LIMIT escalation.

Guard rewire (C3, main_loop.py:2287): _evaluate_anchor_saturation_guard now calls _enter_anchor_idle_mode("anchor_saturation_guard_exceeded") (no source= parameter — anchor idle has no source concept). Prior call signature was _enter_degraded_mode("anchor_saturation_guard_exceeded", source=SOURCE_ANCHOR); both call-sites of that old signature are audited gone from the repo (grep -rn "source=SOURCE_ANCHOR" returns zero hits post-C3).

Exit evaluator rename (C3): _evaluate_anchor_recovery_evaluate_anchor_idle_exit. Gates on MODE_ANCHOR_IDLE instead of reading inventory_truth.degraded_reason (the latter no longer carries the anchor sentinel). Wired into Step 8.4 of _tick (main_loop.py:3825) before the entry evaluator at Step 8.5. Hysteresis + stability window semantics identical to the retired FLAG-042 evaluator — only the gate changed.

Execution pre-trade gate (C2, execution_engine.py:1079): mode == MODE_ANCHOR_IDLE or mode == MODE_DEGRADED or mode == MODE_HALT → BLOCK submit. ANCHOR_IDLE blocks quoting at the same layer as DEGRADED and HALT. No chance of a rogue submit slipping past while the guard is re-triggering on a subsequent tick.

Runtime truth-check interaction (C2, inventory_truth_checker.py): anchor-idle aware — a truth delta observed while MODE_ANCHOR_IDLE still calls _enter_degraded_mode(source=SOURCE_WALLET_TRUTH) (which in turn calls _exit_anchor_idle_on_escalation per Requirement 4(b)). ANCHOR_IDLE does not suppress truth enforcement; the truth check runs at every tick regardless of mode.


Files touched

neo_engine/config.py                     |  41 ++   (recovery_exit_* fields docstring refresh, validator hysteresis enforcement)
neo_engine/execution_engine.py           |   8 ++   (MODE_ANCHOR_IDLE added to pre-trade gate)
neo_engine/inventory_truth_checker.py    |  31 ++   (MODE_ANCHOR_IDLE constant, KEY_ANCHOR_IDLE_* keys, VALID_MODES extension)
neo_engine/main_loop.py                  | 471 ++   (_enter_anchor_idle_mode, _exit_anchor_idle_mode, _exit_anchor_idle_on_escalation,
                                                     _evaluate_anchor_idle_exit rename+gate change, guard rewire, startup reset,
                                                     RECOVERY_CAPPED_SOURCES retirement, _current_truth_mode 4-value expansion,
                                                     escalation hook in _enter_degraded_mode)
tests/test_anchor_idle_state.py          | 499 ++   (new — 7 tests mapped to Atlas T1–T5 contracts)
tests/test_anchor_saturation_guard.py    |  39 ++   (updated to MODE_ANCHOR_IDLE / _enter_anchor_idle_mode assertions)
tests/test_flag_042_degraded_recovery.py | 222 ++   (restructured — anchor-idle gate/hysteresis + drift/corridor recovery split)
tests/test_flag_044_recovery_cooldown.py | 314 ++   (updated — anchor retirement invariants, drift/corridor cap path preserved)

Net: +1181 / −444 across 8 files.


Operator impact

  • Anchor-calm sessions (mean error stays < 6 bps, prevalence < 40%): zero observable change. Guard never fires. No new log tokens emitted. Rolling window still populated and evaluated per tick, but all three entry conditions gate it off.
  • Anchor-hostile session entry (S47-class regime): guard triggers once → [CIRCUIT_BREAKER] anchor_saturation_guard_exceeded WARNING (one-shot) + [ANCHOR_IDLE_ENTER] — cancelling all live orders (regime pause, no episode budget consumed) WARNING → all live orders cancelled → Step 9 submit loop no-ops → engine sits idle emitting per-tick mode observations but no quotes. No DEGRADED entry. No episode counter bump. No cool-down arm. No HALT path. If the hostile regime persists for the entire session, the engine remains idle for the entire session, and the session halts at duration_elapsed with zero fills and zero capital drain.
  • Regime normalization mid-session: exit evaluator counter advances each tick both exit conditions hold. After recovery_stability_ticks consecutive clean ticks → [ANCHOR_IDLE] exit conditions stable — resuming ACTIVE WARNING log → MODE_OK written → engine quoting resumes on the next tick. No extra reconciliation or realignment required; the truth layer has been running throughout.
  • Safety event (drift / corridor / truth) while ANCHOR_IDLE: normal escalation path. [ANCHOR_IDLE_ESCALATED_TO_DEGRADED] WARNING (with to_source + to_reason extras) → anchor-idle bookkeeping cleared → FLAG-044 _bump_episode_count(source)MODE_DEGRADED written → drift/corridor recovery evaluators take over. If the drift/corridor episode cap is hit, HALT escalation flows through HALT_REASON_DEGRADED_EPISODE_LIMIT exactly as pre-FLAG-046.
  • Cancel-fill race during ANCHOR_IDLE entry: FLAG-047 handles this automatically. _enter_anchor_idle_mode calls _cancel_all_live_orders, which is where the tecNO_TARGET demotion to CANCEL_RACE_UNKNOWN lives. No new surface — the coverage is inherited.
  • Startup (fresh session): KEY_ANCHOR_IDLE_SINCE / KEY_ANCHOR_IDLE_REASON cleared to empty strings alongside the existing KEY_MODE = MODE_OK / degraded-key reset. Tick 1 of the new session sees an uncontaminated slate.
  • Startup (recovery restart, parent_session_id != None): anchor-idle keys deliberately preserved so the resuming engine can finish the idle cycle the crashed session was mid-way through. Matches the existing FLAG-041 follow-up recovery contract.

What this buys in an S47-class regression: the S47 session ended at degraded_episode_limit_halt after 3 anchor-sourced DEGRADED episodes consumed the full FLAG-044 budget in 191 seconds. Post-FLAG-046, the same regime would enter MODE_ANCHOR_IDLE once, sit idle until either (a) the regime normalized (exit hysteresis fires), (b) a drift/corridor/truth safety event escalated to DEGRADED (normal episode count applies), or (c) the session duration elapsed. Anchor hostility alone cannot halt the session.


Apply instructions (Windows / PowerShell)

Patches live at 02 Projects/NEO Trading Engine/08 Patches/patches/feat-anchor-idle-state/ (4 files, 0001 → 0004). From Katja's VS Code terminal:

cd C:\Users\Katja\Documents\NEO GitHub\neo-2026
git checkout main
git pull

# Defensive: clear any pre-existing branch from a prior attempt.
git branch -D feat/anchor-idle-state 2>$null

git checkout -b feat/anchor-idle-state

Get-ChildItem "C:\Users\Katja\Documents\Claude Homebase Neo\02 Projects\NEO Trading Engine\08 Patches\patches\feat-anchor-idle-state" -Filter "*.patch" |
    Sort-Object Name |
    ForEach-Object { git am $_.FullName }

# Verify — 4 commits expected
git log --oneline main..HEAD
# Expected (topmost 4):
#   664212d test(anchor_idle): FLAG-046 C4 — 5 Atlas-locked behavioral tests
#   7e0ba1c feat(engine,config,tests): FLAG-046 C3 — retire FLAG-044 for anchor + rewire guard to ANCHOR_IDLE
#   502be63 feat(engine,execution): FLAG-046 C2 — ANCHOR_IDLE entry/exit methods + truth gate + startup reset
#   c3eba41 feat(state): FLAG-046 C1 — ANCHOR_IDLE mode + engine_state keys + stability counter

# Regression — FLAG-046 adjacent suite (65 expected)
python -m pytest tests/test_anchor_idle_state.py `
  tests/test_anchor_saturation_guard.py `
  tests/test_flag_042_degraded_recovery.py `
  tests/test_flag_044_recovery_cooldown.py -q
# Expected: 65 passed, 2 subtests passed in ~0.3s

# FLAG-046 alone (7 tests, fastest sanity)
python -m pytest tests/test_anchor_idle_state.py -v
# Expected: 7 passed

# Broader adjacent FLAG suite (optional — confirms no regression in FLAG-036/037/042/044/047)
python -m pytest tests/test_anchor_idle_state.py `
  tests/test_anchor_saturation_guard.py `
  tests/test_flag_036_wallet_truth_reconciliation.py `
  tests/test_flag_042_degraded_recovery.py `
  tests/test_flag_044_recovery_cooldown.py `
  tests/test_flag_047_cancel_fill_race.py `
  tests/test_reconciler_cancelled_by_engine.py `
  tests/test_reconciler_conservative.py `
  tests/test_reconciler_anomaly_log.py -q

Merge to main (after regression green):

git checkout main
git merge feat/anchor-idle-state
# Expected: Fast-forward merge (no conflicts anticipated — FLAG-047 already merged,
# FLAG-046 built on top of that baseline)

Prerequisite: branch applies on top of main with FLAG-047 (fix/cancel-fill-race) already merged. If main is ahead of the FLAG-047 merge commit but still has CANCEL_RACE_UNKNOWN in OrderStatus and the anchor saturation guard wiring in _tick, the patches apply cleanly — all FLAG-046 hunks are additive around existing guard surface.


Post-merge

After Katja applies patches and confirms the 65-test adjacent-suite green:

  1. Mark FLAG-046 as CLOSED in [C] Open Flags.md with the commit hash and Vesper sign-off timestamp.
  2. Update CLAUDE.md FLAG-046 block: OPEN → CLOSED, add the 4-commit hashes and the one-line ruling.
  3. Run python tools/realign_inventory_to_onchain.py --config config/config_live_stage1.yaml --db neo_live_stage1.db --dry-run before S49 — expected delta ~0.00 XRP / 0.00 RLUSD (post-S48 realignment already ran per CLAUDE.md).
  4. S49 launch with --duration-seconds 7200 per FLAG-044 standing procedure.
  5. Watch the live log for [ANCHOR_IDLE_ENTER] / [ANCHOR_IDLE] exit conditions stable — resuming ACTIVE / [ANCHOR_IDLE_ESCALATED_TO_DEGRADED] tokens. The S47-class regime almost certainly triggers an [ANCHOR_IDLE_ENTER]; the question for S49 is whether regime conditions clear enough for the exit evaluator to fire vs. whether a drift/corridor event escalates to DEGRADED mid-way. Either outcome is the designed behavior; Atlas has both in the post-mortem report template.
  6. Session integrity: if S49 logs [ANCHOR_IDLE_ENTER] → stays idle for the full 2 hours → halts at duration_elapsed with zero fills and capital flat, FLAG-046 is confirmed working in production against hostile anchor regimes. If S49 logs [ANCHOR_IDLE_ENTER][ANCHOR_IDLE] exit conditions stable — resuming ACTIVE → quotes resume → fills accumulate → halts at duration_elapsed without re-entering idle, that's a Phase 7.4 precondition session. Either way, the old degraded_episode_limit_halt failure mode is off the table.

Status

C1–C4 complete. Branch is clean. No deviations from the pre-code ruling. All four Atlas binding requirements addressed in this memo with code anchors. 7 new behavioral tests pass (Atlas T1–T5 fully covered). 65 adjacent-suite tests pass. No net-new failures vs. baseline in the full regression. Awaiting your review.

— Orion