@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
@@ -7,18 +7,23 @@ import { fileURLToPath } from 'node:url';
7
7
  import { spawnSync, spawn } from 'node:child_process';
8
8
  import { createServer } from 'node:net';
9
9
  import { createNode, getNode, subscribe } from '../canvas/canvas.js';
10
- import { closeDb } from '../canvas/db.js';
11
- import { readInboxSince } from '../feed/inbox.js';
10
+ import { closeDb, migrate, openDb } from '../canvas/db.js';
11
+ import { closeScopedStores } from '../scoped-state/db.js';
12
+ import { initializeFreshScopedStore } from '../scoped-state/migrate.js';
13
+ import { beginExecution, recordExecutionPid } from '../canvas/executions.js';
14
+ import { openTurn } from '../canvas/recovery.js';
12
15
  import { superviseTick } from '../../daemon/crtrd.js';
13
16
  import { readPidfile } from '../../daemon/pidfile.js';
14
17
  import { verifyDaemonStartup, waitForDaemonExit } from '../../daemon/manage.js';
15
18
  import { waitForBrokerViewSocket } from '../runtime/placement-tmux.js';
16
19
  import { viewSocketPath } from '../canvas/paths.js';
17
- import { markBusy, clearBusy } from '../runtime/busy.js';
18
20
  import { headlessBrokerHost } from '../runtime/host.js';
19
21
  import { bindTestFleet } from './helpers/fleet.js';
20
22
  import { resetFleetForTests } from '../runtime/fleet.js';
21
23
  let home;
24
+ let scopeHome;
25
+ let originalCwd;
26
+ let originalHome;
22
27
  let tf;
23
28
  function node(id, over = {}) {
24
29
  return {
@@ -43,20 +48,41 @@ function deadPid() {
43
48
  const r = spawnSync('true', [], { stdio: 'ignore' });
44
49
  return r.pid ?? 0x7ffffffe;
45
50
  }
51
+ function wakeMail(nodeId) {
52
+ return openDb().prepare(`SELECT m.sender_node_id AS "from", d.urgency AS tier, m.label
53
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
54
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.wake = 1
55
+ ORDER BY m.seq`).all(nodeId);
56
+ }
46
57
  before(() => {
47
58
  home = mkdtempSync(join(tmpdir(), 'crtr-daemon-boot-'));
59
+ scopeHome = mkdtempSync(join(tmpdir(), 'crtr-daemon-boot-home-'));
60
+ originalCwd = process.cwd();
61
+ originalHome = process.env['HOME'];
62
+ process.chdir(scopeHome);
63
+ process.env['HOME'] = scopeHome;
48
64
  process.env['CRTR_HOME'] = home;
49
65
  // superviseTick + exit-policy delivery require a bound fleet (D-2).
50
66
  tf = bindTestFleet();
51
67
  });
52
68
  beforeEach(() => {
53
69
  closeDb();
70
+ closeScopedStores();
54
71
  rmSync(home, { recursive: true, force: true });
72
+ initializeFreshScopedStore({ kind: 'user', root: join(scopeHome, '.crouter') });
73
+ migrate(openDb());
55
74
  });
56
75
  after(() => {
57
76
  closeDb();
77
+ closeScopedStores();
58
78
  rmSync(home, { recursive: true, force: true });
79
+ process.chdir(originalCwd);
80
+ rmSync(scopeHome, { recursive: true, force: true });
59
81
  delete process.env['CRTR_HOME'];
82
+ if (originalHome === undefined)
83
+ delete process.env['HOME'];
84
+ else
85
+ process.env['HOME'] = originalHome;
60
86
  tf.fleet.dispose();
61
87
  resetFleetForTests();
62
88
  });
@@ -66,13 +92,15 @@ after(() => {
66
92
  // exit is observation-only.
67
93
  test('never-booted broker is marked dead by the tick and surfaced to the parent as urgent', async () => {
68
94
  createNode(node('P', { kind: 'developer', lifecycle: 'resident' }));
95
+ const crashedPid = deadPid();
69
96
  createNode(node('C', {
70
97
  parent: 'P',
71
98
  kind: 'explore',
72
- pi_pid: deadPid(), // launch recorded its process before it died
99
+ pi_pid: crashedPid, // launch recorded its process before it died
73
100
  pi_session_id: null, // never booted
74
101
  intent: null,
75
102
  }));
103
+ recordExecutionPid(beginExecution('C', { daemonEpoch: 'test' }), 'C', crashedPid);
76
104
  spawnEdge('P', 'C');
77
105
  // The launch's real child exit is delivered synthetically here, then the
78
106
  // authoritative tick classifies the dead recorded pid as table row 8:
@@ -84,7 +112,7 @@ test('never-booted broker is marked dead by the tick and surfaced to the parent
84
112
  // Row 8 fans TWO entries: the urgent surfaceBootFailure push and the normal
85
113
  // doctrine wake (design flow B row 8 — crash + surfaceBootFailure + doctrine
86
114
  // wake), both from C.
87
- const inbox = readInboxSince('P');
115
+ const inbox = wakeMail('P');
88
116
  assert.equal(inbox.length, 2, 'parent received the boot-failure push and the doctrine wake');
89
117
  const urgent = inbox.find((e) => e.tier === 'urgent');
90
118
  assert.ok(urgent, 'boot failure is delivered as urgent');
@@ -105,16 +133,19 @@ test('a crashed booted child is relaunched on its saved session, not reaped, and
105
133
  };
106
134
  try {
107
135
  createNode(node('P2', { kind: 'developer', lifecycle: 'resident' }));
136
+ const crashedPid = deadPid();
108
137
  createNode(node('C2', {
109
138
  cwd: home, // a cwd that exists — reviveNode's preflight refuses a missing one
110
139
  parent: 'P2',
111
140
  kind: 'explore',
112
- pi_pid: deadPid(), // engine crashed — its recorded pid is dead
141
+ pi_pid: crashedPid, // engine crashed — its recorded pid is dead
113
142
  pi_session_id: '019e8f00-booted-once', // it booted before dying
114
143
  intent: null,
115
144
  }));
116
145
  spawnEdge('P2', 'C2');
117
- markBusy('C2'); // died mid-generation
146
+ const executionId = beginExecution('C2', { daemonEpoch: 'test' });
147
+ recordExecutionPid(executionId, 'C2', crashedPid);
148
+ openTurn(executionId, { reason: 'agent-start' }); // died mid-generation
118
149
  await superviseTick();
119
150
  const afterFirstTick = getNode('C2');
120
151
  assert.deepEqual(launches, [], 'the first tick records the failed launch without relaunching');
@@ -123,8 +154,7 @@ test('a crashed booted child is relaunched on its saved session, not reaped, and
123
154
  await superviseTick(Date.parse(afterFirstTick.respawn_not_before));
124
155
  assert.deepEqual(launches, ['C2'], 'the matured tick recovers the crash through the policy table');
125
156
  assert.equal(getNode('C2').status, 'active', 'crashed booted child is relaunched, NOT marked dead');
126
- assert.equal(readInboxSince('P2').length, 0, 'no false boot-failure / child-death push on a crash (wake relocated to push/close)');
127
- clearBusy('C2');
157
+ assert.equal(wakeMail('P2').length, 0, 'no false boot-failure / child-death push on a crash (wake relocated to push/close)');
128
158
  }
129
159
  finally {
130
160
  headlessBrokerHost.launch = originalLaunch;
@@ -193,17 +223,24 @@ test('verifyDaemonStartup waits for pidfile, live pid, and serving socket before
193
223
  assert.equal(socketServing, true, 'the Unix API socket must serve before success');
194
224
  assert.equal(slept >= 40, true, 'the guard must actually poll through socket readiness');
195
225
  });
196
- test('runDaemon claims ownership before activation and readiness', () => {
226
+ test('runDaemon initializes SQL before binding restricted repair', () => {
197
227
  const source = readFileSync(fileURLToPath(new URL('../../daemon/crtrd.ts', import.meta.url)), 'utf8');
198
228
  const runDaemonAt = source.indexOf('export async function runDaemon');
199
- const activationAt = source.indexOf('await ensureOnDiskMigrations();', runDaemonAt);
200
229
  const ownershipAt = source.indexOf('claimDaemonOwnership(', runDaemonAt);
201
- const readinessAt = source.indexOf('writePidfile();', runDaemonAt);
230
+ const profileAt = source.indexOf('runProfileManifestMigrations(', ownershipAt);
231
+ const migrateAt = source.indexOf('migrate(openDb());', profileAt);
232
+ const restrictionAt = source.indexOf("phase: 'initializing'", migrateAt);
233
+ const pidfileAt = source.indexOf('writePidfile();', restrictionAt);
234
+ const serverAt = source.indexOf('createApiServer(', pidfileAt);
235
+ const activationAt = source.indexOf('await activateOnDiskMigrations(', serverAt);
202
236
  assert.ok(runDaemonAt >= 0, 'runDaemon exists');
203
237
  assert.ok(ownershipAt > runDaemonAt, 'runDaemon claims ownership');
204
238
  assert.equal(source.slice(runDaemonAt, ownershipAt).includes('await '), false, 'ownership is claimed before startup can await');
205
- assert.ok(ownershipAt < activationAt, 'ownership precedes on-disk activation');
206
- assert.ok(activationAt < readinessAt, 'activation completes before readiness is published');
239
+ assert.ok(ownershipAt < profileAt, 'ownership precedes raw profile migration');
240
+ assert.ok(profileAt < migrateAt, 'profile migration precedes SQL migration');
241
+ assert.ok(migrateAt < restrictionAt, 'SQL migration precedes restriction state');
242
+ assert.ok(restrictionAt < pidfileAt && pidfileAt < serverAt, 'restriction precedes repair listener binding');
243
+ assert.ok(serverAt < activationAt, 'repair listener precedes on-disk activation');
207
244
  });
208
245
  // Regression for the cold-guest recreate blocker: the production startup window
209
246
  // must tolerate a slower cold start than the old 500ms bound.
@@ -20,7 +20,7 @@ export declare class SessionManager {
20
20
  * broker tests do not replay source history, but they must reach the same
21
21
  * post-fork model/session construction path as the real engine. */
22
22
  static forkFrom(_source: string, cwd: string): SessionManager;
23
- /** Resume: round-trip the .jsonl PATH the revive passed via `--session`. */
23
+ /** Resume: round-trip the saved session identity with its .jsonl path. */
24
24
  static open(path: string): SessionManager;
25
25
  getSessionId(): string;
26
26
  getSessionFile(): string;
@@ -91,7 +91,9 @@ export class SessionManager {
91
91
  const sessionFile = join(dir, 'fake-session.jsonl');
92
92
  if (!existsSync(sessionFile))
93
93
  writeFileSync(sessionFile, '');
94
- return new SessionManager(`fake-sess-${randomUUID()}`, sessionFile, false);
94
+ const sessionId = `fake-sess-${randomUUID()}`;
95
+ writeFileSync(`${sessionFile}.id`, sessionId);
96
+ return new SessionManager(sessionId, sessionFile, false);
95
97
  }
96
98
  /** Spawn-time fork: create the fork's distinct fake session. The focused
97
99
  * broker tests do not replay source history, but they must reach the same
@@ -99,9 +101,12 @@ export class SessionManager {
99
101
  static forkFrom(_source, cwd) {
100
102
  return SessionManager.create(cwd);
101
103
  }
102
- /** Resume: round-trip the .jsonl PATH the revive passed via `--session`. */
104
+ /** Resume: round-trip the saved session identity with its .jsonl path. */
103
105
  static open(path) {
104
- return new SessionManager(`fake-sess-resume-${randomUUID()}`, path, true);
106
+ const sessionId = readFileSync(`${path}.id`, 'utf8').trim();
107
+ if (sessionId === '')
108
+ throw new Error(`fake session identity is empty: ${path}.id`);
109
+ return new SessionManager(sessionId, path, true);
105
110
  }
106
111
  getSessionId() {
107
112
  return this.sessionId;
@@ -16,7 +16,7 @@ import { mkdtempSync, rmSync } from 'node:fs';
16
16
  import { tmpdir } from 'node:os';
17
17
  import { join } from 'node:path';
18
18
  import { openFocusRow, setFocusOccupant, setFocusPane, closeFocusRow, getFocusByNode, getFocusByPane, getFocusById, listFocuses, } from '../canvas/focuses.js';
19
- import { openDb, closeDb, MIGRATIONS } from '../canvas/db.js';
19
+ import { openDb, closeDb, migrate, MIGRATIONS } from '../canvas/db.js';
20
20
  import { focusOf, isFocused, focusByPane, focusedNodes, listFocuses as placementListFocuses, } from '../runtime/placement.js';
21
21
  let home;
22
22
  function userVersion(db) {
@@ -25,6 +25,11 @@ function userVersion(db) {
25
25
  function tableNames(db) {
26
26
  return db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((r) => r.name);
27
27
  }
28
+ function freshDb() {
29
+ const db = openDb();
30
+ migrate(db);
31
+ return db;
32
+ }
28
33
  before(() => {
29
34
  home = mkdtempSync(join(tmpdir(), 'crtr-focuses-'));
30
35
  process.env['CRTR_HOME'] = home;
@@ -40,13 +45,13 @@ after(() => {
40
45
  });
41
46
  // Migration v6 — the additive `focuses` table.
42
47
  test('a fresh db migrates to the latest version and has the focuses table', () => {
43
- const db = openDb();
48
+ const db = freshDb();
44
49
  assert.equal(userVersion(db), MIGRATIONS.length);
45
50
  assert.ok(tableNames(db).includes('focuses'), 'focuses table exists on a fresh db');
46
51
  });
47
52
  // Canvas setters / reads — open / setOccupant / setPane / close round-trip.
48
53
  test('open / setOccupant / close round-trip with the reads', () => {
49
- openDb();
54
+ freshDb();
50
55
  openFocusRow('f1', '%a', 'Sa', 'A');
51
56
  // getFocusByNode / getFocusByPane / getFocusById all resolve the same row.
52
57
  const byNode = getFocusByNode('A');
@@ -70,7 +75,7 @@ test('open / setOccupant / close round-trip with the reads', () => {
70
75
  assert.deepEqual(listFocuses(), []);
71
76
  });
72
77
  test('independent focus rows do not contend', () => {
73
- openDb();
78
+ freshDb();
74
79
  openFocusRow('f1', '%a', 'Sa', 'A');
75
80
  openFocusRow('f2', '%b', 'Sb', 'B');
76
81
  // Mutating one viewport leaves the other entirely intact.
@@ -89,7 +94,7 @@ test('independent focus rows do not contend', () => {
89
94
  // can't be fabricated in the ordinary suite, so we cover the prune + null-pane paths
90
95
  // here; the live-pane read is covered in the compiled-seam broker lifecycle suite.
91
96
  test('placement reads GC dead-pane viewer rows and keep null-pane rows', () => {
92
- openDb();
97
+ freshDb();
93
98
  openFocusRow('f1', '%a', 'Sa', 'A'); // %a is not a live tmux pane → pruned on read
94
99
  openFocusRow('f2', null, 'Sb', 'B'); // null pane (not realized yet) → kept
95
100
  // The dead-pane row is GC'd on read: placement returns null AND the row is
@@ -26,15 +26,19 @@ import { homedir, tmpdir } from 'node:os';
26
26
  import { join, dirname } from 'node:path';
27
27
  import { fileURLToPath } from 'node:url';
28
28
  import { createRequire } from 'node:module';
29
- import { createNode, getNode, recordPid, subscribersOf, subscriptionsOf, } from '../../canvas/canvas.js';
30
- import { closeDb } from '../../canvas/db.js';
29
+ import { createNode, getNode, subscribersOf, subscriptionsOf, } from '../../canvas/canvas.js';
30
+ import { closeDb, migrate, openDb } from '../../canvas/db.js';
31
+ import { beginExecution, liveExecutionForNode, recordExecutionPid } from '../../canvas/executions.js';
32
+ import { resetSession } from '../../canvas/sessions.js';
33
+ import { closeScopedStores } from '../../scoped-state/db.js';
34
+ import { initializeFreshScopedStore } from '../../scoped-state/migrate.js';
35
+ import { piAgentDir } from '../../runtime/package-health.js';
31
36
  import { spawnChild } from '../../runtime/spawn.js';
32
37
  import { superviseTick } from '../../../daemon/crtrd.js';
33
38
  import { DaemonFleet } from '../../../daemon/fleet.js';
34
39
  import { apiSocketPath } from '../../canvas/paths.js';
35
40
  import { bindDaemonEventSource, resetEventSourceForTests } from '../../events/source.js';
36
41
  import { bindFleet, resetFleetForTests } from '../../runtime/fleet.js';
37
- import { readInboxSince } from '../../feed/inbox.js';
38
42
  import { capturePidCommand, recordedPidLiveness } from '../../canvas/pid.js';
39
43
  // locations
40
44
  const HERE = dirname(fileURLToPath(import.meta.url));
@@ -157,7 +161,7 @@ export async function createHarness(opts = {}) {
157
161
  // short); a test-only artifact of the long temp base. Use a SHORT base + short
158
162
  // prefix for the canvas home so broker sockets fit. tmpHome (HOME) hosts no
159
163
  // socket, so it can stay on the standard temp base.
160
- const shortTmpBase = existsSync('/tmp') ? '/tmp' : tmpdir();
164
+ const shortTmpBase = tmpdir();
161
165
  const home = mkdtempSync(join(shortTmpBase, 'crh-'));
162
166
  const tmpHome = mkdtempSync(join(tmpdir(), 'crtr-harness-HOME-'));
163
167
  const session = `${opts.sessionPrefix ?? 'crtr-harness'}-${process.pid}-${Date.now().toString(36)}`;
@@ -202,7 +206,10 @@ export async function createHarness(opts = {}) {
202
206
  .join(' ');
203
207
  }
204
208
  process.env['CRTR_BROKER_ENGINE'] = FAKE_ENGINE;
209
+ initializeFreshScopedStore({ kind: 'user', root: join(tmpHome, '.crouter') });
210
+ initializeFreshScopedStore({ kind: 'provider', root: piAgentDir() });
205
211
  closeDb();
212
+ migrate(openDb());
206
213
  // The harness drives superviseTick() in-process, so it stands in for the
207
214
  // daemon process (crtrd.ts:2490 binds the same way at real-daemon startup).
208
215
  // Reset first: `bound` is a single-assignment process global with no other
@@ -432,6 +439,29 @@ export async function createHarness(opts = {}) {
432
439
  function bootCount(nodeId) {
433
440
  return readLines(join(nodeDir(nodeId), 'fake-pi.boots.jsonl')).length;
434
441
  }
442
+ function inbox(nodeId) {
443
+ return openDb().prepare(`SELECT m.message_id AS entry_id, m.operation_id, m.created_at AS ts,
444
+ m.sender_node_id AS "from", m.sender_name AS from_name, d.urgency AS tier,
445
+ m.kind, m.body_ref AS ref, m.label, m.body_json AS data, m.disposition
446
+ FROM deliveries d JOIN messages m ON m.message_id = d.message_id
447
+ WHERE d.destination_kind = 'node_inbox' AND d.destination_id = ? AND d.wake = 1
448
+ ORDER BY m.seq`).all(nodeId).map((row) => {
449
+ const value = row;
450
+ return {
451
+ entry_id: value['entry_id'],
452
+ operation_id: value['operation_id'],
453
+ ts: value['ts'],
454
+ from: value['from'],
455
+ ...(value['from_name'] === null ? {} : { from_name: value['from_name'] }),
456
+ tier: value['tier'],
457
+ kind: value['kind'],
458
+ ...(value['ref'] === null ? {} : { ref: value['ref'] }),
459
+ label: value['label'],
460
+ ...(value['data'] === null ? {} : { data: JSON.parse(value['data']) }),
461
+ ...(value['disposition'] === null ? {} : { disposition: value['disposition'] }),
462
+ };
463
+ });
464
+ }
435
465
  function injected(nodeId) {
436
466
  return readLines(join(nodeDir(nodeId), 'fake-pi.injected.jsonl'))
437
467
  .map((l) => {
@@ -470,16 +500,8 @@ export async function createHarness(opts = {}) {
470
500
  parent: null,
471
501
  };
472
502
  createNode(meta);
473
- // Every real bootRoot records its broker's pid synchronously at launch
474
- // (spawn.ts's bootRoot calls recordPid right after launchBrokerOrCrash,
475
- // well before any daemon tick) — a production root's pi_pid is never
476
- // null for longer than an instant. This in-process fixture has no real
477
- // broker to launch, so mint it a pid that is genuinely alive for the
478
- // rest of the test: this harness process's own pid (already threaded
479
- // into the id scheme above). The pid is metadata post-cutover (fleet
480
- // entries are the liveness authority), but status surfaces and identity
481
- // rechecks still read it, so keep the fixture row shaped like a real one.
482
- recordPid(id, process.pid);
503
+ const executionId = beginExecution(id, { daemonEpoch: fleet.epoch() });
504
+ recordExecutionPid(executionId, id, process.pid);
483
505
  closeDb();
484
506
  return id;
485
507
  },
@@ -502,6 +524,13 @@ export async function createHarness(opts = {}) {
502
524
  pi_pid: o.pi_pid ?? null,
503
525
  };
504
526
  createNode(meta);
527
+ if (o.pi_pid !== null && o.pi_pid !== undefined) {
528
+ const executionId = beginExecution(id, { daemonEpoch: fleet.epoch() });
529
+ recordExecutionPid(executionId, id, o.pi_pid);
530
+ }
531
+ if (o.pi_session_id !== null && o.pi_session_id !== undefined || o.pi_session_file !== null && o.pi_session_file !== undefined) {
532
+ resetSession(id, { sessionId: o.pi_session_id ?? '', transcriptPath: o.pi_session_file ?? null });
533
+ }
505
534
  closeDb();
506
535
  return id;
507
536
  },
@@ -631,7 +660,7 @@ export async function createHarness(opts = {}) {
631
660
  },
632
661
  inbox(nodeId) {
633
662
  closeDb();
634
- return readInboxSince(nodeId);
663
+ return inbox(nodeId);
635
664
  },
636
665
  injected,
637
666
  fakeCmd(nodeId, cmd) {
@@ -644,7 +673,7 @@ export async function createHarness(opts = {}) {
644
673
  catch {
645
674
  /* already gone */
646
675
  }
647
- await waitFor(() => apiServerProc.exitCode === null ? null : true, { label: 'harness API server exit' });
676
+ await waitFor(() => apiServerProc.exitCode === null && apiServerProc.signalCode === null ? null : true, { label: 'harness API server exit' });
648
677
  },
649
678
  dialogResults(nodeId) {
650
679
  return readLines(join(nodeDir(nodeId), 'fake-pi.dialog.jsonl'))
@@ -685,8 +714,9 @@ export async function createHarness(opts = {}) {
685
714
  for (const d of nodeDirs()) {
686
715
  try {
687
716
  const n = getNode(d);
688
- if (n?.pi_pid != null)
689
- pidsToKill.add(n.pi_pid);
717
+ const execution = liveExecutionForNode(d);
718
+ if (execution?.pid != null)
719
+ pidsToKill.add(execution.pid);
690
720
  }
691
721
  catch {
692
722
  /* row gone — fine */
@@ -726,6 +756,7 @@ export async function createHarness(opts = {}) {
726
756
  }
727
757
  }
728
758
  closeDb();
759
+ closeScopedStores();
729
760
  // Stop the standalone API-server process (its socket lives under home/,
730
761
  // removed below). SIGTERM lets it close() its listener and unlink the
731
762
  // socket; the pid is also in pidsToKill as a backstop.
@@ -735,6 +766,7 @@ export async function createHarness(opts = {}) {
735
766
  catch {
736
767
  /* already gone */
737
768
  }
769
+ await waitFor(() => apiServerProc.exitCode === null && apiServerProc.signalCode === null ? null : true, { label: 'harness API server exit' });
738
770
  // Leave the process-global event source + fleet unbound so the next
739
771
  // harness instance in this same test process can bind cleanly (see setup
740
772
  // above). dispose() the fleet first: a pending backoff-respawn timer
@@ -6,13 +6,13 @@
6
6
  // sender (`from`) is visible in both the read body and the artifact title.
7
7
  import { test, before, after, beforeEach } from 'node:test';
8
8
  import assert from 'node:assert/strict';
9
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
9
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
10
10
  import { tmpdir } from 'node:os';
11
11
  import { join } from 'node:path';
12
12
  import { createNode } from '../canvas/canvas.js';
13
- import { closeDb } from '../canvas/db.js';
13
+ import { closeDb, migrate, openDb } from '../canvas/db.js';
14
14
  import { contextDir } from '../canvas/paths.js';
15
- import { appendInbox } from '../feed/inbox.js';
15
+ import { enqueueNodeMail } from '../feed/messages.js';
16
16
  import { buildCorpus, nodeArtifacts, resolveRef } from '../canvas/history.js';
17
17
  let home;
18
18
  function node(id, cwd) {
@@ -34,6 +34,8 @@ before(() => {
34
34
  beforeEach(() => {
35
35
  closeDb();
36
36
  rmSync(home, { recursive: true, force: true });
37
+ mkdirSync(home, { recursive: true });
38
+ migrate(openDb());
37
39
  });
38
40
  after(() => {
39
41
  closeDb();
@@ -64,12 +66,9 @@ test('default history excludes inbox; --type inbox includes it, with full sender
64
66
  createNode(node('sender-1', cwd));
65
67
  const receiver = createNode(node('receiver-1', cwd));
66
68
  const longBody = 'a'.repeat(4000); // well past any old label-clip length
67
- appendInbox(receiver.node_id, {
68
- from: 'sender-1',
69
- tier: 'urgent',
70
- kind: 'message',
71
- label: 'first line of a long message',
72
- data: { body: longBody },
69
+ enqueueNodeMail(receiver.node_id, {
70
+ senderNodeId: 'sender-1', kind: 'message', label: 'first line of a long message',
71
+ bodyJson: { body: longBody }, urgency: 'urgent', wake: 1,
73
72
  });
74
73
  // (a) default corpus (no explicit types) excludes inbox entirely.
75
74
  const defaultCorpus = buildCorpus({ nodes: [receiver.node_id] }, {});
@@ -104,12 +103,9 @@ test('a spilled message body is read RAW — a leading frontmatter-shaped block
104
103
  // survive intact, unlike a report ref (which always has real frontmatter
105
104
  // stripped by fileBodyLoader).
106
105
  const body = '---\nfoo: bar\n---\n' + 'a'.repeat(4000);
107
- appendInbox(receiver.node_id, {
108
- from: 'sender-3',
109
- tier: 'normal',
110
- kind: 'message',
111
- label: 'first line',
112
- data: { body },
106
+ enqueueNodeMail(receiver.node_id, {
107
+ senderNodeId: 'sender-3', kind: 'message', label: 'first line',
108
+ bodyJson: { body }, urgency: 'normal', wake: 1,
113
109
  });
114
110
  const resolved = resolveRef(`${receiver.node_id}:inbox/000000`);
115
111
  assert.ok(resolved !== null);
@@ -118,7 +114,7 @@ test('a spilled message body is read RAW — a leading frontmatter-shaped block
118
114
  test('a null-from (system) inbox entry reads as "system", not blank or thrown', () => {
119
115
  const cwd = '/tmp/hist-work-2';
120
116
  const receiver = createNode(node('receiver-2', cwd));
121
- appendInbox(receiver.node_id, { from: null, tier: 'normal', kind: 'update', label: 'system note' });
117
+ enqueueNodeMail(receiver.node_id, { kind: 'update', label: 'system note', urgency: 'normal', wake: 1 });
122
118
  const resolved = resolveRef(`${receiver.node_id}:inbox/000000`);
123
119
  assert.ok(resolved !== null);
124
120
  assert.ok(resolved.body.includes('from: system'), 'null sender renders as "system"');
@@ -3,29 +3,55 @@ import assert from 'node:assert/strict';
3
3
  import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
- import { closeDb } from '../canvas/db.js';
7
- import { claimActionDelivery, enqueueActionDelivery, readActionDelivery, } from '../canvas/human-deliveries.js';
6
+ import { closeDb, migrate, openDb } from '../canvas/db.js';
7
+ import { ensureScopeInitialized } from '../config.js';
8
+ import { closeScopedStores } from '../scoped-state/db.js';
9
+ import { enqueueExternalAction } from '../feed/messages.js';
10
+ import { claimExternalActionDelivery, readExternalActionDelivery, settleDelivery } from '../canvas/deliveries.js';
8
11
  import { deliveryBackoffMs } from '../canvas/delivery-contract.js';
9
- import { deliverySettlementForOutcome } from '../../daemon/delivery/run-delivery.js';
10
- import { deliverAction } from '../../daemon/human/deliver-action.js';
12
+ import { deliverySettlementForOutcome, runDelivery } from '../../daemon/delivery/run-delivery.js';
11
13
  let home;
12
14
  const priorHome = process.env['CRTR_HOME'];
15
+ const priorUserHome = process.env['HOME'];
16
+ function actionDelivery(requestId) {
17
+ return readExternalActionDelivery(`human-action:${requestId}`);
18
+ }
19
+ function enqueueAction(requestId, argv, cwd, completion) {
20
+ return enqueueExternalAction({
21
+ destinationId: 'human_action',
22
+ adapter: { adapter: 'subprocess', argv, cwd },
23
+ bodyJson: completion,
24
+ idempotencyKey: `human-action:${requestId}`,
25
+ });
26
+ }
27
+ async function deliverAction(deliveryId, argv, cwd, document, attempt, claimOwner) {
28
+ await runDelivery({ argv, cwd, document, attempt }, (settlement) => settleDelivery(deliveryId, attempt, claimOwner, settlement, Date.now()));
29
+ }
13
30
  beforeEach(() => {
14
31
  closeDb();
15
32
  home = mkdtempSync(join(tmpdir(), 'crtr-human-action-delivery-'));
16
33
  process.env['CRTR_HOME'] = home;
34
+ process.env['HOME'] = home;
35
+ ensureScopeInitialized('user', join(home, '.crouter'));
36
+ migrate(openDb());
17
37
  });
18
38
  afterEach(() => {
19
39
  closeDb();
40
+ closeScopedStores();
20
41
  rmSync(home, { recursive: true, force: true });
21
42
  });
22
43
  after(() => {
23
44
  closeDb();
45
+ closeScopedStores();
24
46
  rmSync(home, { recursive: true, force: true });
25
47
  if (priorHome === undefined)
26
48
  delete process.env['CRTR_HOME'];
27
49
  else
28
50
  process.env['CRTR_HOME'] = priorHome;
51
+ if (priorUserHome === undefined)
52
+ delete process.env['HOME'];
53
+ else
54
+ process.env['HOME'] = priorUserHome;
29
55
  });
30
56
  test('delivery retries follow the durable backoff and exit boundary', () => {
31
57
  assert.equal(deliveryBackoffMs(1), 5_000);
@@ -92,15 +118,12 @@ test('the delivery child gets the consent boundary env, never crtrd\'s own', asy
92
118
  process.env['CRTRD_ONLY_SECRET'] = 'must-not-cross';
93
119
  const requestId = 'b'.repeat(64);
94
120
  try {
95
- enqueueActionDelivery({
96
- requestId,
97
- argv: [script],
98
- cwd: project,
99
- completion: { schema: 'crtr.human-completion/v1', requestId, delivery: { attempt: 0 } },
100
- });
101
- const claimed = claimActionDelivery(requestId, Date.now(), 'claimant');
121
+ enqueueAction(requestId, [script], project, { schema: 'crtr.human-completion/v1', requestId, delivery: { attempt: 0 } });
122
+ const delivery = actionDelivery(requestId);
123
+ assert.ok(delivery !== null);
124
+ const claimed = claimExternalActionDelivery(delivery.deliveryId, Date.now(), 'claimant');
102
125
  assert.ok(claimed !== null);
103
- await deliverAction(claimed, 'claimant');
126
+ await deliverAction(claimed.deliveryId, [script], project, { schema: 'crtr.human-completion/v1', requestId, delivery: { attempt: 0 } }, claimed.attempts, 'claimant');
104
127
  }
105
128
  finally {
106
129
  delete process.env['NL_CORE_URL'];
@@ -110,7 +133,7 @@ test('the delivery child gets the consent boundary env, never crtrd\'s own', asy
110
133
  else
111
134
  process.env['HOME'] = priorUserHome;
112
135
  }
113
- assert.equal(readActionDelivery(requestId)?.state, 'accepted');
136
+ assert.equal(actionDelivery(requestId)?.state, 'acknowledged');
114
137
  const childEnv = new Map(readFileSync(envDump, 'utf8').split('\n').flatMap((line) => {
115
138
  const split = line.indexOf('=');
116
139
  return split === -1 ? [] : [[line.slice(0, split), line.slice(split + 1)]];
@@ -122,21 +145,18 @@ test('the delivery child gets the consent boundary env, never crtrd\'s own', asy
122
145
  });
123
146
  test('the guarded claim admits only one contender', () => {
124
147
  const requestId = 'a'.repeat(64);
125
- enqueueActionDelivery({
126
- requestId,
127
- argv: ['/bin/true'],
128
- cwd: home,
129
- completion: { schema: 'crtr.human-completion/v1', delivery: { attempt: 0 } },
130
- });
148
+ enqueueAction(requestId, ['/bin/true'], home, { schema: 'crtr.human-completion/v1', delivery: { attempt: 0 } });
131
149
  const now = Date.now();
132
- const first = claimActionDelivery(requestId, now, 'claimant-a');
133
- const second = claimActionDelivery(requestId, now, 'claimant-b');
134
- assert.equal(first?.state, 'running');
135
- assert.equal(first?.attempt, 1);
150
+ const delivery = actionDelivery(requestId);
151
+ assert.ok(delivery !== null);
152
+ const first = claimExternalActionDelivery(delivery.deliveryId, now, 'claimant-a');
153
+ const second = claimExternalActionDelivery(delivery.deliveryId, now, 'claimant-b');
154
+ assert.equal(first?.state, 'handed_off');
155
+ assert.equal(first?.attempts, 1);
136
156
  assert.equal(second, null);
137
- assert.deepEqual(readActionDelivery(requestId) && {
138
- state: readActionDelivery(requestId).state,
139
- attempt: readActionDelivery(requestId).attempt,
140
- claimOwner: readActionDelivery(requestId).claimOwner,
141
- }, { state: 'running', attempt: 1, claimOwner: 'claimant-a' });
157
+ assert.deepEqual(actionDelivery(requestId) && {
158
+ state: actionDelivery(requestId).state,
159
+ attempt: actionDelivery(requestId).attempts,
160
+ claimOwner: actionDelivery(requestId).claimOwner,
161
+ }, { state: 'handed_off', attempt: 1, claimOwner: 'claimant-a' });
142
162
  });
@@ -8,6 +8,8 @@ import assert from 'node:assert/strict';
8
8
  import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
9
9
  import { tmpdir } from 'node:os';
10
10
  import { join } from 'node:path';
11
+ import { ensureScopeInitialized, writeRawConfig } from '../config.js';
12
+ import { closeScopedStores } from '../scoped-state/db.js';
11
13
  import { resolveHumanAction } from '../human-actions.js';
12
14
  import { createProfile } from '../profiles/manifest.js';
13
15
  import { resetScopeCache } from '../scope.js';
@@ -29,13 +31,16 @@ beforeEach(() => {
29
31
  delete process.env['CRTR_PROFILE_ID'];
30
32
  process.chdir(cwd);
31
33
  resetScopeCache();
34
+ ensureScopeInitialized('user', join(home, '.crouter'));
32
35
  });
33
36
  afterEach(() => {
34
37
  process.chdir(previousCwd);
38
+ closeScopedStores();
35
39
  rmSync(home, { recursive: true, force: true });
36
40
  resetScopeCache();
37
41
  });
38
42
  after(() => {
43
+ closeScopedStores();
39
44
  process.chdir(previousCwd);
40
45
  if (previousHome === undefined)
41
46
  delete process.env['HOME'];
@@ -57,10 +62,9 @@ function writeAction(root, relativeExecutable, relativeCwd) {
57
62
  test('nearest project declaration wins and resolves paths from its authoring root', () => {
58
63
  const userRoot = join(home, '.crouter');
59
64
  writeAction(userRoot, 'user-bin', 'user-cwd');
60
- mkdirSync(userRoot, { recursive: true });
61
- writeFileSync(join(userRoot, 'config.json'), JSON.stringify({
65
+ writeRawConfig('user', {
62
66
  humanActions: { release: { argv: ['user-bin'], cwd: 'user-cwd' }, 'user-only': { argv: ['user-bin'], cwd: 'user-cwd' } },
63
- }));
67
+ });
64
68
  const outer = join(home, 'work', 'outer');
65
69
  writeAction(outer, 'outer-bin', 'outer-cwd');
66
70
  mkdirSync(join(outer, '.crouter'), { recursive: true });