create-walle 0.10.0 → 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 (381) hide show
  1. package/README.md +88 -41
  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 +7 -5
  6. package/package.json +10 -4
  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/capture-codex-rollout-records.js +417 -0
  11. package/template/coding-task-manager/bin/census-codex-lead-history.js +325 -0
  12. package/template/coding-task-manager/bin/claude-hook.js +7 -3
  13. package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
  14. package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
  15. package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
  16. package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
  17. package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
  18. package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
  19. package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
  20. package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
  21. package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
  22. package/template/coding-task-manager/bin/seam-replay-codex.js +301 -0
  23. package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
  24. package/template/coding-task-manager/db.js +705 -286
  25. package/template/coding-task-manager/git-utils.js +44 -0
  26. package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
  27. package/template/coding-task-manager/lib/agent-presets.js +15 -0
  28. package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
  29. package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
  30. package/template/coding-task-manager/lib/api/host-stats.js +98 -0
  31. package/template/coding-task-manager/lib/api/ingress.js +35 -12
  32. package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
  33. package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
  34. package/template/coding-task-manager/lib/api/machines.js +461 -0
  35. package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
  36. package/template/coding-task-manager/lib/api/providers.js +53 -6
  37. package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
  38. package/template/coding-task-manager/lib/api/sessions.js +160 -8
  39. package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
  40. package/template/coding-task-manager/lib/api/worktrees.js +38 -37
  41. package/template/coding-task-manager/lib/approval-drift.js +16 -1
  42. package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
  43. package/template/coding-task-manager/lib/approval-scope.js +68 -4
  44. package/template/coding-task-manager/lib/auth-context.js +32 -2
  45. package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
  46. package/template/coding-task-manager/lib/auth-rules.js +186 -45
  47. package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
  48. package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
  49. package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
  50. package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
  51. package/template/coding-task-manager/lib/claude-session-title.js +6 -30
  52. package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
  53. package/template/coding-task-manager/lib/client-diag-handler.js +25 -0
  54. package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
  55. package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
  56. package/template/coding-task-manager/lib/codex-history-terminal-renderer.js +33 -33
  57. package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
  58. package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
  59. package/template/coding-task-manager/lib/codex-local-links.js +267 -0
  60. package/template/coding-task-manager/lib/codex-markdown-lists.js +160 -0
  61. package/template/coding-task-manager/lib/codex-native-history-prefix.js +4 -0
  62. package/template/coding-task-manager/lib/codex-paths.js +2 -1
  63. package/template/coding-task-manager/lib/codex-source-handoff.js +186 -0
  64. package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
  65. package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
  66. package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
  67. package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
  68. package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
  69. package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
  70. package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
  71. package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
  72. package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
  73. package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
  74. package/template/coding-task-manager/lib/db-domains.js +21 -2
  75. package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
  76. package/template/coding-task-manager/lib/db-owner-worker-client.js +22 -0
  77. package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
  78. package/template/coding-task-manager/lib/feature-manifest.js +1 -3
  79. package/template/coding-task-manager/lib/fs-watcher.js +3 -2
  80. package/template/coding-task-manager/lib/harness-history-transition.js +41 -14
  81. package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
  82. package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
  83. package/template/coding-task-manager/lib/headless-term-service.js +60 -23
  84. package/template/coding-task-manager/lib/history-transition-read-op.js +179 -23
  85. package/template/coding-task-manager/lib/host-control-dial.js +167 -0
  86. package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
  87. package/template/coding-task-manager/lib/host-handoff.js +51 -13
  88. package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
  89. package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
  90. package/template/coding-task-manager/lib/host-stats.js +328 -0
  91. package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
  92. package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
  93. package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
  94. package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
  95. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
  96. package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
  97. package/template/coding-task-manager/lib/instance-manifest.js +13 -1
  98. package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
  99. package/template/coding-task-manager/lib/kernel/diag-log.js +12 -0
  100. package/template/coding-task-manager/lib/launch-model-override.js +160 -1
  101. package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
  102. package/template/coding-task-manager/lib/lead-model-observer.js +17 -2
  103. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
  104. package/template/coding-task-manager/lib/lead-model-switch.js +11 -2
  105. package/template/coding-task-manager/lib/lead-model-transition-runtime.js +42 -4
  106. package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
  107. package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
  108. package/template/coding-task-manager/lib/login-tripwire.js +18 -0
  109. package/template/coding-task-manager/lib/machine-pairing.js +238 -0
  110. package/template/coding-task-manager/lib/machine-pool.js +409 -0
  111. package/template/coding-task-manager/lib/machine-registry.js +749 -0
  112. package/template/coding-task-manager/lib/machine-self.js +66 -0
  113. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +56 -4
  114. package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
  115. package/template/coding-task-manager/lib/media-journal.js +84 -27
  116. package/template/coding-task-manager/lib/media-store-options.js +43 -15
  117. package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
  118. package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
  119. package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +16 -3
  120. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  121. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  122. package/template/coding-task-manager/lib/model-proxy/collaboration-codec.js +94 -0
  123. package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
  124. package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
  125. package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
  126. package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
  127. package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
  128. package/template/coding-task-manager/lib/model-proxy/responses-face.js +24 -2
  129. package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
  130. package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
  131. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  132. package/template/coding-task-manager/lib/native-history-control.js +62 -14
  133. package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
  134. package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
  135. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
  136. package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
  137. package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
  138. package/template/coding-task-manager/lib/prompt-admission-trace.js +69 -0
  139. package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
  140. package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
  141. package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
  142. package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
  143. package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
  144. package/template/coding-task-manager/lib/raw-history-block.js +3 -1
  145. package/template/coding-task-manager/lib/read-pool-client.js +36 -24
  146. package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
  147. package/template/coding-task-manager/lib/read-pool-telemetry.js +14 -5
  148. package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
  149. package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
  150. package/template/coding-task-manager/lib/remote-command-context.js +12 -0
  151. package/template/coding-task-manager/lib/remote-command-runtime.js +18 -5
  152. package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
  153. package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
  154. package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
  155. package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
  156. package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
  157. package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
  158. package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
  159. package/template/coding-task-manager/lib/restart-preflight.js +9 -2
  160. package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
  161. package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
  162. package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
  163. package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
  164. package/template/coding-task-manager/lib/resume-cwd.js +13 -0
  165. package/template/coding-task-manager/lib/resume-from-store.js +12 -1
  166. package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
  167. package/template/coding-task-manager/lib/resume-preflight.js +21 -16
  168. package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
  169. package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
  170. package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
  171. package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
  172. package/template/coding-task-manager/lib/root-prompt-admission.js +43 -11
  173. package/template/coding-task-manager/lib/root-prompt-flow.js +175 -38
  174. package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
  175. package/template/coding-task-manager/lib/server-memory-sampler-runtime.js +10 -0
  176. package/template/coding-task-manager/lib/server-memory-sampler.js +41 -2
  177. package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
  178. package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
  179. package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
  180. package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
  181. package/template/coding-task-manager/lib/session-create/identity.js +20 -1
  182. package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
  183. package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
  184. package/template/coding-task-manager/lib/session-create/provider-process.js +38 -39
  185. package/template/coding-task-manager/lib/session-create/response.js +30 -12
  186. package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
  187. package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
  188. package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
  189. package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
  190. package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
  191. package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
  192. package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
  193. package/template/coding-task-manager/lib/session-host-manager.js +152 -206
  194. package/template/coding-task-manager/lib/session-jobs.js +17 -9
  195. package/template/coding-task-manager/lib/session-lifecycle.js +20 -50
  196. package/template/coding-task-manager/lib/session-model-action-preconditions.js +68 -3
  197. package/template/coding-task-manager/lib/session-resource-sampler.js +67 -9
  198. package/template/coding-task-manager/lib/session-standup.js +67 -4
  199. package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
  200. package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
  201. package/template/coding-task-manager/lib/session-status.js +2 -2
  202. package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
  203. package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
  204. package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
  205. package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
  206. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
  207. package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
  208. package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
  209. package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
  210. package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
  211. package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
  212. package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
  213. package/template/coding-task-manager/lib/transcript-link.js +77 -0
  214. package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
  215. package/template/coding-task-manager/lib/transcript-source.js +8 -3
  216. package/template/coding-task-manager/lib/transcript-store.js +40 -0
  217. package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
  218. package/template/coding-task-manager/lib/typing-interaction-trace.js +136 -2
  219. package/template/coding-task-manager/lib/typing-worker-stages.js +208 -1
  220. package/template/coding-task-manager/lib/usage-ledger/claude-parse.js +53 -2
  221. package/template/coding-task-manager/lib/usage-ledger/ingest.js +13 -4
  222. package/template/coding-task-manager/lib/usage-ledger/read.js +14 -0
  223. package/template/coding-task-manager/lib/usage-ledger/schema.js +49 -1
  224. package/template/coding-task-manager/lib/usage-ledger/store.js +337 -6
  225. package/template/coding-task-manager/lib/usage-ledger/task-read.js +123 -5
  226. package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
  227. package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
  228. package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
  229. package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
  230. package/template/coding-task-manager/lib/ws/input.js +1 -0
  231. package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
  232. package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
  233. package/template/coding-task-manager/lib/ws-router.js +68 -1
  234. package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
  235. package/template/coding-task-manager/oauth-proxy.js +99 -55
  236. package/template/coding-task-manager/package-lock.json +24 -0
  237. package/template/coding-task-manager/package.json +3 -0
  238. package/template/coding-task-manager/protocol/message-table.json +0 -10
  239. package/template/coding-task-manager/protocol/messages.js +0 -10
  240. package/template/coding-task-manager/protocol/ws-schema.json +0 -20
  241. package/template/coding-task-manager/protocol/ws-schema.md +0 -2
  242. package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
  243. package/template/coding-task-manager/providers/codex-mcp.js +14 -5
  244. package/template/coding-task-manager/public/css/app.css +279 -2
  245. package/template/coding-task-manager/public/css/reviews.css +4 -0
  246. package/template/coding-task-manager/public/index.html +297 -240
  247. package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
  248. package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
  249. package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
  250. package/template/coding-task-manager/public/js/background-work.js +13 -7
  251. package/template/coding-task-manager/public/js/client-health-core.js +81 -0
  252. package/template/coding-task-manager/public/js/client-health-main.js +61 -0
  253. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
  254. package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
  255. package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
  256. package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
  257. package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
  258. package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
  259. package/template/coding-task-manager/public/js/history-scroller.js +187 -9
  260. package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
  261. package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
  262. package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
  263. package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -4
  264. package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
  265. package/template/coding-task-manager/public/js/protocol.js +0 -10
  266. package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
  267. package/template/coding-task-manager/public/js/reviews.js +7 -3
  268. package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
  269. package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
  270. package/template/coding-task-manager/public/js/row-join.js +18 -3
  271. package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
  272. package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
  273. package/template/coding-task-manager/public/js/session-usage-view.js +1 -0
  274. package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
  275. package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
  276. package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
  277. package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
  278. package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
  279. package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
  280. package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
  281. package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
  282. package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
  283. package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
  284. package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
  285. package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -3
  286. package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
  287. package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
  288. package/template/coding-task-manager/public/js/walle-session.js +30 -7
  289. package/template/coding-task-manager/public/m/app.css +226 -8
  290. package/template/coding-task-manager/public/m/app.js +502 -9
  291. package/template/coding-task-manager/public/m/index.html +38 -1
  292. package/template/coding-task-manager/public/m/machines.js +1313 -0
  293. package/template/coding-task-manager/public/m/pool-failover.js +391 -0
  294. package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
  295. package/template/coding-task-manager/public/m/sw.js +5 -0
  296. package/template/coding-task-manager/queue-engine.js +13 -0
  297. package/template/coding-task-manager/server/approvals.js +5 -6
  298. package/template/coding-task-manager/server/boot.js +136 -17
  299. package/template/coding-task-manager/server.js +964 -425
  300. package/template/coding-task-manager/session-integrity.js +7 -9
  301. package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
  302. package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
  303. package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
  304. package/template/coding-task-manager/workers/session-host-process.js +14 -3
  305. package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
  306. package/template/coding-task-manager/workers/worktree-worker.js +34 -0
  307. package/template/package-lock.json +2 -2
  308. package/template/package.json +1 -1
  309. package/template/scripts/conversation-storage-v2-migration.js +78 -11
  310. package/template/shared/conversation-storage-v2-contract.js +42 -6
  311. package/template/shared/fixtures/worker-status-payloads.json +392 -0
  312. package/template/shared/git-remote-key.js +83 -0
  313. package/template/shared/host-identity.js +98 -2
  314. package/template/shared/owner-write-safety.js +135 -0
  315. package/template/shared/public-runtime.js +13 -5
  316. package/template/shared/release-schema.json +3 -2
  317. package/template/shared/report-citations.js +14 -0
  318. package/template/shared/sqlite-write-lock.js +361 -223
  319. package/template/shared/storage/contract.js +35 -3
  320. package/template/wall-e/agent.js +21 -0
  321. package/template/wall-e/api-walle.js +5 -0
  322. package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
  323. package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
  324. package/template/wall-e/brain/helper-rules-schema.js +129 -0
  325. package/template/wall-e/brain/migrations.js +51 -22
  326. package/template/wall-e/brain/model-deployment-facts.js +31 -1
  327. package/template/wall-e/brain/owner-call-whitelist.js +16 -0
  328. package/template/wall-e/brain/schema-ensures.js +22 -0
  329. package/template/wall-e/brain.js +1 -1
  330. package/template/wall-e/coding/host-capabilities.js +2 -2
  331. package/template/wall-e/coding/journal-dispatcher.js +51 -0
  332. package/template/wall-e/coding/session-journal.js +28 -40
  333. package/template/wall-e/coding-orchestrator.js +23 -26
  334. package/template/wall-e/fly.toml +1 -1
  335. package/template/wall-e/http/auth.js +18 -1
  336. package/template/wall-e/http/blog-views-api.js +318 -0
  337. package/template/wall-e/http/helper-rules-api.js +321 -0
  338. package/template/wall-e/http/model-proxy-providers.js +3 -1
  339. package/template/wall-e/http/telemetry-api.js +12 -0
  340. package/template/wall-e/lib/blog-view-policy.js +88 -0
  341. package/template/wall-e/lib/blog-view-store.js +169 -0
  342. package/template/wall-e/lib/client-config-lock.js +2 -1
  343. package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
  344. package/template/wall-e/lib/error-kinds.js +10 -1
  345. package/template/wall-e/lib/feature-manifest.js +1 -1
  346. package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
  347. package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
  348. package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
  349. package/template/wall-e/lib/helper-rules/grade.js +277 -0
  350. package/template/wall-e/lib/helper-rules/label.js +247 -0
  351. package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
  352. package/template/wall-e/lib/helper-rules/store.js +667 -0
  353. package/template/wall-e/lib/mcp-integration.js +5 -6
  354. package/template/wall-e/lib/runtime-worker-pool.js +1 -0
  355. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
  356. package/template/wall-e/lib/telemetry-db-schema.js +37 -0
  357. package/template/wall-e/lib/telemetry-process-client.js +76 -1
  358. package/template/wall-e/llm/default-fallback.js +4 -0
  359. package/template/wall-e/llm/model-catalog.js +13 -4
  360. package/template/wall-e/llm/model-roles.js +6 -0
  361. package/template/wall-e/llm/portkey-http.js +150 -0
  362. package/template/wall-e/llm/portkey-sync.js +15 -145
  363. package/template/wall-e/llm/worker-mcp-routing.js +61 -0
  364. package/template/wall-e/llm/worker-route.js +43 -1
  365. package/template/wall-e/loops/brain-custom-writes.js +45 -0
  366. package/template/wall-e/mcp-protocol-contract.js +41 -1
  367. package/template/wall-e/mcp-server.js +13 -2
  368. package/template/wall-e/mcp-worker-schemas.js +20 -2
  369. package/template/wall-e/memory/ctm-context-client.js +25 -5
  370. package/template/wall-e/shared/release-schema.json +3 -2
  371. package/template/wall-e/shared/sqlite-write-lock.js +361 -223
  372. package/template/wall-e/shared/storage/contract.js +35 -3
  373. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  374. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
  375. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
  376. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
  377. package/template/wall-e/tools/local-tools.js +12 -5
  378. package/template/wall-e/workers/brain-owner-worker.js +9 -0
  379. package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
  380. package/template/wall-e/workers/telemetry-worker.js +8 -0
  381. package/template/coding-task-manager/lib/giant-freeze-park.js +0 -31
@@ -363,7 +363,7 @@ const OP_META = {
363
363
  materializeRestoredSessionIndex: { kind: 'write', equality: 'exact', domain: 'core' },
364
364
  persistJsonlReconciliationBatch: { kind: 'write', equality: 'exact', domain: 'core' },
365
365
  getAllSessionTitles: { kind: 'read', equality: 'exact' },
366
- setSessionTitleStatusNew: { kind: 'write', equality: 'exact', domain: 'core' }, setSessionTitleMirror: { kind: 'write', equality: 'exact', domain: 'core' }, getSessionTitleMirror: { kind: 'read', equality: 'exact' }, // WP-1
366
+ setSessionTitleStatusNew: { kind: 'write', equality: 'exact', domain: 'core' }, setSessionTitleMirror: { kind: 'write', equality: 'exact', domain: 'core' }, getSessionTitleMirror: { kind: 'read', equality: 'exact' }, applyAgentTitleBackfillRows: { kind: 'write', equality: 'exact', domain: 'core' }, // WP-1 / session-name WP-6
367
367
  updateSessionTokens: { kind: 'write', equality: 'exact', domain: 'core' },
368
368
  getSessionTokens: { kind: 'read', equality: 'exact' },
369
369
  // WP-1 (design doc §5/§9 S-19): last known physical-footprint sample, persisted at
@@ -426,6 +426,40 @@ const OP_META = {
426
426
  getHostedSyncSchemaStatus: { kind: 'sqlite-only', equality: 'exact' },
427
427
  listHostedWorkspaceBindings: { kind: 'sqlite-only', equality: 'set', idKey: 'logical_id' },
428
428
  upsertHostedWorkspaceBinding: { kind: 'write', equality: 'exact', domain: 'core' },
429
+ // WP-2 (docs/remote-machine-hub-sessions-2026-10-22.html §6.1/§8, deferred from WP-0): the hub
430
+ // machine-registry writes. Single-table (`machines`, classified 'core' in
431
+ // coding-task-manager/lib/db-domain-tables.js) — hence the explicit `tables` list Gate 2i
432
+ // checks against the tag, and the domain tag itself, which is what keeps these off the base
433
+ // (firehose) writer behind conversation import once the domain split is on.
434
+ upsertMachineRow: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machines'] },
435
+ recordMachineProbe: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machines'] },
436
+ deleteMachineRow: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machines'] },
437
+ // WP-1 (docs/machine-pool-picker-pairing-dashboard-2026-10-24.html §6.1, D-11, §9 S-4): the
438
+ // host-sample pair. `recordHostSample` is one INSERT OR REPLACE plus the 24 h DELETE in a single
439
+ // transaction, single-table (`host_samples`, classified 'core' in
440
+ // coding-task-manager/lib/db-domain-tables.js) — so the explicit `tables` list Gate 2i checks
441
+ // against the tag is exact, and Gate 2g finds the routing declaration the action needs.
442
+ recordHostSample: { kind: 'write', equality: 'exact', domain: 'core', tables: ['host_samples'] },
443
+ // The bounded read beside it (oldest first, `sinceMs`/`limit`). `set` equality, keyed on `at`
444
+ // — the same shape as listSyncOutbox below.
445
+ listHostSamples: { kind: 'read', equality: 'set', idKey: 'at' },
446
+ // WP-2a (docs/machine-pool-picker-pairing-dashboard-2026-10-24.html §6.1, D-4/P-5/P-6, §9 S-4):
447
+ // the machine-owned settings write (single row, single table `machine_self`), the P-5 roster
448
+ // replacement (single table `machines`, one transaction) and the leave() sweep (`machines`).
449
+ // Both tables are classified 'core' in coding-task-manager/lib/db-domain-tables.js.
450
+ setMachineSelf: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machine_self'] },
451
+ replaceRosterMachines: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machines'] },
452
+ deleteAllMachineRows: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machines'] },
453
+ // WP-3 (machine-pool-picker-pairing-dashboard-2026-10-24.html §6.3, D-6/D-9, §9 S-15/S-16): the
454
+ // pairing-offer trio — mint one offer, mark exactly one redeemed (single-use), and the AC-3c burn
455
+ // that flips every live offer at once. Single-table (`machine_pairing_offers`, classified 'core' in
456
+ // coding-task-manager/lib/db-domain-tables.js), so Gate 2i's `tables` list is exact. The bounded
457
+ // read beside them (`listLivePairingOffers`, unspent+unexpired as of `at`) is `set` equality keyed
458
+ // on `id`, the same shape as listHostSamples above.
459
+ insertPairingOffer: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machine_pairing_offers'] },
460
+ markPairingOfferRedeemed: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machine_pairing_offers'] },
461
+ burnLivePairingOffers: { kind: 'write', equality: 'exact', domain: 'core', tables: ['machine_pairing_offers'] },
462
+ listLivePairingOffers: { kind: 'read', equality: 'set', idKey: 'id' },
429
463
  seedHostedWorkspaceBindings: { kind: 'sqlite-only', equality: 'exact', domain: 'core', tables: ['workspace_bindings'] },
430
464
  updateStartupTaskWorkspaceMapping: { kind: 'write', equality: 'exact', domain: 'core' },
431
465
  repointStartupTaskWorkspace: { kind: 'sqlite-only', equality: 'exact', domain: 'core', tables: ['startup_tasks', 'sync_outbox', 'synced_entities'] },
@@ -784,8 +818,6 @@ const OP_META = {
784
818
  // the live scrollback_identity table is listed.
785
819
  prepareScrollbackForSession: { kind: 'write', equality: 'exact', domain: 'core', tables: ['scrollback_identity'] },
786
820
  upsertAgentSessionIdentity: { kind: 'write', equality: 'exact', domain: 'core', tables: ['agent_sessions'] },
787
- promoteStartupTaskTitleIfUserNamed: { kind: 'write', equality: 'exact', domain: 'core', tables: ['ctm_sessions'] },
788
- repairSessionTitlesFromStartupTasks: { kind: 'write', equality: 'exact', domain: 'core', tables: ['ctm_sessions'] },
789
821
  repairCodexRolloutAgentIdentities: { kind: 'write', equality: 'exact', domain: 'core', tables: ['agent_sessions', 'ctm_sessions'] },
790
822
  cleanupOrphanScrollbackIdentities: { kind: 'write', equality: 'exact', domain: 'core', tables: ['scrollback_identity', 'startup_tasks'] },
791
823
  // --- writer:'base' pins (deliberately NOT re-routed; behaviour unchanged) ---
@@ -1790,6 +1790,27 @@ async function main() {
1790
1790
  onError: (err) => console.error('[weekly-eval] Error:', err.message),
1791
1791
  });
1792
1792
 
1793
+ // Lead-cost routing WP-5b §6.8: the daily rules probe. The outcome stream alone cannot move
1794
+ // an off explore cell -- the 7-day probe window needs elapsed time -- so one job per day runs
1795
+ // the evaluator over every explore cell. The write crosses to the brain owner through
1796
+ // runRuntimeJob (the $WALLE_OWNER path), the same shape as the sibling daily brain jobs; there
1797
+ // is no day-of-week gate because the evaluator owns its own timing.
1798
+ scheduler.register({
1799
+ name: 'helper-rules-probe',
1800
+ networkClass: 'local', // local sqlite read + one owner-path write, no provider call
1801
+ run: async () => runRuntimeJob(
1802
+ 'helper-rules-probe',
1803
+ () => require('./loops/brain-write').brainWrite('helperRulesDailyTick'),
1804
+ ),
1805
+ intervalMs: 24 * 60 * 60 * 1000, // daily
1806
+ pool: 'maintenance',
1807
+ lane: 2,
1808
+ priority: 11,
1809
+ restartCatchupPolicy: 'defer',
1810
+ startDelayMs: 240000, // 4min — well off the boot/readiness path and unique in the maintenance pool
1811
+ onError: (err) => console.error('[helper-rules-probe] Error:', err.message),
1812
+ });
1813
+
1793
1814
  // Note: session mining runs inside weekly-eval-loop (step 3), no separate scheduler needed.
1794
1815
 
1795
1816
  const _bootPostRegisterT = _perf.now();
@@ -12,6 +12,7 @@ const syncApiModule = require('./http/sync-api');
12
12
  const { handleAdminApi } = require('./http/admin-api');
13
13
  const { handleAssistantApi } = require('./http/assistant-api');
14
14
  const { handleBackgroundCompletionApi } = require('./http/background-completion-api');
15
+ const { handleHelperRulesApi } = require('./http/helper-rules-api');
15
16
  const { handleContextApi } = require('./http/context-api');
16
17
  const { handleFeedbackApi } = require('./http/feedback-api');
17
18
  const { handleCodingApi } = require('./http/coding-api');
@@ -2337,6 +2338,10 @@ function routeWalleApi(req, res, url, routeHandlers = {}) {
2337
2338
  if (!brain || !ensureBrainInit()) return jsonResponse(res, { error: 'Brain module not available' }, 503), true;
2338
2339
  return handleBackgroundCompletionApi(req, res, url, { brain });
2339
2340
  }
2341
+ // WP-4 (lead-cost-routing-round1-2026-10-21.html §6.4): the stdio MCP proxy's read-only state
2342
+ // read, dispatched here (not inside the frozen handleWalleApi) alongside the block above.
2343
+ // WP-7 extends the same handler with the panel's /cells routes; it answers false for anything else under the prefix.
2344
+ if (url.pathname === '/api/wall-e/helper-rules/guidance' || url.pathname.startsWith('/api/wall-e/helper-rules/cells')) return handleHelperRulesApi(req, res, url, { brain, ensureBrainInit });
2340
2345
  if ((url.pathname === '/api/decisions' && req.method === 'POST') || (url.pathname === '/api/decisions/status' && req.method === 'GET')) return require('./http/decisions-api').handleDecisionsApi(req, res, url, routeHandlers.decisions);
2341
2346
  // multi-machine WP-5 (§6.6): the brain sync pair. Dispatched here, not inside
2342
2347
  // the frozen handleWalleApi, because the doc's spelling is `/api/walle/sync/*`
@@ -234,11 +234,12 @@ function isConfiguredBrain(dbPath) {
234
234
  }
235
235
 
236
236
  // Returns '' when the db is not live, otherwise the reason. The write-lock owner check is the one
237
- // check --force-live does not skip.
237
+ // check --force-live does not skip. The protocol-2 record persists after release (D-3), so a live
238
+ // pid here means "the last write-lock holder is still alive", not "a write is in flight".
238
239
  async function liveReason(dbPath, { forceLive, deps }) {
239
240
  try {
240
241
  const info = JSON.parse(fs.readFileSync(`${dbPath}.write-lock`, 'utf8'));
241
- if (info && info.pid !== process.pid && pidAlive(info.pid)) return `write-lock held by alive pid ${info.pid}`;
242
+ if (info && info.pid !== process.pid && pidAlive(info.pid)) return `last write-lock holder pid ${info.pid} is alive`;
242
243
  } catch {
243
244
  /* expected: no lock file, or an unparseable stale one, is "no alive owner" */
244
245
  }
@@ -8,6 +8,7 @@ const { ctmPort: resolveCtmPort } = require('../lib/ctm-endpoint');
8
8
  const {
9
9
  handleProtocolMessage,
10
10
  } = require('../mcp-protocol-contract');
11
+ const { fetchExploreCellState, httpJsonGet, leadAgentFromClientInfo } = require('../lib/helper-rules/cell-state');
11
12
  const {
12
13
  attachDelegateCallerContext,
13
14
  resolveCallerParentAgentSessionId,
@@ -166,10 +167,15 @@ function _localControlPlaneEnabled() {
166
167
  return !/^(0|false|no|off)$/i.test(String(process.env.WALLE_MCP_LOCAL_CONTROL_PLANE || '').trim());
167
168
  }
168
169
 
169
- function _localControlResponseOne(message) {
170
- let control = handleProtocolMessage(message);
170
+ async function _localControlResponseOne(message) {
171
+ // WP-4 §6.4: the caller harness's `explore` cell lives in the daemon (this process owns no
172
+ // brain): read-only endpoint, existing breaker machinery, 1500 ms bound, fail-open 'on'.
173
+ const options = message?.method === 'initialize'
174
+ ? { leadGuidance: { explore: await fetchExploreCellState({ agent: leadAgentFromClientInfo(message.params?.clientInfo), requestFn: (path) => _executeDaemonRequest(() => httpJsonGet({ host: process.env.WALLE_HOST || '127.0.0.1', port: _wallePort(), path })) }) } }
175
+ : undefined;
176
+ let control = handleProtocolMessage(message, options);
171
177
  if (!control.handled) {
172
- control = require('../mcp-contract').handleControlMessage(message);
178
+ control = require('../mcp-contract').handleControlMessage(message, options);
173
179
  }
174
180
  return control.handled ? {
175
181
  handled: true,
@@ -178,10 +184,10 @@ function _localControlResponseOne(message) {
178
184
  } : { handled: false, response: null, protocolVersion: '' };
179
185
  }
180
186
 
181
- function _localControlResponse(message) {
187
+ async function _localControlResponse(message) {
182
188
  if (!_localControlPlaneEnabled()) return { handled: false, response: null };
183
189
  if (!Array.isArray(message)) return _localControlResponseOne(message);
184
- const results = message.map(_localControlResponseOne);
190
+ const results = await Promise.all(message.map((item) => _localControlResponseOne(item)));
185
191
  if (!results.every((result) => result.handled)) return { handled: false, response: null };
186
192
  const responses = results.map((result) => result.response).filter(Boolean);
187
193
  return { handled: true, response: responses.length ? responses : null, protocolVersion: '' };
@@ -655,7 +661,7 @@ async function handleOne(message) {
655
661
  protocol,
656
662
  });
657
663
  }
658
- const control = _localControlResponse(message);
664
+ const control = await _localControlResponse(message);
659
665
  if (control.handled) {
660
666
  const selectedProtocol = control.protocolVersion || protocol;
661
667
  mcpDiagnostics.recordPhase('local_response_built', {
@@ -0,0 +1,129 @@
1
+ 'use strict';
2
+
3
+ // Lead-cost routing WP-5a (design §6.6): the helper-rules record — three tables and one
4
+ // seeded kv row. The DDL is a plain string with two callers rather than an exec-in-place,
5
+ // exactly like brain/schema.js's ensureBrainSyncSchema: `MIGRATIONS[100]` runs it on an
6
+ // existing brain walking the ladder forward, and the runner's `ensures` list
7
+ // (brain/schema-ensures.js) runs it on a fresh install, which stamps SCHEMA_VERSION
8
+ // directly and never replays a migration body. Both paths execute the SAME text, which is
9
+ // what tests-integration/migration-baseline-parity.test.js compares.
10
+ //
11
+ // Migration IDs, once written, are never renumbered. Additive and idempotent: a second run
12
+ // changes nothing (IF NOT EXISTS throughout, INSERT OR IGNORE for the seed).
13
+
14
+ const HELPER_RULES_DDL = `
15
+ CREATE TABLE IF NOT EXISTS helper_outcomes (
16
+ id TEXT PRIMARY KEY, -- wait-watch: '<wait_id>:<worker_id>:<n>' ; explore: the delegation invocation id
17
+ cell_key TEXT NOT NULL, -- wait-watch|default|<route_alias> or explore|default|<codex|claude>
18
+ kind TEXT NOT NULL, -- 'wait-watch' | 'explore'
19
+ seq INTEGER NOT NULL, -- per-cell sequence (trailing windows)
20
+ label TEXT, -- wait-watch: useful | empty | late | silent_useful | silent_empty ; explore: NULL
21
+ decision TEXT, -- wait-watch: KEEP_WAITING | WAKE_LEAD ; explore: open | handoff | fallback
22
+ helper_usd REAL, lead_usd REAL,
23
+ seconds_to_report REAL, reread_rate REAL, report_rows INTEGER,
24
+ grade_json TEXT, -- WP-6 fills it; NULL here
25
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
26
+ );
27
+ CREATE INDEX IF NOT EXISTS idx_helper_outcomes_cell ON helper_outcomes(cell_key, seq);
28
+ CREATE TABLE IF NOT EXISTS helper_rule_cells (
29
+ cell_key TEXT PRIMARY KEY, kind TEXT NOT NULL,
30
+ state TEXT NOT NULL DEFAULT 'on', -- on | off | probe (missing row = on)
31
+ user_veto INTEGER NOT NULL DEFAULT 0, -- no probe while set
32
+ metrics_json TEXT,
33
+ state_changed_at TEXT, updated_at TEXT NOT NULL DEFAULT (datetime('now'))
34
+ );
35
+ CREATE TABLE IF NOT EXISTS helper_rule_events (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT, cell_key TEXT NOT NULL, from_state TEXT, to_state TEXT NOT NULL,
37
+ reason TEXT NOT NULL, evidence_json TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now'))
38
+ );
39
+ `;
40
+
41
+ // Lead-cost routing round 2 WP-2 (§6.4, S-15): migration 101's additive columns. Round 1 kept
42
+ // only the derived label (`report_rows`), which is precisely what made the mislabelled round-1
43
+ // history impossible to correct — the observed inputs were thrown away. These three columns keep
44
+ // them: `worker_id` is the ctm_session_id the row is addressable by (D-5, `closeOutcomeByWorker`),
45
+ // `facts_json` is the §6.2 facts object the label is re-derivable from (D-1/D-5), and
46
+ // `label_version` says which rule produced the stored decision (NULL facts ⇒ 1, the round-1 rows).
47
+ //
48
+ // SQLite has no `ADD COLUMN IF NOT EXISTS`, so the driver probes `PRAGMA table_info` per column —
49
+ // the same shape brain/recall-hints-migration.js uses. `MIGRATIONS[101]` runs this on an existing
50
+ // brain; the runner's ensures list runs it (right after migration 100's text) on a fresh install,
51
+ // so `baseline + stamp` and the ladder produce byte-identical schemas
52
+ // (tests-integration/migration-baseline-parity.test.js compares sqlite_master, and an ALTER
53
+ // rewrites the stored CREATE TABLE text, so this is load-bearing).
54
+ const HELPER_RULES_V2_COLUMNS = [
55
+ ['worker_id', 'TEXT'], // ctm_session_id of the review worker (explore) or watched worker (wait-watch)
56
+ ['facts_json', 'TEXT'], // round-2 facts; NULL on round-1 rows
57
+ ['label_version', 'INTEGER NOT NULL DEFAULT 1'],
58
+ ];
59
+ const HELPER_RULES_V2_INDEX = 'CREATE INDEX IF NOT EXISTS idx_helper_outcomes_worker ON helper_outcomes(worker_id)';
60
+
61
+ // D-6: the `helper_rules.relabelled_to` watermark — the label version the one-time relabel +
62
+ // recovery pass has already run for. Seeded at 1 (the round-1 version) so the FIRST run at
63
+ // LABEL_VERSION 2 does the recovery, and every later call is a no-op.
64
+ const RELABELLED_TO_KEY = 'helper_rules.relabelled_to';
65
+ const RELABELLED_TO_SEED = '1';
66
+
67
+ // WP-0's census measurement (the lead's own cost per burst): the explore rule's
68
+ // "handing off costs more than the lead was already spending" ceiling. Seeded, not
69
+ // hard-coded, so the panel and the evaluator read one number the user can move.
70
+ const LEAD_BURST_USD_KEY = 'helper_rules.lead_burst_usd';
71
+ const LEAD_BURST_USD_SEED = '0.46';
72
+
73
+ // WP-6 (§6.5, D-19): the spot-check grader's pin. Seeded, not hard-coded at the call site, so the
74
+ // user can move it in kv_store and the grader follows — the ONE place a grader model id appears.
75
+ // No fallback is derived from it: an empty value stores the sample as `unavailable`.
76
+ const GRADER_MODEL_KEY = 'helper_rules.grader_model';
77
+ const GRADER_MODEL_SEED = 'claude-opus-5-5';
78
+
79
+ /** Idempotent, additive. Both the ladder (migration 100) and the fresh-install ensures call this. */
80
+ function ensureHelperRulesSchema(d) {
81
+ if (!d || typeof d.exec !== 'function') return;
82
+ d.exec(HELPER_RULES_DDL);
83
+ // Same shape as brain.js's kv writes (INSERT ... ON CONFLICT): a plain INSERT OR IGNORE
84
+ // so a value the user (or a later census) already wrote is never overwritten.
85
+ const seedKv = d.prepare("INSERT OR IGNORE INTO kv_store (key, value, updated_at) VALUES (?, ?, datetime('now'))");
86
+ seedKv.run(LEAD_BURST_USD_KEY, LEAD_BURST_USD_SEED);
87
+ seedKv.run(GRADER_MODEL_KEY, GRADER_MODEL_SEED);
88
+ }
89
+
90
+ /**
91
+ * Lead-cost routing round 2 WP-2 (S-15): the migration-101 additions — `worker_id`, `facts_json`,
92
+ * `label_version`, their index, and the `helper_rules.relabelled_to` watermark. Idempotent: each
93
+ * ALTER is skipped once the column exists, the index is `IF NOT EXISTS`, and the seed is
94
+ * `INSERT OR IGNORE`, so the invariant-repair rewind and the fresh-install ensures may re-run it
95
+ * any number of times.
96
+ */
97
+ function ensureHelperRulesSchemaV2(d) {
98
+ if (!d || typeof d.exec !== 'function') return;
99
+ // Migration 101 sits directly above 100, and the ensures list runs 100's text first, so the
100
+ // table always exists on both paths; a bare handle still gets a complete v2 schema rather than
101
+ // an "no such table" from the first ALTER.
102
+ const readColumns = () => d.prepare('PRAGMA table_info(helper_outcomes)').all().map((row) => row.name);
103
+ let present = new Set(readColumns());
104
+ if (!present.size) {
105
+ ensureHelperRulesSchema(d);
106
+ present = new Set(readColumns());
107
+ }
108
+ for (const [name, type] of HELPER_RULES_V2_COLUMNS) {
109
+ if (present.has(name)) continue;
110
+ d.exec(`ALTER TABLE helper_outcomes ADD COLUMN ${name} ${type}`);
111
+ }
112
+ d.exec(HELPER_RULES_V2_INDEX);
113
+ d.prepare("INSERT OR IGNORE INTO kv_store (key, value, updated_at) VALUES (?, ?, datetime('now'))")
114
+ .run(RELABELLED_TO_KEY, RELABELLED_TO_SEED);
115
+ }
116
+
117
+ module.exports = {
118
+ GRADER_MODEL_KEY,
119
+ GRADER_MODEL_SEED,
120
+ HELPER_RULES_DDL,
121
+ HELPER_RULES_V2_COLUMNS,
122
+ HELPER_RULES_V2_INDEX,
123
+ LEAD_BURST_USD_KEY,
124
+ LEAD_BURST_USD_SEED,
125
+ RELABELLED_TO_KEY,
126
+ RELABELLED_TO_SEED,
127
+ ensureHelperRulesSchema,
128
+ ensureHelperRulesSchemaV2,
129
+ };
@@ -160,6 +160,32 @@ function ensureSlackInboundJobSeed(d, { now = Date.now } = {}) {
160
160
  return SLACK_INBOUND_JOB_ID;
161
161
  }
162
162
 
163
+ // Migration 57's body (WP-2 fix round 1, db-migration-consolidation), lifted
164
+ // into a named export (clock-free r4 R-4). Heals databases whose
165
+ // conversation_context_snapshots.messages_json was physically dropped by
166
+ // migration 52's now-retired fresh-only branch. That branch used to run right
167
+ // after createTables() on every from-0 install, so any DB created while it was
168
+ // live (almost every live fresh install pre-WP-2) is missing this column —
169
+ // including the column existing but no longer nullable-write-compatible: the
170
+ // write path (_appendRunAttempt, brain/conversation.js) names messages_json
171
+ // unconditionally (with a literal '[]' placeholder — it never carries real
172
+ // content, storage minimization is unaffected), so a DB without the column
173
+ // throws "no such column" on every run-attempt write. Additive + idempotent
174
+ // (column probe). Also run every boot from brain/schema-ensures.js because a
175
+ // later CTM v2 purge can drop it again — clock-free r4 R-4.
176
+ function ensureContextSnapshotMessagesJson(d) {
177
+ const hasTable = d.prepare(
178
+ "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'conversation_context_snapshots' LIMIT 1"
179
+ ).get();
180
+ if (!hasTable) return;
181
+ const hasColumn = d.prepare("PRAGMA table_info(conversation_context_snapshots)").all()
182
+ .some((row) => row.name === 'messages_json');
183
+ if (hasColumn) return;
184
+ d.prepare(
185
+ "ALTER TABLE conversation_context_snapshots ADD COLUMN messages_json TEXT NOT NULL DEFAULT '[]'"
186
+ ).run();
187
+ }
188
+
163
189
  function createMigrationsModule({
164
190
  backfillChatMessageParents,
165
191
  backfillDefaultBenchmarkProvidersForDb,
@@ -1821,29 +1847,12 @@ const MIGRATIONS = {
1821
1847
  // deletion/repair rules. Idempotent: a second run finds nothing to do.
1822
1848
  sweepModelRegistryAnomalies(d);
1823
1849
  },
1824
- // Migration 57 (WP-2 fix round 1, db-migration-consolidation): heals databases
1825
- // whose conversation_context_snapshots.messages_json was physically dropped by
1826
- // migration 52's now-retired fresh-only branch. That branch used to run right
1827
- // after createTables() on every from-0 install, so any DB created while it was
1828
- // live (almost every live fresh install pre-WP-2) is missing this column —
1829
- // including the column existing but no longer nullable-write-compatible: the
1830
- // write path (_appendRunAttempt) names messages_json unconditionally (with a
1831
- // literal '[]' placeholder — it never carries real content, storage
1832
- // minimization is unaffected), so a DB without the column throws "no such
1833
- // column" on every run-attempt write. Additive + idempotent (column probe) —
1850
+ // Migration 57 (WP-2 fix round 1, db-migration-consolidation): re-adds a dropped
1851
+ // conversation_context_snapshots.messages_json. The body is
1852
+ // ensureContextSnapshotMessagesJson() above (see its comment), which the boot
1853
+ // `ensures` list also runs (clock-free r4 R-4). Additive + idempotent, so
1834
1854
  // rerunSafe:true is correct.
1835
- 57: (d) => {
1836
- const hasTable = d.prepare(
1837
- "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'conversation_context_snapshots' LIMIT 1"
1838
- ).get();
1839
- if (!hasTable) return;
1840
- const hasColumn = d.prepare("PRAGMA table_info(conversation_context_snapshots)").all()
1841
- .some((row) => row.name === 'messages_json');
1842
- if (hasColumn) return;
1843
- d.prepare(
1844
- "ALTER TABLE conversation_context_snapshots ADD COLUMN messages_json TEXT NOT NULL DEFAULT '[]'"
1845
- ).run();
1846
- },
1855
+ 57: (d) => ensureContextSnapshotMessagesJson(d),
1847
1856
  // Migration 58 (WP-3, db-migration-consolidation S-7): the one-time destructive
1848
1857
  // RENAME/DROP rebuild of conversation_artifact_provenance's PK (3-col -> 7-col),
1849
1858
  // moved here from ensurePromptPurgeSchema (conversation/prompt-purge.js), which now
@@ -2528,6 +2537,21 @@ const MIGRATIONS = {
2528
2537
  99: (d) => {
2529
2538
  d.exec(DROP_TASK_LEDGER_REMNANTS_SQL);
2530
2539
  },
2540
+ // Migration 100 (lead-cost routing WP-5a, §6.6): the helper-rules record — the two rule
2541
+ // cells' outcome/event log, their state rows and the WP-0 `lead_burst_usd` seed. The DDL
2542
+ // lives in brain/helper-rules-schema.js so the fresh-install path (baseline + stamp, zero
2543
+ // migration bodies) creates the identical text through brain/schema-ensures.js — see
2544
+ // tests-integration/migration-baseline-parity.test.js. Additive and idempotent.
2545
+ 100: (d) => { require('./helper-rules-schema').ensureHelperRulesSchema(d); },
2546
+ // Migration 101 (lead-cost routing round 2 WP-2, §6.4 D-1/D-5): the ADDITIVE half of the
2547
+ // helper-rules record — `worker_id` (D-5: a row is addressable by worker, so a close survives
2548
+ // a Wall-E restart), `facts_json` (D-1: the observed inputs the label is re-derivable from) and
2549
+ // `label_version` (which rule produced the stored label), plus the worker index and the
2550
+ // `helper_rules.relabelled_to` watermark the one-time relabel/recovery pass reads and sets.
2551
+ // Same two callers as 100 (this ladder entry + the ensures list in brain/schema-ensures.js),
2552
+ // same text, same reason (tests-integration/migration-baseline-parity.test.js). Additive and
2553
+ // idempotent: the ALTERs are `PRAGMA table_info`-guarded, the index is `IF NOT EXISTS`.
2554
+ 101: (d) => { require('./helper-rules-schema').ensureHelperRulesSchemaV2(d); },
2531
2555
  };
2532
2556
 
2533
2557
  // --- Migration 91 (surface-redesign WP-3) ---------------------------------
@@ -2937,6 +2961,11 @@ module.exports = createMigrationsModule;
2937
2961
  // path run — a seed that lived only in MIGRATIONS[94] would exist on an upgraded
2938
2962
  // brain and be missing on a brand-new one.
2939
2963
  module.exports.ensureSlackInboundJobSeed = ensureSlackInboundJobSeed;
2964
+ // clock-free r4 R-4 (WP-2): migration 57's body, shared with the boot `ensures`
2965
+ // list (brain/schema-ensures.js). A migration body runs once per ladder, so a
2966
+ // column dropped by a LATER CTM v2 purge was never healed; the ensures list runs
2967
+ // on every open.
2968
+ module.exports.ensureContextSnapshotMessagesJson = ensureContextSnapshotMessagesJson;
2940
2969
  module.exports.SLACK_INBOUND_JOB_ID = SLACK_INBOUND_JOB_ID;
2941
2970
  module.exports.SLACK_INBOUND_JOB_CONFIG = SLACK_INBOUND_JOB_CONFIG;
2942
2971
  // WP-14: the exact table set migration 97 drops, so the migration test asserts
@@ -11,6 +11,11 @@ function _isoStored(value) {
11
11
  return parsed.toISOString();
12
12
  }
13
13
 
14
+ // D-5: an anthropic-messages route relays the Claude CLI's own
15
+ // `output_config.effort` verbatim, so a deployment's levels are the Claude
16
+ // matrix for its own wire model id -- see `perModelLevels` below.
17
+ const ANTHROPIC_MESSAGES_PROTOCOL = 'anthropic-messages';
18
+
14
19
  function buildModelDeploymentFacts({ db, providerId, protocol, getFactRow, routePolicy = {}, routeConfigRevision, routeReasoningLevels = [] } = {}) {
15
20
  if (!db || typeof db.prepare !== 'function') throw new TypeError('supplied db handle required');
16
21
  if (typeof getFactRow !== 'function') throw new TypeError('getFactRow required');
@@ -23,6 +28,23 @@ function buildModelDeploymentFacts({ db, providerId, protocol, getFactRow, route
23
28
  || routeReasoningLevels.some((level) => typeof level !== 'string' || !['low', 'medium', 'xhigh'].includes(level))) {
24
29
  throw new TypeError('routeReasoningLevels invalid');
25
30
  }
31
+ // D-1/D-2: the per-(route, wire model) producer (llm/worker-route.js
32
+ // routeModelReasoningLevels), called with each registry row's wireModelId.
33
+ // Only an anthropic-messages route has one: `protocol` IS that route's
34
+ // classified upstream API (upstreamApisForRow returns anthropic-messages for
35
+ // exactly one row type), so it is the serializable fact that decides the
36
+ // branch -- a function callback cannot travel here, this module runs inside
37
+ // the brain read worker, whose JSON IPC drops it. The require is lazy for the
38
+ // same reason brain/ never requires llm/ at load time (the seam
39
+ // brain/delegate-model-prefs-store.js already uses). A non-array producer
40
+ // result is a programming error and fails validateModelDeployments below.
41
+ const perModelLevels = protocol === ANTHROPIC_MESSAGES_PROTOCOL
42
+ ? (wireModelId) => require('../llm/worker-route').routeModelReasoningLevels({
43
+ type: 'anthropic',
44
+ upstream_apis: [ANTHROPIC_MESSAGES_PROTOCOL],
45
+ model_id: wireModelId,
46
+ })
47
+ : null;
26
48
  const routeId = String(providerId || '');
27
49
  if (!routeId) throw new TypeError('providerId required');
28
50
  const rows = db.prepare(`SELECT r.provider_id, r.model_id, r.max_context_tokens, r.max_output_tokens, r.source AS registry_source, r.last_seen_at,
@@ -46,6 +68,14 @@ function buildModelDeploymentFacts({ db, providerId, protocol, getFactRow, route
46
68
  const factRow = limits.factRow;
47
69
  const observedAt = _isoStored(factRow?.updated_at || factRow?.fetched_at || row.last_seen_at);
48
70
  const sourceKey = factRow ? (factRow.source === 'provider-api' ? 'model-facts-provider-api' : 'model-facts-vendor') : 'unknown';
71
+ // D-2: a per-model routePolicy override still wins (existing precedence);
72
+ // otherwise an anthropic-messages deployment gets the Claude matrix for its
73
+ // own wire model id, and every other route keeps the route-wide list. The
74
+ // caller's array is copied, never aliased.
75
+ let reasoningLevels = routePolicy[wireModelId]?.reasoningLevels;
76
+ if (!Array.isArray(reasoningLevels)) {
77
+ reasoningLevels = perModelLevels ? perModelLevels(wireModelId) : routeReasoningLevels.slice();
78
+ }
49
79
  const descriptorBase = {
50
80
  version: 1, providerId: routeId, routeId, wireModelId, identityId,
51
81
  protocol,
@@ -54,7 +84,7 @@ function buildModelDeploymentFacts({ db, providerId, protocol, getFactRow, route
54
84
  sourceKey, observedAt,
55
85
  inputModalities: Array.isArray(routePolicy[wireModelId]?.inputModalities) ? routePolicy[wireModelId].inputModalities : ['text'],
56
86
  toolCalling: typeof routePolicy[wireModelId]?.toolCalling === 'boolean' ? routePolicy[wireModelId].toolCalling : null,
57
- reasoningLevels: Array.isArray(routePolicy[wireModelId]?.reasoningLevels) ? routePolicy[wireModelId].reasoningLevels : routeReasoningLevels.slice(),
87
+ reasoningLevels,
58
88
  };
59
89
  const revisionInput = {
60
90
  version: descriptorBase.version,
@@ -257,6 +257,22 @@ const BRAIN_CALL_WHITELIST = new Set([
257
257
  // 500 so the live 2,619-question backlog never holds one unbounded write. No
258
258
  // provider is reachable from here, so the owner stays credential-free.
259
259
  'resolveMemoryConflicts',
260
+ // WP-5a (lead-cost routing §6.6/§6.8): the helper-rules record's four writes. Same
261
+ // CUSTOM_BRAIN_CALLS pattern as commitThinkExtraction above — see
262
+ // wall-e/loops/brain-custom-writes.js and wall-e/lib/helper-rules/store.js (the only
263
+ // writer of the three helper_* tables).
264
+ 'helperRulesRecordOutcome',
265
+ 'helperRulesCloseOutcome',
266
+ 'helperRulesSetCellState',
267
+ 'helperRulesDailyTick',
268
+ // Round 2 WP-2 (§6.4 S-16): the close path resolves an open explore row BY worker id (D-5 —
269
+ // a restart cannot orphan it), and the one-time relabel/recovery pass (D-5/D-6) runs through
270
+ // the same owner seam. `helperRulesCloseOutcome` above stays for the id-keyed callers.
271
+ 'helperRulesCloseOutcomeByWorker',
272
+ 'helperRulesRelabel',
273
+ // WP-6 (§6.5): the spot-check grader's verdict on one explore row — pure DB, written
274
+ // fire-and-forget from the lead-call hook, which runs in the read-only MCP worker.
275
+ 'helperRulesSetGrade',
260
276
  ]);
261
277
 
262
278
  const { OWNER_METHOD_PRIORITY, ownerPriorityFor } = require('../lib/brain-owner-op-priority');
@@ -20,6 +20,28 @@ function createSchemaEnsuresModule({ conversationProjectionV2, ensureModelFactsS
20
20
  // MIGRATIONS[95]. Idempotent (INSERT OR IGNORE), so re-running on every boot
21
21
  // changes nothing. Required lazily, inside run(), exactly like the two above.
22
22
  { label: 'chat-thread-backfill', run: (d) => require('./chat-thread-store').backfillChatThreads(d) },
23
+ // WP-5a (lead-cost routing §6.6): the helper-rules record + the `helper_rules.lead_burst_usd`
24
+ // seed, which MIGRATIONS[100] owns for an existing brain. Same reasoning as the two above —
25
+ // a brand-new database runs `baseline + stamp` and never replays a migration body, and
26
+ // brain/schema.js's createTables() is frozen (lint/file-size.config.json frozenFunctions), so
27
+ // this list is the one vehicle both paths run for the new tables. Idempotent
28
+ // (IF NOT EXISTS / INSERT OR IGNORE), so re-running on every boot changes nothing.
29
+ { label: 'helper-rules-schema', run: (d) => require('./helper-rules-schema').ensureHelperRulesSchema(d) },
30
+ // Lead-cost routing round 2 WP-2 (§6.4 S-15): migration 101's additive columns — `worker_id`,
31
+ // `facts_json`, `label_version`, their index and the `helper_rules.relabelled_to` watermark.
32
+ // Runs immediately after the entry above for the same reason (a fresh database is
33
+ // `baseline + stamp` and never replays a migration body), and the ordering is load-bearing:
34
+ // the ALTERs need the table migration 100's text just created. Idempotent.
35
+ { label: 'helper-rules-schema-v2', run: (d) => require('./helper-rules-schema').ensureHelperRulesSchemaV2(d) },
36
+ // clock-free r4 R-4 (WP-2): migration 57 owns this heal for the ladder, but a
37
+ // migration body runs only once — a CTM v2 purge that drops
38
+ // conversation_context_snapshots.messages_json AFTER 57 already ran was never
39
+ // repaired, and every run-attempt write (_appendRunAttempt) then threw "table
40
+ // conversation_context_snapshots has no column named messages_json". Running
41
+ // the same body on every boot closes that hole. Idempotent (column probe), so
42
+ // re-running on every boot changes nothing. Same lazy-require reasoning as the
43
+ // entries above.
44
+ { label: 'context-snapshot-messages-json', run: (d) => require('./migrations').ensureContextSnapshotMessagesJson(d) },
23
45
  ];
24
46
  }
25
47
  module.exports = { brainSchemaEnsures: createSchemaEnsuresModule };
@@ -1912,7 +1912,7 @@ function initDb(dbPath) {
1912
1912
  installSqliteWriteLock(newDb, dbPath, {
1913
1913
  label: 'wall-e-brain', purpose: 'wall-e-brain-primary', // R3 identity (WP-0)
1914
1914
  holdBudgetMs: WRITE_LOCK_HOLD_BUDGET_MS, // WP-7b Item 4: bound the PRIMARY brain lock's holder
1915
- timeoutMs: _sqliteWriteLockTimeoutMs(), staleMs: Number(process.env.WALL_E_SQLITE_WRITE_LOCK_STALE_MS || 10 * 60 * 1000),
1915
+ timeoutMs: _sqliteWriteLockTimeoutMs(),
1916
1916
  onEvent: _onWriteLockEvent,
1917
1917
  onAcquire: () => _assertSchemaUnchanged(newDb, isBrainOwnerWorker), // D-3: mid-life schema-moved guard, owner-role only
1918
1918
  incidentLog: { record: (rec) => require('./lib/incident-log').getIncidentLog().record(rec) }, // R3: lazy sink, shared/ never requires this
@@ -369,7 +369,7 @@ function _execCapture(bin, args, { timeoutMs = 5000 } = {}) { // timeoutMs: prod
369
369
  }
370
370
  }
371
371
 
372
- async function _probeBrowserHeadless() {
372
+ async function _probeBrowserHeadless(probeOptions) { // probeOptions.timeoutMs: the seam inspectChromeCapability hands to _autoChromeUsable; its production default (15000) is unchanged and no production caller passes this
373
373
  // eslint-disable-next-line global-require
374
374
  const { inspectChromeCapability } = require('../tools/local-tools');
375
375
  // D-3: inspectChromeCapability() is async (a concurrent work package is
@@ -379,7 +379,7 @@ async function _probeBrowserHeadless() {
379
379
  // works" without one — omitting it silently turns `capability` into a
380
380
  // Promise, `capability.detected` into undefined, and every host gets
381
381
  // misreported as binary_missing. Do not "simplify" this back to a bare call.
382
- const capability = await inspectChromeCapability();
382
+ const capability = await inspectChromeCapability({ probeOptions });
383
383
  const detectedCount = (capability.detected || []).length;
384
384
  const evidence = [`chrome candidates on disk: ${detectedCount}`];
385
385
  if (capability.executable) {
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ // One chooser for the coding-journal write path: an explicit dispatcher wins,
4
+ // then the daemon's shared brain-owner client, then the bounded one-shot child
5
+ // this process owns. Choosing a route resolves no brain DB path, starts no
6
+ // child and creates no brain — those stay inside the later persistence
7
+ // callback. The explicit dispatcher option is captured once, so a caller's
8
+ // accessor property is read exactly once.
9
+ function selectJournalDispatcher(options = {}, { createJournalWriteDispatcher, defaultBrainDbPath }) {
10
+ const explicitDispatcher = options.dispatcher;
11
+ if (explicitDispatcher) return Object.freeze({
12
+ dispatcher: explicitDispatcher, defaultAwaitTerminalJournal: true,
13
+ });
14
+ const { getSharedBrainOwnerClient } = require('../lib/brain-owner-worker-client');
15
+ const shared = getSharedBrainOwnerClient();
16
+ if (shared) return Object.freeze({
17
+ dispatcher: createJournalWriteDispatcher({ client: shared }),
18
+ defaultAwaitTerminalJournal: false,
19
+ });
20
+ // Stop hooks and standalone lifecycle processes cannot see the daemon's
21
+ // process-local singleton. The bounded one-shot child owns SQLite and keeps
22
+ // this process free from better-sqlite3 teardown hazards.
23
+ const { writeJournalEntryInChild } = require('../lib/coding-journal-writer-child-client');
24
+ const dispatcher = createJournalWriteDispatcher({
25
+ client: {
26
+ persistCodingJournal(entry) {
27
+ const dbPath = options.dbPath || defaultBrainDbPath(options.env);
28
+ // J-1: no daemon is actually serving a brain (no explicit dbPath, no
29
+ // WALL_E_DATA_DIR, no live-data instance-id.json) — never spawn the
30
+ // one-shot child, which holds an owner grant and would create/migrate
31
+ // the phantom ~/.walle/data brain. Write nothing; skip loudly.
32
+ if (!dbPath) {
33
+ console.error('[wall-e] coding journal write skipped: reason=no_served_brain (no daemon-served brain to write to)');
34
+ return Promise.resolve({ ok: true, skipped: true, reason: 'no_served_brain' });
35
+ }
36
+ return writeJournalEntryInChild(entry, {
37
+ dbPath,
38
+ // S-8: state THIS process's schema generation so the fresh-code child
39
+ // refuses rather than migrating the brain out from under us.
40
+ parentSchemaVersion: require('../brain').SCHEMA_VERSION,
41
+ timeoutMs: options.timeoutMs,
42
+ forkImpl: options.forkImpl,
43
+ env: options.env,
44
+ });
45
+ },
46
+ },
47
+ });
48
+ return Object.freeze({ dispatcher, defaultAwaitTerminalJournal: true });
49
+ }
50
+
51
+ module.exports = { selectJournalDispatcher };