dexbot 1.6.5 → 1.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CHANGELOG.md +36 -4
  2. package/README.md +104 -35
  3. package/analysis/README.md +11 -16
  4. package/analysis/account_resolver.ts +1 -11
  5. package/analysis/bot_usage/kibana_bot_queries.ts +1 -1
  6. package/analysis/chain_pool.ts +1 -1
  7. package/analysis/fills_source.ts +0 -6
  8. package/analysis/grid_correction_check.ts +20 -26
  9. package/analysis/resolve_source.ts +1 -1
  10. package/analysis/trade_profitability.ts +2 -5
  11. package/analysis/trend_detection/README.md +1 -3
  12. package/analysis/trend_detection/package.json +1 -1
  13. package/claw/package.json +1 -1
  14. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  15. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  16. package/claw/tests/test_claw_mcp_transport.ts +2 -2
  17. package/dist/analysis/account_resolver.d.ts +1 -4
  18. package/dist/analysis/account_resolver.d.ts.map +1 -1
  19. package/dist/analysis/account_resolver.js +1 -1
  20. package/dist/analysis/account_resolver.js.map +1 -1
  21. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +1 -2
  22. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
  23. package/dist/analysis/bot_usage/kibana_bot_queries.js +1 -1
  24. package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
  25. package/dist/analysis/chain_pool.d.ts +1 -3
  26. package/dist/analysis/chain_pool.d.ts.map +1 -1
  27. package/dist/analysis/chain_pool.js +1 -1
  28. package/dist/analysis/chain_pool.js.map +1 -1
  29. package/dist/analysis/fills_source.d.ts +1 -51
  30. package/dist/analysis/fills_source.d.ts.map +1 -1
  31. package/dist/analysis/fills_source.js +1 -1
  32. package/dist/analysis/fills_source.js.map +1 -1
  33. package/dist/analysis/grid_correction_check.d.ts +2 -3
  34. package/dist/analysis/grid_correction_check.d.ts.map +1 -1
  35. package/dist/analysis/grid_correction_check.js +18 -26
  36. package/dist/analysis/grid_correction_check.js.map +1 -1
  37. package/dist/analysis/resolve_source.d.ts +1 -1
  38. package/dist/analysis/resolve_source.d.ts.map +1 -1
  39. package/dist/analysis/trade_profitability.d.ts.map +1 -1
  40. package/dist/analysis/trade_profitability.js +2 -7
  41. package/dist/analysis/trade_profitability.js.map +1 -1
  42. package/dist/dexbot.d.ts.map +1 -1
  43. package/dist/dexbot.js +60 -74
  44. package/dist/dexbot.js.map +1 -1
  45. package/dist/market_adapter/core/kibana_candles.d.ts +1 -15
  46. package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -1
  47. package/dist/market_adapter/core/kibana_candles.js +1 -1
  48. package/dist/market_adapter/core/kibana_candles.js.map +1 -1
  49. package/dist/market_adapter/inputs/fetch_lp_data.d.ts +1 -35
  50. package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
  51. package/dist/market_adapter/inputs/fetch_lp_data.js +10 -54
  52. package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
  53. package/dist/market_adapter/inputs/kibana_feed_source.d.ts +1 -8
  54. package/dist/market_adapter/inputs/kibana_feed_source.d.ts.map +1 -1
  55. package/dist/market_adapter/inputs/kibana_feed_source.js +1 -1
  56. package/dist/market_adapter/inputs/kibana_feed_source.js.map +1 -1
  57. package/dist/market_adapter/inputs/window_cache.d.ts +2 -63
  58. package/dist/market_adapter/inputs/window_cache.d.ts.map +1 -1
  59. package/dist/market_adapter/inputs/window_cache.js +22 -102
  60. package/dist/market_adapter/inputs/window_cache.js.map +1 -1
  61. package/dist/modules/account_bots.d.ts +16 -2
  62. package/dist/modules/account_bots.d.ts.map +1 -1
  63. package/dist/modules/account_bots.js +107 -44
  64. package/dist/modules/account_bots.js.map +1 -1
  65. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -1
  66. package/dist/modules/bitshares-native/crypto/ecc.browser.js +0 -31
  67. package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -1
  68. package/dist/modules/credit_runtime.d.ts +0 -2
  69. package/dist/modules/credit_runtime.d.ts.map +1 -1
  70. package/dist/modules/credit_runtime.js +1 -10
  71. package/dist/modules/credit_runtime.js.map +1 -1
  72. package/dist/modules/dexbot_class.d.ts +13 -10
  73. package/dist/modules/dexbot_class.d.ts.map +1 -1
  74. package/dist/modules/dexbot_class.js +0 -9
  75. package/dist/modules/dexbot_class.js.map +1 -1
  76. package/dist/modules/dexbot_cow_runtime.d.ts +13 -0
  77. package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
  78. package/dist/modules/dexbot_cow_runtime.js +13 -1
  79. package/dist/modules/dexbot_cow_runtime.js.map +1 -1
  80. package/dist/modules/order/accounting.d.ts +4 -18
  81. package/dist/modules/order/accounting.d.ts.map +1 -1
  82. package/dist/modules/order/accounting.js +4 -34
  83. package/dist/modules/order/accounting.js.map +1 -1
  84. package/dist/modules/order/export.d.ts +9 -12
  85. package/dist/modules/order/export.d.ts.map +1 -1
  86. package/dist/modules/order/export.js +9 -12
  87. package/dist/modules/order/export.js.map +1 -1
  88. package/dist/modules/order/grid.d.ts +35 -40
  89. package/dist/modules/order/grid.d.ts.map +1 -1
  90. package/dist/modules/order/grid.js +43 -43
  91. package/dist/modules/order/grid.js.map +1 -1
  92. package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
  93. package/dist/modules/order/grid_reconcile.js +111 -20
  94. package/dist/modules/order/grid_reconcile.js.map +1 -1
  95. package/dist/modules/order/grid_reconcile_internal.d.ts +5 -3
  96. package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
  97. package/dist/modules/order/grid_reconcile_internal.js +18 -3
  98. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  99. package/dist/modules/order/manager.d.ts +0 -8
  100. package/dist/modules/order/manager.d.ts.map +1 -1
  101. package/dist/modules/order/manager.js +0 -10
  102. package/dist/modules/order/manager.js.map +1 -1
  103. package/dist/modules/order/utils/order.d.ts +22 -7
  104. package/dist/modules/order/utils/order.d.ts.map +1 -1
  105. package/dist/modules/order/utils/order.js +103 -21
  106. package/dist/modules/order/utils/order.js.map +1 -1
  107. package/dist/modules/order/utils/withPoolRef.d.ts +12 -0
  108. package/dist/modules/order/utils/withPoolRef.d.ts.map +1 -1
  109. package/dist/modules/order/utils/withPoolRef.js +17 -0
  110. package/dist/modules/order/utils/withPoolRef.js.map +1 -1
  111. package/dist/modules/order/working_grid.d.ts +8 -13
  112. package/dist/modules/order/working_grid.d.ts.map +1 -1
  113. package/dist/modules/order/working_grid.js +8 -29
  114. package/dist/modules/order/working_grid.js.map +1 -1
  115. package/dist/scripts/sync-version.js +0 -22
  116. package/dist/scripts/sync-version.js.map +1 -1
  117. package/dist/scripts/update_dist_freshness.d.ts +12 -0
  118. package/dist/scripts/update_dist_freshness.d.ts.map +1 -1
  119. package/dist/scripts/update_dist_freshness.js +30 -1
  120. package/dist/scripts/update_dist_freshness.js.map +1 -1
  121. package/docs/BITSHARES_ONBOARDING.md +9 -6
  122. package/docs/COW_INVARIANTS.md +1 -0
  123. package/docs/DEXBOT_COMPARISON.md +11 -11
  124. package/docs/EVOLUTION.md +8 -7
  125. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
  126. package/docs/GRID_PRICE_INVARIANT.md +518 -0
  127. package/docs/GRID_RECALCULATION.md +1 -1
  128. package/docs/GRID_RECONCILE.md +60 -39
  129. package/docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md +556 -0
  130. package/docs/README.md +3 -3
  131. package/docs/WORKFLOW.md +12 -6
  132. package/docs/architecture.md +40 -38
  133. package/docs/developer_guide.md +4 -8
  134. package/market_adapter/README.md +16 -12
  135. package/modules/README.md +1 -1
  136. package/package.json +7 -3
  137. package/scripts/README.md +0 -9
  138. package/scripts/reset-settings.sh +1 -1
  139. package/analysis/ama_fitting/package.json +0 -12
  140. package/analysis/analyze_derivatives.ts +0 -320
  141. package/analysis/derivative_chart_generator.ts +0 -874
  142. package/analysis/trend_detection/SIGNAL_DOCUMENTATION.md +0 -403
  143. package/analysis/trend_detection/derivative_analyzer.ts +0 -1059
  144. package/dist/analysis/analyze_derivatives.d.ts +0 -68
  145. package/dist/analysis/analyze_derivatives.d.ts.map +0 -1
  146. package/dist/analysis/analyze_derivatives.js +0 -287
  147. package/dist/analysis/analyze_derivatives.js.map +0 -1
  148. package/dist/analysis/derivative_chart_generator.d.ts +0 -12
  149. package/dist/analysis/derivative_chart_generator.d.ts.map +0 -1
  150. package/dist/analysis/derivative_chart_generator.js +0 -891
  151. package/dist/analysis/derivative_chart_generator.js.map +0 -1
  152. package/dist/analysis/trend_detection/derivative_analyzer.d.ts +0 -248
  153. package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +0 -1
  154. package/dist/analysis/trend_detection/derivative_analyzer.js +0 -910
  155. package/dist/analysis/trend_detection/derivative_analyzer.js.map +0 -1
@@ -22,7 +22,7 @@ Reconciliation aligns the bot's model with on-chain reality. It runs at startup
22
22
 
23
23
  The reconcile runs from [`recalculateGrid`](../modules/order/grid.ts) 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.
24
24
 
25
- Phase 1 does all reasoning in memory under `_gridLock` (fast); Phases 2 and 3 execute outside the lock — holding a lock across RPC calls would block fills, sync, and divergence checks for hundreds of milliseconds each. All execution operations in Phases 2–3 re-acquire `_gridLock` individually (via `synchronizeWithChain` / per-op guards) so the lock is held briefly per operation, never for an entire phase.
25
+ 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. Phase 2 holds the broadcasting single-flight marker, while the sync/apply paths it invokes follow the canonical lock hierarchy. No planning-phase grid lock spans a network operation.
26
26
 
27
27
  Phase 2 and 3 both respect the `dryRun` flag: when true, no on-chain mutations are attempted — plans are logged but not executed.
28
28
 
@@ -52,8 +52,8 @@ Phase 2 and 3 both respect the `dryRun` flag: when true, no on-chain mutations a
52
52
  ┌──────────────────────────────────────────────────┐
53
53
  │ PHASE 2: Execution (outside _gridLock) │
54
54
  │ │
55
- │ 1. Cancellations — duplicates, edge releases, │
56
- │ excess chain orders │
55
+ │ 1. Cancellations — revalidate each plan against │
56
+ │ live ownership, geometry, and chain state │
57
57
  │ 2. Updates — batch (3 retries), then sequential │
58
58
  │ fallback with per-failure recovery sync │
59
59
  │ 3. Creates — outside-in pairing (outermost grid │
@@ -78,7 +78,7 @@ Phase 2 and 3 both respect the `dryRun` flag: when true, no on-chain mutations a
78
78
 
79
79
  ### Phase 1 — Pure Planning Under `_gridLock`
80
80
 
81
- **`grid_reconcile.ts:208-375`**
81
+ **Source:** `reconcileGridOrders()` Phase 1 in `grid_reconcile.ts`.
82
82
 
83
83
  1. **Phantom order sanitization** (lines 216-245): For each `isOrderPlaced()` order whose `orderId` is not in the chain snapshot, reset it to VIRTUAL with `skipAccounting` to prevent fund inflation. Two absence-decision guards make this safe:
84
84
  - **Freshly-assigned deferral** (lines 226-235): an `orderId` assigned within `TIMING.SYNC_LOCK_TIMEOUT_MS` (via `manager._orderIdAssignedAt`) may be an in-flight create/adopt whose broadcast has not landed or is not yet visible to 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.
@@ -101,29 +101,52 @@ Returns `{ plannedCreates, plannedUpdates, plannedCancels, chainSellCount, chain
101
101
 
102
102
  ### Phase 2 — Blockchain Execution Outside Lock
103
103
 
104
- **`grid_reconcile.ts:376-500`**
104
+ **Source:** `reconcileGridOrders()` Phase 2 in `grid_reconcile.ts`.
105
105
 
106
- Each sub-phase releases `_gridLock` before starting and re-acquires it per operation (through `synchronizeWithChain` in individual helpers). No single long-held lock blocks fills, sync, or divergence checks — but each operation still runs under the lock for consistency.
106
+ Phase 1 releases `_gridLock` before network I/O. Phase 2 runs under the
107
+ broadcasting single-flight marker so a fill-driven COW rebalance cannot plan the
108
+ same slots concurrently; individual sync/apply paths follow the canonical lock
109
+ hierarchy. No planning-phase `_gridLock` is held across RPC calls.
107
110
 
108
- **Cancellations** (lines 384-411): Execute `plannedCancels`. Each `_cancelChainOrder` acquires `_gridLock` internally. This covers duplicate cancels, edge-release cancels, and excess-order cancels.
111
+ **Cancellations — v1.6.6 stale-plan guard:** Before submitting any
112
+ `plannedCancels`, reconcile performs one guarded pre-cancel read and builds a
113
+ per-order signature (type, price, size) from both the Phase-1 and current
114
+ snapshots. For every plan, `_startupCancelPlanStillCurrent()` requires:
109
115
 
110
- **Updates** (lines 413-485):
116
+ - the target chain order still exists with its exact Phase-1 signature;
117
+ - a matched plan still owns the same chain order in the same live grid slot;
118
+ - planned type, slot price, boundary, and gap geometry still match; and
119
+ - an originally unmatched surplus is still not owned by any live slot.
120
+
121
+ Validation is per-plan: an earlier cancellation or unrelated order change does
122
+ not invalidate an otherwise untouched plan. A truncated or failed pre-cancel
123
+ read skips all cancellation submissions. A complete empty read is authoritative
124
+ but likewise causes no plans to execute because no target remains present.
125
+ This closes stale-cancellation replays without requiring the whole book to
126
+ remain unchanged.
127
+
128
+ Surplus settlement uses the same live-ownership decision. Only an order that is
129
+ still untracked and signature-unchanged reaches `_cancelChainOrder`; a now-owned
130
+ or geometry-changed order is skipped rather than released from the stale
131
+ snapshot.
132
+
133
+ **Updates:**
111
134
  - Batch via `_executeStartupUpdateBatch` when `supportsBatchUpdate` is available
112
135
  - Retry up to 3× (`maxBatchAttempts = 3`)
113
136
  - On each failure: `_recoverStartupSyncFailure()` re-fetches open orders from chain (guarded read) and re-syncs `manager` state via `manager.syncFromOpenOrders()`, then `_refreshStartupUpdatePlans()` rebuilds plans against the fresh chain state
114
- - If retries exhausted or batch helpers are unavailable → `_executeStartupSequentialUpdateFallback()` one-by-one with per-failure recovery
137
+ - If retries are exhausted or batch helpers are unavailable → `_executeStartupSequentialUpdateFallback()` one-by-one with per-failure recovery
115
138
 
116
- **Creates** (lines 487-500): `_executePlannedStartupCreates` runs with the outside-in pair grouping — grouped from 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.
139
+ **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.
117
140
 
118
141
  ### Phase 3 — Fresh Re-read, Adoption, Stale Surplus Cleanup
119
142
 
120
- **`grid_reconcile.ts:502-641`** (guarded by `if (!dryRun)` at line 504)
143
+ **Source:** Phase 3 in `reconcileGridOrders()` (guarded by `if (!dryRun)`).
121
144
 
122
- 1. **Guarded fresh re-read** (lines 512-516): `readOpenOrdersGuarded` re-fetches all open orders. On a truncated/empty read it returns early (defers), keeping the pre-Phase-2 counts for the summary log — a capped window omits exactly the freshest Phase-2 creates.
145
+ 1. **Guarded fresh re-read:** `readOpenOrdersGuarded` re-fetches all open orders. A truncated result returns `null` and Phase 3 defers, keeping the pre-Phase-2 counts for the summary log; a capped window omits exactly the freshest Phase-2 creates.
123
146
 
124
- 2. **Adopt uncertain-landed creates** (lines 533-578): For any fresh chain order not matching a grid `orderId` and 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. Full `syncFromOpenOrders` is 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.
147
+ 2. **Adopt uncertain-landed creates:** For any fresh chain order not matching a grid `orderId` and 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. Full `syncFromOpenOrders` is 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.
125
148
 
126
- 3. **Stale surplus cancellation** (lines 579-625): Per side, count orders exceeding `targetCount` that no grid slot holds via `orderId` (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.
149
+ 3. **Stale surplus cancellation:** Per side, count orders exceeding `targetCount` that no grid slot holds via `orderId` (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.
127
150
 
128
151
  ### Partial Failure State
129
152
 
@@ -132,8 +155,8 @@ If Phase 2 partially succeeds (some cancels, some creates fail), there is no rol
132
155
  ### Timeouts and Read Coverage
133
156
 
134
157
  - **No per-attempt race** around the reconcile itself — the 1.4.8 change removed it to avoid orphaning mid-batch broadcasts (see the [`recalculateGrid`](../modules/order/grid.ts) call site in `modules/order/grid.ts`).
135
- - The whole resync is bounded by a **10-minute total timeout** (`PIPELINE_TIMING.TIMEOUT_MS * 2` at `grid.ts:1511`), applied via `Promise.race` at `grid.ts:1617`.
136
- - Every internal chain read goes through `readOpenOrdersGuarded` (`chain_orders.ts:608`) with the 30s / 3-retry / node-failover standard, and empty/truncated reads are treated as **ambiguous** — never as authoritative absence.
158
+ - The whole resync is bounded by a **10-minute total timeout** (`PIPELINE_TIMING.TIMEOUT_MS * 2`) and applied with `Promise.race` in `recalculateGrid()`.
159
+ - Every internal chain read goes through `readOpenOrdersGuarded` with the 30s / 3-retry / node-failover standard. Truncated reads are always ambiguous and return `null`; empty reads are ambiguous only when a caller explicitly sets `deferEmpty: true`. Phase-2 cancellation uses the default empty-aware policy, then validates each plan against the fresh per-order signature before any mutation.
137
160
 
138
161
  ---
139
162
 
@@ -141,7 +164,7 @@ If Phase 2 partially succeeds (some cancels, some creates fail), there is no rol
141
164
 
142
165
  ### Fresh Grid Guard (`matchedOnGrid > 0`)
143
166
 
144
- **`grid_reconcile_internal.ts:1738`**
167
+ **`_reconcileStartupSide()` in `grid_reconcile_internal.ts`**
145
168
 
146
169
  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:
147
170
 
@@ -155,27 +178,25 @@ When `matchedOnGrid === 0` AND scaling up (`neededSlots > 0`), excess cancellati
155
178
 
156
179
  ### Grid-Edge Lock
157
180
 
158
- **`grid_reconcile_internal.ts:355`** — `_isGridEdgeFullyActive` detects when the grid boundary is fully active (all slots on-chain) before cancelling excess orders.
181
+ **`_isGridEdgeFullyActive()` in `grid_reconcile_internal.ts`** detects when the grid boundary is fully active (all slots on-chain) before cancelling excess orders.
159
182
 
160
- 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`, line 425) 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.
183
+ 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.
161
184
 
162
- Detection (`_isGridEdgeFullyActive`, line 355): sort orders by price (BUY descending, SELL ascending), and check the outermost ones are all `isOrderPlaced()`.
185
+ Detection sorts orders by price (BUY descending, SELL ascending) and checks that the outermost ones are all `isOrderPlaced()`.
163
186
 
164
187
  ### Duplicate Cancellation
165
188
 
166
- **`grid_reconcile.ts:278`**
189
+ **`reconcileGridOrders()` in `grid_reconcile.ts`**
167
190
 
168
191
  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.
169
192
 
170
193
  ### Batch Update Failure Recovery
171
194
 
172
- **`grid_reconcile.ts:436`**
173
-
174
- Up to 3 batch attempts. 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).
195
+ 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).
175
196
 
176
197
  ### Phantom Orders via Reconcile
177
198
 
178
- **`grid_reconcile.ts:227-253`** — 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 freshly-assigned deferral protects in-flight broadcasts, and the ghost heuristic lets known fills pass. See [`developer_guide.md`](developer_guide.md#phantom-orders-prevention-defense-in-depth) for the full 3-layer defense.
199
+ 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 freshly-assigned deferral protects in-flight broadcasts, and the ghost heuristic lets known fills pass. See [`developer_guide.md`](developer_guide.md#phantom-orders-prevention-defense-in-depth) for the full 3-layer defense.
179
200
 
180
201
  ### Shelf Orders (Fork-Kept Manual Orders)
181
202
 
@@ -187,7 +208,7 @@ Live on-chain orders with non-slot-N ids below the rail (e.g. `deep-*` manuals k
187
208
 
188
209
  ### COW Interaction
189
210
 
190
- 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`](COPY_ON_WRITE_MASTER_PLAN.md#safety-guardrails) and [`COW_INVARIANTS.md`](COW_INVARIANTS.md#reconcile-grid_reconcilemd) for COW rules.
211
+ 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`](COPY_ON_WRITE_MASTER_PLAN.md#safety-guardrails) and [`COW_INVARIANTS.md`](COW_INVARIANTS.md#reconcile) for COW rules.
191
212
 
192
213
  ### Slot-Price Invariant at the Reconcile Emission Sites
193
214
 
@@ -195,11 +216,11 @@ Three of the six guarded emission sites are reconcile sites (`RECONCILE-CREATE`,
195
216
 
196
217
  ### Truncated-Read Ambiguity (since 1.4.8)
197
218
 
198
- Every chain read feeding an absence/surplus decision goes through `readOpenOrdersGuarded` (`chain_orders.ts:608`) and treats an empty or truncated snapshot as **unreadable** — never as "nothing landed" or "nothing to cancel":
219
+ 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:
199
220
 
200
- - `_recoverSyncFromChain` (`grid_reconcile_internal.ts:726`) — plus its three 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.
201
- - `_adoptPossiblyLandedCreate` (`grid_reconcile_internal.ts:1112`) defers to an uncertain outcome on truncated reads, and the startup group batch uncertain verification follows the same rule.
202
- - Phase 3 final refresh (`grid_reconcile.ts:533`) skips adoption/surplus-cancel on a truncated read, keeping the pre-phase-2 counts for the summary log.
221
+ - `_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.
222
+ - `_adoptPossiblyLandedCreate()` defers to an uncertain outcome on truncated reads, and the startup group batch uncertain verification follows the same rule.
223
+ - Phase 3 final refresh skips adoption/surplus-cancel on a truncated read, keeping the pre-phase-2 counts for the summary log.
203
224
  - Adoption paths (`_adoptPossiblyLandedCreate`, grouping path, reconcile adoption loop) apply the create-fee deduction via `_applySync` for accounting parity.
204
225
 
205
226
  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.
@@ -208,7 +229,7 @@ The underlying rule is `INV-BROADCAST-004`: a capped `get_full_accounts` window
208
229
 
209
230
  ## Lock Hierarchy
210
231
 
211
- **`manager.ts:593-597`** — canonical reference in [`developer_guide.md`](developer_guide.md#lock-ordering-for-deadlock-prevention).
232
+ **`manager.ts` lock declaration** — canonical reference in [`developer_guide.md`](developer_guide.md#lock-ordering-for-deadlock-prevention).
212
233
 
213
234
  ```
214
235
  Level 0: _fillProcessingLock Level 1: _divergenceLock
@@ -234,10 +255,10 @@ Commit `e64db685` replaced 6 single-value boolean state fields with refcounts/st
234
255
 
235
256
  | Constant | Value | File | Role |
236
257
  |----------|-------|------|------|
237
- | `maxBatchAttempts` | `3` | `grid_reconcile.ts:436` | Update-batch retry limit |
238
- | `PRICE_TOLERANCE_MAX_PERCENT` | `0.01` (1%) | `constants.ts:566` | Cap on price tolerance |
239
- | `PRICE_TOLERANCE_MIN_ABSOLUTE` | `0.0001` | `constants.ts:570` | Floor for price tolerance |
240
- | `PIPELINE_TIMING.TIMEOUT_MS` | `300000` (5min) | `constants.ts:973` | Base pipeline timing; resync uses 2× (10 min) |
258
+ | `maxBatchAttempts` | `3` | `grid_reconcile.ts` | Update-batch retry limit |
259
+ | `PRICE_TOLERANCE_MAX_PERCENT` | `0.01` (1%) | `constants.ts::GRID_LIMITS` | Cap on price tolerance |
260
+ | `PRICE_TOLERANCE_MIN_ABSOLUTE` | `0.0001` | `constants.ts::GRID_LIMITS` | Floor for price tolerance |
261
+ | `PIPELINE_TIMING.TIMEOUT_MS` | `300000` (5min) | `constants.ts::PIPELINE_TIMING` | Base pipeline timing; resync uses 2× (10 min) |
241
262
 
242
263
  ---
243
264
 
@@ -246,7 +267,7 @@ Commit `e64db685` replaced 6 single-value boolean state fields with refcounts/st
246
267
  | Test File | Coverage |
247
268
  |-----------|----------|
248
269
  | `tests/test_grid_reconcile.ts` | 8: grid detection, largest-order cancel, ordering |
249
- | `tests/test_grid_reconcile_regressions.ts` | 6: unmatched-cancel guard, verifiedAfterFailure, slot-mapped skip, storeGrid await, `matchedOnGrid` guard, Phase 3 surplus; plus 1.4.8 regression 1b (empty refetch after verified cancel defers) |
270
+ | `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 |
250
271
  | `tests/test_resync_duplicate_race.ts` | Phase 3 duplicate race |
251
272
  | `tests/test_resync_balance_fix.ts` | Fund reuse during Phase 3 |
252
273
  | `tests/test_resync_invariants.ts` | Fund invariant suppression during transient resync |
@@ -263,10 +284,10 @@ Commit `e64db685` replaced 6 single-value boolean state fields with refcounts/st
263
284
 
264
285
  | File | Role |
265
286
  |------|------|
266
- | `modules/order/grid_reconcile.ts` | Public API + 3-phase orchestrator (670 lines) |
267
- | `modules/order/grid_reconcile_internal.ts` | Internal helpers — `_reconcileStartupSide`, grid detection, recovery, uncertainty (2132 lines) |
287
+ | `modules/order/grid_reconcile.ts` | Public API + 3-phase orchestrator, including per-plan Phase-2 cancellation validation |
288
+ | `modules/order/grid_reconcile_internal.ts` | Internal helpers — `_reconcileStartupSide`, grid detection, recovery, uncertainty |
268
289
  | `modules/order/manager.ts` | Lock hierarchy definition, `_applyOrderUpdate`, phantom guard, `reconcileGrid` entry, COW integration |
269
- | `modules/order/async_lock.ts` | AsyncLock engine with ALS re-entrancy (435 lines) |
290
+ | `modules/order/async_lock.ts` | AsyncLock engine with ALS re-entrancy |
270
291
  | `modules/order/sync_engine.ts` | Blockchain sync pipeline |
271
292
  | `modules/order/grid.ts` | Grid creation, `recalculateGrid` (full resync) calls reconcile |
272
293
  | `modules/chain_orders.ts` | `readOpenOrdersGuarded` / guarded read infrastructure |