@north-light/crouter 0.3.233 → 0.3.234

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 (379) hide show
  1. package/dist/api/client.js +16 -15
  2. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +1 -1
  3. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +14 -17
  4. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -11
  5. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +8 -8
  6. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +10 -11
  7. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/pi-shell-runner.ts +2 -2
  8. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +3 -3
  9. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -7
  10. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +8 -8
  11. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +6 -6
  12. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/sysprompt-window.ts +3 -3
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +1 -1
  14. package/dist/clients/attach/chrome/ticket-panel.js +15 -2
  15. package/dist/clients/attach/overlays/graph.js +0 -3
  16. package/dist/clients/attach/overlays/mcp.js +1 -2
  17. package/dist/clients/attach/render/chat-view.js +0 -12
  18. package/dist/clients/attach/render/crtr-output.js +0 -2
  19. package/dist/clients/attach/render/tool-calls.js +0 -6
  20. package/dist/clients/attach/slash/dispatch.js +8 -11
  21. package/dist/clients/attach/viewer.js +532 -534
  22. package/dist/clients/conversation/projection.js +0 -2
  23. package/dist/clients/inbox/controller.js +22 -3
  24. package/dist/clients/inbox/review/companion-pane.js +0 -4
  25. package/dist/clients/inbox/review-adapter.js +0 -7
  26. package/dist/clients/inbox/tui/slots.d.ts +3 -3
  27. package/dist/clients/inbox/tui/types.d.ts +2 -2
  28. package/dist/commands/api-client.js +0 -8
  29. package/dist/commands/attention.js +0 -8
  30. package/dist/commands/canvas-browse.js +2 -1
  31. package/dist/commands/canvas-config.js +0 -10
  32. package/dist/commands/canvas-history.js +2 -1
  33. package/dist/commands/cron.js +5 -18
  34. package/dist/commands/dashboard.js +0 -2
  35. package/dist/commands/human/feedback.js +2 -1
  36. package/dist/commands/human/prompts.js +2 -5
  37. package/dist/commands/human/queue.js +2 -7
  38. package/dist/commands/human/request.js +0 -14
  39. package/dist/commands/human/review.js +2 -1
  40. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
  41. package/dist/commands/memory/find.js +2 -2
  42. package/dist/commands/memory/lint.js +2 -1
  43. package/dist/commands/memory/read.d.ts +2 -7
  44. package/dist/commands/memory/read.js +5 -16
  45. package/dist/commands/memory/shared.js +4 -7
  46. package/dist/commands/memory/write.js +4 -3
  47. package/dist/commands/node/bash.js +2 -1
  48. package/dist/commands/node/create.js +2 -1
  49. package/dist/commands/node/inspect.js +0 -6
  50. package/dist/commands/node/lifecycle.js +4 -13
  51. package/dist/commands/node/message.js +3 -2
  52. package/dist/commands/node/subscription.js +2 -1
  53. package/dist/commands/node/wait.js +4 -3
  54. package/dist/commands/node-context.js +2 -1
  55. package/dist/commands/node-lifecycle-revive.js +0 -2
  56. package/dist/commands/node-worktree.js +2 -1
  57. package/dist/commands/pkg/browse/actions.d.ts +1 -1
  58. package/dist/commands/pkg/browse/actions.js +4 -4
  59. package/dist/commands/pkg/browse/catalog.d.ts +1 -1
  60. package/dist/commands/pkg/browse/catalog.js +15 -15
  61. package/dist/commands/pkg/browse/command-view.d.ts +1 -1
  62. package/dist/commands/pkg/browse/command-view.js +3 -3
  63. package/dist/commands/pkg/browse/detail-panel.d.ts +1 -1
  64. package/dist/commands/pkg/browse/detail-panel.js +8 -8
  65. package/dist/commands/pkg/browse/doc-view.d.ts +1 -1
  66. package/dist/commands/pkg/browse/doc-view.js +3 -3
  67. package/dist/commands/pkg/browse/list-panel.d.ts +1 -1
  68. package/dist/commands/pkg/browse/list-panel.js +3 -3
  69. package/dist/commands/pkg/browse/model.d.ts +3 -3
  70. package/dist/commands/pkg/browse/shell.d.ts +2 -2
  71. package/dist/commands/pkg/browse/shell.js +13 -13
  72. package/dist/commands/pkg/plugin-manage.js +2 -1
  73. package/dist/commands/pkg/shared.js +0 -2
  74. package/dist/commands/profile/delete.js +2 -1
  75. package/dist/commands/push.js +2 -3
  76. package/dist/commands/revive.js +0 -2
  77. package/dist/commands/surface/node/placement.js +2 -1
  78. package/dist/commands/surface-edit.js +2 -1
  79. package/dist/commands/sys/config.js +0 -4
  80. package/dist/commands/sys/daemon.js +0 -10
  81. package/dist/commands/sys/doctor.js +3 -2
  82. package/dist/commands/sys/panels/broker-limits-panel.d.ts +2 -2
  83. package/dist/commands/sys/panels/broker-limits-panel.js +4 -4
  84. package/dist/commands/sys/panels/dependencies-panel.d.ts +2 -2
  85. package/dist/commands/sys/panels/dependencies-panel.js +2 -2
  86. package/dist/commands/sys/panels/keybindings-panel.d.ts +1 -1
  87. package/dist/commands/sys/panels/keybindings-panel.js +5 -5
  88. package/dist/commands/sys/panels/models-panel.d.ts +4 -4
  89. package/dist/commands/sys/panels/models-panel.js +11 -10
  90. package/dist/commands/sys/panels/plugins-panel.d.ts +2 -2
  91. package/dist/commands/sys/panels/plugins-panel.js +2 -2
  92. package/dist/commands/sys/panels/prefs-panel.d.ts +2 -2
  93. package/dist/commands/sys/panels/prefs-panel.js +4 -4
  94. package/dist/commands/sys/panels/profiles-panel.d.ts +1 -1
  95. package/dist/commands/sys/panels/profiles-panel.js +14 -13
  96. package/dist/commands/sys/panels/provider-panel.d.ts +3 -3
  97. package/dist/commands/sys/panels/provider-panel.js +8 -8
  98. package/dist/commands/sys/settings-shell.d.ts +3 -3
  99. package/dist/commands/sys/settings-shell.js +15 -15
  100. package/dist/commands/sys/setup-settings-runner.d.ts +3 -3
  101. package/dist/commands/sys/setup-settings-runner.js +5 -5
  102. package/dist/commands/sys/setup-wizard.d.ts +4 -4
  103. package/dist/commands/sys/setup-wizard.js +8 -8
  104. package/dist/commands/sys/support/prepare.js +4 -4
  105. package/dist/commands/sys/support/submit.js +2 -2
  106. package/dist/commands/sys/sync-deps.js +0 -2
  107. package/dist/commands/sys/sync-project-guidance.js +2 -1
  108. package/dist/commands/sys/sysprompt.js +2 -1
  109. package/dist/core/__tests__/boot.test.js +0 -8
  110. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +23 -1
  111. package/dist/core/__tests__/canvas-db-boundary.test.d.ts +1 -0
  112. package/dist/core/__tests__/canvas-db-boundary.test.js +46 -0
  113. package/dist/core/__tests__/canvas.test.js +1 -1
  114. package/dist/core/__tests__/child-death-wake.test.js +0 -2
  115. package/dist/core/__tests__/cron-armed-context-failure.test.d.ts +1 -0
  116. package/dist/core/__tests__/cron-armed-context-failure.test.js +145 -0
  117. package/dist/core/__tests__/daemon-wedge.test.js +0 -4
  118. package/dist/core/__tests__/dead-node-policy-table.test.js +0 -4
  119. package/dist/core/__tests__/fixtures/fake-engine.js +4 -14
  120. package/dist/core/__tests__/fixtures/fake-pi-host.js +5 -5
  121. package/dist/core/__tests__/focuses.test.js +0 -6
  122. package/dist/core/__tests__/fs-utils.test.d.ts +1 -0
  123. package/dist/core/__tests__/fs-utils.test.js +50 -0
  124. package/dist/core/__tests__/helpers/harness.js +1 -1
  125. package/dist/core/__tests__/inline-memory-refs.test.js +0 -2
  126. package/dist/core/__tests__/integration/broker-fork-seam.test.js +0 -2
  127. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +3 -16
  128. package/dist/core/__tests__/integration/command-plugins.test.js +0 -24
  129. package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -6
  130. package/dist/core/__tests__/integration/flagship-lifecycle.test.js +0 -26
  131. package/dist/core/__tests__/integration/live-mutation-verbs.test.js +0 -4
  132. package/dist/core/__tests__/integration/live-mutation.test.js +1 -5
  133. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +1 -5
  134. package/dist/core/__tests__/integration/revive.test.js +0 -12
  135. package/dist/core/__tests__/integration/subscription-delivery.test.js +0 -6
  136. package/dist/core/__tests__/integration/tmux-surface.test.js +24 -8
  137. package/dist/core/__tests__/integration/worktree-reap.test.js +0 -6
  138. package/dist/core/__tests__/lifecycle.test.js +0 -18
  139. package/dist/core/__tests__/memory-resolver-precedence.test.js +1 -1
  140. package/dist/core/__tests__/migration.test.js +2 -2
  141. package/dist/core/__tests__/on-read-dedup-resume.test.js +3 -3
  142. package/dist/core/__tests__/pid-identity-match.test.js +1 -1
  143. package/dist/core/__tests__/plugin-kinds.test.js +1 -1
  144. package/dist/core/__tests__/plugin-page-components.test.js +1 -1
  145. package/dist/core/__tests__/preview-registry-sync.test.js +42 -1
  146. package/dist/core/__tests__/respawn-throttle.test.js +0 -4
  147. package/dist/core/__tests__/revive-all.test.js +5 -5
  148. package/dist/core/__tests__/revive-parked-fresh.test.js +62 -11
  149. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +0 -8
  150. package/dist/core/__tests__/seam/broker-attach-stream.test.js +0 -8
  151. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +0 -2
  152. package/dist/core/__tests__/tmux-driver-boundary.test.d.ts +1 -0
  153. package/dist/core/__tests__/tmux-driver-boundary.test.js +50 -0
  154. package/dist/core/activity-signal.d.ts +7 -0
  155. package/dist/core/activity-signal.js +38 -0
  156. package/dist/core/auto-update.js +2 -1
  157. package/dist/core/bootstrap.js +2 -1
  158. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
  159. package/dist/core/canvas/__tests__/remote-transport.test.js +1 -1
  160. package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
  161. package/dist/core/canvas/browse/app.js +3 -2
  162. package/dist/core/canvas/browse/model.d.ts +1 -1
  163. package/dist/core/canvas/browse/model.js +0 -4
  164. package/dist/core/canvas/canvas.d.ts +22 -0
  165. package/dist/core/canvas/canvas.js +50 -18
  166. package/dist/core/canvas/crons.js +0 -6
  167. package/dist/core/canvas/db.d.ts +14 -20
  168. package/dist/core/canvas/db.js +30 -1332
  169. package/dist/core/canvas/focuses.js +0 -4
  170. package/dist/core/canvas/history.js +2 -1
  171. package/dist/core/canvas/migrations.d.ts +20 -0
  172. package/dist/core/canvas/migrations.js +1339 -0
  173. package/dist/core/canvas/nav-model.js +1 -11
  174. package/dist/core/canvas/nav-render.js +0 -8
  175. package/dist/core/canvas/remote-canvas-source.js +0 -2
  176. package/dist/core/canvas/render-source.js +31 -13
  177. package/dist/core/command-manifests/manifest.js +0 -10
  178. package/dist/core/command-manifests/schema.js +0 -4
  179. package/dist/core/command-plugins/transport/http-invoke.js +0 -14
  180. package/dist/core/command.js +0 -4
  181. package/dist/core/config.js +2 -1
  182. package/dist/core/feed/inbox.d.ts +10 -0
  183. package/dist/core/feed/inbox.js +18 -12
  184. package/dist/core/fs-utils.d.ts +9 -2
  185. package/dist/core/fs-utils.js +7 -15
  186. package/dist/core/help.js +0 -8
  187. package/dist/core/host-exports/export.d.ts +2 -2
  188. package/dist/core/host-exports/export.js +9 -12
  189. package/dist/core/human/answer.d.ts +2 -2
  190. package/dist/core/human/convention.d.ts +2 -5
  191. package/dist/core/human/convention.js +3 -13
  192. package/dist/core/human/page-schema.d.ts +2 -2
  193. package/dist/core/human/page-schema.js +2 -2
  194. package/dist/core/human/root.js +6 -23
  195. package/dist/core/human/scan.js +24 -7
  196. package/dist/core/human/tickets.d.ts +4 -1
  197. package/dist/core/human/tickets.js +20 -6
  198. package/dist/core/io.js +0 -12
  199. package/dist/core/memory/identity.js +0 -4
  200. package/dist/core/memory/inline-ref-inventory.js +3 -2
  201. package/dist/core/memory/lint.js +3 -6
  202. package/dist/core/memory-resolver.d.ts +5 -0
  203. package/dist/core/memory-resolver.js +14 -12
  204. package/dist/core/pagination.js +0 -2
  205. package/dist/core/preview-registry.d.ts +14 -0
  206. package/dist/core/preview-registry.js +2 -4
  207. package/dist/core/preview-result-path.js +2 -1
  208. package/dist/core/profiles/manifest.d.ts +37 -5
  209. package/dist/core/profiles/manifest.js +44 -24
  210. package/dist/core/profiles/select.js +0 -4
  211. package/dist/core/review/signal.js +4 -23
  212. package/dist/core/review/ticket-filter.d.ts +3 -3
  213. package/dist/core/review/ticket-filter.js +4 -9
  214. package/dist/core/runtime/__tests__/engine-route.test.js +1 -1
  215. package/dist/core/runtime/__tests__/spawn-env.test.js +0 -8
  216. package/dist/core/runtime/bearings-render.d.ts +56 -0
  217. package/dist/core/runtime/bearings-render.js +241 -0
  218. package/dist/core/runtime/bearings.d.ts +2 -17
  219. package/dist/core/runtime/bearings.js +12 -198
  220. package/dist/core/runtime/broker/auth-reload.d.ts +39 -0
  221. package/dist/core/runtime/broker/auth-reload.js +149 -0
  222. package/dist/core/runtime/broker/engine-drive.d.ts +26 -0
  223. package/dist/core/runtime/broker/engine-drive.js +257 -0
  224. package/dist/core/runtime/broker/engine-routing.d.ts +143 -0
  225. package/dist/core/runtime/broker/engine-routing.js +211 -0
  226. package/dist/core/runtime/broker/event-projection.js +3 -2
  227. package/dist/core/runtime/broker/fault-retry.js +5 -4
  228. package/dist/core/runtime/broker/frame-client.d.ts +38 -0
  229. package/dist/core/runtime/broker/frame-client.js +129 -0
  230. package/dist/core/runtime/broker/frame-dispatch.d.ts +12 -189
  231. package/dist/core/runtime/broker/frame-dispatch.js +89 -1476
  232. package/dist/core/runtime/broker/frame-engine-control.d.ts +20 -0
  233. package/dist/core/runtime/broker/frame-engine-control.js +231 -0
  234. package/dist/core/runtime/broker/frame-memory-refs.d.ts +19 -0
  235. package/dist/core/runtime/broker/frame-memory-refs.js +41 -0
  236. package/dist/core/runtime/broker/frame-model-query.d.ts +13 -0
  237. package/dist/core/runtime/broker/frame-model-query.js +69 -0
  238. package/dist/core/runtime/broker/frame-read.d.ts +19 -0
  239. package/dist/core/runtime/broker/frame-read.js +116 -0
  240. package/dist/core/runtime/broker/frame-replies.d.ts +24 -0
  241. package/dist/core/runtime/broker/frame-replies.js +64 -0
  242. package/dist/core/runtime/broker/frame-session.d.ts +34 -0
  243. package/dist/core/runtime/broker/frame-session.js +203 -0
  244. package/dist/core/runtime/broker/turn-ignition.d.ts +42 -0
  245. package/dist/core/runtime/broker/turn-ignition.js +74 -0
  246. package/dist/core/runtime/broker-extension-render.js +9 -85
  247. package/dist/core/runtime/broker-protocol.js +0 -4
  248. package/dist/core/runtime/broker-sdk.js +2 -1
  249. package/dist/core/runtime/broker.d.ts +1 -1
  250. package/dist/core/runtime/broker.js +5 -23
  251. package/dist/core/runtime/fault.js +3 -2
  252. package/dist/core/runtime/headless-pi.js +3 -2
  253. package/dist/core/runtime/host.js +2 -3
  254. package/dist/core/runtime/kickoff.js +0 -4
  255. package/dist/core/runtime/launch-target.js +2 -1
  256. package/dist/core/runtime/launch.js +3 -6
  257. package/dist/core/runtime/lifecycle.js +17 -26
  258. package/dist/core/runtime/managed-provider-cooling.d.ts +1 -1
  259. package/dist/core/runtime/naming.js +0 -2
  260. package/dist/core/runtime/nodes.js +2 -1
  261. package/dist/core/runtime/persona.js +0 -6
  262. package/dist/core/runtime/placement-tmux.js +2 -5
  263. package/dist/core/runtime/placement.js +4 -11
  264. package/dist/core/runtime/recap.js +4 -3
  265. package/dist/core/runtime/reopen.js +4 -7
  266. package/dist/core/runtime/reset.js +0 -4
  267. package/dist/core/runtime/resume-root.js +0 -2
  268. package/dist/core/runtime/revive.js +17 -13
  269. package/dist/core/runtime/session-list-cache.js +2 -2
  270. package/dist/core/runtime/shell-expansion.js +0 -2
  271. package/dist/core/runtime/spawn.js +0 -2
  272. package/dist/core/runtime/stream-watchdog.d.ts +0 -1
  273. package/dist/core/runtime/stream-watchdog.js +13 -18
  274. package/dist/core/runtime/tmux-bindings.js +0 -2
  275. package/dist/core/runtime/tmux-driver.d.ts +12 -0
  276. package/dist/core/runtime/tmux-driver.js +18 -13
  277. package/dist/core/runtime/warm-pool.js +0 -4
  278. package/dist/core/scope.js +4 -3
  279. package/dist/core/spawn.js +2 -3
  280. package/dist/core/subscription-state.js +10 -17
  281. package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +3 -3
  282. package/dist/core/substrate/render.js +0 -12
  283. package/dist/core/substrate/schema.js +0 -12
  284. package/dist/core/substrate/surface-match.js +0 -2
  285. package/dist/core/termrender/display.js +2 -1
  286. package/dist/core/tui/page-host.d.ts +3 -3
  287. package/dist/core/tui/page-host.js +4 -4
  288. package/dist/core/tui/panel.js +3 -3
  289. package/dist/core/wake.js +0 -12
  290. package/dist/core/worktree.js +0 -2
  291. package/dist/daemon/__tests__/reconciler-signature.test.d.ts +1 -0
  292. package/dist/daemon/__tests__/reconciler-signature.test.js +36 -0
  293. package/dist/daemon/api/__tests__/seam/api-server.test.js +2 -16
  294. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +3 -11
  295. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +0 -6
  296. package/dist/daemon/api/handlers/attach.js +2 -1
  297. package/dist/daemon/api/handlers/broker-ops.js +3 -6
  298. package/dist/daemon/api/handlers/canvas.js +12 -18
  299. package/dist/daemon/api/handlers/feedback-comments.js +0 -8
  300. package/dist/daemon/api/handlers/health.js +3 -2
  301. package/dist/daemon/api/handlers/human-requests.js +0 -10
  302. package/dist/daemon/api/handlers/human.js +1 -5
  303. package/dist/daemon/api/handlers/inbox.js +0 -18
  304. package/dist/daemon/api/handlers/memory.js +1 -1
  305. package/dist/daemon/api/handlers/messages.js +4 -8
  306. package/dist/daemon/api/handlers/nodes.js +10 -21
  307. package/dist/daemon/api/handlers/reports.js +1 -1
  308. package/dist/daemon/api/handlers/reviews.js +1 -6
  309. package/dist/daemon/api/map.js +0 -6
  310. package/dist/daemon/api/router.js +0 -4
  311. package/dist/daemon/api/server.js +2 -5
  312. package/dist/daemon/boot-migrations.d.ts +14 -0
  313. package/dist/daemon/boot-migrations.js +31 -0
  314. package/dist/daemon/cron/armed-context.d.ts +21 -0
  315. package/dist/daemon/cron/armed-context.js +100 -0
  316. package/dist/daemon/cron/capture.d.ts +20 -0
  317. package/dist/daemon/cron/capture.js +45 -0
  318. package/dist/daemon/cron/disposition.d.ts +19 -0
  319. package/dist/daemon/cron/disposition.js +52 -0
  320. package/dist/daemon/cron/escalation.d.ts +12 -0
  321. package/dist/daemon/cron/escalation.js +124 -0
  322. package/dist/daemon/cron/held.d.ts +4 -0
  323. package/dist/daemon/cron/held.js +20 -0
  324. package/dist/daemon/cron/lease-recovery.d.ts +3 -0
  325. package/dist/daemon/cron/lease-recovery.js +119 -0
  326. package/dist/daemon/cron/live-runs.d.ts +11 -0
  327. package/dist/daemon/cron/live-runs.js +23 -0
  328. package/dist/daemon/cron/passes.d.ts +11 -0
  329. package/dist/daemon/cron/passes.js +78 -0
  330. package/dist/daemon/cron/settle.d.ts +28 -0
  331. package/dist/daemon/cron/settle.js +37 -0
  332. package/dist/daemon/cron/sinks.d.ts +2 -0
  333. package/dist/daemon/cron/sinks.js +100 -0
  334. package/dist/daemon/cron-run.d.ts +5 -41
  335. package/dist/daemon/cron-run.js +193 -882
  336. package/dist/daemon/crtrd-cli.js +2 -1
  337. package/dist/daemon/crtrd.js +22 -22
  338. package/dist/daemon/fleet.js +0 -4
  339. package/dist/daemon/human/deliver-action.js +2 -1
  340. package/dist/daemon/human/sweep.js +15 -4
  341. package/dist/daemon/manage.d.ts +2 -2
  342. package/dist/daemon/manage.js +4 -13
  343. package/dist/daemon/pidfile.js +2 -2
  344. package/dist/daemon/reconcilers/broker-supervision.js +26 -29
  345. package/dist/daemon/reconcilers/dormant-inbox.d.ts +1 -1
  346. package/dist/daemon/reconcilers/dormant-inbox.js +2 -1
  347. package/dist/daemon/reconcilers/live-obligation.js +2 -2
  348. package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
  349. package/dist/daemon/reconcilers/pending-review-submit.js +2 -1
  350. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -1
  351. package/dist/daemon/reconcilers/storage-maintenance.js +2 -1
  352. package/dist/daemon/review/companion.js +2 -2
  353. package/dist/daemon/review/sweep.js +8 -3
  354. package/dist/migrations/001-surfaces-frontmatter.js +0 -6
  355. package/dist/migrations/corpus.js +2 -5
  356. package/dist/migrations/registry.js +15 -2
  357. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +0 -4
  358. package/dist/pi-extensions/broker-local.js +2 -1
  359. package/dist/pi-extensions/canvas-bash-valve.js +3 -17
  360. package/dist/pi-extensions/canvas-context-intro.js +2 -7
  361. package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
  362. package/dist/pi-extensions/canvas-goal-capture.js +2 -1
  363. package/dist/pi-extensions/canvas-inbox-watcher.js +5 -12
  364. package/dist/pi-extensions/canvas-passive-context.js +2 -5
  365. package/dist/pi-extensions/canvas-preview-result.js +2 -1
  366. package/dist/pi-extensions/canvas-prompt-scrub.js +2 -1
  367. package/dist/pi-extensions/canvas-recap.js +3 -16
  368. package/dist/pi-extensions/canvas-review-boundary.js +2 -5
  369. package/dist/pi-extensions/canvas-stophook.js +2 -26
  370. package/dist/pi-extensions/canvas-structured-output.js +2 -1
  371. package/dist/pi-extensions/canvas-tool-guide.js +2 -1
  372. package/dist/shared/__tests__/env-boundary.test.d.ts +1 -0
  373. package/dist/shared/__tests__/env-boundary.test.js +93 -0
  374. package/dist/shared/env.d.ts +68 -0
  375. package/dist/shared/env.js +171 -0
  376. package/package.json +3 -1
  377. package/runtime.lock.json +2 -2
  378. package/dist/core/canvas/render.d.ts +0 -47
  379. package/dist/core/canvas/render.js +0 -245
@@ -3,6 +3,7 @@
3
3
  // raw fatal boundary; a winning daemon stays alive through its scheduled loop.
4
4
  import '../suppress-experimental-warnings.js';
5
5
  import { runDaemon } from './crtrd.js';
6
+ import { envCrtrdTcp } from '../shared/env.js';
6
7
  /** Extract `--tcp <host:port>` (or `--tcp=<host:port>`) from argv; falls back to
7
8
  * `CRTRD_TCP`. Absent → unix socket only (§4.1). */
8
9
  function parseTcpArg(argv) {
@@ -13,6 +14,6 @@ function parseTcpArg(argv) {
13
14
  if (arg !== undefined && arg.startsWith('--tcp='))
14
15
  return arg.slice('--tcp='.length);
15
16
  }
16
- return process.env['CRTRD_TCP'];
17
+ return envCrtrdTcp();
17
18
  }
18
19
  await runDaemon({ tcp: parseTcpArg(process.argv.slice(2)) });
@@ -71,7 +71,7 @@ import { setInstallId } from '../core/runtime/nodes.js';
71
71
  import { claimDaemonOwnership, releaseDaemonOwnership } from '../core/canvas/daemon-owner.js';
72
72
  import { prewarmRecentRecipes, reapStaleSpares } from '../core/runtime/warm-pool.js';
73
73
  import { DaemonFleet } from './fleet.js';
74
- import { migrateLegacyInboxIdentities } from '../core/feed/inbox-migrate.js';
74
+ import { BOOT_MIGRATIONS } from './boot-migrations.js';
75
75
  import { emitEvent } from '../core/events/emit.js';
76
76
  import { operationIdContext } from '../core/events/operation-id.js';
77
77
  import { bindDaemonEventSource } from '../core/events/source.js';
@@ -88,7 +88,6 @@ import { ensureOnDiskMigrations } from '../migrations/activation.js';
88
88
  export function brokerCommandDeclaresCanvasHome(command, home) {
89
89
  return command.includes(` --canvas-home ${home} `);
90
90
  }
91
- // ---------------------------------------------------------------------------
92
91
  // Startup epoch census (design flow C step 1): before this daemon writes its
93
92
  // readiness mirror, reap every broker of a PRIOR epoch on its own canvas.
94
93
  // Ownership is never adopted across epochs — a prior daemon's ChildProcess
@@ -96,7 +95,6 @@ export function brokerCommandDeclaresCanvasHome(command, home) {
96
95
  // let the startup recovery sweep reclassify their rows through the policy
97
96
  // table. A broker whose argv carries NO `--epoch` tag is a legacy-generation
98
97
  // broker and gets the same verdict (reap): it predates epoch ownership.
99
- // ---------------------------------------------------------------------------
100
98
  /** The `--epoch <id>` tag host.ts stamps into every broker's argv, or null
101
99
  * for a legacy/untagged broker (which the census treats as prior-epoch). */
102
100
  export function brokerCommandEpoch(command) {
@@ -113,7 +111,11 @@ async function reapPriorEpochBrokers(epoch) {
113
111
  // No process table — the census cannot run. The startup recovery sweep
114
112
  // still reclassifies rows; a surviving prior-epoch broker is at worst a
115
113
  // duplicate engine risk the next daemon boot's census retries.
116
- emitEvent({ level: 'warn', event: 'daemon.census.snapshot_failed' });
114
+ emitEvent({
115
+ level: 'error',
116
+ event: 'daemon.census.snapshot_failed',
117
+ fields: { invariant: 'prior-epoch brokers are reaped before readiness' },
118
+ });
117
119
  return;
118
120
  }
119
121
  const home = crtrHome();
@@ -177,7 +179,6 @@ async function reapPriorEpochBrokers(epoch) {
177
179
  fields: { prior_epoch_brokers: victims.length, sigtermed, sigkilled },
178
180
  });
179
181
  }
180
- // ---------------------------------------------------------------------------
181
182
  // Daemon-shutdown fleet teardown (invariant 2: no broker outlives the daemon
182
183
  // epoch that launched it). SIGTERM every live entry, race each handle's real
183
184
  // exit against ONE shared bounded grace, then SIGKILL each survivor's captured
@@ -185,7 +186,6 @@ async function reapPriorEpochBrokers(epoch) {
185
186
  // startup recovery sweep classifies them through the policy table, exactly as
186
187
  // it would after a daemon crash, so clean shutdown and crash converge on one
187
188
  // recovery path.
188
- // ---------------------------------------------------------------------------
189
189
  const FLEET_TEARDOWN_GRACE_MS = 5_000;
190
190
  async function teardownFleet(fleet) {
191
191
  const entries = Array.from(fleet.entries());
@@ -270,9 +270,7 @@ export async function runStartupRecoverySweep(fleet) {
270
270
  });
271
271
  }
272
272
  }
273
- // ---------------------------------------------------------------------------
274
273
  // Pidfile
275
- // ---------------------------------------------------------------------------
276
274
  function writePidfile() {
277
275
  // Ensure the pidfile parent exists before writing.
278
276
  mkdirSync(dirname(pidfilePath()), { recursive: true });
@@ -341,8 +339,8 @@ export async function superviseTick(now = Date.now(), lifecycle = directTickLife
341
339
  dormantInbox.run(now, { rows, fleet, hasBrokerCapacity });
342
340
  cronLane.run(now, { lifecycle, hasBrokerCapacity });
343
341
  humanDeliveryLane.run(now, { lifecycle });
344
- pendingReviewSubmit.run({ lifecycle });
345
- storageMaintenance.run(now);
342
+ pendingReviewSubmit.run(now, { lifecycle });
343
+ storageMaintenance.run(now, {});
346
344
  }
347
345
  /** Start the supervisor loop after winning the authoritative canvas claim.
348
346
  *
@@ -446,7 +444,6 @@ export async function runDaemon(opts = {}) {
446
444
  process.off('exit', onExit);
447
445
  }
448
446
  }
449
- // -------------------------------------------------------------------------
450
447
  // Handover — the daemon replaces ITSELF (POST /v1/daemon/restart)
451
448
  //
452
449
  // The routine reason to restart crtrd is to put a newly selected runtime
@@ -462,7 +459,6 @@ export async function runDaemon(opts = {}) {
462
459
  // pi persisting the tool result. It is NOT a correctness fallback; a broker
463
460
  // SIGTERMed mid-turn still gets `abortAndExit`'s own bounded persistence
464
461
  // window, and the successor's startup recovery sweep resumes it either way.
465
- // -------------------------------------------------------------------------
466
462
  const HANDOVER_ACK_GRACE_MS = 1_000;
467
463
  let handoverArmed = false;
468
464
  /** Arm the handover. Idempotent: a retry while one is already scheduled
@@ -684,17 +680,21 @@ export async function runDaemon(opts = {}) {
684
680
  });
685
681
  }
686
682
  });
687
- // Pre-cutover inbox state (rows without identities, ISO-timestamp cursors)
688
- // is fatal to the strict reader and crash-loops every legacy broker at
689
- // inbox seed; migrate it once before supervision can revive anything.
683
+ // Boot-time filesystem repairs (design: src/daemon/boot-migrations.ts) run
684
+ // unconditionally, in order, before supervision can revive anything.
685
+ // Today's one entry is the pre-cutover inbox-identity migration: rows
686
+ // without identities or ISO-timestamp cursors are fatal to the strict
687
+ // reader and crash-loop every legacy broker at inbox seed.
690
688
  operationIdContext.fresh(() => {
691
- const inboxMigration = migrateLegacyInboxIdentities();
692
- if (inboxMigration.nodes > 0 || inboxMigration.failures > 0) {
693
- emitEvent({
694
- level: 'info',
695
- event: 'daemon.inbox_identity.migrated',
696
- fields: { ...inboxMigration },
697
- });
689
+ for (const migration of BOOT_MIGRATIONS) {
690
+ const result = migration.apply();
691
+ if (result.changed) {
692
+ emitEvent({
693
+ level: 'info',
694
+ event: 'daemon.boot_migration.applied',
695
+ fields: { migration: migration.id, ...result.summary },
696
+ });
697
+ }
698
698
  }
699
699
  });
700
700
  // Startup epoch census (design flow C step 1): reap every prior-epoch
@@ -23,10 +23,8 @@ import { hasPendingWake } from './reconcilers/live-obligation.js';
23
23
  import { reviveNode } from '../core/runtime/revive.js';
24
24
  import { pushUrgent } from '../core/feed/feed.js';
25
25
  import { emitEvent } from '../core/events/emit.js';
26
- // ---------------------------------------------------------------------------
27
26
  // Shared crash helpers (moved from crtrd.ts — the fleet's policy paths are now
28
27
  // their primary consumers; crtrd imports them back for its surviving passes).
29
- // ---------------------------------------------------------------------------
30
28
  /** Surface a vehicle that never booted (or exhausted its respawn budget).
31
29
  *
32
30
  * A pi that dies before its first session_start (so `pi_session_id` was never
@@ -111,9 +109,7 @@ export function classifyDeadNode(row, busy, pendingWake = true) {
111
109
  return 'respawn-fresh'; // row 7: bounded ordinary fork-birth retry
112
110
  return 'boot-failure'; // row 8
113
111
  }
114
- // ---------------------------------------------------------------------------
115
112
  // Respawn throttle (replaces the stranded-relaunch counters, event-driven)
116
- // ---------------------------------------------------------------------------
117
113
  /** An exit with uptime at or above this resets the consecutive-failure
118
114
  * counter before classification; a shorter-lived exit increments it. */
119
115
  export const HEALTHY_UPTIME_MS = 60_000;
@@ -1,3 +1,4 @@
1
+ import { envHomeOverride } from '../../shared/env.js';
1
2
  import { spawn } from 'node:child_process';
2
3
  import { settleActionDelivery, } from '../../core/canvas/human-deliveries.js';
3
4
  import { killProcessGroup } from '../../core/canvas/pid.js';
@@ -158,7 +159,7 @@ function actionDeliveryEnv(cwd) {
158
159
  const env = buildOperationalEnvBase({ targetCwd: cwd, targetProfileId: null });
159
160
  // Runtime identity, not host state: an action that calls `crtr` must reach
160
161
  // the daemon that delivered to it, exactly as a cron body does.
161
- const canvasHome = process.env['CRTR_HOME'];
162
+ const canvasHome = envHomeOverride();
162
163
  if (canvasHome !== undefined && canvasHome !== '')
163
164
  env['CRTR_HOME'] = canvasHome;
164
165
  return env;
@@ -1,11 +1,16 @@
1
- import { readdirSync, statSync } from 'node:fs';
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { getRow } from '../../core/canvas/canvas.js';
4
4
  import { getReviewByBridge } from '../../core/review/store.js';
5
5
  import { emitEvent } from '../../core/events/emit.js';
6
+ import { reviewPath } from '../../core/human/convention.js';
6
7
  import { ticketsRoot } from '../../core/human/root.js';
7
8
  import { readTicketResult } from '../../core/human/tickets.js';
8
- import { deliverTerminalResult, releaseActionDelivery, settleFeedbackCompanion } from './finish.js';
9
+ import { cancelHumanTicket, deliverTerminalResult, releaseActionDelivery, settleFeedbackCompanion } from './finish.js';
10
+ /** No surface can open a review ticket that has no canonical row, so cancellation is its
11
+ * only reachable end. Boot runs this before API traffic, and a row is committed before its
12
+ * ticket is published, so an in-flight review is never mistaken for one of these. */
13
+ const UNBACKED_REVIEW_REASON = 'this review predates canonical review records and cannot be opened; re-file the review';
9
14
  function isTerminalBridge(status) {
10
15
  return status === 'done' || status === 'dead' || status === 'canceled';
11
16
  }
@@ -21,12 +26,18 @@ export async function reconcileUndeliveredTickets() {
21
26
  if (!statSync(dir).isDirectory())
22
27
  continue;
23
28
  scanned += 1;
29
+ const canonical = getReviewByBridge(entry) !== null;
24
30
  const ticketResult = readTicketResult(dir);
25
- if (ticketResult === null)
31
+ if (ticketResult === null) {
32
+ if (!canonical && existsSync(reviewPath(dir))) {
33
+ await cancelHumanTicket(entry, { reason: UNBACKED_REVIEW_REASON, actor: 'system' });
34
+ delivered += 1;
35
+ }
26
36
  continue;
37
+ }
27
38
  // Canonical reviews own terminal delivery and bridge retirement. Their
28
39
  // finisher retries only when its winning operation is explicitly repeated.
29
- if (getReviewByBridge(entry) !== null)
40
+ if (canonical)
30
41
  continue;
31
42
  // A terminal page ticket retires its feedback companion whether or not a
32
43
  // reply bridge exists: inbox-only pages have no bridge row, yet the
@@ -7,8 +7,8 @@
7
7
  * singleton supervisor, never "a node" itself, so none of this belongs in its
8
8
  * env regardless of whether today's code happens to read it — and at least one
9
9
  * of these IS actively read: `host.ts`'s broker-engine resolution falls back to
10
- * `process.env['CRTR_BROKER_ENGINE']` verbatim (nodeEnv() never sets that key,
11
- * so nothing overrides it per child launch), so a daemon that inherits a
10
+ * `envBrokerEngine()`'s raw `CRTR_BROKER_ENGINE` read verbatim (nodeEnv() never
11
+ * sets that key, so nothing overrides it per child launch), so a daemon that inherits a
12
12
  * stale/dev override throws inside `headlessBrokerHost.launch()` on EVERY
13
13
  * relaunch it ever attempts, for its whole lifetime — the 2026-07-06 diagnosis
14
14
  * root cause behind 19 nodes killed with "failed to relaunch and is now dead". */
@@ -15,9 +15,8 @@ import { hostExecPath } from '../core/runtime/branded-host.js';
15
15
  // reaches openDb) so the CLI daemon front door stays canvas.db-free (plan B-0).
16
16
  import { isDaemonRunning, readPidfile, isPidAlive } from './pidfile.js';
17
17
  import { DAEMON_STARTUP_WINDOW_MS } from './startup-policy.js';
18
- // ---------------------------------------------------------------------------
18
+ import { envNoDaemonAutostart } from '../shared/env.js';
19
19
  // Daemon env sanitization
20
- // ---------------------------------------------------------------------------
21
20
  /** Env keys that must NEVER reach the daemon process. Restarting crtrd is
22
21
  * overwhelmingly done from inside an agent node's own bash tool (`crtr sys
23
22
  * daemon stop && start`), which inherits that node's FULL env — its identity
@@ -27,8 +26,8 @@ import { DAEMON_STARTUP_WINDOW_MS } from './startup-policy.js';
27
26
  * singleton supervisor, never "a node" itself, so none of this belongs in its
28
27
  * env regardless of whether today's code happens to read it — and at least one
29
28
  * of these IS actively read: `host.ts`'s broker-engine resolution falls back to
30
- * `process.env['CRTR_BROKER_ENGINE']` verbatim (nodeEnv() never sets that key,
31
- * so nothing overrides it per child launch), so a daemon that inherits a
29
+ * `envBrokerEngine()`'s raw `CRTR_BROKER_ENGINE` read verbatim (nodeEnv() never
30
+ * sets that key, so nothing overrides it per child launch), so a daemon that inherits a
32
31
  * stale/dev override throws inside `headlessBrokerHost.launch()` on EVERY
33
32
  * relaunch it ever attempts, for its whole lifetime — the 2026-07-06 diagnosis
34
33
  * root cause behind 19 nodes killed with "failed to relaunch and is now dead". */
@@ -76,9 +75,7 @@ export function sanitizedDaemonEnv() {
76
75
  delete env[key];
77
76
  return env;
78
77
  }
79
- // ---------------------------------------------------------------------------
80
78
  // Entry point resolution
81
- // ---------------------------------------------------------------------------
82
79
  /** Locate the package or generation root from a regular build, the standalone
83
80
  * attach bundle, or a tsx source checkout. */
84
81
  function generationRoot() {
@@ -117,9 +114,7 @@ async function resolveDaemonLaunch() {
117
114
  }
118
115
  return [result.runtime.entryPath];
119
116
  }
120
- // ---------------------------------------------------------------------------
121
117
  // Stray-daemon sweep
122
- // ---------------------------------------------------------------------------
123
118
  /** A crtrd declares its owning canvas in argv, so ownership is an exact match
124
119
  * on this process's canvas home. Untagged daemons retain their own
125
120
  * supervision and are never counted or killed here. */
@@ -191,9 +186,7 @@ export function sweepStrayDaemons(keepPid) {
191
186
  }
192
187
  return strays;
193
188
  }
194
- // ---------------------------------------------------------------------------
195
189
  // daemon launch — low-level spawn/exec
196
- // ---------------------------------------------------------------------------
197
190
  /** Replace PID 1 with the selected daemon generation. Docker keeps a container
198
191
  * alive only while PID 1 lives, so a handover there must preserve its pid. */
199
192
  export async function execDaemon() {
@@ -376,9 +369,7 @@ export async function spawnDaemon() {
376
369
  }
377
370
  return { started: true, pid };
378
371
  }
379
- // ---------------------------------------------------------------------------
380
372
  // ensureDaemon — fire-and-forget front-door helper
381
- // ---------------------------------------------------------------------------
382
373
  /** Whether autostart is suppressed for this invocation. `--no-autostart` or
383
374
  * `CRTR_NO_DAEMON_AUTOSTART=1` forces fail-loud on a cold socket (spec §7.1) —
384
375
  * for environments that manage crtrd externally and want the diagnostic, and
@@ -393,7 +384,7 @@ export async function spawnDaemon() {
393
384
  * explicit `crtr sys daemon start` calls `spawnDaemon` directly and is
394
385
  * deliberately unaffected. */
395
386
  export function autostartDisabled() {
396
- return process.env['CRTR_NO_DAEMON_AUTOSTART'] === '1' || process.argv.includes('--no-autostart');
387
+ return envNoDaemonAutostart() || process.argv.includes('--no-autostart');
397
388
  }
398
389
  /** Start the daemon if it is not already running. No-op if already up, or if
399
390
  * autostart is suppressed for this invocation.
@@ -13,13 +13,13 @@ import { existsSync, readFileSync } from 'node:fs';
13
13
  import { join } from 'node:path';
14
14
  import { crtrHome } from '../core/canvas/paths.js';
15
15
  import { isPidAlive } from '../core/canvas/pid.js';
16
+ import { envPidfileOverride } from '../shared/env.js';
16
17
  // Re-exported so `isPidAlive` stays reachable from the daemon layer's public
17
18
  // surface (crtrd.ts already re-exports it; callers may reach it either way).
18
19
  export { isPidAlive };
19
20
  /** Absolute path to crtrd's pidfile. `CRTR_PIDFILE` overrides for tests. */
20
21
  export function pidfilePath() {
21
- const override = process.env['CRTR_PIDFILE'];
22
- return override !== undefined && override !== '' ? override : join(crtrHome(), 'crtrd.pid');
22
+ return envPidfileOverride() ?? join(crtrHome(), 'crtrd.pid');
23
23
  }
24
24
  /** Read the pid stored in the pidfile, or null if absent / malformed. */
25
25
  export function readPidfile() {
@@ -17,7 +17,7 @@ import { reloadAuthLive } from '../../core/runtime/auth-reload.js';
17
17
  import { readConfig } from '../../core/config.js';
18
18
  import { emitEvent } from '../../core/events/emit.js';
19
19
  import { operationIdContext } from '../../core/events/operation-id.js';
20
- import { readCursor, readInboxSince } from '../../core/feed/inbox.js';
20
+ import { hasUnseenWakeMail } from '../../core/feed/inbox.js';
21
21
  import { activeBackgroundBashJobs } from '../../core/bash-jobs.js';
22
22
  import { focusOf } from '../../core/runtime/placement.js';
23
23
  import { hasAttachedBrokerViewers } from '../../core/runtime/broker/client-registry.js';
@@ -26,18 +26,12 @@ import { BrokerUnreachableError } from '../../core/runtime/broker-request.js';
26
26
  import { formatCard, PARK_SUMMARY_PROMPT } from '../../shared/generated-context.js';
27
27
  import { beginPendingPark, consumePendingPark, pendingParkIds, pendingParkStartedAt } from '../park-pending.js';
28
28
  import { applyParkEvent, parkEvent } from './live-obligation.js';
29
+ import { envMaxLiveBrokers, envNoDaemonAutostart, envTestParkSummaryGraceMs, envTestUnattendedParkMs, envWarnLiveBrokers, } from '../../shared/env.js';
29
30
  const execFileAsync = promisify(execFile);
30
- function configuredPositiveInteger(name, fallback) {
31
- const raw = process.env[name];
32
- if (raw === undefined || raw === '')
33
- return fallback;
34
- const n = Number(raw);
35
- return Number.isSafeInteger(n) && n >= 1 ? n : fallback;
36
- }
37
31
  export function brokerThresholdsForDaemon() {
38
32
  const configured = readConfig('user').brokerThresholds;
39
- const automaticReviveCap = configuredPositiveInteger('CRTR_MAX_LIVE_BROKERS', configured.automaticReviveCap);
40
- const warning = configuredPositiveInteger('CRTR_WARN_LIVE_BROKERS', configured.warning);
33
+ const automaticReviveCap = envMaxLiveBrokers() ?? configured.automaticReviveCap;
34
+ const warning = envWarnLiveBrokers() ?? configured.warning;
41
35
  return { warning: Math.min(warning, automaticReviveCap), automaticReviveCap };
42
36
  }
43
37
  export const YIELD_STALL_GRACE_MS = 3 * 60_000;
@@ -51,18 +45,12 @@ const DEFAULT_UNATTENDED_PARK_MS = 15 * 60_000;
51
45
  // stuck parking turn cannot hold a broker for a whole extra clock period.
52
46
  const DEFAULT_PARK_SUMMARY_GRACE_MS = 5 * 60_000;
53
47
  export function unattendedParkMsForDaemon() {
54
- const raw = process.env['CRTR_TEST_UNATTENDED_PARK_MS'];
55
- if (raw === undefined)
48
+ if (!envNoDaemonAutostart())
56
49
  return DEFAULT_UNATTENDED_PARK_MS;
57
- const parsed = Number(raw);
58
- return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_UNATTENDED_PARK_MS;
50
+ return envTestUnattendedParkMs() ?? DEFAULT_UNATTENDED_PARK_MS;
59
51
  }
60
52
  export function parkSummaryGraceMsForDaemon() {
61
- const raw = process.env['CRTR_TEST_PARK_SUMMARY_GRACE_MS'];
62
- if (raw === undefined)
63
- return DEFAULT_PARK_SUMMARY_GRACE_MS;
64
- const parsed = Number(raw);
65
- return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_PARK_SUMMARY_GRACE_MS;
53
+ return envTestParkSummaryGraceMs() ?? DEFAULT_PARK_SUMMARY_GRACE_MS;
66
54
  }
67
55
  export function yieldStallVerdict(piPidAlive, intent, busy, stalledFor) {
68
56
  if (piPidAlive !== true)
@@ -152,7 +140,7 @@ export class BrokerSupervisionReconciler {
152
140
  lastOverCapWarnAt = Number.NEGATIVE_INFINITY;
153
141
  lastCapacityLogAt = Number.NEGATIVE_INFINITY;
154
142
  async run(now, ctx) {
155
- await this.handleAuthReload(now, ctx.fleet);
143
+ await this.handleAuthReload(ctx.fleet);
156
144
  const thresholds = brokerThresholdsForDaemon();
157
145
  if (ctx.fleet.size() >= thresholds.warning && ctx.lifecycle.acceptsDetachedWork()) {
158
146
  const warning = operationIdContext.fresh(() => this.warnOverCapInTmux(ctx.fleet.size(), thresholds.warning, thresholds.automaticReviveCap));
@@ -218,7 +206,7 @@ export class BrokerSupervisionReconciler {
218
206
  && (fault.retry.nextAt !== undefined || nextFaultRetry(fault, now) !== null);
219
207
  if (activeAutoProviderRetry)
220
208
  return true;
221
- return readInboxSince(id, readCursor(id)).some((entry) => entry.tier !== 'deferred');
209
+ return hasUnseenWakeMail(id);
222
210
  }
223
211
  handleUnattendedParking(id, row, livePid, now, ctx) {
224
212
  // An unclaimed warm-pool spare is not a conversation: the booted broker it
@@ -371,14 +359,19 @@ export class BrokerSupervisionReconciler {
371
359
  timeout: 2000,
372
360
  });
373
361
  }
374
- catch {
375
- /* no display channel for this client */
362
+ catch (err) {
363
+ emitEvent({
364
+ level: 'error',
365
+ event: 'broker.capacity.tmux_display_failed',
366
+ error: err,
367
+ fields: { client },
368
+ });
376
369
  }
377
370
  }
378
371
  }));
379
372
  }
380
- catch {
381
- /* no tmux server — the canonical event above still records the condition */
373
+ catch (err) {
374
+ emitEvent({ level: 'error', event: 'broker.capacity.tmux_list_clients_failed', error: err });
382
375
  }
383
376
  }
384
377
  handleYieldStall(row, pid, now) {
@@ -521,8 +514,13 @@ export class BrokerSupervisionReconciler {
521
514
  message: label,
522
515
  });
523
516
  }
524
- catch {
525
- /* best-effort — the doctrine wake below is the load-bearing notice */
517
+ catch (err) {
518
+ emitEvent({
519
+ level: 'error',
520
+ event: 'broker.wedge.fault_record_failed',
521
+ ...(isSafeNodeId(id) ? { node_id: id } : { fields: { affected_node_id: id } }),
522
+ error: err,
523
+ });
526
524
  }
527
525
  fanDoctrineWake(id, subscribersOf(id), label, {
528
526
  reason: 'child-wedged',
@@ -584,8 +582,7 @@ export class BrokerSupervisionReconciler {
584
582
  message: firstLine,
585
583
  });
586
584
  }
587
- async handleAuthReload(now, fleet) {
588
- void now;
585
+ async handleAuthReload(fleet) {
589
586
  let mtimeMs = 0;
590
587
  for (const path of piCredentialPaths()) {
591
588
  try {
@@ -10,7 +10,7 @@ export interface DormantInboxContext extends ControllerDeathContext {
10
10
  hasBrokerCapacity(): boolean;
11
11
  }
12
12
  export declare class ControllerDeathReconciler {
13
- run(_now: number, ctx: ControllerDeathContext): void;
13
+ run(now: number, ctx: ControllerDeathContext): void;
14
14
  }
15
15
  export declare class DormantInboxReconciler {
16
16
  private readonly relaunchState;
@@ -18,7 +18,8 @@ const IDLE_RELEASE_RELAUNCH_MAX_ATTEMPTS = 6;
18
18
  const IDLE_RELEASE_RELAUNCH_MAX_BACKOFF_MS = 10 * 60_000;
19
19
  const IDLE_RELEASE_RELAUNCH_BASE_MS = 20_000;
20
20
  export class ControllerDeathReconciler {
21
- run(_now, ctx) {
21
+ run(now, ctx) {
22
+ void now; // no time-gating here — the death check is already-controller-status-driven; the param is kept for six-way call-site uniformity, matching every other reconciler's run(now, ctx) shape.
22
23
  // Settle every wait whose controller has ended, independent of the waiter's
23
24
  // current status. Active and focused terminal nodes own a live inbox
24
25
  // watcher; idle-released nodes are revived by the following reconciler.
@@ -1,7 +1,7 @@
1
1
  import { hasActiveLiveSubscription, hasLiveMessageWait, hasLiveSubscriber, hasPendingCancelOnWakeCron, subscribersOf, } from '../../core/canvas/index.js';
2
2
  import { fullName } from '../../core/canvas/labels.js';
3
3
  import { countTickets } from '../../core/canvas/attention.js';
4
- import { readCursor, readInboxSince } from '../../core/feed/inbox.js';
4
+ import { hasUnseenWakeMail, readCursor, readInboxSince } from '../../core/feed/inbox.js';
5
5
  import { readOutputRequest } from '../../core/runtime/structured-output.js';
6
6
  import { focusOf } from '../../core/runtime/placement.js';
7
7
  import { hasAttachedBrokerViewers } from '../../core/runtime/broker/client-registry.js';
@@ -26,7 +26,7 @@ export function hasLiveObligation(nodeId) {
26
26
  export function hasPendingWake(nodeId) {
27
27
  if (hasLiveObligation(nodeId))
28
28
  return true;
29
- if (readInboxSince(nodeId, readCursor(nodeId)).some((entry) => entry.tier !== 'deferred'))
29
+ if (hasUnseenWakeMail(nodeId))
30
30
  return true;
31
31
  return focusOf(nodeId) !== null || hasAttachedBrokerViewers(nodeId);
32
32
  }
@@ -3,5 +3,5 @@ export interface PendingReviewSubmitContext {
3
3
  lifecycle: DetachedWorkLifecycle;
4
4
  }
5
5
  export declare class PendingReviewSubmitReconciler {
6
- run(ctx: PendingReviewSubmitContext): void;
6
+ run(now: number, ctx: PendingReviewSubmitContext): void;
7
7
  }
@@ -11,7 +11,8 @@ import { operationIdContext } from '../../core/events/operation-id.js';
11
11
  import { listSubmitRequestedReviews } from '../../core/review/store.js';
12
12
  import { completeRequestedSubmit } from '../review/finish.js';
13
13
  export class PendingReviewSubmitReconciler {
14
- run(ctx) {
14
+ run(now, ctx) {
15
+ void now; // this pass lists submit-requested reviews unconditionally — nothing here is time-gated; the param is kept for six-way call-site uniformity, matching every other reconciler's run(now, ctx) shape.
15
16
  if (!ctx.lifecycle.acceptsDetachedWork())
16
17
  return;
17
18
  let pending;
@@ -1,10 +1,11 @@
1
1
  export declare const DEAD_REAP_GRACE_MS: number;
2
+ export type StorageMaintenanceContext = Record<string, never>;
2
3
  /** Recurring storage cleanup. The in-memory throttle clocks intentionally reset
3
4
  * on daemon boot, so the first tick runs both full sweeps. */
4
5
  export declare class StorageMaintenanceReconciler {
5
6
  private lastSpareSweepAt;
6
7
  private lastGhostSweepAt;
7
- run(now: number): void;
8
+ run(now: number, ctx: StorageMaintenanceContext): void;
8
9
  /** Drop rows whose on-disk node dir is gone. Such a row can never be revived,
9
10
  * focused, or closed — every one of those resolves the node through meta.json —
10
11
  * so it sits in the roster jamming the surfaces that try. One readdir per sweep. */
@@ -16,7 +16,8 @@ const GHOST_SWEEP_INTERVAL_MS = 60 * 1000;
16
16
  export class StorageMaintenanceReconciler {
17
17
  lastSpareSweepAt = Number.NEGATIVE_INFINITY;
18
18
  lastGhostSweepAt = Number.NEGATIVE_INFINITY;
19
- run(now) {
19
+ run(now, ctx) {
20
+ void ctx; // no consumer today — kept only for six-way call-site uniformity
20
21
  // Placement reap MUST precede focus GC, so GC catches a focus row the reap
21
22
  // strands. Both run after dormant-inbox reconciliation, so a same-tick
22
23
  // revive has already had the chance to re-anchor its focus.
@@ -14,7 +14,7 @@
14
14
  // the wait exists to protect.
15
15
  import { getNode } from '../../core/canvas/canvas.js';
16
16
  import { isPidAlive } from '../../core/canvas/pid.js';
17
- import { readCursor, readInboxSince } from '../../core/feed/inbox.js';
17
+ import { hasUnseenWakeMail } from '../../core/feed/inbox.js';
18
18
  import { isBusy } from '../../core/runtime/busy.js';
19
19
  /** Is the companion inside a turn right now? */
20
20
  export function companionMidTurn(nodeId) {
@@ -27,7 +27,7 @@ export function companionMidTurn(nodeId) {
27
27
  * excluded for the same reason the settlement policy excludes it: it never
28
28
  * starts a turn of its own. */
29
29
  export function companionHasQueuedWork(nodeId) {
30
- return readInboxSince(nodeId, readCursor(nodeId)).some((entry) => entry.tier !== 'deferred');
30
+ return hasUnseenWakeMail(nodeId);
31
31
  }
32
32
  /** Would submitting right now step on the companion's work? */
33
33
  export function companionStillWorking(nodeId) {
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readdirSync, rmSync } from 'node:fs';
2
2
  import { basename } from 'node:path';
3
+ import { emitEvent } from '../../core/events/emit.js';
3
4
  import { reviewPath } from '../../core/human/convention.js';
4
5
  import { ticketDir } from '../../core/human/root.js';
5
6
  import { submitReview } from '../../core/human/tickets.js';
@@ -91,8 +92,10 @@ export async function sweepReviews() {
91
92
  catch (error) {
92
93
  // A missing immutable branch is an expected recovery outcome: realization
93
94
  // cancels the binding and never captures a replacement branch.
94
- if (!(error instanceof ReviewOperationError && error.code === 'branch_lost'))
95
+ if (!(error instanceof ReviewOperationError && error.code === 'branch_lost')) {
96
+ emitEvent({ level: 'warn', event: 'daemon.review_sweep.realize_failed', fields: { review_id: review.review_id, error: errorText(error) } });
95
97
  failed += 1;
98
+ }
96
99
  }
97
100
  }
98
101
  try {
@@ -102,7 +105,8 @@ export async function sweepReviews() {
102
105
  ]);
103
106
  collected = collectOrphanReviewDirectories(reviewIds);
104
107
  }
105
- catch {
108
+ catch (error) {
109
+ emitEvent({ level: 'warn', event: 'daemon.review_sweep.collect_orphans_failed', fields: { error: errorText(error) } });
106
110
  failed += 1;
107
111
  }
108
112
  for (const review of listReviews({ state: 'approved' })) {
@@ -123,7 +127,8 @@ export async function sweepReviews() {
123
127
  try {
124
128
  republishTicketReview(review);
125
129
  }
126
- catch {
130
+ catch (error) {
131
+ emitEvent({ level: 'warn', event: 'daemon.review_sweep.republish_failed', fields: { review_id: review.review_id, error: errorText(error) } });
127
132
  failed += 1;
128
133
  }
129
134
  }
@@ -19,9 +19,7 @@ import { parse as parseYaml } from 'yaml';
19
19
  import { parseFrontmatterGeneric } from '../core/frontmatter.js';
20
20
  import { appendRenderedKey, dropTopLevelKeys, proveFrontmatterRoundTrip, rebuildDocSource, } from './frontmatter-splice.js';
21
21
  const OLD_FIELDS = ['system-prompt-visibility', 'file-read-visibility', 'applies-to', 'read-when'];
22
- // ---------------------------------------------------------------------------
23
22
  // Frozen old-schema parsing (pre-cut substrate/schema.ts semantics).
24
- // ---------------------------------------------------------------------------
25
23
  const RUNGS = ['none', 'name', 'preview', 'content'];
26
24
  function rungRank(r) {
27
25
  return RUNGS.indexOf(r);
@@ -67,12 +65,10 @@ function classify(doc) {
67
65
  hasOldFields: OLD_FIELDS.some((k) => k in fm),
68
66
  };
69
67
  }
70
- // ---------------------------------------------------------------------------
71
68
  // Frozen boot-ceiling walk (pre-cut substrate/ceiling.ts), per store: a
72
69
  // directory INDEX's rung caps every descendant's boot rung; the migration
73
70
  // bakes the effective (post-ceiling) rung into each doc so the rendered boot
74
71
  // tree is preserved once the ceiling machinery is deleted.
75
- // ---------------------------------------------------------------------------
76
72
  function buildCeiling(docs) {
77
73
  const m = new Map();
78
74
  for (const d of docs) {
@@ -129,11 +125,9 @@ function planEntries(d, bootRung) {
129
125
  }
130
126
  return entries;
131
127
  }
132
- // ---------------------------------------------------------------------------
133
128
  // Serialization: cut the dead keys' exact line spans out of the raw
134
129
  // frontmatter text, append the rendered `surfaces:` block, re-parse to prove
135
130
  // the splice is meaning-preserving.
136
- // ---------------------------------------------------------------------------
137
131
  function migratedYaml(rawYaml, entries, docPath) {
138
132
  let out = dropTopLevelKeys(rawYaml, OLD_FIELDS, docPath);
139
133
  if (entries.length > 0)