@moretea-labs/forge 1.5.1 → 1.7.0

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 (434) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +46 -0
  3. package/CLAUDE.md +2 -0
  4. package/DESIGN.md +7 -2
  5. package/README.md +4 -4
  6. package/README.zh-CN.md +2 -2
  7. package/SKILL.md +2 -2
  8. package/THIRD_PARTY_NOTICES.md +1 -1
  9. package/agents/openai.yaml +1 -1
  10. package/assets/hooks/hook-input.sh +11 -8
  11. package/assets/hooks/lib/session-state.sh +10 -7
  12. package/assets/hooks/lib/workflow-state.sh +13 -177
  13. package/assets/hooks/post-tool-observer.sh +8 -2
  14. package/assets/hooks/pre-edit-guard.sh +8 -39
  15. package/assets/hooks/prompt-guard.sh +7 -83
  16. package/assets/hooks/session-start-context.sh +56 -1
  17. package/assets/partials/05-workflow.partial.md +1 -1
  18. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  19. package/assets/plugin-registry.v1.json +54 -3
  20. package/assets/reference-configs/document-generation.md +1 -1
  21. package/assets/reference-configs/harness-overview.md +1 -1
  22. package/assets/reference-configs/hook-operations.md +1 -1
  23. package/assets/reference-configs/sprint-contracts.md +3 -3
  24. package/assets/skill-version.json +2 -2
  25. package/assets/templates/contract.template.md +2 -7
  26. package/assets/templates/helpers/capture-plan.sh +2 -2
  27. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  28. package/assets/templates/helpers/contract-worktree.sh +1 -21
  29. package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/new-plan.sh +1 -1
  32. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  33. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  34. package/assets/templates/helpers/sprint-backlog.sh +6 -6
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/plan.template.md +1 -1
  38. package/assets/templates/review.template.md +9 -34
  39. package/assets/workflow-contract.v1.json +10 -19
  40. package/docs/README.md +10 -10
  41. package/docs/architecture/CURRENT.md +170 -0
  42. package/docs/architecture/EVOLUTION.md +42 -0
  43. package/docs/architecture/current/README.md +4 -156
  44. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  45. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  46. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  47. package/docs/architecture/history/README.md +7 -0
  48. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  49. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  50. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  51. package/docs/architecture/history.md +3 -34
  52. package/docs/architecture/index.md +15 -98
  53. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  54. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  55. package/docs/architecture/versions/1.6.md +38 -0
  56. package/docs/architecture/versions/1.7.md +41 -0
  57. package/docs/forge-chatgpt-browser-engine.md +2 -2
  58. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  59. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  60. package/docs/forge-github-issue-launcher.md +1 -1
  61. package/docs/forge-runtime-self-healing-loop.md +1 -1
  62. package/docs/operations/controller-browser-plugin.md +38 -22
  63. package/docs/operations/controller-performance-and-502.md +124 -0
  64. package/docs/operations/controller-reliability-runbook.md +1 -1
  65. package/docs/operations/features.md +3 -3
  66. package/docs/operations/github-repository.md +1 -1
  67. package/docs/operations/homebrew.md +2 -2
  68. package/docs/operations/mcp-tool-exposure.md +5 -3
  69. package/docs/tutorials/01-install-and-start.md +1 -1
  70. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Installation.md +1 -1
  74. package/docs/wiki/Integrations.md +2 -2
  75. package/docs/wiki/Quick-Start.md +1 -1
  76. package/docs/wiki/Work-Lifecycle.md +1 -1
  77. package/package.json +29 -10
  78. package/scripts/activate-source-baseline.ts +17 -13
  79. package/scripts/benchmark-background-check-overlap.ts +228 -0
  80. package/scripts/benchmark-mcp-host.sh +117 -0
  81. package/scripts/benchmark-mcp-transport.ts +170 -0
  82. package/scripts/benchmark-quality-harness.ts +146 -0
  83. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  84. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  85. package/scripts/benchmark-typescript-navigation.ts +186 -0
  86. package/scripts/benchmark-work-efficiency.ts +334 -0
  87. package/scripts/capture-plan.sh +3 -3
  88. package/scripts/check-agent-tooling.sh +8 -35
  89. package/scripts/check-architecture-sync.sh +17 -86
  90. package/scripts/check-mcp-compatibility.ts +14 -0
  91. package/scripts/check-public-docs.sh +14 -1
  92. package/scripts/check-runtime-architecture.mjs +107 -64
  93. package/scripts/check-stable-baseline.ts +92 -0
  94. package/scripts/check-task-workflow.sh +4 -4
  95. package/scripts/contract-worktree.sh +1 -21
  96. package/scripts/ensure-task-workflow.sh +22 -55
  97. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  98. package/scripts/install-standalone-recovery.ts +24 -0
  99. package/scripts/lib/project-init-lib.sh +15 -44
  100. package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
  101. package/scripts/migrate-project-template.sh +23 -1179
  102. package/scripts/new-plan.sh +1 -1
  103. package/scripts/plan-to-todo.sh +11 -36
  104. package/scripts/run-governed-gate.ts +2 -1
  105. package/scripts/ship-worktrees.sh +2 -29
  106. package/scripts/smoke-ios-physical-device.ts +4 -2
  107. package/scripts/smoke-runtime-recovery.ts +1 -35
  108. package/scripts/smoke-schedule-engine.ts +1 -20
  109. package/scripts/sprint-backlog.sh +1 -1
  110. package/scripts/verify-contract.sh +1 -111
  111. package/scripts/verify-forge-runtime.sh +0 -1
  112. package/scripts/verify-sprint.sh +3 -180
  113. package/scripts/workflow-contract.ts +1 -5
  114. package/src/cli/commands/chatgpt.ts +31 -2
  115. package/src/cli/commands/init-hook.ts +3 -3
  116. package/src/cli/commands/mcp.ts +9 -5
  117. package/src/cli/commands/plugin.ts +37 -10
  118. package/src/cli/commands/recovery.ts +25 -3
  119. package/src/cli/commands/runtime.ts +55 -3
  120. package/src/cli/commands/security.ts +12 -1
  121. package/src/cli/commands/setup-profile.ts +31 -21
  122. package/src/cli/controller/check-runner.ts +106 -3
  123. package/src/cli/controller/composite-result.ts +1 -1
  124. package/src/cli/controller/context/focus.ts +51 -0
  125. package/src/cli/controller/context/instruction-context.ts +114 -0
  126. package/src/cli/controller/context/known-paths.ts +76 -0
  127. package/src/cli/controller/context/query-planning.ts +110 -0
  128. package/src/cli/controller/context/rule-resolution.ts +532 -0
  129. package/src/cli/controller/context/source-materializer.ts +278 -0
  130. package/src/cli/controller/context/structural.ts +16 -0
  131. package/src/cli/controller/context/types.ts +286 -0
  132. package/src/cli/controller/context-pack.ts +898 -430
  133. package/src/cli/controller/launch-agents.ts +77 -2
  134. package/src/cli/controller/operational-plan.ts +2 -2
  135. package/src/cli/controller/work-mode.ts +6 -4
  136. package/src/cli/editing/edit-session.ts +60 -7
  137. package/src/cli/github/github.ts +133 -0
  138. package/src/cli/hook/prompt-guard-decision.ts +17 -18
  139. package/src/cli/hook/prompt-intents.ts +1 -1
  140. package/src/cli/installer/managed-entries.ts +15 -1
  141. package/src/cli/local-bridge/console-automations.ts +225 -67
  142. package/src/cli/local-bridge/console-view-models.ts +3 -35
  143. package/src/cli/local-bridge/facade-api.ts +2 -217
  144. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  145. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
  146. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  147. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
  148. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
  149. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
  150. package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
  151. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  152. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
  153. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
  154. package/src/cli/local-bridge/frontend/src/styles.css +27 -11
  155. package/src/cli/local-bridge/frontend/src/types.ts +8 -7
  156. package/src/cli/local-bridge/job-store.ts +1 -490
  157. package/src/cli/local-bridge/server.ts +29 -532
  158. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  159. package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
  160. package/src/cli/mcp/auth.ts +5 -5
  161. package/src/cli/mcp/instructions.ts +3 -2
  162. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  163. package/src/cli/mcp/multi-repository.ts +16 -5
  164. package/src/cli/mcp/oauth.ts +175 -26
  165. package/src/cli/mcp/paths.ts +8 -2
  166. package/src/cli/mcp/repository-tools.ts +356 -101
  167. package/src/cli/mcp/server.ts +625 -54
  168. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  169. package/src/cli/mcp/toolset-selection.ts +1 -1
  170. package/src/cli/mcp/toolset.ts +42 -5
  171. package/src/cli/mcp/transports/http.ts +125 -15
  172. package/src/cli/mcp/transports/session-registry.ts +67 -5
  173. package/src/cli/mcp/types.ts +3 -3
  174. package/src/cli/repositories/command-classifier.ts +209 -14
  175. package/src/cli/repositories/command-executor.ts +55 -521
  176. package/src/cli/repositories/command-process.ts +108 -0
  177. package/src/cli/repositories/command-scope.ts +99 -41
  178. package/src/cli/repositories/controller-home.ts +7 -8
  179. package/src/cli/repositories/git-command-executor.ts +2 -0
  180. package/src/cli/repositories/locks.ts +53 -16
  181. package/src/cli/repositories/repository-snapshot.ts +121 -0
  182. package/src/cli/repositories/safe-patch.ts +6 -4
  183. package/src/cli/repositories/selected-path-actions.ts +17 -9
  184. package/src/cli/repositories/structured-git.ts +110 -10
  185. package/src/cli/repository/inspector.ts +565 -62
  186. package/src/cli/repository/session-cache.ts +58 -17
  187. package/src/cli/v81-entry.ts +1 -4
  188. package/src/effects/fs-transaction.ts +85 -17
  189. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  190. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  191. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  192. package/src/runtime/context/lsp-client.ts +283 -0
  193. package/src/runtime/context/semantic-navigation.ts +275 -0
  194. package/src/runtime/context/swift-navigation.ts +565 -0
  195. package/src/runtime/context/typescript-navigation.ts +226 -0
  196. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  197. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
  198. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  199. package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
  200. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  201. package/src/runtime/control-plane/execution/validation.ts +15 -2
  202. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  203. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  204. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  205. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  206. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  207. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
  208. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
  209. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
  210. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  211. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  212. package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
  213. package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
  214. package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
  215. package/src/runtime/control-plane/facade/index.ts +1 -0
  216. package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
  217. package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
  218. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  219. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  220. package/src/runtime/control-plane/facade/types.ts +55 -11
  221. package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
  222. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  223. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  224. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  225. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  226. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  227. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  228. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  229. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  230. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  231. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  232. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  233. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  234. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  235. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  236. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  237. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  238. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  239. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  240. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  241. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
  242. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  243. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  244. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  245. package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
  246. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  247. package/src/runtime/control-plane/release-retention.ts +62 -2
  248. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  249. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  250. package/src/runtime/diagnostics/mcp-timing.ts +19 -0
  251. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  252. package/src/runtime/diagnostics/performance.ts +24 -1
  253. package/src/runtime/evidence/event-ledger.ts +1 -1
  254. package/src/runtime/execution/jobs/store.ts +58 -4
  255. package/src/runtime/execution/jobs/types.ts +1 -1
  256. package/src/runtime/execution/managed-workspace.ts +203 -1
  257. package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
  258. package/src/runtime/execution/process-runtime/check-result.ts +63 -2
  259. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  260. package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
  261. package/src/runtime/execution/process-runtime/gc.ts +98 -12
  262. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  263. package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
  264. package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
  265. package/src/runtime/execution/process-runtime/runtime.ts +59 -4
  266. package/src/runtime/execution/process-runtime/store.ts +7 -0
  267. package/src/runtime/execution/process-runtime/types.ts +2 -0
  268. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  269. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  270. package/src/runtime/execution/workers/executor.ts +0 -48
  271. package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
  272. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  273. package/src/runtime/gateway/mcp/router.ts +185 -83
  274. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  275. package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
  276. package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
  277. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  278. package/src/runtime/plugins/browser-adapter.ts +2925 -318
  279. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  280. package/src/runtime/plugins/browser-handoff-host.ts +6 -31
  281. package/src/runtime/plugins/browser-handoff.ts +92 -5
  282. package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
  283. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  284. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  285. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  286. package/src/runtime/plugins/browser-runtime.ts +183 -0
  287. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  288. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  289. package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
  290. package/src/runtime/plugins/external-adapter.ts +324 -3
  291. package/src/runtime/plugins/external-registration.ts +84 -5
  292. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  293. package/src/runtime/plugins/first-party-registry.ts +4 -2
  294. package/src/runtime/plugins/github-adapter.ts +125 -0
  295. package/src/runtime/plugins/gmail-adapter.ts +28 -2
  296. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  297. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  298. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  299. package/src/runtime/plugins/ios-adapter.ts +106 -16
  300. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  301. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  302. package/src/runtime/plugins/lightweight-action.ts +153 -0
  303. package/src/runtime/plugins/local-system-adapter.ts +125 -8
  304. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  305. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  306. package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
  307. package/src/runtime/plugins/resend-adapter.ts +1 -1
  308. package/src/runtime/plugins/store.ts +366 -24
  309. package/src/runtime/plugins/types.ts +39 -2
  310. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  311. package/src/runtime/projections/git-status-sampler.ts +16 -16
  312. package/src/runtime/projections/materialized-view.ts +28 -10
  313. package/src/runtime/recovery/actions.ts +0 -10
  314. package/src/runtime/recovery/classifier.ts +0 -9
  315. package/src/runtime/recovery/maintenance-executor.ts +415 -18
  316. package/src/runtime/recovery/probe.ts +0 -1
  317. package/src/runtime/recovery/types.ts +0 -1
  318. package/src/runtime/root/mcp-transport.ts +180 -10
  319. package/src/runtime/root/package-connector-service.ts +186 -8
  320. package/src/runtime/root/package-runtime-release.ts +143 -17
  321. package/src/runtime/root/package-runtime-service.ts +430 -22
  322. package/src/runtime/root/release-manifest.ts +7 -2
  323. package/src/runtime/root/release-materialize.ts +162 -19
  324. package/src/runtime/root/release-store.ts +48 -11
  325. package/src/runtime/root/runtime.ts +23 -5
  326. package/src/runtime/root/service.ts +100 -27
  327. package/src/runtime/root/types.ts +2 -0
  328. package/src/runtime/safe-tooling/index.ts +0 -1
  329. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  330. package/src/runtime/safe-tooling/types.ts +0 -30
  331. package/src/runtime/shared/trusted-executable.ts +62 -0
  332. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  333. package/src/runtime/standalone-recovery/core.ts +702 -94
  334. package/src/runtime/standalone-recovery/entry.ts +99 -23
  335. package/src/runtime/standalone-recovery/installer.ts +19 -3
  336. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  337. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  338. package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
  339. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
  340. package/src/runtime/workflow/schedules/engine.ts +509 -80
  341. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  342. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  343. package/src/runtime/workflow/schedules/store.ts +131 -10
  344. package/src/runtime/workflow/schedules/types.ts +6 -3
  345. package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
  346. package/src/testing/test-governance.ts +8 -40
  347. package/assets/reference-configs/heartbeat-triage.md +0 -57
  348. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  349. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  350. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  351. package/bin/forge-desktop-helper.mjs +0 -204
  352. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  353. package/docs/architecture/current/architecture-invariants.md +0 -331
  354. package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
  355. package/docs/architecture/current/codegraph-context-provider.md +0 -226
  356. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  357. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  358. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
  359. package/docs/architecture/current/entity-model.md +0 -592
  360. package/docs/architecture/current/external-plugin-governance.md +0 -396
  361. package/docs/architecture/current/failure-recovery.md +0 -447
  362. package/docs/architecture/current/governance.md +0 -213
  363. package/docs/architecture/current/human-interaction-plane.md +0 -87
  364. package/docs/architecture/current/implementation-status.md +0 -128
  365. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  366. package/docs/architecture/current/legacy-governance.md +0 -58
  367. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  368. package/docs/architecture/current/migration-roadmap.md +0 -105
  369. package/docs/architecture/current/multi-repository-execution.md +0 -371
  370. package/docs/architecture/current/operations-runbook.md +0 -45
  371. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  372. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  373. package/docs/architecture/current/repository-access-modes.md +0 -102
  374. package/docs/architecture/current/route-policy.md +0 -15
  375. package/docs/architecture/current/route-session-concurrency.md +0 -85
  376. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  377. package/docs/architecture/current/runtime-directory-map.md +0 -49
  378. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  379. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  380. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  381. package/docs/architecture/current/system-overview.md +0 -144
  382. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  383. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  384. package/docs/architecture/current/thin-harness-v1.md +0 -311
  385. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  386. package/docs/forge-autonomous-goal-loop.md +0 -9
  387. package/docs/forge-provider-configuration.md +0 -7
  388. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
  389. package/docs/operations/controller-desktop-plugin.md +0 -112
  390. package/docs/operations/gmail-assistant-routines.md +0 -186
  391. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  392. package/docs/operations/provider-configuration.md +0 -31
  393. package/scripts/harness-trace-grade.sh +0 -150
  394. package/scripts/heartbeat-triage.sh +0 -361
  395. package/scripts/maintenance-triage.sh +0 -167
  396. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  397. package/src/cli/repositories/goal-registry.ts +0 -239
  398. package/src/runtime/assistant/action-proposals.ts +0 -279
  399. package/src/runtime/assistant/intent.ts +0 -395
  400. package/src/runtime/assistant/model-provider.ts +0 -460
  401. package/src/runtime/assistant/openapi.ts +0 -223
  402. package/src/runtime/assistant/policy.ts +0 -89
  403. package/src/runtime/assistant/readiness.ts +0 -258
  404. package/src/runtime/assistant/routine-runtime.ts +0 -477
  405. package/src/runtime/assistant/schedule-binding.ts +0 -171
  406. package/src/runtime/assistant/standing-grants.ts +0 -375
  407. package/src/runtime/assistant/store.ts +0 -243
  408. package/src/runtime/assistant/types.ts +0 -135
  409. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  410. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  411. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  412. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  413. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  414. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  415. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  416. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  417. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  418. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  419. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  420. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  421. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  422. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  423. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  424. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  425. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  426. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  427. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  428. package/src/runtime/plugins/desktop-adapter.ts +0 -374
  429. package/src/runtime/safe-tooling/browser-safe.ts +0 -133
  430. package/src/runtime/workflow/findings/store.ts +0 -204
  431. package/src/runtime/workflow/findings/types.ts +0 -58
  432. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  433. package/src/runtime/workflow/portfolio/store.ts +0 -185
  434. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -1,6 +1,7 @@
1
1
  import { createHash } from 'crypto';
2
2
  import { existsSync } from 'fs';
3
3
  import { dirname, resolve } from 'path';
4
+ import { fileURLToPath } from 'url';
4
5
  import {
5
6
  controllerCheckExecutionIdentity,
6
7
  listControllerChecks,
@@ -23,9 +24,18 @@ import {
23
24
  type RunCheckFacadeResult,
24
25
  } from './check-facade';
25
26
  import { claimsForCheck, toProcessClaims } from './resource-claims';
26
- import { spawnManagedProcess } from './runtime';
27
+ import { getProcessHandle, spawnManagedProcess } from './runtime';
28
+ import { getProcessRecord, getProcessRequestBinding } from './store';
27
29
  import { allocatePersistedCheckResultReceiptPath } from './check-result';
28
30
  import { durationAwareInteractiveWaitMs } from './interactive-admission';
31
+
32
+ /**
33
+ * Work verification may overlap another Work's repository-wide build cache.
34
+ * Wait through one ordinary sibling check, but never past Process Runtime's
35
+ * bounded lease-admission ceiling; explicit callers may still choose less.
36
+ */
37
+ export const DEFAULT_WORK_CHECK_LEASE_WAIT_MS = 30_000;
38
+
29
39
  const PERSISTED_CHECK_SOURCE_ENTRY = 'src/runtime/execution/process-runtime/check-runner-sidecar.ts';
30
40
 
31
41
  export function resolvePersistedCheckCliInvocation(
@@ -37,7 +47,10 @@ export function resolvePersistedCheckCliInvocation(
37
47
  }
38
48
 
39
49
  export function resolveRuntimeCliTarget(controllerHome?: string): CliRuntimeTarget {
40
- const moduleSourceRoot = resolve(import.meta.dir, '..', '..', '..', '..');
50
+ const moduleDir = typeof import.meta.dir === 'string' && import.meta.dir
51
+ ? import.meta.dir
52
+ : dirname(fileURLToPath(import.meta.url));
53
+ const moduleSourceRoot = resolve(moduleDir, '..', '..', '..', '..');
41
54
  const sourceEntry = resolve(moduleSourceRoot, 'src', 'cli', 'index.ts');
42
55
  const source = resolveControllerRuntimeSourceRoot({
43
56
  explicitRoot: existsSync(sourceEntry) ? moduleSourceRoot : undefined,
@@ -78,7 +91,7 @@ export function resolvePersistedCheckRuntimeExecutable(
78
91
  entryExists: (path: string) => boolean = existsSync,
79
92
  ): string {
80
93
  if (cliTarget.runtimeKind !== 'compiled_bun_release') return runtimeExecutable;
81
- const checkRunner = resolve(dirname(runtimeExecutable), 'forge-check-runner');
94
+ const checkRunner = resolve(cliTarget.cwd, 'forge-check-runner');
82
95
  if (!entryExists(checkRunner)) {
83
96
  throw new Error(`PERSISTED_CHECK_RUNNER_MISSING: ${checkRunner}`);
84
97
  }
@@ -124,6 +137,38 @@ export async function runPersistedCheckViaProcessRuntime(
124
137
  workerSpawnCount: 0,
125
138
  projectionUpdateCount: 0,
126
139
  };
140
+ const requestId = input.requestId?.trim();
141
+ const requestSemanticFingerprint = input.requestSemanticFingerprint?.trim();
142
+ if (requestId && requestSemanticFingerprint) {
143
+ const binding = getProcessRequestBinding(input.controllerHome, input.repoId, input.checkoutId, requestId);
144
+ if (binding) {
145
+ const record = getProcessRecord(input.controllerHome, input.repoId, binding.processId);
146
+ const exactIdentity = Boolean(
147
+ record
148
+ && record.repoId === input.repoId
149
+ && (record.checkoutId?.trim() || undefined) === (input.checkoutId?.trim() || undefined)
150
+ && (record.workId?.trim() || undefined) === (input.workId?.trim() || undefined)
151
+ && record.origin?.surface === 'check'
152
+ && record.origin.requestId === requestId
153
+ && record.origin.checkId === input.checkId
154
+ && record.origin.requestSemanticFingerprint === requestSemanticFingerprint,
155
+ );
156
+ if (!exactIdentity) throw new Error(`PROCESS_REQUEST_ID_CONFLICT: ${requestId}`);
157
+ const existing = getProcessHandle(input.controllerHome, input.repoId, binding.processId);
158
+ if (!existing) {
159
+ throw new Error(`PROCESS_REQUEST_INCOMPLETE: request ${requestId} is bound to missing process ${binding.processId}; refusing re-execution`);
160
+ }
161
+ const check = listControllerChecks(input.repoRoot).find((entry) => entry.id === input.checkId);
162
+ return {
163
+ mode: existing.completed ? 'direct' : 'managed',
164
+ checkId: input.checkId,
165
+ check,
166
+ process: { ...existing, deduplicated: true, requestId },
167
+ ok: existing.completed ? existing.ok : undefined,
168
+ durableSideEffects: emptyEffects,
169
+ };
170
+ }
171
+ }
127
172
  const verificationSnapshot = input.verificationSnapshot
128
173
  ? materializeWorkVerificationSnapshot({
129
174
  controllerHome: input.controllerHome,
@@ -149,7 +194,7 @@ export async function runPersistedCheckViaProcessRuntime(
149
194
  durableSideEffects: emptyEffects,
150
195
  };
151
196
  }
152
- if (input.forceDurable || checkRequiresDurableWorkflow(input.checkId, check)) {
197
+ if (input.forceDurable || (checkRequiresDurableWorkflow(input.checkId, check) && input.allowDurableCheckExecution !== true)) {
153
198
  cleanupVerificationSnapshot();
154
199
  return {
155
200
  mode: 'durable',
@@ -164,7 +209,8 @@ export async function runPersistedCheckViaProcessRuntime(
164
209
  }
165
210
 
166
211
  const interactiveWaitMs = durationAwareInteractiveWaitMs(check.command, input.interactiveWaitMs);
167
- const leaseWaitMs = input.leaseWaitMs;
212
+ const leaseWaitMs = input.leaseWaitMs
213
+ ?? (input.workId?.trim() ? DEFAULT_WORK_CHECK_LEASE_WAIT_MS : undefined);
168
214
  const timeoutMs = Math.min(
169
215
  check.timeoutMs,
170
216
  typeof input.timeoutMs === 'number' && Number.isFinite(input.timeoutMs)
@@ -214,7 +260,11 @@ export async function runPersistedCheckViaProcessRuntime(
214
260
  scopeKey: persistedCheckSemanticScopeKey(input, semanticCheck.reuseScope),
215
261
  };
216
262
  const cliTarget = resolveRuntimeCliTarget(input.controllerHome);
217
- const checkResultReceiptPath = allocatePersistedCheckResultReceiptPath(input.controllerHome, input.repoId);
263
+ const checkResultReceiptPath = allocatePersistedCheckResultReceiptPath(
264
+ input.controllerHome,
265
+ input.repoId,
266
+ input.requestId,
267
+ );
218
268
  const checkArgs = [
219
269
  '--repo',
220
270
  executionRoot,
@@ -254,6 +304,7 @@ export async function runPersistedCheckViaProcessRuntime(
254
304
  toolName: 'run_check',
255
305
  checkId: input.checkId,
256
306
  requestId: input.requestId,
307
+ requestSemanticFingerprint: input.requestSemanticFingerprint,
257
308
  correlationId: input.workId,
258
309
  executionSessionId: input.verificationBinding?.executionSessionId,
259
310
  editSessionId: input.verificationBinding?.editSessionId,
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  import type { ResourceClaimSpec } from '../jobs/types';
7
+ import { readFileSync } from 'fs';
8
+ import { join } from 'path';
7
9
  import type { ControllerCheckEffects } from '../../../cli/controller/check-runner';
8
10
  import { classifyRepositoryCommand, fixedShellWrapperCommand } from '../../../cli/repositories/command-classifier';
9
11
  import { normalizeRepositoryCommand } from '../../../cli/repositories/command-normalization';
@@ -157,7 +159,89 @@ function iosSimulatorTestCommandClaims(
157
159
  ]);
158
160
  }
159
161
 
160
- function hostOnlyCommandClaims(command: string | readonly string[]): ResourceClaimSpec[] | undefined {
162
+ function localHttpServerClaims(command: string | readonly string[]): ResourceClaimSpec[] | undefined {
163
+ const canonical = normalizeRepositoryCommand(command);
164
+ if (canonical.kind !== 'argv') return undefined;
165
+ const program = (canonical.executable ?? '').split(/[\\/]/).at(-1)?.toLowerCase();
166
+ if (program !== 'python' && program !== 'python3') return undefined;
167
+ const args = canonical.args ?? [];
168
+ if (args[0] !== '-m' || args[1] !== 'http.server' || args.includes('--cgi')) return undefined;
169
+ let bind = '0.0.0.0';
170
+ let port = '8000';
171
+ for (let index = 2; index < args.length; index += 1) {
172
+ const arg = args[index]!;
173
+ if (arg === '--bind') { bind = args[index + 1] ?? bind; index += 1; continue; }
174
+ if (arg === '--directory' || arg === '-d' || arg === '--protocol') { index += 1; continue; }
175
+ if (!arg.startsWith('-') && /^\d{1,5}$/.test(arg)) port = arg;
176
+ }
177
+ return [claimHostService(`tcp-listen:${bind}:${port}`, 'write')];
178
+ }
179
+
180
+ function loopbackCurlClaims(command: string | readonly string[], repoId: string): ResourceClaimSpec[] | undefined {
181
+ const canonical = normalizeRepositoryCommand(command);
182
+ if (canonical.kind !== 'argv') return undefined;
183
+ const program = (canonical.executable ?? '').split(/[\\/]/).at(-1)?.toLowerCase();
184
+ if (program !== 'curl') return undefined;
185
+ const args = canonical.args ?? [];
186
+ if (args.some((arg) => /^(?:-X|--request|-d|--data|--data-raw|--data-binary|-F|--form|-T|--upload-file|-o|--output|-O|--remote-name)$/i.test(arg))) return undefined;
187
+ const explicitMethodIndex = args.findIndex((arg) => arg === '-X' || arg === '--request');
188
+ if (explicitMethodIndex >= 0 && !/^(?:GET|HEAD)$/i.test(args[explicitMethodIndex + 1] ?? '')) return undefined;
189
+ const urls = args.filter((arg) => /^https?:\/\//i.test(arg));
190
+ if (urls.length === 0 || urls.some((url) => {
191
+ try {
192
+ const host = new URL(url).hostname.toLowerCase();
193
+ return !['127.0.0.1', 'localhost', '::1'].includes(host);
194
+ } catch { return true; }
195
+ })) return undefined;
196
+ return [claimNetwork(repoId, 'read')];
197
+ }
198
+
199
+ function simpleViteScript(script: string): boolean {
200
+ const normalized = script.trim();
201
+ if (!normalized || /[;&|`$<>\n\r]/.test(normalized)) return false;
202
+ return /^(?:(?:\.\/)?node_modules\/.bin\/)?vite(?:\s|$)/.test(normalized);
203
+ }
204
+
205
+ function viteServiceClaims(
206
+ command: string | readonly string[],
207
+ repoId: string,
208
+ checkoutId?: string,
209
+ repoRoot?: string,
210
+ ): ResourceClaimSpec[] | undefined {
211
+ const canonical = normalizeRepositoryCommand(command);
212
+ if (canonical.kind !== 'argv') return undefined;
213
+ const program = (canonical.executable ?? '').split(/[\\/]/).at(-1)?.toLowerCase();
214
+ const args = canonical.args ?? [];
215
+ let vite = program === 'vite';
216
+ if (!vite && (program === 'npx' || program === 'bunx') && args[0]?.toLowerCase() === 'vite') vite = true;
217
+ if (!vite && program === 'bun' && args[0]?.toLowerCase() === 'x' && args[1]?.toLowerCase() === 'vite') vite = true;
218
+ if (!vite && repoRoot && ['npm', 'pnpm', 'yarn', 'bun'].includes(program ?? '')) {
219
+ const runIndex = args[0]?.toLowerCase() === 'run' ? 1 : program === 'yarn' ? 0 : -1;
220
+ const scriptName = runIndex >= 0 ? args[runIndex]?.toLowerCase() : undefined;
221
+ if (scriptName === 'dev') {
222
+ try {
223
+ const pkg = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf8')) as { scripts?: Record<string, unknown> };
224
+ const script = typeof pkg.scripts?.dev === 'string' ? pkg.scripts.dev : '';
225
+ vite = simpleViteScript(script);
226
+ } catch {
227
+ vite = false;
228
+ }
229
+ }
230
+ }
231
+ if (!vite) return undefined;
232
+ return normalizeClaims([
233
+ claimBuildCacheWrite(repoId),
234
+ claimHostService(`vite:${checkoutScope(checkoutId)}`, 'write'),
235
+ ]);
236
+ }
237
+
238
+ function hostOnlyCommandClaims(command: string | readonly string[], repoId: string, checkoutId?: string, repoRoot?: string): ResourceClaimSpec[] | undefined {
239
+ const localServer = localHttpServerClaims(command);
240
+ if (localServer) return localServer;
241
+ const loopbackCurl = loopbackCurlClaims(command, repoId);
242
+ if (loopbackCurl) return loopbackCurl;
243
+ const vite = viteServiceClaims(command, repoId, checkoutId, repoRoot);
244
+ if (vite) return vite;
161
245
  const canonical = normalizeRepositoryCommand(command);
162
246
  if (canonical.kind === 'shell') {
163
247
  return browserAppleScriptHeredocClaims(canonical.shellCommand ?? '');
@@ -347,13 +431,14 @@ export function claimsForRepositoryCommand(
347
431
  repoId: string,
348
432
  checkoutId?: string,
349
433
  defaultBranch?: string,
434
+ repoRoot?: string,
350
435
  ): ResourceClaimSpec[] {
351
436
  const classification = classifyRepositoryCommand(command, defaultBranch);
352
437
  const canonical = normalizeRepositoryCommand(command);
353
438
  const focused = isFocusedCheckCommand(command);
354
439
  const iosSimulatorClaims = iosSimulatorTestCommandClaims(command, repoId, checkoutId);
355
440
  if (iosSimulatorClaims) return iosSimulatorClaims;
356
- const hostClaims = hostOnlyCommandClaims(command);
441
+ const hostClaims = hostOnlyCommandClaims(command, repoId, checkoutId, repoRoot);
357
442
  if (hostClaims) return hostClaims;
358
443
 
359
444
  if (classification.risk === 'readonly') {
@@ -426,12 +511,16 @@ export function claimsForCheck(
426
511
  ): ResourceClaimSpec[] {
427
512
  const heavy = /(?:^|:)(?:test(?::coverage)?|check:(?:ci|forge-runtime|public-export|release(?:-[a-z0-9-]+)?))$/.test(checkId)
428
513
  || /release|migration|integrate/i.test(checkId);
429
- if (heavy) return [claimHeavyCheck(repoId)];
430
- if (effects) return claimsForDeclaredCheckEffects(checkId, effects, repoId, checkoutId);
431
- if (staticAnalysisCheckId(checkId)) return [claimWorkspaceRead(checkoutId), claimBuildCacheWrite(repoId)];
432
- if (command && command.length > 0) return claimsForRepositoryCommand(command, repoId, checkoutId);
433
- // Unknown named checks without a command/effects contract remain conservative.
434
- return [claimWorkspaceWrite(checkoutId)];
514
+ const baseClaims = effects
515
+ ? claimsForDeclaredCheckEffects(checkId, effects, repoId, checkoutId)
516
+ : staticAnalysisCheckId(checkId)
517
+ ? [claimWorkspaceRead(checkoutId), claimBuildCacheWrite(repoId)]
518
+ : command && command.length > 0
519
+ ? claimsForRepositoryCommand(command, repoId, checkoutId)
520
+ : [claimWorkspaceWrite(checkoutId)];
521
+ // Heavy-check is an additional cross-check serialization fence, not a
522
+ // substitute for the resources the check actually reads or writes.
523
+ return heavy ? normalizeClaims([claimHeavyCheck(repoId), ...baseClaims]) : baseClaims;
435
524
  }
436
525
 
437
526
  export function scopeResourceClaims(
@@ -258,8 +258,23 @@ function captureIdentity(pid: number | undefined): {
258
258
  } {
259
259
  if (!pid || pid <= 0) return {};
260
260
  const probe = defaultProcessIdentityProbe;
261
- if (!probe.isAlive(pid)) return {};
261
+ // The combined inspect probe already proves the PID exists on the normal
262
+ // POSIX path. Avoid an immediately preceding ps(stat) child process; only
263
+ // fall back to the separate alive/command/start-time probes when the combined
264
+ // observation is unavailable or incomplete.
262
265
  const inspected = probe.inspect?.(pid);
266
+ if (inspected?.command && inspected.startTime) {
267
+ return {
268
+ identity: {
269
+ pid,
270
+ processStartTime: inspected.startTime,
271
+ executableFingerprint: executableFingerprint(inspected.command),
272
+ processGroupId: process.platform !== 'win32' ? pid : undefined,
273
+ },
274
+ identityUntrusted: false,
275
+ };
276
+ }
277
+ if (!probe.isAlive(pid)) return {};
263
278
  const command = inspected?.command ?? probe.command(pid);
264
279
  const startTime = inspected?.startTime ?? probe.startTime(pid);
265
280
  if (!command || !startTime) {
@@ -291,8 +306,9 @@ function identityStillMatches(identity: ManagedProcessRecord['identity'] | undef
291
306
  }
292
307
  if (!isProcessAlive(identity.pid)) return false;
293
308
  const probe = defaultProcessIdentityProbe;
294
- const startTime = probe.startTime(identity.pid);
295
- const command = probe.command(identity.pid);
309
+ const inspected = probe.inspect?.(identity.pid);
310
+ const startTime = inspected?.startTime ?? probe.startTime(identity.pid);
311
+ const command = inspected?.command ?? probe.command(identity.pid);
296
312
  if (!startTime || !command) return false;
297
313
  if (startTime !== identity.processStartTime) return false;
298
314
  if (executableFingerprint(command) !== identity.executableFingerprint) return false;
@@ -526,7 +542,7 @@ export function completeProcessFromEvidence(
526
542
  stdoutTail?: string;
527
543
  stderrTail?: string;
528
544
  },
529
- options: { authority?: 'runtime_writer' | 'durable_exit_receipt' | 'durable_pre_spawn_abandonment' } = {},
545
+ options: { authority?: 'runtime_writer' | 'durable_exit_receipt' | 'durable_pre_spawn_abandonment' | 'durable_stale_active_reconciliation' } = {},
530
546
  ): ManagedProcessRecord | undefined {
531
547
  // Validated independent evidence may only perform the monotonic,
532
548
  // fence-token-bound terminal CAS below. It does not grant general writer
@@ -612,6 +628,45 @@ export function reconcileAbandonedPreSpawnProcess(
612
628
  }, { authority: 'durable_pre_spawn_abandonment' });
613
629
  }
614
630
 
631
+ /**
632
+ * Bounded maintenance reconciliation for stale active Process records that are
633
+ * no longer reachable through the v2 recovery index. This never deletes an
634
+ * active record and never re-executes a command: it first consumes any durable
635
+ * Runner receipt, preserves live monitors / matching process identities, and
636
+ * only then commits a monotonic terminal state for a provably dead identity.
637
+ */
638
+ export function reconcileStaleManagedProcessForMaintenance(
639
+ controllerHome: string,
640
+ repoId: string,
641
+ processId: string,
642
+ options: { nowMs?: number; minAgeMs?: number } = {},
643
+ ): ManagedProcessRecord | undefined {
644
+ let record = getProcessRecord(controllerHome, repoId, processId);
645
+ if (!record || !isManagedProcessActive(record)) return record;
646
+
647
+ const nowMs = options.nowMs ?? Date.now();
648
+ const minAgeMs = Math.max(1_000, options.minAgeMs ?? DEFAULT_PRE_SPAWN_ABANDONMENT_MS);
649
+ const updatedAtMs = Date.parse(record.updatedAt || record.startedAt);
650
+ if (!Number.isFinite(updatedAtMs) || nowMs - updatedAtMs < minAgeMs) return record;
651
+
652
+ const fromReceipt = applyReceiptIfPresent(controllerHome, repoId, processId, record);
653
+ if (fromReceipt) return fromReceipt;
654
+
655
+ if (record.status === 'starting' && !record.identity) {
656
+ return reconcileAbandonedPreSpawnProcess(controllerHome, repoId, processId, { nowMs, minAgeMs });
657
+ }
658
+
659
+ record = getProcessRecord(controllerHome, repoId, processId) ?? record;
660
+ if (!isManagedProcessActive(record) || liveMonitors.has(processId)) return record;
661
+ if (identityStillMatches(record.identity, record.identityUntrusted)) return record;
662
+
663
+ return completeProcessFromEvidence(controllerHome, repoId, processId, record.terminalFenceToken, {
664
+ status: 'completed_unknown',
665
+ finishedAt: new Date(nowMs).toISOString(),
666
+ errorMessage: 'PROCESS_STALE_ACTIVE_RECONCILED: stale active record has no live matching process identity or durable exit receipt',
667
+ }, { authority: 'durable_stale_active_reconciliation' });
668
+ }
669
+
615
670
  async function killTree(child: ChildProcess): Promise<void> {
616
671
  const pid = child.pid;
617
672
  if (!pid) return;
@@ -299,6 +299,7 @@ function sanitizeProcessRecord(record: ManagedProcessRecord): { record: ManagedP
299
299
  toolName: safeText(record.origin.toolName),
300
300
  requestId: safeText(record.origin.requestId),
301
301
  checkId: safeText(record.origin.checkId),
302
+ requestSemanticFingerprint: safeText(record.origin.requestSemanticFingerprint),
302
303
  correlationId: safeText(record.origin.correlationId),
303
304
  } : undefined,
304
305
  };
@@ -308,6 +309,12 @@ function sanitizeProcessRecord(record: ManagedProcessRecord): { record: ManagedP
308
309
  function readProcessRecord(path: string): ManagedProcessRecord | undefined {
309
310
  const record = readJson<ManagedProcessRecord>(path);
310
311
  if (!record || record.schemaVersion !== 1) return undefined;
312
+ // Legacy/partial Process files can predate the required command descriptor.
313
+ // They are not executable recovery authority and must never be repaired by
314
+ // inventing a command. Treat them as unavailable to normal Runtime readers;
315
+ // bounded GC has its own metadata-only parser so one such file cannot block
316
+ // cleanup of unrelated proven terminal records.
317
+ if (!record.command || typeof record.command !== 'object' || Array.isArray(record.command)) return undefined;
311
318
  const sanitized = sanitizeProcessRecord(record);
312
319
  if (sanitized.changed) atomicWrite(path, sanitized.record);
313
320
  return sanitized.record;
@@ -239,6 +239,8 @@ export interface ManagedProcessRecord {
239
239
  toolName?: string;
240
240
  requestId?: string;
241
241
  checkId?: string;
242
+ /** Stable semantic identity supplied by a facade for exact request reattachment. */
243
+ requestSemanticFingerprint?: string;
242
244
  correlationId?: string;
243
245
  /** Controller MCP session that initiated this check, when applicable. */
244
246
  executionSessionId?: string;
@@ -10,6 +10,9 @@ export interface BoundedProcessOptions {
10
10
  signal?: AbortSignal;
11
11
  /** When true (default on non-Windows), create a new process group for tree kill. */
12
12
  processGroup?: boolean;
13
+ onSpawn?: (pid: number) => void;
14
+ onStdout?: (chunk: string) => void;
15
+ onStderr?: (chunk: string) => void;
13
16
  }
14
17
 
15
18
  export interface BoundedProcessResult {
@@ -114,6 +117,7 @@ export async function runBoundedProcess(
114
117
  stdio: ['ignore', 'pipe', 'pipe'],
115
118
  detached: useProcessGroup,
116
119
  });
120
+ if (child.pid) options.onSpawn?.(child.pid);
117
121
 
118
122
  const finish = (exitCode: number) => {
119
123
  if (settled) return;
@@ -150,8 +154,14 @@ export async function runBoundedProcess(
150
154
 
151
155
  options.signal?.addEventListener('abort', onAbort, { once: true });
152
156
 
153
- child.stdout?.on('data', (chunk: Buffer) => stdout.write(chunk));
154
- child.stderr?.on('data', (chunk: Buffer) => stderr.write(chunk));
157
+ child.stdout?.on('data', (chunk: Buffer) => {
158
+ stdout.write(chunk);
159
+ options.onStdout?.(chunk.toString('utf8'));
160
+ });
161
+ child.stderr?.on('data', (chunk: Buffer) => {
162
+ stderr.write(chunk);
163
+ options.onStderr?.(chunk.toString('utf8'));
164
+ });
155
165
  child.on('error', (error) => {
156
166
  spawnError = error.message;
157
167
  });
@@ -5,7 +5,12 @@
5
5
  import { isMainThread, parentPort, workerData, Worker } from 'worker_threads';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { getMcpPolicy } from '../../../cli/mcp/policy';
8
- import { searchRepository } from '../../../cli/repository/inspector';
8
+ import type { McpPolicy } from '../../../cli/mcp/types';
9
+ import {
10
+ searchRepository,
11
+ searchRepositoryMany,
12
+ type SearchRepositoryManyResult,
13
+ } from '../../../cli/repository/inspector';
9
14
 
10
15
  export interface SearchWorkerInput {
11
16
  repoRoot: string;
@@ -14,14 +19,44 @@ export interface SearchWorkerInput {
14
19
  maxFiles: number;
15
20
  }
16
21
 
22
+ export interface SearchManyWorkerInput {
23
+ repoRoot: string;
24
+ policy: McpPolicy;
25
+ queries: string[];
26
+ files?: string[];
27
+ includeGlobs?: string[];
28
+ excludeGlobs?: string[];
29
+ maxResultsPerQuery?: number;
30
+ maxFiles?: number;
31
+ caseSensitive?: boolean;
32
+ cacheKey?: string;
33
+ }
34
+
17
35
  export interface SearchWorkerResult {
18
36
  ok: boolean;
19
37
  payload?: Record<string, unknown>;
20
38
  error?: string;
21
39
  }
22
40
 
23
- function runSearchSync(input: SearchWorkerInput): SearchWorkerResult {
41
+ function isManySearch(input: SearchWorkerInput | SearchManyWorkerInput): input is SearchManyWorkerInput {
42
+ return Array.isArray((input as SearchManyWorkerInput).queries);
43
+ }
44
+
45
+ function runSearchSync(input: SearchWorkerInput | SearchManyWorkerInput): SearchWorkerResult {
24
46
  try {
47
+ if (isManySearch(input)) {
48
+ const result = searchRepositoryMany(input.repoRoot, input.policy, {
49
+ queries: input.queries,
50
+ files: input.files,
51
+ includeGlobs: input.includeGlobs,
52
+ excludeGlobs: input.excludeGlobs,
53
+ maxResultsPerQuery: input.maxResultsPerQuery,
54
+ maxFiles: input.maxFiles,
55
+ caseSensitive: input.caseSensitive,
56
+ cacheKey: input.cacheKey,
57
+ }) as unknown as Record<string, unknown>;
58
+ return { ok: true, payload: result };
59
+ }
25
60
  const result = searchRepository(input.repoRoot, getMcpPolicy('controller', { repoRoot: input.repoRoot }), {
26
61
  query: input.query,
27
62
  maxResults: input.maxResults,
@@ -36,8 +71,8 @@ function runSearchSync(input: SearchWorkerInput): SearchWorkerResult {
36
71
  }
37
72
  }
38
73
 
39
- export async function runInspectorSearchInWorker(
40
- input: SearchWorkerInput,
74
+ async function runSearchWorker(
75
+ input: SearchWorkerInput | SearchManyWorkerInput,
41
76
  options: { timeoutMs?: number; signal?: AbortSignal } = {},
42
77
  ): Promise<Record<string, unknown>> {
43
78
  if (options.signal?.aborted) throw new Error('CANCELLED: search aborted');
@@ -108,7 +143,21 @@ export async function runInspectorSearchInWorker(
108
143
  });
109
144
  }
110
145
 
146
+ export async function runInspectorSearchInWorker(
147
+ input: SearchWorkerInput,
148
+ options: { timeoutMs?: number; signal?: AbortSignal } = {},
149
+ ): Promise<Record<string, unknown>> {
150
+ return await runSearchWorker(input, options);
151
+ }
152
+
153
+ export async function runInspectorSearchManyInWorker(
154
+ input: SearchManyWorkerInput,
155
+ options: { timeoutMs?: number; signal?: AbortSignal } = {},
156
+ ): Promise<SearchRepositoryManyResult> {
157
+ return await runSearchWorker(input, options) as unknown as SearchRepositoryManyResult;
158
+ }
159
+
111
160
  if (!isMainThread && parentPort) {
112
- const input = workerData as SearchWorkerInput;
161
+ const input = workerData as SearchWorkerInput | SearchManyWorkerInput;
113
162
  parentPort.postMessage(runSearchSync(input));
114
163
  }
@@ -21,7 +21,6 @@ import {
21
21
  } from '../jobs/child-reference';
22
22
  import { markOperationDelegated } from '../jobs/receipt-store';
23
23
  import { assertAutomatedOperationAllowed } from '../../control-plane/governance/external-effects';
24
- import { recordCandidateFinding, updateCandidateFinding } from '../../workflow/findings/store';
25
24
  import { triggerWorkspaceAgent } from '../workspace-agent';
26
25
  import { executeAssistantPluginAction } from '../../plugins/store';
27
26
  import { CONTROLLER_SCOPE_REPO_ID, controllerSystemRoot, repositoryControllerRoot } from '../../../cli/repositories/controller-home';
@@ -29,7 +28,6 @@ import { writeExecutionArtifact } from '../../evidence/artifact-store';
29
28
  import { existsSync } from 'fs';
30
29
  import { isAbsolute, relative, resolve, sep } from 'path';
31
30
  import { isAssistantPluginError } from '../../plugins/errors';
32
- import { executeAssistantRoutineRuntime } from '../../assistant/routine-runtime';
33
31
 
34
32
 
35
33
  function childReferenceFromLocalJob(
@@ -445,20 +443,6 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
445
443
  }
446
444
  bindRepositoryEntities(repository);
447
445
 
448
- if (job.payload.target === 'runtime' && job.payload.operation === 'assistant_routine_execute') {
449
- const routineId = String(job.payload.arguments?.routineId ?? job.payload.arguments?.routine_id ?? '').trim();
450
- if (!routineId) throw new Error('ASSISTANT_ROUTINE_ID_REQUIRED');
451
- const routineResult = await executeAssistantRoutineRuntime({
452
- controllerHome,
453
- repository,
454
- routineId,
455
- requestId: job.requestId,
456
- origin: job.origin,
457
- occurrenceId: typeof job.payload.occurrenceId === 'string' ? job.payload.occurrenceId : undefined,
458
- });
459
- return { ok: true, result: { assistantRoutine: routineResult }, repoRoot };
460
- }
461
-
462
446
  if (job.payload.target === 'runtime' && job.payload.operation === 'legacy-local-job') {
463
447
  const localJobId = String(job.payload.arguments?.localJobId ?? '').trim();
464
448
  if (!localJobId) throw new Error('LEGACY_JOB_ID_REQUIRED');
@@ -511,26 +495,6 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
511
495
  : { ok: false, error: { code: 'RELEASE_GATE_BLOCKED', message: 'Release gate has blockers.', retryable: false, details: { gate } }, repoRoot };
512
496
  }
513
497
 
514
- if (job.payload.operation === 'record_candidate_finding') {
515
- const args = job.payload.arguments ?? {};
516
- const finding = recordCandidateFinding(controllerHome, {
517
- repoId: job.repoId,
518
- requestId: job.requestId,
519
- semanticKey: String(args.semantic_key ?? args.semanticKey ?? '').trim(),
520
- title: String(args.title ?? '').trim(),
521
- summary: typeof args.summary === 'string' ? args.summary : undefined,
522
- severity: ['low', 'medium', 'high', 'critical'].includes(String(args.severity))
523
- ? String(args.severity) as 'low' | 'medium' | 'high' | 'critical'
524
- : 'medium',
525
- evidence: {
526
- source: job.origin.surface,
527
- reference: typeof args.reference === 'string' ? args.reference : job.origin.correlationId,
528
- details: args.evidence && typeof args.evidence === 'object' ? args.evidence as Record<string, unknown> : undefined,
529
- },
530
- });
531
- return { ok: true, result: { finding }, repoRoot };
532
- }
533
-
534
498
  const allowedAgents = Array.isArray(job.payload.allowedAgents)
535
499
  ? job.payload.allowedAgents.filter((value): value is string => typeof value === 'string')
536
500
  : [];
@@ -651,18 +615,6 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
651
615
  );
652
616
  }
653
617
 
654
- if (job.origin.actor === 'promote_candidate_finding' && job.origin.correlationId) {
655
- try {
656
- updateCandidateFinding(controllerHome, job.repoId, job.origin.correlationId, (current) => ({
657
- ...current,
658
- status: result.isError ? 'candidate' : 'promoted',
659
- promotedJobId: job.jobId,
660
- }), job.requestId, result.isError ? 'candidate_promotion_failed' : 'candidate_finding_promoted');
661
- } catch {
662
- // The Issue result remains authoritative even if the candidate projection needs reconciliation.
663
- }
664
- }
665
-
666
618
  if (result.isError) {
667
619
  return { ok: false, error: { code: 'MCP_TOOL_FAILED', message: JSON.stringify(record), retryable: false }, repoRoot };
668
620
  }