Orion → Vesper, Atlas, Katja — FLAG-031 landed¶
When: 2026-04-17
From: Orion
Subject: Six-commit sequence green. Branch feat/flag-031-basis-model ready. Awaiting push from Katja's terminal.
Summary¶
All six commits from the approved plan shipped on feat/flag-031-basis-model, stacked on fix/flag-030-capital-overlay @ 37692b0. Nothing touched main.
132 / 132 FLAG-031 tests pass. Full suite shows the same FLAG-016 pre-existing failure pattern (73 fewer failures than baseline, 53 new passing tests — all mine). No new regressions.
Commits¶
| SHA | Subject | Tests added |
|---|---|---|
483b0c1 |
flag-031: add basis_delta_rlusd column migration |
4 |
3560aa0 |
flag-031: classify_withdrawal_basis pure function |
9 |
9731b19 |
flag-031: accept basis_commit in record_capital_event |
17 |
56b1e59 |
flag-031: get_net_basis_rlusd read-path |
9 |
d3c67fe |
flag-031: inject_capital.py with atomic RLUSD injection |
8 |
b157dc4 |
flag-031: write_synthetic_initial_basis.py one-shot migration |
8 |
All commits stacked cleanly. Will rebase onto main without conflict once FLAG-030 is merged.
Design notes vs. the plan¶
Two small adjustments worth calling out — neither changes the contract, both noted here for audit traceability.
1. Atomicity lives on StateManager, not in the script.
I considered two paths for the two-write atomicity in inject_capital.py: (a) have the script manage the transaction inline, calling low-level SQL, or (b) add StateManager.inject_rlusd_capital_atomic() that wraps both writes in one _transaction. I took (b). The transaction boundary belongs to StateManager — exposing raw SQL to the script would split ownership of the "what must be atomic" rule across two files. The script is now a thin CLI over the method. Forced-failure test injects a wrapper around the connection and confirms the rollback invariant.
2. Basis-commit with basis_delta = 0.
Per Vesper Q3: allow it. I allow it in validation, but the amount > 0 rule (FLAG-008 pattern, unchanged) implies |basis_delta| > 0 as well. In practice the smallest legitimate marker is basis_delta = 1e-9; a literal 0 row is rejected by the amount rule. Flagging it because the test case test_basis_commit_zero_delta_is_allowed uses 1e-9 rather than 0. If you want a literal-zero marker accepted, I'd have to relax amount > 0 to amount >= 0 for basis_commit only, which I can do as a one-line follow-up.
3. FLAG-008 tests updated for the extended contract.
Two FLAG-008 tests in test_inventory_manager.py needed touch-ups:
- test_rejects_invalid_event_type: FLAG-031 now owns the valid-event-type list (it added basis_commit), so the error tag moved from [FLAG-008] to [FLAG-031]. Test updated, comment explains why.
- test_accepts_valid_withdrawal: withdrawals now require a caller-supplied basis_delta_rlusd. Test passes basis_delta_rlusd=-amount to model a pure-principal withdrawal.
FLAG-008's underlying invariant ("bad rows cannot reach disk") still holds; only the ownership tag changed on one of the checks.
Key invariants confirmed in tests¶
- Atomicity: forced failure in the
INSERT INTO engine_statestep after thecapital_eventsINSERT executes → both writes roll back.capital_eventsCOUNT returns to 0,engine_statereturns to its pre-transaction value. (test_engine_state_failure_rolls_back_capital_event) basis_commitbalance-neutrality:get_capital_delta_totalexcludesbasis_commiton both assets. Synthetic row contributes tonet_basisbut not to any FLAG-030 overlay. (test_synthetic_row_does_not_affect_capital_delta_total)- NULL-safe read-path:
get_net_basis_rlusd()treats pre-migration rows with NULLbasis_delta_rlusdas 0.0 via COALESCE. (test_null_basis_delta_on_pre_migration_row_coalesces_to_zero) - Idempotency of synthetic backfill: running
write_synthetic_initial_basis.pytwice yields one row. Dry-run reports the existing-row message too. (test_second_run_is_noop_and_exits_zero) - Dry-run is truly read-only: SHA-256 of the DB file unchanged after
inject_capital.py --dry-run. (test_dry_run_does_not_mutate_db)
How to use the scripts¶
Synthetic backfill (historical):
python scripts/write_synthetic_initial_basis.py --db neo_live_stage1.db \
--amount 137.284078 --dry-run
# If the dry-run output looks right:
python scripts/write_synthetic_initial_basis.py --db neo_live_stage1.db \
--amount 137.284078
Live injection (post-synthetic):
python scripts/inject_capital.py --db neo_live_stage1.db --amount 50 --dry-run
# Verify the FLAG-032 invariants (below), then:
python scripts/inject_capital.py --db neo_live_stage1.db --amount 50 \
--source-note manual_injection_20260418 --tx-hash <hash>
Still needed before Katja injects $50¶
Per the FLAG-032 pre-injection gate — these are verification steps only, no code changes:
- Copy
neo_live_stage1.dbto a dry-run DB (e.g./tmp/neo_inject_dryrun.db). - Run
write_synthetic_initial_basis.py --amount 137.284078on the copy. Confirm one newbasis_commitrow. - Run
inject_capital.py --amount 50 --dry-runon the copy and assert: net_basis_before = 137.284078,net_basis_after = 187.284078(delta = +50)paper.pnl_starting_value_rlusd (before) = <current>,... (after) = <current + 50>- Take pre/post dashboard snapshots of NET DEPOSITS, TOTAL VALUE, TOTAL PNL, TRADING VALUE on the copy after
inject_capital.pyruns for real. Confirm the four FLAG-032 invariants: - If all four pass: inject for real on
neo_live_stage1.db. If any fail: stop, report numbers.
I can run steps 1–4 end-to-end against a DB copy on Katja's call — just need the live DB mounted or a recent snapshot path.
Push¶
No GitHub credentials in my sandbox. Branch feat/flag-031-basis-model exists in the local repo at /sessions/peaceful-admiring-allen/mnt/neo-2026. When ready:
PR target: whichever base you prefer — will rebase cleanly onto main once FLAG-030 lands there.
Files changed (vs. FLAG-030): neo_engine/state_manager.py, tests/test_state_manager.py, tests/test_inventory_manager.py (2 FLAG-008 test touch-ups), scripts/inject_capital.py, scripts/write_synthetic_initial_basis.py, scripts/__init__.py, tests/test_inject_capital.py, tests/test_write_synthetic_initial_basis.py. Total: ~1,578 insertions, 32 deletions.
— Orion