@north-light/crouter 0.3.311 → 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 (520) 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 +17 -5
  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 +312 -1
  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 +62 -19
  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 +4 -3
  140. package/dist/core/__tests__/on-read-dedup-resume.test.js +8 -7
  141. package/dist/core/__tests__/on-read-nested-store.test.js +8 -7
  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 +35 -32
  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 +5 -4
  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 +19 -5
  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/command-hooks/artifact.d.ts +6 -0
  217. package/dist/core/command-hooks/artifact.js +22 -6
  218. package/dist/core/command-hooks/discovery.d.ts +2 -0
  219. package/dist/core/command-hooks/discovery.js +2 -0
  220. package/dist/core/command-hooks/lifecycle-catalog.d.ts +20 -2
  221. package/dist/core/command-hooks/lifecycle-catalog.js +7 -2
  222. package/dist/core/command-hooks/lifecycle-create.d.ts +4 -0
  223. package/dist/core/command-hooks/lifecycle-create.js +51 -0
  224. package/dist/core/command-hooks/schema.d.ts +2 -2
  225. package/dist/core/command-hooks/schema.js +5 -4
  226. package/dist/core/command-hooks/transport/exec-invoke.js +3 -1
  227. package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +18 -8
  228. package/dist/core/command-hooks/transport/exec-lifecycle.js +89 -30
  229. package/dist/core/config.d.ts +17 -12
  230. package/dist/core/config.js +106 -155
  231. package/dist/core/errors.d.ts +1 -0
  232. package/dist/core/errors.js +4 -0
  233. package/dist/core/feed/__tests__/inbox-migrate.test.js +27 -31
  234. package/dist/core/feed/feed.d.ts +0 -6
  235. package/dist/core/feed/feed.js +68 -56
  236. package/dist/core/feed/inbox.d.ts +7 -81
  237. package/dist/core/feed/inbox.js +46 -378
  238. package/dist/core/feed/messages.d.ts +58 -0
  239. package/dist/core/feed/messages.js +217 -0
  240. package/dist/core/fs-utils.d.ts +4 -0
  241. package/dist/core/fs-utils.js +21 -1
  242. package/dist/core/human/answer.d.ts +1 -1
  243. package/dist/core/human/answer.js +1 -1
  244. package/dist/core/human/claim.d.ts +1 -1
  245. package/dist/core/human/claim.js +2 -4
  246. package/dist/core/human/component-docs.js +1 -1
  247. package/dist/core/human/feedback-companion.js +4 -3
  248. package/dist/core/human/feedback.d.ts +5 -8
  249. package/dist/core/human/feedback.js +57 -85
  250. package/dist/core/human/page-authoring.d.ts +30 -0
  251. package/dist/core/human/page-authoring.js +19 -0
  252. package/dist/core/human/requests.d.ts +136 -0
  253. package/dist/core/human/requests.js +515 -0
  254. package/dist/core/human/root.d.ts +0 -7
  255. package/dist/core/human/root.js +0 -17
  256. package/dist/core/human/scan.d.ts +2 -7
  257. package/dist/core/human/scan.js +48 -132
  258. package/dist/core/human/summary.d.ts +1 -1
  259. package/dist/core/human/summary.js +1 -1
  260. package/dist/core/human/tickets.d.ts +23 -83
  261. package/dist/core/human/tickets.js +92 -243
  262. package/dist/core/human/types.d.ts +5 -1
  263. package/dist/core/installed-plugins.js +8 -3
  264. package/dist/core/keybindings/__tests__/resolve.test.js +19 -17
  265. package/dist/core/locked-file.d.ts +6 -3
  266. package/dist/core/locked-file.js +36 -9
  267. package/dist/core/memory/extensions.js +2 -0
  268. package/dist/core/preview-registry.js +0 -1
  269. package/dist/core/profiles/__tests__/env-store.test.js +2 -0
  270. package/dist/core/profiles/default-binding.d.ts +0 -4
  271. package/dist/core/profiles/default-binding.js +12 -58
  272. package/dist/core/profiles/env-store.js +8 -3
  273. package/dist/core/profiles/manifest.d.ts +2 -37
  274. package/dist/core/profiles/manifest.js +35 -161
  275. package/dist/core/profiles/select.js +3 -0
  276. package/dist/core/review/__tests__/capture-origin.test.js +13 -2
  277. package/dist/core/review/__tests__/comments.test.js +28 -10
  278. package/dist/core/review/__tests__/remap.test.js +28 -10
  279. package/dist/core/review/__tests__/stage-identity.test.js +17 -7
  280. package/dist/core/review/birth.d.ts +1 -1
  281. package/dist/core/review/birth.js +34 -5
  282. package/dist/core/review/comments.js +1 -1
  283. package/dist/core/review/companion.d.ts +1 -1
  284. package/dist/core/review/companion.js +2 -2
  285. package/dist/core/review/document.js +1 -1
  286. package/dist/core/review/project.d.ts +5 -1
  287. package/dist/core/review/project.js +14 -36
  288. package/dist/core/review/realize.d.ts +3 -6
  289. package/dist/core/review/realize.js +51 -168
  290. package/dist/core/review/remap.js +1 -1
  291. package/dist/core/review/stage.d.ts +3 -2
  292. package/dist/core/review/stage.js +2 -3
  293. package/dist/core/review/store.d.ts +3 -21
  294. package/dist/core/review/store.js +140 -186
  295. package/dist/core/review/types.d.ts +0 -1
  296. package/dist/core/runtime/__tests__/install-id-cache-scoping.test.js +9 -5
  297. package/dist/core/runtime/__tests__/spawn-env.test.js +8 -9
  298. package/dist/core/runtime/broker/auth-reload.js +9 -10
  299. package/dist/core/runtime/broker/daemon-ops.d.ts +25 -8
  300. package/dist/core/runtime/broker/daemon-ops.js +42 -9
  301. package/dist/core/runtime/broker/extension-abort.d.ts +3 -0
  302. package/dist/core/runtime/broker/extension-abort.js +11 -0
  303. package/dist/core/runtime/broker/fault-retry.d.ts +15 -6
  304. package/dist/core/runtime/broker/fault-retry.js +142 -95
  305. package/dist/core/runtime/broker/inbox.d.ts +4 -32
  306. package/dist/core/runtime/broker/inbox.js +4 -92
  307. package/dist/core/runtime/broker/rebind.d.ts +1 -0
  308. package/dist/core/runtime/broker/rebind.js +21 -12
  309. package/dist/core/runtime/broker-persona-guidance.d.ts +1 -1
  310. package/dist/core/runtime/broker-persona-guidance.js +2 -13
  311. package/dist/core/runtime/broker.js +28 -22
  312. package/dist/core/runtime/close.d.ts +3 -9
  313. package/dist/core/runtime/close.js +16 -27
  314. package/dist/core/runtime/fault.d.ts +3 -93
  315. package/dist/core/runtime/fault.js +9 -419
  316. package/dist/core/runtime/kickoff.d.ts +3 -6
  317. package/dist/core/runtime/kickoff.js +12 -18
  318. package/dist/core/runtime/launch.js +1 -1
  319. package/dist/core/runtime/lifecycle.js +40 -7
  320. package/dist/core/runtime/nodes.d.ts +5 -1
  321. package/dist/core/runtime/nodes.js +26 -11
  322. package/dist/core/runtime/placement.js +2 -2
  323. package/dist/core/runtime/recycle.js +24 -14
  324. package/dist/core/runtime/reset.js +26 -7
  325. package/dist/core/runtime/revive.js +226 -217
  326. package/dist/core/runtime/spawn.d.ts +4 -1
  327. package/dist/core/runtime/spawn.js +43 -5
  328. package/dist/core/runtime/warm-pool.d.ts +2 -2
  329. package/dist/core/runtime/warm-pool.js +23 -3
  330. package/dist/core/scoped-state/db.d.ts +7 -0
  331. package/dist/core/scoped-state/db.js +92 -0
  332. package/dist/core/scoped-state/migrate.d.ts +11 -0
  333. package/dist/core/scoped-state/migrate.js +198 -0
  334. package/dist/core/scoped-state/paths.d.ts +9 -0
  335. package/dist/core/scoped-state/paths.js +27 -0
  336. package/dist/core/scoped-state/profiles.d.ts +12 -0
  337. package/dist/core/scoped-state/profiles.js +62 -0
  338. package/dist/core/scoped-state/providers.d.ts +16 -0
  339. package/dist/core/scoped-state/providers.js +8 -0
  340. package/dist/core/scoped-state/schema.d.ts +3 -0
  341. package/dist/core/scoped-state/schema.js +26 -0
  342. package/dist/core/scoped-state/settings.d.ts +28 -0
  343. package/dist/core/scoped-state/settings.js +82 -0
  344. package/dist/core/subscription-state.d.ts +29 -48
  345. package/dist/core/subscription-state.js +176 -712
  346. package/dist/core/worktree-close.js +4 -4
  347. package/dist/core/worktree-mutation-async.js +2 -2
  348. package/dist/core/worktree-sweep.js +11 -16
  349. package/dist/core/worktree.js +6 -12
  350. package/dist/daemon/__tests__/autostart-storm.test.js +1 -39
  351. package/dist/daemon/__tests__/helpers/source-daemon.d.ts +2 -2
  352. package/dist/daemon/__tests__/helpers/source-daemon.js +7 -6
  353. package/dist/daemon/__tests__/integration/migration-startup.test.js +59 -26
  354. package/dist/daemon/__tests__/node-deadline-lane.test.js +2 -1
  355. package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +41 -17
  356. package/dist/daemon/__tests__/node-outcome-delivery.test.js +39 -10
  357. package/dist/daemon/__tests__/reconciler-signature.test.js +2 -2
  358. package/dist/daemon/__tests__/subtree-filter-freshness.test.js +2 -2
  359. package/dist/daemon/api/__tests__/broker-settle-park.test.js +17 -12
  360. package/dist/daemon/api/__tests__/canvas-snapshot-fields.test.js +27 -5
  361. package/dist/daemon/api/__tests__/model-config.test.js +8 -8
  362. package/dist/daemon/api/__tests__/node-create-description.test.js +22 -5
  363. package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -1
  364. package/dist/daemon/api/__tests__/nodes-activity-query.test.js +10 -12
  365. package/dist/daemon/api/__tests__/profile-launch-gates.test.js +6 -3
  366. package/dist/daemon/api/__tests__/profile-metadata-route.test.js +4 -1
  367. package/dist/daemon/api/__tests__/reopen-delivery.test.js +26 -8
  368. package/dist/daemon/api/__tests__/seam/api-server.test.js +24 -32
  369. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +4 -1
  370. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +5 -4
  371. package/dist/daemon/api/__tests__/seam/profile-delete.test.js +29 -43
  372. package/dist/daemon/api/__tests__/submit-result.test.js +3 -2
  373. package/dist/daemon/api/bridge.js +1 -1
  374. package/dist/daemon/api/handlers/bash-jobs.js +6 -7
  375. package/dist/daemon/api/handlers/broker-ops.js +47 -84
  376. package/dist/daemon/api/handlers/broker-recovery.d.ts +4 -0
  377. package/dist/daemon/api/handlers/broker-recovery.js +251 -0
  378. package/dist/daemon/api/handlers/canvas.js +2 -44
  379. package/dist/daemon/api/handlers/daemon.js +41 -0
  380. package/dist/daemon/api/handlers/feedback-comments.js +2 -2
  381. package/dist/daemon/api/handlers/health.js +10 -5
  382. package/dist/daemon/api/handlers/human-requests.js +168 -188
  383. package/dist/daemon/api/handlers/human.js +3 -148
  384. package/dist/daemon/api/handlers/inbox-page.d.ts +4 -0
  385. package/dist/daemon/api/handlers/inbox-page.js +89 -0
  386. package/dist/daemon/api/handlers/inbox.js +21 -120
  387. package/dist/daemon/api/handlers/messages.d.ts +1 -0
  388. package/dist/daemon/api/handlers/messages.js +73 -15
  389. package/dist/daemon/api/handlers/node-outcomes.js +58 -13
  390. package/dist/daemon/api/handlers/nodes.d.ts +1 -0
  391. package/dist/daemon/api/handlers/nodes.js +41 -42
  392. package/dist/daemon/api/handlers/reviews.js +4 -29
  393. package/dist/daemon/api/map.d.ts +25 -3
  394. package/dist/daemon/api/map.js +84 -14
  395. package/dist/daemon/api/server.js +68 -9
  396. package/dist/daemon/control.d.ts +18 -0
  397. package/dist/daemon/cron/disposition.d.ts +2 -0
  398. package/dist/daemon/cron/disposition.js +8 -5
  399. package/dist/daemon/cron/settle.js +37 -13
  400. package/dist/daemon/cron/sinks.d.ts +18 -1
  401. package/dist/daemon/cron/sinks.js +47 -93
  402. package/dist/daemon/crtrd-cli.js +4 -19
  403. package/dist/daemon/crtrd.d.ts +2 -0
  404. package/dist/daemon/crtrd.js +206 -81
  405. package/dist/daemon/fleet.js +5 -6
  406. package/dist/daemon/human/finish.d.ts +12 -24
  407. package/dist/daemon/human/finish.js +88 -285
  408. package/dist/daemon/human/settle.d.ts +40 -0
  409. package/dist/daemon/human/settle.js +89 -0
  410. package/dist/daemon/human/sweep.d.ts +20 -5
  411. package/dist/daemon/human/sweep.js +56 -60
  412. package/dist/daemon/manage.d.ts +21 -16
  413. package/dist/daemon/manage.js +85 -51
  414. package/dist/daemon/messaging/node-message.d.ts +1 -1
  415. package/dist/daemon/messaging/node-message.js +9 -12
  416. package/dist/daemon/park-activity.js +3 -3
  417. package/dist/daemon/reconcilers/bash-deadline.js +6 -7
  418. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  419. package/dist/daemon/reconcilers/broker-supervision.js +41 -19
  420. package/dist/daemon/reconcilers/controller-death.js +6 -18
  421. package/dist/daemon/reconcilers/delivery-lane.d.ts +9 -0
  422. package/dist/daemon/reconcilers/delivery-lane.js +130 -0
  423. package/dist/daemon/reconcilers/live-obligation.js +3 -3
  424. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  425. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -1
  426. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +10 -12
  427. package/dist/daemon/reconcilers/node-lifecycle/tick.js +5 -5
  428. package/dist/daemon/reconcilers/node-lifecycle/wants-execution.js +1 -1
  429. package/dist/daemon/reconcilers/pending-review-submit.js +3 -3
  430. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -8
  431. package/dist/daemon/reconcilers/storage-maintenance.js +13 -43
  432. package/dist/daemon/review/companion.js +5 -11
  433. package/dist/daemon/review/deliver.d.ts +1 -5
  434. package/dist/daemon/review/deliver.js +3 -52
  435. package/dist/daemon/review/finish.d.ts +2 -27
  436. package/dist/daemon/review/finish.js +79 -263
  437. package/dist/daemon/review/sweep.d.ts +3 -10
  438. package/dist/daemon/review/sweep.js +13 -131
  439. package/dist/daemon/startup-policy.d.ts +0 -5
  440. package/dist/daemon/startup-policy.js +0 -5
  441. package/dist/hook-authoring.d.ts +28 -2
  442. package/dist/hook-authoring.js +53 -1
  443. package/dist/index.d.ts +3 -2
  444. package/dist/index.js +1 -1
  445. package/dist/migrations/__tests__/activation-concurrency.test.js +3 -2
  446. package/dist/migrations/__tests__/activation.test.js +9 -6
  447. package/dist/migrations/activation.d.ts +3 -2
  448. package/dist/migrations/activation.js +2 -4
  449. package/dist/migrations/corpus.d.ts +2 -5
  450. package/dist/migrations/corpus.js +9 -20
  451. package/dist/migrations/runner.d.ts +3 -1
  452. package/dist/migrations/runner.js +6 -2
  453. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +21 -89
  454. package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +4 -2
  455. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +51 -33
  456. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +16 -2
  457. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +13 -1
  458. package/dist/pi-extensions/canvas-inbox-watcher.js +98 -244
  459. package/dist/pi-extensions/canvas-passive-context.d.ts +4 -8
  460. package/dist/pi-extensions/canvas-passive-context.js +85 -37
  461. package/dist/pi-extensions/canvas-stophook.js +47 -31
  462. package/dist/shared/env.d.ts +1 -0
  463. package/dist/shared/env.js +3 -0
  464. package/package.json +1 -1
  465. package/runtime.lock.json +5 -5
  466. package/dist/clients/attach/__tests__/page-block.test.js +0 -54
  467. package/dist/clients/inbox/review-visibility.d.ts +0 -5
  468. package/dist/clients/inbox/review-visibility.js +0 -19
  469. package/dist/commands/canvas-rebuild-index.d.ts +0 -2
  470. package/dist/commands/canvas-rebuild-index.js +0 -46
  471. package/dist/core/__tests__/human-cancel-guard.test.js +0 -110
  472. package/dist/core/__tests__/human-deliver.test.d.ts +0 -1
  473. package/dist/core/__tests__/human-deliver.test.js +0 -256
  474. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +0 -1
  475. package/dist/core/__tests__/human-node-not-supervised.test.js +0 -89
  476. package/dist/core/__tests__/integration/human-deliver-e2e.test.d.ts +0 -1
  477. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +0 -131
  478. package/dist/core/__tests__/phase4-review-store.test.d.ts +0 -1
  479. package/dist/core/__tests__/phase4-review-store.test.js +0 -144
  480. package/dist/core/__tests__/watchdog-abort-arms-retry.test.d.ts +0 -1
  481. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +0 -110
  482. package/dist/core/canvas/human-deliveries.d.ts +0 -39
  483. package/dist/core/canvas/human-deliveries.js +0 -75
  484. package/dist/core/canvas/node-outcome-deliveries.d.ts +0 -64
  485. package/dist/core/canvas/node-outcome-deliveries.js +0 -159
  486. package/dist/core/feed/passive.d.ts +0 -3
  487. package/dist/core/feed/passive.js +0 -53
  488. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +0 -1
  489. package/dist/core/human/__tests__/inbox-cancel-worker.js +0 -7
  490. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +0 -1
  491. package/dist/core/human/__tests__/inbox-claim-worker.js +0 -17
  492. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +0 -1
  493. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +0 -8
  494. package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +0 -1
  495. package/dist/core/human/__tests__/integration/inbox-core.test.js +0 -145
  496. package/dist/core/human/__tests__/page-tickets.test.d.ts +0 -1
  497. package/dist/core/human/__tests__/page-tickets.test.js +0 -161
  498. package/dist/core/review/ticket-filter.d.ts +0 -17
  499. package/dist/core/review/ticket-filter.js +0 -21
  500. package/dist/core/runtime/broker/passive.d.ts +0 -17
  501. package/dist/core/runtime/broker/passive.js +0 -44
  502. package/dist/core/runtime/busy.d.ts +0 -46
  503. package/dist/core/runtime/busy.js +0 -150
  504. package/dist/daemon/__tests__/startup-block-marker.test.d.ts +0 -1
  505. package/dist/daemon/__tests__/startup-block-marker.test.js +0 -86
  506. package/dist/daemon/api/__tests__/review-terminal-conflict.test.d.ts +0 -1
  507. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +0 -98
  508. package/dist/daemon/human/deliver-action.d.ts +0 -3
  509. package/dist/daemon/human/deliver-action.js +0 -9
  510. package/dist/daemon/node-outcome/deliver-outcome.d.ts +0 -3
  511. package/dist/daemon/node-outcome/deliver-outcome.js +0 -9
  512. package/dist/daemon/reconcilers/human-delivery-lane.d.ts +0 -10
  513. package/dist/daemon/reconcilers/human-delivery-lane.js +0 -41
  514. package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +0 -10
  515. package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +0 -41
  516. package/dist/daemon/startup-block-marker.d.ts +0 -25
  517. package/dist/daemon/startup-block-marker.js +0 -99
  518. /package/dist/{clients/attach/__tests__/page-block.test.d.ts → api/dto/mail.js} +0 -0
  519. /package/dist/{commands/sys/__tests__/migrate.test.d.ts → api/dto/recovery.js} +0 -0
  520. /package/dist/{core/__tests__/human-cancel-guard.test.d.ts → commands/__tests__/integration/migrate.test.d.ts} +0 -0
@@ -27,9 +27,12 @@ import { tmpdir } from 'node:os';
27
27
  import { join } from 'node:path';
28
28
  import registerCanvasInboxWatcher from '../../pi-extensions/canvas-inbox-watcher.js';
29
29
  import { createNode } from '../canvas/canvas.js';
30
- import { closeDb } from '../canvas/db.js';
30
+ import { closeDb, migrate, openDb } from '../canvas/db.js';
31
+ import { acknowledgeNodeMail, claimNodeMail } from '../canvas/deliveries.js';
32
+ import { beginExecution } from '../canvas/executions.js';
31
33
  import { apiSocketPath } from '../canvas/paths.js';
32
- import { appendInbox } from '../feed/inbox.js';
34
+ import { enqueueNodeMail } from '../feed/messages.js';
35
+ import { nodeMailEntry } from '../feed/inbox.js';
33
36
  import { BROKER_IDLE_TURN_START } from '../runtime/broker/held-deferred-inbox.js';
34
37
  const TICK_MS = 20;
35
38
  const DEBOUNCE_MS = 25;
@@ -107,19 +110,35 @@ function registerWatcher(pi) {
107
110
  function freshNode(id) {
108
111
  process.env['CRTR_NODE_ID'] = id;
109
112
  createNode(node(id));
113
+ process.env['CRTR_EXECUTION_ID'] = beginExecution(id, { daemonEpoch: 'test' });
114
+ }
115
+ function publishMail(nodeId, input) {
116
+ return enqueueNodeMail(nodeId, {
117
+ kind: input.kind,
118
+ label: input.label,
119
+ senderNodeId: input.from,
120
+ bodyRef: input.ref,
121
+ bodyJson: input.data,
122
+ disposition: input.disposition,
123
+ urgency: input.tier,
124
+ wake: 1,
125
+ });
110
126
  }
111
127
  let origHome;
112
128
  let origNode;
129
+ let origExecution;
113
130
  let origTick;
114
131
  let origDebounce;
115
132
  before(async () => {
116
133
  origHome = process.env['CRTR_HOME'];
117
134
  origNode = process.env['CRTR_NODE_ID'];
135
+ origExecution = process.env['CRTR_EXECUTION_ID'];
118
136
  origTick = process.env['CRTR_WATCHER_TICK_MS'];
119
137
  origDebounce = process.env['CRTR_WATCHER_DEBOUNCE_MS'];
120
138
  home = mkdtempSync(join(tmpdir(), 'crtr-inbox-naming-'));
121
139
  mkdirSync(join(home, 'nodes'), { recursive: true });
122
140
  process.env['CRTR_HOME'] = home;
141
+ migrate(openDb());
123
142
  process.env['CRTR_WATCHER_TICK_MS'] = String(TICK_MS);
124
143
  process.env['CRTR_WATCHER_DEBOUNCE_MS'] = String(DEBOUNCE_MS);
125
144
  socket = createServer((connection) => {
@@ -151,9 +170,35 @@ before(async () => {
151
170
  respond(connection, '200 OK', { applied: false });
152
171
  return;
153
172
  }
154
- // Everything else (e.g. advanceBrokerInboxCursor) — a harmless error is
155
- // fine: the watcher retries cursor commits later and never blocks
156
- // visible delivery on them.
173
+ const nodeId = nodeIdFromPath(path ?? '');
174
+ const bodyText = raw.split('\r\n\r\n')[1] ?? '';
175
+ const body = JSON.parse(bodyText);
176
+ if (method === 'POST' && path?.endsWith('/mail/claim')) {
177
+ const result = claimNodeMail(nodeId, body['execution_id'], body['channel']);
178
+ respond(connection, '200 OK', {
179
+ action: result.action,
180
+ deliveries: result.deliveries.map((delivery) => ({
181
+ delivery_id: delivery.deliveryId,
182
+ attempts: delivery.attempts,
183
+ handoff: delivery.handoff,
184
+ entry: nodeMailEntry(delivery),
185
+ })),
186
+ });
187
+ return;
188
+ }
189
+ if (method === 'POST' && path?.endsWith('/mail/acknowledge')) {
190
+ const receipts = body['deliveries'];
191
+ const result = acknowledgeNodeMail(nodeId, body['execution_id'], receipts.map((receipt) => ({
192
+ deliveryId: receipt.delivery_id,
193
+ attempts: receipt.attempts,
194
+ disposition: receipt.disposition,
195
+ })));
196
+ respond(connection, '200 OK', {
197
+ action: result.action,
198
+ results: result.results.map((receipt) => ({ delivery_id: receipt.deliveryId, outcome: receipt.outcome })),
199
+ });
200
+ return;
201
+ }
157
202
  respond(connection, '500 Internal Server Error', { error: { code: 'internal', message: 'stub daemon' } });
158
203
  });
159
204
  });
@@ -175,6 +220,10 @@ after(async () => {
175
220
  delete process.env['CRTR_NODE_ID'];
176
221
  else
177
222
  process.env['CRTR_NODE_ID'] = origNode;
223
+ if (origExecution === undefined)
224
+ delete process.env['CRTR_EXECUTION_ID'];
225
+ else
226
+ process.env['CRTR_EXECUTION_ID'] = origExecution;
178
227
  if (origTick === undefined)
179
228
  delete process.env['CRTR_WATCHER_TICK_MS'];
180
229
  else
@@ -189,7 +238,7 @@ test('an inbox-delivered human first message reaches the naming pipeline and cap
189
238
  const pi = makeFakePi();
190
239
  disposers.push(registerWatcher(pi));
191
240
  await wait(TICK_MS + 100);
192
- appendInbox('inbox-human-unnamed', {
241
+ publishMail('inbox-human-unnamed', {
193
242
  from: 'human',
194
243
  tier: 'normal',
195
244
  kind: 'message',
@@ -209,7 +258,7 @@ test('a card/envelope delivery never reaches naming or goal capture', async () =
209
258
  const pi = makeFakePi();
210
259
  const dispose = registerWatcher(pi);
211
260
  await wait(TICK_MS + 100);
212
- appendInbox('inbox-card-only', { from: 'child-9', tier: 'normal', kind: 'update', label: 'phase one done' });
261
+ publishMail('inbox-card-only', { from: 'child-9', tier: 'normal', kind: 'update', label: 'phase one done' });
213
262
  await waitFor(() => pi.injected.length >= 1, 3000);
214
263
  assert.equal(pi.injected.length, 1, 'the card still reaches the digest as a visible send');
215
264
  dispose(); // release ownership of this node id before a second watcher binds a new one
@@ -221,7 +270,7 @@ test('a card/envelope delivery never reaches naming or goal capture', async () =
221
270
  const clockPi = makeFakePi();
222
271
  disposers.push(registerWatcher(clockPi));
223
272
  await wait(TICK_MS + 100);
224
- appendInbox('inbox-card-clock', { from: 'human', tier: 'normal', kind: 'message', label: 'clock', data: { body: 'clock' } });
273
+ publishMail('inbox-card-clock', { from: 'human', tier: 'normal', kind: 'message', label: 'clock', data: { body: 'clock' } });
225
274
  await waitFor(() => getRequests.some((line) => line.includes('inbox-card-clock')));
226
275
  // Exactly the one startup GET (report-node seed) — no second, naming-triggered fetch.
227
276
  assert.equal(getRequestCount('inbox-card-only'), 1, 'a card/envelope delivery never asks the naming pipeline to name the node');
@@ -233,7 +282,7 @@ test('an already-described node fetches state but never commits a new name', asy
233
282
  const pi = makeFakePi();
234
283
  const dispose = registerWatcher(pi);
235
284
  await wait(TICK_MS + 100);
236
- appendInbox('inbox-already-named', {
285
+ publishMail('inbox-already-named', {
237
286
  from: 'human',
238
287
  tier: 'normal',
239
288
  kind: 'message',
@@ -254,7 +303,7 @@ test('an already-described node fetches state but never commits a new name', asy
254
303
  const clockPi = makeFakePi();
255
304
  disposers.push(registerWatcher(clockPi));
256
305
  await wait(TICK_MS + 100);
257
- appendInbox('inbox-commit-clock', { from: 'human', tier: 'normal', kind: 'message', label: 'clock', data: { body: 'clock' } });
306
+ publishMail('inbox-commit-clock', { from: 'human', tier: 'normal', kind: 'message', label: 'clock', data: { body: 'clock' } });
258
307
  await waitFor(() => getRequests.some((line) => line.includes('inbox-commit-clock')));
259
308
  await wait(DEBOUNCE_MS + 100);
260
309
  assert.equal(postRequests.some((line) => line.includes('inbox-already-named')), false, 'an already-described node is untouched: the guard short-circuits before any generated-name commit');
@@ -12,13 +12,17 @@ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
12
12
  import { tmpdir } from 'node:os';
13
13
  import { join } from 'node:path';
14
14
  import registerCanvasInboxWatcher from '../../pi-extensions/canvas-inbox-watcher.js';
15
- import { createNode } from '../canvas/canvas.js';
15
+ import { createNode, getNode } from '../canvas/canvas.js';
16
+ import { claimNodeMail } from '../canvas/deliveries.js';
16
17
  import { reportsDir } from '../canvas/paths.js';
17
- import { closeDb } from '../canvas/db.js';
18
- import { appendInbox, coalesce, finalizeInboxEntry } from '../feed/inbox.js';
18
+ import { closeDb, migrate, openDb } from '../canvas/db.js';
19
+ import { beginExecution } from '../canvas/executions.js';
20
+ import { enqueueNodeMail } from '../feed/messages.js';
21
+ import { coalesce, nodeMailEntry } from '../feed/inbox.js';
19
22
  import { appendSituationalContext, SITUATIONAL_CONTEXT_CUSTOM_TYPE } from '../runtime/situational-context.js';
20
23
  import { parseCard } from '../../shared/generated-context.js';
21
24
  import { BROKER_IDLE_TURN_START } from '../runtime/broker/held-deferred-inbox.js';
25
+ import { createApiServer } from '../../daemon/api/server.js';
22
26
  // Drive the watcher's injectable cadence seam (CRTR_WATCHER_TICK_MS /
23
27
  // CRTR_WATCHER_DEBOUNCE_MS) at a fast tempo so the watcher delivers in tens of
24
28
  // milliseconds, not seconds — exactly the resolve+seed tick, read tick, and
@@ -29,15 +33,53 @@ const TICK_MS = 20;
29
33
  const DEBOUNCE_MS = 25;
30
34
  let origHome;
31
35
  let origNode;
36
+ let origExecution;
32
37
  const homes = [];
38
+ let server;
33
39
  const disposers = [];
34
40
  /** Point CRTR_HOME at a fresh temp canvas root and bind CRTR_NODE_ID. */
35
41
  function freshNode(nodeId) {
36
- const home = join(tmpdir(), `crtr-canvas-watcher-${Date.now()}-${Math.random().toString(36).slice(2)}`);
42
+ closeDb();
43
+ const home = join(tmpdir(), `cw-${Date.now()}-${Math.random().toString(36).slice(2)}`);
37
44
  mkdirSync(home, { recursive: true });
38
45
  homes.push(home);
39
46
  process.env['CRTR_HOME'] = home;
40
47
  process.env['CRTR_NODE_ID'] = nodeId;
48
+ migrate(openDb());
49
+ }
50
+ function ensureWatcherNode(nodeId) {
51
+ if (getNode(nodeId) !== null)
52
+ return;
53
+ createNode({
54
+ node_id: nodeId,
55
+ name: nodeId,
56
+ created: new Date().toISOString(),
57
+ cwd: '/tmp/work',
58
+ kind: 'general',
59
+ mode: 'base',
60
+ lifecycle: 'resident',
61
+ status: 'active',
62
+ });
63
+ }
64
+ function publishMail(nodeId, input) {
65
+ ensureWatcherNode(nodeId);
66
+ return enqueueNodeMail(nodeId, {
67
+ kind: input.kind,
68
+ label: input.label,
69
+ senderNodeId: input.from,
70
+ bodyRef: input.ref,
71
+ bodyJson: input.data,
72
+ disposition: input.disposition,
73
+ urgency: input.tier,
74
+ wake: 1,
75
+ });
76
+ }
77
+ function claimedMail(nodeId, input) {
78
+ publishMail(nodeId, input);
79
+ const claim = claimNodeMail(nodeId, beginExecution(nodeId, { daemonEpoch: 'test' }), 'wake');
80
+ assert.equal(claim.action, 'claimed');
81
+ assert.equal(claim.deliveries.length, 1);
82
+ return nodeMailEntry(claim.deliveries[0]);
41
83
  }
42
84
  function makeFakePi() {
43
85
  const handlers = {};
@@ -63,8 +105,12 @@ function onlyDelivery(deliveries) {
63
105
  // registerCanvasInboxWatcher() alone only wires up handlers; without this the
64
106
  // watcher never polls or sends anything.
65
107
  function registerWatcher(pi) {
108
+ const nodeId = process.env['CRTR_NODE_ID'];
109
+ ensureWatcherNode(nodeId);
110
+ process.env['CRTR_EXECUTION_ID'] = beginExecution(nodeId, { daemonEpoch: 'test' });
66
111
  const dispose = registerCanvasInboxWatcher(pi);
67
- pi.fire('session_start', { type: 'session_start' }, undefined);
112
+ server ??= createApiServer();
113
+ void server.ready.then(() => pi.fire('session_start', { type: 'session_start' }, undefined));
68
114
  return dispose;
69
115
  }
70
116
  // Poll `predicate` until it holds or `timeoutMs` elapses, then return either
@@ -100,7 +146,7 @@ test('coalesce renders one inbox card whose entries inline short report bodies a
100
146
  mkdirSync(reportsDir(child.node_id), { recursive: true });
101
147
  writeFileSync(reportPath, '---\nnode: child\nkind: update\n---\nshort report body', 'utf8');
102
148
  const digest = onlyDelivery(coalesce([
103
- finalizeInboxEntry({ from: 'child', tier: 'normal', kind: 'update', label: 'short report', ref: 'child:reports/short-update.md' }),
149
+ claimedMail('child', { from: 'child', tier: 'normal', kind: 'update', label: 'short report', ref: 'child:reports/short-update.md' }),
104
150
  ]));
105
151
  // The envelope is the whole message: a reader that finds it anywhere else
106
152
  // attributes the digest to the person.
@@ -121,7 +167,7 @@ test('coalesce renders one inbox card whose entries inline short report bodies a
121
167
  const canonical = parseCard({
122
168
  role: 'user',
123
169
  content: onlyDelivery(coalesce([
124
- finalizeInboxEntry({ from: 'child', tier: 'normal', kind: 'final', label: 'done', ref: 'child:reports/final.md' }),
170
+ claimedMail('child', { from: 'child', tier: 'normal', kind: 'final', label: 'done', ref: 'child:reports/final.md' }),
125
171
  ])),
126
172
  });
127
173
  assert.deepEqual(canonical.senders[0].entries, [
@@ -131,7 +177,7 @@ test('coalesce renders one inbox card whose entries inline short report bodies a
131
177
  const spilled = parseCard({
132
178
  role: 'user',
133
179
  content: onlyDelivery(coalesce([
134
- finalizeInboxEntry({ from: 'child', tier: 'normal', kind: 'message', label: 'long', ref: '/tmp/message.md', data: { body: 'long body' } }),
180
+ claimedMail('child', { from: 'child', tier: 'normal', kind: 'message', label: 'long', ref: '/tmp/message.md', data: { body: 'long body' } }),
135
181
  ])),
136
182
  });
137
183
  assert.deepEqual(spilled.senders[0].entries, [
@@ -143,13 +189,14 @@ test('a human message coalesces verbatim — a conversation turn, not a digest',
143
189
  // A human typing directly into a node's box is a CONVERSATION TURN, so the
144
190
  // dormant wake path must read identically to the live path: no card, no
145
191
  // `<from>` section. Every other sender is digest content.
192
+ freshNode('human');
146
193
  const body = 'first line\n\nsecond paragraph';
147
194
  const human = onlyDelivery(coalesce([
148
- finalizeInboxEntry({ from: 'human', tier: 'normal', kind: 'message', label: 'first line', data: { body } }),
195
+ claimedMail('human', { from: 'human', tier: 'normal', kind: 'message', label: 'first line', data: { body } }),
149
196
  ]));
150
197
  assert.equal(human, body);
151
198
  const agent = onlyDelivery(coalesce([
152
- finalizeInboxEntry({ from: 'mqifzplr-0753bde3', tier: 'normal', kind: 'message', label: 'go', data: { body: 'go' } }),
199
+ claimedMail('agent', { from: 'mqifzplr-0753bde3', tier: 'normal', kind: 'message', label: 'go', data: { body: 'go' } }),
153
200
  ]));
154
201
  assert.equal(agent.startsWith('<runtime kind="inbox"'), true);
155
202
  assert.match(agent, /<from id="mqifzplr-0753bde3">/);
@@ -157,8 +204,9 @@ test('a human message coalesces verbatim — a conversation turn, not a digest',
157
204
  test('a mixed batch splits: human words verbatim, node reports in one card, order preserved', () => {
158
205
  // Concatenating the two into one message left `parseCard` unable to see the
159
206
  // envelope, so the whole delivery read as the person's words.
160
- const humanEntry = (body) => finalizeInboxEntry({ from: 'human', tier: 'normal', kind: 'message', label: body, data: { body } });
161
- const nodeEntry = (from, body) => finalizeInboxEntry({ from, tier: 'normal', kind: 'update', label: body, data: { body } });
207
+ freshNode('mixed');
208
+ const humanEntry = (body) => claimedMail('mixed-human', { from: 'human', tier: 'normal', kind: 'message', label: body, data: { body } });
209
+ const nodeEntry = (from, body) => claimedMail('mixed-node', { from, tier: 'normal', kind: 'update', label: body, data: { body } });
162
210
  for (const [name, entries] of [
163
211
  ['human first', [humanEntry('do the thing'), nodeEntry('mqifzplr-0753bde3', 'phase one done')]],
164
212
  ['node first', [nodeEntry('mqifzplr-0753bde3', 'phase one done'), humanEntry('do the thing')]],
@@ -182,14 +230,18 @@ let origDebounce;
182
230
  before(() => {
183
231
  origHome = process.env['CRTR_HOME'];
184
232
  origNode = process.env['CRTR_NODE_ID'];
233
+ origExecution = process.env['CRTR_EXECUTION_ID'];
185
234
  origTick = process.env['CRTR_WATCHER_TICK_MS'];
186
235
  origDebounce = process.env['CRTR_WATCHER_DEBOUNCE_MS'];
187
236
  process.env['CRTR_WATCHER_TICK_MS'] = String(TICK_MS);
188
237
  process.env['CRTR_WATCHER_DEBOUNCE_MS'] = String(DEBOUNCE_MS);
189
238
  });
190
- afterEach(() => {
239
+ afterEach(async () => {
191
240
  while (disposers.length > 0)
192
241
  disposers.pop()();
242
+ await server?.close();
243
+ server = undefined;
244
+ closeDb();
193
245
  });
194
246
  after(() => {
195
247
  if (origHome === undefined)
@@ -200,6 +252,10 @@ after(() => {
200
252
  delete process.env['CRTR_NODE_ID'];
201
253
  else
202
254
  process.env['CRTR_NODE_ID'] = origNode;
255
+ if (origExecution === undefined)
256
+ delete process.env['CRTR_EXECUTION_ID'];
257
+ else
258
+ process.env['CRTR_EXECUTION_ID'] = origExecution;
203
259
  if (origTick === undefined)
204
260
  delete process.env['CRTR_WATCHER_TICK_MS'];
205
261
  else
@@ -223,7 +279,7 @@ describe('canvas inbox watcher — finished-node delivery', () => {
223
279
  // Subscriber is actively streaming when the worker finishes.
224
280
  pi.fire('agent_start', { type: 'agent_start' }, { isIdle: () => false });
225
281
  await wait(TICK_MS + 100);
226
- appendInbox('node-final', { from: 'child-1', tier: 'normal', kind: 'final', label: 'all done' });
282
+ publishMail('node-final', { from: 'child-1', tier: 'normal', kind: 'final', label: 'all done' });
227
283
  await waitFor(() => pi.injected.length >= 1);
228
284
  assert.equal(pi.injected.length, 1, 'one coalesced injection');
229
285
  assert.equal(pi.injected[0].deliverAs, 'steer', 'a finished node steers, not follows up');
@@ -233,8 +289,8 @@ describe('canvas inbox watcher — finished-node delivery', () => {
233
289
  const pi = makeFakePi();
234
290
  disposers.push(registerWatcher(pi));
235
291
  await wait(TICK_MS + 100);
236
- appendInbox('node-mixed', { from: 'human', tier: 'normal', kind: 'message', label: 'do the thing', data: { body: 'do the thing' } });
237
- appendInbox('node-mixed', { from: 'child-9', tier: 'normal', kind: 'update', label: 'phase one done' });
292
+ publishMail('node-mixed', { from: 'human', tier: 'normal', kind: 'message', label: 'do the thing', data: { body: 'do the thing' } });
293
+ publishMail('node-mixed', { from: 'child-9', tier: 'normal', kind: 'update', label: 'phase one done' });
238
294
  // One visible send per cycle, so the second part lands a tick later.
239
295
  await waitFor(() => pi.injected.length >= 2);
240
296
  assert.equal(pi.injected.length, 2, 'the batch split into two visible messages');
@@ -250,7 +306,7 @@ describe('canvas inbox watcher — finished-node delivery', () => {
250
306
  disposers.push(registerWatcher(pi));
251
307
  pi.fire('agent_start', { type: 'agent_start' }, { isIdle: () => false });
252
308
  await wait(TICK_MS + 100);
253
- appendInbox('node-update', { from: 'child-2', tier: 'normal', kind: 'update', label: 'still working' });
309
+ publishMail('node-update', { from: 'child-2', tier: 'normal', kind: 'update', label: 'still working' });
254
310
  await waitFor(() => pi.injected.length >= 1);
255
311
  assert.equal(pi.injected.length, 1);
256
312
  assert.equal(pi.injected[0].deliverAs, 'followUp', 'a normal update is not urgent → followUp');
@@ -262,7 +318,7 @@ describe('canvas inbox watcher — hidden situational-context delivery', () => {
262
318
  appendSituationalContext('node-situational-only', 'the user is mid-checkout on the store applet');
263
319
  const pi = makeFakePi();
264
320
  disposers.push(registerWatcher(pi));
265
- appendInbox('node-situational-only', {
321
+ publishMail('node-situational-only', {
266
322
  from: null,
267
323
  tier: 'normal',
268
324
  kind: 'message',
@@ -288,7 +344,7 @@ describe('canvas inbox watcher — hidden situational-context delivery', () => {
288
344
  appendSituationalContext('node-situational-combined', 'the hidden ambient checkout state');
289
345
  const pi = makeFakePi();
290
346
  disposers.push(registerWatcher(pi));
291
- appendInbox('node-situational-combined', {
347
+ publishMail('node-situational-combined', {
292
348
  from: 'child-3',
293
349
  tier: 'normal',
294
350
  kind: 'message',
@@ -323,7 +379,7 @@ describe('canvas inbox watcher — hidden situational-context delivery', () => {
323
379
  realSendMessage(message, options);
324
380
  };
325
381
  disposers.push(registerWatcher(pi));
326
- appendInbox('node-situational-retry', {
382
+ publishMail('node-situational-retry', {
327
383
  from: null,
328
384
  tier: 'normal',
329
385
  kind: 'message',
@@ -1,10 +1,10 @@
1
1
  import { test, before, after, beforeEach } from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import { mkdtempSync, rmSync, existsSync, readFileSync, writeFileSync, statSync } from 'node:fs';
3
+ import { mkdtempSync, rmSync, existsSync, writeFileSync } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
- import { createNode, getNode, getRow, updateNode, setStatus, subscribe, unsubscribe, setSubscriptionActive, recordSpawn, subscribersOf, subscriptionsOf, view, hasActiveLiveSubscription, rebuildIndex, } from '../canvas/canvas.js';
7
- import { closeDb } from '../canvas/db.js';
6
+ import { createNode, getNode, getRow, updateNode, setStatus, subscribe, unsubscribe, setSubscriptionActive, recordSpawn, subscribersOf, subscriptionsOf, view, hasActiveLiveSubscription, } from '../canvas/canvas.js';
7
+ import { closeDb, migrate, openDb } from '../canvas/db.js';
8
8
  import { nodeMetaPath } from '../canvas/paths.js';
9
9
  let home;
10
10
  function node(id, over = {}) {
@@ -28,25 +28,22 @@ beforeEach(() => {
28
28
  // Fresh db + dirs per test for isolation.
29
29
  closeDb();
30
30
  rmSync(home, { recursive: true, force: true });
31
+ migrate(openDb());
31
32
  });
32
33
  after(() => {
33
34
  closeDb();
34
35
  rmSync(home, { recursive: true, force: true });
35
36
  delete process.env['CRTR_HOME'];
36
37
  });
37
- test('meta.json is the source of truth on disk', () => {
38
+ test('SQL is the source of truth; a node directory contains artifacts only', () => {
38
39
  createNode(node('a', { kind: 'developer' }));
39
- const raw = JSON.parse(readFileSync(nodeMetaPath('a'), 'utf8'));
40
- assert.equal(raw.kind, 'developer');
40
+ assert.equal(getNode('a')?.kind, 'developer');
41
+ assert.equal(existsSync(nodeMetaPath('a')), false, 'creating a node does not write a metadata authority file');
41
42
  });
42
- // Regression: cycle_pending is an IDENTITY field (meta.json-only, no db
43
- // column — like pi_session_file) written by reviveNode/canvas-stophook and
44
- // read back by reviveNode's locked launch arbitration. writeMeta only persists
45
- // keys listed in IDENTITY_KEYS; if cycle_pending is missing from that whitelist,
46
- // updateNode silently drops it and a competing caller can request strict resume.
43
+ // cycle_pending is stored on the node row and read by revive launch arbitration.
47
44
  test('cycle_pending survives the updateNode/getNode meta.json round-trip', () => {
48
45
  createNode(node('a'));
49
- assert.equal(getNode('a')?.cycle_pending, undefined); // never set
46
+ assert.equal(getNode('a')?.cycle_pending, false); // SQL default
50
47
  updateNode('a', { cycle_pending: true });
51
48
  assert.equal(getNode('a')?.cycle_pending, true);
52
49
  updateNode('a', { cycle_pending: false });
@@ -118,50 +115,24 @@ test('recordSpawn writes the audit-only spawned_by edge', () => {
118
115
  assert.equal(subscriptionsOf('child').length, 0);
119
116
  assert.equal(subscribersOf('parent').length, 0);
120
117
  });
121
- // Durability contract: meta.json is durable and never truncated
122
- // writeMeta uses atomic rename with fsync on both the temp file and parent
123
- // directory to ensure atomicity and durability. readMeta/rebuildIndex tolerate
124
- // an empty/corrupt meta.json as MISSING (returning null) rather than throwing,
125
- // gracefully handling rare disk/NFS anomalies without breaking recovery.
126
- test('writeMeta produces a durable, non-empty, atomically-named meta.json', () => {
118
+ test('artifact metadata does not alter a SQL node', () => {
127
119
  createNode(node('a'));
128
- const p = nodeMetaPath('a');
129
- assert.ok(statSync(p).size > 0, 'meta.json must be non-empty');
130
- assert.ok(!existsSync(`${p}.tmp`), 'temp file must be renamed away, not left behind');
131
- assert.equal(getNode('a')?.node_id, 'a');
132
- });
133
- test('getNode returns null (not throw) on a 0-byte meta.json', () => {
134
- createNode(node('a'));
135
- writeFileSync(nodeMetaPath('a'), ''); // the observed NFS 0-byte corruption
136
- assert.equal(getNode('a'), null);
120
+ writeFileSync(nodeMetaPath('a'), JSON.stringify({ node_id: 'a', kind: 'other' }));
121
+ assert.equal(getNode('a')?.kind, 'general');
137
122
  });
138
- test('getNode returns null (not throw) on an unparseable meta.json', () => {
123
+ test('corrupt artifact metadata does not make a SQL node unreadable', () => {
139
124
  createNode(node('a'));
140
125
  writeFileSync(nodeMetaPath('a'), '{ broken json');
141
- assert.equal(getNode('a'), null);
142
- });
143
- test('rebuildIndex skips a corrupt-meta node instead of throwing', () => {
144
- createNode(node('good'));
145
- createNode(node('bad'));
146
- writeFileSync(nodeMetaPath('bad'), ''); // 0-byte corruption on one node
147
- closeDb();
148
- rmSync(join(home, 'canvas.db'), { force: true });
149
- rmSync(join(home, 'canvas.db-wal'), { force: true });
150
- rmSync(join(home, 'canvas.db-shm'), { force: true });
151
- assert.doesNotThrow(() => rebuildIndex());
152
- assert.equal(getRow('good')?.node_id, 'good'); // healthy node reindexed
153
- assert.equal(getRow('bad'), null); // corrupt node skipped, no crash
126
+ assert.equal(getNode('a')?.node_id, 'a');
154
127
  });
155
- test('rebuildIndex reconstructs node rows from on-disk metas', () => {
128
+ test('deleting the database does not rebuild nodes from artifact directories', () => {
156
129
  createNode(node('a', { parent: null }));
157
130
  createNode(node('b', { parent: 'a' }));
158
- // wipe just the db, keep the node dirs
159
131
  closeDb();
160
132
  rmSync(join(home, 'canvas.db'), { force: true });
161
133
  rmSync(join(home, 'canvas.db-wal'), { force: true });
162
134
  rmSync(join(home, 'canvas.db-shm'), { force: true });
163
- assert.equal(getRow('a'), null); // gone from index
164
- rebuildIndex();
165
- assert.equal(getRow('a')?.node_id, 'a');
166
- assert.equal(getRow('b')?.parent, 'a');
135
+ migrate(openDb());
136
+ assert.equal(getRow('a'), null);
137
+ assert.equal(getRow('b'), null);
167
138
  });
@@ -6,14 +6,18 @@ import { tmpdir } from 'node:os';
6
6
  import { join } from 'node:path';
7
7
  import { spawnSync } from 'node:child_process';
8
8
  import { createNode, getNode, subscribe } from '../canvas/canvas.js';
9
- import { closeDb } from '../canvas/db.js';
10
- import { readInboxSince } from '../feed/inbox.js';
9
+ import { closeDb, openDb, migrate } from '../canvas/db.js';
10
+ import { beginExecution, recordExecutionPid } from '../canvas/executions.js';
11
+ import { bindSession } from '../canvas/sessions.js';
12
+ import { ensureScopeInitialized } from '../config.js';
13
+ import { closeScopedStores } from '../scoped-state/db.js';
11
14
  import { closeNode } from '../runtime/close.js';
12
15
  import { superviseTick } from '../../daemon/crtrd.js';
13
16
  import { bindTestFleet } from './helpers/fleet.js';
14
17
  import { resetFleetForTests } from '../runtime/fleet.js';
15
18
  import { headlessBrokerHost } from '../runtime/host.js';
16
19
  let home;
20
+ let priorUserHome;
17
21
  let tf;
18
22
  function node(id, over = {}) {
19
23
  return {
@@ -55,21 +59,30 @@ function deadEngineChild(id, over = {}) {
55
59
  pane: GONE_PANE,
56
60
  tmux_session: 'crtr-cdw',
57
61
  window: '@1',
58
- pi_pid: deadPid(),
59
- pi_session_id: 'booted',
60
62
  intent: null,
61
63
  status: 'active',
62
64
  ...over,
63
65
  }));
66
+ const executionId = beginExecution(id, { daemonEpoch: 'test' });
67
+ assert.equal(bindSession(id, { executionId, sessionId: 'booted', transcriptPath: withSession(id) }), 'bound');
68
+ assert.equal(recordExecutionPid(executionId, id, deadPid()), 'recorded');
64
69
  }
65
70
  /** A dormant inbox-waiting PARENT — only ever the wake TARGET, asserted via its
66
71
  * inbox. */
67
72
  function inboxWaitingParent(id) {
68
73
  createNode(node(id, { status: 'active' }));
69
74
  }
70
- const wakeFromChild = (parent, child) => readInboxSince(parent).find((e) => e.from === child);
75
+ function nodeMail(nodeId) {
76
+ return openDb().prepare(`SELECT m.sender_node_id AS "from", m.label
77
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
78
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ?
79
+ ORDER BY m.seq`).all(nodeId);
80
+ }
81
+ const wakeFromChild = (parent, child) => nodeMail(parent).find((entry) => entry.from === child);
71
82
  before(() => {
72
83
  home = mkdtempSync(join(tmpdir(), 'crtr-child-death-wake-'));
84
+ priorUserHome = process.env['HOME'];
85
+ process.env['HOME'] = home;
73
86
  process.env['CRTR_HOME'] = home;
74
87
  // superviseTick requires a bound fleet (D-2). It starts empty: none of these
75
88
  // fabricated rows has a live broker.
@@ -78,11 +91,18 @@ before(() => {
78
91
  beforeEach(() => {
79
92
  closeDb();
80
93
  rmSync(home, { recursive: true, force: true });
94
+ ensureScopeInitialized('user', join(home, '.crouter'));
95
+ migrate(openDb());
81
96
  });
82
97
  after(() => {
83
98
  closeDb();
99
+ closeScopedStores();
84
100
  rmSync(home, { recursive: true, force: true });
85
101
  delete process.env['CRTR_HOME'];
102
+ if (priorUserHome === undefined)
103
+ delete process.env['HOME'];
104
+ else
105
+ process.env['HOME'] = priorUserHome;
86
106
  resetFleetForTests();
87
107
  });
88
108
  test('a dead-engine booted child enters durable backoff before recovery, is NOT reaped, and fans NO wake to its dormant parent', async () => {
@@ -108,7 +128,7 @@ test('a dead-engine booted child enters durable backoff before recovery, is NOT
108
128
  // the row is relaunched on its saved session, never reaped to dead/done/idle.
109
129
  assert.deepEqual(launches, ['CHILD'], 'the matured tick recovers the dead engine through the policy table');
110
130
  assert.equal(getNode('CHILD').status, 'active', 'CHILD is recovered, NOT reaped');
111
- assert.equal(readInboxSince('PARENT').length, 0, 'PARENT inbox EMPTY — no daemon liveness wake');
131
+ assert.equal(nodeMail('PARENT').length, 0, 'PARENT has no delivery from daemon liveness');
112
132
  }
113
133
  finally {
114
134
  headlessBrokerHost.launch = originalLaunch;
@@ -118,7 +138,9 @@ test('a dead-engine booted child enters durable backoff before recovery, is NOT
118
138
  test('node close of a child wakes its SURVIVING manager (the parent outside the closing set)', async () => {
119
139
  inboxWaitingParent('PARENT');
120
140
  // A booted child; closeNode tears its (paneless-for-the-test) engine down.
121
- createNode(node('CHILD', { pane: GONE_PANE, tmux_session: 'crtr-cdw', window: '@1', pi_session_id: 'booted', pi_session_file: withSession('CHILD'), status: 'active' }));
141
+ createNode(node('CHILD', { pane: GONE_PANE, tmux_session: 'crtr-cdw', window: '@1', status: 'active' }));
142
+ const executionId = beginExecution('CHILD', { daemonEpoch: 'test' });
143
+ assert.equal(bindSession('CHILD', { executionId, sessionId: 'booted', transcriptPath: withSession('CHILD') }), 'bound');
122
144
  subscribe('PARENT', 'CHILD', true); // PARENT is a manager OUTSIDE the close set
123
145
  // Closing CHILD: closingSet({CHILD}) cannot pull in PARENT (PARENT is a
124
146
  // SUBSCRIBER of CHILD, never a descendant), so PARENT survives + must wake.
@@ -14,6 +14,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
14
14
  import { tmpdir } from 'node:os';
15
15
  import { join } from 'node:path';
16
16
  import { buildRoot } from '../../build-root.js';
17
+ import { ensureScopeInitialized } from '../config.js';
17
18
  import { resetScopeCache } from '../scope.js';
18
19
  // buildRoot() composes external command-plugin branches from the effective
19
20
  // enabled plugins. Pin HOME to an isolated no-plugin home: a third party's
@@ -28,6 +29,7 @@ before(() => {
28
29
  delete process.env['CRTR_PROFILE_ID'];
29
30
  process.env['HOME'] = home;
30
31
  resetScopeCache();
32
+ ensureScopeInitialized('user', join(home, '.crouter'));
31
33
  });
32
34
  after(() => {
33
35
  if (realHome !== undefined)