@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,13 +1,14 @@
1
1
  import type { ProcessCheckReceiptEvidence } from '../../evidence/process-check-receipt';
2
2
  import type { CompletionReceipt as RepositoryCompletionReceipt } from '../../../cli/controller/types';
3
3
  import type { AccessMode } from '../governance/access-policy';
4
- import { decideRoute, type RouteDecision, type RoutePolicyInput } from '../routing/route-policy';
4
+ import { decideRoute, type ExplicitTaskMode, type RouteDecision, type RoutePolicyInput } from '../routing/route-policy';
5
5
 
6
6
  export const EXECUTION_MODES = ['direct_control', 'goal_workloop', 'handoff_only'] as const;
7
7
  export type ExecutionMode = (typeof EXECUTION_MODES)[number];
8
8
 
9
9
  export const FACADE_TOOLS = ['rh_access', 'rh_status', 'rh_inbox', 'rh_context', 'rh_work'] as const;
10
10
  export type FacadeTool = (typeof FACADE_TOOLS)[number];
11
+ export type CapabilityExecutionSurface = FacadeTool | 'plugin_action_execute';
11
12
 
12
13
  export const FACADE_STATUSES = ['ok', 'blocked', 'failed', 'approval_required', 'not_found'] as const;
13
14
  export type FacadeStatus = (typeof FACADE_STATUSES)[number];
@@ -156,18 +157,35 @@ export interface LocalEffectCompletionReceipt {
156
157
  recordedAt: string;
157
158
  }
158
159
 
160
+ /** Durable completion authority for one typed remote plugin effect. */
161
+ export interface RemoteEffectCompletionReceipt {
162
+ schemaVersion: 1;
163
+ /** Reuse the authoritative PLG-* receipt id rather than manufacturing parallel evidence. */
164
+ receiptId: string;
165
+ source: 'remote_effect';
166
+ workId: string;
167
+ pluginId: string;
168
+ actionId: string;
169
+ requestId: string;
170
+ semanticKey: string;
171
+ resultDigest: string;
172
+ recordedAt: string;
173
+ }
174
+
159
175
  /**
160
176
  * Completion authority for a standalone Direct Edit that is not bound to the
161
- * retired Issue/Task completion model. The receipt proves that one finalized
162
- * edit session is present at an exact reachable Git revision with clean owned
163
- * paths, while keeping the WorkContract as the canonical lifecycle authority.
177
+ * retired Issue/Task completion model. Normal completion proves one finalized
178
+ * edit session at an exact reachable Git revision. Historical completion may
179
+ * instead reference one explicit reviewed reconciliation after exact revision,
180
+ * path-scope, verification, reachability, and cleanup proof checks.
164
181
  */
165
182
  export interface DirectEditWorkCompletionReceipt {
166
183
  schemaVersion: 1;
167
184
  receiptId: string;
168
185
  source: 'direct_edit_work';
169
186
  workId: string;
170
- editSessionId: string;
187
+ editSessionId?: string;
188
+ reconciliationId?: string;
171
189
  targetBranch: string;
172
190
  targetRevision: string;
173
191
  sourceRevision?: string;
@@ -179,12 +197,18 @@ export interface DirectEditWorkCompletionReceipt {
179
197
  recordedAt: string;
180
198
  }
181
199
 
182
- export type WorkCompletionReceipt = RepositoryCompletionReceipt | DirectEditWorkCompletionReceipt | LocalEffectCompletionReceipt;
200
+ export type WorkCompletionReceipt = RepositoryCompletionReceipt | DirectEditWorkCompletionReceipt | LocalEffectCompletionReceipt | RemoteEffectCompletionReceipt;
183
201
 
184
202
  export function isRepositoryCompletionReceipt(
185
203
  receipt: WorkCompletionReceipt,
186
204
  ): receipt is RepositoryCompletionReceipt {
187
- return receipt.source !== 'local_effect' && receipt.source !== 'direct_edit_work';
205
+ return receipt.source !== 'local_effect' && receipt.source !== 'remote_effect' && receipt.source !== 'direct_edit_work';
206
+ }
207
+
208
+ export function isRemoteEffectCompletionReceipt(
209
+ receipt: WorkCompletionReceipt,
210
+ ): receipt is RemoteEffectCompletionReceipt {
211
+ return receipt.source === 'remote_effect';
188
212
  }
189
213
 
190
214
  export function isDirectEditWorkCompletionReceipt(
@@ -330,6 +354,8 @@ export interface WorkContractConstraints {
330
354
  conflictsWithThinHarnessPolicy?: boolean;
331
355
  /** True when the change alters the default routing or execution mode visible to users. */
332
356
  changesDefaultExecutionStrategy?: boolean;
357
+ /** Repository-change delivery must publish the exact integrated revision before Work terminalization. */
358
+ remoteDeliveryRequired?: boolean;
333
359
  }
334
360
 
335
361
  export interface WorkContractDriverPolicy {
@@ -431,6 +457,10 @@ export interface WorkContract {
431
457
  phaseEvidence: WorkPhaseEvidenceMap;
432
458
  /** Explicit v2 execution semantics. `status` is retained for compatibility. */
433
459
  workKind: WorkKind;
460
+ /** Primary is an objective-level business execution lane; execution_child is a resumable low-level operation handle owned by a primary Work or standalone caller. */
461
+ lifecycleRole?: 'primary' | 'execution_child';
462
+ /** Optional objective-level parent when this Work is only an execution child. */
463
+ parentWorkId?: string;
434
464
  dispatchState: DispatchState;
435
465
  evidenceState: EvidenceState;
436
466
  completionOutcome?: CompletionOutcome;
@@ -449,6 +479,18 @@ export interface WorkContract {
449
479
  planStepId?: string;
450
480
  planSourceRevision?: string;
451
481
  scopeSummary?: string;
482
+ /**
483
+ * Discovery evidence, never an authorization boundary. Initial guesses,
484
+ * inspected paths, and observed changes remain distinct so early retrieval
485
+ * cannot masquerade as complete implementation scope.
486
+ */
487
+ scopeEvidence?: {
488
+ initialLikelyPaths: string[];
489
+ inspectedPaths: string[];
490
+ actualChangedPaths: string[];
491
+ recordedAt: string;
492
+ };
493
+ /** Explicit policy fence. This is not semantic completeness evidence. */
452
494
  allowedPaths: string[];
453
495
  forbiddenPaths: string[];
454
496
  checks: string[];
@@ -468,13 +510,12 @@ export interface WorkContract {
468
510
  continuationPrompt?: string;
469
511
  worktreeRef?: string;
470
512
  workerRef?: string;
471
- /** Stable idempotency key when this Work was accepted via work_submit. */
472
513
  requestId?: string;
473
- /** Accepted Kernel operation envelope. Does not imply ExecutionJob creation. */
514
+ /** Typed execution metadata; it does not imply ExecutionJob creation. */
474
515
  submittedOperation?: SubmittedWorkOperation;
475
516
  }
476
517
 
477
- /** Operation metadata retained on WorkContracts accepted through work_submit. */
518
+ /** Typed execution metadata retained on execution-child WorkContracts. */
478
519
  export interface SubmittedWorkOperation {
479
520
  name: string;
480
521
  semanticKey: string;
@@ -603,7 +644,7 @@ export interface CapabilityDescriptor {
603
644
  group: CapabilityGroup;
604
645
  operationClass: CapabilityOperationClass;
605
646
  risk: CapabilityRisk;
606
- exposedVia: FacadeTool;
647
+ exposedVia: CapabilityExecutionSurface;
607
648
  schemaExposure: CapabilitySchemaExposure;
608
649
  summary: string;
609
650
  }
@@ -612,6 +653,7 @@ export interface CapabilityGroupSummary {
612
653
  group: CapabilityGroup;
613
654
  capabilityCount: number;
614
655
  domains: CapabilityDomain[];
656
+ executionSurfaces: CapabilityExecutionSurface[];
615
657
  facadeTools: FacadeTool[];
616
658
  operationClasses: CapabilityOperationClass[];
617
659
  risks: CapabilityRisk[];
@@ -640,6 +682,7 @@ export interface ExecutionModeSelectionInput {
640
682
  requiresInvestigation?: boolean;
641
683
  requiresLongRunningChecks?: boolean;
642
684
  requiresParallelism?: boolean;
685
+ explicitMode?: ExplicitTaskMode;
643
686
  needsDependencies?: boolean;
644
687
  requiresRecovery?: boolean;
645
688
  /** True only when the user explicitly requested an agent/worker executor. Complexity alone must not enable workers. */
@@ -690,6 +733,7 @@ export function selectExecutionMode(input: ExecutionModeSelectionInput): Executi
690
733
  requiresInvestigation: input.requiresInvestigation,
691
734
  requiresLongRunningChecks: input.requiresLongRunningChecks,
692
735
  requiresParallelism: input.requiresParallelism,
736
+ explicitMode: input.explicitMode,
693
737
  needsDependencies: input.needsDependencies,
694
738
  agentRequested: input.requiresWorker,
695
739
  },
@@ -18,10 +18,10 @@ import {
18
18
  type DispatchState,
19
19
  type EvidenceState,
20
20
  type PolicyDecision,
21
- type SubmittedWorkOperation,
22
21
  type SuggestedNextAction,
23
22
  type VerificationRecord,
24
23
  type WorkReconciliationRecord,
24
+ type SubmittedWorkOperation,
25
25
  type WorkContract,
26
26
  type WorkContractStatus,
27
27
  type WorkRisk,
@@ -32,6 +32,7 @@ import {
32
32
  type WorkPhaseEvidenceState,
33
33
  type WorkContractStore,
34
34
  isDirectEditWorkCompletionReceipt,
35
+ isRemoteEffectCompletionReceipt,
35
36
  isRepositoryCompletionReceipt,
36
37
  isTerminalWorkContractStatus,
37
38
  } from './types';
@@ -195,7 +196,7 @@ function legacyPhaseEvidence(
195
196
  state,
196
197
  source: completedByReceipt ? 'recorded' : source,
197
198
  summary: completedByReceipt
198
- ? `Phase ${phase} accepted by Work completion receipt ${contract.completionReceipt!.receiptId}.`
199
+ ? `Phase ${phase} satisfied by Work completion receipt ${contract.completionReceipt!.receiptId}.`
199
200
  : index < currentIndex
200
201
  ? `Legacy Work advanced beyond ${phase}.`
201
202
  : `Legacy Work phase ${phase} is ${state}.`,
@@ -285,13 +286,20 @@ function validateWorkSemantics(contract: WorkContract): WorkContract {
285
286
  if (receipt.delivery.status !== 'integrated' || !receipt.delivery.reachable) throw new Error('WORK_COMPLETION_RECEIPT_DELIVERY_NOT_PROVEN');
286
287
  if (!['complete', 'maintenance_warning'].includes(receipt.cleanup.status) || receipt.cleanup.blockers.length > 0) throw new Error('WORK_COMPLETION_RECEIPT_CLEANUP_NOT_PROVEN');
287
288
  } else if (isDirectEditWorkCompletionReceipt(receipt)) {
288
- if (!receipt.editSessionId.trim()) throw new Error('WORK_COMPLETION_RECEIPT_DIRECT_EDIT_SESSION_REQUIRED');
289
+ const directEditAuthorityCount = Number(Boolean(receipt.editSessionId?.trim())) + Number(Boolean(receipt.reconciliationId?.trim()));
290
+ if (directEditAuthorityCount !== 1) throw new Error('WORK_COMPLETION_RECEIPT_DIRECT_EDIT_AUTHORITY_REQUIRED');
289
291
  if (!receipt.targetBranch.trim() || !receipt.targetRevision.trim()) throw new Error('WORK_COMPLETION_RECEIPT_TARGET_REQUIRED');
290
292
  if (receipt.delivery.status !== 'integrated' || !receipt.delivery.reachable) throw new Error('WORK_COMPLETION_RECEIPT_DELIVERY_NOT_PROVEN');
291
293
  if (!['complete', 'maintenance_warning'].includes(receipt.cleanup.status) || receipt.cleanup.blockers.length > 0) throw new Error('WORK_COMPLETION_RECEIPT_CLEANUP_NOT_PROVEN');
292
294
  if (!['repository_change', 'completed_no_change'].includes(contract.workKind)) throw new Error('WORK_COMPLETION_RECEIPT_DIRECT_EDIT_KIND_REQUIRED');
293
295
  const expectedOutcome = contract.workKind === 'completed_no_change' ? 'completed_no_change' : 'completed_changed';
294
296
  if (contract.completionOutcome !== expectedOutcome) throw new Error('WORK_COMPLETION_RECEIPT_DIRECT_EDIT_OUTCOME_REQUIRED');
297
+ } else if (isRemoteEffectCompletionReceipt(receipt)) {
298
+ if (contract.workKind !== 'remote_effect') throw new Error('WORK_COMPLETION_RECEIPT_REMOTE_EFFECT_KIND_REQUIRED');
299
+ if (contract.completionOutcome !== 'completed_remote') throw new Error('WORK_COMPLETION_RECEIPT_REMOTE_EFFECT_OUTCOME_REQUIRED');
300
+ if (!receipt.pluginId.trim() || !receipt.actionId.trim() || !receipt.requestId.trim() || !receipt.semanticKey.trim() || !receipt.resultDigest.trim()) {
301
+ throw new Error('WORK_COMPLETION_RECEIPT_REMOTE_EFFECT_IDENTITY_REQUIRED');
302
+ }
295
303
  } else {
296
304
  if (contract.workKind !== 'local_effect') throw new Error('WORK_COMPLETION_RECEIPT_LOCAL_EFFECT_KIND_REQUIRED');
297
305
  if (contract.completionOutcome !== 'completed_local') throw new Error('WORK_COMPLETION_RECEIPT_LOCAL_EFFECT_OUTCOME_REQUIRED');
@@ -335,6 +343,9 @@ function validateWorkSemantics(contract: WorkContract): WorkContract {
335
343
  if (outcome === 'completed_remote' && contract.workKind !== 'remote_effect') {
336
344
  throw new Error('WORK_SEMANTICS_INVALID: completed_remote requires remote_effect WorkKind');
337
345
  }
346
+ if (outcome === 'completed_remote' && contract.completionReceipt?.source !== 'remote_effect') {
347
+ throw new Error('WORK_SEMANTICS_INVALID: completed_remote requires a remote_effect completion receipt');
348
+ }
338
349
  if (outcome === 'superseded' && contract.workKind !== 'superseded') {
339
350
  throw new Error('WORK_SEMANTICS_INVALID: superseded requires superseded WorkKind');
340
351
  }
@@ -359,11 +370,25 @@ const EVIDENCE_TRANSITIONS: Readonly<Record<EvidenceState, readonly EvidenceStat
359
370
  failed: ['failed', 'partial', 'valid', 'contradictory'],
360
371
  };
361
372
 
362
- function validateWorkSemanticTransition(current: WorkContract, next: WorkContract): WorkContract {
373
+ function validateWorkSemanticTransition(
374
+ current: WorkContract,
375
+ next: WorkContract,
376
+ options: { allowRetainedCancelledResume?: boolean } = {},
377
+ ): WorkContract {
363
378
  const retryingFailedWork = current.status === 'failed'
364
379
  && !current.completionOutcome
365
380
  && ['claimed', 'launching', 'running', 'blocked'].includes(next.dispatchState);
366
- if (!retryingFailedWork && !DISPATCH_TRANSITIONS[current.dispatchState].includes(next.dispatchState)) {
381
+ const resumingRetainedCancelledWork = options.allowRetainedCancelledResume === true
382
+ && current.status === 'cancelled'
383
+ && current.dispatchState === 'terminal'
384
+ && current.phase === 'cleanup'
385
+ && current.phaseEvidence.cleanup.state === 'skipped'
386
+ && !current.completionReceipt
387
+ && !current.completionOutcome
388
+ && next.status === 'running'
389
+ && next.dispatchState === 'running'
390
+ && next.phase === 'implementation';
391
+ if (!retryingFailedWork && !resumingRetainedCancelledWork && !DISPATCH_TRANSITIONS[current.dispatchState].includes(next.dispatchState)) {
367
392
  throw new Error(`WORK_SEMANTICS_TRANSITION_INVALID: dispatch ${current.dispatchState} -> ${next.dispatchState}`);
368
393
  }
369
394
  if (!EVIDENCE_TRANSITIONS[current.evidenceState].includes(next.evidenceState)) {
@@ -372,9 +397,20 @@ function validateWorkSemanticTransition(current: WorkContract, next: WorkContrac
372
397
  if (current.completionOutcome && current.completionOutcome !== next.completionOutcome) {
373
398
  throw new Error(`WORK_SEMANTICS_TRANSITION_INVALID: completion outcome ${current.completionOutcome} is immutable`);
374
399
  }
400
+ if ((current.lifecycleRole ?? 'primary') !== (next.lifecycleRole ?? 'primary')) {
401
+ throw new Error('WORK_SEMANTICS_TRANSITION_INVALID: lifecycleRole is immutable');
402
+ }
403
+ if (current.parentWorkId !== next.parentWorkId) {
404
+ throw new Error('WORK_SEMANTICS_TRANSITION_INVALID: parentWorkId is immutable');
405
+ }
375
406
  return next;
376
407
  }
377
408
 
409
+ function suggestedActionsForStatus(status: WorkContractStatus, actions: readonly SuggestedNextAction[]): SuggestedNextAction[] {
410
+ if (status === 'completed' || status === 'cancelled') return [];
411
+ return actions.slice(0, 8);
412
+ }
413
+
378
414
  function sqliteBacked(options: WorkContractStoreOptions): options is WorkContractStoreOptions & { controllerHome: string; repoId: string } {
379
415
  // A caller-provided root is a test/portable compatibility store. Runtime
380
416
  // controller state always carries controllerHome + repoId and is SQLite.
@@ -412,6 +448,7 @@ function normalizeWorkContractStore(store: WorkContractStore): WorkContractStore
412
448
  workKind: legacy.workKind ?? 'repository_change',
413
449
  dispatchState: legacy.dispatchState ?? inferredDispatchState(status),
414
450
  evidenceState: legacy.evidenceState ?? inferredEvidenceState(status),
451
+ suggestedNextActions: suggestedActionsForStatus(status, legacy.suggestedNextActions ?? []),
415
452
  reconciliations: legacy.reconciliations ?? [],
416
453
  driver: (legacy.driver as unknown as { preferred?: string } | undefined)?.preferred === 'codex_worker'
417
454
  ? { ...legacy.driver, preferred: 'external_controller', allowWorker: false }
@@ -524,7 +561,7 @@ export function createWorkContract(options: WorkContractStoreOptions, input: Cre
524
561
  if (input.status === 'completed' || input.completionReceipt || input.completionOutcome) {
525
562
  throw new Error('WORK_COMPLETION_REQUIRES_RECORD_API');
526
563
  }
527
- return withWorkContractStoreWrite(options, () => {
564
+ const create = (): WorkContract => {
528
565
  const at = input.createdAt ?? input.updatedAt ?? nowIso(options);
529
566
  const workId = sanitizeFileComponent(input.workId);
530
567
  const contract: WorkContract = validateWorkSemantics({
@@ -544,6 +581,8 @@ export function createWorkContract(options: WorkContractStoreOptions, input: Cre
544
581
  constraints: input.constraints ?? { requireHandoffOnAmbiguity: true },
545
582
  risk: input.risk ?? 'medium',
546
583
  workKind: input.workKind ?? 'repository_change',
584
+ lifecycleRole: input.lifecycleRole ?? 'primary',
585
+ parentWorkId: input.parentWorkId?.trim() || undefined,
547
586
  dispatchState: input.dispatchState ?? inferredDispatchState(input.status ?? 'open'),
548
587
  evidenceState: input.evidenceState ?? inferredEvidenceState(input.status ?? 'open'),
549
588
  completionOutcome: input.completionOutcome,
@@ -566,6 +605,12 @@ export function createWorkContract(options: WorkContractStoreOptions, input: Cre
566
605
  planStepId: input.planStepId,
567
606
  planSourceRevision: input.planSourceRevision,
568
607
  scopeSummary: input.scopeSummary?.slice(0, 1_000),
608
+ scopeEvidence: input.scopeEvidence ? {
609
+ initialLikelyPaths: [...new Set(input.scopeEvidence.initialLikelyPaths)].slice(0, 100),
610
+ inspectedPaths: [...new Set(input.scopeEvidence.inspectedPaths)].slice(0, 500),
611
+ actualChangedPaths: [...new Set(input.scopeEvidence.actualChangedPaths)].slice(0, 500),
612
+ recordedAt: input.scopeEvidence.recordedAt,
613
+ } : undefined,
569
614
  allowedPaths: (input.allowedPaths ?? []).slice(0, 50),
570
615
  forbiddenPaths: (input.forbiddenPaths ?? []).slice(0, 50),
571
616
  checks: (input.checks ?? []).slice(0, 30),
@@ -599,6 +644,23 @@ export function createWorkContract(options: WorkContractStoreOptions, input: Cre
599
644
  submittedOperation: input.submittedOperation,
600
645
  });
601
646
 
647
+ if (sqliteBacked(options)) {
648
+ withControlPlaneTransaction(options.controllerHome, (database) => {
649
+ if (readControlPlaneRecordWithinTransaction<WorkContract>(database, 'work_contract', options.repoId, contract.workId)) {
650
+ throw new Error(`work contract already exists: ${contract.workId}`);
651
+ }
652
+ writeControlPlaneRecordWithinTransaction(database, {
653
+ namespace: 'work_contract',
654
+ scope: options.repoId,
655
+ key: contract.workId,
656
+ schemaVersion: 2,
657
+ value: contract,
658
+ action: 'work_contract_created',
659
+ expectedRevision: null,
660
+ });
661
+ });
662
+ return contract;
663
+ }
602
664
  const store = readWorkContractStore(options);
603
665
  if (store.contracts.some((existing) => existing.workId === contract.workId)) {
604
666
  throw new Error(`work contract already exists: ${contract.workId}`);
@@ -610,7 +672,8 @@ export function createWorkContract(options: WorkContractStoreOptions, input: Cre
610
672
  };
611
673
  writeWorkContractStore(options, nextStore);
612
674
  return contract;
613
- });
675
+ };
676
+ return create();
614
677
  }
615
678
 
616
679
  interface WorkRequestIndexRecord {
@@ -628,25 +691,6 @@ function workRequestIndexPath(controllerHome: string, requestId: string): string
628
691
  return join(root, `${hash}.json`);
629
692
  }
630
693
 
631
- export interface AcceptSubmittedWorkInput {
632
- requestId: string;
633
- repoId: string;
634
- semanticKey: string;
635
- operation: SubmittedWorkOperation;
636
- objective?: string;
637
- mode?: WorkContract['mode'];
638
- requestedBy?: WorkContract['requestedBy'];
639
- principalId?: string;
640
- controllerInstanceId?: string;
641
- workKind?: WorkKind;
642
- risk?: WorkRisk;
643
- allowedPaths?: string[];
644
- forbiddenPaths?: string[];
645
- checks?: string[];
646
- acceptanceCriteria?: string[];
647
- constraints?: WorkContract['constraints'];
648
- }
649
-
650
694
  export function getWorkContractByRequestId(
651
695
  controllerHome: string,
652
696
  requestId: string,
@@ -667,8 +711,8 @@ export function getWorkContractByRequestId(
667
711
  }
668
712
 
669
713
  /**
670
- * Accept one work_submit operation as a WorkContract without creating ExecutionJobs.
671
- * Same controllerHome + requestId + semanticKey returns the original contract.
714
+ * Create or reuse the execution-child Work used by typed callers. This is not
715
+ * a generic MCP submission authority and never creates an ExecutionJob.
672
716
  */
673
717
  export function acceptSubmittedWorkContract(
674
718
  controllerHome: string,
@@ -676,46 +720,39 @@ export function acceptSubmittedWorkContract(
676
720
  options: WorkContractStoreOptions = {},
677
721
  ): { contract: WorkContract; deduplicated: boolean } {
678
722
  const home = ensureControllerHome(controllerHome);
679
- const normalizedRequestId = input.requestId.trim();
680
- if (!normalizedRequestId) {
681
- throw new Error('INVALID_ARGUMENT: work_submit is missing required argument(s): request_id');
682
- }
683
- const normalizedSemanticKey = input.semanticKey.trim();
684
- if (!normalizedSemanticKey) {
685
- throw new Error('INVALID_ARGUMENT: work_submit requires a semantic operation key');
686
- }
687
- if (!input.repoId.trim()) {
688
- throw new Error('INVALID_ARGUMENT: work_submit is missing required argument(s): repo_id');
723
+ const requestId = input.requestId.trim();
724
+ const semanticKey = input.semanticKey.trim();
725
+ if (!requestId) throw new Error('INVALID_ARGUMENT: typed execution is missing request_id');
726
+ if (!semanticKey) throw new Error('INVALID_ARGUMENT: typed execution requires a semantic operation key');
727
+ if (!input.repoId.trim()) throw new Error('INVALID_ARGUMENT: typed execution is missing repo_id');
728
+ if (!input.operation?.name?.trim()) throw new Error('INVALID_ARGUMENT: typed execution is missing operation');
729
+ const parentWorkId = input.parentWorkId?.trim() || undefined;
730
+ if (parentWorkId) {
731
+ const parent = getWorkContract({ controllerHome: home, repoId: input.repoId, now: options.now }, parentWorkId);
732
+ if (!parent) throw new Error(`PARENT_WORK_NOT_FOUND: ${parentWorkId}`);
733
+ if (isTerminalWorkContractStatus(parent.status)) throw new Error(`PARENT_WORK_TERMINAL: ${parentWorkId}:${parent.status}`);
734
+ if ((parent.lifecycleRole ?? 'primary') !== 'primary') throw new Error(`PARENT_WORK_NOT_PRIMARY: ${parentWorkId}`);
689
735
  }
690
- if (!input.operation?.name?.trim()) {
691
- throw new Error('INVALID_ARGUMENT: work_submit is missing required argument(s): operation');
692
- }
693
-
694
- const requestLockId = createHash('sha256').update(normalizedRequestId).digest('hex').slice(0, 24);
695
- return withControllerLock(home, { scope: 'global', resource: `work-request-${requestLockId}` }, `accept-work:${normalizedRequestId}`, () => {
696
- const recordPath = workRequestIndexPath(home, normalizedRequestId);
736
+ const lockId = createHash('sha256').update(requestId).digest('hex').slice(0, 24);
737
+ return withControllerLock(home, { scope: 'global', resource: `work-request-${lockId}` }, `accept-work:${requestId}`, () => {
738
+ const recordPath = workRequestIndexPath(home, requestId);
697
739
  if (existsSync(recordPath)) {
698
740
  const record = readJsonFile<WorkRequestIndexRecord>(recordPath);
699
- if (record.repoId !== input.repoId) {
700
- throw new Error(`REQUEST_ID_REPO_CONFLICT: ${normalizedRequestId} already belongs to repository ${record.repoId}`);
701
- }
702
- if (record.semanticKey !== normalizedSemanticKey) {
703
- throw new Error(`REQUEST_ID_CONFLICT: ${normalizedRequestId} already belongs to ${record.semanticKey}`);
704
- }
741
+ if (record.repoId !== input.repoId) throw new Error(`REQUEST_ID_REPO_CONFLICT: ${requestId} already belongs to repository ${record.repoId}`);
742
+ if (record.semanticKey !== semanticKey) throw new Error(`REQUEST_ID_CONFLICT: ${requestId} already belongs to ${record.semanticKey}`);
705
743
  const existing = getWorkContract({ controllerHome: home, repoId: record.repoId, now: options.now }, record.workId);
706
- if (!existing) {
707
- throw new Error(`WORK_ACCEPTANCE_LOST: request ${normalizedRequestId} is indexed without a readable WorkContract`);
708
- }
744
+ if (!existing) throw new Error(`WORK_ACCEPTANCE_LOST: request ${requestId} is indexed without a readable WorkContract`);
709
745
  return { contract: existing, deduplicated: true };
710
746
  }
711
-
712
747
  const contract = createWorkContract({ controllerHome: home, repoId: input.repoId, now: options.now }, {
713
748
  workId: `WORK-${Date.now()}-${randomUUID().slice(0, 8)}`,
714
749
  repoId: input.repoId,
715
750
  principalId: input.principalId,
716
751
  controllerInstanceId: input.controllerInstanceId,
717
752
  mode: input.mode ?? 'direct_control',
718
- objective: (input.objective ?? `Accepted operation ${input.operation.name}`).slice(0, 2_000),
753
+ lifecycleRole: 'execution_child',
754
+ parentWorkId,
755
+ objective: (input.objective ?? `Typed operation ${input.operation.name}`).slice(0, 2_000),
719
756
  acceptanceCriteria: input.acceptanceCriteria ?? [],
720
757
  workKind: input.workKind,
721
758
  risk: input.risk,
@@ -725,13 +762,9 @@ export function acceptSubmittedWorkContract(
725
762
  checks: input.checks ?? [],
726
763
  requestedBy: input.requestedBy ?? 'chatgpt',
727
764
  status: 'open',
728
- requestId: normalizedRequestId,
765
+ requestId,
729
766
  submittedOperation: input.operation,
730
- driver: {
731
- preferred: 'external_controller',
732
- allowWorker: false,
733
- allowDirectEdit: input.operation.mode === 'readonly' || input.operation.mode === 'mutating',
734
- },
767
+ driver: { preferred: 'external_controller', allowWorker: false, allowDirectEdit: input.operation.mode === 'readonly' || input.operation.mode === 'mutating' },
735
768
  suggestedNextActions: [{
736
769
  label: 'Claim controller ownership',
737
770
  tool: 'rh_work',
@@ -741,15 +774,7 @@ export function acceptSubmittedWorkContract(
741
774
  reason: 'Claim this Work before launching an external SuperController or Process Runtime command.',
742
775
  }],
743
776
  });
744
-
745
- writeJsonAtomic(recordPath, {
746
- requestId: normalizedRequestId,
747
- repoId: input.repoId,
748
- workId: contract.workId,
749
- semanticKey: normalizedSemanticKey,
750
- createdAt: contract.createdAt,
751
- } satisfies WorkRequestIndexRecord);
752
-
777
+ writeJsonAtomic(recordPath, { requestId, repoId: input.repoId, workId: contract.workId, semanticKey, createdAt: contract.createdAt } satisfies WorkRequestIndexRecord);
753
778
  return { contract, deduplicated: false };
754
779
  });
755
780
  }
@@ -936,6 +961,7 @@ function updateWorkContractInternal(
936
961
  patch: Partial<Omit<WorkContract, 'schemaVersion' | 'workId' | 'repoId' | 'createdAt'>>,
937
962
  allowCompletionWrite: boolean,
938
963
  allowPhaseWrite = false,
964
+ allowRetainedCancelledResume = false,
939
965
  ): WorkContract {
940
966
  return withWorkContractStoreWrite(options, () => {
941
967
  const sanitizedId = sanitizeFileComponent(workId);
@@ -993,13 +1019,13 @@ function updateWorkContractInternal(
993
1019
  completionOutcome: patch.completionOutcome ?? current.completionOutcome,
994
1020
  evidenceRefs: (patch.evidenceRefs ?? current.evidenceRefs).slice(0, current.evidencePolicy.maxEvidenceRefs),
995
1021
  handoffRefs: (patch.handoffRefs ?? current.handoffRefs).slice(0, 20),
996
- suggestedNextActions: (patch.suggestedNextActions ?? current.suggestedNextActions).slice(0, 8),
1022
+ suggestedNextActions: suggestedActionsForStatus(patch.status ?? current.status, patch.suggestedNextActions ?? current.suggestedNextActions),
997
1023
  policyDecisions: (patch.policyDecisions ?? current.policyDecisions).slice(0, 20),
998
1024
  checkRefs: (patch.checkRefs ?? current.checkRefs).slice(0, 50),
999
1025
  reconciliations: (patch.reconciliations ?? current.reconciliations ?? []).slice(0, 20),
1000
1026
  objective: (patch.objective ?? current.objective).slice(0, 2_000),
1001
1027
  continuationPrompt: (patch.continuationPrompt ?? current.continuationPrompt)?.slice(0, 2_000),
1002
- }));
1028
+ }), { allowRetainedCancelledResume });
1003
1029
  const contracts = [...store.contracts];
1004
1030
  contracts[index] = next;
1005
1031
  writeWorkContractStore(options, { schemaVersion: 2, updatedAt: at, contracts });
@@ -1015,6 +1041,83 @@ export function updateWorkContract(
1015
1041
  return updateWorkContractInternal(options, workId, patch, false);
1016
1042
  }
1017
1043
 
1044
+ /**
1045
+ * Reopen one explicitly retained cancelled repository Work after the facade has
1046
+ * reauthenticated the same principal and revalidated physical Work ownership.
1047
+ * Generic mutation APIs intentionally cannot perform terminal -> running.
1048
+ */
1049
+ export function resumeRetainedCancelledWorkContract(
1050
+ options: WorkContractStoreOptions,
1051
+ workId: string,
1052
+ input: {
1053
+ principalId: string;
1054
+ controllerInstanceId: string;
1055
+ summary: string;
1056
+ checkoutId?: string;
1057
+ worktreeRef?: string;
1058
+ },
1059
+ ): WorkContract {
1060
+ const current = getWorkContract(options, workId);
1061
+ if (!current) throw new Error(`work contract not found: ${workId}`);
1062
+ if (current.status !== 'cancelled' || current.dispatchState !== 'terminal' || current.phase !== 'cleanup') {
1063
+ throw new Error(`WORK_CANCELLED_RESUME_STATUS_INVALID: ${workId}`);
1064
+ }
1065
+ if (current.workKind !== 'repository_change') throw new Error(`WORK_CANCELLED_RESUME_KIND_INVALID: ${workId}`);
1066
+ if (current.completionReceipt || current.completionOutcome) throw new Error(`WORK_CANCELLED_RESUME_COMPLETION_CONFLICT: ${workId}`);
1067
+ if (current.phaseEvidence.cleanup.state !== 'skipped' || current.phaseEvidence.cleanup.source !== 'recorded') {
1068
+ throw new Error(`WORK_CANCELLED_RESUME_HISTORY_AMBIGUOUS: ${workId}`);
1069
+ }
1070
+ const originalPrincipal = current.principalId?.trim();
1071
+ if (!originalPrincipal || originalPrincipal !== input.principalId.trim()) {
1072
+ throw new Error(`WORK_CANCELLED_RESUME_PRINCIPAL_MISMATCH: ${workId}`);
1073
+ }
1074
+ const at = nowIso(options);
1075
+ const evidenceRefs = [...current.evidenceRefs, {
1076
+ title: 'explicit current-user Work reauthorization',
1077
+ summary: input.summary.trim().slice(0, 1_000),
1078
+ detailLevel: 'summary' as const,
1079
+ }].slice(-current.evidencePolicy.maxEvidenceRefs);
1080
+ const phaseEvidence = transitionPhaseEvidence({ ...current, evidenceRefs }, 'implementation', {
1081
+ status: 'running',
1082
+ summary: input.summary,
1083
+ evidenceRefs,
1084
+ recordedAt: at,
1085
+ source: 'recorded',
1086
+ });
1087
+ return updateWorkContractInternal(options, workId, {
1088
+ status: 'running',
1089
+ phase: 'implementation',
1090
+ phaseEvidence,
1091
+ dispatchState: 'running',
1092
+ evidenceRefs,
1093
+ checkoutId: input.checkoutId ?? current.checkoutId,
1094
+ worktreeRef: input.worktreeRef ?? current.worktreeRef,
1095
+ controllerInstanceId: input.controllerInstanceId,
1096
+ continuationPrompt: input.summary,
1097
+ }, false, true, true);
1098
+ }
1099
+
1100
+ /** Merge non-authoritative discovery/change evidence without changing policy fences. */
1101
+ export function recordWorkScopeEvidence(
1102
+ options: WorkContractStoreOptions,
1103
+ workId: string,
1104
+ input: { initialLikelyPaths?: string[]; inspectedPaths?: string[]; actualChangedPaths?: string[] },
1105
+ ): WorkContract {
1106
+ const current = getWorkContract(options, workId);
1107
+ if (!current) throw new Error(`work contract not found: ${workId}`);
1108
+ const previous = current.scopeEvidence ?? {
1109
+ initialLikelyPaths: [], inspectedPaths: [], actualChangedPaths: [], recordedAt: current.createdAt,
1110
+ };
1111
+ return updateWorkContract(options, workId, {
1112
+ scopeEvidence: {
1113
+ initialLikelyPaths: [...new Set([...previous.initialLikelyPaths, ...(input.initialLikelyPaths ?? [])])].slice(0, 100),
1114
+ inspectedPaths: [...new Set([...previous.inspectedPaths, ...(input.inspectedPaths ?? [])])].slice(0, 500),
1115
+ actualChangedPaths: [...new Set([...previous.actualChangedPaths, ...(input.actualChangedPaths ?? [])])].slice(0, 500),
1116
+ recordedAt: nowIso(options),
1117
+ },
1118
+ });
1119
+ }
1120
+
1018
1121
  export function transitionWorkContractPhase(
1019
1122
  options: WorkContractStoreOptions,
1020
1123
  workId: string,
@@ -1097,6 +1200,9 @@ export function recordWorkCompletionReceipt(
1097
1200
  return current;
1098
1201
  }
1099
1202
  const recordedAt = receipt.recordedAt;
1203
+ const receiptChangedPaths = isRepositoryCompletionReceipt(receipt) || isDirectEditWorkCompletionReceipt(receipt)
1204
+ ? receipt.changedPaths
1205
+ : [];
1100
1206
  const phaseEvidence = Object.fromEntries((['implementation', 'verification', 'delivery', 'cleanup'] as WorkPhase[]).map((phase) => [phase, {
1101
1207
  state: 'satisfied',
1102
1208
  source: 'recorded',
@@ -1114,5 +1220,30 @@ export function recordWorkCompletionReceipt(
1114
1220
  completionOutcome,
1115
1221
  ...(completionWorkKind ? { workKind: completionWorkKind } : {}),
1116
1222
  completionReceipt: receipt,
1223
+ scopeEvidence: {
1224
+ initialLikelyPaths: current.scopeEvidence?.initialLikelyPaths ?? current.allowedPaths,
1225
+ inspectedPaths: current.scopeEvidence?.inspectedPaths ?? [],
1226
+ actualChangedPaths: [...new Set(receiptChangedPaths)].slice(0, 500),
1227
+ recordedAt,
1228
+ },
1117
1229
  }, true, true);
1118
1230
  }
1231
+ export interface AcceptSubmittedWorkInput {
1232
+ requestId: string;
1233
+ repoId: string;
1234
+ parentWorkId?: string;
1235
+ semanticKey: string;
1236
+ operation: SubmittedWorkOperation;
1237
+ objective?: string;
1238
+ mode?: WorkContract['mode'];
1239
+ requestedBy?: WorkContract['requestedBy'];
1240
+ principalId?: string;
1241
+ controllerInstanceId?: string;
1242
+ workKind?: WorkKind;
1243
+ risk?: WorkRisk;
1244
+ allowedPaths?: string[];
1245
+ forbiddenPaths?: string[];
1246
+ checks?: string[];
1247
+ acceptanceCriteria?: string[];
1248
+ constraints?: WorkContract['constraints'];
1249
+ }