dexbot 1.6.5 → 1.6.7

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 (228) hide show
  1. package/CHANGELOG.md +76 -4
  2. package/README.md +108 -36
  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 +82 -83
  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 +108 -44
  64. package/dist/modules/account_bots.js.map +1 -1
  65. package/dist/modules/account_orders.d.ts +20 -2
  66. package/dist/modules/account_orders.d.ts.map +1 -1
  67. package/dist/modules/account_orders.js +63 -3
  68. package/dist/modules/account_orders.js.map +1 -1
  69. package/dist/modules/bitshares-native/chain_client.d.ts +10 -0
  70. package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -1
  71. package/dist/modules/bitshares-native/chain_client.js +197 -28
  72. package/dist/modules/bitshares-native/chain_client.js.map +1 -1
  73. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -1
  74. package/dist/modules/bitshares-native/crypto/ecc.browser.js +0 -31
  75. package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -1
  76. package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
  77. package/dist/modules/bitshares-native/subscriptions.js +312 -10
  78. package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
  79. package/dist/modules/bitshares-native/transport.d.ts +1 -0
  80. package/dist/modules/bitshares-native/transport.d.ts.map +1 -1
  81. package/dist/modules/bitshares-native/transport.js +97 -3
  82. package/dist/modules/bitshares-native/transport.js.map +1 -1
  83. package/dist/modules/bitshares_client.d.ts +20 -0
  84. package/dist/modules/bitshares_client.d.ts.map +1 -1
  85. package/dist/modules/bitshares_client.js +29 -0
  86. package/dist/modules/bitshares_client.js.map +1 -1
  87. package/dist/modules/chain_keys.d.ts +37 -22
  88. package/dist/modules/chain_keys.d.ts.map +1 -1
  89. package/dist/modules/chain_keys.js +145 -44
  90. package/dist/modules/chain_keys.js.map +1 -1
  91. package/dist/modules/cli_start_onboarding.d.ts +10 -0
  92. package/dist/modules/cli_start_onboarding.d.ts.map +1 -0
  93. package/dist/modules/cli_start_onboarding.js +15 -0
  94. package/dist/modules/cli_start_onboarding.js.map +1 -0
  95. package/dist/modules/cli_start_output.d.ts +6 -0
  96. package/dist/modules/cli_start_output.d.ts.map +1 -0
  97. package/dist/modules/cli_start_output.js +8 -0
  98. package/dist/modules/cli_start_output.js.map +1 -0
  99. package/dist/modules/constants.d.ts +13 -0
  100. package/dist/modules/constants.d.ts.map +1 -1
  101. package/dist/modules/constants.js +103 -1
  102. package/dist/modules/constants.js.map +1 -1
  103. package/dist/modules/credit_runtime.d.ts +0 -2
  104. package/dist/modules/credit_runtime.d.ts.map +1 -1
  105. package/dist/modules/credit_runtime.js +1 -10
  106. package/dist/modules/credit_runtime.js.map +1 -1
  107. package/dist/modules/dexbot_class.d.ts +13 -10
  108. package/dist/modules/dexbot_class.d.ts.map +1 -1
  109. package/dist/modules/dexbot_class.js +0 -9
  110. package/dist/modules/dexbot_class.js.map +1 -1
  111. package/dist/modules/dexbot_cow_runtime.d.ts +13 -0
  112. package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
  113. package/dist/modules/dexbot_cow_runtime.js +62 -10
  114. package/dist/modules/dexbot_cow_runtime.js.map +1 -1
  115. package/dist/modules/dexbot_fill_runtime.d.ts +6 -1
  116. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
  117. package/dist/modules/dexbot_fill_runtime.js +22 -3
  118. package/dist/modules/dexbot_fill_runtime.js.map +1 -1
  119. package/dist/modules/dexbot_maintenance_runtime.d.ts +24 -1
  120. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  121. package/dist/modules/dexbot_maintenance_runtime.js +45 -1
  122. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  123. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  124. package/dist/modules/dexbot_state_recovery.js +13 -1
  125. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  126. package/dist/modules/order/accounting.d.ts +4 -18
  127. package/dist/modules/order/accounting.d.ts.map +1 -1
  128. package/dist/modules/order/accounting.js +4 -34
  129. package/dist/modules/order/accounting.js.map +1 -1
  130. package/dist/modules/order/export.d.ts +9 -12
  131. package/dist/modules/order/export.d.ts.map +1 -1
  132. package/dist/modules/order/export.js +9 -12
  133. package/dist/modules/order/export.js.map +1 -1
  134. package/dist/modules/order/grid.d.ts +35 -40
  135. package/dist/modules/order/grid.d.ts.map +1 -1
  136. package/dist/modules/order/grid.js +76 -44
  137. package/dist/modules/order/grid.js.map +1 -1
  138. package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
  139. package/dist/modules/order/grid_reconcile.js +111 -20
  140. package/dist/modules/order/grid_reconcile.js.map +1 -1
  141. package/dist/modules/order/grid_reconcile_internal.d.ts +5 -3
  142. package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
  143. package/dist/modules/order/grid_reconcile_internal.js +29 -3
  144. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  145. package/dist/modules/order/logger.d.ts +12 -2
  146. package/dist/modules/order/logger.d.ts.map +1 -1
  147. package/dist/modules/order/logger.js +29 -7
  148. package/dist/modules/order/logger.js.map +1 -1
  149. package/dist/modules/order/manager.d.ts +49 -8
  150. package/dist/modules/order/manager.d.ts.map +1 -1
  151. package/dist/modules/order/manager.js +72 -34
  152. package/dist/modules/order/manager.js.map +1 -1
  153. package/dist/modules/order/utils/order.d.ts +22 -7
  154. package/dist/modules/order/utils/order.d.ts.map +1 -1
  155. package/dist/modules/order/utils/order.js +200 -33
  156. package/dist/modules/order/utils/order.js.map +1 -1
  157. package/dist/modules/order/utils/system.d.ts +97 -0
  158. package/dist/modules/order/utils/system.d.ts.map +1 -1
  159. package/dist/modules/order/utils/system.js +274 -2
  160. package/dist/modules/order/utils/system.js.map +1 -1
  161. package/dist/modules/order/utils/withPoolRef.d.ts +12 -0
  162. package/dist/modules/order/utils/withPoolRef.d.ts.map +1 -1
  163. package/dist/modules/order/utils/withPoolRef.js +17 -0
  164. package/dist/modules/order/utils/withPoolRef.js.map +1 -1
  165. package/dist/modules/order/working_grid.d.ts +8 -13
  166. package/dist/modules/order/working_grid.d.ts.map +1 -1
  167. package/dist/modules/order/working_grid.js +8 -29
  168. package/dist/modules/order/working_grid.js.map +1 -1
  169. package/dist/modules/paths.d.ts +7 -1
  170. package/dist/modules/paths.d.ts.map +1 -1
  171. package/dist/modules/paths.js +10 -3
  172. package/dist/modules/paths.js.map +1 -1
  173. package/dist/modules/utils/text_width.d.ts +19 -0
  174. package/dist/modules/utils/text_width.d.ts.map +1 -0
  175. package/dist/modules/utils/text_width.js +97 -0
  176. package/dist/modules/utils/text_width.js.map +1 -0
  177. package/dist/pm2.d.ts.map +1 -1
  178. package/dist/pm2.js +95 -18
  179. package/dist/pm2.js.map +1 -1
  180. package/dist/scripts/sync-version.js +0 -22
  181. package/dist/scripts/sync-version.js.map +1 -1
  182. package/dist/scripts/update.js +62 -13
  183. package/dist/scripts/update.js.map +1 -1
  184. package/dist/scripts/update_dist_freshness.d.ts +12 -0
  185. package/dist/scripts/update_dist_freshness.d.ts.map +1 -1
  186. package/dist/scripts/update_dist_freshness.js +30 -1
  187. package/dist/scripts/update_dist_freshness.js.map +1 -1
  188. package/dist/unlock.d.ts +14 -2
  189. package/dist/unlock.d.ts.map +1 -1
  190. package/dist/unlock.js +73 -4
  191. package/dist/unlock.js.map +1 -1
  192. package/docs/BITSHARES_ONBOARDING.md +9 -6
  193. package/docs/COW_INVARIANTS.md +9 -0
  194. package/docs/CREDENTIAL_SECURITY.md +26 -0
  195. package/docs/DEXBOT_COMPARISON.md +11 -11
  196. package/docs/EVOLUTION.md +9 -7
  197. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
  198. package/docs/GRID_PRICE_INVARIANT.md +573 -0
  199. package/docs/GRID_RECALCULATION.md +1 -1
  200. package/docs/GRID_RECONCILE.md +60 -39
  201. package/docs/LOGGING.md +17 -2
  202. package/docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md +555 -0
  203. package/docs/README.md +3 -3
  204. package/docs/WORKFLOW.md +12 -6
  205. package/docs/architecture.md +40 -38
  206. package/docs/developer_guide.md +4 -8
  207. package/market_adapter/README.md +16 -12
  208. package/modules/README.md +1 -1
  209. package/package.json +8 -4
  210. package/scripts/README.md +0 -9
  211. package/scripts/reset-settings.sh +1 -1
  212. package/analysis/ama_fitting/package.json +0 -12
  213. package/analysis/analyze_derivatives.ts +0 -320
  214. package/analysis/derivative_chart_generator.ts +0 -874
  215. package/analysis/trend_detection/SIGNAL_DOCUMENTATION.md +0 -403
  216. package/analysis/trend_detection/derivative_analyzer.ts +0 -1059
  217. package/dist/analysis/analyze_derivatives.d.ts +0 -68
  218. package/dist/analysis/analyze_derivatives.d.ts.map +0 -1
  219. package/dist/analysis/analyze_derivatives.js +0 -287
  220. package/dist/analysis/analyze_derivatives.js.map +0 -1
  221. package/dist/analysis/derivative_chart_generator.d.ts +0 -12
  222. package/dist/analysis/derivative_chart_generator.d.ts.map +0 -1
  223. package/dist/analysis/derivative_chart_generator.js +0 -891
  224. package/dist/analysis/derivative_chart_generator.js.map +0 -1
  225. package/dist/analysis/trend_detection/derivative_analyzer.d.ts +0 -248
  226. package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +0 -1
  227. package/dist/analysis/trend_detection/derivative_analyzer.js +0 -910
  228. 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 |
package/docs/LOGGING.md CHANGED
@@ -43,6 +43,8 @@ Module → Logger.log() ──┬→ console (stdout/stderr)
43
43
  + JSON lines (optional)
44
44
  ```
45
45
 
46
+ **Under PM2** the console branch is the only active sink: PM2 captures stdout/stderr into `profiles/logs/<app>.log`, so the Logger suppresses its own file writes and its timestamps (PM2's `log_date_format` prefixes each line). Keep console output enabled under PM2 — auto-quieting it would leave PM2 with nothing to capture. Use `quietUnderPm2: true` only if you deliberately want a silent process.
47
+
46
48
  ---
47
49
 
48
50
  ## Log Levels
@@ -155,7 +157,13 @@ The default `LOG_LEVEL` is `"info"`. For production or minimal output, set to `"
155
157
 
156
158
  Example: 1.1GB budget with 10 rotated files → each file rotates at ~100MB, max total ~1.1GB.
157
159
 
158
- Under PM2, rotation is auto-suppressed — PM2 handles its own log files.
160
+ Under PM2, the Logger's own file writes and rotation are suppressed — PM2 owns the log files. PM2 core does **not** rotate them (the per-app `max_size` field is ignored), so `dexbot pm2` installs and configures the `pm2-logrotate` module (100M per file, retain 10, compressed) on first start. To manage rotation yourself, install it ahead of time:
161
+
162
+ ```bash
163
+ pm2 install pm2-logrotate
164
+ pm2 set pm2-logrotate:max_size 100M
165
+ pm2 set pm2-logrotate:retain 10
166
+ ```
159
167
 
160
168
  ---
161
169
 
@@ -265,6 +273,13 @@ New/updated operator-visible messages added by the uncertain-broadcast and COW h
265
273
  | `⚠ FAILED attempt N/3` / `✗ BLACKLISTED after N failures` | Daemon node health ledger — per-node retry exhaustion then blacklist |
266
274
  | `[DUST] Chain refetch after verified cancel is TRUNCATED/EMPTY; applying local cancel sync` | Truncated-read fallback in the dust-cancel refetch path |
267
275
  | `authoritative absence verified` | Aligned retry log wording — re-broadcast only on provable absence |
276
+ | `Fill channel DEGRADED for <account>: N consecutive history-scan failures … forcing reconnect` | Fill-history channel stayed dead while the socket looked open; the watchdog forced a reconnect (which re-establishes the session and fires the post-reconnect safety-net sync) |
277
+ | `Fill channel recovered for <account> [after N forced reconnect(s)]` | A previously degraded channel completed a successful history scan; `N` is how many forced reconnects it took (the clause is omitted when it recovered without an issued reconnect) |
278
+ | `processObjects (fill-poll): error … (+N suppressed)` | Throttled fill-channel error; `+N suppressed` counts repeats collapsed within the log interval |
279
+ | `processObjects (retry<N>-after-<context>): error …` | Fast re-scan rung fired after a channel failure (ladder 5s/10s/15s), to verify a recovery attempt without waiting for the next 60s poll tick. `N` is the rung |
280
+ | `Fill channel for <account> did NOT recover after N forced reconnects … fills may be missed … restart the bot` | **Operator action required.** Automatic recovery ran its full escalation and the channel is still dead — the log line that makes a failed recovery visible instead of a silent reconnect loop |
281
+ | `⚠ <node>… FAILED attempt N/3 (fill channel unrecoverable for <account>: …)` | **Node strike recorded.** Emitted only after N forced reconnects failed to clear the channel, i.e. the node itself is suspect. A forced reconnect on its own never costs a node a strike — a session-level wedge that clears on the first cycle leaves the strike ledger untouched. Three of these blacklist the node for 24h |
282
+ | `Forcing reconnect on <node> (<reason>)` | Transport-level forced reconnect (stale api_id escalation or fill-channel watchdog); the node is reported failed so the reconnect prefers another node |
268
283
 
269
284
  ---
270
285
 
@@ -316,7 +331,7 @@ Yes — each bot entry in `profiles/bots.json` accepts an optional `logging` fie
316
331
  The per-bot `logging` is deep-merged on top of the global config from `general.settings.json`. See `modules/runtime_settings.ts` for the merge logic and `modules/order/manager.ts` for where the merged config reaches the logger.
317
332
 
318
333
  **Q: What about PM2?**
319
- The logger auto-detects PM2 and suppresses file writes (PM2 captures stdout/stderr). File rotation is also suppressed under PM2.
334
+ The logger auto-detects PM2 and suppresses its own file writes because PM2 captures stdout/stderr into `profiles/logs/<app>.log`. It never auto-quiets under PM2 (that would drop every line — stdout is the only sink). Log rotation moves to `pm2-logrotate`, which `dexbot pm2` installs automatically on first start.
320
335
 
321
336
  **Q: Are log lines lost on crash?**
322
337
  Queued-but-unwritten lines could be lost. Critical errors go to stderr immediately (PM2 captures those). Queue drains every 100ms. Call `flush()` on shutdown.