@north-light/crouter 0.3.312 → 0.3.313

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 (495) hide show
  1. package/dist/api/client.d.ts +27 -30
  2. package/dist/api/client.js +80 -67
  3. package/dist/api/dto/broker-ops.d.ts +10 -20
  4. package/dist/api/dto/canvas.d.ts +1 -10
  5. package/dist/api/dto/health.d.ts +113 -0
  6. package/dist/api/dto/human-requests.d.ts +14 -3
  7. package/dist/api/dto/human.d.ts +2 -48
  8. package/dist/api/dto/human.js +3 -2
  9. package/dist/api/dto/inbox.d.ts +14 -0
  10. package/dist/api/dto/mail.d.ts +43 -0
  11. package/dist/api/dto/nodes.d.ts +11 -8
  12. package/dist/api/dto/recovery.d.ts +96 -0
  13. package/dist/api/dto/reviews.d.ts +2 -2
  14. package/dist/api/routes.d.ts +9 -6
  15. package/dist/api/routes.js +11 -11
  16. package/dist/build-root.d.ts +4 -0
  17. package/dist/build-root.js +11 -1
  18. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +35 -47
  19. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +1 -1
  20. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +12 -5
  21. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
  22. package/dist/cli.js +10 -1
  23. package/dist/clients/__tests__/helpers/human-daemon.d.ts +11 -0
  24. package/dist/clients/__tests__/helpers/human-daemon.js +60 -0
  25. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  26. package/dist/clients/attach/chrome/inbox-strip.d.ts +3 -3
  27. package/dist/clients/attach/chrome/inbox-strip.js +17 -9
  28. package/dist/clients/attach/chrome/ticket-panel.d.ts +2 -2
  29. package/dist/clients/attach/chrome/ticket-panel.js +77 -46
  30. package/dist/clients/attach/overlays/graph.js +6 -9
  31. package/dist/clients/attach/render/chat-view.d.ts +5 -4
  32. package/dist/clients/attach/render/chat-view.js +22 -13
  33. package/dist/clients/attach/render/page-block.d.ts +11 -7
  34. package/dist/clients/attach/render/page-block.js +64 -55
  35. package/dist/clients/attach/viewer.js +746 -735
  36. package/dist/clients/inbox/__tests__/integration/inbox-controller.test.js +56 -56
  37. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +45 -32
  38. package/dist/clients/inbox/controller.d.ts +42 -14
  39. package/dist/clients/inbox/controller.js +240 -124
  40. package/dist/clients/inbox/dto-adapt.d.ts +28 -0
  41. package/dist/clients/inbox/dto-adapt.js +93 -0
  42. package/dist/clients/inbox/page-adapter.d.ts +5 -4
  43. package/dist/clients/inbox/page-adapter.js +12 -12
  44. package/dist/clients/inbox/resolve.d.ts +10 -5
  45. package/dist/clients/inbox/resolve.js +13 -12
  46. package/dist/clients/inbox/review/review-client.d.ts +0 -1
  47. package/dist/clients/inbox/review/review-client.js +0 -4
  48. package/dist/clients/inbox/review-adapter.d.ts +2 -1
  49. package/dist/clients/inbox/review-adapter.js +3 -5
  50. package/dist/clients/inbox/surface.d.ts +2 -2
  51. package/dist/clients/inbox/surface.js +19 -8
  52. package/dist/clients/inbox/tui.d.ts +3 -3
  53. package/dist/commands/__tests__/human.test.js +31 -15
  54. package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +36 -11
  55. package/dist/commands/{sys/__tests__ → __tests__/integration}/migrate.test.js +142 -36
  56. package/dist/commands/__tests__/revive-now-gate.test.js +2 -0
  57. package/dist/commands/api-client.d.ts +5 -4
  58. package/dist/commands/api-client.js +13 -7
  59. package/dist/commands/canvas-history/shared.js +1 -1
  60. package/dist/commands/canvas-history.js +1 -1
  61. package/dist/commands/canvas-roster.js +1 -1
  62. package/dist/commands/canvas.js +1 -2
  63. package/dist/commands/human/feedback.js +20 -17
  64. package/dist/commands/human/prompts.js +45 -36
  65. package/dist/commands/human/queue.js +73 -123
  66. package/dist/commands/human/review.js +6 -16
  67. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +2 -0
  68. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +2 -0
  69. package/dist/commands/memory/__tests__/repository-root-lint.test.js +2 -0
  70. package/dist/commands/node-inspect-artifacts.js +1 -1
  71. package/dist/commands/node-lifecycle-revive.d.ts +2 -2
  72. package/dist/commands/node-lifecycle-revive.js +9 -19
  73. package/dist/commands/pkg/plugin-manage.js +0 -1
  74. package/dist/commands/profile/show.js +4 -12
  75. package/dist/commands/surface-tmux-spread.js +1 -5
  76. package/dist/commands/sys/__tests__/config-model-ladders.test.js +23 -30
  77. package/dist/commands/sys/__tests__/sync-project-guidance.test.js +37 -11
  78. package/dist/commands/sys/branch.d.ts +2 -0
  79. package/dist/commands/sys/branch.js +19 -0
  80. package/dist/commands/sys/config.js +106 -33
  81. package/dist/commands/sys/daemon.d.ts +2 -0
  82. package/dist/commands/sys/daemon.js +130 -7
  83. package/dist/commands/sys/doctor.js +5 -15
  84. package/dist/commands/sys/migrate.js +21 -92
  85. package/dist/commands/sys/setup-settings-runner.js +4 -2
  86. package/dist/commands/sys.js +2 -15
  87. package/dist/core/__tests__/boot-fault-attempt.test.js +23 -1
  88. package/dist/core/__tests__/boot.test.js +32 -25
  89. package/dist/core/__tests__/broker-launch-candidates.test.js +49 -0
  90. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +149 -176
  91. package/dist/core/__tests__/canvas-inbox-watcher-naming.test.js +59 -10
  92. package/dist/core/__tests__/canvas-inbox-watcher.test.js +76 -20
  93. package/dist/core/__tests__/canvas.test.js +18 -47
  94. package/dist/core/__tests__/child-death-wake.test.js +29 -7
  95. package/dist/core/__tests__/cli-help-contract.test.js +2 -0
  96. package/dist/core/__tests__/close.test.js +29 -10
  97. package/dist/core/__tests__/collectible.test.js +2 -1
  98. package/dist/core/__tests__/config-change-delta.test.js +2 -0
  99. package/dist/core/__tests__/context-intro.test.js +13 -1
  100. package/dist/core/__tests__/cron-armed-context-failure.test.js +17 -12
  101. package/dist/core/__tests__/cron-held-settlement.test.js +21 -3
  102. package/dist/core/__tests__/cron-node-sink-parked-root.test.js +29 -10
  103. package/dist/core/__tests__/cron-self-cancel-delivery.test.js +14 -2
  104. package/dist/core/__tests__/cron-timeout-descendants.test.js +13 -1
  105. package/dist/core/__tests__/daemon-boot.test.js +51 -14
  106. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  107. package/dist/core/__tests__/fixtures/fake-engine.js +8 -3
  108. package/dist/core/__tests__/focuses.test.js +10 -5
  109. package/dist/core/__tests__/helpers/harness.js +50 -18
  110. package/dist/core/__tests__/history-inbox.test.js +12 -16
  111. package/dist/core/__tests__/human-action-delivery.test.js +48 -28
  112. package/dist/core/__tests__/human-actions.test.js +7 -3
  113. package/dist/core/__tests__/inline-memory-refs.test.js +7 -2
  114. package/dist/core/__tests__/integration/broker-fork-seam.test.js +37 -10
  115. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +42 -0
  116. package/dist/core/__tests__/integration/broker-snapshot-history.test.js +42 -29
  117. package/dist/core/__tests__/integration/broker-wedge-supervision.test.js +36 -35
  118. package/dist/core/__tests__/integration/command-plugins.test.js +15 -6
  119. package/dist/core/__tests__/integration/deferred-no-wake.test.js +113 -92
  120. package/dist/core/__tests__/integration/host-teardown-process-group.test.js +9 -5
  121. package/dist/core/__tests__/integration/lifecycle-hooks.test.js +16 -3
  122. package/dist/core/__tests__/integration/plugin-revalidate.test.js +4 -0
  123. package/dist/core/__tests__/integration/profile-pause.test.js +4 -4
  124. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +9 -12
  125. package/dist/core/__tests__/integration/revive.test.js +67 -21
  126. package/dist/core/__tests__/integration/spawn-root.test.js +51 -8
  127. package/dist/core/__tests__/integration/subscription-delivery.test.js +33 -21
  128. package/dist/core/__tests__/integration/worktree-land.test.js +5 -2
  129. package/dist/core/__tests__/integration/worktree-reap.test.js +15 -1
  130. package/dist/core/__tests__/kickoff.test.js +36 -39
  131. package/dist/core/__tests__/lifecycle-terminal-reasons.test.js +2 -1
  132. package/dist/core/__tests__/lifecycle.test.js +2 -1
  133. package/dist/core/__tests__/live-lifecycle-prompt.test.js +13 -1
  134. package/dist/core/__tests__/memory-resolver-precedence.test.js +2 -0
  135. package/dist/core/__tests__/migration.test.js +145 -31
  136. package/dist/core/__tests__/model-pin-durability.test.js +4 -1
  137. package/dist/core/__tests__/model-routes-config.test.js +13 -5
  138. package/dist/core/__tests__/node-outcome.test.js +93 -26
  139. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -1
  141. package/dist/core/__tests__/on-read-nested-store.test.js +2 -1
  142. package/dist/core/__tests__/passive-subscription.test.js +50 -157
  143. package/dist/core/__tests__/plugin-kinds.test.js +6 -4
  144. package/dist/core/__tests__/plugin-link-symlinked-scope-root.test.js +6 -1
  145. package/dist/core/__tests__/plugin-page-components.test.js +6 -4
  146. package/dist/core/__tests__/preview-registry-sync.test.js +2 -0
  147. package/dist/core/__tests__/profile-project-memory-delivery.test.js +4 -1
  148. package/dist/core/__tests__/prune.test.js +11 -5
  149. package/dist/core/__tests__/push-final-guard.test.js +14 -7
  150. package/dist/core/__tests__/relaunch-root.test.js +30 -2
  151. package/dist/core/__tests__/remote-canvas-target.test.js +7 -1
  152. package/dist/core/__tests__/repository-root-identity.test.js +4 -0
  153. package/dist/core/__tests__/review-model-floor.test.js +6 -3
  154. package/dist/core/__tests__/revive-all.test.js +13 -3
  155. package/dist/core/__tests__/revive-capacity.test.js +26 -4
  156. package/dist/core/__tests__/revive-parked-fresh.test.js +31 -8
  157. package/dist/core/__tests__/seam/broker-attach-stream.test.js +1 -1
  158. package/dist/core/__tests__/seam/broker-cap-freeze.test.js +4 -3
  159. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +11 -14
  160. package/dist/core/__tests__/seam/broker-provider-retry.test.js +85 -71
  161. package/dist/core/__tests__/seam/dormancy-release.test.js +33 -19
  162. package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +7 -5
  163. package/dist/core/__tests__/seam/inbox-reference-guidance.test.js +20 -6
  164. package/dist/core/__tests__/seam/node-outcome-delivery.test.js +27 -3
  165. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +2 -1
  166. package/dist/core/__tests__/stop-guard.test.js +2 -1
  167. package/dist/core/__tests__/subscription-pool-credential-guard.test.js +4 -3
  168. package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +18 -4
  169. package/dist/core/__tests__/yield-ensures-daemon.test.js +2 -1
  170. package/dist/core/bootstrap.js +2 -0
  171. package/dist/core/broker-client/__tests__/transport-socket.test.js +51 -6
  172. package/dist/core/broker-client/transport-socket.d.ts +1 -0
  173. package/dist/core/broker-client/transport-socket.js +13 -5
  174. package/dist/core/canvas/__tests__/attention.test.js +29 -35
  175. package/dist/core/canvas/__tests__/history-transcript.test.js +6 -6
  176. package/dist/core/canvas/__tests__/render-remote.test.js +25 -9
  177. package/dist/core/canvas/attention.d.ts +2 -5
  178. package/dist/core/canvas/attention.js +15 -64
  179. package/dist/core/canvas/boot.d.ts +6 -7
  180. package/dist/core/canvas/boot.js +5 -9
  181. package/dist/core/canvas/canvas.d.ts +13 -109
  182. package/dist/core/canvas/canvas.js +140 -401
  183. package/dist/core/canvas/crons.d.ts +2 -0
  184. package/dist/core/canvas/crons.js +16 -15
  185. package/dist/core/canvas/daemon-owner.js +10 -0
  186. package/dist/core/canvas/db.d.ts +2 -2
  187. package/dist/core/canvas/db.js +2 -18
  188. package/dist/core/canvas/deliveries.d.ts +104 -0
  189. package/dist/core/canvas/deliveries.js +354 -0
  190. package/dist/core/canvas/executions.d.ts +29 -0
  191. package/dist/core/canvas/executions.js +68 -0
  192. package/dist/core/canvas/history.js +35 -35
  193. package/dist/core/canvas/index.d.ts +3 -1
  194. package/dist/core/canvas/index.js +3 -1
  195. package/dist/core/canvas/migrations.js +20 -12
  196. package/dist/core/canvas/nav-model.js +1 -3
  197. package/dist/core/canvas/nav-render.d.ts +4 -4
  198. package/dist/core/canvas/nav-render.js +11 -21
  199. package/dist/core/canvas/node-sql-migration.d.ts +3 -0
  200. package/dist/core/canvas/node-sql-migration.js +261 -0
  201. package/dist/core/canvas/paths.d.ts +2 -16
  202. package/dist/core/canvas/paths.js +2 -31
  203. package/dist/core/canvas/recovery.d.ts +76 -0
  204. package/dist/core/canvas/recovery.js +588 -0
  205. package/dist/core/canvas/remote-canvas-source.js +4 -0
  206. package/dist/core/canvas/render-source.d.ts +16 -27
  207. package/dist/core/canvas/render-source.js +16 -45
  208. package/dist/core/canvas/sessions.d.ts +33 -0
  209. package/dist/core/canvas/sessions.js +78 -0
  210. package/dist/core/canvas/types.d.ts +33 -33
  211. package/dist/core/canvas/types.js +2 -5
  212. package/dist/core/canvas/waits.d.ts +18 -0
  213. package/dist/core/canvas/waits.js +70 -0
  214. package/dist/core/canvas/worktrees.d.ts +40 -0
  215. package/dist/core/canvas/worktrees.js +93 -0
  216. package/dist/core/config.d.ts +17 -12
  217. package/dist/core/config.js +106 -155
  218. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  219. package/dist/core/feed/feed.d.ts +0 -6
  220. package/dist/core/feed/feed.js +68 -56
  221. package/dist/core/feed/inbox.d.ts +7 -81
  222. package/dist/core/feed/inbox.js +46 -378
  223. package/dist/core/feed/messages.d.ts +58 -0
  224. package/dist/core/feed/messages.js +217 -0
  225. package/dist/core/fs-utils.d.ts +4 -0
  226. package/dist/core/fs-utils.js +21 -1
  227. package/dist/core/human/answer.d.ts +1 -1
  228. package/dist/core/human/answer.js +1 -1
  229. package/dist/core/human/claim.d.ts +1 -1
  230. package/dist/core/human/claim.js +2 -4
  231. package/dist/core/human/component-docs.js +1 -1
  232. package/dist/core/human/feedback-companion.js +3 -2
  233. package/dist/core/human/feedback.d.ts +5 -8
  234. package/dist/core/human/feedback.js +57 -85
  235. package/dist/core/human/page-authoring.d.ts +30 -0
  236. package/dist/core/human/page-authoring.js +19 -0
  237. package/dist/core/human/requests.d.ts +136 -0
  238. package/dist/core/human/requests.js +515 -0
  239. package/dist/core/human/root.d.ts +0 -7
  240. package/dist/core/human/root.js +0 -17
  241. package/dist/core/human/scan.d.ts +2 -7
  242. package/dist/core/human/scan.js +48 -132
  243. package/dist/core/human/summary.d.ts +1 -1
  244. package/dist/core/human/summary.js +1 -1
  245. package/dist/core/human/tickets.d.ts +23 -83
  246. package/dist/core/human/tickets.js +92 -243
  247. package/dist/core/human/types.d.ts +5 -1
  248. package/dist/core/installed-plugins.js +8 -3
  249. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  250. package/dist/core/locked-file.d.ts +6 -3
  251. package/dist/core/locked-file.js +36 -9
  252. package/dist/core/memory/extensions.js +2 -0
  253. package/dist/core/preview-registry.js +0 -1
  254. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  255. package/dist/core/profiles/default-binding.d.ts +0 -4
  256. package/dist/core/profiles/default-binding.js +12 -58
  257. package/dist/core/profiles/env-store.js +8 -3
  258. package/dist/core/profiles/manifest.d.ts +2 -37
  259. package/dist/core/profiles/manifest.js +35 -161
  260. package/dist/core/profiles/select.js +3 -0
  261. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  262. package/dist/core/review/__tests__/comments.test.js +28 -10
  263. package/dist/core/review/__tests__/remap.test.js +28 -10
  264. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  265. package/dist/core/review/birth.d.ts +1 -1
  266. package/dist/core/review/birth.js +34 -5
  267. package/dist/core/review/comments.js +1 -1
  268. package/dist/core/review/document.js +1 -1
  269. package/dist/core/review/project.d.ts +5 -1
  270. package/dist/core/review/project.js +14 -36
  271. package/dist/core/review/realize.d.ts +3 -6
  272. package/dist/core/review/realize.js +51 -168
  273. package/dist/core/review/remap.js +1 -1
  274. package/dist/core/review/stage.d.ts +3 -2
  275. package/dist/core/review/stage.js +2 -3
  276. package/dist/core/review/store.d.ts +3 -21
  277. package/dist/core/review/store.js +140 -186
  278. package/dist/core/review/types.d.ts +0 -1
  279. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  280. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  281. package/dist/core/runtime/broker/auth-reload.js +9 -10
  282. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  283. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  284. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  285. package/dist/core/runtime/broker/extension-abort.js +11 -0
  286. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  287. package/dist/core/runtime/broker/fault-retry.js +142 -95
  288. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  289. package/dist/core/runtime/broker/inbox.js +4 -92
  290. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  291. package/dist/core/runtime/broker/rebind.js +21 -12
  292. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  293. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  294. package/dist/core/runtime/broker.js +28 -22
  295. package/dist/core/runtime/close.d.ts +3 -9
  296. package/dist/core/runtime/close.js +16 -27
  297. package/dist/core/runtime/fault.d.ts +3 -93
  298. package/dist/core/runtime/fault.js +9 -419
  299. package/dist/core/runtime/kickoff.d.ts +3 -6
  300. package/dist/core/runtime/kickoff.js +12 -18
  301. package/dist/core/runtime/launch.js +1 -1
  302. package/dist/core/runtime/lifecycle.js +40 -7
  303. package/dist/core/runtime/placement.js +2 -2
  304. package/dist/core/runtime/recycle.js +8 -2
  305. package/dist/core/runtime/reset.js +22 -6
  306. package/dist/core/runtime/revive.js +226 -218
  307. package/dist/core/runtime/spawn.js +14 -3
  308. package/dist/core/scoped-state/db.d.ts +7 -0
  309. package/dist/core/scoped-state/db.js +92 -0
  310. package/dist/core/scoped-state/migrate.d.ts +11 -0
  311. package/dist/core/scoped-state/migrate.js +198 -0
  312. package/dist/core/scoped-state/paths.d.ts +9 -0
  313. package/dist/core/scoped-state/paths.js +27 -0
  314. package/dist/core/scoped-state/profiles.d.ts +12 -0
  315. package/dist/core/scoped-state/profiles.js +62 -0
  316. package/dist/core/scoped-state/providers.d.ts +16 -0
  317. package/dist/core/scoped-state/providers.js +8 -0
  318. package/dist/core/scoped-state/schema.d.ts +3 -0
  319. package/dist/core/scoped-state/schema.js +26 -0
  320. package/dist/core/scoped-state/settings.d.ts +28 -0
  321. package/dist/core/scoped-state/settings.js +82 -0
  322. package/dist/core/subscription-state.d.ts +29 -48
  323. package/dist/core/subscription-state.js +176 -712
  324. package/dist/core/worktree-close.js +4 -4
  325. package/dist/core/worktree-mutation-async.js +2 -2
  326. package/dist/core/worktree-sweep.js +11 -16
  327. package/dist/core/worktree.js +6 -12
  328. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  329. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  330. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  331. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  332. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  333. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +39 -15
  334. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  335. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  336. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  337. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  338. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  339. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  340. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  341. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  342. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  343. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +4 -1
  344. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  345. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  346. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  347. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  348. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  349. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +27 -41
  350. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  351. package/dist/daemon/api/bridge.js +1 -1
  352. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  353. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  354. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  355. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  356. package/dist/daemon/api/handlers/canvas.js +2 -44
  357. package/dist/daemon/api/handlers/daemon.js +41 -0
  358. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  359. package/dist/daemon/api/handlers/health.js +10 -5
  360. package/dist/daemon/api/handlers/human-requests.js +168 -188
  361. package/dist/daemon/api/handlers/human.js +3 -151
  362. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  363. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  364. package/dist/daemon/api/handlers/inbox.js +21 -120
  365. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  366. package/dist/daemon/api/handlers/messages.js +73 -15
  367. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  368. package/dist/daemon/api/handlers/nodes.js +7 -13
  369. package/dist/daemon/api/handlers/reviews.js +4 -29
  370. package/dist/daemon/api/map.d.ts +25 -3
  371. package/dist/daemon/api/map.js +84 -14
  372. package/dist/daemon/api/server.js +68 -9
  373. package/dist/daemon/control.d.ts +18 -0
  374. package/dist/daemon/cron/disposition.d.ts +2 -0
  375. package/dist/daemon/cron/disposition.js +8 -5
  376. package/dist/daemon/cron/settle.js +37 -13
  377. package/dist/daemon/cron/sinks.d.ts +18 -1
  378. package/dist/daemon/cron/sinks.js +47 -93
  379. package/dist/daemon/crtrd-cli.js +4 -19
  380. package/dist/daemon/crtrd.d.ts +2 -0
  381. package/dist/daemon/crtrd.js +206 -81
  382. package/dist/daemon/fleet.js +5 -6
  383. package/dist/daemon/human/finish.d.ts +12 -24
  384. package/dist/daemon/human/finish.js +88 -285
  385. package/dist/daemon/human/settle.d.ts +40 -0
  386. package/dist/daemon/human/settle.js +89 -0
  387. package/dist/daemon/human/sweep.d.ts +20 -5
  388. package/dist/daemon/human/sweep.js +56 -60
  389. package/dist/daemon/manage.d.ts +21 -16
  390. package/dist/daemon/manage.js +85 -51
  391. package/dist/daemon/messaging/node-message.d.ts +1 -1
  392. package/dist/daemon/messaging/node-message.js +9 -12
  393. package/dist/daemon/park-activity.js +3 -3
  394. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  395. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  396. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  397. package/dist/daemon/reconcilers/controller-death.js +6 -18
  398. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  399. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  400. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  401. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  402. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  403. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  404. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  405. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  406. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  407. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  408. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  409. package/dist/daemon/review/companion.js +5 -11
  410. package/dist/daemon/review/deliver.d.ts +1 -5
  411. package/dist/daemon/review/deliver.js +3 -52
  412. package/dist/daemon/review/finish.d.ts +2 -27
  413. package/dist/daemon/review/finish.js +79 -263
  414. package/dist/daemon/review/sweep.d.ts +3 -10
  415. package/dist/daemon/review/sweep.js +13 -131
  416. package/dist/daemon/startup-policy.d.ts +0 -5
  417. package/dist/daemon/startup-policy.js +0 -5
  418. package/dist/index.d.ts +2 -1
  419. package/dist/index.js +1 -1
  420. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  421. package/dist/migrations/__tests__/activation.test.js +9 -6
  422. package/dist/migrations/activation.d.ts +3 -2
  423. package/dist/migrations/activation.js +2 -4
  424. package/dist/migrations/corpus.d.ts +2 -5
  425. package/dist/migrations/corpus.js +9 -20
  426. package/dist/migrations/runner.d.ts +3 -1
  427. package/dist/migrations/runner.js +6 -2
  428. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  429. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  430. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  431. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  432. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  433. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  434. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  435. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  436. package/dist/pi-extensions/canvas-stophook.js +47 -31
  437. package/dist/shared/env.d.ts +1 -0
  438. package/dist/shared/env.js +3 -0
  439. package/package.json +1 -1
  440. package/runtime.lock.json +5 -5
  441. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  442. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  443. package/dist/clients/inbox/review-visibility.js +0 -19
  444. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  445. package/dist/commands/canvas-rebuild-index.js +0 -46
  446. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  447. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  448. package/dist/core/__tests__/human-deliver.test.js +0 -256
  449. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  450. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  451. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  452. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  453. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  454. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  455. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  456. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  457. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  458. package/dist/core/canvas/human-deliveries.js +0 -75
  459. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  460. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  461. package/dist/core/feed/passive.d.ts +0 -3
  462. package/dist/core/feed/passive.js +0 -53
  463. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  464. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  465. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  466. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  467. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  468. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  469. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  470. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  471. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  472. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  473. package/dist/core/review/ticket-filter.d.ts +0 -17
  474. package/dist/core/review/ticket-filter.js +0 -21
  475. package/dist/core/runtime/broker/passive.d.ts +0 -17
  476. package/dist/core/runtime/broker/passive.js +0 -44
  477. package/dist/core/runtime/busy.d.ts +0 -46
  478. package/dist/core/runtime/busy.js +0 -150
  479. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  480. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  481. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  482. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  483. package/dist/daemon/human/deliver-action.d.ts +0 -3
  484. package/dist/daemon/human/deliver-action.js +0 -9
  485. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  486. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  487. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  488. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  489. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  490. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  491. package/dist/daemon/startup-block-marker.d.ts +0 -25
  492. package/dist/daemon/startup-block-marker.js +0 -99
  493. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  494. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  495. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -3,10 +3,14 @@ import { spawnSync } from 'node:child_process';
3
3
  import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { dirname, join, relative } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
6
+ import { fileURLToPath, pathToFileURL } from 'node:url';
7
7
  import test from 'node:test';
8
8
  import { parse as parseYaml } from 'yaml';
9
9
  import { onDiskMigrationIdentity } from '../../../migrations/registry.js';
10
+ import { initializeFreshScopedStore } from '../../../core/scoped-state/migrate.js';
11
+ import { userStore } from '../../../core/scoped-state/paths.js';
12
+ import { writeRawSettings } from '../../../core/scoped-state/settings.js';
13
+ import { createSourceDaemonFixture, startSourceDaemon, waitForHealth, } from '../../../daemon/__tests__/helpers/source-daemon.js';
10
14
  const HERE = dirname(fileURLToPath(import.meta.url));
11
15
  const CLI = join(HERE, '../../../cli.ts');
12
16
  const TSX_LOADER = import.meta.resolve('tsx/esm');
@@ -49,8 +53,17 @@ function write(path, content) {
49
53
  }
50
54
  /** Store keys and target ids are realpaths, so the fixture must be one too:
51
55
  * macOS hands `mkdtemp` a `/var/folders` path whose realpath is `/private/...`. */
56
+ const migrateCanvasHomes = new Map();
52
57
  function fixtureHome(prefix) {
53
- return realpathSync(mkdtempSync(join(tmpdir(), prefix)));
58
+ const home = realpathSync(mkdtempSync(join(tmpdir(), prefix)));
59
+ initializeFreshScopedStore({ kind: 'user', root: join(home, '.crouter') });
60
+ const root = mkdtempSync(join(tmpdir(), 'crtr-sys-migrate-daemon-'));
61
+ migrateCanvasHomes.set(home, join(root, 'canvas'));
62
+ return home;
63
+ }
64
+ function writeUserConfig(home, raw) {
65
+ const store = userStore(join(home, '.crouter'));
66
+ writeRawSettings({ store, key: 'user', kind: 'user', root: store.root }, raw);
54
67
  }
55
68
  /** Fixture git, with identity and signing pinned so the ambient user's global
56
69
  * config cannot fail a commit this test needs. */
@@ -66,8 +79,11 @@ function initRepo(dir) {
66
79
  git(dir, 'add', 'README.md');
67
80
  git(dir, 'commit', '-m', 'fixture');
68
81
  }
82
+ function canvasHomeFor(home) {
83
+ return migrateDaemons.get(home)?.fixture.canvasHome ?? migrateCanvasHomes.get(home) ?? join(home, '.crouter', 'canvas');
84
+ }
69
85
  function markerPath(home) {
70
- return join(home, '.crouter', 'canvas', 'on-disk-migrations.json');
86
+ return join(canvasHomeFor(home), 'on-disk-migrations.json');
71
87
  }
72
88
  function readMarker(home) {
73
89
  return JSON.parse(readFileSync(markerPath(home), 'utf8'));
@@ -112,11 +128,64 @@ function migrateEnv(home, profileId) {
112
128
  if (key.startsWith('CRTR_'))
113
129
  delete env[key];
114
130
  }
131
+ env['CRTR_HOME'] = canvasHomeFor(home);
115
132
  env['CRTR_PROFILE_ID'] = profileId;
133
+ env['CRTR_NO_DAEMON_AUTOSTART'] = '1';
116
134
  env['CRTR_NO_AUTO_INIT'] = '1';
117
135
  env['CRTR_NO_AUTO_UPDATE'] = '1';
118
136
  return env;
119
137
  }
138
+ const migrateDaemons = new Map();
139
+ async function startMigrateDaemon(home) {
140
+ const canvasHome = migrateCanvasHomes.get(home);
141
+ assert.ok(canvasHome, `fixture has no canvas home: ${home}`);
142
+ const fixture = await createSourceDaemonFixture({
143
+ root: dirname(canvasHome),
144
+ home,
145
+ cwd: home,
146
+ canvasHome,
147
+ });
148
+ const process = startSourceDaemon(fixture, { CRTR_PROFILE_ID: 'root-00000000' });
149
+ try {
150
+ await waitForHealth(process, fixture.tcpPort);
151
+ }
152
+ catch (error) {
153
+ await process.stop();
154
+ fixture.dispose();
155
+ throw error;
156
+ }
157
+ migrateDaemons.set(home, { fixture, process });
158
+ }
159
+ async function stopMigrateDaemon(home) {
160
+ const daemon = migrateDaemons.get(home);
161
+ if (daemon === undefined)
162
+ return;
163
+ migrateDaemons.delete(home);
164
+ const exit = await daemon.process.stop();
165
+ daemon.fixture.dispose();
166
+ migrateCanvasHomes.delete(home);
167
+ assert.deepEqual(exit, { code: 0, signal: null }, `migration daemon must stop cleanly; stderr:\n${daemon.process.stderr()}`);
168
+ }
169
+ function seedNodeMemoryTarget(home, target) {
170
+ const canvas = pathToFileURL(join(HERE, '../../../core/canvas/canvas.ts')).href;
171
+ const program = `
172
+ import { createNode } from ${JSON.stringify(canvas)};
173
+ const target = JSON.parse(process.argv[1]);
174
+ createNode({
175
+ node_id: target.nodeId,
176
+ name: target.nodeId,
177
+ created: new Date().toISOString(),
178
+ cwd: target.cwd,
179
+ kind: 'developer',
180
+ mode: 'base',
181
+ lifecycle: 'terminal',
182
+ status: 'done',
183
+ profile_id: target.profileId,
184
+ });
185
+ `;
186
+ const result = spawnSync(process.execPath, ['--conditions=crtr-src', '--import', TSX_LOADER, '--input-type=module', '--eval', program, JSON.stringify(target)], { cwd: home, env: migrateEnv(home, target.profileId ?? 'root-00000000'), encoding: 'utf8' });
187
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
188
+ }
120
189
  function runMigrate(home, opts = {}) {
121
190
  const result = spawnSync(process.execPath, [
122
191
  '--conditions=crtr-src',
@@ -186,7 +255,7 @@ function manifest(name, projects) {
186
255
  last_used_at: null,
187
256
  });
188
257
  }
189
- test('sys migrate converges manifests, documents, and the whole repository-root corpus', () => {
258
+ test('sys migrate converges manifests, documents, and the whole repository-root corpus', async () => {
190
259
  const home = fixtureHome('crtr-sys-migrate-');
191
260
  const userRoot = join(home, '.crouter');
192
261
  const rootProfile = join(userRoot, 'profiles', 'root-00000000');
@@ -200,7 +269,7 @@ test('sys migrate converges manifests, documents, and the whole repository-root
200
269
  const embeddedPluginRoot = join(nestedOnlyProject, 'embedded', '.crouter', 'plugins', 'embedded-memory');
201
270
  const disabledPluginRoot = join(userRoot, 'plugins', 'disabled-memory');
202
271
  const nodeId = 'node-aaaa1111';
203
- const nodeStore = join(userRoot, 'canvas', 'nodes', nodeId, 'context', 'memory');
272
+ const nodeStore = join(canvasHomeFor(home), 'nodes', nodeId, 'context', 'memory');
204
273
  const userDoc = join(userRoot, 'memory', 'user-link.md');
205
274
  const rootDoc = join(rootProfile, 'memory', 'root.md');
206
275
  const appletDoc = join(appletProfile, 'memory', 'identity.md');
@@ -215,6 +284,8 @@ test('sys migrate converges manifests, documents, and the whole repository-root
215
284
  const disabledPluginDoc = join(disabledPluginRoot, 'memory', 'guide.md');
216
285
  const nodeDoc = join(nodeStore, 'note.md');
217
286
  try {
287
+ await startMigrateDaemon(home);
288
+ seedNodeMemoryTarget(home, { nodeId, cwd: appletProject, profileId: 'applet-deadbeef' });
218
289
  // Root's string entries migrate to `preview`; every other profile's to
219
290
  // `content`. The applet manifest is mixed: its already-current entry must
220
291
  // survive untouched while the string beside it converts.
@@ -225,7 +296,7 @@ test('sys migrate converges manifests, documents, and the whole repository-root
225
296
  write(userDoc, `${OLD_DOC}\nSee [[user/scoped]].\n`);
226
297
  write(join(userRoot, 'memory', 'scoped', 'INDEX.md'), CURRENT_DOC);
227
298
  // Root profile store: an INDEX spelling to collapse, an unqualified project
228
- // name to qualify, and a name its own target stack cannot see.
299
+ // name outside its own target stack, and a name its own target stack cannot see.
229
300
  write(rootDoc, `${OLD_DOC}\nSee [[notes/INDEX]], [[project]], and [[shared]].\n`);
230
301
  write(join(rootProfile, 'memory', 'notes', 'INDEX.md'), CURRENT_DOC);
231
302
  write(appletDoc, `${OLD_DOC}\nSee [[shared]].\n`);
@@ -267,12 +338,12 @@ See [[project]].
267
338
  write(embeddedPluginDoc, OLD_DOC);
268
339
  // A DISABLED plugin: no target mounts it, so its references resolve against
269
340
  // its own store alone — and it is still migrated.
270
- write(join(userRoot, 'config.json'), JSON.stringify({ plugins: { 'disabled-memory': { enabled: false } } }));
341
+ writeUserConfig(home, { plugins: { 'disabled-memory': { enabled: false } } });
271
342
  write(join(disabledPluginRoot, '.crouter-plugin', 'plugin.json'), JSON.stringify({ name: 'disabled-memory', version: '1.0.0' }));
272
343
  write(join(disabledPluginRoot, 'memory', 'notes', 'INDEX.md'), CURRENT_DOC);
273
344
  write(disabledPluginDoc, `${CURRENT_DOC}\nSee [[disabled-memory/notes/INDEX]].\n`);
274
- // A node store plus the durable meta.json the corpus reads with no daemon.
275
- write(join(userRoot, 'canvas', 'nodes', nodeId, 'meta.json'), JSON.stringify({ node_id: nodeId, cwd: appletProject, profile_id: 'applet-deadbeef', kind: 'developer' }));
345
+ // A node store whose SQL row was seeded before its documents. No meta.json
346
+ // exists: corpus discovery must use the daemon-owned roster.
276
347
  write(nodeDoc, `${CURRENT_DOC}\nSee [[project]].\n`);
277
348
  // Reached by --dir only: no manifest names it and it is under no discovered
278
349
  // project root. Its `name:` is the FIRST frontmatter key, so the removal and
@@ -281,7 +352,7 @@ See [[project]].
281
352
  write(join(extraProject, '.crouter', 'memory', 'INDEX.md'), `---\nname: extra-dir-project\nkind: knowledge\n---\n# Extra Dir Project\n`);
282
353
  write(join(extraProject, '.crouter', 'memory', 'topic.md'), CURRENT_DOC);
283
354
  const invisible = migrateJson(home, { dryRun: true });
284
- assert.equal(existsSync(markerPath(home)), false);
355
+ assert.deepEqual(readMarker(home), onDiskMigrationIdentity(), 'the daemon completed its empty-corpus startup before fixture bytes were seeded');
285
356
  assert.equal(invisible.discovered_stores.some((row) => row.store.startsWith(extraProject)), false, 'the --dir project must be outside the ambient discovery frontier');
286
357
  const output = migrateJson(home, { dirs: [extraProject] });
287
358
  assert.deepEqual(readMarker(home), onDiskMigrationIdentity());
@@ -301,8 +372,6 @@ See [[project]].
301
372
  assert.doesNotMatch(migrated, /system-prompt-visibility|file-read-visibility/);
302
373
  assert.match(migrated, /surfaces:\n - on: boot\n at: content/);
303
374
  }
304
- // Durable node metadata, read with no daemon.
305
- assert.deepEqual(output.node_metadata, { available: true, nodes: 1 });
306
375
  const nodeTarget = output.discovered_targets.find((row) => row.kind === 'node');
307
376
  assert.equal(nodeTarget?.node_id, nodeId);
308
377
  assert.equal(nodeTarget?.origin, nodeId);
@@ -379,7 +448,7 @@ surfaces:
379
448
  // completion.
380
449
  const rootDocText = readFileSync(rootDoc, 'utf8');
381
450
  assert.match(rootDocText, /\[\[notes\]\]/);
382
- assert.match(rootDocText, /\[\[applet-project\/project\]\]/);
451
+ assert.match(rootDocText, /\[\[project\]\]/);
383
452
  assert.match(rootDocText, /\[\[shared\]\]/);
384
453
  assert.match(readFileSync(userDoc, 'utf8'), /\[\[user\/scoped\]\]/);
385
454
  assert.match(readFileSync(appletDoc, 'utf8'), /\[\[shared\]\]/);
@@ -442,6 +511,7 @@ See [[applet-project/project]].
442
511
  assertTreeUnchanged(converged, snapshotTree(home));
443
512
  }
444
513
  finally {
514
+ await stopMigrateDaemon(home);
445
515
  rmSync(home, { recursive: true, force: true });
446
516
  }
447
517
  });
@@ -471,10 +541,12 @@ function seedConvergenceFixture(home, start) {
471
541
  write(join(nestedOwner, '.crouter', 'memory', 'INDEX.md'), supersededSyntheticIndex('pkg'));
472
542
  write(markerPath(home), JSON.stringify([{ id: '003-workspace-qualified-memory-identity', lane: 'corpus' }]));
473
543
  }
474
- test('pre-003 and superseded-003 corpora converge to the same bytes and the same current marker', () => {
544
+ test('pre-003 and superseded-003 corpora converge to the same bytes and the same current marker', async () => {
475
545
  const preHome = fixtureHome('crtr-sys-migrate-conv-pre-');
476
546
  const oldHome = fixtureHome('crtr-sys-migrate-conv-old-');
477
547
  try {
548
+ await startMigrateDaemon(preHome);
549
+ await startMigrateDaemon(oldHome);
478
550
  seedConvergenceFixture(preHome, 'pre-003');
479
551
  seedConvergenceFixture(oldHome, 'superseded-003');
480
552
  const fromPre = migrateJson(preHome);
@@ -519,17 +591,20 @@ test('pre-003 and superseded-003 corpora converge to the same bytes and the same
519
591
  assert.equal(prefixRow(fromPre, join(preHome, 'conv-project', 'pkg')).nested_declaration_action, 'none');
520
592
  }
521
593
  finally {
594
+ await stopMigrateDaemon(preHome);
595
+ await stopMigrateDaemon(oldHome);
522
596
  rmSync(preHome, { recursive: true, force: true });
523
597
  rmSync(oldHome, { recursive: true, force: true });
524
598
  }
525
599
  });
526
- test('a repository declaration is kept when declared, derived from the root guide, or created when no root index exists', () => {
600
+ test('a repository declaration is kept when declared, derived from the root guide, or created when no root index exists', async () => {
527
601
  const home = fixtureHome('crtr-sys-migrate-decisions-');
528
602
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
529
603
  const kept = join(home, 'kept-project');
530
604
  const derived = join(home, 'derived-project');
531
605
  const created = join(home, 'created-project');
532
606
  try {
607
+ await startMigrateDaemon(home);
533
608
  write(join(rootProfile, 'profile.json'), manifest('root', [kept, derived, created].map((path) => ({ path, memory: 'content' }))));
534
609
  write(join(kept, '.crouter', 'memory', 'INDEX.md'), `---\nnamespace: kept-ns\nkind: knowledge\n---\n# Kept\n`);
535
610
  write(join(kept, '.crouter', 'memory', 'note.md'), CURRENT_DOC);
@@ -563,15 +638,17 @@ This project's memory store. Every document here answers at \`created-project/<l
563
638
  assert.equal(migrateJson(home).changed, 0);
564
639
  }
565
640
  finally {
641
+ await stopMigrateDaemon(home);
566
642
  rmSync(home, { recursive: true, force: true });
567
643
  }
568
644
  });
569
- test('a git repository whose only stores are descendants gets its repository-root declaration created', () => {
645
+ test('a git repository whose only stores are descendants gets its repository-root declaration created', async () => {
570
646
  const home = fixtureHome('crtr-sys-migrate-git-descendant-');
571
647
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
572
648
  const repo = join(home, 'pkg-repo');
573
649
  const nestedOwner = join(repo, 'pkg');
574
650
  try {
651
+ await startMigrateDaemon(home);
575
652
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: repo, memory: 'content' }]));
576
653
  initRepo(repo);
577
654
  write(join(nestedOwner, '.crouter', 'memory', 'note.md'), CURRENT_DOC);
@@ -590,19 +667,22 @@ test('a git repository whose only stores are descendants gets its repository-roo
590
667
  assert.deepEqual(output.store_prefixes.map((row) => [row.owner_dir, row.owner_relative_path, row.effective_prefix]), [[nestedOwner, 'pkg', 'pkg-repo/pkg']]);
591
668
  assert.match(readFileSync(join(repo, '.crouter', 'memory', 'INDEX.md'), 'utf8'), /^---\nnamespace: "pkg-repo"\n/);
592
669
  assert.equal(output.renames.some((row) => row.from === 'note' && row.to === 'pkg-repo/pkg/note'), true, JSON.stringify(output.renames));
593
- assert.equal(migrateJson(home).changed, 0);
670
+ const second = migrateJson(home);
671
+ assert.equal(second.changed, 0, JSON.stringify(second.applied));
594
672
  }
595
673
  finally {
674
+ await stopMigrateDaemon(home);
596
675
  rmSync(home, { recursive: true, force: true });
597
676
  }
598
677
  });
599
- test('linked worktrees agree on one declaration, and a linked root missing one is filled from it', () => {
678
+ test('linked worktrees agree on one declaration, and a linked root missing one is filled from it', async () => {
600
679
  for (const mode of ['agree', 'fill']) {
601
680
  const home = fixtureHome(`crtr-sys-migrate-worktree-${mode}-`);
602
681
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
603
682
  const main = join(home, 'shared-main');
604
683
  const linked = join(home, 'shared-linked');
605
684
  try {
685
+ await startMigrateDaemon(home);
606
686
  write(join(rootProfile, 'profile.json'), manifest('root', [main, linked].map((path) => ({ path, memory: 'content' }))));
607
687
  initRepo(main);
608
688
  git(main, 'worktree', 'add', linked, '-b', 'linked');
@@ -628,16 +708,18 @@ test('linked worktrees agree on one declaration, and a linked root missing one i
628
708
  assert.equal(migrateJson(home).changed, 0);
629
709
  }
630
710
  finally {
711
+ await stopMigrateDaemon(home);
631
712
  rmSync(home, { recursive: true, force: true });
632
713
  }
633
714
  }
634
715
  });
635
- test('linked worktree roots declaring different namespaces block the corpus', () => {
716
+ test('linked worktree roots declaring different namespaces block the corpus', async () => {
636
717
  const home = fixtureHome('crtr-sys-migrate-worktree-conflict-');
637
718
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
638
719
  const main = join(home, 'shared-main');
639
720
  const linked = join(home, 'shared-linked');
640
721
  try {
722
+ await startMigrateDaemon(home);
641
723
  write(join(rootProfile, 'profile.json'), manifest('root', [main, linked].map((path) => ({ path, memory: 'content' }))));
642
724
  initRepo(main);
643
725
  git(main, 'worktree', 'add', linked, '-b', 'linked');
@@ -656,10 +738,11 @@ test('linked worktree roots declaring different namespaces block the corpus', ()
656
738
  assertTreeUnchanged(before, snapshotTree(main));
657
739
  }
658
740
  finally {
741
+ await stopMigrateDaemon(home);
659
742
  rmSync(home, { recursive: true, force: true });
660
743
  }
661
744
  });
662
- test('a nested declaration is removed, an exact synthetic index is deleted, and a substantive one is kept', () => {
745
+ test('a nested declaration is removed, an exact synthetic index is deleted, and a substantive one is kept', async () => {
663
746
  const home = fixtureHome('crtr-sys-migrate-nested-declarations-');
664
747
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
665
748
  const project = join(home, 'nest-repo');
@@ -668,6 +751,7 @@ test('a nested declaration is removed, an exact synthetic index is deleted, and
668
751
  const syntheticIndex = join(syntheticOwner, '.crouter', 'memory', 'INDEX.md');
669
752
  const substantiveIndex = join(substantiveOwner, '.crouter', 'memory', 'INDEX.md');
670
753
  try {
754
+ await startMigrateDaemon(home);
671
755
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: project, memory: 'content' }]));
672
756
  write(join(project, '.crouter', 'memory', 'INDEX.md'), `---\nnamespace: nest-repo\nkind: knowledge\n---\n# Nest Repo\n`);
673
757
  write(syntheticIndex, supersededSyntheticIndex('syn'));
@@ -684,16 +768,18 @@ test('a nested declaration is removed, an exact synthetic index is deleted, and
684
768
  assert.equal(migrateJson(home).changed, 0);
685
769
  }
686
770
  finally {
771
+ await stopMigrateDaemon(home);
687
772
  rmSync(home, { recursive: true, force: true });
688
773
  }
689
774
  });
690
- test('a rootless repository takes its front door from the one legacy guide that owns it', () => {
775
+ test('a rootless repository takes its front door from the one legacy guide that owns it', async () => {
691
776
  const home = fixtureHome('crtr-sys-migrate-front-door-');
692
777
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
693
778
  const project = join(home, 'fd-root');
694
779
  const guide = join(project, '.crouter', 'memory', 'guide.md');
695
780
  const rootIndex = join(project, '.crouter', 'memory', 'INDEX.md');
696
781
  try {
782
+ await startMigrateDaemon(home);
697
783
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: project, memory: 'content' }]));
698
784
  write(guide, `---
699
785
  kind: knowledge
@@ -751,10 +837,11 @@ The authored body, preserved byte for byte.
751
837
  assertTreeUnchanged(converged, snapshotTree(home));
752
838
  }
753
839
  finally {
840
+ await stopMigrateDaemon(home);
754
841
  rmSync(home, { recursive: true, force: true });
755
842
  }
756
843
  });
757
- test('repository-relative ordering prefixes do not block migration and derive canonical owner paths', () => {
844
+ test('repository-relative ordering prefixes do not block migration and derive canonical owner paths', async () => {
758
845
  const home = fixtureHome('crtr-sys-migrate-ordering-prefix-');
759
846
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
760
847
  const repository = join(home, 'ordered-repository');
@@ -762,6 +849,7 @@ test('repository-relative ordering prefixes do not block migration and derive ca
762
849
  const rootIndex = join(repository, '.crouter', 'memory', 'INDEX.md');
763
850
  const nestedDoc = join(nestedOwner, '.crouter', 'memory', 'guide.md');
764
851
  try {
852
+ await startMigrateDaemon(home);
765
853
  initRepo(repository);
766
854
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: repository, memory: 'content' }]));
767
855
  write(rootIndex, '---\nnamespace: acme\nkind: knowledge\n---\n# Repository\n');
@@ -772,10 +860,11 @@ test('repository-relative ordering prefixes do not block migration and derive ca
772
860
  assert.equal(prefixRow(output, nestedOwner).effective_prefix, 'acme/api');
773
861
  }
774
862
  finally {
863
+ await stopMigrateDaemon(home);
775
864
  rmSync(home, { recursive: true, force: true });
776
865
  }
777
866
  });
778
- test('the legacy front door is found in a nested store of the same repository, and a half-done transfer finishes', () => {
867
+ test('the legacy front door is found in a nested store of the same repository, and a half-done transfer finishes', async () => {
779
868
  const home = fixtureHome('crtr-sys-migrate-front-door-nested-');
780
869
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
781
870
  const project = join(home, 'fd-nested');
@@ -784,6 +873,7 @@ test('the legacy front door is found in a nested store of the same repository, a
784
873
  const guide = join(nestedOwner, '.crouter', 'memory', 'guide.md');
785
874
  const legacyNamed = join(nestedOwner, '.crouter', 'memory', 'deep-note.md');
786
875
  try {
876
+ await startMigrateDaemon(home);
787
877
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: project, memory: 'content' }]));
788
878
  write(rootIndex, `---\nnamespace: fd-nested\nkind: knowledge\n---\n# FD Nested\n`);
789
879
  write(guide, `---
@@ -850,10 +940,11 @@ surfaces:
850
940
  assert.doesNotMatch(readFileSync(guide, 'utf8'), /workspace-open/);
851
941
  }
852
942
  finally {
943
+ await stopMigrateDaemon(home);
853
944
  rmSync(home, { recursive: true, force: true });
854
945
  }
855
946
  });
856
- test('two legacy front doors, and a root workspace-open that is not at content, each block the corpus', () => {
947
+ test('two legacy front doors, and a root workspace-open that is not at content, each block the corpus', async () => {
857
948
  for (const shape of ['two-leaves', 'root-predicate']) {
858
949
  const home = fixtureHome(`crtr-sys-migrate-front-door-${shape}-`);
859
950
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
@@ -866,6 +957,7 @@ test('two legacy front doors, and a root workspace-open that is not at content,
866
957
  match: "./**"
867
958
  at: content`;
868
959
  try {
960
+ await startMigrateDaemon(home);
869
961
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: project, memory: 'content' }]));
870
962
  if (shape === 'two-leaves') {
871
963
  write(rootIndex, `---\nnamespace: fd-blocked\nkind: knowledge\n---\n# FD Blocked\n`);
@@ -896,16 +988,18 @@ test('two legacy front doors, and a root workspace-open that is not at content,
896
988
  assertTreeUnchanged(before, snapshotTree(project));
897
989
  }
898
990
  finally {
991
+ await stopMigrateDaemon(home);
899
992
  rmSync(home, { recursive: true, force: true });
900
993
  }
901
994
  }
902
995
  });
903
- test('a leaf carrying both the legacy front door and a translated route keeps every retained authored byte', () => {
996
+ test('a leaf carrying both the legacy front door and a translated route keeps every retained authored byte', async () => {
904
997
  const home = fixtureHome('crtr-sys-migrate-front-door-compose-');
905
998
  const rootProfile = join(home, '.crouter', 'profiles', 'root-00000000');
906
999
  const project = join(home, 'compose-repo');
907
1000
  const workspaceDoc = join(project, '.crouter', 'memory', 'workspace.md');
908
1001
  try {
1002
+ await startMigrateDaemon(home);
909
1003
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: project, memory: 'content' }]));
910
1004
  // Pre-003 identity: nothing declares a namespace, so `topics/*` still names
911
1005
  // the store's own documents and must be translated in the same pass that
@@ -966,15 +1060,17 @@ Body bytes that must survive both transforms.
966
1060
  assert.equal(migrateJson(home).changed, 0);
967
1061
  }
968
1062
  finally {
1063
+ await stopMigrateDaemon(home);
969
1064
  rmSync(home, { recursive: true, force: true });
970
1065
  }
971
1066
  });
972
- test('an unreadable profile manifest blocks forced completion and remains in skipped output', () => {
1067
+ test('an unreadable profile manifest blocks forced completion and remains in skipped output', async () => {
973
1068
  const home = fixtureHome('crtr-sys-migrate-unreadable-profile-');
974
1069
  const userRoot = join(home, '.crouter');
975
1070
  const profileId = 'bad-deadbeef';
976
1071
  const profilePath = join(userRoot, 'profiles', profileId, 'profile.json');
977
1072
  try {
1073
+ await startMigrateDaemon(home);
978
1074
  write(profilePath, '{not json');
979
1075
  write(markerPath(home), JSON.stringify(onDiskMigrationIdentity()));
980
1076
  const payload = migrateBlocked(home);
@@ -991,16 +1087,18 @@ test('an unreadable profile manifest blocks forced completion and remains in ski
991
1087
  assert.equal(existsSync(markerPath(home)), false);
992
1088
  }
993
1089
  finally {
1090
+ await stopMigrateDaemon(home);
994
1091
  rmSync(home, { recursive: true, force: true });
995
1092
  }
996
1093
  });
997
- test('sys migrate blocks the whole corpus when one link names different documents per target, and writes nothing', () => {
1094
+ test('sys migrate blocks the whole corpus when one link names different documents per target, and writes nothing', async () => {
998
1095
  const home = fixtureHome('crtr-sys-migrate-divergent-');
999
1096
  const userRoot = join(home, '.crouter');
1000
1097
  const rootProfile = join(userRoot, 'profiles', 'root-00000000');
1001
1098
  const alpha = join(home, 'alpha-project');
1002
1099
  const beta = join(home, 'beta-project');
1003
1100
  try {
1101
+ await startMigrateDaemon(home);
1004
1102
  // Already current for the manifest and document lanes: the only thing this
1005
1103
  // run could write is the corpus cut, so "nothing was written" is provable
1006
1104
  // byte for byte.
@@ -1045,13 +1143,13 @@ test('sys migrate blocks the whole corpus when one link names different document
1045
1143
  assert.ok(payload.received.renames.some((row) => row.store_root === store && row.to.startsWith(`${relative(home, project)}/`)), JSON.stringify(payload.received.renames));
1046
1144
  }
1047
1145
  assert.ok(Array.isArray(payload.received.references));
1048
- assert.equal(payload.received.node_metadata.available, true);
1049
1146
  }
1050
1147
  finally {
1148
+ await stopMigrateDaemon(home);
1051
1149
  rmSync(home, { recursive: true, force: true });
1052
1150
  }
1053
1151
  });
1054
- test('a corpus blocker never claims nothing was written when the earlier lanes already wrote', () => {
1152
+ test('a corpus blocker never claims nothing was written when the earlier lanes already wrote', async () => {
1055
1153
  const home = fixtureHome('crtr-sys-migrate-earlier-writes-');
1056
1154
  const userRoot = join(home, '.crouter');
1057
1155
  const rootProfile = join(userRoot, 'profiles', 'root-00000000');
@@ -1059,6 +1157,7 @@ test('a corpus blocker never claims nothing was written when the earlier lanes a
1059
1157
  const beta = join(home, 'beta-project');
1060
1158
  const userDoc = join(userRoot, 'memory', 'pointer.md');
1061
1159
  try {
1160
+ await startMigrateDaemon(home);
1062
1161
  // A PRE-migration manifest and an old-format document, so the manifest and
1063
1162
  // convergent lanes both write before the corpus lane plans — and then one
1064
1163
  // `[[guide]]` naming a different document per target blocks that lane.
@@ -1086,10 +1185,11 @@ test('a corpus blocker never claims nothing was written when the earlier lanes a
1086
1185
  assertTreeUnchanged(projectsBefore, snapshotTree(alpha));
1087
1186
  }
1088
1187
  finally {
1188
+ await stopMigrateDaemon(home);
1089
1189
  rmSync(home, { recursive: true, force: true });
1090
1190
  }
1091
1191
  });
1092
- test('a verification failure after both write phases reports the rewrites that are on disk', () => {
1192
+ test('a verification failure after both write phases reports the rewrites that are on disk', async () => {
1093
1193
  const home = fixtureHome('crtr-sys-migrate-verification-');
1094
1194
  const userRoot = join(home, '.crouter');
1095
1195
  const rootProfile = join(userRoot, 'profiles', 'root-00000000');
@@ -1097,6 +1197,7 @@ test('a verification failure after both write phases reports the rewrites that a
1097
1197
  const projectIndex = join(project, '.crouter', 'memory', 'INDEX.md');
1098
1198
  const strayDoc = join(project, '.crouter', 'memory', 'stray.md');
1099
1199
  try {
1200
+ await startMigrateDaemon(home);
1100
1201
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: project, memory: 'content' }]));
1101
1202
  write(projectIndex, `---\nkind: knowledge\n---\n# Verify Project\n`);
1102
1203
  // `namespace:` on a NON-root document: not an identity field this lane
@@ -1130,22 +1231,24 @@ surfaces:
1130
1231
  `);
1131
1232
  }
1132
1233
  finally {
1234
+ await stopMigrateDaemon(home);
1133
1235
  rmSync(home, { recursive: true, force: true });
1134
1236
  }
1135
1237
  });
1136
- test('a nested store reached only through a node target inherits the ancestor repository declaration', () => {
1238
+ test('a nested store reached only through a node target inherits the ancestor repository declaration', async () => {
1137
1239
  const home = fixtureHome('crtr-sys-migrate-node-nested-');
1138
1240
  const userRoot = join(home, '.crouter');
1139
1241
  const rootProfile = join(userRoot, 'profiles', 'root-00000000');
1140
1242
  const nodeId = 'node-bbbb2222';
1141
- const nodeStore = join(userRoot, 'canvas', 'nodes', nodeId, 'context', 'memory');
1243
+ const nodeStore = join(canvasHomeFor(home), 'nodes', nodeId, 'context', 'memory');
1142
1244
  const nodeProject = join(home, 'node-cwd-project');
1143
1245
  const nestedOwner = join(nodeProject, 'nested');
1144
1246
  try {
1145
- // No project in any manifest and none above the cwd: the node's durable
1146
- // metadata is the ONLY path to this project root.
1247
+ await startMigrateDaemon(home);
1248
+ seedNodeMemoryTarget(home, { nodeId, cwd: nodeProject, profileId: 'root-00000000' });
1249
+ // No project in any manifest and none above the cwd: the daemon's SQL row
1250
+ // is the ONLY path to this project root.
1147
1251
  write(join(rootProfile, 'profile.json'), manifest('root', []));
1148
- write(join(userRoot, 'canvas', 'nodes', nodeId, 'meta.json'), JSON.stringify({ node_id: nodeId, cwd: nodeProject, profile_id: 'root-00000000', kind: 'developer' }));
1149
1252
  write(join(nodeStore, 'note.md'), CURRENT_DOC);
1150
1253
  write(join(nodeProject, '.crouter', 'memory', 'INDEX.md'), `---\nkind: knowledge\n---\n# Node Cwd Project\n`);
1151
1254
  write(join(nestedOwner, '.crouter', 'memory', 'topic.md'), CURRENT_DOC);
@@ -1165,16 +1268,18 @@ test('a nested store reached only through a node target inherits the ancestor re
1165
1268
  assert.equal(output.discovered_targets.some((row) => row.kind === 'project' && row.origin === nestedOwner), true);
1166
1269
  }
1167
1270
  finally {
1271
+ await stopMigrateDaemon(home);
1168
1272
  rmSync(home, { recursive: true, force: true });
1169
1273
  }
1170
1274
  });
1171
- test('a memory-read route translates through an INDEX collapse and a root-plus-descendant split', () => {
1275
+ test('a memory-read route translates through an INDEX collapse and a root-plus-descendant split', async () => {
1172
1276
  const home = fixtureHome('crtr-sys-migrate-routes-');
1173
1277
  const userRoot = join(home, '.crouter');
1174
1278
  const rootProfile = join(userRoot, 'profiles', 'root-00000000');
1175
1279
  const project = join(home, 'route-project');
1176
1280
  const carrier = join(project, '.crouter', 'memory', 'carrier.md');
1177
1281
  try {
1282
+ await startMigrateDaemon(home);
1178
1283
  write(join(rootProfile, 'profile.json'), manifest('root', [{ path: project, memory: 'content' }]));
1179
1284
  write(join(project, '.crouter', 'memory', 'INDEX.md'), `---\nkind: knowledge\n---\n# Route Project\n`);
1180
1285
  // `topics/INDEX` collapses onto `route-project/topics`, and `topics/**`
@@ -1219,6 +1324,7 @@ surfaces:
1219
1324
  assert.deepEqual(second.references, []);
1220
1325
  }
1221
1326
  finally {
1327
+ await stopMigrateDaemon(home);
1222
1328
  rmSync(home, { recursive: true, force: true });
1223
1329
  }
1224
1330
  });
@@ -7,6 +7,8 @@ import { test } from 'node:test';
7
7
  import assert from 'node:assert/strict';
8
8
  import { isNowEligibleFault } from '../node-lifecycle-revive.js';
9
9
  import { faultNeedsYou } from '../../core/canvas/status-glyph.js';
10
+ // The gate reads the fault the daemon projected onto the node, so the fixture
11
+ // builds the projected shape, not the wider parse-time one.
10
12
  function fault(over) {
11
13
  return {
12
14
  link: 'pi→provider',
@@ -2,7 +2,8 @@ import { CrtrClient } from '../api/index.js';
2
2
  import type { CanvasSource } from '../core/canvas/source.js';
3
3
  import type { NodeStatus, SubscriptionRef } from '../core/canvas/types.js';
4
4
  import type { Scope } from '../types.js';
5
- import type { NodeDetailDTO, NodeSummaryDTO } from '../api/dto/nodes.js';
5
+ import type { NodeDetailDTO } from '../api/dto/nodes.js';
6
+ import type { NodeMeta, NodeRow } from '../core/canvas/types.js';
6
7
  /** Rethrow an `ApiError` as the clean structured CLI error agents parse
7
8
  * (`core/io` `handle`) — instead of the generic "internal bug" stderr path a
8
9
  * raw non-`CrtrError` throw would take. The mapping itself lives in ONE place
@@ -60,11 +61,11 @@ export declare class ApiCanvasSource implements CanvasSource {
60
61
  /** Map a 404 from the API to the interface's `null` "not found" contract;
61
62
  * re-throw everything else. */
62
63
  private orNull;
63
- getNode(nodeId: string): Promise<NodeDetailDTO | null>;
64
- getRow(nodeId: string): Promise<NodeDetailDTO | null>;
64
+ getNode(nodeId: string): Promise<NodeMeta | null>;
65
+ getRow(nodeId: string): Promise<NodeRow | null>;
65
66
  listNodes(filter?: {
66
67
  status?: NodeStatus | NodeStatus[];
67
- }): Promise<NodeSummaryDTO[]>;
68
+ }): Promise<NodeRow[]>;
68
69
  subscriptionsOf(subscriber: string): Promise<SubscriptionRef[]>;
69
70
  subscribersOf(publisher: string): Promise<SubscriptionRef[]>;
70
71
  /** BFS over the active `subscribes_to` edges from `root` (its transitive view),
@@ -19,12 +19,19 @@ import { join } from 'node:path';
19
19
  import { CrtrClient, ApiError } from '../api/index.js';
20
20
  import { apiErrorToCliError } from '../core/io.js';
21
21
  import { autostartDisabled, ensureDaemon } from '../daemon/manage.js';
22
- import { daemonStartupBlocked } from '../daemon/startup-block-marker.js';
23
22
  import { DAEMON_STARTUP_WINDOW_MS } from '../daemon/startup-policy.js';
24
23
  import { readState } from '../core/config.js';
25
24
  import { resolveRemoteCanvasTarget } from '../core/remote-canvas-target.js';
26
25
  import { RemoteCanvasSource } from '../core/canvas/remote-canvas-source.js';
27
26
  import { crtrHome } from '../core/canvas/paths.js';
27
+ /** The API projects exactly the fault a node row carries; `api/` simply cannot
28
+ * express the branded `operation_id` (it imports Node built-ins only), so the
29
+ * wire value is asserted back to the row's fault type here rather than
30
+ * reshaped. `broker↔crtrd` is deliberately absent from the wire link union: a
31
+ * stored fault can never carry it. */
32
+ function withRowFault(dto) {
33
+ return { ...dto, fault: (dto.fault ?? null) };
34
+ }
28
35
  // Error translation
29
36
  /** Rethrow an `ApiError` as the clean structured CLI error agents parse
30
37
  * (`core/io` `handle`) — instead of the generic "internal bug" stderr path a
@@ -127,9 +134,6 @@ export function cliClient(opts) {
127
134
  // On a cold-start timeout, enrich the bare `daemon_unavailable` with the
128
135
  // tail of crtrd's event log instead of discarding it.
129
136
  coldStartDiagnostic: readColdStartDiagnostic,
130
- // A blocked startup is standing: every poll left in the window would report
131
- // the same thing this already knows, so fail now and carry the repair.
132
- coldStartAbort: daemonStartupBlocked,
133
137
  coldStartPollWindowMs: DAEMON_STARTUP_WINDOW_MS,
134
138
  });
135
139
  }
@@ -193,10 +197,12 @@ export class ApiCanvasSource {
193
197
  }
194
198
  }
195
199
  async getNode(nodeId) {
196
- return this.orNull(() => this.client.getNode(nodeId));
200
+ const detail = await this.orNull(() => this.client.getNode(nodeId));
201
+ return detail === null ? null : withRowFault(detail);
197
202
  }
198
203
  async getRow(nodeId) {
199
- return this.orNull(() => this.client.getNode(nodeId));
204
+ const detail = await this.orNull(() => this.client.getNode(nodeId));
205
+ return detail === null ? null : withRowFault(detail);
200
206
  }
201
207
  async listNodes(filter) {
202
208
  const status = filter?.status;
@@ -214,7 +220,7 @@ export class ApiCanvasSource {
214
220
  else {
215
221
  rows = await this.client.listNodes();
216
222
  }
217
- return rows;
223
+ return rows.map((row) => withRowFault(row));
218
224
  }
219
225
  async subscriptionsOf(subscriber) {
220
226
  return this.roster().then((index) => index.subscriptions.get(subscriber) ?? []);