create-walle 0.10.1 → 0.10.2

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 (341) hide show
  1. package/README.md +4 -0
  2. package/lib/owner-write-safety.js +135 -0
  3. package/lib/public-runtime.js +13 -5
  4. package/lib/release-schema.json +3 -2
  5. package/lib/runtime-closure-policy.json +6 -4
  6. package/package.json +1 -1
  7. package/template/bin/deps-doctor.js +1 -1
  8. package/template/bin/node-bin.sh +11 -0
  9. package/template/coding-task-manager/api-prompts.js +10 -22
  10. package/template/coding-task-manager/bin/claude-hook.js +7 -3
  11. package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
  12. package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
  13. package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
  14. package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
  15. package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
  16. package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
  17. package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
  18. package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
  19. package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
  20. package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
  21. package/template/coding-task-manager/db.js +705 -286
  22. package/template/coding-task-manager/git-utils.js +44 -0
  23. package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
  24. package/template/coding-task-manager/lib/agent-presets.js +15 -0
  25. package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
  26. package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
  27. package/template/coding-task-manager/lib/api/host-stats.js +98 -0
  28. package/template/coding-task-manager/lib/api/ingress.js +35 -12
  29. package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
  30. package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
  31. package/template/coding-task-manager/lib/api/machines.js +461 -0
  32. package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
  33. package/template/coding-task-manager/lib/api/providers.js +53 -6
  34. package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
  35. package/template/coding-task-manager/lib/api/sessions.js +160 -8
  36. package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
  37. package/template/coding-task-manager/lib/api/worktrees.js +38 -37
  38. package/template/coding-task-manager/lib/approval-drift.js +16 -1
  39. package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
  40. package/template/coding-task-manager/lib/approval-scope.js +68 -4
  41. package/template/coding-task-manager/lib/auth-context.js +32 -2
  42. package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
  43. package/template/coding-task-manager/lib/auth-rules.js +186 -45
  44. package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
  45. package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
  46. package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
  47. package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
  48. package/template/coding-task-manager/lib/claude-session-title.js +6 -30
  49. package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
  50. package/template/coding-task-manager/lib/client-diag-handler.js +5 -0
  51. package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
  52. package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
  53. package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
  54. package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
  55. package/template/coding-task-manager/lib/codex-paths.js +2 -1
  56. package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
  57. package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
  58. package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
  59. package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
  60. package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
  61. package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
  62. package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
  63. package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
  64. package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
  65. package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
  66. package/template/coding-task-manager/lib/db-domains.js +21 -2
  67. package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
  68. package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
  69. package/template/coding-task-manager/lib/feature-manifest.js +1 -1
  70. package/template/coding-task-manager/lib/fs-watcher.js +3 -2
  71. package/template/coding-task-manager/lib/harness-history-transition.js +3 -3
  72. package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
  73. package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
  74. package/template/coding-task-manager/lib/headless-term-service.js +55 -17
  75. package/template/coding-task-manager/lib/host-control-dial.js +167 -0
  76. package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
  77. package/template/coding-task-manager/lib/host-handoff.js +51 -13
  78. package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
  79. package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
  80. package/template/coding-task-manager/lib/host-stats.js +328 -0
  81. package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
  82. package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
  83. package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
  84. package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
  85. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
  86. package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
  87. package/template/coding-task-manager/lib/instance-manifest.js +13 -1
  88. package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
  89. package/template/coding-task-manager/lib/launch-model-override.js +160 -1
  90. package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
  91. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
  92. package/template/coding-task-manager/lib/lead-model-switch.js +3 -2
  93. package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
  94. package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
  95. package/template/coding-task-manager/lib/login-tripwire.js +1 -0
  96. package/template/coding-task-manager/lib/machine-pairing.js +238 -0
  97. package/template/coding-task-manager/lib/machine-pool.js +409 -0
  98. package/template/coding-task-manager/lib/machine-registry.js +749 -0
  99. package/template/coding-task-manager/lib/machine-self.js +66 -0
  100. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +52 -4
  101. package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
  102. package/template/coding-task-manager/lib/media-journal.js +84 -27
  103. package/template/coding-task-manager/lib/media-store-options.js +43 -15
  104. package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
  105. package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
  106. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  107. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  108. package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
  109. package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
  110. package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
  111. package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
  112. package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
  113. package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
  114. package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
  115. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  116. package/template/coding-task-manager/lib/native-history-control.js +2 -2
  117. package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
  118. package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
  119. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
  120. package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
  121. package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
  122. package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
  123. package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
  124. package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
  125. package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
  126. package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
  127. package/template/coding-task-manager/lib/read-pool-client.js +5 -2
  128. package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
  129. package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
  130. package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
  131. package/template/coding-task-manager/lib/remote-command-context.js +12 -0
  132. package/template/coding-task-manager/lib/remote-command-runtime.js +13 -1
  133. package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
  134. package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
  135. package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
  136. package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
  137. package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
  138. package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
  139. package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
  140. package/template/coding-task-manager/lib/restart-preflight.js +9 -2
  141. package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
  142. package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
  143. package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
  144. package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
  145. package/template/coding-task-manager/lib/resume-cwd.js +13 -0
  146. package/template/coding-task-manager/lib/resume-from-store.js +12 -1
  147. package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
  148. package/template/coding-task-manager/lib/resume-preflight.js +21 -16
  149. package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
  150. package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
  151. package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
  152. package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
  153. package/template/coding-task-manager/lib/root-prompt-admission.js +33 -2
  154. package/template/coding-task-manager/lib/root-prompt-flow.js +167 -33
  155. package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
  156. package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
  157. package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
  158. package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
  159. package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
  160. package/template/coding-task-manager/lib/session-create/identity.js +20 -1
  161. package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
  162. package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
  163. package/template/coding-task-manager/lib/session-create/provider-process.js +34 -37
  164. package/template/coding-task-manager/lib/session-create/response.js +30 -12
  165. package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
  166. package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
  167. package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
  168. package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
  169. package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
  170. package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
  171. package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
  172. package/template/coding-task-manager/lib/session-host-manager.js +152 -206
  173. package/template/coding-task-manager/lib/session-jobs.js +17 -9
  174. package/template/coding-task-manager/lib/session-lifecycle.js +18 -0
  175. package/template/coding-task-manager/lib/session-model-action-preconditions.js +9 -0
  176. package/template/coding-task-manager/lib/session-resource-sampler.js +19 -9
  177. package/template/coding-task-manager/lib/session-standup.js +67 -4
  178. package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
  179. package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
  180. package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
  181. package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
  182. package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
  183. package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
  184. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
  185. package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
  186. package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
  187. package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
  188. package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
  189. package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
  190. package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
  191. package/template/coding-task-manager/lib/transcript-link.js +77 -0
  192. package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
  193. package/template/coding-task-manager/lib/transcript-source.js +8 -3
  194. package/template/coding-task-manager/lib/transcript-store.js +40 -0
  195. package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
  196. package/template/coding-task-manager/lib/typing-interaction-trace.js +92 -0
  197. package/template/coding-task-manager/lib/typing-worker-stages.js +60 -0
  198. package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
  199. package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
  200. package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
  201. package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
  202. package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
  203. package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
  204. package/template/coding-task-manager/lib/ws-router.js +68 -1
  205. package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
  206. package/template/coding-task-manager/oauth-proxy.js +99 -55
  207. package/template/coding-task-manager/package-lock.json +24 -0
  208. package/template/coding-task-manager/package.json +3 -0
  209. package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
  210. package/template/coding-task-manager/providers/codex-mcp.js +14 -5
  211. package/template/coding-task-manager/public/css/app.css +279 -1
  212. package/template/coding-task-manager/public/css/reviews.css +4 -0
  213. package/template/coding-task-manager/public/index.html +294 -223
  214. package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
  215. package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
  216. package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
  217. package/template/coding-task-manager/public/js/background-work.js +13 -7
  218. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
  219. package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
  220. package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
  221. package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
  222. package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
  223. package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
  224. package/template/coding-task-manager/public/js/history-scroller.js +187 -9
  225. package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
  226. package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
  227. package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
  228. package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -8
  229. package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
  230. package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
  231. package/template/coding-task-manager/public/js/reviews.js +7 -3
  232. package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
  233. package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
  234. package/template/coding-task-manager/public/js/row-join.js +4 -2
  235. package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
  236. package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
  237. package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
  238. package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
  239. package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
  240. package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
  241. package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
  242. package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
  243. package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
  244. package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
  245. package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
  246. package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
  247. package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
  248. package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -1
  249. package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
  250. package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
  251. package/template/coding-task-manager/public/js/walle-session.js +30 -7
  252. package/template/coding-task-manager/public/m/app.css +226 -8
  253. package/template/coding-task-manager/public/m/app.js +502 -9
  254. package/template/coding-task-manager/public/m/index.html +38 -1
  255. package/template/coding-task-manager/public/m/machines.js +1313 -0
  256. package/template/coding-task-manager/public/m/pool-failover.js +391 -0
  257. package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
  258. package/template/coding-task-manager/public/m/sw.js +5 -0
  259. package/template/coding-task-manager/queue-engine.js +13 -0
  260. package/template/coding-task-manager/server/approvals.js +5 -6
  261. package/template/coding-task-manager/server/boot.js +136 -17
  262. package/template/coding-task-manager/server.js +902 -239
  263. package/template/coding-task-manager/session-integrity.js +7 -9
  264. package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
  265. package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
  266. package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
  267. package/template/coding-task-manager/workers/session-host-process.js +14 -3
  268. package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
  269. package/template/coding-task-manager/workers/worktree-worker.js +34 -0
  270. package/template/package-lock.json +2 -2
  271. package/template/package.json +1 -1
  272. package/template/scripts/conversation-storage-v2-migration.js +78 -11
  273. package/template/shared/conversation-storage-v2-contract.js +42 -6
  274. package/template/shared/fixtures/worker-status-payloads.json +392 -0
  275. package/template/shared/git-remote-key.js +83 -0
  276. package/template/shared/host-identity.js +98 -2
  277. package/template/shared/owner-write-safety.js +135 -0
  278. package/template/shared/public-runtime.js +13 -5
  279. package/template/shared/release-schema.json +3 -2
  280. package/template/shared/report-citations.js +14 -0
  281. package/template/shared/sqlite-write-lock.js +361 -223
  282. package/template/shared/storage/contract.js +35 -3
  283. package/template/wall-e/agent.js +21 -0
  284. package/template/wall-e/api-walle.js +5 -0
  285. package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
  286. package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
  287. package/template/wall-e/brain/helper-rules-schema.js +129 -0
  288. package/template/wall-e/brain/migrations.js +51 -22
  289. package/template/wall-e/brain/model-deployment-facts.js +31 -1
  290. package/template/wall-e/brain/owner-call-whitelist.js +16 -0
  291. package/template/wall-e/brain/schema-ensures.js +22 -0
  292. package/template/wall-e/brain.js +1 -1
  293. package/template/wall-e/coding/journal-dispatcher.js +51 -0
  294. package/template/wall-e/coding/session-journal.js +28 -40
  295. package/template/wall-e/coding-orchestrator.js +23 -26
  296. package/template/wall-e/http/auth.js +18 -1
  297. package/template/wall-e/http/blog-views-api.js +318 -0
  298. package/template/wall-e/http/helper-rules-api.js +321 -0
  299. package/template/wall-e/http/model-proxy-providers.js +3 -1
  300. package/template/wall-e/http/telemetry-api.js +12 -0
  301. package/template/wall-e/lib/blog-view-policy.js +88 -0
  302. package/template/wall-e/lib/blog-view-store.js +169 -0
  303. package/template/wall-e/lib/client-config-lock.js +2 -1
  304. package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
  305. package/template/wall-e/lib/error-kinds.js +10 -1
  306. package/template/wall-e/lib/feature-manifest.js +1 -1
  307. package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
  308. package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
  309. package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
  310. package/template/wall-e/lib/helper-rules/grade.js +277 -0
  311. package/template/wall-e/lib/helper-rules/label.js +247 -0
  312. package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
  313. package/template/wall-e/lib/helper-rules/store.js +667 -0
  314. package/template/wall-e/lib/mcp-integration.js +5 -6
  315. package/template/wall-e/lib/runtime-worker-pool.js +1 -0
  316. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
  317. package/template/wall-e/lib/telemetry-db-schema.js +37 -0
  318. package/template/wall-e/lib/telemetry-process-client.js +76 -1
  319. package/template/wall-e/llm/default-fallback.js +4 -0
  320. package/template/wall-e/llm/model-catalog.js +13 -4
  321. package/template/wall-e/llm/model-roles.js +6 -0
  322. package/template/wall-e/llm/portkey-http.js +150 -0
  323. package/template/wall-e/llm/portkey-sync.js +15 -145
  324. package/template/wall-e/llm/worker-mcp-routing.js +61 -0
  325. package/template/wall-e/llm/worker-route.js +43 -1
  326. package/template/wall-e/loops/brain-custom-writes.js +45 -0
  327. package/template/wall-e/mcp-protocol-contract.js +41 -1
  328. package/template/wall-e/mcp-server.js +13 -2
  329. package/template/wall-e/mcp-worker-schemas.js +20 -2
  330. package/template/wall-e/memory/ctm-context-client.js +25 -5
  331. package/template/wall-e/shared/release-schema.json +3 -2
  332. package/template/wall-e/shared/sqlite-write-lock.js +361 -223
  333. package/template/wall-e/shared/storage/contract.js +35 -3
  334. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  335. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
  336. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
  337. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
  338. package/template/wall-e/tools/local-tools.js +6 -4
  339. package/template/wall-e/workers/brain-owner-worker.js +9 -0
  340. package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
  341. package/template/wall-e/workers/telemetry-worker.js +8 -0
@@ -85,6 +85,18 @@ function claudeProjectDirForCwd(cwd, opts = {}) {
85
85
  return path.join(configDir, 'projects', encodeClaudeProjectEntry(cwd, opts));
86
86
  }
87
87
 
88
+ // WP-2 (worker-transcript-link-derived-not-discovered-2026-10-25.html §5 D-4/D-7): the path
89
+ // Claude Code writes a session's transcript to, derived from (cwd, session id) alone -- no fs
90
+ // read, no directory scan. A resume preflight that finds an empty agent_sessions.jsonl_path
91
+ // uses this to look where the file deterministically is before refusing, then verifies it
92
+ // through lib/transcript-source.js (lib/transcript-link.js) rather than trusting the guess.
93
+ function deriveClaudeTranscriptPath({ cwd, agentSessionId, homeDir = process.env.HOME, claudeConfigDir = '' } = {}) {
94
+ const dir = String(cwd || '').trim();
95
+ const id = String(agentSessionId || '').trim();
96
+ if (!dir || !id) throw new TypeError('deriveClaudeTranscriptPath requires cwd and agentSessionId');
97
+ return path.join(claudeProjectDirForCwd(dir, { homeDir, claudeConfigDir }), `${id}.jsonl`);
98
+ }
99
+
88
100
  // D-3 (main-loop-sync-io doc §5 D-3, §8 WP-0): decodeClaudeProjectEntry used to duplicate
89
101
  // session-utils.js's own backtracking search AND add the authoritative-transcript-cwd fallback
90
102
  // session-utils.js lacked, with no memo at all (R-2's 439,403-calls-in-88-min hot path). The full
@@ -166,6 +178,7 @@ module.exports = {
166
178
  canonicalizeClaudeProjectPath,
167
179
  resetCanonicalizeClaudeProjectPathMemo,
168
180
  claudeProjectDirForCwd,
181
+ deriveClaudeTranscriptPath,
169
182
  encodeClaudeProjectEntry,
170
183
  encodeClaudeProjectSegment,
171
184
  decodeClaudeProjectEntry,
@@ -372,7 +372,18 @@ function resolveResumeRung(input = {}, probes = {}) {
372
372
  if (_RESTORE_STATES.has(state)) return done('restore');
373
373
  const fileName = plainPath ? plainPath.split(/[\\/]/).pop() : '';
374
374
  const trashed = fileName ? probes.trashEntry(fileName) : null;
375
- if (trashed) return done('trash', { detail: { trashPath: trashed.trashPath, originalPath: trashed.originalPath } });
375
+ // WP-3 (D-13): the trash rung rides the SAME two recorded facts as the preflight path, so the
376
+ // card's "why" sentence is identical whichever route found the copy.
377
+ if (trashed) {
378
+ return done('trash', {
379
+ detail: {
380
+ trashPath: trashed.trashPath,
381
+ originalPath: trashed.originalPath,
382
+ trashReason: trashed.reason || '',
383
+ trashedAt: trashed.trashedAt || '',
384
+ },
385
+ });
386
+ }
376
387
  if (probes.storeHasHuman(ctmSessionId)) return done('store');
377
388
  return done('fresh');
378
389
  }
@@ -43,7 +43,7 @@ function _peerInstanceIds(syncPeers) {
43
43
  }
44
44
 
45
45
  function resumeOriginFacts(db, ctmSessionId, options = {}) {
46
- const facts = { origin: 'unknown', originHostname: '', originIsPeer: false };
46
+ const facts = { origin: 'unknown', originHostname: '', originInstanceId: '', originIsPeer: false };
47
47
  try {
48
48
  const id = String(ctmSessionId || '').trim();
49
49
  if (!id) return facts;
@@ -57,13 +57,46 @@ function resumeOriginFacts(db, ctmSessionId, options = {}) {
57
57
  const originId = _originIdOf(handle.prepare('SELECT origin_instance_id FROM ctm_sessions WHERE id = ?').get(id));
58
58
  facts.origin = resumeOriginFor({ origin_instance_id: originId }, identity);
59
59
  facts.originHostname = String(birth?.hostname || '').trim();
60
+ // WP-6 (S-8/S-25, AC-6d): the raw MINTED origin id travels too, because it — never the hostname
61
+ // (S-5) — is the key a machine registration is pinned by (lib/machine-registry.js, D-3). It is
62
+ // what lets the resume-blocked card offer "open on <machine>" and what routes the resume itself.
63
+ facts.originInstanceId = originId;
60
64
  facts.originIsPeer = !!originId && _peerInstanceIds(options.syncPeers).has(originId);
65
+ // WP-6 (S-8, AC-6d): when the origin is a REGISTERED, REACHABLE machine, the card can act on it
66
+ // instead of dead-ending at `on_other_machine`. Additive, and absent (not null) otherwise, so
67
+ // every existing payload keeps its exact shape. `displayName` is the registry's user label; a
68
+ // hostname is never used here.
69
+ const openOnMachine = _openOnMachineFor(originId, facts.origin);
70
+ if (openOnMachine) facts.openOnMachine = openOnMachine;
61
71
  return facts;
62
72
  } catch {
63
73
  return facts; // expected: pre-boot / a test harness with no database -> the card says what it knows, which is nothing
64
74
  }
65
75
  }
66
76
 
77
+ // A machine that is not registered, is disabled, or is not reachable is NOT an actionable origin:
78
+ // the 60 s health walk (P3) means a reachable verdict is the only one worth offering an action on.
79
+ function _openOnMachineFor(originId, origin) {
80
+ const id = String(originId || '').trim();
81
+ if (!id || origin !== 'other_instance') return null;
82
+ try {
83
+ const registry = require('./machine-registry');
84
+ const db = require('../db-owner-client');
85
+ const { machines } = registry.listMachines({ db }) || {};
86
+ return _openOnMachineIn(Array.isArray(machines) ? machines : [], id);
87
+ } catch {
88
+ return null; // expected: pre-boot / no machines table -> today's card copy, byte-for-byte
89
+ }
90
+ }
91
+
92
+ function _openOnMachineIn(machines, originId) {
93
+ const row = (Array.isArray(machines) ? machines : [])
94
+ .find((machine) => machine && String(machine.instanceId || '') === originId);
95
+ if (!row || row.enabled === false) return null;
96
+ if (String(row.health || '') !== 'reachable') return null;
97
+ return { instanceId: String(row.instanceId || ''), displayName: String(row.displayName || '') };
98
+ }
99
+
67
100
  // WP-3 (§6.4): the three sync-table facts the resume-blocked card needs to distinguish "the copy is
68
101
  // here and gone", "the copy is on its way" and "everything is on the other machine". Every read is
69
102
  // best-effort: a missing sync_transcript_replicas table (a pre-WP-3 database) or a pre-boot harness
@@ -152,7 +185,11 @@ function resumeOriginRecordFields(sessionId, info = {}) {
152
185
  return {
153
186
  origin,
154
187
  originHostname: String(i.originHostname || facts.originHostname || ''),
188
+ originInstanceId: String(i.originInstanceId || facts.originInstanceId || ''),
155
189
  originIsPeer: i.originIsPeer === true || facts.originIsPeer === true,
190
+ // WP-6 (S-8/AC-6d): the actionable-origin fact survives the record round-trip, so a caller that
191
+ // re-derives the wire object from the record still offers "open on <machine>".
192
+ ...(i.openOnMachine || facts.openOnMachine ? { openOnMachine: i.openOnMachine || facts.openOnMachine } : {}),
156
193
  ...syncFacts,
157
194
  transcriptState: i.transcriptState || deriveTranscriptState({
158
195
  origin, localHistory: normalizeLocalHistory(i), ...syncFacts,
@@ -122,26 +122,21 @@ async function recheckResumeBlockedRecord(record, deps = {}) {
122
122
  }
123
123
 
124
124
  // Builds the human-readable "removed" sentence shared by both the Claude and Codex missing-
125
- // transcript messages (R10): says the file was removed from this machine (with size + last-seen
126
- // date), that CTM still has the conversation, and — when a trash copy was found — where it is
127
- // and that moving it back makes Retry work.
125
+ // transcript messages (R10, D-15): says the file was removed from this machine (with size +
126
+ // last-seen date) and whether CTM kept a copy it can put back. No path, no "move it back", no
127
+ // "Retry will work" — the user never handles a file, and the card's Details row carries paths.
128
128
  function _removedTranscriptSentence(fields) {
129
129
  const dateText = fields.lastSeenAt ? fields.lastSeenAt.slice(0, 10) : 'an earlier session';
130
- let sentence = `was removed from this machine (${_formatBytes(fields.lastSeenBytes)}, last seen ${dateText}). `
131
- + `CTM still has the cached conversation history.`;
132
- if (fields.trashPath) {
133
- sentence += ` A copy is in CTM's transcript trash at ${fields.trashPath} — move it back to `
134
- + `${fields.originalPath || 'its original location'} and Retry will work.`;
135
- } else {
136
- sentence += ' Start a new session from the cached history, or restore the missing transcript from backup.';
137
- }
138
- return sentence;
130
+ return `was removed from this machine (${_formatBytes(fields.lastSeenBytes)}, last seen ${dateText}). `
131
+ + (fields.trashPath
132
+ ? 'CTM kept a copy and can put it back.'
133
+ : 'CTM still has the cached conversation history.');
139
134
  }
140
135
 
141
136
  // Pure (no fs/DB): given a `localHistory === 'removed'` preflight result and a WP-1
142
- // findTrashedTranscript() lookup result (`{ trashPath, originalPath, trashedAt } | null`, found by
143
- // the CALLER -- this module never runs that async fs read itself), returns a new result with
144
- // `trashPath`/`originalPath` set and `message` rebuilt to name the trash location. `trashed ===
137
+ // findTrashedTranscript() lookup result (`{ trashPath, originalPath, trashedAt, reason } | null`,
138
+ // found by the CALLER -- this module never runs that async fs read itself), returns a new result
139
+ // with `trashPath`/`originalPath`/`trashReason`/`trashedAt` set and `message` rebuilt. `trashed ===
145
140
  // null` (nothing found, or no trashRoot configured) returns `result` unchanged -- same message as
146
141
  // before. Never mutates its input.
147
142
  function withTrashLocation(result, trashed) {
@@ -151,7 +146,14 @@ function withTrashLocation(result, trashed) {
151
146
  const isCodex = result.code === 'missing_codex_rollout';
152
147
  const message = `Cannot resume ${isCodex ? 'Codex' : 'Claude'} session ${shortId}: `
153
148
  + `${isCodex ? 'the local Codex rollout file' : 'the local Claude transcript file'} ${_removedTranscriptSentence(fields)}`;
154
- return { ...result, trashPath: trashed.trashPath, originalPath: trashed.originalPath, message };
149
+ return {
150
+ ...result,
151
+ trashPath: trashed.trashPath,
152
+ originalPath: trashed.originalPath,
153
+ trashReason: trashed.reason || '',
154
+ trashedAt: trashed.trashedAt || '',
155
+ message,
156
+ };
155
157
  }
156
158
 
157
159
  // A Claude transcript is "real" only if it carries at least one conversation
@@ -676,6 +678,9 @@ module.exports = {
676
678
  claudeLiveTranscriptShouldRestore,
677
679
  restoreClaudeLiveTranscriptFromBak,
678
680
  withTrashLocation,
681
+ // WP-3 (D-15, AC-3c): exported so the exact sentence is pinned by a unit test instead of only by the
682
+ // message it is embedded in.
683
+ _removedTranscriptSentence,
679
684
  withResumeRung,
680
685
  recheckResumeBlockedRecord,
681
686
  MISSING_TRANSCRIPT_CODES,
@@ -14,9 +14,19 @@ const os = require('os');
14
14
  const path = require('path');
15
15
 
16
16
  const REJECT_SEGMENTS = new Set(['node_modules', '.cache']);
17
- // bin/pre-commit-hook.sh mktemps `<root>/.worktrees/precommit-staged.XXXXXX` per commit and
18
- // deletes it after; admitting one leaves a registry row whose cwd vanishes (`spawn git ENOENT`).
19
- const REJECT_BASENAME_PATTERNS = [/^precommit-staged\./];
17
+ // Every scratch checkout the repo's own tooling mktemps under `<repo>/.worktrees/` and deletes
18
+ // when it is done. Admitting one leaves a registry row (or a /api/projects picker row, WP-2 of
19
+ // docs/remote-hub-followups-credential-projects-rows-idempotency-2026-10-23.html) whose cwd
20
+ // vanishes (`spawn git ENOENT`) or that the user never asked for. Widened from the single
21
+ // pre-commit template (S-10) to all four; each one is pinned by name in
22
+ // tests-integration/api-projects-rows.test.js (AC-2e), which reads the templates back out of the
23
+ // gate scripts themselves.
24
+ const GATE_SCRATCH_BASENAME_PATTERNS = Object.freeze([
25
+ /^test-gate-merged\./, // bin/test-gate.sh (merged-tree gate worktree)
26
+ /^precommit-staged\./, // bin/pre-commit-hook.sh
27
+ /^nightly-gate\./, // bin/nightly-gate.sh
28
+ /^deflake-snapshot-/, // bin/deflake-source-snapshot.js
29
+ ]);
20
30
  const WORKTREE_PARENT_SEGMENTS = ['.claude/worktrees', '.walle/worktrees', '.worktrees'];
21
31
 
22
32
  function _segments(p) {
@@ -44,12 +54,28 @@ function _rejectReason(candidatePath, env) {
44
54
  if (REJECT_SEGMENTS.has(seg)) return 'dependency-dir';
45
55
  }
46
56
  for (const seg of _segments(candidatePath)) {
47
- if (REJECT_BASENAME_PATTERNS.some((re) => re.test(seg))) return 'temp';
57
+ if (GATE_SCRATCH_BASENAME_PATTERNS.some((re) => re.test(seg))) return 'temp';
48
58
  }
49
59
  if (env.hidden && env.hidden.has(candidatePath)) return 'hidden';
50
60
  return null;
51
61
  }
52
62
 
63
+ /**
64
+ * True iff `p` is a gate scratch checkout: its PARENT directory is named `.worktrees` and its
65
+ * basename matches one of the gate's own mktemp templates (GATE_SCRATCH_BASENAME_PATTERNS).
66
+ * A real worktree the user created (e.g. `<repo>/.worktrees/feature-x`) is NOT gate scratch and
67
+ * stays visible; the four tooling templates never do. Used by the worktree worker's `projectRows`
68
+ * op (WP-2) — the Review Hub admission filter below keeps its own segment scan over the same list.
69
+ * @param {string} p
70
+ * @returns {boolean}
71
+ */
72
+ function isGateScratchPath(p) {
73
+ const value = String(p || '');
74
+ const name = path.basename(value);
75
+ if (path.basename(path.dirname(value)) !== '.worktrees') return false;
76
+ return GATE_SCRATCH_BASENAME_PATTERNS.some((re) => re.test(name));
77
+ }
78
+
53
79
  /**
54
80
  * @returns {{ok:true, realpath, kind:'primary'|'worktree', repoRoot, gitDir, viaSubdir?:true}
55
81
  * | {ok:false, reason:'missing'|'outside-home'|'temp'|'scratch'|'dependency-dir'|'not-git'|'hidden'}}
@@ -97,7 +123,7 @@ function admitReviewRepo(candidatePath, env) {
97
123
  }
98
124
 
99
125
  for (const seg of _segments(realpath)) {
100
- if (REJECT_BASENAME_PATTERNS.some((re) => re.test(seg))) return { ok: false, reason: 'temp' };
126
+ if (GATE_SCRATCH_BASENAME_PATTERNS.some((re) => re.test(seg))) return { ok: false, reason: 'temp' };
101
127
  }
102
128
 
103
129
  if (env.hidden && env.hidden.has(realpath)) return { ok: false, reason: 'hidden' };
@@ -145,4 +171,11 @@ function defaultAdmissionEnv({ home = os.homedir(), scratchRoot = null } = {}) {
145
171
  };
146
172
  }
147
173
 
148
- module.exports = { admitReviewRepo, defaultAdmissionEnv, REJECT_SEGMENTS, WORKTREE_PARENT_SEGMENTS };
174
+ module.exports = {
175
+ admitReviewRepo,
176
+ defaultAdmissionEnv,
177
+ isGateScratchPath,
178
+ GATE_SCRATCH_BASENAME_PATTERNS,
179
+ REJECT_SEGMENTS,
180
+ WORKTREE_PARENT_SEGMENTS,
181
+ };
@@ -665,6 +665,23 @@ function createReviewRepoRegistry({ db, gitRead, sessionFeed, readSources, broad
665
665
  next.watcher = watchFn(dir, { persistent: false }, () => {
666
666
  for (const p of next.repoPaths) _scheduleWatchRefresh(p);
667
667
  });
668
+ // F-2 (clock-free follow-ups r3): a synchronous try/catch is NOT an 'error' listener.
669
+ // On macOS libuv broadcasts one FSEventStreamStart failure (UV_EMFILE) to EVERY
670
+ // directory watcher in the loop (src/unix/fsevents.c), asynchronously — so without
671
+ // this listener the EventEmitter throws and the whole CTM process exits (observed:
672
+ // "EMFILE: too many open files, watch" 13 s after a dev restart).
673
+ next.watcher.on('error', (error) => {
674
+ // REPORT once per dir; the next discover() pass (boot, a new transcript, or the client
675
+ // Refresh) re-watches it via _watchRepo. Until then the 60 s sweep keeps status correct.
676
+ // No timer and no immediate re-attach: a re-attach rebuilds the one shared FSEvents
677
+ // stream, which would just re-fail every handle (D-2).
678
+ if (watchedDirs.get(dir) === next) watchedDirs.delete(dir);
679
+ try { next.watcher.close(); } catch { /* expected: an errored FSWatcher may throw on close */ }
680
+ if (!warnedWatchDirs.has(dir)) {
681
+ warnedWatchDirs.add(dir);
682
+ console.warn('[review-registry] watch error', dir, error && error.code);
683
+ }
684
+ });
668
685
  } catch (error) {
669
686
  // REPORT once per dir: the 60s sweep is the fallback for a dir this process can't
670
687
  // watch (e.g. an fs.watch fd/inotify-limit failure), not a hard failure.
@@ -51,6 +51,7 @@ function createRootDeliveryRuntime(deps = {}) {
51
51
  const {
52
52
  coordinator, readLatestDelivery, readJobText, preflight, buildWritePayload,
53
53
  deliverTerminal, isDeliveryCurrent = () => true, now = Date.now,
54
+ reportDiagnostic = () => {},
54
55
  } = deps;
55
56
  const required = { 'coordinator.execute': coordinator?.execute, readLatestDelivery, readJobText,
56
57
  preflight, buildWritePayload, deliverTerminal };
@@ -58,10 +59,23 @@ function createRootDeliveryRuntime(deps = {}) {
58
59
  if (typeof callback !== 'function') throw new TypeError(`Root delivery runtime requires ${name}`);
59
60
  }
60
61
 
62
+ // WP-0: one bounded diagnostic per rejected handoff stage. `phase` is the exact
63
+ // stage the driver gave up at and `code` is a static, non-parameterized token --
64
+ // no prompt text, attachment, auth fact, path, receipt id, or raw exception text
65
+ // ever rides along, and a throwing sink can never change an outcome.
66
+ const report = (phase, code) => {
67
+ if (typeof reportDiagnostic !== 'function') return;
68
+ try { reportDiagnostic(Object.freeze({ phase, code })); return true; }
69
+ catch { return false; } // Diagnostics are advisory; their failure cannot change delivery.
70
+ };
71
+
61
72
  async function drive(admissionHandle, { automatic = false, nowMs = now() } = {}) {
62
73
  const committed = getCommittedRootAdmission(admissionHandle);
63
74
  if (!committed || typeof committed.isCurrent !== 'function' || !committed.selector
64
- || !committed.metadata || !committed.target || typeof committed.target.isCurrent !== 'function') return unavailable();
75
+ || !committed.metadata || !committed.target || typeof committed.target.isCurrent !== 'function') {
76
+ report('admission_metadata', 'root_admission_metadata_unavailable');
77
+ return unavailable();
78
+ }
65
79
  const intent = rootPromptIntent.getCapturedRootPromptIntent(committed.intentHandle);
66
80
  const parent = committed.metadata;
67
81
  const deliveryCurrent = () => {
@@ -70,6 +84,7 @@ function createRootDeliveryRuntime(deps = {}) {
70
84
  if (!intent || intent.sessionId !== parent.sessionId || intent.source !== parent.source
71
85
  || !committed.isCurrent()) {
72
86
  retiredJobs.add(admissionHandle);
87
+ report('admission_metadata', 'root_admission_metadata_unavailable');
73
88
  return unavailable();
74
89
  }
75
90
  if (!deliveryCurrent()) return unavailable('root_delivery_expired');
@@ -98,22 +113,37 @@ function createRootDeliveryRuntime(deps = {}) {
98
113
  };
99
114
  const selector = committed.selector;
100
115
  let snapshot;
101
- try { snapshot = await readLatestDelivery(selector); } catch { return unavailable(); }
116
+ try { snapshot = await readLatestDelivery(selector); } catch {
117
+ report('delivery_snapshot', 'root_delivery_snapshot_unavailable');
118
+ return unavailable();
119
+ }
102
120
  if (!stillJobCurrent()) return unavailable();
103
121
  if (!deliveryCurrent()) return unavailable('root_delivery_expired');
104
- if (!snapshot || !snapshot.parent || !sameMetadata(snapshot.parent, parent)) return unavailable();
122
+ if (!snapshot || !snapshot.parent || !sameMetadata(snapshot.parent, parent)) {
123
+ report('delivery_snapshot', 'root_delivery_snapshot_unavailable');
124
+ return unavailable();
125
+ }
105
126
  const selected = selectRootDeliveryAttempt(snapshot, { automatic, nowMs });
106
- if (!selected?.ok) return selected || unavailable();
127
+ if (!selected?.ok) {
128
+ report('delivery_selection', 'root_delivery_selection_refused');
129
+ return selected || unavailable();
130
+ }
107
131
 
108
132
  let workerResult;
109
- try { workerResult = await readJobText(selector, parent.textBytes); } catch { return unavailable(); }
133
+ try { workerResult = await readJobText(selector, parent.textBytes); } catch {
134
+ report('delivery_text', 'root_delivery_text_unavailable');
135
+ return unavailable();
136
+ }
110
137
  if (!stillJobCurrent()) return unavailable();
111
138
  if (!deliveryCurrent()) return unavailable('root_delivery_expired');
112
139
  const buffer = workerResult?.__resultBuffer;
113
140
  const metadata = workerResult?.__resultMetadata;
114
141
  if (!Buffer.isBuffer(buffer) || !sameMetadata(metadata, parent) || buffer.byteLength !== parent.textBytes
115
142
  || crypto.createHash('sha256').update(buffer).digest('hex') !== parent.textHash
116
- || !buffer.equals(Buffer.from(intent.text, 'utf8'))) return unavailable();
143
+ || !buffer.equals(Buffer.from(intent.text, 'utf8'))) {
144
+ report('delivery_text', 'root_delivery_text_unavailable');
145
+ return unavailable();
146
+ }
117
147
  const text = buffer.toString('utf8');
118
148
 
119
149
  const expectedChildId = remoteCommandStore.remoteCommandReceiptId(selector.namespaceHash, selected.commandId);
@@ -128,18 +158,26 @@ function createRootDeliveryRuntime(deps = {}) {
128
158
  authorize,
129
159
  dispatch: async (message) => {
130
160
  if (message?.id !== expectedChildId || message?.type !== ROOT_DELIVERY_ACTION || message?.version !== 1
131
- || !message.body || JSON.stringify(message.body) !== JSON.stringify(selected.body)
132
- || !stillJobCurrent()) return unavailable();
161
+ || !message.body || JSON.stringify(message.body) !== JSON.stringify(selected.body)) {
162
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
163
+ return unavailable();
164
+ }
165
+ if (!stillJobCurrent()) return unavailable();
133
166
  let blocker;
134
- try { blocker = preflight(parent.sessionId, committed.target.session, text); } catch { return unavailable('root_delivery_preflight_unavailable'); }
167
+ try { blocker = preflight(parent.sessionId, committed.target.session, text); } catch {
168
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
169
+ return unavailable('root_delivery_preflight_unavailable');
170
+ }
135
171
  if (blocker && typeof blocker?.then === 'function') {
136
172
  // A promise violates the synchronous preflight contract; consume either settlement without exposing it.
137
173
  void Promise.resolve(blocker).then(() => false, () => false);
174
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
138
175
  return unavailable('root_delivery_preflight_unavailable');
139
176
  }
140
177
  if (blocker !== null) {
141
178
  if (!blocker || typeof blocker !== 'object' || Array.isArray(blocker)
142
179
  || blocker.ok !== false || !PREFLIGHT.has(blocker.error)) {
180
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
143
181
  return unavailable('root_delivery_preflight_unavailable');
144
182
  }
145
183
  const blockerCode = blocker.error;
@@ -150,16 +188,29 @@ function createRootDeliveryRuntime(deps = {}) {
150
188
  } };
151
189
  }
152
190
  if (!deliveryCurrent()) return unavailable('root_delivery_expired');
153
- if (!stillTargetCurrent()) return unavailable('terminal_target_replaced');
191
+ if (!stillTargetCurrent()) {
192
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
193
+ return unavailable('terminal_target_replaced');
194
+ }
154
195
  let payload;
155
- try { payload = buildWritePayload(committed.target.session, text); } catch { return unavailable('root_delivery_payload_unavailable'); }
196
+ try { payload = buildWritePayload(committed.target.session, text); } catch {
197
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
198
+ return unavailable('root_delivery_payload_unavailable');
199
+ }
156
200
  if (payload && typeof payload?.then === 'function') {
157
201
  // Payload construction is synchronous by contract; consume async contract violations safely.
158
202
  void Promise.resolve(payload).then(() => false, () => false);
203
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
204
+ return unavailable('root_delivery_payload_unavailable');
205
+ }
206
+ if (typeof payload !== 'string' || !payload || !stillJobCurrent()) {
207
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
159
208
  return unavailable('root_delivery_payload_unavailable');
160
209
  }
161
- if (typeof payload !== 'string' || !payload || !stillJobCurrent()) return unavailable('root_delivery_payload_unavailable');
162
- if (!stillTargetCurrent()) return unavailable('terminal_target_replaced');
210
+ if (!stillTargetCurrent()) {
211
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
212
+ return unavailable('terminal_target_replaced');
213
+ }
163
214
  const permit = Object.freeze({});
164
215
  const permitState = { admissionHandle, committed, target: committed.target, parent, deliveryCurrent,
165
216
  text, payload, active: true, phase: 'text', pending: null };
@@ -174,12 +225,18 @@ function createRootDeliveryRuntime(deps = {}) {
174
225
  delivered = await deliverTerminal({ permit, target: committed.target, sessionId: parent.sessionId,
175
226
  text, submissionId: parent.receiptId, source: intent.source,
176
227
  attachments: intent.attachments });
177
- } catch { return { ok: false, error: 'root_delivery_failed', retryable: false }; }
228
+ } catch {
229
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
230
+ return { ok: false, error: 'root_delivery_failed', retryable: false };
231
+ }
178
232
  if (!deliveryCurrent()) return unavailable('root_delivery_expired');
179
233
  return cleanDeliveryResult(delivered);
180
234
  },
181
235
  });
182
- } catch { result = unavailable('remote_command_unresolved'); }
236
+ } catch {
237
+ report('delivery_dispatch', 'root_delivery_dispatch_failed');
238
+ result = unavailable('remote_command_unresolved');
239
+ }
183
240
  finally {
184
241
  if (issuedPermit) retirePermit(issuedPermit);
185
242
  }
@@ -16,6 +16,21 @@ const AUTOMATIC_BLOCKERS = new Set([
16
16
  ]);
17
17
  const AUTO_LIMIT = 5;
18
18
  const AUTO_AGE_MS = 30 * 60 * 1000;
19
+ // A parent row that reached one of these statuses is itself the recorded proof
20
+ // that the prompt reached the terminal (see prompt-submission-contract's
21
+ // TERMINAL_STATUS_MAP). Nothing to recover: the client is told "sent".
22
+ const SENT_PARENT_STATUSES = new Set([
23
+ 'sent', 'accepted_by_terminal', 'durable_user_message_seen', 'response_started', 'queued_by_agent',
24
+ ]);
25
+
26
+ // WP-0 (queue-delivery-recovery): the ONLY three verdicts the recovery classifier
27
+ // may return. `recoveryAction` is the action a client should offer; `deliveryState`
28
+ // is what CTM can prove about the prompt. Each is a frozen singleton so a caller can
29
+ // never mutate a verdict, and none carries a permit, proof, or receipt id -- this
30
+ // classifier never grants delivery authority.
31
+ const RECOVERY_SENT = Object.freeze({ recoveryAction: 'none', deliveryState: 'sent' });
32
+ const RECOVERY_NOT_STARTED = Object.freeze({ recoveryAction: 'resend', deliveryState: 'not_started' });
33
+ const RECOVERY_UNKNOWN = Object.freeze({ recoveryAction: 'review', deliveryState: 'unknown' });
19
34
 
20
35
  function unavailable() {
21
36
  return Object.freeze({ ok: false, code: 'root_job_unavailable', retryable: false });
@@ -119,4 +134,25 @@ function selectRootDeliveryAttempt(snapshot, { automatic = false, nowMs = Date.n
119
134
  });
120
135
  }
121
136
 
122
- module.exports = { selectRootDeliveryAttempt };
137
+ // Truthful recovery classification for a parent whose local delivery job is gone
138
+ // (process restart, replayed id, lost registry). It answers one question only:
139
+ // may the user resend the SAME text, or must a human review first?
140
+ //
141
+ // confirmed sent parent status -> sent / none
142
+ // valid no-child or valid certified no-effect child -> not_started / resend
143
+ // everything else (invalid, read-failed, reserved,
144
+ // unknown, ambiguous, exhausted) -> unknown / review
145
+ //
146
+ // `sent` requires the durable parent row itself to say so. A proven no-child
147
+ // snapshot (`latest: null`) and a certified preflight_no_effect child both permit
148
+ // `resend`; a snapshot whose `latest` field is absent or malformed proves nothing
149
+ // and stays `review`.
150
+ function selectRootDeliveryRecovery(snapshot) {
151
+ const parent = validParent(snapshot);
152
+ if (!parent) return RECOVERY_UNKNOWN;
153
+ if (SENT_PARENT_STATUSES.has(parent.status)) return RECOVERY_SENT;
154
+ const selected = selectRootDeliveryAttempt(snapshot, { automatic: false });
155
+ return selected?.ok === true ? RECOVERY_NOT_STARTED : RECOVERY_UNKNOWN;
156
+ }
157
+
158
+ module.exports = { selectRootDeliveryAttempt, selectRootDeliveryRecovery };
@@ -315,6 +315,7 @@ function createRootPromptAdmission(deps = {}) {
315
315
  });
316
316
  const expected = Object.freeze({ ...expectedJob });
317
317
  let metadata = null;
318
+ let metadataReadDiagnostic = null;
318
319
  try {
319
320
  if (currentIdentity(getDb, getInstanceId, originalDb, instanceId)) {
320
321
  const candidate = await promptAdmissionTrace.time('metadata.read', () => readAcceptedMetadata(selector));
@@ -322,8 +323,38 @@ function createRootPromptAdmission(deps = {}) {
322
323
  metadata = validateMetadata(candidate, selector, expected);
323
324
  }
324
325
  }
325
- } catch { metadata = null; }
326
- if (!metadata) return { response, admission: null };
326
+ } catch (error) {
327
+ metadata = null;
328
+ metadataReadDiagnostic = canonicalReadError(error, originalDb);
329
+ }
330
+ if (!metadata) {
331
+ // WP-0 criterion 5: this admission dispatched a FRESH parent and confirmed
332
+ // its durable record, but the bounded metadata read did not reproduce the
333
+ // committed descriptor. That is a typed handoff failure -- the caller must
334
+ // never see the cached acceptance without a committed in-memory handle. The
335
+ // committed historical record is left untouched: no permission is recreated
336
+ // from the database, and nothing is written back. A plain cached replay
337
+ // never reaches here (it does not dispatch), so it is not mislabelled.
338
+ if (didDispatch && confirmed && expectedJob) {
339
+ reportDiagnosticSafely(reportDiagnostic, {
340
+ phase: 'admission_metadata',
341
+ code: 'root_admission_metadata_unavailable',
342
+ ...(metadataReadDiagnostic ? {
343
+ errorKind: metadataReadDiagnostic.errorKind,
344
+ nativeHandleOpen: metadataReadDiagnostic.nativeHandleOpen,
345
+ nativeHandleClosed: metadataReadDiagnostic.nativeHandleClosed,
346
+ ...(metadataReadDiagnostic.failureClass ? { failureClass: metadataReadDiagnostic.failureClass } : {}),
347
+ ...(metadataReadDiagnostic.sourceFile ? { sourceFile: metadataReadDiagnostic.sourceFile } : {}),
348
+ ...(metadataReadDiagnostic.sourceLine ? { sourceLine: metadataReadDiagnostic.sourceLine } : {}),
349
+ } : {}),
350
+ });
351
+ return { response: { ...blocked('root_admission_metadata_unavailable', 503),
352
+ recoveryAction: 'resend',
353
+ message: 'CTM saved your prompt but could not start delivery. Resend to try again.',
354
+ }, admission: null };
355
+ }
356
+ return { response, admission: null };
357
+ }
327
358
  const handle = Object.freeze({});
328
359
  const record = Object.freeze({
329
360
  selector, metadata, intentHandle, target: target || null,