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:
- Anchor saturation does not enter DEGRADED, ever. The only mode it can write is
MODE_ANCHOR_IDLE. - 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."
- 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:
- Calls
_enter_anchor_idle_mode("anchor_saturation_guard_exceeded")— idempotent; no-op on re-fire. - Sets
self._anchor_guard_triggered_this_session = True— one-shot latch so subsequent entries this session do not re-emitrecord_circuit_breaker_event. - Returns
intents=[]so the Step 9 submit loop no-ops (belt-and-braces with the execution pre-trade gate atexecution_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:
- No-ops if current mode isn't
MODE_ANCHOR_IDLE(defensive — matches the enter guard). - Writes
KEY_MODE=MODE_OK, clearsKEY_ANCHOR_IDLE_SINCE+KEY_ANCHOR_IDLE_REASON. - Resets
self._anchor_idle_stability_ticks = 0. - 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. - Emits
[ANCHOR_IDLE] exit conditions stable — resuming ACTIVEWARNING 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 assertMODE_ANCHOR_IDLE/_enter_anchor_idle_mode/_anchor_guard_triggered_this_sessioninstead 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-046degraded_reason=anchor_saturation_guard_exceededgate (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_046assert 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 bytest_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 writesMODE_ANCHOR_IDLEand returns.- Drift and corridor retain the full FLAG-044 contract: episode counter + cap + cool-down +
HALT_REASON_DEGRADED_EPISODE_LIMITescalation.
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_exceededWARNING (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 atduration_elapsedwith zero fills and zero capital drain. - Regime normalization mid-session: exit evaluator counter advances each tick both exit conditions hold. After
recovery_stability_ticksconsecutive clean ticks →[ANCHOR_IDLE] exit conditions stable — resuming ACTIVEWARNING log →MODE_OKwritten → 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 (withto_source+to_reasonextras) → anchor-idle bookkeeping cleared → FLAG-044_bump_episode_count(source)→MODE_DEGRADEDwritten → drift/corridor recovery evaluators take over. If the drift/corridor episode cap is hit, HALT escalation flows throughHALT_REASON_DEGRADED_EPISODE_LIMITexactly as pre-FLAG-046. - Cancel-fill race during ANCHOR_IDLE entry: FLAG-047 handles this automatically.
_enter_anchor_idle_modecalls_cancel_all_live_orders, which is where thetecNO_TARGETdemotion toCANCEL_RACE_UNKNOWNlives. No new surface — the coverage is inherited. - Startup (fresh session):
KEY_ANCHOR_IDLE_SINCE/KEY_ANCHOR_IDLE_REASONcleared to empty strings alongside the existingKEY_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:
- Mark FLAG-046 as CLOSED in
[C] Open Flags.mdwith the commit hash and Vesper sign-off timestamp. - Update CLAUDE.md FLAG-046 block: OPEN → CLOSED, add the 4-commit hashes and the one-line ruling.
- Run
python tools/realign_inventory_to_onchain.py --config config/config_live_stage1.yaml --db neo_live_stage1.db --dry-runbefore S49 — expected delta ~0.00 XRP / 0.00 RLUSD (post-S48 realignment already ran per CLAUDE.md). - S49 launch with
--duration-seconds 7200per FLAG-044 standing procedure. - 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. - Session integrity: if S49 logs
[ANCHOR_IDLE_ENTER]→ stays idle for the full 2 hours → halts atduration_elapsedwith 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 atduration_elapsedwithout re-entering idle, that's a Phase 7.4 precondition session. Either way, the olddegraded_episode_limit_haltfailure 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