@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
@@ -34,8 +34,12 @@ import { join } from 'node:path';
34
34
  import { spawn, spawnSync } from 'node:child_process';
35
35
  import { createNode, deleteNode, getNode, subscribe, updateNode } from '../../canvas/canvas.js';
36
36
  import { armCron, getCron } from '../../canvas/crons.js';
37
- import { readInboxSince } from '../../feed/inbox.js';
38
- import { closeDb } from '../../canvas/db.js';
37
+ import { closeDb, migrate, openDb } from '../../canvas/db.js';
38
+ import { closeScopedStores } from '../../scoped-state/db.js';
39
+ import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
40
+ import { userScopeRoot } from '../../scope.js';
41
+ import { resetSession } from '../../canvas/sessions.js';
42
+ import { liveExecutionForNode } from '../../canvas/executions.js';
39
43
  import { apiSocketPath } from '../../canvas/paths.js';
40
44
  import { buildPiArgv } from '../../runtime/launch.js';
41
45
  import { headlessBrokerHost } from '../../runtime/host.js';
@@ -45,12 +49,15 @@ import { parseCard } from '../../../shared/generated-context.js';
45
49
  import { seedRoadmap } from '../../runtime/roadmap.js';
46
50
  import { InputError } from '../../io.js';
47
51
  import { CrtrClient } from '../../../api/client.js';
48
- import { readFault } from '../../runtime/fault.js';
52
+ import { readFault } from '../../canvas/recovery.js';
49
53
  import { createHarness } from '../helpers/harness.js';
50
54
  import { bindTestFleet } from '../helpers/fleet.js';
51
55
  import { resetFleetForTests } from '../../runtime/fleet.js';
52
56
  import { emptyContextExposureState, exposureTarget, initializeForkContextExposure, loadContextExposureState, registerExposure, saveContextExposureState, } from '../../substrate/injected-store.js';
53
57
  let home;
58
+ let scopeCwd;
59
+ let originalCwd;
60
+ let originalHome;
54
61
  function node(id, over = {}) {
55
62
  return {
56
63
  node_id: id,
@@ -64,6 +71,26 @@ function node(id, over = {}) {
64
71
  ...over,
65
72
  };
66
73
  }
74
+ function fabricateInScope(h, options = {}) {
75
+ const id = h.fabricateBrokerNode(options);
76
+ updateNode(id, { cwd: scopeCwd });
77
+ return id;
78
+ }
79
+ function createNodeWithSession(meta) {
80
+ createNode(meta);
81
+ if (meta.pi_session_id !== undefined || meta.pi_session_file !== undefined) {
82
+ resetSession(meta.node_id, { sessionId: meta.pi_session_id ?? '', transcriptPath: meta.pi_session_file ?? null });
83
+ }
84
+ }
85
+ function wakeMail(nodeId) {
86
+ return openDb().prepare(`SELECT m.sender_node_id AS "from", m.body_json AS data
87
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
88
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.wake = 1
89
+ ORDER BY m.seq`).all(nodeId).map((row) => {
90
+ const mail = row;
91
+ return { from: mail.from, data: mail.data === null ? null : JSON.parse(mail.data) };
92
+ });
93
+ }
67
94
  /** A launch prompt is the fresh-revive kickoff when it carries that card kind. */
68
95
  function isReviveKickoff(arg) {
69
96
  return parseCard({ role: 'user', content: arg })?.kind === 'revive';
@@ -88,16 +115,33 @@ before(() => {
88
115
  // to /private/var, and the runtime resolves a fork's durable source file, so a
89
116
  // raw temp path would compare unequal to the very path the runtime replays.
90
117
  home = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-revive-')));
118
+ scopeCwd = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-revive-scope-')));
119
+ originalCwd = process.cwd();
120
+ originalHome = process.env['HOME'];
121
+ process.chdir(scopeCwd);
122
+ process.env['HOME'] = scopeCwd;
91
123
  process.env['CRTR_HOME'] = home;
92
124
  });
93
125
  beforeEach(() => {
94
126
  closeDb();
127
+ closeScopedStores();
95
128
  rmSync(home, { recursive: true, force: true });
129
+ rmSync(userScopeRoot(), { recursive: true, force: true });
130
+ migrate(openDb());
131
+ initializeFreshScopedStore({ kind: 'user', root: userScopeRoot() });
96
132
  });
97
133
  after(() => {
98
134
  closeDb();
135
+ closeScopedStores();
99
136
  rmSync(home, { recursive: true, force: true });
137
+ rmSync(userScopeRoot(), { recursive: true, force: true });
100
138
  delete process.env['CRTR_HOME'];
139
+ process.chdir(originalCwd);
140
+ rmSync(scopeCwd, { recursive: true, force: true });
141
+ if (originalHome === undefined)
142
+ delete process.env['HOME'];
143
+ else
144
+ process.env['HOME'] = originalHome;
101
145
  for (const pid of livePids) {
102
146
  try {
103
147
  process.kill(pid, 'SIGKILL');
@@ -188,7 +232,7 @@ test('a resumed revive replays the persisted LaunchSpec (model, tools, extension
188
232
  env: { FOO: 'bar' },
189
233
  },
190
234
  });
191
- createNode(m); // scaffolds the node dir so the system prompt persists to a file
235
+ createNodeWithSession(m); // scaffolds the node dir so the system prompt persists to a file
192
236
  const { argv, env } = buildPiArgv(m, resumeArgs(m, true));
193
237
  // Persona extensions, in order.
194
238
  const ea = argv.indexOf('/ext/a.js');
@@ -223,7 +267,7 @@ test('reviveNode no-ops when the node has a live fleet entry — double-revive g
223
267
  // synchronously, so a live entry means a broker is already on this .jsonl
224
268
  // and re-launching would double-spawn onto it. No pid probe is involved
225
269
  // anywhere — the entry alone is the authority.
226
- const M = h.fabricateBrokerNode({
270
+ const M = fabricateInScope(h, {
227
271
  status: 'active',
228
272
  intent: null,
229
273
  pi_pid: deadPid(),
@@ -251,7 +295,7 @@ test('reviveNode PROCEEDS when the node has NO fleet entry — cycle counter bum
251
295
  const stalePid = deadPid();
252
296
  mkdirSync(join(home, 'sessions'), { recursive: true });
253
297
  writeFileSync(sessionFile, '{}\n', 'utf8');
254
- const M = h.fabricateBrokerNode({
298
+ const M = fabricateInScope(h, {
255
299
  status: 'active',
256
300
  intent: null,
257
301
  pi_pid: stalePid,
@@ -301,11 +345,11 @@ function armDeadline(anchor, cronId) {
301
345
  test('a recovery relaunch keeps the armed deadline; a wake revive consumes it', async () => {
302
346
  const h = await createHarness({ headless: true, sessionPrefix: 'crtr-revive-deadline' });
303
347
  try {
304
- const recovered = h.fabricateBrokerNode({ status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'uuid-1' });
348
+ const recovered = fabricateInScope(h, { status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'uuid-1' });
305
349
  armDeadline(recovered, 'cron-recovery');
306
350
  await reviveNode(recovered, { resume: true, recovery: true, capacity: 'freeze' });
307
351
  assert.equal(getCron('cron-recovery') !== null, true, 'the daemon replacing a dead broker is not a wake — the deadline survives so the wait can still settle');
308
- const woken = h.fabricateBrokerNode({ status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'uuid-2' });
352
+ const woken = fabricateInScope(h, { status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'uuid-2' });
309
353
  armDeadline(woken, 'cron-wake');
310
354
  await reviveNode(woken, { resume: true, capacity: 'freeze' });
311
355
  assert.equal(getCron('cron-wake'), null, 'a wake revive won the race and cancels the deadline');
@@ -320,15 +364,17 @@ test('a second broker session-bound request is shut down instead of adopting a l
320
364
  const sessionFile = join(home, 'sessions', 'owned.jsonl');
321
365
  mkdirSync(join(home, 'sessions'), { recursive: true });
322
366
  writeFileSync(sessionFile, '{}\n', 'utf8');
323
- const owner = h.fabricateBrokerNode({
367
+ const owner = fabricateInScope(h, {
324
368
  id: 'live-session-owner',
325
369
  pi_pid: process.pid,
326
370
  pi_session_id: 'owner-session',
327
371
  pi_session_file: sessionFile,
328
372
  });
329
- const claimant = h.fabricateBrokerNode({ id: 'session-claimant', pi_pid: deadPid() });
373
+ const claimant = fabricateInScope(h, { id: 'session-claimant', pi_pid: process.pid });
330
374
  const client = new CrtrClient({ socketPath: apiSocketPath(), autostart: false });
375
+ assert.equal(getNode(owner)?.pi_pid, process.pid, 'the owner has a live SQL execution');
331
376
  const result = await client.bindBrokerSession(claimant, {
377
+ executionId: liveExecutionForNode(claimant).executionId,
332
378
  piSessionId: 'claimant-session', sessionFile, pid: process.pid, reason: 'startup', reviewBoundaryIds: [],
333
379
  });
334
380
  assert.deepEqual(result, { action: 'shutdown' }, 'the second broker is refused before it can bind the live transcript');
@@ -351,7 +397,7 @@ test('reviveNode fresh-fallback clears the stale session identity (Major-3: no p
351
397
  // retried forever instead of surfaced as a boot failure.
352
398
  const missingFile = join(home, 'sessions', 'never-written.jsonl');
353
399
  const stalePid = deadPid();
354
- const M = h.fabricateBrokerNode({
400
+ const M = fabricateInScope(h, {
355
401
  status: 'active',
356
402
  intent: null,
357
403
  pi_pid: stalePid,
@@ -378,7 +424,7 @@ test('unbound forks replay one durable source file for node-id, path, and UUID o
378
424
  try {
379
425
  mkdirSync(home, { recursive: true });
380
426
  writeFileSync(forkFile, '{}\n', 'utf8');
381
- createNode(node('ordinary-fork-source', { cwd: home, pi_session_file: forkFile }));
427
+ createNodeWithSession(node('ordinary-fork-source', { cwd: home, pi_session_file: forkFile }));
382
428
  const sourceExposure = emptyContextExposureState();
383
429
  registerExposure(exposureTarget(sourceExposure, 'system'), 'source-system-only', 'content');
384
430
  registerExposure(exposureTarget(sourceExposure, 'transcript'), 'source-transcript', 'content');
@@ -392,7 +438,7 @@ test('unbound forks replay one durable source file for node-id, path, and UUID o
392
438
  const operands = ['ordinary-fork-source', forkFile, '019e8ce3-322e-7000-8000-000000000001'];
393
439
  for (const [index, operand] of operands.entries()) {
394
440
  const destination = `ordinary-fork-${index}`;
395
- createNode(node(destination, {
441
+ createNodeWithSession(node(destination, {
396
442
  cwd: home,
397
443
  fork_from: operand,
398
444
  fork_source_file: forkFile,
@@ -431,7 +477,7 @@ test('reviveNode throws a typed cwd_missing error and does NOT crash the node wh
431
477
  const h = await createHarness({ headless: true, sessionPrefix: 'crtr-revive-cwdmissing' });
432
478
  try {
433
479
  const missingCwd = join(home, 'never-created-cwd');
434
- const M = h.fabricateBrokerNode({
480
+ const M = fabricateInScope(h, {
435
481
  status: 'active',
436
482
  intent: null,
437
483
  pi_pid: deadPid(),
@@ -452,7 +498,7 @@ test('reviveNode relaunches fresh when the session header cwd was removed (NL-14
452
498
  const sessionFile = join(home, 'sessions', 'stale-cwd.jsonl');
453
499
  mkdirSync(join(home, 'sessions'), { recursive: true });
454
500
  writeFileSync(sessionFile, `${JSON.stringify({ type: 'session', version: 3, id: 'stale-session', cwd: removedCwd })}\n`, 'utf8');
455
- const M = h.fabricateBrokerNode({
501
+ const M = fabricateInScope(h, {
456
502
  status: 'active', intent: null, pi_pid: deadPid(), pi_session_id: 'stale-session', pi_session_file: sessionFile,
457
503
  });
458
504
  const result = await reviveNode(M, { resume: true, capacity: 'freeze', recovery: true });
@@ -471,7 +517,7 @@ test('reviveNode crashes and clears pid when launch/preflight throws', async ()
471
517
  const sessionFile = join(home, 'sessions', 'crtr-revive-fail.jsonl');
472
518
  mkdirSync(join(home, 'sessions'), { recursive: true });
473
519
  writeFileSync(sessionFile, '{}\n', 'utf8');
474
- const M = h.fabricateBrokerNode({
520
+ const M = fabricateInScope(h, {
475
521
  status: 'active',
476
522
  intent: null,
477
523
  pi_pid: deadPid(),
@@ -500,7 +546,7 @@ test('reviveNode fans a doctrine wake to subscribers when launch/preflight throw
500
546
  const sessionFile = join(home, 'sessions', 'crtr-revive-crashwake.jsonl');
501
547
  mkdirSync(join(home, 'sessions'), { recursive: true });
502
548
  writeFileSync(sessionFile, '{}\n', 'utf8');
503
- const M = h.fabricateBrokerNode({
549
+ const M = fabricateInScope(h, {
504
550
  parent: root,
505
551
  status: 'active',
506
552
  intent: null,
@@ -514,7 +560,7 @@ test('reviveNode fans a doctrine wake to subscribers when launch/preflight throw
514
560
  headlessBrokerHost.launch = (_nodeId, _inv, _opts) => { throw new Error('boom'); };
515
561
  await assert.rejects(reviveNode(M, { resume: true, capacity: 'freeze' }), /refusing to revive/i);
516
562
  assert.equal(h.node(M).status, 'dead', 'failed revive marks the node dead');
517
- const entries = readInboxSince(root, undefined);
563
+ const entries = wakeMail(root);
518
564
  const notice = entries.find((e) => e.data?.['reason'] === 'child-crashed' && e.data?.['child'] === M);
519
565
  assert.ok(notice !== undefined, 'the surviving parent got a doctrine wake for the dead child');
520
566
  assert.equal(notice.from, M, 'the notice is attributed to the crashed child');
@@ -546,18 +592,18 @@ test('an unstarted node relaunches as a birth, not a "revived fresh" kickoff', a
546
592
  };
547
593
  const missing = join(home, 'sessions', 'never-written.jsonl');
548
594
  // (1) Bare interactive node — nothing on disk anywhere: launch promptless.
549
- createNode(node('bare-unstarted', { cwd: home, pi_session_id: 'uuid-a', pi_session_file: missing }));
595
+ createNodeWithSession(node('bare-unstarted', { cwd: home, pi_session_id: 'uuid-a', pi_session_file: missing }));
550
596
  await reviveNode('bare-unstarted', { resume: true, capacity: 'freeze' });
551
597
  const bare = launches[0];
552
598
  assert.equal(bare.some((a) => isReviveKickoff(a)), false, 'an unstarted node is never told it lost context it never had');
553
599
  assert.deepEqual(bare, buildPiArgv(getNode('bare-unstarted'), {}).argv, 'a promptless boot — the argv carries no trailing prompt positional at all');
554
600
  // (2) Spawned with a mandate whose first turn never persisted: replay it.
555
- createNode(node('mandate-unstarted', { cwd: home, pi_session_id: 'uuid-b', pi_session_file: missing }));
601
+ createNodeWithSession(node('mandate-unstarted', { cwd: home, pi_session_id: 'uuid-b', pi_session_file: missing }));
556
602
  writeGoal('mandate-unstarted', 'do the thing');
557
603
  await reviveNode('mandate-unstarted', { resume: true, capacity: 'freeze' });
558
604
  assert.equal(launches[1].at(-1), 'do the thing', 'a birth replays the spawn mandate');
559
605
  // (3) Real durable progress on disk → still a genuine fresh revive.
560
- createNode(node('progressed', { cwd: home, pi_session_id: 'uuid-c', pi_session_file: missing }));
606
+ createNodeWithSession(node('progressed', { cwd: home, pi_session_id: 'uuid-c', pi_session_file: missing }));
561
607
  seedRoadmap('progressed', { goal: 'ship it' });
562
608
  await reviveNode('progressed', { resume: true, capacity: 'freeze' });
563
609
  assert.equal(isReviveKickoff(launches[2].at(-1)), true, 'a node with a roadmap still rebuilds from the kickoff');
@@ -9,9 +9,13 @@ import { join } from 'node:path';
9
9
  import { createServer } from 'node:net';
10
10
  import { spawn as spawnProcess } from 'node:child_process';
11
11
  import { createNode, getNode, getRow, subscribersOf } from '../../canvas/canvas.js';
12
- import { coalesce, readInboxSince } from '../../feed/inbox.js';
12
+ import { coalesce } from '../../feed/inbox.js';
13
13
  import { coalesceBrokerInbox } from '../../runtime/broker/inbox.js';
14
- import { closeDb } from '../../canvas/db.js';
14
+ import { closeDb, migrate, openDb } from '../../canvas/db.js';
15
+ import { resetSession } from '../../canvas/sessions.js';
16
+ import { closeScopedStores } from '../../scoped-state/db.js';
17
+ import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
18
+ import { userScopeRoot } from '../../scope.js';
15
19
  import { nodeDir } from '../../canvas/paths.js';
16
20
  import { spawnNode } from '../../runtime/nodes.js';
17
21
  import { readGoal } from '../../runtime/kickoff.js';
@@ -19,12 +23,35 @@ import { spawnChild, resolveSpawner } from '../../runtime/spawn.js';
19
23
  import { headlessBrokerHost } from '../../runtime/host.js';
20
24
  import { resetFleetForTests } from '../../runtime/fleet.js';
21
25
  import { bindTestFleet } from '../helpers/fleet.js';
22
- import { recordFault } from '../../runtime/fault.js';
26
+ import { recordFault } from '../../canvas/recovery.js';
23
27
  import { piSessionsRoot } from '../../runtime/pi-vendored.js';
24
28
  import { emptyContextExposureState, exposureTarget, loadContextExposureState, registerExposure, saveContextExposureState, } from '../../substrate/injected-store.js';
25
29
  import { ROOT_PROFILE_ID } from '../../profiles/manifest.js';
26
30
  let home;
31
+ let scopedHome;
32
+ let previousHome;
27
33
  let previousPidfile;
34
+ function initializeTestUserScope() {
35
+ initializeFreshScopedStore({ kind: 'user', root: userScopeRoot() });
36
+ }
37
+ function mailFor(nodeId) {
38
+ return openDb().prepare(`SELECT m.message_id, m.operation_id, m.created_at, m.sender_node_id, m.sender_name, m.kind, m.label, m.body_ref, m.body_json, m.disposition, d.urgency
39
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
40
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.canceled_at IS NULL
41
+ ORDER BY m.seq`).all(nodeId).map((row) => ({
42
+ entry_id: row['message_id'],
43
+ operation_id: row['operation_id'],
44
+ ts: row['created_at'],
45
+ from: row['sender_node_id'],
46
+ ...(row['sender_name'] === null ? {} : { from_name: row['sender_name'] }),
47
+ tier: row['urgency'],
48
+ kind: row['kind'],
49
+ ...(row['body_ref'] === null ? {} : { ref: row['body_ref'] }),
50
+ label: row['label'],
51
+ ...(row['body_json'] === null ? {} : { data: JSON.parse(row['body_json']) }),
52
+ ...(row['disposition'] === null ? {} : { disposition: row['disposition'] }),
53
+ }));
54
+ }
28
55
  function spawner(id) {
29
56
  return {
30
57
  node_id: id,
@@ -41,6 +68,9 @@ before(() => {
41
68
  // Unix-domain socket paths are capped at ~104 bytes on macOS; keep the
42
69
  // canvas home short because root readiness now opens a real view.sock.
43
70
  home = mkdtempSync('/tmp/crtr-spawn-root-');
71
+ scopedHome = mkdtempSync('/tmp/crtr-spawn-root-scope-');
72
+ previousHome = process.env['HOME'];
73
+ process.env['HOME'] = scopedHome;
44
74
  process.env['CRTR_HOME'] = home;
45
75
  previousPidfile = process.env['CRTR_PIDFILE'];
46
76
  process.env['CRTR_PIDFILE'] = join(home, 'crtrd.pid');
@@ -50,8 +80,12 @@ before(() => {
50
80
  });
51
81
  beforeEach(() => {
52
82
  closeDb();
83
+ closeScopedStores();
53
84
  rmSync(home, { recursive: true, force: true });
85
+ rmSync(userScopeRoot(), { recursive: true, force: true });
54
86
  mkdirSync(home, { recursive: true });
87
+ migrate(openDb());
88
+ initializeTestUserScope();
55
89
  // These tests exercise direct spawn behavior, not daemon autostart. Keep
56
90
  // ensureDaemon() from launching a background daemon whose startup verifier
57
91
  // would outlive the test's next CRTR_HOME reset.
@@ -59,16 +93,22 @@ beforeEach(() => {
59
93
  });
60
94
  after(() => {
61
95
  closeDb();
96
+ closeScopedStores();
62
97
  resetFleetForTests();
63
98
  rmSync(home, { recursive: true, force: true });
99
+ rmSync(scopedHome, { recursive: true, force: true });
100
+ if (previousHome === undefined)
101
+ delete process.env['HOME'];
102
+ else
103
+ process.env['HOME'] = previousHome;
64
104
  if (previousPidfile === undefined)
65
105
  delete process.env['CRTR_PIDFILE'];
66
106
  else
67
107
  process.env['CRTR_PIDFILE'] = previousPidfile;
68
108
  });
69
- test('managed child: parent on the spine + active subscription + provenance', () => {
109
+ test('managed child: parent on the spine + active subscription + provenance', async () => {
70
110
  createNode(spawner('A'));
71
- const child = spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
111
+ const child = await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
72
112
  const meta = getNode(child.node_id);
73
113
  assert.equal(meta.parent, 'A', 'child keeps its spine parent');
74
114
  assert.equal(meta.spawned_by, 'A', 'child provenance defaults to its parent');
@@ -78,13 +118,13 @@ test('managed child: parent on the spine + active subscription + provenance', ()
78
118
  assert.equal(subs[0].node_id, 'A');
79
119
  assert.equal(subs[0].active, true, 'parent subscription is active');
80
120
  });
81
- test('external child birth is deferred, persists its creator, and renders its age at delivery in both inbox mirrors', () => {
121
+ test('external child birth is deferred, persists its creator, and renders its age at delivery in both inbox mirrors', async () => {
82
122
  createNode(spawner('A'));
83
- const creator = spawnNode({ kind: 'general', cwd: '/tmp/work', parent: null, name: 'Core' });
84
- const child = spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: creator.node_id, name: 'worker' });
123
+ const creator = await spawnNode({ kind: 'general', cwd: '/tmp/work', parent: null, name: 'Core' });
124
+ const child = await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: creator.node_id, name: 'worker' });
85
125
  const saved = getNode(child.node_id);
86
126
  assert.equal(saved.creator, creator.node_id, 'creator persists through meta.json and getNode');
87
- const [entry] = readInboxSince('A');
127
+ const [entry] = mailFor('A');
88
128
  assert.ok(entry, 'the parent receives exactly one birth entry');
89
129
  assert.equal(entry.from, child.node_id, 'birth groups with the child’s future reports');
90
130
  assert.equal(entry.tier, 'deferred', 'birth never starts a parent cycle');
@@ -106,14 +146,14 @@ test('external child birth is deferred, persists its creator, and renders its ag
106
146
  assert.doesNotMatch(card, /\[\[CRTR_BIRTH_ANNOUNCEMENT_AGE\]\]/, 'the marker is not visible to the recipient');
107
147
  }
108
148
  }
109
- spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
110
- assert.equal(readInboxSince('A').length, 1, 'a parent-created child stays silent');
111
- spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: null });
112
- assert.equal(readInboxSince('A').length, 1, 'a root never announces');
149
+ await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'A', creator: 'A' });
150
+ assert.equal(mailFor('A').length, 1, 'a parent-created child stays silent');
151
+ await spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: null });
152
+ assert.equal(mailFor('A').length, 1, 'a root never announces');
113
153
  });
114
- test('independent root: provenance only, no parent, no subscription', () => {
154
+ test('independent root: provenance only, no parent, no subscription', async () => {
115
155
  createNode(spawner('A'));
116
- const root = spawnNode({
156
+ const root = await spawnNode({
117
157
  kind: 'developer',
118
158
  cwd: '/tmp/work',
119
159
  parent: null,
@@ -135,10 +175,10 @@ test('resolveSpawner: a --root spawn needs no caller; a managed child does (regr
135
175
  assert.equal(resolveSpawner('A', null, false), 'A', 'explicit parent satisfies a child spawn');
136
176
  assert.throws(() => resolveSpawner(undefined, null, false), /requires a calling node/, 'a managed child with no caller still throws');
137
177
  });
138
- test('unknown parent: spawnNode throws and mints NO node dir / row (validate before create)', () => {
178
+ test('unknown parent: spawnNode throws and mints NO node dir / row (validate before create)', async () => {
139
179
  // Pre-allocate the would-be id so we can prove nothing was scaffolded for it.
140
180
  const orphanId = 'orphan-under-ghost';
141
- assert.throws(() => spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'ghost', nodeId: orphanId }), /cannot spawn under unknown parent node: ghost/);
181
+ await assert.rejects(() => spawnNode({ kind: 'developer', cwd: '/tmp/work', parent: 'ghost', nodeId: orphanId }), /cannot spawn under unknown parent node: ghost/);
142
182
  // No half-born orphan: no meta (getNode null), no db row, no node dir on disk.
143
183
  assert.equal(getNode(orphanId), null, 'no meta.json written for the orphan');
144
184
  assert.equal(getRow(orphanId), null, 'no index row written for the orphan');
@@ -178,6 +218,7 @@ test('fork spawn resolves path and UUID provenance to the same transcript-only e
178
218
  const launched = [];
179
219
  try {
180
220
  process.env['HOME'] = localHome;
221
+ initializeTestUserScope();
181
222
  process.env['PI_CODING_AGENT_DIR'] = join(localHome, 'pi-agent');
182
223
  process.env['CRTR_NODE_ID'] = 'fork-source';
183
224
  delete process.env['TMUX'];
@@ -195,9 +236,8 @@ test('fork spawn resolves path and UUID provenance to the same transcript-only e
195
236
  createNode({
196
237
  ...spawner('fork-source'),
197
238
  cwd: localCwd,
198
- pi_session_id: sessionId,
199
- pi_session_file: sourceFile,
200
239
  });
240
+ resetSession('fork-source', { sessionId, transcriptPath: sourceFile });
201
241
  const canonicalSourceFile = getNode('fork-source').pi_session_file;
202
242
  const sourceExposure = emptyContextExposureState();
203
243
  registerExposure(exposureTarget(sourceExposure, 'system'), 'source-system', 'content');
@@ -243,6 +283,7 @@ test('fork spawn resolves path and UUID provenance to the same transcript-only e
243
283
  }
244
284
  finally {
245
285
  headlessBrokerHost.launch = originalLaunch;
286
+ closeScopedStores();
246
287
  if (previousHome === undefined)
247
288
  delete process.env['HOME'];
248
289
  else
@@ -281,6 +322,7 @@ test('spawnChild: a root from a shell gets the stable root profile id', async ()
281
322
  const previousStdoutTty = Object.getOwnPropertyDescriptor(process.stdout, 'isTTY');
282
323
  try {
283
324
  process.env.HOME = localHome;
325
+ initializeTestUserScope();
284
326
  Object.defineProperty(process.stdin, 'isTTY', { value: false, configurable: true, writable: true });
285
327
  Object.defineProperty(process.stdout, 'isTTY', { value: false, configurable: true, writable: true });
286
328
  Reflect.deleteProperty(env, 'CRTR_NODE_ID');
@@ -303,6 +345,7 @@ test('spawnChild: a root from a shell gets the stable root profile id', async ()
303
345
  }
304
346
  finally {
305
347
  headlessBrokerHost.launch = originalLaunch;
348
+ closeScopedStores();
306
349
  if (previousHome === undefined)
307
350
  delete process.env.HOME;
308
351
  else
@@ -1,7 +1,7 @@
1
1
  // Run with: npm run test:integration
2
2
  //
3
3
  // THE DAEMON WAKE HALF of delivery-vs-wake, on a graph >=2 levels deep. Routing
4
- // itself (active → inbox.jsonl, passive → passive.jsonl) is owned by
4
+ // itself (active → wake delivery, passive → non-waking delivery) is owned by
5
5
  // passive-subscription.test.ts; what this file owns is the decision ONE real
6
6
  // `superviseTick` makes on that routing: the ACTIVE subscriber of a finished
7
7
  // target is revived, the PASSIVE subscriber on the same target is not.
@@ -30,27 +30,40 @@
30
30
  import { test } from 'node:test';
31
31
  import assert from 'node:assert/strict';
32
32
  import { spawnSync } from 'node:child_process';
33
- import { existsSync, readFileSync } from 'node:fs';
34
33
  import { createHarness } from '../helpers/harness.js';
35
- import { closeDb } from '../../canvas/db.js';
34
+ import { openDb } from '../../canvas/db.js';
36
35
  import { subscribe } from '../../canvas/canvas.js';
37
36
  import { pushFinal } from '../../feed/feed.js';
38
- import { passivePath } from '../../canvas/paths.js';
39
37
  // A pid reaped (dead) by the time spawnSync returns — a released broker's exited
40
38
  // process (the headless analog of a closed pane).
41
39
  function deadPid() {
42
40
  const r = spawnSync('true', [], { stdio: 'ignore' });
43
41
  return r.pid ?? 0x7ffffffe;
44
42
  }
45
- // LOCAL helper: read a node's PASSIVE accumulator (passive.jsonl) straight off
46
- // the data layer, mirroring the harness's own `inbox()` reader. The harness
47
- // exposes inbox() but NOT passive() — this fills that gap.
43
+ // Read the canonical non-waking delivery rows, mirroring the harness's SQL
44
+ // wake-delivery reader.
48
45
  function passive(nodeId) {
49
- closeDb();
50
- const path = passivePath(nodeId);
51
- if (!existsSync(path))
52
- return [];
53
- return readFileSync(path, 'utf8').split('\n').filter(Boolean).map((line) => JSON.parse(line));
46
+ return openDb().prepare(`SELECT m.message_id AS entry_id, m.operation_id, m.created_at AS ts,
47
+ m.sender_node_id AS "from", m.sender_name AS from_name, d.urgency AS tier,
48
+ m.kind, m.body_ref AS ref, m.label, m.body_json AS data, m.disposition
49
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
50
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.wake = 0
51
+ ORDER BY m.seq`).all(nodeId).map((row) => {
52
+ const value = row;
53
+ return {
54
+ entry_id: value['entry_id'],
55
+ operation_id: value['operation_id'],
56
+ ts: value['ts'],
57
+ from: value['from'],
58
+ ...(value['from_name'] === null ? {} : { from_name: value['from_name'] }),
59
+ tier: value['tier'],
60
+ kind: value['kind'],
61
+ ...(value['ref'] === null ? {} : { ref: value['ref'] }),
62
+ label: value['label'],
63
+ ...(value['data'] === null ? {} : { data: JSON.parse(value['data']) }),
64
+ ...(value['disposition'] === null ? {} : { disposition: value['disposition'] }),
65
+ };
66
+ });
54
67
  }
55
68
  // `{node_id, active}` arrays → a stable comparable set of `id:active|passive`.
56
69
  function edgeSet(arr) {
@@ -87,15 +100,14 @@ test('one daemon tick revives the active subscriber of a finished target and lea
87
100
  assert.equal(h.node(B).cycles ?? 0, 0, 'B not yet revived');
88
101
  assert.equal(h.node(P).cycles ?? 0, 0, 'P not yet revived');
89
102
  // T FINISHES via the model layer — the precondition the daemon decides on:
90
- // B's pointer lands in inbox.jsonl (the wake channel), P's in passive.jsonl.
103
+ // B gets a wake delivery, P gets a non-waking delivery.
91
104
  await pushFinal(T, 'TARGET-FINAL-BODY: the worker completed');
92
- closeDb();
93
- assert.ok(h.inbox(B).find((e) => e.from === T && e.kind === 'final'), 'ACTIVE subscriber B: T-final pointer delivered to inbox.jsonl');
94
- assert.equal(h.inbox(P).length, 0, 'PASSIVE subscriber P: inbox.jsonl stays empty (no wake channel)');
95
- assert.equal(passive(P).length, 1, 'PASSIVE subscriber P: exactly one passive entry');
105
+ assert.ok(h.inbox(B).find((e) => e.from === T && e.kind === 'final'), 'ACTIVE subscriber B: T-final wake delivery is durable');
106
+ assert.equal(h.inbox(P).length, 0, 'PASSIVE subscriber P: wake-delivery set stays empty');
107
+ assert.equal(passive(P).length, 1, 'PASSIVE subscriber P: exactly one non-waking delivery');
96
108
  // The DAEMON decision pass. Its 2nd pass revives an idle-released node
97
- // ONLY on an unseen INBOX entry. B has one (active) → REVIVED. P has none
98
- // (its entry went to passive.jsonl) → stays idle. Decided in ONE tick.
109
+ // ONLY on an unseen wake delivery. B has one (active) → REVIVED. P has none
110
+ // (its entry is non-waking) → stays idle. Decided in ONE tick.
99
111
  await h.tick();
100
112
  // ACTIVE subscriber: WOKEN — reviveNode bumped cycles + transition(revive).
101
113
  {
@@ -111,8 +123,8 @@ test('one daemon tick revives the active subscriber of a finished target and lea
111
123
  assert.equal(p.status, 'idle', 'P STILL idle — daemon did NOT wake it');
112
124
  assert.equal(p.intent, 'idle-release', 'P still intent=idle-release (untouched)');
113
125
  }
114
- assert.equal(passive(P).length, 1, "P's passive entry is still pending (drained only on its next message)");
115
- assert.equal(h.inbox(P).length, 0, "P's inbox still empty");
126
+ assert.equal(passive(P).length, 1, "P's non-waking delivery is still pending (drained only on its next message)");
127
+ assert.equal(h.inbox(P).length, 0, "P's wake-delivery set is still empty");
116
128
  }
117
129
  finally {
118
130
  await h.dispose();
@@ -13,7 +13,8 @@ import { createManagedWorktree, closeManagedWorktree, managedWorktreePath, rollb
13
13
  import { closeManagedWorktreeAsync } from '../../worktree-close.js';
14
14
  import { reconcileManagedWorktree } from '../../worktree-sweep.js';
15
15
  import { createNode, getNode } from '../../canvas/canvas.js';
16
- import { closeDb } from '../../canvas/db.js';
16
+ import { closeDb, migrate, openDb } from '../../canvas/db.js';
17
+ import { resetSession } from '../../canvas/sessions.js';
17
18
  let home;
18
19
  let remoteDir;
19
20
  let sourceDir;
@@ -61,6 +62,7 @@ beforeEach(() => {
61
62
  closeDb();
62
63
  rmSync(home, { recursive: true, force: true });
63
64
  process.env['CRTR_HOME'] = home;
65
+ migrate(openDb());
64
66
  });
65
67
  after(() => {
66
68
  closeDb();
@@ -81,7 +83,8 @@ test('closeManagedWorktree leaves the active caller checkout intact with named d
81
83
  mkdirSync(sessionDir, { recursive: true });
82
84
  const sessionFile = join(sessionDir, 'session.jsonl');
83
85
  writeFileSync(sessionFile, `${JSON.stringify({ type: 'session', version: 3, id: 'wt-session', cwd: wt.path })}\n`);
84
- createNode(node(nodeId, { cwd: wt.path, managed_worktree: wt, pi_session_id: 'wt-session', pi_session_file: sessionFile }));
86
+ createNode(node(nodeId, { cwd: wt.path, managed_worktree: wt }));
87
+ resetSession(nodeId, { sessionId: 'wt-session', transcriptPath: sessionFile });
85
88
  const result = closeManagedWorktree(nodeId);
86
89
  assert.equal(result.worktree_removed, false);
87
90
  assert.match(result.worktree_remove_error ?? '', /cleanup pending/);
@@ -22,10 +22,14 @@ import { headlessBrokerHost } from '../../runtime/host.js';
22
22
  import { reviveNode } from '../../runtime/revive.js';
23
23
  import { bindTestFleet } from '../helpers/fleet.js';
24
24
  import { resetFleetForTests } from '../../runtime/fleet.js';
25
- import { closeDb } from '../../canvas/db.js';
25
+ import { closeDb, migrate, openDb } from '../../canvas/db.js';
26
+ import { closeScopedStores } from '../../scoped-state/db.js';
27
+ import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
28
+ import { userScopeRoot } from '../../scope.js';
26
29
  let home;
27
30
  let remoteDir;
28
31
  let sourceDir;
32
+ let previousHome;
29
33
  const scratchRoot = join(homedir(), '.crouter', 'canvas', 'scratch');
30
34
  function run(args, cwd) {
31
35
  const res = gitSync(args, cwd);
@@ -53,6 +57,8 @@ before(() => {
53
57
  // directory. Every path-ownership check must judge identity, not spelling.
54
58
  mkdirSync(scratchRoot, { recursive: true });
55
59
  home = mkdtempSync(join(scratchRoot, 'crtr-worktree-home-'));
60
+ previousHome = process.env['HOME'];
61
+ process.env['HOME'] = home;
56
62
  process.env['CRTR_HOME'] = home;
57
63
  // Bare "remote" wired up as origin so the test can prove landing NEVER pushes
58
64
  // to it (managed worktrees are based on and land onto local `main` only).
@@ -70,15 +76,23 @@ before(() => {
70
76
  });
71
77
  beforeEach(() => {
72
78
  closeDb();
79
+ closeScopedStores();
73
80
  rmSync(home, { recursive: true, force: true });
74
81
  process.env['CRTR_HOME'] = home;
82
+ migrate(openDb());
83
+ initializeFreshScopedStore({ kind: 'user', root: userScopeRoot() });
75
84
  });
76
85
  after(() => {
77
86
  closeDb();
87
+ closeScopedStores();
78
88
  rmSync(home, { recursive: true, force: true });
79
89
  rmSync(remoteDir, { recursive: true, force: true });
80
90
  rmSync(sourceDir, { recursive: true, force: true });
81
91
  delete process.env['CRTR_HOME'];
92
+ if (previousHome === undefined)
93
+ delete process.env['HOME'];
94
+ else
95
+ process.env['HOME'] = previousHome;
82
96
  });
83
97
  // Fix B (#327/#333): a managed worktree with zero commits ahead of its base
84
98
  // and nothing uncommitted has nothing to land — push-final's auto-drop path