Summary¶
FLAG-037 CANCELLED_BY_ENGINE layer delivered per Atlas ruling 2026-04-21 and your pre-code approval the same day. Three commits, 5 new tests, regression clean on the touched surface. S46 phantom-fill loop is closed: the reconciler can no longer infer a fill on an order the engine explicitly cancelled.
Patch bundle¶
Location: 08 Patches/fix-reconciler-disappeared-order-conservative-ext/
0001-fix-models-state_manager-CANCELLED_BY_ENGINE-status-.patch(C5)0002-fix-reconciler-execution_engine-CANCELLED_BY_ENGINE-.patch(C6)0003-test-reconciler-CANCELLED_BY_ENGINE-guard-5-mandator.patch(C7)
Diff stat (main..HEAD):
neo_engine/ledger_reconciler.py | 66 +++-
neo_engine/main_loop.py | 38 +++
neo_engine/models.py | 18 +-
neo_engine/state_manager.py | 68 ++++
tests/test_reconciler_cancelled_by_engine.py | 466 +++++++++++++++++++++++++++
5 files changed, 654 insertions(+), 2 deletions(-)
What changed¶
C5 — Schema + enum + method (f28fd6c)¶
neo_engine/models.py:
- OrderStatus.CANCELLED_BY_ENGINE = "CANCELLED_BY_ENGINE" — British
spelling preserved per Atlas spec; distinct from existing US-spelled
CANCELED (cancellation confirmed on-ledger).
- Order.cancelled_at: Optional[str] — ISO 8601, set by the engine the
moment it decides to cancel (not the ledger-confirmation timestamp).
- Order.cancel_reason: Optional[str] — free-form context
("degraded_entry_cancel_all", "shutdown_cancel", etc.).
neo_engine/state_manager.py:
- _ensure_column(conn, "orders", "cancelled_at", "TEXT") and the same
for cancel_reason, added to initialize_database(). Idempotent; legacy
rows stay NULL.
- _row_to_order extended with the defensive
if "col" in row.keys() else None pattern for both new columns so
rows fetched from an un-migrated DB still construct cleanly.
- mark_cancelled_by_engine(order_id, *, cancelled_at=None, reason=None)
— dedicated intent-verb method (per your ruling, not folded into
update_order_status). Single transaction: writes status,
cancelled_at, cancel_reason, and refreshes updated_at.
C6 — cancel_all integration + reconciler guard (5122226)¶
neo_engine/main_loop.py — _cancel_all_live_orders:
- For each live order in the iteration, the engine now calls
self._state.mark_cancelled_by_engine(order.id, reason=context) before
self._gateway.submit_offer_cancel(order.offer_sequence). Per your ruling,
DB write first.
- Failure posture:
- DB write raises → log ERROR, skip gateway submit, continue to next
order. The unmarked order will fall through the ambiguous-disappeared
age gate on the next tick — which is the correct conservative
outcome if we failed to persist intent.
- DB write succeeded, gateway submit raises → log ERROR, continue. The
CANCELLED_BY_ENGINE status protects the order from the phantom-fill
path regardless of whether the OfferCancel tx reaches the ledger
this tick.
neo_engine/ledger_reconciler.py:
- _handle_disappeared_active_order — CANCELLED_BY_ENGINE guard added as
the first check, ahead of cancel_tx_hash and the age gate.
Atlas canonical evaluation order now reads as:
1. CANCELLED_BY_ENGINE → never phantom-fill; emit RECONCILER_SKIP_ENGINE_CANCEL
2. cancel_tx_hash set → existing cancel-race short-circuit
3. Age-threshold gate → existing FLAG-037 C2 logic
RECONCILER_SKIP_ENGINE_CANCEL — disappeared order was cancelled by
engine; skipping phantom-fill path) with order_id, offer_sequence,
cancelled_at, cancel_reason. No anomaly row is written (skip is
expected behavior, not an anomaly).
- _get_orders_for_reconciliation now includes
OrderStatus.CANCELLED_BY_ENGINE alongside ACTIVE / PARTIALLY_FILLED /
CANCEL_PENDING. Per your ruling this is defense-in-depth, not filter-
only bonus: it ensures the guard actually runs (and emits its log) on
the post-cancel tick, including after a restart.
- _reconcile_order dispatches CANCELLED_BY_ENGINE orders directly into
_handle_disappeared_active_order as the first branch, bypassing the
ACTIVE/PARTIALLY_FILLED offer lookup. The guard fires regardless of
whether the offer is still present on the ledger (e.g. OfferCancel
still in flight).
C7 — 5 mandatory tests (0e74b13)¶
tests/test_reconciler_cancelled_by_engine.py — all five Atlas-required
tests, Windows-safe fixture (StateManager.close() before TemporaryDirectory.cleanup()):
| # | Test | Key assertion |
|---|---|---|
| 1 | test_degraded_cancel_then_reconcile_skips_phantom_fill |
engine.record_full_fill not called; no anomaly row; RECONCILER_SKIP_ENGINE_CANCEL emitted exactly once |
| 2 | test_ambiguous_disappearance_routes_through_age_gate |
Age-gate path unchanged — young→phantom-fill, old→held; no skip log on either path |
| 3 | test_restart_continuity_fresh_reconciler_still_skips |
Fresh StateManager + fresh LedgerReconciler on the same DB file still skips; proves the guard rides on persistent SQL state |
| 4 | test_mixed_cancelled_and_ambiguous_processes_only_ambiguous |
One CANCELLED_BY_ENGINE + one ambiguous young; exactly one phantom-fill (ambiguous), exactly one skip log (cancelled) |
| 5 | test_truth_gate_preserved_zero_inventory_delta |
4-order batch all CANCELLED_BY_ENGINE → zero record_full_fill calls, engine signal stays RUNNING, one skip log per order |
Regression¶
Scoped to the touched surface:
$ python -m pytest tests/test_reconciler_conservative.py \
tests/test_reconciler_cancelled_by_engine.py \
tests/test_ledger_reconciler.py \
tests/test_config.py \
tests/test_state_manager.py -q
121 passed in 2.74s
Pre-existing failures (not this branch)¶
tests/test_execution_engine.py and tests/test_xrpl_gateway.py have
pre-existing TypeError: OrderSizeConfig.__init__() missing 1 required
positional argument: 'max_size_pct_of_portfolio' failures across ~100
tests. I verified (git stash no-op on untracked test file, run against
committed branch code) that these failures are present on the branch's
committed code and reproduce the same TypeError. They are config-signature
drift unrelated to FLAG-037 — flagging per your standing instruction.
Standing-instruction compliance (your Apr 14 rules)¶
- No pre-created branch during investigation. ✅ — branch
fix/reconciler-disappeared-order-conservative-extwas created only when I was ready to commit C5. Investigation was on throwaway state. - No
*.patchglob in PowerShell. The apply block below usesGet-ChildItem ... | Sort-Object Name | ForEach-Object { git am $_.FullName }. - Defensive
git branch -Dbeforegit checkout -b. Included below.
Apply instruction (PowerShell)¶
Run in Katja's VS Code terminal from C:\Users\Katja\Documents\NEO GitHub\neo-2026\:
# 1. From main, ensure clean working tree and up-to-date
git checkout main
git pull
git status # should be clean
# 2. Defensive branch delete (standing rule #3) + create fresh
git branch -D fix/reconciler-disappeared-order-conservative-ext 2>$null
git checkout -b fix/reconciler-disappeared-order-conservative-ext
# 3. Apply the three patches in order (standing rule #2 — no glob)
Get-ChildItem "C:\Users\Katja\Documents\Claude Homebase Neo\02 Projects\NEO Trading Engine\08 Patches\fix-reconciler-disappeared-order-conservative-ext" -Filter "*.patch" | Sort-Object Name | ForEach-Object { git am $_.FullName }
# 4. Verify
git log --oneline main..HEAD
# expected, top-to-bottom:
# test(reconciler): CANCELLED_BY_ENGINE guard — 5 mandatory tests (FLAG-037)
# fix(reconciler,execution_engine): CANCELLED_BY_ENGINE guard in cancel_all + reconciler skip path (FLAG-037)
# fix(models,state_manager): CANCELLED_BY_ENGINE status + cancelled_at field on orders table (FLAG-037)
python -m pytest tests/test_reconciler_conservative.py tests/test_reconciler_cancelled_by_engine.py tests/test_ledger_reconciler.py tests/test_config.py tests/test_state_manager.py -q
# expected: 121 passed
If any patch fails mid-apply, run git am --abort and flag it — do not
try to push through.
Local main drift — acknowledgement¶
Per your note: I developed this branch atop my local main, which may be a few commits behind Katja's canonical tree at apply time. The three patches only touch:
neo_engine/models.py(one enum + two Order fields, both additive)neo_engine/state_manager.py(schema migration via_ensure_column, newmark_cancelled_by_enginemethod, defensive reads in_row_to_order)neo_engine/main_loop.py(_cancel_all_live_orders— surgical edit inside the existing loop body)neo_engine/ledger_reconciler.py(new guard block, filter extension, dispatch branch — additive to existing logic)tests/test_reconciler_cancelled_by_engine.py(new file)
No conflicts expected against any of the other Phase 7.3 merges
already in main (anchor/drift/corridor guards, FLAG-041, FLAG-042,
FLAG-044, startup-mode-reset). If git am fuzz appears I'll recut
from the canonical main.
Open items that should route behind this merge¶
- FLAG-037 closure on the Open Flags board — after merge + one clean live session, I can draft the closure note for Katja.
- FLAG-040 (WAC correction) — Atlas's pre-code spec said this lands post-FLAG-037. Ready to start investigation on your say-so.
- FLAG-038 / FLAG-039 — not touched by this branch.
Ready for code review.
— orion