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
@@ -15,6 +15,7 @@
15
15
 
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
+ const { writeStartupPointerGuarded, readStartupTaskProjectDir } = require('./lib/startup-pointer-write');
18
19
  const crypto = require('crypto');
19
20
  const claudeDesktopSessions = require('./lib/claude-desktop-sessions');
20
21
  const { agentRowGhostVerdict } = require('./lib/session-identity-guard');
@@ -909,13 +910,8 @@ function recoverMismatches(db, issues, getAllSessionFiles, opts = {}) {
909
910
  if (!issue.sessionId || !agentId || !resumeId) { result.skipped++; break; }
910
911
  try {
911
912
  const projectDir = issue.details?.jsonl_path ? path.dirname(issue.details.jsonl_path) : '';
912
- const cols = new Set(db.prepare('PRAGMA table_info(startup_tasks)').all().map(c => c.name));
913
- const sets = ['args = ?'];
914
- const vals = [JSON.stringify(['resume', resumeId])];
915
- if (cols.has('agent_project_dir')) { sets.push('agent_project_dir = ?'); vals.push(projectDir || null); }
916
- if (cols.has('claude_project_dir')) { sets.push('claude_project_dir = ?'); vals.push(projectDir || null); }
917
- vals.push(issue.sessionId);
918
- const fixed = db.prepare(`UPDATE startup_tasks SET ${sets.join(', ')} WHERE ctm_session_id = ?`).run(...vals).changes || 0;
913
+ // WP-3: guarded writer (lib/startup-pointer-write.js) — an active transition's candidate fork is refused.
914
+ const fixed = writeStartupPointerGuarded(db, issue.sessionId, ['resume', resumeId], projectDir);
919
915
  let removedSelfRows = 0;
920
916
  if (issue.details?.has_empty_self_row) {
921
917
  removedSelfRows = db.prepare(`
@@ -968,8 +964,10 @@ function recoverMismatches(db, issues, getAllSessionFiles, opts = {}) {
968
964
  return arg;
969
965
  });
970
966
  if (changed) {
971
- fixed += db.prepare('UPDATE startup_tasks SET args = ? WHERE ctm_session_id = ?')
972
- .run(JSON.stringify(healedArgs), issue.sessionId).changes || 0;
967
+ // WP-3: guarded writer; the stored dir columns are handed back so the alias heal never clears them.
968
+ fixed += writeStartupPointerGuarded(
969
+ db, issue.sessionId, healedArgs, readStartupTaskProjectDir(db, issue.sessionId),
970
+ );
973
971
  }
974
972
  }
975
973
  }
@@ -48,6 +48,11 @@ const {
48
48
 
49
49
  const db = require('../db');
50
50
  const walleTranscript = require('../lib/walle-transcript');
51
+ // WP-3 (worker-transcript-link-derived-not-discovered-2026-10-25.html §5 D-5): the boot backfill
52
+ // predicts a transcript path from (cwd, agent_session_id) and VERIFIES it before linking
53
+ // (lib/transcript-link.js) -- the same derived-not-discovered pair WP-2's resume preflight uses.
54
+ const { deriveClaudeTranscriptPath } = require('../lib/resume-cwd');
55
+ const { verifyClaudeTranscriptAt } = require('../lib/transcript-link');
51
56
  // WP-0a: the coding-session model-owner batch body (see the store's module doc). Required
52
57
  // here (not lazily) because this worker is the only caller, and the store has no db.js edge.
53
58
  const { persistModelSelectionBatch } = require('../lib/storage/coding-session-model-store');
@@ -673,7 +678,6 @@ async function _runOp(op, payload = {}, task = null) {
673
678
  return { heldMs: durationMs };
674
679
  }
675
680
  case 'runBootRepairs': {
676
- const titleRepair = db.repairSessionTitlesFromStartupTasks?.({ limit: 2000 }) || null;
677
681
  const rolloutIdentityRepair = db.repairCodexRolloutAgentIdentities?.() || null;
678
682
  const metadataRepair = db.repairCodexSessionMetadataFromConversations?.() || null;
679
683
  const walleIdentity = require('../lib/migrations/dc-walle-identity-rekey');
@@ -685,7 +689,7 @@ async function _runOp(op, payload = {}, task = null) {
685
689
  coreDb: db.getDomainDb('core'),
686
690
  });
687
691
  return {
688
- titleRepair, rolloutIdentityRepair, metadataRepair,
692
+ rolloutIdentityRepair, metadataRepair,
689
693
  walleIdentityRekey, walleIdentityAudit,
690
694
  };
691
695
  }
@@ -998,12 +1002,15 @@ async function _runOp(op, payload = {}, task = null) {
998
1002
  // agent_sessions is CORE; this op runs on the base worker, so route the write to the
999
1003
  // core handle (pre-flip/flag-off === getDb(); post-flip === core.db). The SQLite write
1000
1004
  // lock serializes it against the core worker's own agent_sessions writes.
1005
+ // jsonl_path is an optional guarded link (WP-1/D-7): an empty payload value never blanks
1006
+ // an existing link, so a caller that only carries file metadata cannot unlink the row.
1001
1007
  const result = db.getDomainDb('core').prepare(
1002
- "UPDATE agent_sessions SET file_size = ?, compact_bak_size = ?, modified_at = ?, updated_at = datetime('now') WHERE agent_session_id = ?",
1008
+ "UPDATE agent_sessions SET file_size = ?, compact_bak_size = ?, modified_at = ?, jsonl_path = COALESCE(NULLIF(?, ''), jsonl_path), updated_at = datetime('now') WHERE agent_session_id = ?",
1003
1009
  ).run(
1004
1010
  Number(payload.fileSize || 0),
1005
1011
  Number(payload.compactBakSize || 0),
1006
1012
  String(payload.modifiedAt || ''),
1013
+ String(payload.jsonlPath || ''),
1007
1014
  sessionId,
1008
1015
  );
1009
1016
  return { updated: result.changes > 0 };
@@ -1163,8 +1170,8 @@ async function _runOp(op, payload = {}, task = null) {
1163
1170
  case 'flushSessionDiagnostics': {
1164
1171
  // Durable session-diagnostics batch write (INSERT + per-session retention sweep, one txn).
1165
1172
  // Runs on THIS worker — the designated serial writer — so the cross-process SQLite
1166
- // write-lock claim (_writeLockClaim's sync writeFileSync/linkSync) never blocks the main
1167
- // event loop under multi-session contention.
1173
+ // write lock (and any wait for it) never blocks the main event loop under
1174
+ // multi-session contention.
1168
1175
  const entries = Array.isArray(payload.entries) ? payload.entries : [];
1169
1176
  if (!entries.length) return { inserted: 0, sessionsSwept: 0 };
1170
1177
  return db.flushSessionDiagnostics(entries);
@@ -1278,10 +1285,10 @@ async function _runOp(op, payload = {}, task = null) {
1278
1285
  case 'persistModelSelections': {
1279
1286
  // The batch body lives in the model-owner store (WP-0a corrective pass); its two helper
1280
1287
  // modules are required here and injected, so the store never requires them itself.
1281
- const { isGenericCodingSessionLabel, shouldApplyCodexAutoTitle } = require('../lib/coding-agent-models');
1288
+ const { isGenericCodingSessionLabel } = require('../lib/coding-agent-models');
1282
1289
  const { applyProviderTitle, canWriteTier3Title } = require('../lib/session-title-mirror');
1283
1290
  return persistModelSelectionBatch(db, payload, {
1284
- isGenericCodingSessionLabel, shouldApplyCodexAutoTitle, applyProviderTitle, canWriteTier3Title,
1291
+ isGenericCodingSessionLabel, applyProviderTitle, canWriteTier3Title,
1285
1292
  });
1286
1293
  }
1287
1294
  case 'bindCodingSessionModelState':
@@ -1481,6 +1488,45 @@ async function _runOp(op, payload = {}, task = null) {
1481
1488
  tabs,
1482
1489
  });
1483
1490
  }
1491
+ case 'linkDerivedClaudeTranscripts': {
1492
+ // WP-3 / D-5: bounded, idempotent. agent_sessions is CORE -> flip-aware core handle (see
1493
+ // the updateAgentSessionFileMetadata comment above). `jsonl_path = ''` is the selector, so a
1494
+ // row this op has already linked (or any other writer has) is never re-selected: the pass is
1495
+ // idempotent by construction, not by a marker. A row is linked only when the derived path
1496
+ // resolves to a plain local file whose first record names this very session
1497
+ // (lib/transcript-link.js), so a wrong prediction is counted, never written.
1498
+ const limit = Math.min(500, Math.max(1, Number(payload.limit) || 500));
1499
+ const homeDir = String(payload.homeDir || '');
1500
+ const claudeConfigDir = String(payload.claudeConfigDir || '');
1501
+ const startedAt = Date.now();
1502
+ const rows = db.getDomainDb('core').prepare(
1503
+ "SELECT agent_session_id, project_path FROM agent_sessions WHERE provider = 'claude' AND COALESCE(jsonl_path, '') = '' AND COALESCE(project_path, '') <> '' ORDER BY created_at DESC LIMIT ?",
1504
+ ).all(limit);
1505
+ const out = { checked: rows.length, linked: 0, missing: 0, mismatched: 0, unreadable: 0, ms: 0 };
1506
+ for (const row of rows) {
1507
+ _assertTaskLive(task); // cooperative cancellation: never outlive the caller's deadline
1508
+ let derived;
1509
+ try {
1510
+ derived = deriveClaudeTranscriptPath({
1511
+ cwd: row.project_path,
1512
+ agentSessionId: row.agent_session_id,
1513
+ homeDir: homeDir || undefined,
1514
+ claudeConfigDir,
1515
+ });
1516
+ } catch {
1517
+ out.missing += 1;
1518
+ continue; // expected: an unusable project_path/session id counts as "no transcript there"
1519
+ }
1520
+ const verified = await verifyClaudeTranscriptAt(derived, row.agent_session_id);
1521
+ if (verified.ok) {
1522
+ out.linked += db.updateAgentSessionTranscriptLink(row.agent_session_id, derived).updated ? 1 : 0;
1523
+ } else if (verified.state === 'id-mismatch') out.mismatched += 1;
1524
+ else if (verified.state === 'unreadable' || verified.state === 'archived') out.unreadable += 1;
1525
+ else out.missing += 1;
1526
+ }
1527
+ out.ms = Date.now() - startedAt;
1528
+ return out;
1529
+ }
1484
1530
  case 'recoverSessionIntegrity': {
1485
1531
  // Apply ONLY the recovery WRITES for issues the off-worker (read-pool) detect already
1486
1532
  // found, so the heavy full-corpus detection scan never runs on this single serial
@@ -21,6 +21,7 @@ require('../../shared/network/external-network-guard').armExternalNetworkGuardFr
21
21
  const { parentPort, workerData } = require('node:worker_threads');
22
22
  const fs = require('node:fs'), path = require('node:path');
23
23
  const transcriptSource = require('../lib/transcript-source');
24
+ const { readAgentTitleBackfillPage } = require('../lib/agent-title-backfill-read');
24
25
  const { serializeCleanupError } = require('../lib/worker-error-metadata');
25
26
  const { createErrorSink } = require('../lib/error-sink');
26
27
  const {
@@ -149,13 +150,13 @@ function _recycleReadonlyDbIfNeeded(now = Date.now()) {
149
150
  const expired = !!_roDb
150
151
  && (now - _roDbOpenedAt > _roConnMaxAgeMs || _roDbOpsServed >= _roConnMaxOps);
151
152
  if (!forced && !expired) return false;
152
- const recycled = !!_roDb;
153
- if (_roDb) _closeReadonlyDb();
153
+ // Forced or expired (expired implies an open handle): close and certify a reader gap (R3 D-1b).
154
+ _closeReadonlyDb();
154
155
  _roRecycleRequested = false;
155
156
  for (const requestId of _roRecycleRequestIds.splice(0)) {
156
- try { parentPort?.postMessage({ type: 'recycle-conn-complete', requestId, recycled }); } catch {}
157
+ try { parentPort?.postMessage({ type: 'recycle-conn-complete', requestId, recycled: true }); } catch {}
157
158
  }
158
- return recycled;
159
+ return true;
159
160
  }
160
161
 
161
162
  function _requestReadonlyRecycle(requestId = 0) {
@@ -1751,7 +1752,7 @@ async function _runOp(op, payload = {}, task = null) {
1751
1752
  const conn = _readonlyRoutedDb(payload.dbPath);
1752
1753
  const rowsForCatalog = require('../lib/all-sessions-query').queryAllSessionsData(conn);
1753
1754
  observeRecentCatalogBacking(rowsForCatalog, { sourceExists });
1754
- input.sessions = require('../lib/recent-catalog-query').mapRecentSessionRows(rowsForCatalog, { hostname: payload.hostname, liveSessionIds: new Set(payload.liveSessionIds || []) });
1755
+ input.sessions = require('../lib/recent-catalog-query').mapRecentSessionRows(rowsForCatalog, { hostname: payload.hostname, liveSessionIds: new Set(payload.liveSessionIds || []), machineTags: payload.machineTags });
1755
1756
  const rows = conn.prepare("SELECT key, value FROM settings WHERE key IN ('ui_pinned_sessions', 'last_active_project')").all();
1756
1757
  for (const row of rows) {
1757
1758
  let value = null;
@@ -2712,13 +2713,21 @@ async function _runOp(op, payload = {}, task = null) {
2712
2713
  // The retired-store branch was already unreachable once agent_sessions existed. Keeping this
2713
2714
  // query on current metadata tables also prevents an obsolete-schema error path.
2714
2715
  const conn = _readonlyRoutedDb(payload.dbPath);
2716
+ // D-7 (WP-3): the tier-3 input filter classifies a candidate's first_message with
2717
+ // shared/conversation-provenance.js, which reads the row's provider. Project it, but only
2718
+ // when the schema carries it — same "include the column only when present" guard as
2719
+ // startupTasksScan below, so a minimal/legacy agent_sessions never throws "no such column".
2720
+ // ctm_sessions.provider mirrors the same value, so agent_sessions is the only probe needed.
2721
+ const agentCols = new Set(conn.prepare('PRAGMA table_info(agent_sessions)').all().map((c) => c.name));
2722
+ const providerExpr = agentCols.has('provider') ? "COALESCE(NULLIF(a.provider, ''), 'claude')" : "'claude'";
2715
2723
  const rows = conn.prepare(`
2716
- SELECT session_id, first_message, user_msg_count
2724
+ SELECT session_id, first_message, user_msg_count, provider
2717
2725
  FROM (
2718
2726
  SELECT
2719
2727
  COALESCE(NULLIF(a.ctm_session_id, ''), a.agent_session_id) AS session_id,
2720
2728
  a.first_message,
2721
2729
  COALESCE(a.user_msg_count, 0) AS user_msg_count,
2730
+ ${providerExpr} AS provider,
2722
2731
  COALESCE(NULLIF(a.modified_at, ''), a.updated_at, a.created_at) AS sort_ts,
2723
2732
  ROW_NUMBER() OVER (
2724
2733
  PARTITION BY COALESCE(NULLIF(a.ctm_session_id, ''), a.agent_session_id)
@@ -2741,6 +2750,8 @@ async function _runOp(op, payload = {}, task = null) {
2741
2750
  return _transferJson({ rows });
2742
2751
  }
2743
2752
 
2753
+ case 'backfillAgentTitles': return _transferJson(readAgentTitleBackfillPage(_readonlyRoutedDb(payload.dbPath), payload)); // WP-6: <=64 KiB tail per row
2754
+
2744
2755
  case 'sessionSearchStatus': {
2745
2756
  const conn = _readonlyRoutedDb(payload.dbPath);
2746
2757
  let runtimeAvailable = false;
@@ -207,10 +207,15 @@ function createSessionState(sessionId, o) {
207
207
  };
208
208
  state.mirrorFeed = createHostedMirrorFeedCoalescer({
209
209
  cmd: () => state.cmd || '',
210
- deliver: (data) => {
210
+ // WP-4 (clock-free-tests-followups-r4-2026-10-24.html §5 R-9, §6.5): same `receivedAt` ->
211
+ // `hostReceivedAt` relay as the dedicated host (workers/session-host-process.js) -- this op
212
+ // never leaves this process, so the stamp rides the 'typing-stage' message instead.
213
+ deliver: (data, meta) => {
211
214
  if (!data) return;
212
215
  if (process.env.CTM_DEBUG_MIRROR_FEED) console.error(`[mirror-feed-debug] deliver sid=${sessionId.slice(0, 8)} bytes=${data.length}`);
213
- Promise.resolve(headless.handleMessage({ type: 'feed', sessionId, data }))
216
+ Promise.resolve(headless.handleMessage({
217
+ type: 'feed', sessionId, data, hostReceivedAt: meta && meta.receivedAt,
218
+ }))
214
219
  .then(() => { if (process.env.CTM_DEBUG_MIRROR_FEED) console.error(`[mirror-feed-debug] fed-ok sid=${sessionId.slice(0, 8)} bytes=${data.length}`); })
215
220
  .catch((err) => hostError(sessionId, err, { code: 'headless_feed_failed' }));
216
221
  },
@@ -516,7 +521,7 @@ async function handleHello(msg, ctx) {
516
521
  // WP-3: protocolVersion is echoed unconditionally -- the dialer (manager or
517
522
  // probeHostAlive) is what decides whether it matches; this worker doesn't
518
523
  // validate the hello's own protocolVersion.
519
- ctx.reply({ type: 'state', recoveryShutdownVersion: 1, protocolVersion: PROTOCOL_VERSION, floorVersion: FLOOR_VERSION, closureHash: HOST_CLOSURE.closureHash, closureFiles: HOST_CLOSURE.closureFiles, sessions: buildSessionsState() });
524
+ ctx.reply({ type: 'state', recoveryShutdownVersion: 1, protocolVersion: PROTOCOL_VERSION, floorVersion: FLOOR_VERSION, closureHash: HOST_CLOSURE.closureHash, closureFiles: HOST_CLOSURE.closureFiles, sessions: buildSessionsState(), threadpool: _threadpoolSnapshotForState() });
520
525
  if (role === CONTROL_CONNECTION_ROLE.CONTROLLER) probeHold.release(); // probe: held frames go out AFTER `state` (the dial ignores earlier frames)
521
526
  if (role === CONTROL_CONNECTION_ROLE.PROBE) return;
522
527
  for (const [sessionId, state] of Array.from(sessions)) {
@@ -675,12 +680,19 @@ function shutdownOnSignal() {
675
680
  }
676
681
  process.on('SIGTERM', shutdownOnSignal);
677
682
  process.on('SIGHUP', shutdownOnSignal);
683
+ // D-6 (docs/nightly-followups-r3-pty-selector-flakes-2026-10-22.html §5): process.exit(1) in the
684
+ // same turn as a spawn closes the masters with node-pty's helpers still pre-exec, which parks them
685
+ // in XNU's carrier wait and strands them as ppid-1 orphans (census B, Table 2). SIGHUP every open
686
+ // PTY group synchronously first -- the exit code and the log line are unchanged, and for an
687
+ // already-exec'd child a SIGHUP before exit has the same effect as the kernel hang-up at exit (P-2).
678
688
  process.on('uncaughtException', (err) => {
679
689
  hostError('', err, { code: 'uncaught_exception' });
690
+ for (const state of sessions.values()) { if (!state.ptyClosed) signalPtyGroup(state, 'SIGHUP'); }
680
691
  process.exit(1);
681
692
  });
682
693
  process.on('unhandledRejection', (err) => {
683
694
  hostError('', err, { code: 'unhandled_rejection' });
695
+ for (const state of sessions.values()) { if (!state.ptyClosed) signalPtyGroup(state, 'SIGHUP'); }
684
696
  process.exit(1);
685
697
  });
686
698
 
@@ -735,10 +747,12 @@ if (!CONTROL_SOCKET_PATH) send({ type: 'ready', pid: process.pid, closureHash: H
735
747
  // Host self-truth watchdog (D-4): a worker_thread reads a SharedArrayBuffer stamped before every headless-term op, so a wedged main
736
748
  // thread stays observable (lib/host-self-truth-watchdog.js). Deferred to setImmediate so new Worker() cannot delay the socket chmod.
737
749
  setImmediate(() => startHostSelfTruthWatchdog({ role: 'session-host', hostId: `pool-${process.pid}` }));
738
-
750
+ function _threadpoolSnapshotForState() { try { const s = _threadpoolMonitor.snapshot(); return s ? { state: s.state, oldestPendingMs: Number(s.oldestPendingMs) || 0 } : null; } catch { return null; } /* expected: a state reply must never fail on the probe (WP-3 D-4/S-9) */ }
739
751
  // Dead libuv threadpool = a named, REPORT-ONLY fact within 10s (lib/host-threadpool-monitor.js).
740
752
  const _threadpoolMonitor = createHostThreadpoolMonitor({
741
753
  rootDir: getDataDir(),
754
+ // WP-0 (D-6): every frame names the sessions with an op in flight (5 oldest).
755
+ extraSnapshot: () => ({ sessionOps: headless.oldestInFlightOps(5) }),
742
756
  onFrame: (frame) => send({ type: 'host-threadpool', pid: process.pid, ...frame }),
743
757
  });
744
758
  _threadpoolMonitor.start();
@@ -123,10 +123,15 @@ const headless = bindHeadlessTerminalService({
123
123
  const mirrorFeed = createHostedMirrorFeedCoalescer({
124
124
  cmd: () => sessionCmd,
125
125
  protectIncompleteOnFlush: MIRROR_CONTROL_FLUSH_GUARD_ENABLED,
126
- deliver: (data) => {
126
+ // WP-4 (clock-free-tests-followups-r4-2026-10-24.html §5 R-9, §6.5): carry the coalescer's own
127
+ // receipt time (first byte of a held batch, now() for the immediate path) on the op, so
128
+ // lib/headless-term-service.js can stamp 'host-pty-data' at it. An older caller that passes no
129
+ // meta simply yields `undefined`, and the service then stamps nothing.
130
+ deliver: (data, meta) => {
127
131
  if (!data) return;
128
- Promise.resolve(headless.handleMessage({ type: 'feed', sessionId, data }))
129
- .catch((err) => hostError(err, { code: 'headless_feed_failed' }));
132
+ Promise.resolve(headless.handleMessage({
133
+ type: 'feed', sessionId, data, hostReceivedAt: meta && meta.receivedAt,
134
+ })).catch((err) => hostError(err, { code: 'headless_feed_failed' }));
130
135
  },
131
136
  });
132
137
 
@@ -566,12 +571,18 @@ function shutdownOnSignal() {
566
571
  }
567
572
  process.on('SIGTERM', shutdownOnSignal);
568
573
  process.on('SIGHUP', shutdownOnSignal);
574
+ // D-6 (docs/nightly-followups-r3-pty-selector-flakes-2026-10-22.html §5): process.exit(1) in the
575
+ // same turn as a spawn closes the master with node-pty's helper still pre-exec, parking it in
576
+ // XNU's carrier wait as a ppid-1 orphan. SIGHUP the open PTY group synchronously first; the exit
577
+ // code and the log line are unchanged (for an exec'd child this matches the kernel hang-up at exit).
569
578
  process.on('uncaughtException', (err) => {
570
579
  hostError(err, { code: 'uncaught_exception' });
580
+ if (!ptyClosed) signalPtyGroup('SIGHUP');
571
581
  process.exit(1);
572
582
  });
573
583
  process.on('unhandledRejection', (err) => {
574
584
  hostError(err, { code: 'unhandled_rejection' });
585
+ if (!ptyClosed) signalPtyGroup('SIGHUP');
575
586
  process.exit(1);
576
587
  });
577
588
 
@@ -42,7 +42,7 @@ async function runOp(message) {
42
42
  const input = message.input || {};
43
43
  const opts = { ...input, store: createStore(message.requestId) };
44
44
  switch (message.op) {
45
- case 'archive': return archiver.runArchiveCycle(opts); // WP-9: archive, purge, scrub
45
+ case 'archive': return archiver.runArchiveCycle(opts); // WP-1/WP-9: archive, recover, scrub
46
46
  case 'digest': return archiver.runDigestCycle(opts); // D-24: digest idle files whose size changed, no compression
47
47
  case 'restore': return archiver.restoreUnit(input, opts); // D-20: archived / trashed members back as plain files
48
48
  default:
@@ -10,6 +10,12 @@ const { createErrorSink } = require('../lib/error-sink');
10
10
  const { scanReviewRoots } = require('../lib/review-root-scan');
11
11
  const { lsTree } = require('../lib/review-tree-scan')(gitUtils.git);
12
12
  const untrackedDiffer = require('../lib/review-untracked-diff').createReviewUntrackedDiff({ git: gitUtils.git, parseDiff: gitUtils.parseDiff });
13
+ // WP-2 (docs/remote-hub-followups-credential-projects-rows-idempotency-2026-10-23.html §6): the ONE
14
+ // lossless project-entry decoder (lib/decode-project-entry.js — backtracking existence search, then
15
+ // the transcript "cwd" fallback; memoized) and the gate-scratch predicate. Both are pure/DB-free,
16
+ // which is why this worker (never db.js) can load them.
17
+ const { decodeClaudeProjectEntry } = require('../lib/decode-project-entry');
18
+ const { isGateScratchPath } = require('../lib/review-repo-admission');
13
19
 
14
20
  const errorSink = createErrorSink({ name: 'worker:worktree' });
15
21
  let activeTask = null;
@@ -104,6 +110,34 @@ async function runOp(op, payload = {}, context = {}) {
104
110
  return { observation: await observeWorktree(cwd, payload.integrationBaseRef) };
105
111
  case 'listWorktrees':
106
112
  return { worktrees: await gitUtils.listWorktrees(cwd) };
113
+ case 'gitRemoteKeys': {
114
+ // Remote machine hub WP-4 (§6.5): normalized origin key per /api/projects path ('' for a
115
+ // non-repo), so a hub can match the same repo across machines. Cached in git-utils.
116
+ const rows = (Array.isArray(payload.paths) ? payload.paths : []).map(p => ({ path: p }));
117
+ await gitUtils.attachGitRemoteKeys(rows);
118
+ return { keys: Object.fromEntries(rows.map(row => [row.path, row.gitRemoteKey || ''])) };
119
+ }
120
+ case 'projectRows': {
121
+ // Remote hub follow-ups WP-2 (D-5..D-8, §9 S-8/S-9): the /api/projects rows for
122
+ // `$HOME/.claude/projects`. Claude encodes EVERY non-alphanumeric character of a project's
123
+ // cwd as '-', so the old main-thread `entry.replace(/-/g,'/')` named a path that does not
124
+ // exist for 529 of the lead's 540 entries (214 ms cold for 540 entries — hence this op).
125
+ // Per entry, in input order: decode with the lossless decoder, keep the row only when the
126
+ // decoded path really is a directory and is not one of the gate's own scratch checkouts,
127
+ // then attach the same normalized origin key the gitRemoteKeys op uses. The entry (not the
128
+ // decoded path) stays the row id, so the client's project identity is unchanged.
129
+ const rows = [];
130
+ for (const entry of Array.isArray(payload.entries) ? payload.entries : []) {
131
+ const projectPath = decodeClaudeProjectEntry(entry);
132
+ let isDirectory = false;
133
+ try { isDirectory = fs.statSync(projectPath).isDirectory(); }
134
+ catch { /* expected: a decoded path that is gone (or that never existed) is simply not listed */ }
135
+ if (!isDirectory || isGateScratchPath(projectPath)) continue;
136
+ rows.push({ id: entry, path: projectPath, exists: true });
137
+ }
138
+ await gitUtils.attachGitRemoteKeys(rows);
139
+ return { rows: rows.map(row => ({ id: row.id, path: row.path, exists: true, gitRemoteKey: row.gitRemoteKey || '' })) };
140
+ }
107
141
  case 'scanRoots':
108
142
  // Review Hub root feed (WP-3, task-4-brief.md D-2b): fs-only depth-limited walk, no
109
143
  // git at all — a candidate is any directory containing a `.git` entry (file or dir,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "walle",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "walle",
9
- "version": "0.10.0",
9
+ "version": "0.10.2",
10
10
  "hasInstallScript": true,
11
11
  "dependencies": {
12
12
  "pg": "^8.22.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "walle",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "private": true,
5
5
  "description": "Wall-E — your personal digital twin",
6
6
  "walleRuntime": {
@@ -25,6 +25,7 @@ const {
25
25
  classifyRetainedConversationRow,
26
26
  retainedHistorySqlPredicate: _retainedHistoryRowPredicate,
27
27
  sanitizeInspectionViolations,
28
+ WALLE_BASELINE_EMPTY_COLUMNS,
28
29
  } = require('../shared/conversation-storage-v2-contract');
29
30
  const { classifyUserProvenance } = require('../shared/conversation-provenance');
30
31
  const ctmStore = require('../coding-task-manager/lib/conversation-store-v2');
@@ -973,7 +974,11 @@ function _convertWalleConversationEventPayload(db) {
973
974
  forked_from_turn_id=substr(json_extract(payload_json,'$.forked_from_turn_id'),1,240),
974
975
  permanent=CAST(json_extract(payload_json,'$.permanent') AS INTEGER)
975
976
  WHERE json_valid(payload_json)`);
976
- return _dropColumns(db, 'conversation_events', ['payload_json']);
977
+ // R-2 (clock-free r4 D-1): payload_json is one of the 8 allowed Wall-E baseline
978
+ // columns, so the typed-column conversion above is followed by an in-place
979
+ // sanitize instead of a DROP. The returned change count replaces the old
980
+ // _dropColumns result in the purge report.
981
+ return db.prepare("UPDATE conversation_events SET payload_json='{}' WHERE payload_json <> '{}'").run().changes;
977
982
  }
978
983
 
979
984
  function _retainSafeCtmHistoryRows(db) {
@@ -1091,24 +1096,24 @@ function purgeWalleLegacy(db) {
1091
1096
  }
1092
1097
  if (_tableExists(db, 'chat_messages')) {
1093
1098
  report.chat_messages_deleted = db.prepare("DELETE FROM chat_messages WHERE role NOT IN ('user','assistant') OR length(trim(content))=0").run().changes;
1094
- if (_columns(db, 'chat_messages').includes('attachments')) {
1095
- report.chat_messages_sanitized = db.prepare('UPDATE chat_messages SET attachments=NULL WHERE attachments IS NOT NULL').run().changes;
1096
- }
1097
1099
  }
1098
1100
  if (_tableExists(db, 'runtime_events')) {
1099
1101
  report.runtime_events_deleted = _deleteAll(db, 'runtime_events');
1100
1102
  }
1101
1103
  try { db.exec('DROP INDEX IF EXISTS idx_chat_message_parts_type; DROP INDEX IF EXISTS idx_runtime_events_tool;'); } catch {}
1102
- report.chat_message_columns = _dropColumns(db, 'chat_messages', ['attachments']);
1103
1104
  report.chat_part_columns = _dropColumns(db, 'chat_message_parts', ['tool_name','status','title','summary','data_json','model_projection']);
1104
1105
  report.runtime_event_columns = _dropColumns(db, 'runtime_events', ['message_id','tool_name','summary','payload_json']);
1105
- report.skill_execution_columns = _dropColumns(db, 'skill_executions', ['tool_calls','tool_results','error','decision_reason']);
1106
+ report.skill_execution_columns = _dropColumns(db, 'skill_executions', ['tool_calls','tool_results','error']);
1106
1107
  report.coding_session_columns = _dropColumns(db, 'coding_agent_sessions', ['tool_calls','files_modified','git_diff']);
1107
- report.conversation_turn_columns = _dropColumns(db, 'conversation_turns', ['attachments_json']);
1108
- report.context_snapshot_columns = _dropColumns(db, 'conversation_context_snapshots', ['messages_json','provider_request_ref']);
1109
- report.attempt_columns = _dropColumns(db, 'conversation_attempts', ['error']);
1110
- report.context_generation_columns = _dropColumns(db, 'conversation_context_generations', ['messages_json']);
1111
1108
  report.conversation_event_columns = _convertWalleConversationEventPayload(db);
1109
+ // R-2 (clock-free r4 D-1): the 8 Wall-E baseline columns are sanitized in place, never
1110
+ // dropped -- Wall-E still writes conversation_context_snapshots.messages_json, so a
1111
+ // DROP here breaks every later appendRunAttempt. The generic step replaces the former
1112
+ // inline `UPDATE chat_messages SET attachments=NULL` and the 8 _dropColumns entries;
1113
+ // the other prohibited columns keep their drops above. It runs AFTER
1114
+ // _convertWalleConversationEventPayload so conversation_events.payload_json is still
1115
+ // readable when the typed-column extraction reads it (that call owns emptying it).
1116
+ report.baseline_empty_sanitized = _sanitizeWalleBaselineEmptyColumns(db);
1112
1117
  report.initiative_columns = _dropColumns(db, 'initiative_log', ['state_snapshot','reasoning','decision_data']);
1113
1118
  report.session_columns = _dropColumns(db, 'sessions', ['compacted_messages','metadata']);
1114
1119
  report.shadow_result_columns = _dropColumns(db, 'shadow_results', ['judge_reasoning','error']);
@@ -1175,6 +1180,49 @@ function _prohibitedColumns(kind) {
1175
1180
  };
1176
1181
  }
1177
1182
 
1183
+ // D-1 (clock-free r4): the conversation-storage v2 contract protects CONTENT, not
1184
+ // columns. These 8 columns are Wall-E's own baseline DDL (wall-e/brain/schema.js
1185
+ // createTables(), additive-only) and are legitimately present on every fresh brain.
1186
+ // Their mere presence is not a violation; a non-empty value is
1187
+ // (code 'prohibited_rows'). The empty set lives in the contract module so no
1188
+ // literal is duplicated here. `_quoteIdentifier` guards the identifier; the empty
1189
+ // value is a fixed contract constant, inlined as an SQL literal because
1190
+ // _countWhere/_existsWhere take no bind parameters.
1191
+ function _walleBaselineEmptyColumns() {
1192
+ return Object.entries(WALLE_BASELINE_EMPTY_COLUMNS)
1193
+ .map(([id, { empty }]) => {
1194
+ const separator = id.indexOf('.');
1195
+ return { id, table: id.slice(0, separator), column: id.slice(separator + 1), empty };
1196
+ });
1197
+ }
1198
+
1199
+ function _walleEmptySqlLiteral(empty) {
1200
+ return empty === null ? 'NULL' : `'${String(empty).replace(/'/g, "''")}'`;
1201
+ }
1202
+
1203
+ function _walleBaselineEmptyPredicate(column, empty) {
1204
+ const col = _quoteIdentifier(column);
1205
+ // A NULL value is always "empty"; a NOT NULL column (three of the 8) still
1206
+ // takes its literal empty value, which is what the equality branch matches.
1207
+ if (empty === null) return `${col} IS NOT NULL`;
1208
+ return `NOT (${col} IS NULL OR ${col} = ${_walleEmptySqlLiteral(empty)})`;
1209
+ }
1210
+
1211
+ // R-2 (clock-free r4): sanitize the 8 in place instead of dropping them, so a
1212
+ // Wall-E writer naming one of these columns (observed: conversation_context_snapshots.messages_json,
1213
+ // wall-e/brain/conversation.js:315-318) keeps working after the purge.
1214
+ function _sanitizeWalleBaselineEmptyColumns(db) {
1215
+ const report = {};
1216
+ for (const { id, table, column, empty } of _walleBaselineEmptyColumns()) {
1217
+ if (!_columns(db, table).includes(column)) continue;
1218
+ const where = _walleBaselineEmptyPredicate(column, empty);
1219
+ report[id] = db.prepare(
1220
+ `UPDATE ${_quoteIdentifier(table)} SET ${_quoteIdentifier(column)} = ${_walleEmptySqlLiteral(empty)} WHERE ${where}`,
1221
+ ).run().changes;
1222
+ }
1223
+ return report;
1224
+ }
1225
+
1178
1226
  function _countWhere(db, table, where = '1=1') {
1179
1227
  if (!_tableExists(db, table)) return 0;
1180
1228
  try { return Number(db.prepare(`SELECT COUNT(*) AS n FROM ${_quoteIdentifier(table)} WHERE ${where}`).get().n); }
@@ -1195,9 +1243,15 @@ function inspectDatabaseAbsence(db, {
1195
1243
  allowRetainedCtmHistoryRows = false, countViolations = false,
1196
1244
  } = {}) {
1197
1245
  const prohibitedColumns = [];
1246
+ // D-1 (clock-free r4): the 8 Wall-E baseline columns are allowed to be present
1247
+ // while empty, so they are skipped here and content-checked below instead.
1248
+ const baselineEmptyIds = kind === 'walle' ? new Set(Object.keys(WALLE_BASELINE_EMPTY_COLUMNS)) : null;
1198
1249
  for (const [table, names] of Object.entries(_prohibitedColumns(kind))) {
1199
1250
  const present = new Set(_columns(db, table));
1200
- for (const column of names) if (present.has(column)) prohibitedColumns.push({ table, column });
1251
+ for (const column of names) {
1252
+ if (baselineEmptyIds?.has(`${table}.${column}`)) continue;
1253
+ if (present.has(column)) prohibitedColumns.push({ table, column });
1254
+ }
1201
1255
  }
1202
1256
  const existingLegacyTables = kind === 'ctm'
1203
1257
  ? CTM_LEGACY_CONVERSATION_TABLES.filter((table) => _tableExists(db, table))
@@ -1270,6 +1324,19 @@ function inspectDatabaseAbsence(db, {
1270
1324
  addRows('chat_messages', "role NOT IN ('user','assistant') OR length(trim(content))=0");
1271
1325
  if (_tableExists(db, 'memories')) addRows('memories',
1272
1326
  "source IN ('claude-code-jsonl','codex-jsonl','gemini-jsonl','opencode-jsonl','walle-jsonl') OR memory_type LIKE 'coding_session_%'");
1327
+ // D-1 (clock-free r4): each allowed baseline column still fails closed on
1328
+ // CONTENT -- a row whose value is neither NULL nor the column's empty value
1329
+ // is a prohibited_rows violation keyed by `<table>.<column>`.
1330
+ for (const { id, table, column, empty } of _walleBaselineEmptyColumns()) {
1331
+ if (!_columns(db, table).includes(column)) continue;
1332
+ const where = _walleBaselineEmptyPredicate(column, empty);
1333
+ if (countViolations) {
1334
+ const count = _countWhere(db, table, where);
1335
+ if (count > 0) prohibitedRows.push({ table: id, count });
1336
+ } else if (_existsWhere(db, table, where)) {
1337
+ prohibitedRows.push({ table: id, count: 1 });
1338
+ }
1339
+ }
1273
1340
  }
1274
1341
  const violations = sanitizeInspectionViolations([
1275
1342
  ...prohibitedColumns.map(({ table, column }) => ({
@@ -171,8 +171,19 @@ const INSPECTION_CODES = Object.freeze(new Set([
171
171
  'prohibited_tables',
172
172
  'retained_history_guard_missing',
173
173
  'retained_history_dirty',
174
+ 'inspection_failed',
174
175
  ]));
175
176
 
177
+ // REPORT (clock-free r6 OQ-5): an inspection that throws still plans a rerun, but the
178
+ // boot log names the error class -- the handful of SQLite driver codes and errno-style
179
+ // `E*` names -- instead of an empty violation list. Anything else is 'unknown_error'.
180
+ const INSPECTION_ERROR_CLASS_RE = /^(?:SQLITE_[A-Z_]{1,40}|E[A-Z]{2,20})$/;
181
+
182
+ function inspectionErrorClass(error) {
183
+ const code = error && typeof error === 'object' ? error.code : undefined;
184
+ return typeof code === 'string' && INSPECTION_ERROR_CLASS_RE.test(code) ? code : 'unknown_error';
185
+ }
186
+
176
187
  // The walle prohibited-table and table.column ids below are copied verbatim from
177
188
  // _prohibitedColumns('walle') in scripts/conversation-storage-v2-migration.js so that a
178
189
  // walle inspection violation is nameable in the boot log instead of collapsing to
@@ -230,6 +241,21 @@ const WALLE_PROHIBITED_COLUMN_IDS = Object.freeze([
230
241
  'eval_diagnostics.tool_sequence',
231
242
  ]);
232
243
 
244
+ // The 8 inherited Wall-E baseline columns (wall-e/brain/schema.js createTables(), additive-only
245
+ // ruling wall-e/brain/migrations.js:1741-1745). Allowed while every value is empty; any other
246
+ // value is a prohibited_rows violation (user ruling 2026-10-24, clock-free r4 D-1).
247
+ const WALLE_BASELINE_EMPTY_COLUMNS = Object.freeze({
248
+ 'chat_messages.attachments': Object.freeze({ empty: null }),
249
+ 'skill_executions.decision_reason': Object.freeze({ empty: null }),
250
+ 'conversation_turns.attachments_json': Object.freeze({ empty: '[]' }),
251
+ 'conversation_context_snapshots.messages_json': Object.freeze({ empty: '[]' }),
252
+ 'conversation_context_snapshots.provider_request_ref': Object.freeze({ empty: null }),
253
+ 'conversation_attempts.error': Object.freeze({ empty: null }),
254
+ 'conversation_events.payload_json': Object.freeze({ empty: '{}' }),
255
+ 'conversation_context_generations.messages_json': Object.freeze({ empty: '[]' }),
256
+ });
257
+ // exported next to WALLE_PROHIBITED_COLUMN_IDS; every key MUST also be in that list.
258
+
233
259
  const FIXED_INSPECTION_OBJECT_IDS = Object.freeze(new Set([
234
260
  ...CTM_KNOWN_CONVERSATION_TABLES,
235
261
  ...CTM_RETAINED_HISTORY_TABLES,
@@ -346,12 +372,18 @@ function sanitizeInspectionViolations(violations) {
346
372
  for (const violation of violations) {
347
373
  if (!violation || typeof violation !== 'object' || Array.isArray(violation)) continue;
348
374
  if (typeof violation.code !== 'string' || !INSPECTION_CODES.has(violation.code)) continue;
349
- const objectId = typeof violation.objectId === 'string'
350
- && (FIXED_INSPECTION_OBJECT_IDS.has(violation.objectId)
351
- || (violation.code === 'unknown_conversation_table_rows'
352
- && UNKNOWN_TABLE_OBJECT_ID_RE.test(violation.objectId)))
353
- ? violation.objectId
354
- : 'unknown_schema_object';
375
+ const objectId = violation.code === 'inspection_failed'
376
+ ? (typeof violation.objectId === 'string'
377
+ && (violation.objectId === 'unknown_error'
378
+ || INSPECTION_ERROR_CLASS_RE.test(violation.objectId))
379
+ ? violation.objectId
380
+ : 'unknown_error')
381
+ : (typeof violation.objectId === 'string'
382
+ && (FIXED_INSPECTION_OBJECT_IDS.has(violation.objectId)
383
+ || (violation.code === 'unknown_conversation_table_rows'
384
+ && UNKNOWN_TABLE_OBJECT_ID_RE.test(violation.objectId)))
385
+ ? violation.objectId
386
+ : 'unknown_schema_object');
355
387
  const count = Number(violation.count);
356
388
  if (!Number.isFinite(count) || count <= 0) continue;
357
389
  const key = `${violation.code}\0${objectId}`;
@@ -380,7 +412,11 @@ module.exports = {
380
412
  CTM_CONTENT_BEARING_TABLES,
381
413
  CTM_KNOWN_CONVERSATION_TABLES,
382
414
  CONVERSATION_TABLE_NAME_RE,
415
+ WALLE_PROHIBITED_TABLES,
416
+ WALLE_PROHIBITED_COLUMN_IDS,
417
+ WALLE_BASELINE_EMPTY_COLUMNS,
383
418
  classifyRetainedConversationRow,
384
419
  retainedHistorySqlPredicate,
385
420
  sanitizeInspectionViolations,
421
+ inspectionErrorClass,
386
422
  };