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
@@ -0,0 +1,392 @@
1
+ {
2
+ "schema_version": 1,
3
+ "note": "lead-cost routing round 2 §6.3 (WP-0). The shared contract between CTM (producer: coding-task-manager/tests-integration/api-sessions-worker-payload-shape.test.js) and Wall-E (consumer: the helper-rules hook). The producer asserts each variant's keys are a SUBSET of the live response for that call shape; the consumer drives the hook with each variant verbatim. Every path is a /tmp fixture path, never a real machine path. `ctm_session_id` rides only on the whole-set wait's nested `worker` and its `workers[]` summary rows -- the plain status-by-id body has no such key, which is why the producer omits it for the status shapes.",
4
+ "variants": {
5
+ "status_by_id": {
6
+ "status": "report_ready",
7
+ "report_path": "/tmp/ctm-fixture/worker-reports/w-1.md",
8
+ "report_present": true,
9
+ "report_source": "report",
10
+ "report_text": "# Report\n\nEvery file:line below is a distinct citation.\n\n| where | what |\n| --- | --- |\n| `src/alpha.js:12` | the first |\n| src/beta.ts:340 | the second |\n| (lib/gamma.mjs:7) | the third |\n",
11
+ "report_text_truncated": false,
12
+ "report_citation_rows": 13,
13
+ "report_chars": 25567,
14
+ "report_provisional": false,
15
+ "last_message_path": "/tmp/ctm-fixture/worker-reports/w-1.last.md",
16
+ "turns": 42,
17
+ "last_activity_at": 1761350400000,
18
+ "exitCode": null,
19
+ "generation": 1,
20
+ "request_budget": {
21
+ "requestCount": 29,
22
+ "knownOutputTokens": 48000,
23
+ "terminalCode": "",
24
+ "incomplete_streams": 0,
25
+ "incomplete_stream_ms": 0
26
+ },
27
+ "context_tokens": 120000,
28
+ "peak_context_tokens": 140000,
29
+ "compactions": [],
30
+ "tool_counts": { "Read": 23, "Grep": 18, "Bash": 8, "Write": 1 },
31
+ "write_tool_calls": 1,
32
+ "read_tool_calls": 41,
33
+ "bash_calls": 8,
34
+ "harness": "claude-code",
35
+ "worker_profile": "review",
36
+ "original_brief_sha256": "1111111111111111111111111111111111111111111111111111111111111111",
37
+ "rendered_brief_sha256": "2222222222222222222222222222222222222222222222222222222222222222",
38
+ "rendered_brief_path": "/tmp/ctm-fixture/worker-reports/w-1.brief.md",
39
+ "final_result": null,
40
+ "progress": null,
41
+ "advisories": [],
42
+ "receipt_state": null,
43
+ "event_receipts": [],
44
+ "provider_thread_id": "thread-w-1",
45
+ "agent_consumed": true,
46
+ "identity": "marked",
47
+ "failure": null,
48
+ "terminal_path": "/tmp/ctm-fixture/worker-reports/w-1.terminal.json",
49
+ "terminal_present": true,
50
+ "spend": { "usd": 0.054, "priced_requests": 29, "unpriced_requests": 0, "pricing_basis": "model_registry" },
51
+ "cli_reported_cost_usd": 0.42,
52
+ "task_requests_so_far": 29,
53
+ "task_output_tokens_so_far": 48000,
54
+ "lease": {
55
+ "current": 1,
56
+ "ceiling": 4,
57
+ "renewals": [],
58
+ "scope": "task",
59
+ "base_requests": 324,
60
+ "base_output_tokens": 100000,
61
+ "task_requests": 29,
62
+ "task_output_tokens": 48000,
63
+ "headroom_requests": 295,
64
+ "headroom_output_tokens": 52000,
65
+ "resume_accepted": true
66
+ },
67
+ "lead_posture": { "source": "ctm_launch", "sandbox": "none", "network": "on", "cwd": "/tmp/ctm-fixture" },
68
+ "generations": [{ "generation": 1, "unit": "turn", "turns": 42, "terminal_code": "", "lease": 1 }],
69
+ "parent_ctm_session_id": "lead-1",
70
+ "retention": {
71
+ "terminal_at": "2026-10-24T00:00:00.000Z",
72
+ "consumed_at": "",
73
+ "acknowledged_at": "",
74
+ "hide_at": "",
75
+ "retain_ms": 14400000
76
+ },
77
+ "owned_paths": ["/tmp/ctm-fixture/**"],
78
+ "request_budget_diagnostic": "worker_budget_proxy_unavailable",
79
+ "denied_tool_calls": [],
80
+ "scope_violations": [],
81
+ "poll_after_ms": 0
82
+ },
83
+ "wait_by_id": {
84
+ "status": "report_ready",
85
+ "report_path": "/tmp/ctm-fixture/worker-reports/w-1.md",
86
+ "report_present": true,
87
+ "report_source": "report",
88
+ "report_citation_rows": 13,
89
+ "report_chars": 25567,
90
+ "report_provisional": false,
91
+ "last_message_path": "/tmp/ctm-fixture/worker-reports/w-1.last.md",
92
+ "turns": 42,
93
+ "last_activity_at": 1761350400000,
94
+ "exitCode": null,
95
+ "generation": 1,
96
+ "request_budget": {
97
+ "requestCount": 29,
98
+ "knownOutputTokens": 48000,
99
+ "terminalCode": "",
100
+ "incomplete_streams": 0,
101
+ "incomplete_stream_ms": 0
102
+ },
103
+ "context_tokens": 120000,
104
+ "peak_context_tokens": 140000,
105
+ "compactions": [],
106
+ "tool_counts": { "Read": 23, "Grep": 18, "Bash": 8, "Write": 1 },
107
+ "write_tool_calls": 1,
108
+ "read_tool_calls": 41,
109
+ "bash_calls": 8,
110
+ "harness": "claude-code",
111
+ "worker_profile": "review",
112
+ "original_brief_sha256": "1111111111111111111111111111111111111111111111111111111111111111",
113
+ "rendered_brief_sha256": "2222222222222222222222222222222222222222222222222222222222222222",
114
+ "rendered_brief_path": "/tmp/ctm-fixture/worker-reports/w-1.brief.md",
115
+ "progress": null,
116
+ "advisories": [],
117
+ "receipt_state": null,
118
+ "event_receipts": [],
119
+ "provider_thread_id": "thread-w-1",
120
+ "agent_consumed": true,
121
+ "identity": "marked",
122
+ "failure": null,
123
+ "terminal_path": "/tmp/ctm-fixture/worker-reports/w-1.terminal.json",
124
+ "terminal_present": true,
125
+ "spend": { "usd": 0.054, "priced_requests": 29, "unpriced_requests": 0, "pricing_basis": "model_registry" },
126
+ "cli_reported_cost_usd": 0.42,
127
+ "task_requests_so_far": 29,
128
+ "task_output_tokens_so_far": 48000,
129
+ "lease": {
130
+ "current": 1,
131
+ "ceiling": 4,
132
+ "renewals": [],
133
+ "scope": "task",
134
+ "base_requests": 324,
135
+ "base_output_tokens": 100000,
136
+ "task_requests": 29,
137
+ "task_output_tokens": 48000,
138
+ "headroom_requests": 295,
139
+ "headroom_output_tokens": 52000,
140
+ "resume_accepted": true
141
+ },
142
+ "lead_posture": { "source": "ctm_launch", "sandbox": "none", "network": "on", "cwd": "/tmp/ctm-fixture" },
143
+ "generations": [{ "generation": 1, "unit": "turn", "turns": 42, "terminal_code": "", "lease": 1 }],
144
+ "parent_ctm_session_id": "lead-1",
145
+ "retention": {
146
+ "terminal_at": "2026-10-24T00:00:00.000Z",
147
+ "consumed_at": "",
148
+ "acknowledged_at": "",
149
+ "hide_at": "",
150
+ "retain_ms": 14400000
151
+ },
152
+ "owned_paths": ["/tmp/ctm-fixture/**"],
153
+ "request_budget_diagnostic": "worker_budget_proxy_unavailable",
154
+ "denied_tool_calls": [],
155
+ "scope_violations": [],
156
+ "poll_after_ms": 0,
157
+ "woke_on": "terminal",
158
+ "waited_ms": 1200,
159
+ "watcher_reason": null,
160
+ "watcher_decisions": [],
161
+ "lead_next_step": null
162
+ },
163
+ "wait_no_id": {
164
+ "woke_on": "terminal",
165
+ "waited_ms": 139896,
166
+ "watcher_reason": null,
167
+ "watcher_decisions": [],
168
+ "worker": {
169
+ "status": "report_ready",
170
+ "report_path": "/tmp/ctm-fixture/worker-reports/w-1.md",
171
+ "report_present": true,
172
+ "report_source": "report",
173
+ "report_citation_rows": 13,
174
+ "report_chars": 25567,
175
+ "report_provisional": false,
176
+ "last_message_path": "/tmp/ctm-fixture/worker-reports/w-1.last.md",
177
+ "turns": 42,
178
+ "last_activity_at": 1761350400000,
179
+ "exitCode": null,
180
+ "generation": 1,
181
+ "request_budget": {
182
+ "requestCount": 29,
183
+ "knownOutputTokens": 48000,
184
+ "terminalCode": "",
185
+ "incomplete_streams": 0,
186
+ "incomplete_stream_ms": 0
187
+ },
188
+ "context_tokens": 120000,
189
+ "peak_context_tokens": 140000,
190
+ "compactions": [],
191
+ "tool_counts": { "Read": 23, "Grep": 18, "Bash": 8, "Write": 1 },
192
+ "write_tool_calls": 1,
193
+ "read_tool_calls": 41,
194
+ "bash_calls": 8,
195
+ "harness": "claude-code",
196
+ "worker_profile": "review",
197
+ "original_brief_sha256": "1111111111111111111111111111111111111111111111111111111111111111",
198
+ "rendered_brief_sha256": "2222222222222222222222222222222222222222222222222222222222222222",
199
+ "rendered_brief_path": "/tmp/ctm-fixture/worker-reports/w-1.brief.md",
200
+ "progress": null,
201
+ "advisories": [],
202
+ "receipt_state": null,
203
+ "event_receipts": [],
204
+ "provider_thread_id": "thread-w-1",
205
+ "agent_consumed": true,
206
+ "identity": "marked",
207
+ "failure": null,
208
+ "terminal_path": "/tmp/ctm-fixture/worker-reports/w-1.terminal.json",
209
+ "terminal_present": true,
210
+ "spend": { "usd": 0.054, "priced_requests": 29, "unpriced_requests": 0, "pricing_basis": "model_registry" },
211
+ "cli_reported_cost_usd": 0.42,
212
+ "task_requests_so_far": 29,
213
+ "task_output_tokens_so_far": 48000,
214
+ "lease": {
215
+ "current": 1,
216
+ "ceiling": 4,
217
+ "renewals": [],
218
+ "scope": "task",
219
+ "base_requests": 324,
220
+ "base_output_tokens": 100000,
221
+ "task_requests": 29,
222
+ "task_output_tokens": 48000,
223
+ "headroom_requests": 295,
224
+ "headroom_output_tokens": 52000,
225
+ "resume_accepted": true
226
+ },
227
+ "lead_posture": { "source": "ctm_launch", "sandbox": "none", "network": "on", "cwd": "/tmp/ctm-fixture" },
228
+ "generations": [{ "generation": 1, "unit": "turn", "turns": 42, "terminal_code": "", "lease": 1 }],
229
+ "parent_ctm_session_id": "lead-1",
230
+ "retention": {
231
+ "terminal_at": "2026-10-24T00:00:00.000Z",
232
+ "consumed_at": "",
233
+ "acknowledged_at": "",
234
+ "hide_at": "",
235
+ "retain_ms": 14400000
236
+ },
237
+ "owned_paths": ["/tmp/ctm-fixture/**"],
238
+ "request_budget_diagnostic": "worker_budget_proxy_unavailable",
239
+ "denied_tool_calls": [],
240
+ "scope_violations": [],
241
+ "poll_after_ms": 0,
242
+ "ctm_session_id": "w-1"
243
+ },
244
+ "workers": [{ "ctm_session_id": "w-1", "status": "report_ready", "generation": 1, "terminal": true }],
245
+ "lead_next_step": null
246
+ },
247
+ "wait_no_id_timeout": {
248
+ "woke_on": "timeout",
249
+ "waited_ms": 240003,
250
+ "watcher_reason": null,
251
+ "watcher_decisions": [],
252
+ "worker": null,
253
+ "workers": [{ "ctm_session_id": "w-1", "status": "working", "generation": 1, "terminal": false }],
254
+ "lead_next_step": null
255
+ },
256
+ "wait_by_route": {
257
+ "woke_on": "route_clear",
258
+ "waited_ms": 0,
259
+ "route": "r-1",
260
+ "admission": {
261
+ "recent_throttles": 0,
262
+ "throttled_until": "2026-10-24T00:05:00.000Z",
263
+ "quota_held": true,
264
+ "reason": "quota_held",
265
+ "refusals": 0,
266
+ "last_refusal_at": null
267
+ },
268
+ "lead_next_step": null
269
+ },
270
+ "codex_exited": {
271
+ "status": "exited",
272
+ "report_path": "/tmp/ctm-fixture/worker-reports/w-2.md",
273
+ "report_present": false,
274
+ "report_source": "last_message",
275
+ "report_citation_rows": 21,
276
+ "report_chars": 8946,
277
+ "report_provisional": false,
278
+ "last_message_path": "/tmp/ctm-fixture/worker-reports/w-2.last.md",
279
+ "turns": 17,
280
+ "last_activity_at": 1761350500000,
281
+ "exitCode": 0,
282
+ "generation": 1,
283
+ "request_budget": null,
284
+ "context_tokens": null,
285
+ "peak_context_tokens": null,
286
+ "compactions": [],
287
+ "tool_counts": null,
288
+ "write_tool_calls": 0,
289
+ "read_tool_calls": null,
290
+ "bash_calls": null,
291
+ "harness": "codex-cli",
292
+ "worker_profile": "implementation",
293
+ "original_brief_sha256": "3333333333333333333333333333333333333333333333333333333333333333",
294
+ "rendered_brief_sha256": "4444444444444444444444444444444444444444444444444444444444444444",
295
+ "rendered_brief_path": "/tmp/ctm-fixture/worker-reports/w-2.brief.md",
296
+ "progress": null,
297
+ "advisories": [],
298
+ "receipt_state": null,
299
+ "event_receipts": [],
300
+ "provider_thread_id": "thread-w-2",
301
+ "agent_consumed": true,
302
+ "identity": "marked",
303
+ "failure": null,
304
+ "terminal_path": "/tmp/ctm-fixture/worker-reports/w-2.terminal.json",
305
+ "terminal_present": true,
306
+ "spend": null,
307
+ "cli_reported_cost_usd": null,
308
+ "task_requests_so_far": null,
309
+ "task_output_tokens_so_far": null,
310
+ "lease": null,
311
+ "lead_posture": null,
312
+ "generations": [{ "generation": 1, "unit": "request", "turns": null, "terminal_code": "", "lease": null }],
313
+ "parent_ctm_session_id": "lead-1",
314
+ "retention": {
315
+ "terminal_at": "2026-10-24T00:01:00.000Z",
316
+ "consumed_at": "",
317
+ "acknowledged_at": "",
318
+ "hide_at": "",
319
+ "retain_ms": 14400000
320
+ },
321
+ "owned_paths": [],
322
+ "request_budget_diagnostic": "worker_budget_proxy_unavailable",
323
+ "denied_tool_calls": [],
324
+ "scope_violations": [],
325
+ "poll_after_ms": 0,
326
+ "ctm_session_id": "w-2"
327
+ },
328
+ "failed": {
329
+ "status": "failed",
330
+ "report_path": "/tmp/ctm-fixture/worker-reports/w-3.md",
331
+ "report_present": false,
332
+ "report_source": null,
333
+ "report_citation_rows": 0,
334
+ "report_chars": 0,
335
+ "report_provisional": false,
336
+ "last_message_path": "/tmp/ctm-fixture/worker-reports/w-3.last.md",
337
+ "turns": 3,
338
+ "last_activity_at": 1761350600000,
339
+ "exitCode": 1,
340
+ "generation": 1,
341
+ "request_budget": null,
342
+ "context_tokens": null,
343
+ "peak_context_tokens": null,
344
+ "compactions": [],
345
+ "tool_counts": null,
346
+ "write_tool_calls": 0,
347
+ "read_tool_calls": null,
348
+ "bash_calls": null,
349
+ "harness": "claude-code",
350
+ "worker_profile": "implementation",
351
+ "original_brief_sha256": "5555555555555555555555555555555555555555555555555555555555555555",
352
+ "rendered_brief_sha256": "6666666666666666666666666666666666666666666666666666666666666666",
353
+ "rendered_brief_path": "/tmp/ctm-fixture/worker-reports/w-3.brief.md",
354
+ "progress": null,
355
+ "advisories": [],
356
+ "receipt_state": null,
357
+ "event_receipts": [],
358
+ "provider_thread_id": "",
359
+ "agent_consumed": true,
360
+ "identity": "marked",
361
+ "failure": {
362
+ "class": "terminal",
363
+ "code": "error_during_execution",
364
+ "resumable": false,
365
+ "next_step": "Relaunching will not help; report the failure and its message to the user."
366
+ },
367
+ "terminal_path": "/tmp/ctm-fixture/worker-reports/w-3.terminal.json",
368
+ "terminal_present": true,
369
+ "spend": null,
370
+ "cli_reported_cost_usd": null,
371
+ "task_requests_so_far": null,
372
+ "task_output_tokens_so_far": null,
373
+ "lease": null,
374
+ "lead_posture": null,
375
+ "generations": [{ "generation": 1, "unit": "turn", "turns": 3, "terminal_code": "error_during_execution", "lease": null }],
376
+ "parent_ctm_session_id": "lead-1",
377
+ "retention": {
378
+ "terminal_at": "2026-10-24T00:02:00.000Z",
379
+ "consumed_at": "",
380
+ "acknowledged_at": "",
381
+ "hide_at": "",
382
+ "retain_ms": 14400000
383
+ },
384
+ "owned_paths": [],
385
+ "request_budget_diagnostic": "worker_budget_proxy_unavailable",
386
+ "denied_tool_calls": [],
387
+ "scope_violations": [],
388
+ "poll_after_ms": 0,
389
+ "ctm_session_id": "w-3"
390
+ }
391
+ }
392
+ }
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ // WP-4 (docs/remote-machine-hub-sessions-2026-10-22.html §6.5, §9 S-21, prior-art P5):
4
+ // the ONE canonical git-remote key shared by both ends of the hub/box pair.
5
+ //
6
+ // Why a key at all (AC-4b/AC-4c): the hub must decide "is this laptop project the SAME repo as
7
+ // that box checkout?" without asking either machine. Paths never match (different homes, different
8
+ // clone roots) but the origin remote does, once every spelling of one repo collapses to one
9
+ // string. The picker (AC-4c) and the workspace_bindings.git_remote column (S-21) both key on it.
10
+ //
11
+ // The normal form is `<host>/<path>` — lowercase host, path case PRESERVED (GitHub is
12
+ // case-insensitive on host but a self-hosted Gitea/GHE install is not guaranteed to be on path):
13
+ //
14
+ // https://example.com/Owner/Repo.git -> example.com/Owner/Repo
15
+ // git@example.com:Owner/Repo.git -> example.com/Owner/Repo (scp form)
16
+ // ssh://git@example.com:22/Owner/Repo.git -> example.com/Owner/Repo (scheme + port)
17
+ // https://token@example.com:443/Owner/Repo/ -> example.com/Owner/Repo (userinfo + slash)
18
+ // git@example.org:team/sub/Repo.git -> example.org/team/sub/Repo (deeper path kept)
19
+ //
20
+ // Anything that is not clearly a remote (empty, a local path, prose, a bare host with no path)
21
+ // normalizes to '' — callers treat '' as "no key", never as a key that might accidentally match
22
+ // another keyless row.
23
+
24
+ /**
25
+ * Normalize one git remote URL to the canonical `host/path` key.
26
+ * Pure, dependency-free, and total: never throws, returns '' for anything unusable.
27
+ *
28
+ * @param {unknown} url
29
+ * @returns {string} '' when the input is not a recognisable remote
30
+ */
31
+ function normalizeGitRemote(url) {
32
+ if (url == null) return '';
33
+ let raw;
34
+ try {
35
+ raw = String(url).trim();
36
+ } catch {
37
+ return ''; // a null-prototype object / a hostile toString — not a remote, not our problem
38
+ }
39
+ if (!raw || /\s/.test(raw)) return '';
40
+
41
+ let host = '';
42
+ let pathPart = '';
43
+
44
+ const scheme = raw.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):\/\/(.*)$/);
45
+ if (scheme) {
46
+ // [user[:pass]@]host[:port]/path — userinfo may itself contain '@', so split on the LAST one.
47
+ let rest = scheme[2];
48
+ const at = rest.lastIndexOf('@');
49
+ if (at >= 0) rest = rest.slice(at + 1);
50
+ const slash = rest.indexOf('/');
51
+ if (slash <= 0) return '';
52
+ const authority = rest.slice(0, slash);
53
+ pathPart = rest.slice(slash + 1);
54
+ // Bracketed IPv6 keeps its colons; a bare host:port loses only the port.
55
+ host = authority.startsWith('[')
56
+ ? authority.slice(0, authority.indexOf(']') + 1)
57
+ : authority.replace(/:\d+$/, '');
58
+ } else {
59
+ // scp-like `[user@]host:path` — the only other remote shape git accepts. A local path
60
+ // (`/x/y`), a bare word, a Windows drive (`C:\x\y`, the only reason paths skip backslashes)
61
+ // or prose has no matching host:path pair.
62
+ const scp = raw.match(/^(?:[^/@\\\s]+@)?([^/:\s@\\]+):([^\s\\]+)$/);
63
+ if (!scp) return '';
64
+ host = scp[1];
65
+ pathPart = scp[2];
66
+ }
67
+
68
+ host = host.trim().toLowerCase();
69
+ if (!host) return '';
70
+
71
+ // Trailing slashes are stripped TWICE because `.git/` is a spelling of `.git`: strip the
72
+ // separator, then the suffix it was hiding.
73
+ const cleaned = pathPart
74
+ .replace(/^\/+/, '')
75
+ .replace(/\/+$/, '')
76
+ .replace(/\.git$/i, '')
77
+ .replace(/\/+$/, '');
78
+ if (!cleaned) return '';
79
+
80
+ return `${host}/${cleaned}`;
81
+ }
82
+
83
+ module.exports = { normalizeGitRemote };
@@ -18,6 +18,37 @@ function computeHostFingerprint({ hostname, port, dbPath } = {}) {
18
18
  .slice(0, 24);
19
19
  }
20
20
 
21
+ // WP-B D-B1 (docs/remote-hub-fences-followups-r2-clone-truth-and-repair-2026-10-23.html §5/§6):
22
+ // the host's OS machine id, hashed. macOS reads IOPlatformUUID from ioreg (~21 ms, measured; the
23
+ // caller memoizes it per process); Linux reads /etc/machine-id, then /var/lib/dbus/machine-id.
24
+ // Any other platform, an empty read, or any failure ⇒ '' so the caller falls back to the
25
+ // fingerprint verdict — today's behavior, never worse. The RAW id never leaves this function
26
+ // (D-B3: machine-id(5) calls it confidential, and equality is all any caller needs); `readFile` /
27
+ // `execFile` are injectable so both readers are unit-tested with no real OS call (AC-Be).
28
+ function readOsMachineIdHash({
29
+ platform = process.platform,
30
+ readFile = fs.readFileSync,
31
+ execFile = require('child_process').execFileSync,
32
+ } = {}) {
33
+ let raw = '';
34
+ try {
35
+ if (platform === 'darwin') {
36
+ const output = execFile('/usr/sbin/ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], { encoding: 'utf8', timeout: 2000 });
37
+ raw = (String(output || '').match(/"IOPlatformUUID" = "([0-9A-Fa-f-]+)"/) || [])[1] || '';
38
+ } else if (platform === 'linux') {
39
+ for (const machineIdPath of ['/etc/machine-id', '/var/lib/dbus/machine-id']) {
40
+ try {
41
+ raw = String(readFile(machineIdPath, 'utf8')).split('\n').map(line => line.trim()).find(Boolean) || '';
42
+ } catch { /* expected: this candidate path is absent on this distro — try the next one */ }
43
+ if (raw) break;
44
+ }
45
+ }
46
+ } catch { /* expected: the platform reader is unavailable (missing binary, timeout, unreadable file) ⇒ no machine id ⇒ fingerprint fallback */ }
47
+ const clean = String(raw || '').trim();
48
+ if (!clean) return '';
49
+ return crypto.createHash('sha256').update(`ctm-machine-id:${clean}`).digest('hex').slice(0, 24);
50
+ }
51
+
21
52
  function defaultInstanceId({ hostname, port } = {}) {
22
53
  const host = _token(hostname || process.env.HOSTNAME || os.hostname() || 'unknown-host', 'unknown-host');
23
54
  const cleanPort = _token(String(port || process.env.CTM_PORT || '3456'), '3456');
@@ -182,9 +213,15 @@ function ensureHostIdentity({
182
213
  // ANY id in the current instance's lineage component is recognized as the same host, while a
183
214
  // truly unrelated instance id (never connected to `currentId` by any lineage row) still fails.
184
215
  //
216
+ // WP-C §5 D-C1 (docs/remote-hub-fences-followups-r2-clone-truth-and-repair-2026-10-23.html): a row
217
+ // with cross_host = 1 was minted ACROSS MACHINES (WP-B), so the id on its far side is another
218
+ // machine's — never this host's own history — and is not traversed unless the caller asks with
219
+ // `{ traverseCrossHost: true }`. cross_host 0 / NULL / absent traverses exactly as before.
220
+ //
185
221
  // Pure: takes the lineage rows as data (caller reads them from instance_lineage) so this stays
186
222
  // unit-testable with no database at all — see tests/local-remote-state-ownership.test.js.
187
- // `lineageRows`: array of {priorInstanceId|prior_instance_id, newInstanceId|new_instance_id}.
223
+ // `lineageRows`: array of {priorInstanceId|prior_instance_id, newInstanceId|new_instance_id,
224
+ // cross_host|crossHost}.
188
225
  // Returns the SET of every instance id transitively reachable from `currentId` (including
189
226
  // `currentId` itself) — the connected component isSameOwnerLineage below tests membership of.
190
227
  // Exposed separately (not just the boolean) so a caller that must stay synchronous and
@@ -192,7 +229,7 @@ function ensureHostIdentity({
192
229
  // WP-3 comment and tests/runtime-db-isolation.test.js's static "no dbModule." scan) can
193
230
  // precompute this ONCE (e.g. at boot, alongside its own instance-id snapshot) and consult a
194
231
  // plain in-memory Set forever after, instead of calling into the DB from the hot path.
195
- function ownerLineageComponent(currentId, lineageRows) {
232
+ function ownerLineageComponent(currentId, lineageRows, { traverseCrossHost = false } = {}) {
196
233
  const current = String(currentId || '').trim();
197
234
  const seen = new Set(current ? [current] : []);
198
235
  if (!current) return seen;
@@ -205,6 +242,7 @@ function ownerLineageComponent(currentId, lineageRows) {
205
242
  adjacency.get(b).add(a);
206
243
  };
207
244
  for (const row of (Array.isArray(lineageRows) ? lineageRows : [])) {
245
+ if (!traverseCrossHost && Number(row?.cross_host ?? row?.crossHost) === 1) continue; // foreign machine's id
208
246
  addEdge(
209
247
  String((row && (row.priorInstanceId ?? row.prior_instance_id)) || '').trim(),
210
248
  String((row && (row.newInstanceId ?? row.new_instance_id)) || '').trim(),
@@ -231,11 +269,69 @@ function isSameOwnerLineage(candidateId, currentId, lineageRows) {
231
269
  return ownerLineageComponent(current, lineageRows).has(candidate);
232
270
  }
233
271
 
272
+ // The port segments a default instance id can carry: `ctm-<host>-<port>` (defaultInstanceId) or
273
+ // `ctm-<host>-<port>-<6-hex suffix>` (mintInstanceId). The host token may itself contain digits
274
+ // and hyphens, so only the trailing forms are read. The two forms are mutually exclusive on the
275
+ // same tail (a 6-char suffix never fits the 1-5 digit bare port), so at most one port is returned;
276
+ // the result is a list so a caller iterates candidates without a null check.
277
+ function instanceIdPorts(instanceId) {
278
+ const id = String(instanceId || '').trim();
279
+ const match = id.match(/-(\d{1,5})-[0-9a-f]{6}$/) || id.match(/-(\d{1,5})$/);
280
+ return match ? [match[1]] : [];
281
+ }
282
+
283
+ // Cross-host verdict for a re-mint edge (CTM WP-2 §5 D-3 / §9 S-5, AC-2c; WP-B D-B2).
284
+ // WP-B: when BOTH sides know their OS machine id — `priorIdentityRow.machine_id` and `liveMachineId`
285
+ // both non-empty — the machine id decides ALONE (`prior === live` ⇒ 0 else 1), regardless of
286
+ // hostname / port / data-dir churn: a hostname rename or a data-dir move on ONE machine is a
287
+ // same-host edge, and a copied DB on a different machine is foreign even if the fingerprints match.
288
+ // Only when either side is unknown (legacy rows written before the column, an unsupported platform)
289
+ // do the head-comment's fingerprint rules below apply, byte-for-byte as before — never worse.
290
+ // * prior row's stored host_fingerprint non-empty and equal to this host's live fingerprint or
291
+ // one of its compatible variants — at this boot's port or at a port the prior instance id
292
+ // names → 0 (proven same host),
293
+ // * prior row's stored host_fingerprint empty → 0 (nothing to compare: FAIL TOWARD TODAY'S
294
+ // BEHAVIOR, i.e. the edge stays claimable — a false "foreign" would strand legitimate rows),
295
+ // * otherwise → 1 (the DB was minted on a different machine).
296
+ // The prior-port recompute exists because computeHostFingerprint hashes hostname:port:dbPath, so
297
+ // a same-host re-mint on a NEW port (restart-ctm onto a different port, the render harness's
298
+ // restartServer portPolicy 'fresh') never matches this boot's set; without it every port move
299
+ // was misclassified as a cross-host clone and its startup rows parked (observed 2026-10-23:
300
+ // render-dev-server-identity RL-1b, 3 rows parked 'foreign-clone' on a plain same-dir restart).
301
+ // `fingerprintsAtPort(port)` returns a { live, compatible } set for this host at that port.
302
+ function lineageCrossHost(priorIdentityRow, fingerprints, fingerprintsAtPort = null, liveMachineId = '') {
303
+ const priorMachineId = String(priorIdentityRow?.machine_id || '').trim();
304
+ const live = String(liveMachineId || '').trim();
305
+ if (priorMachineId && live) return priorMachineId === live ? 0 : 1;
306
+ const stored = String((priorIdentityRow && priorIdentityRow.host_fingerprint) || '').trim();
307
+ if (!stored) return 0;
308
+ const matchesSet = (set) => {
309
+ const live = String((set && set.live) || '').trim();
310
+ if (live && stored === live) return true;
311
+ const compatible = set && set.compatible;
312
+ return Array.isArray(compatible)
313
+ && compatible.some((value) => { const clean = String(value || '').trim(); return clean && clean === stored; });
314
+ };
315
+ if (matchesSet(fingerprints)) return 0;
316
+ if (typeof fingerprintsAtPort === 'function') {
317
+ for (const priorPort of instanceIdPorts(priorIdentityRow && priorIdentityRow.instance_id)) {
318
+ let set = null;
319
+ try { set = fingerprintsAtPort(priorPort); }
320
+ catch { /* expected: a malformed prior id yields no candidate set; the verdict falls through to 1 */ }
321
+ if (matchesSet(set)) return 0;
322
+ }
323
+ }
324
+ return 1;
325
+ }
326
+
234
327
  module.exports = {
328
+ instanceIdPorts,
329
+ lineageCrossHost,
235
330
  computeHostFingerprint,
236
331
  defaultInstanceId,
237
332
  ensureHostIdentity,
238
333
  identityFilePath,
334
+ readOsMachineIdHash,
239
335
  readIdentityFile,
240
336
  writeIdentityFile,
241
337
  isSameOwnerLineage,