DEXBot2 holds an intended grid state — which orders should be on-chain, at what prices and sizes, each assigned a grid slot with an orderId. The blockchain holds the actual state — the limit orders that physically exist. Fills, partial cancellations, race conditions, and external cancellations or manual order edits cause these to diverge.
| Side | What it holds | Ground truth? |
|---|---|---|
Bot model (manager.orders) |
Grid slots with target price, size, state, orderId |
Optimistic — set before confirmation, updated after broadcast |
| Blockchain (BitShares DEX) | Limit orders with ID, price, for_sale, filled | Yes — this is reality |
Reconciliation aligns the bot's model with on-chain reality. It runs at startup when the gap is widest (bot was offline, fills happened, grid may have been regenerated).
- No atomic cancel+create on BitShares —
limit_order_cancelcancels the full order; there is no partial size reduction - A full teardown leaves the bot unable to trade during the rebuild window
The reconcile runs from recalculateGrid during startup full-resync. There is no per-attempt wall-clock race around the reconcile itself — an outer timeout would fire mid-batch and orphan in-flight broadcasts (the duplicate-accumulation death spiral). The reconcile is bounded only by the 10-minute total resync safety net, and every internal chain read follows the shared guarded-read standard.
Phase 1 does all reasoning in memory under _gridLock (fast); Phases 2 and 3 execute outside that planning lock — holding it across RPC calls would block fills, sync, and divergence checks. Both phases run inside the exclusive placement region (manager.acquirePlacement('startup-reconcile'), acquired before the chain snapshot is trusted and released after stopBroadcasting), so no other chain-mutating path can broadcast between Phase 1's plan and Phase 2's execution. Phase 2 additionally holds the broadcasting single-flight marker (which only defers fills), while the sync/apply paths it invokes follow the canonical lock hierarchy. No planning-phase grid lock spans a network operation.
The reconcile is not purely time-unbounded: besides the 10-minute total resync safety net it also waits up to 120 s for the placement region and re-reads the open-orders snapshot after acquiring it (planning from a snapshot older than the region is exactly the duplicate class). A reconcile that cannot get the region returns null and defers to the next sync cycle.
Phase 2 and 3 both respect the dryRun flag: when true, no on-chain mutations are attempted — plans are logged but not executed.
targetCount (per side, targetSell/targetBuy) is sourced from bot config (coreOrders window plus edgeOrders edge ladder) and determines how many live orders each side should maintain. The internal planOnly flag controls whether _reconcileStartupSide records plans for Phase 2 or executes inline — Phase 1 always calls with planOnly=true.
Grid generated
│
▼
┌──────────────────────────────────────────────────┐
│ PHASE 1: Planning (under _gridLock) │
│ │
│ • Sanitize phantom orders (ACTIVE/PARTIAL with │
│ orderId absent on-chain → VIRTUAL, skip); │
│ defer freshly-assigned orderIds, ghost pass │
│ • Detect suspected duplicates (exact price) → │
│ queue for Phase 2 cancel │
│ • Match unmatched chain orders to virtual slots │
│ → plan updates │
│ • Detect grid-edge lockup → plan largest-order │
│ cancel to free funds │
│ • Detect excess chain orders → plan cancels │
│ • No on-chain RPC calls inside this phase │
└──────────────────┬───────────────────────────────┘
▼
returns { plannedCreates, plannedUpdates, plannedCancels }
┌──────────────────────────────────────────────────┐
│ PHASE 2: Execution (outside _gridLock) │
│ │
│ 1. Cancellations — revalidate each plan against │
│ live ownership, geometry, and chain state │
│ 2. Updates — batch (3 retries), then sequential │
│ fallback with per-failure recovery sync │
│ 3. Creates — outside-in pairing (outermost grid │
│ slots first, BUY desc / SELL asc), batched │
│ where the DEX supports batch creates; │
│ uncertain lands adopt; created IDs tracked │
│ for Phase-3 protection │
└──────────────────┬───────────────────────────────┘
▼
┌──────────────────────────────────────────────────┐
│ PHASE 3: Fresh Count + Stale Surplus Cleanup │
│ │
│ • Re-fetch chain state after Phase 2 (guarded) │
│ • Adopt uncertain-landed creates into VIRTUAL │
│ slots │
│ • Protect Phase-2 created IDs from surplus │
│ cancel │
│ • Cancel orders exceeding per-side target not │
│ tracked by any grid slot's orderId (orphans) │
└──────────────────────────────────────────────────┘
Source: reconcileGridOrders() Phase 1 in grid_reconcile.ts.
-
Phantom order sanitization (the
isOrderPlaced()absence pass inreconcileGridOrders()): For eachisOrderPlaced()order whoseorderIdis not in the chain snapshot, reset it to VIRTUAL withskipAccountingto prevent fund inflation. One absence-decision guard makes this safe:- In-flight deferral (
LifecycleView.isInFlight(), read through the slot withreadSlotLifecycle): an order whose chain id was assigned/committed within the guard window, or whose lifecycle phase is still unresolved (unknown/broadcasting— a lost CREATE receipt, an ambiguous or unparseable read), may be live and merely missing from a lagging/truncated read — virtualizing it and re-creating would duplicate a real live order (the reconcile-timeout death-spiral root cause). It is skipped (deferred) rather than virtualized. The same predicate is the only absence guard in the sync pass-1 phantom cleanup and the fund recalibration sweep; asking a per-site subset is the bug class ORDER_LIFECYCLE.md exists to remove. - Ghost heuristic (same absence pass): an order with
size <= 0&&PARTIAL(a known filled ghost) still passes through so known fills get cleaned up. - Virtualization always uses
{ skipAccounting: true }so startup cleanup never inflatesChainFree.
- In-flight deferral (
-
Duplicate detection (the unmatched-order duplicate pass): For each unmatched chain order, find the nearest active same-side grid order. If its price equals that grid order's slot price exactly (
priceSlotEqualat the asset precision), flag it as a suspected duplicate and queue for a Phase 2 cancel (never cancelled under lock). Non-equal neighbours are only logged with nearest-same-side diagnostics and continue into per-side reconciliation. Duplicate IDs are removed from the unmatched set so they aren't also paired for updates/creates. -
Per-side reconciliation via
_reconcileStartupSide(planOnly=true)(grid_reconcile_internal.ts):- Count
matchedOnGrid(active grid orders withorderId) neededSlots = targetCount - matchedOnGrid; pick virtual slots to activate- Match sorted unmatched chain orders to virtual slots →
plannedUpdates - Detect grid-edge lock and plan a largest-order cancel
- Plan creates for remaining slots
- Plan excess cancellations (guarded by
matchedOnGrid > 0): orphans first, then matched surplus (chainCount - targetCount, edge slots last) — ONE selection feeds both the planOnly and the inline sink (theexcessPlanslist), so planning can never drift from execution; matched slots omitreleaseUntrackedFunds(their funds are tracked on the grid slot). Every plan stamps the gap geometry fromresolveGapSlots(never the raw_gapSlotsfield), which is also what the Phase-2 plan validator compares against - Fork-kept shelf orders (live non-slot-N ids below the rail, e.g.
deep-*) are never cancel candidates or reserve members — see Shelf Orders - Vacated-rail refill: each PROCEEDING update whose vacated price exactly matches (
priceSlotEqual) an empty, sized, in-rail slot of the same side queues a refill CREATE in the same plan (source startupVacatedRailRefill) — skipped updates, ghost prices (lattice moved), in-band slots, and already-desired slots never refill; refill targets require VIRTUAL state with noorderId
- Count
Returns { plannedCreates, plannedUpdates, plannedCancels, chainSellCount, chainBuyCount }.
Source: reconcileGridOrders() Phase 2 in grid_reconcile.ts.
Phase 1 releases _gridLock before network I/O. Phase 2 runs under the
broadcasting single-flight marker so a fill-driven COW rebalance cannot plan the
same slots concurrently; individual sync/apply paths follow the canonical lock
hierarchy. No planning-phase _gridLock is held across RPC calls.
Cancellations — v1.6.6 stale-plan guard: Before submitting any
plannedCancels, reconcile performs one guarded pre-cancel read and builds a
per-order signature (type, price, size) from both the Phase-1 and current
snapshots. For every plan, _startupCancelPlanStillCurrent() requires:
- the target chain order still exists with its exact Phase-1 signature;
- a matched plan still owns the same chain order in the same live grid slot;
- planned type, slot price, boundary, and gap geometry still match (both sides
read the geometry through
resolveGapSlots, so an unpublished_gapSlotsfield cannot make every plan look stale); and - an originally unmatched surplus is still not owned by any live slot.
Validation is per-plan: an earlier cancellation or unrelated order change does not invalidate an otherwise untouched plan. A truncated or failed pre-cancel read skips all cancellation submissions. A complete empty read is authoritative but likewise causes no plans to execute because no target remains present. This closes stale-cancellation replays without requiring the whole book to remain unchanged.
Fresh-placement grace (TIMING.SURPLUS_CANCEL_GRACE_MS, 15 min): surplus
and cancel-only cancellations also skip any order whose orderId was placed
inside the grace window. The timestamp is written only by the lifecycle write
funnel (_syncLifecycleFromMutation -> markPlaced) into manager._placedAt
and tested with isFreshlyPlacedOrder; the reconciler's CANCEL
actions are filtered by COWRebalanceEngine.execute, which drops the whole slot's
action group (a paired CANCEL+CREATE must not lose only its CANCEL, or the CREATE
folds back into an UPDATE and re-materializes the placement) and re-syncs the
deferred slots from master via workingGrid.syncFromMaster before commit. The
order stays ACTIVE and is re-drained once the window expires. grid-load/
grid-init contexts are excluded so a restart does not suppress the sweep.
Surplus settlement uses the same live-ownership decision. Only an order that is
still untracked, signature-unchanged, and outside the fresh-placement grace
window reaches _cancelChainOrder; a now-owned or geometry-changed order is
skipped rather than released from the stale snapshot.
Updates:
- Batch via
_executeStartupUpdateBatchwhensupportsBatchUpdateis available - Retry up to 3× (
maxBatchAttempts = 3) - On each failure:
_recoverStartupSyncFailure()re-fetches open orders from chain (guarded read) and re-syncsmanagerstate viamanager.syncFromOpenOrders(), then_refreshStartupUpdatePlans()rebuilds plans against the fresh chain state - If retries are exhausted or batch helpers are unavailable →
_executeStartupSequentialUpdateFallback()one-by-one with per-failure recovery
Creates: _executePlannedStartupCreates groups the outermost grid slots toward the center (BUY descending / SELL ascending), so the most price-critical orders are placed first. BitShares DEX batch-create operations are used where supported. Every created chain ID is captured into phase2CreatedOrderIds so Phase 3 cannot later cancel the freshly-created orders.
Source: Phase 3 in reconcileGridOrders() (guarded by if (!dryRun)).
-
Guarded fresh re-read:
readOpenOrdersGuardedre-fetches all open orders. A truncated result returnsnulland Phase 3 defers, keeping the pre-Phase-2 counts for the summary log; a capped window omits exactly the freshest Phase-2 creates. -
Adopt uncertain-landed creates: For any fresh chain order not matching a grid
orderIdand not created by a slot, it attempts targeted slot adoption — matching a VIRTUAL slot by type+price+size (within tolerance) and registering it via_applySync(..., 'createOrder')with the create-fee deduction. FullsyncFromOpenOrdersis deliberately not used here (its pass-1 virtualizes ACTIVE slots missing from the snapshot, and a lagging read right after the Phase-2 broadcast would destroy the confirmed grid). If adoption fails, the ID is still protected from surplus-cancel; the next sync loop's orphan adoption registers it. -
Stale surplus cancellation: Per side, count orders exceeding
targetCountthat no grid slot holds viaorderId(including the Phase-2 created IDs). Cancel only these untracked surplus orders, sorted by chain ID for determinism. This catches orphans lost during grid reinitialization — on-chain orders with no corresponding grid slot.
If Phase 2 partially succeeds (some cancels, some creates fail), there is no rollback. The bot proceeds with the resulting state. Because the reconcile runs at startup before the fill pipeline activates, no fills are missed during this window. Remaining mismatches are caught by the next maintenance/structural divergence cycle or the next startup reconcile.
- No per-attempt race around the reconcile itself — the 1.4.8 change removed it to avoid orphaning mid-batch broadcasts (see the
recalculateGridcall site inmodules/order/grid.ts). - The whole resync is bounded by a 10-minute total timeout (
PIPELINE_TIMING.TIMEOUT_MS * 2) and applied withPromise.raceinrecalculateGrid(). - Every internal chain read goes through
readOpenOrdersGuardedwith the 30s / 3-retry / node-failover standard. Truncated reads are always ambiguous and returnnull; empty reads are ambiguous only when a caller explicitly setsdeferEmpty: true. Phase-2 cancellation uses the default empty-aware policy, then validates each plan against the fresh per-order signature before any mutation.
_reconcileStartupSide() in grid_reconcile_internal.ts
When a brand-new grid is generated, every slot is VIRTUAL — matchedOnGrid = 0. Without a guard, every on-chain order appears "unmatched" and would be cancelled as excess:
if (matchedOnGrid > 0 || neededSlots === 0) {
cancelCount = Math.max(0, chainCount - targetCount);
}When matchedOnGrid === 0 AND scaling up (neededSlots > 0), excess cancellation is skipped — the guard covers both the fresh-grid scenario and the scale-down case (neededSlots === 0). Stale duplicates are still caught by the SUSPECTED DUPLICATE detection in Phase 1 of reconcileGridOrders.
_isGridEdgeFullyActive() in grid_reconcile_internal.ts detects when the grid boundary is fully active (all slots on-chain) before cancelling excess orders.
When all outermost orders of a side are ACTIVE with orderId, all balance is committed to the edges. Cancel the largest order among the update candidates (_cancelLargestOrder()) to free maximum funds with minimum operations, since the DEX does not expose partial-reduce in one operation. The cancelled slot gets a replacement create.
Detection sorts orders by price (BUY descending, SELL ascending) and checks that the outermost ones are all isOrderPlaced().
reconcileGridOrders() in grid_reconcile.ts
An unmatched chain order whose price equals an active same-type grid slot's price — exact slot-price equality via priceSlotEqual at the asset precision — is a suspected duplicate → queued for Phase 2 cancellation (not cancelled under lock). Non-equal neighbours are only logged with nearest-same-side diagnostics, never cancelled here. The earlier fuzzy SUSPECTED_DUPLICATE_TOLERANCE_MULTIPLIER (5× calculatePriceTolerance) was removed.
Up to 3 batch attempts (maxBatchAttempts = 3). Each failure triggers a recovery sync + plan refresh. If all plans are empty → resolved early. After 3× → sequential fallback with per-plan recovery (each individual failure triggers a recovery sync + queue refresh).
Reconcile's role in the defense-in-depth: during Phase 1, any ACTIVE/PARTIAL order whose orderId is not found on-chain is reset to VIRTUAL with skipAccounting. The in-flight deferral (LifecycleView.isInFlight()) protects in-flight broadcasts and unresolved receipts, and the ghost heuristic lets known fills pass. See developer_guide.md for the full 3-layer defense.
Live on-chain orders with non-slot-N ids below the rail (e.g. deep-* manuals kept across a fork) are shelf orders: they sit outside the grid contract and must survive every startup path untouched. Three gates enforce this (all no-ops on grids that only mint slot-N ids):
- Reserve classification/placement (
order.ts,grid_reconcile_internal.ts,manager.ts):edgeOrderIdSet, the Tier-2 live-anchor scan,_pickEdgeSlots, andpickEdgeSlotsall gate toparseSlotIndex(id) !== null, so a shelf can never count as the edge (which would mask a real edge deficit) nor be activated as an edge order it would never be counted as. The live-edge count additionally excludes window members vialiveWindowIdSet, so an edge-reaching window cannot masquerade as dedicated edge orders. - Startup excess cancels (
grid_reconcile_internal.ts): the matched-excess selection filters to slot-N ids in both planOnly and execute branches — the cheapest-first sort would otherwise wipe the shelf on the next boot. - Geometric size recalc (
grid.ts):_recalculateGridOrderSizesFromBlockchainskips non-slot-N slots in the per-slot loop, so divergence-triggered resizing never overwrites manual shelf sizes on-chain (the shelf stays in the budget denominator, so allocation math is unchanged).
Reconcile Phase 1 runs under _gridLock with no side effects on the frozen master Map. The working grid is not involved — reconcile is a startup operation that runs before the COW pipeline is active. See COPY_ON_WRITE_MASTER_PLAN.md and COW_INVARIANTS.md for COW rules.
Three of the six guarded emission sites are reconcile sites (RECONCILE-CREATE, RECONCILE-UPDATE, STARTUP-CREATE in grid_reconcile_internal.ts): every op reconcile emits is checked against the slot's genesis level and an off-grid emission is skipped, not broadcast — see GRID_PRICE_INVARIANT.md. The coupling is bidirectional: the invariant guard's persistent-rejection escalation and the deferred-hold escalation both exit through the same structural resync (debounced reload → full reset) described here, and a full reset's update-first reconcile emits the rail's genesis level, so the guard does not block its own resolution.
Reconcile derives its window from the master grid, which is only as fresh as the last applied fill. Two gates keep a top-up from being derived from geometry that has not absorbed the latest trades (both added after the 2026-11-02 buy-higher incident: 9 buy-lower violations, 23.5% of the window's BTS):
- Unsettled-fill gate (
hasUnsettledFills,utils/system.ts): while fills are queued (_incomingFillQueue), draining (_deferredFillsPending), in flight (_fillBatchInFlight) or owed a crawl (_pendingFillCrawls),maybeRunTargetedDriftReconciliationdefers the targeted reconcile entirely and_reconcileStartupSidedefers only the window top-up creates (1:1 repairs — cancelled-slot replacement, vacated-rail refill — still run). The master still counts filled slots as ACTIVE in that window, so a top-up would restore the expensive end of the rail. - Last-fill placement guard (
isLastFillPlacementBlocked,utils/system.ts): every reconcile placement (create in_createOrderFromGridand the batch create path, plus the re-pricing relocation in_prepareStartupUpdatePlan) is refused when it sits on the wrong side of the last fill or on the same grid level (within half an increment — a slot level and its execution price differ by rounding). A same-price relocation is an adoption, not a placement, and is exempt._pickVirtualSlotsToActivateapplies the same predicate so the pick budget rolls to the next level down/up instead of being spent on a level the create would refuse — the filled level is never re-stamped same-side (the "rotate to bottom BUY / counter SELL" behaviour).
A fill whose order id no longer sits on an ACTIVE/PARTIAL slot is no longer dropped: attributeUnmatchedFillToGrid (shared by SyncEngine._absorbUnmatchedFill and the orphan-credit sweep in dexbot_fill_runtime.ts) records the pivot + owed crawl from the real execution price. It resolves the slot two ways — the chain id when a virtualised slot still owns it, otherwise the genesis ladder level of the execution price (the common case, because virtualizeOrder NULLS orderId; this is what the incident actually hit). A crawl is only owed by a HOLE, and a price outside the frozen rail is refused so a shelf order cannot arm the pivot. Without this the pivot stays on a stale, higher fill and the direction rule then permits re-buys above the market. The sweep is replay-safe: the processed-fill ledger is the authority, so attribution runs only when the accounting is not a duplicate. A replay already armed the pivot/crawl on its first delivery; re-attributing it re-records an old execution price (live: a 12-minute-old replay re-recorded four crawls and moved the boundary 165 → 169).
The dark level it leaves behind is repaired too. A slot virtualised while its order stayed live on chain reads as not active (_countActiveOnGrid needs ACTIVE/PARTIAL) while its order still counts in chainCount, so the side reports a shortfall it can never repair — and the planner sees a free level that already holds an order. Three pieces close that:
reviveVirtualSlotFromChainre-materialises the slot in place (master-only, no broadcast) when the chain order is still live and rests at that slot's level. Wired into sync pass 1 (sync-pass1-revive) and reconcile Phase 1 (startup-revive). A rotation alias (chain order at another level) is never resurrected.- Sync pass 1 also clears a stale id on a non-placed slot (
sync-clear-stale-id) without booking a fill, so the level becomes a normal hole again. validateCreateTargetSlotstreats any slot that still owns anorderId(any state) asslot_occupied(hard: the batch aborts), and a slot with a possibly-landed CREATE as the softerslot_create_uncertain(only that CREATE is withheld — an unknown outcome is not proof of a live order).reconcileGridno longer treats a virtualised slot that owns an id as a hole. Together with the stale-id cleanup this blocks a duplicate CREATE at a price that already holds an order without stranding a level whose id was merely stale.
The spread-correction planner judges candidates with the same predicate as the placement guards (direction plus same grid level), so it no longer plans a create the broadcast guard will refuse; an unplaceable side reports the last-fill-blocked stall instead of burning a cycle. The COW build loop reaches the same verdict without a band term: it snaps the fill reference onto the ladder first (resolveOnGridPivot, tolerance one increment) and then applies the direction rule, so a target on the just-filled level equals the snapped pivot and is refused. A reference too far off-ladder to snap is judged raw (fail-open with a warning) rather than rewritten onto the grid — which is why the raw placement predicate is deliberately not wired into that lane (pinned by tests/test_last_fill_guard.ts PIVOT-004).
Every chain read feeding an absence/surplus decision goes through readOpenOrdersGuarded. A truncated snapshot is always unreadable. An empty snapshot is also unreadable at callers that set deferEmpty: true; the Phase-2 cancellation pre-read intentionally accepts a complete empty result, executes no plan, and leaves all remaining state for the normal reconcile paths:
_recoverSyncFromChain()— plus its recovery sites in_createOrderFromGrid/_cancelChainOrder— defers on empty/truncated reads (deferEmpty: true). A pass-1 phantom cleanup would otherwise virtualize live slots from a partial window._adoptPossiblyLandedCreate()defers to an uncertain outcome on truncated reads, and the startup group batch uncertain verification follows the same rule.- Phase 3 final refresh skips adoption/surplus-cancel on a truncated read, keeping the pre-phase-2 counts for the summary log.
- Adoption paths (
_adoptPossiblyLandedCreate, grouping path, reconcile adoption loop) apply the create-fee deduction via_applySyncfor accounting parity.
The underlying rule is INV-BROADCAST-004: a capped get_full_accounts window omits the freshest orders (fresh creates sort last), so absence can never be authoritative on a truncated read.
manager.ts lock declaration — canonical reference in developer_guide.md.
Level 0: _fillProcessingLock Level 0.5: placement region
Level 1: _divergenceLock Level 2: _syncLock
Level 3: _gridLock Level 4: _fundLock
Acquire in ascending level order only. AsyncLock is re-entrant (nested acquire() run directly, not queued); the placement region (modules/order/placement_lock.ts) is NOT re-entrant — it is a FIFO lease, and nested work must reuse the holder's lease (verified with manager.isPlacementLeaseCurrent()).
Before 1.4.6, _syncLock was Level 3 and _gridLock was Level 2, causing ABBA deadlock when reconcile needed _gridLock (old Level 2) while holding _syncLock (old Level 3). The workaround flag gridLockAlreadyHeld patched 8 call sites.
Commit 705cde9c fixed it: swapped levels (_syncLock → 2, _gridLock → 3), eliminated the flag, and restructured Phase 1 to be purely in-memory so no RPC calls run under _gridLock (developer_guide.md §Startup Sequence).
Commit e64db685 replaced 6 single-value boolean state fields with refcounts/stacks to prevent premature resume from re-entrant nested acquisitions.
| Constant | Value | File | Role |
|---|---|---|---|
maxBatchAttempts |
3 |
grid_reconcile.ts |
Update-batch retry limit |
PRICE_TOLERANCE_MAX_PERCENT |
0.01 (1%) |
constants.ts::GRID_LIMITS |
Cap on price tolerance |
PRICE_TOLERANCE_MIN_ABSOLUTE |
0.0001 |
constants.ts::GRID_LIMITS |
Floor for price tolerance |
PIPELINE_TIMING.TIMEOUT_MS |
300000 (5min) |
constants.ts::PIPELINE_TIMING |
Base pipeline timing; resync uses 2× (10 min) |
| Test File | Coverage |
|---|---|
tests/test_grid_reconcile.ts |
8: grid detection, largest-order cancel, ordering |
tests/test_grid_reconcile_regressions.ts |
16 startup scenarios: fund/fund-lock behavior, verified-after-failure, slot/skip cases, matchedOnGrid, Phase-3 surplus, phantom deferral, duplicate ownership and settlement, persistent-duplicate escalation, changed-plan rejection, per-plan independence, and truncated/failed pre-cancel reads |
tests/test_resync_duplicate_race.ts |
Phase 3 duplicate race |
tests/test_resync_balance_fix.ts |
Fund reuse during Phase 3 |
tests/test_resync_invariants.ts |
Fund invariant suppression during transient resync |
tests/test_uncertain_broadcast.ts |
Startup uncertain-create adoption + truncated-read deferral (UNC-013e–g, 1.4.8) |
tests/test_race_condition_fixes_batch1.ts |
ABBA deadlock (RC-1B) |
tests/test_async_lock_force_release.ts |
Nested multi-lock re-entrancy |
tests/test_targeted_drift_reconcile.ts |
Active-order shortfall triggers sync |
tests/test_edge_orders.ts |
Reserve startup coverage: fully-placed matched-surplus cancels, orphan+matched ordering, at-target silence, plan/execute parity, shelf-order survival, reserve-deficit trigger (buy reserves 0/2) with filled/disabled/empty-budget silence, window-exclusion counting |
tests/test_grid_fill_discipline.ts |
Placement predicate (direction + same grid level), picker fall-through on both sides, create refusal before broadcast, window top-up deferral while fills are unsettled, slot revive (ACTIVE/PARTIAL + all refusals), CREATE occupancy gate, dark-level hole gate, spread planner/guard parity, orphan-sweep attribution |
tests/test_last_fill_pivot_rearm.ts + tests/test_last_fill_pivot_persistence.ts |
Re-arm on a new generation, timestamp provenance (the row that supplied the price also supplies the clock), and the pivot lifetime enforced at read time as well as at restore (expired rows neither arm nor re-arm; unknown age never expires) |
tests/test_fill_attribution_virtual_slot.ts |
Fill absorbed into a virtualised slot: pivot + owed crawl from the execution price (chain-id and ladder identity), unknown-order/no-stacking/dryRun cases, occupied-level and out-of-grid refusals, incident ladder refusals, hasUnsettledFills states |
tests/repro_phantom_orders.ts |
Phantom order prevention |
| File | Role |
|---|---|
modules/order/grid_reconcile.ts |
Public API + 3-phase orchestrator, including per-plan Phase-2 cancellation validation |
modules/order/grid_reconcile_internal.ts |
Internal helpers — _reconcileStartupSide, grid detection, recovery, uncertainty |
modules/order/manager.ts |
Lock hierarchy definition, _applyOrderUpdate, phantom guard, reconcileGrid entry, COW integration |
modules/order/async_lock.ts |
AsyncLock engine with ALS re-entrancy |
modules/order/sync_engine.ts |
Blockchain sync pipeline |
modules/order/grid.ts |
Grid creation, recalculateGrid (full resync) calls reconcile |
modules/chain_orders.ts |
readOpenOrdersGuarded / guarded read infrastructure |
modules/constants.ts |
Timing, tolerance, retry constants |