Vesper Ruling — Inventory Corridor Guard Q1–Q5¶
To: Orion (he/him)
From: Vesper (she/her)
CC: Katja (Captain), Atlas (he/him)
Date: 2026-04-19
Re: Pre-code rulings for feat/inventory-corridor-guard — green light given
Q1 — Inventory state: CONFIRMED¶
inventory local at main_loop.py:2292 (post-reconciliation get_snapshot(mid_price)) is the correct source. inventory.xrp_pct, inventory.xrp_balance, inventory.rlusd_balance, inventory.total_value_in_rlusd are all pre-computed — no additional arithmetic in the guard. Same source as pre-trade gate. Confirmed.
Q2 — Mid price availability: CONFIRMED¶
mid_price local at main_loop.py:2291 (snapshot.mid_price or 0.0) is the correct value. Same mid used for quote construction and inventory snapshot. Already in scope at Step 8.5c. Confirmed.
Q3 — Insertion point (Step 8.5c): CONFIRMED¶
After drift guard (8.5b), before no-intents log and Step 9. Ordering (anchor → drift → composition) matches Atlas's priority sequencing. _enter_degraded_mode idempotent, independent one-shot flag, intents threaded through — no ordering conflict. Confirmed.
Q4 — Mid price None handling: OPTION A APPROVED¶
Fail-open on mid_price None/0 for the composition check. Reasoning confirmed:
- Guard lives inside
if snapshot.is_valid():which guaranteesmid_price > 0in practice — fail-open is defensive, not routine. - Composition is undefined at
mid_price == 0—xrp_pctwould be 0, producing a guaranteed false trigger on min_xrp_pct. Fail-open is the only sensible choice. - Mirrors anchor guard (missing data → skip tick, don't increment, don't reset counter).
- Atlas invariant held by the RLUSD floor, which fires unconditionally — no dependency on mid price.
Implementation note: On a missing-data tick, _corridor_ticks_outside is neither incremented nor reset. Treat it as a no-observation tick. The lookback counter resumes from its current value on the next valid tick.
Q5 — Existing inventory bounds: CONFIRMED¶
No double-fire risk. The existing max_xrp_exposure / max_rlusd_exposure HALT gates are absolute caps at a different scale (1000.0 / 150.0 RLUSD). The strategy-layer intent advisories are weaker and in a different domain. The corridor guard occupies a previously-empty slot. Confirmed.
circuit_breaker_inventory_drift_pct / circuit_breaker_inventory_drift_window_seconds finding: Noted. Confirmed out of scope for this branch. Flag for a future config-wiring-pass — these appear to be dead parameters that should either be wired to an evaluator or removed.
Config placement: CORRECTED — use strategy:, not top-level Config¶
Orion's proposal places inventory_corridor_guard as a top-level Config field. This is incorrect for this guard.
The correct placement is under StrategyConfig — the same as anchor_saturation_guard and directional_drift_guard. All three are strategy execution guards that govern trading behavior. The reconciler_conservative top-level placement was a special case approved specifically because the reconciler is infrastructure, not strategy. That precedent does not apply here.
Required:
- InventoryCorridorGuardConfig field on StrategyConfig, not on Config
- YAML under strategy: in all three config files
- Main loop accesses via config.strategy.inventory_corridor_guard
This matches anchor/drift guard placement exactly.
One-shot flag reset: CONFIRMED PATTERN¶
Anchor and drift guards' one-shot flags (_anchor_guard_triggered_this_session, _drift_guard_triggered_this_session) reset at session boundary in _start_new_session() (or equivalent). Mirror that pattern exactly for _corridor_guard_triggered_this_session and _corridor_ticks_outside.
Green light¶
All five questions resolved. Config placement correction issued. No other blockers. Build as planned.
Branch off main. Deliver in standard format. Vesper reviews before merge.
— Vesper