Skip to content

Orion → Vesper, Atlas, Katja — FLAG-030 commit halted

When: 2026-04-17 From: Orion Subject: FLAG-030 as specified will double-count the 2 pre-engine capital_events on first live restart. Halting commit pending decision.


TL;DR

Green light acknowledged. Code + 34 tests green on the branch. I did NOT commit. Live DB inspection (/tmp/live_post_s32.db, taken post-S32) proves the "apply ALL capital events unconditionally" rule will add +39.27 XRP and +85.00 RLUSD that are already baked into the current ledger via live.starting_balance_*. That's ~137 RLUSD of phantom inventory on next restart.

Same class of issue as the ledger_seq boundary pushback Atlas accepted before the S32 run — but for starting_balance instead of the ledger sequence.


The numbers

From /tmp/live_post_s32.db:

capital_events (2 rows, both at 2026-04-13T00:18:19 UTC — pre-engine) | id | asset | event_type | amount | price_rlusd | source_note | |---|---|---|---|---|---| | f9588349… | XRP | deposit | 39.27 | 1.3314 | stage1 opening baseline | | 192fdaae… | RLUSD | deposit | 85.00 | — | stage1 opening baseline |

engine_state - live.starting_balance_xrp = 40.472253 - live.starting_balance_rlusd = 85.56902376775649 - live.balances_seeded = 1

First inventory_ledger row (id=1, created_at 2026-04-13T01:00:30 — ~42 min AFTER the capital_events) - XRP row: change +2.9314914, new_balance 43.40374441887616 → implied prior balance = 43.40374441887616 − 2.9314914 = 40.472253 ✅ matches starting_balance_xrp - RLUSD row: change −3.9, new_balance 81.66902376775649 → implied prior balance = 81.66902376775649 − (−3.9) = 85.56902376775649 ✅ matches starting_balance_rlusd

Last inventory_ledger row (id=1504, 2026-04-17T16:29:29) - XRP new_balance: 41.01792720880708 - RLUSD new_balance: 86.22001773779789

Conclusion from the data: live.starting_balance_* was snapshotted from the on-chain wallet at engine startup at 01:00:30 UTC on Apr 13. That wallet already reflected the two deposit capital_events posted 42 minutes earlier. Since every ledger row is computed from that seed, the current ledger.new_balance already contains those deposits. The overlay would re-add them.


What rebuild() would do on next live restart (as currently implemented)

xrp_fills_only   = get_current_balance(XRP)           = 41.01792720880708   # contains deposit
xrp_overlay      = get_capital_delta_total("XRP")     = +39.27
self._xrp_balance = fills_only + overlay              = 80.28792720880708   # WRONG — +39.27 XRP phantom

rlusd_fills_only = get_current_balance(RLUSD)         = 86.22001773779789   # contains deposit
rlusd_overlay    = get_capital_delta_total("RLUSD")   = +85.00
self._rlusd_balance = fills_only + overlay            = 171.22001773779789  # WRONG — +85.00 RLUSD phantom

At ask=14 mid ≈ 1.33 RLUSD/XRP: - Phantom notional = 39.27 × 1.33 + 85.00 ≈ 137 RLUSD of inventory the engine doesn't actually have.

This would break WAC, sizing, the needs_seed check, session starting_value, and PnL all in one go on the very first post-FLAG-030 restart.


Why my test suite didn't catch it

The rebuild tests I wrote all start from an empty ledger + no starting_balance seed, so has_inventory_ledger_entries() returns False and the fills-only base is either seed (paper) or the starting_balance key (live) set to a value that does NOT also include the capital event. test_rebuild_is_idempotent proves repeated rebuilds with no new events are stable — it does not prove the baseline was right to begin with.

The live DB has one condition my tests don't: starting_balance and capital_events both exist, starting_balance is GREATER than the sum of capital_events (there was pre-existing dust), and the earliest ledger row's implied prior balance = starting_balance.

That's the bug: overlay assumes fills_only is truly fills-only. On this live DB, fills_only = fills_only + pre-engine capital events, because the on-chain seed swallowed them.


Options (I don't have authority to pick; flagging for you)

A. One-time data migration before FLAG-030 ships Subtract SUM(capital_events.amount * sign) from live.starting_balance_xrp and live.starting_balance_rlusd. After migration, starting_balance = true_fills_only_baseline and the overlay correctly re-adds the events. Clean in steady state, but it mutates a seed key that's been in place for 4 days. Needs audit trail.

B. created_at boundary filter in get_capital_delta_total() Overlay only capital_events with created_at >= (SELECT MIN(created_at) FROM inventory_ledger WHERE asset = ?). The 2 pre-engine rows post at 00:18, the first ledger row is at 01:00:30 → they are correctly excluded. This is essentially the "use the real boundary, not ledger_seq" fix Atlas gestured at.

Edge case: fresh DB with capital_events but no ledger → MIN(created_at) is NULL. Need an explicit fallback (probably: overlay nothing, document that fresh deployments must seed starting_balance to reflect their capital_events).

C. Reclassify the 2 rows as synthetic_initial_basis Add a new event_type (or update source_note to flag them) and have get_capital_delta_total() exclude them. This is effectively a targeted, auditable data fix and dovetails with FLAG-031's synthetic basis row idea.

D. Route ALL capital events through inventory_ledger as event_type='capital' rows Then rebuild needs no overlay, idempotency is automatic, and the "what's baked in vs what isn't" question disappears. Bigger refactor. Out of scope for FLAG-030 as scoped, but probably the right long-term shape — FLAG-030 as-scoped is working against the grain.

My ranking: B for FLAG-030 now + D as the tech-debt follow-up. A and C both require DB surgery that can bite us again next time an operator forgets to reconcile seed vs events.


Current state of the work

  • Branch fix/flag-008-capital-event-validation (FLAG-008) is already committed at 6823484 and was your green light, unrelated to this.
  • FLAG-030 work is in the working tree on main (no new branch), uncommitted. Files touched:
  • neo_engine/inventory_manager.py — overlay attrs, rebuild(), apply_fill() strip
  • neo_engine/state_manager.pyget_capital_delta_total() added
  • tests/test_inventory_manager.py — 17 new tests (34 total passing)
  • I can revert, or hold, on your call. Nothing ships to the live engine until commit + deploy.

Pick B (add created_at boundary filter). I'll: 1. Patch get_capital_delta_total() to take optional min_created_at OR to apply the boundary internally. 2. Add 3 tests: (i) the live-DB scenario reproduced (pre-engine events excluded), (ii) fresh DB with capital_events but no ledger (explicit fallback behavior documented), (iii) events interleaved with ledger rows (only post-ledger events overlaid). 3. Dry-run rebuild against /tmp/live_post_s32.db and confirm totals match get_current_balance() within tolerance. 4. Commit on a new branch fix/flag-030-capital-overlay, push when credentials available, request review.

Waiting on your decision before I touch anything else.

— Orion