@moretea-labs/forge 1.7.2 → 1.8.1

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 (900) hide show
  1. package/AGENTS.md +121 -62
  2. package/CHANGELOG.md +40 -0
  3. package/CLAUDE.md +57 -62
  4. package/CONTRIBUTING.md +3 -2
  5. package/README.md +2 -2
  6. package/SKILL.md +0 -1
  7. package/THIRD_PARTY_NOTICES.md +5 -2
  8. package/adapters/browser/index.ts +2 -0
  9. package/adapters/chatgpt/browser-delivery-host.ts +77 -0
  10. package/adapters/chatgpt/browser-delivery-runtime.ts +1256 -0
  11. package/adapters/chatgpt/controller-binding-store.ts +134 -0
  12. package/adapters/chatgpt/controller-round-host.ts +68 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +99 -0
  14. package/adapters/chatgpt/provider-delivery.ts +340 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +218 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  18. package/adapters/computer/chatgpt-target-router.ts +103 -0
  19. package/adapters/computer/chrome-extension/background.js +385 -0
  20. package/adapters/computer/chrome-extension/content.js +378 -0
  21. package/adapters/computer/chrome-extension/core.js +24 -0
  22. package/adapters/computer/chrome-extension/manifest.json +30 -0
  23. package/adapters/computer/desktop-operator-contract.ts +7 -0
  24. package/adapters/computer/desktop-operator-discovery.ts +181 -0
  25. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  26. package/adapters/computer/desktop-operator-provider.ts +312 -0
  27. package/adapters/computer/index.ts +1 -0
  28. package/adapters/computer/interaction-target-authority.ts +1068 -0
  29. package/adapters/controller-process/binding-store.ts +34 -0
  30. package/adapters/controller-process/controller-host.ts +26 -0
  31. package/adapters/mcp/access-mode.ts +132 -0
  32. package/adapters/mcp/audit.ts +32 -0
  33. package/adapters/mcp/auth.ts +406 -0
  34. package/adapters/mcp/instructions.ts +37 -0
  35. package/adapters/mcp/legacy-context.ts +18 -0
  36. package/adapters/mcp/multi-repository.ts +421 -0
  37. package/adapters/mcp/oauth.ts +464 -0
  38. package/adapters/mcp/paths.ts +143 -0
  39. package/adapters/mcp/policy.ts +232 -0
  40. package/adapters/mcp/proxy-env.ts +62 -0
  41. package/adapters/mcp/readiness-projection.ts +24 -0
  42. package/adapters/mcp/redaction.ts +21 -0
  43. package/adapters/mcp/repo.ts +24 -0
  44. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  45. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  46. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  47. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  48. package/adapters/mcp/runtime-gateway/context-adapter.ts +1167 -0
  49. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  50. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +206 -0
  51. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  52. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +130 -0
  53. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  54. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  55. package/adapters/mcp/runtime-gateway/execution-tools.ts +173 -0
  56. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  57. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +412 -0
  58. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  59. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  60. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  61. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  62. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +380 -0
  63. package/adapters/mcp/runtime-gateway/process-tools.ts +457 -0
  64. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +88 -0
  65. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +285 -0
  66. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  67. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  68. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +79 -0
  69. package/adapters/mcp/runtime-gateway/result-adapter.ts +114 -0
  70. package/adapters/mcp/runtime-gateway/router.ts +242 -0
  71. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  72. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1200 -0
  73. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  74. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +574 -0
  75. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  76. package/adapters/mcp/runtime-gateway/runtime-tools.ts +78 -0
  77. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +187 -0
  78. package/adapters/mcp/runtime-gateway/shared-adapter.ts +45 -0
  79. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +962 -0
  80. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  81. package/adapters/mcp/runtime-gateway/work-adapter.ts +325 -0
  82. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  83. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  84. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  85. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  86. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  87. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  88. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  89. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  90. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  91. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  92. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  93. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  94. package/adapters/mcp/server.ts +1092 -0
  95. package/adapters/mcp/setup.ts +789 -0
  96. package/adapters/mcp/tool-capability-cache.ts +87 -0
  97. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  98. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5139 -0
  99. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +145 -0
  100. package/adapters/mcp/tool-mapping/repository-tools.ts +1682 -0
  101. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  102. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  103. package/adapters/mcp/toolset-names.ts +79 -0
  104. package/adapters/mcp/toolset-selection.ts +41 -0
  105. package/adapters/mcp/toolset.ts +337 -0
  106. package/adapters/mcp/transports/http-observation.ts +365 -0
  107. package/adapters/mcp/transports/http.ts +933 -0
  108. package/adapters/mcp/transports/oauth-http.ts +322 -0
  109. package/adapters/mcp/transports/session-registry.ts +519 -0
  110. package/adapters/mcp/transports/stdio.ts +8 -0
  111. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +223 -0
  112. package/adapters/mcp/types.ts +41 -0
  113. package/assets/AGENTS.md +2 -16
  114. package/assets/CLAUDE.md +2 -16
  115. package/assets/hooks/AGENTS.md +3 -14
  116. package/assets/hooks/CLAUDE.md +3 -14
  117. package/assets/hooks/hook-input.sh +9 -2
  118. package/assets/hooks/lib/workflow-state.sh +28 -10
  119. package/assets/hooks/prompt-guard.sh +63 -37
  120. package/assets/hooks/security-sentinel.sh +2 -1
  121. package/assets/hooks/session-start-context.sh +7 -5
  122. package/assets/hooks/stop-orchestrator.sh +13 -3
  123. package/assets/initializer-question-pack.v4.json +11 -7
  124. package/assets/partials/05-workflow.partial.md +18 -73
  125. package/assets/partials/07-footer.partial.md +1 -1
  126. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  127. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  128. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  129. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  130. package/assets/plan-map.json +11 -11
  131. package/assets/plugin-registry.v1.json +11 -3
  132. package/assets/plugin-trust-policy.v1.json +6 -0
  133. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  134. package/assets/recovery/forge-wsl-rescue +271 -0
  135. package/assets/reference-configs/agentic-development-flow.md +25 -0
  136. package/assets/reference-configs/development-protocol.md +11 -15
  137. package/assets/reference-configs/document-generation.md +1 -1
  138. package/assets/reference-configs/global-working-rules.md +28 -1
  139. package/assets/reference-configs/harness-overview.md +22 -112
  140. package/assets/reference-configs/hook-operations.md +1 -1
  141. package/assets/reference-configs/sprint-contracts.md +2 -2
  142. package/assets/skill-version.json +2 -2
  143. package/assets/templates/helpers/architecture-event.ts +3 -34
  144. package/assets/templates/helpers/architecture-queue.sh +61 -10
  145. package/assets/templates/helpers/archive-workflow.sh +90 -180
  146. package/assets/templates/helpers/capability-config.ts +8 -11
  147. package/assets/templates/helpers/capability-resolver.ts +37 -19
  148. package/assets/templates/helpers/capture-plan.sh +2 -2
  149. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  150. package/assets/templates/helpers/check-task-sync.sh +82 -11
  151. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  152. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  153. package/assets/templates/helpers/contract-worktree.sh +26 -24
  154. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  155. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  156. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  157. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  158. package/assets/templates/helpers/new-plan.sh +2 -2
  159. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  160. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  161. package/assets/templates/helpers/switch-plan.sh +1 -1
  162. package/assets/templates/helpers/workflow-contract.ts +1 -0
  163. package/assets/workflow-contract.v1.json +63 -171
  164. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  165. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  166. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  167. package/docs/architecture/CURRENT.md +233 -28
  168. package/docs/architecture/EVOLUTION.md +13 -0
  169. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  170. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  171. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  172. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  173. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  174. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  175. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +37 -0
  176. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +311 -0
  177. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  178. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  179. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  180. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  181. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  182. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  183. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  184. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  185. package/docs/architecture/domains/verification.md +8 -0
  186. package/docs/architecture/domains/workflow-engine.md +7 -1
  187. package/docs/architecture/index.md +3 -3
  188. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  189. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  190. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  191. package/docs/forge-plugin-management.md +20 -4
  192. package/docs/forge-runtime-self-healing-loop.md +2 -0
  193. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  194. package/docs/operations/controller-browser-plugin.md +3 -1
  195. package/docs/operations/controller-performance-and-502.md +17 -0
  196. package/docs/operations/controller-reliability-runbook.md +2 -2
  197. package/docs/operations/features.md +1 -1
  198. package/docs/operations/features.zh-CN.md +1 -1
  199. package/docs/operations/homebrew.md +2 -2
  200. package/docs/operations/mcp-tool-exposure.md +2 -0
  201. package/docs/operations/platform-support.md +16 -8
  202. package/docs/operations/platform-support.zh-CN.md +2 -2
  203. package/docs/operations/releasing.md +2 -0
  204. package/docs/operations/standalone-disaster-recovery.md +38 -5
  205. package/docs/operations/uu-remote-rescue.md +41 -0
  206. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  207. package/docs/public-usage-guide.md +1 -1
  208. package/docs/public-usage-guide.zh-CN.md +1 -1
  209. package/docs/tutorials/01-install-and-start.md +1 -1
  210. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  211. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  212. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  213. package/docs/wiki/Implementation.md +1 -1
  214. package/package.json +46 -26
  215. package/packages/kernel/cognition/api/index.ts +6 -0
  216. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  217. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  218. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  219. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  220. package/packages/kernel/cognition/domain/learning.ts +106 -0
  221. package/packages/kernel/cognition/domain/memory.ts +254 -0
  222. package/packages/kernel/controller/api/index.ts +3 -0
  223. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  224. package/packages/kernel/controller/application/controller-round-service.ts +41 -0
  225. package/packages/kernel/controller/application/controller-service.ts +30 -0
  226. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +529 -0
  227. package/packages/kernel/controller/domain/controller-round.ts +110 -0
  228. package/packages/kernel/controller/domain/execution-quality.ts +135 -0
  229. package/packages/kernel/controller/domain/types.ts +56 -0
  230. package/packages/kernel/controller/index.ts +1 -0
  231. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  232. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1970 -0
  233. package/packages/kernel/controller/infrastructure/controller-session-store.ts +804 -0
  234. package/packages/kernel/controller/ports/controller-host.ts +18 -0
  235. package/packages/kernel/goal/api/index.ts +1 -0
  236. package/packages/kernel/goal/domain/types.ts +40 -0
  237. package/packages/kernel/goal/index.ts +1 -0
  238. package/packages/kernel/identity/api/index.ts +15 -0
  239. package/packages/kernel/identity/application/identity-service.ts +67 -0
  240. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  241. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  242. package/packages/kernel/identity/domain/grant.ts +72 -0
  243. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  244. package/packages/kernel/identity/domain/scope.ts +63 -0
  245. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  246. package/packages/kernel/identity/domain/types.ts +44 -0
  247. package/packages/kernel/identity/domain/user-request.ts +93 -0
  248. package/packages/kernel/identity/domain/workspace.ts +56 -0
  249. package/packages/kernel/identity/index.ts +1 -0
  250. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  251. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  252. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  253. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  254. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  255. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  256. package/packages/kernel/memory/api/index.ts +5 -0
  257. package/packages/kernel/memory/application/experience-service.ts +118 -0
  258. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  259. package/packages/kernel/memory/domain/experience.ts +103 -0
  260. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  261. package/packages/kernel/memory/index.ts +1 -0
  262. package/packages/kernel/scheduler/api/index.ts +4 -0
  263. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  264. package/packages/kernel/scheduler/application/schedule-service.ts +27 -0
  265. package/packages/kernel/scheduler/application/settlement.ts +182 -0
  266. package/packages/kernel/scheduler/domain/schedule.ts +236 -0
  267. package/packages/kernel/scheduler/index.ts +1 -0
  268. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +805 -0
  269. package/packages/kernel/work/api/index.ts +16 -0
  270. package/packages/kernel/work/application/work-service.ts +123 -0
  271. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  272. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  273. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  274. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  275. package/packages/kernel/work/domain/engineering-design.ts +286 -0
  276. package/packages/kernel/work/domain/engineering-profile.ts +238 -0
  277. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  278. package/packages/kernel/work/domain/execution-concurrency.ts +247 -0
  279. package/packages/kernel/work/domain/execution-snapshot.ts +2 -0
  280. package/packages/kernel/work/domain/implementation-review.ts +193 -0
  281. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  282. package/packages/kernel/work/domain/project-engineering-contract.ts +224 -0
  283. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  284. package/{src/runtime/control-plane/facade/work-state-machine.ts → packages/kernel/work/domain/state-machine.ts} +37 -66
  285. package/packages/kernel/work/domain/types.ts +534 -0
  286. package/packages/kernel/work/index.ts +1 -0
  287. package/packages/kernel/work/infrastructure/work-contract-store.ts +1967 -0
  288. package/packages/kernel/work/ports/work-contract-store.ts +21 -0
  289. package/packages/plugin-runtime/browser/index.ts +3 -0
  290. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  291. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  292. package/packages/plugin-runtime/browser/session-authority.ts +62 -0
  293. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  294. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  295. package/packages/plugin-runtime/computer/index.ts +8 -0
  296. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  297. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  298. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  299. package/packages/plugin-runtime/computer/provider.ts +19 -0
  300. package/packages/plugin-runtime/computer/target-authority.ts +215 -0
  301. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  302. package/packages/plugin-runtime/external/index.ts +1 -0
  303. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +581 -0
  304. package/packages/plugin-runtime/index.ts +3 -0
  305. package/packages/protocols/browser/index.ts +1 -0
  306. package/packages/protocols/browser/session.ts +98 -0
  307. package/packages/protocols/computer/contract.ts +225 -0
  308. package/packages/protocols/computer/index.ts +1 -0
  309. package/packages/protocols/failure/contract.ts +158 -0
  310. package/packages/protocols/failure/index.ts +1 -0
  311. package/packages/protocols/handoff/index.ts +1 -0
  312. package/packages/protocols/handoff/status.ts +22 -0
  313. package/packages/protocols/mcp/execution-context.ts +11 -0
  314. package/packages/protocols/mcp/index.ts +2 -0
  315. package/packages/protocols/mcp/tool-contract.ts +42 -0
  316. package/packages/workflow-runtime/api/index.ts +2 -0
  317. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  318. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  319. package/packages/workflow-runtime/index.ts +1 -0
  320. package/scripts/activate-source-baseline.ts +7 -9
  321. package/scripts/assemble-template.ts +1 -1
  322. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  323. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  324. package/scripts/canary-controller-semantic-wait.ts +183 -0
  325. package/scripts/canary-global-hook.sh +2 -2
  326. package/scripts/check-mcp-compatibility.ts +85 -7
  327. package/scripts/check-platform-support.mjs +28 -2
  328. package/scripts/check-public-docs.sh +1 -1
  329. package/scripts/check-repository-hygiene.mjs +92 -0
  330. package/scripts/check-runtime-architecture.mjs +1643 -127
  331. package/scripts/check-source-duplication.mjs +9 -1
  332. package/scripts/controller-ngrok-rotation.sh +5 -4
  333. package/scripts/create-project-dirs.sh +10 -74
  334. package/scripts/forge-local-recovery-helper.mjs +282 -0
  335. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  336. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  337. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  338. package/scripts/hook-shim.sh +2 -2
  339. package/scripts/init-project.sh +12 -70
  340. package/scripts/install-desktop-operator-registration.ts +4 -2
  341. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  342. package/scripts/install-standalone-recovery.ts +122 -10
  343. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  344. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  345. package/scripts/lib/controller-home-env.sh +6 -3
  346. package/scripts/lib/project-init-lib.sh +42 -582
  347. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  348. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  349. package/scripts/open-source-audit-allowlist.txt +11 -1
  350. package/scripts/portability-bindings.mjs +70 -0
  351. package/scripts/run-bounded-command.mjs +60 -0
  352. package/scripts/run-bun-test-file.ts +22 -6
  353. package/scripts/run-governed-gate.ts +4 -2
  354. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  355. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  356. package/scripts/smoke-mcp-http-runtime.ts +6 -1
  357. package/scripts/smoke-runtime-recovery.ts +13 -11
  358. package/scripts/smoke-schedule-engine.ts +24 -16
  359. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  360. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  361. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  362. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  363. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  364. package/scripts/smoke-workflow-supervisor.ts +103 -0
  365. package/scripts/test-governance.ts +51 -2
  366. package/scripts/verify-forge-runtime.sh +2 -1
  367. package/src/cli/agent-jobs/executor-health.ts +11 -34
  368. package/src/cli/agent-jobs/integration.ts +18 -49
  369. package/src/cli/agent-jobs/job-manager.ts +17 -4
  370. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  371. package/src/cli/chatgpt-browser/binding.ts +20 -10
  372. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  373. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  374. package/src/cli/chatgpt-browser/engine.ts +10 -11
  375. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  376. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  377. package/src/cli/chatgpt-browser/types.ts +1 -0
  378. package/src/cli/commands/bootstrap-control.ts +334 -0
  379. package/src/cli/commands/brain-assistant.ts +52 -0
  380. package/src/cli/commands/brain.ts +191 -40
  381. package/src/cli/commands/chatgpt.ts +32 -67
  382. package/src/cli/commands/computer.ts +437 -0
  383. package/src/cli/commands/doctor.ts +18 -1
  384. package/src/cli/commands/init-hook.ts +71 -61
  385. package/src/cli/commands/init.ts +3 -1
  386. package/src/cli/commands/mcp.ts +16 -10
  387. package/src/cli/commands/plugin.ts +38 -12
  388. package/src/cli/commands/recovery.ts +507 -80
  389. package/src/cli/commands/runtime.ts +12 -7
  390. package/src/cli/commands/setup-profile.ts +97 -39
  391. package/src/cli/commands/status.ts +2 -1
  392. package/src/cli/controller/check-runner.ts +500 -100
  393. package/src/cli/controller/completion-backlog.ts +3 -3
  394. package/src/cli/controller/completion-orchestrator.ts +4 -43
  395. package/src/cli/controller/completion-target.ts +3 -72
  396. package/src/cli/controller/context/source-materializer.ts +81 -100
  397. package/src/cli/controller/context/types.ts +7 -1
  398. package/src/cli/controller/context-pack.ts +28 -6
  399. package/src/cli/controller/execution-completion.ts +4 -7
  400. package/src/cli/controller/execution-policy.ts +36 -153
  401. package/src/cli/controller/governance.ts +2 -3
  402. package/src/cli/controller/issue-store.ts +4 -99
  403. package/src/cli/controller/launch-agents.ts +59 -18
  404. package/src/cli/controller/operational-plan.ts +24 -51
  405. package/src/cli/controller/runtime-config.ts +5 -1
  406. package/src/cli/controller/stuck-state-migration.ts +2 -3
  407. package/src/cli/controller/systemd-user.ts +148 -0
  408. package/src/cli/controller/types.ts +10 -70
  409. package/src/cli/controller/worklog.ts +67 -13
  410. package/src/cli/editing/edit-operation-contract.ts +92 -0
  411. package/src/cli/editing/edit-session.ts +290 -20
  412. package/src/cli/github/plugin.ts +17 -12
  413. package/src/cli/hook/route-registry.ts +3 -5
  414. package/src/cli/hook/runtime.ts +49 -3
  415. package/src/cli/index.ts +3 -0
  416. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  417. package/src/cli/local-bridge/console-automations.ts +20 -15
  418. package/src/cli/local-bridge/console-view-models.ts +3 -12
  419. package/src/cli/local-bridge/facade-api.ts +105 -205
  420. package/src/cli/local-bridge/job-store.ts +1 -1
  421. package/src/cli/local-bridge/server.ts +48 -174
  422. package/src/cli/local-bridge/types.ts +0 -1
  423. package/src/cli/mcp/access-mode.ts +2 -132
  424. package/src/cli/mcp/access-tools.ts +2 -387
  425. package/src/cli/mcp/audit.ts +2 -32
  426. package/src/cli/mcp/auth.ts +2 -404
  427. package/src/cli/mcp/instructions.ts +2 -34
  428. package/src/cli/mcp/legacy-context.ts +2 -16
  429. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  430. package/src/cli/mcp/multi-repository.ts +2 -436
  431. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  432. package/src/cli/mcp/oauth.ts +2 -375
  433. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  434. package/src/cli/mcp/paths.ts +2 -140
  435. package/src/cli/mcp/policy.ts +2 -232
  436. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  437. package/src/cli/mcp/proxy-env.ts +2 -62
  438. package/src/cli/mcp/readiness-projection.ts +2 -24
  439. package/src/cli/mcp/redaction.ts +2 -21
  440. package/src/cli/mcp/repo.ts +2 -24
  441. package/src/cli/mcp/repository-tools.ts +2 -1613
  442. package/src/cli/mcp/server.ts +2 -951
  443. package/src/cli/mcp/setup.ts +2 -737
  444. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  445. package/src/cli/mcp/tool-contract.ts +2 -14
  446. package/src/cli/mcp/tools.ts +2 -9
  447. package/src/cli/mcp/toolset-names.ts +2 -70
  448. package/src/cli/mcp/toolset-selection.ts +2 -41
  449. package/src/cli/mcp/toolset.ts +2 -336
  450. package/src/cli/mcp/transports/AGENTS.md +1 -10
  451. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  452. package/src/cli/mcp/transports/http.ts +2 -1414
  453. package/src/cli/mcp/transports/session-registry.ts +2 -490
  454. package/src/cli/mcp/transports/stdio.ts +2 -8
  455. package/src/cli/mcp/types.ts +2 -41
  456. package/src/cli/mcp/umbrella-tools.ts +2 -117
  457. package/src/cli/repositories/command-executor.ts +91 -39
  458. package/src/cli/repositories/command-scope.ts +139 -4
  459. package/src/cli/repositories/controller-home.ts +167 -1
  460. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  461. package/src/cli/repositories/git-command-executor.ts +2 -2
  462. package/src/cli/repositories/identity.ts +13 -0
  463. package/src/cli/repositories/registry.ts +145 -60
  464. package/src/cli/repositories/repository-snapshot.ts +53 -7
  465. package/src/cli/repositories/runtime-storage.ts +27 -2
  466. package/src/cli/repositories/safe-patch.ts +34 -38
  467. package/src/cli/repositories/selected-path-actions.ts +22 -2
  468. package/src/cli/repositories/structured-git.ts +167 -27
  469. package/src/cli/repository/session-cache.ts +92 -6
  470. package/src/cli/runtime/helper-runner.ts +7 -0
  471. package/src/cli/runtime-invocation.ts +8 -1
  472. package/src/cli/tools/codegraph.ts +44 -25
  473. package/src/core/adoption/manifest-templates.ts +1 -1
  474. package/src/core/adoption/plan.ts +3 -26
  475. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  476. package/src/effects/process-runner.ts +8 -14
  477. package/src/runtime/context/assistant-context.ts +167 -0
  478. package/src/runtime/context/assistant-work-context.ts +209 -0
  479. package/src/runtime/context/automatic-learning.ts +565 -0
  480. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  481. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  482. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  483. package/src/runtime/context/cognitive-attention.ts +173 -0
  484. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  485. package/src/runtime/context/context-closure.ts +305 -0
  486. package/src/runtime/context/context-pack-process.ts +103 -0
  487. package/src/runtime/context/context-plane.ts +203 -0
  488. package/src/runtime/context/direct-controller-learning.ts +298 -0
  489. package/src/runtime/context/project-engineering-contract.ts +60 -0
  490. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  491. package/src/runtime/context/semantic-navigation.ts +6 -34
  492. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  493. package/src/runtime/context/typescript-navigation.ts +89 -3
  494. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  495. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  496. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  497. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  498. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  499. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  500. package/src/runtime/control-plane/cleanup-artifact-retention.ts +531 -0
  501. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  502. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +282 -0
  503. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +380 -0
  504. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +113 -16
  505. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  506. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  507. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  508. package/src/runtime/control-plane/execution/retained-work-resume.ts +439 -102
  509. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  510. package/src/runtime/control-plane/execution/validation.ts +34 -11
  511. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +87 -0
  512. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  513. package/src/runtime/control-plane/execution/work-completion-authority.ts +31 -44
  514. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  515. package/src/runtime/control-plane/execution/work-evidence-policy.ts +46 -53
  516. package/src/runtime/control-plane/execution/work-execution-support.ts +85 -164
  517. package/src/runtime/control-plane/execution/work-handle-authority.ts +349 -73
  518. package/src/runtime/control-plane/execution/work-handle-store.ts +55 -2
  519. package/src/runtime/control-plane/execution/work-head-settlement.ts +108 -0
  520. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +173 -28
  521. package/src/runtime/control-plane/execution/work-operation-service.ts +92 -40
  522. package/src/runtime/control-plane/execution/work-preparation-service.ts +145 -269
  523. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  524. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  525. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +543 -225
  526. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +59 -45
  527. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  528. package/src/runtime/control-plane/execution/work-verification-service.ts +662 -52
  529. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +205 -16
  530. package/src/runtime/control-plane/facade/capability-registry.ts +380 -44
  531. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  532. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  533. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  534. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  535. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -22
  536. package/src/runtime/control-plane/facade/goal-workloop.ts +565 -1202
  537. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +145 -0
  538. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +266 -52
  539. package/src/runtime/control-plane/facade/index.ts +4 -3
  540. package/src/runtime/control-plane/facade/operation-digest.ts +34 -73
  541. package/src/runtime/control-plane/facade/plan-contract-store.ts +671 -245
  542. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  543. package/src/runtime/control-plane/facade/repository-work-admission.ts +31 -23
  544. package/src/runtime/control-plane/facade/requirement-authority.ts +121 -0
  545. package/src/runtime/control-plane/facade/requirement-board.ts +9 -4
  546. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +85 -0
  547. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  548. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  549. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -15
  550. package/src/runtime/control-plane/facade/types.ts +218 -620
  551. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  552. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  553. package/src/runtime/control-plane/generated-cache-retention.ts +336 -19
  554. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  555. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +13 -2
  556. package/src/runtime/control-plane/global-scheduler/maintenance.ts +274 -84
  557. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  558. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  559. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -2
  560. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  561. package/src/runtime/control-plane/global-scheduler/scheduler.ts +227 -109
  562. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  563. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  564. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  565. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  566. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +160 -0
  567. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  568. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +139 -670
  569. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  570. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +25 -6
  571. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -194
  572. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  573. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  574. package/src/runtime/control-plane/persistence/experience-store.ts +302 -0
  575. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  576. package/src/runtime/control-plane/persistence/requirement-store.ts +230 -80
  577. package/src/runtime/control-plane/persistence/sqlite-store.ts +654 -78
  578. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  579. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  580. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  581. package/src/runtime/control-plane/release-retention.ts +154 -26
  582. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  583. package/src/runtime/control-plane/runtime-cleanup.ts +844 -38
  584. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  585. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  586. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +479 -0
  587. package/src/runtime/control-plane/workspace/workspace-store.ts +249 -0
  588. package/src/runtime/diagnostics/incident-repair.ts +573 -0
  589. package/src/runtime/diagnostics/mcp-timing.ts +239 -6
  590. package/src/runtime/diagnostics/performance.ts +6 -14
  591. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  592. package/src/runtime/evidence/artifact-store.ts +216 -35
  593. package/src/runtime/evidence/evidence-store.ts +112 -8
  594. package/src/runtime/evidence/operational-shadow.ts +38 -0
  595. package/src/runtime/evidence/output-handle-index.ts +225 -0
  596. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  597. package/src/runtime/evidence/result-store.ts +158 -44
  598. package/src/runtime/evidence/sensitive-output.ts +4 -4
  599. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  600. package/src/runtime/execution/jobs/store.ts +124 -6
  601. package/src/runtime/execution/managed-workspace.ts +112 -5
  602. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  603. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  604. package/src/runtime/execution/process-runtime/check-facade.ts +24 -10
  605. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  606. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  607. package/src/runtime/execution/process-runtime/check-result.ts +177 -2
  608. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +58 -9
  609. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  610. package/src/runtime/execution/process-runtime/command-facade.ts +117 -114
  611. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  612. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  613. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  614. package/src/runtime/execution/process-runtime/index.ts +4 -0
  615. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  616. package/src/runtime/execution/process-runtime/lightweight-managed.ts +283 -73
  617. package/src/runtime/execution/process-runtime/persisted-check.ts +130 -23
  618. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  619. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  620. package/src/runtime/execution/process-runtime/resource-claims.ts +125 -64
  621. package/src/runtime/execution/process-runtime/runtime.ts +167 -17
  622. package/src/runtime/execution/process-runtime/store.ts +30 -2
  623. package/src/runtime/execution/process-runtime/types.ts +27 -0
  624. package/src/runtime/execution/workers/executor.ts +18 -19
  625. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  626. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  627. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  628. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  629. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  630. package/src/runtime/gateway/mcp/router.ts +2 -1125
  631. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  632. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
  633. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  634. package/src/runtime/maintenance/cleanup.ts +38 -24
  635. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  636. package/src/runtime/platform/computer-platform.ts +14 -0
  637. package/src/runtime/platform/executable-discovery.ts +54 -0
  638. package/src/runtime/platform/fetch-http-server.ts +90 -0
  639. package/src/runtime/platform/service-inventory.ts +130 -0
  640. package/src/runtime/platform/service-manager.ts +146 -0
  641. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  642. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  643. package/src/runtime/plugins/action-application.ts +192 -0
  644. package/src/runtime/plugins/app-store-connect-adapter.ts +47 -35
  645. package/src/runtime/plugins/browser-adapter.ts +898 -135
  646. package/src/runtime/plugins/browser-automation-service.ts +33 -34
  647. package/src/runtime/plugins/browser-macos-bridge.ts +386 -46
  648. package/src/runtime/plugins/browser-manifest-surface.ts +75 -17
  649. package/src/runtime/plugins/browser-native-session-service.ts +64 -14
  650. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  651. package/src/runtime/plugins/browser-registration.ts +32 -0
  652. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  653. package/src/runtime/plugins/browser-runtime.ts +55 -10
  654. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  655. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  656. package/src/runtime/plugins/browser-session-store.ts +243 -80
  657. package/src/runtime/plugins/browser-session-types.ts +2 -98
  658. package/src/runtime/plugins/capability-authorization-grants.ts +272 -59
  659. package/src/runtime/plugins/catalog-trust.ts +82 -0
  660. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  661. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  662. package/src/runtime/plugins/computer-registration.ts +1050 -0
  663. package/src/runtime/plugins/config-store.ts +119 -0
  664. package/src/runtime/plugins/desktop-operator-external-policy.ts +964 -0
  665. package/src/runtime/plugins/desktop-operator-registration.ts +123 -12
  666. package/src/runtime/plugins/errors.ts +8 -1
  667. package/src/runtime/plugins/external-adapter.ts +82 -323
  668. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  669. package/src/runtime/plugins/external-registration.ts +30 -2
  670. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  671. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  672. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  673. package/src/runtime/plugins/first-party-registry.ts +3 -1
  674. package/src/runtime/plugins/github-adapter.ts +1 -2
  675. package/src/runtime/plugins/gmail-adapter.ts +29 -8
  676. package/src/runtime/plugins/google-calendar-adapter.ts +8 -8
  677. package/src/runtime/plugins/google-shared.ts +45 -53
  678. package/src/runtime/plugins/google-tasks-adapter.ts +8 -8
  679. package/src/runtime/plugins/ios-adapter.ts +2 -3
  680. package/src/runtime/plugins/lightweight-action.ts +208 -2
  681. package/src/runtime/plugins/local-system-adapter.ts +192 -2
  682. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  683. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  684. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  685. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  686. package/src/runtime/plugins/resend-adapter.ts +15 -24
  687. package/src/runtime/plugins/store.ts +1003 -387
  688. package/src/runtime/plugins/types.ts +28 -1
  689. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  690. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  691. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  692. package/src/runtime/plugins/xiaohongshu-publish.ts +102 -430
  693. package/src/runtime/projections/controller-context.ts +12 -6
  694. package/src/runtime/projections/git-status-sampler.ts +73 -22
  695. package/src/runtime/projections/invalidation.ts +7 -3
  696. package/src/runtime/projections/materialized-view.ts +93 -43
  697. package/src/runtime/recovery/actions.ts +3 -3
  698. package/src/runtime/recovery/capability-action-application.ts +229 -0
  699. package/src/runtime/recovery/index.ts +3 -0
  700. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  701. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  702. package/src/runtime/recovery/maintenance-executor.ts +701 -168
  703. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  704. package/src/runtime/recovery/store.ts +29 -11
  705. package/src/runtime/release/identity.ts +6 -0
  706. package/src/runtime/release/release-coordinator.ts +296 -0
  707. package/src/runtime/release/release-session.ts +716 -0
  708. package/src/runtime/release/release-state-migration.ts +25 -0
  709. package/src/runtime/resources/leases/store.ts +56 -25
  710. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  711. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  712. package/src/runtime/root/browser-session-composition.ts +59 -0
  713. package/src/runtime/root/computer-composition.ts +376 -0
  714. package/src/runtime/root/computer-target-composition.ts +16 -0
  715. package/src/runtime/root/computer-target-persistence.ts +39 -0
  716. package/src/runtime/root/controller-progression-composition.ts +192 -0
  717. package/src/runtime/root/controller-round-composition.ts +126 -0
  718. package/src/runtime/root/controller-services.ts +2 -0
  719. package/src/runtime/root/deployment-topology.ts +106 -0
  720. package/src/runtime/root/entry.ts +8 -6
  721. package/src/runtime/root/gateway-adapter.ts +3 -3
  722. package/src/runtime/root/known-good-recovery.ts +184 -0
  723. package/src/runtime/root/local-bridge.ts +5 -3
  724. package/src/runtime/root/mcp-transport.ts +116 -23
  725. package/src/runtime/root/ownership.ts +261 -9
  726. package/src/runtime/root/package-connector-service.ts +212 -74
  727. package/src/runtime/root/package-runtime-release.ts +199 -33
  728. package/src/runtime/root/package-runtime-service.ts +77 -108
  729. package/src/runtime/root/release-execution-canary.ts +103 -0
  730. package/src/runtime/root/release-loader.ts +150 -0
  731. package/src/runtime/root/release-manifest.ts +185 -7
  732. package/src/runtime/root/release-materialize.ts +474 -31
  733. package/src/runtime/root/release-store.ts +425 -37
  734. package/src/runtime/root/runtime-lane.ts +191 -0
  735. package/src/runtime/root/runtime.ts +379 -32
  736. package/src/runtime/root/scheduled-controller-composition.ts +76 -0
  737. package/src/runtime/root/scheduler.ts +12 -4
  738. package/src/runtime/root/service-runner.ts +3 -3
  739. package/src/runtime/root/service.ts +73 -45
  740. package/src/runtime/root/status.ts +49 -0
  741. package/src/runtime/root/types.ts +55 -3
  742. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  743. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  744. package/src/runtime/root/write-fence.ts +50 -2
  745. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  746. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  747. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  748. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  749. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  750. package/src/runtime/shared/process-environment.ts +36 -10
  751. package/src/runtime/shared/response-budget.ts +1 -3
  752. package/src/runtime/shared/storage-capacity.ts +68 -0
  753. package/src/runtime/standalone-recovery/controller-home-migration.ts +1108 -0
  754. package/src/runtime/standalone-recovery/core.ts +4609 -495
  755. package/src/runtime/standalone-recovery/entry.ts +642 -81
  756. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  757. package/src/runtime/standalone-recovery/installer.ts +399 -177
  758. package/src/runtime/standalone-recovery/mcp-server.ts +79 -0
  759. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  760. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  761. package/src/runtime/standalone-recovery/performance.ts +173 -0
  762. package/src/runtime/standalone-recovery/release.ts +58 -5
  763. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  764. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  765. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  766. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  767. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -4
  768. package/src/runtime/workflow/schedules/engine.ts +298 -484
  769. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  770. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  771. package/src/runtime/workflow/schedules/store.ts +2 -607
  772. package/src/runtime/workflow/schedules/types.ts +2 -127
  773. package/src/runtime/workflow/schedules/work-continuation.ts +220 -16
  774. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  775. package/src/runtime/workflows/runtime.ts +482 -0
  776. package/src/testing/test-governance.ts +95 -30
  777. package/supervisor/chatgpt-conversation.ts +23 -0
  778. package/supervisor/client.ts +201 -0
  779. package/supervisor/control-plane.ts +868 -0
  780. package/supervisor/entry.ts +15 -0
  781. package/supervisor/forge-validators.ts +80 -0
  782. package/supervisor/index.ts +5 -0
  783. package/supervisor/native-browser-adapter.ts +786 -0
  784. package/supervisor/native-messaging/host.ts +39 -0
  785. package/supervisor/native-messaging/manifest.ts +26 -0
  786. package/supervisor/native-messaging/protocol.ts +27 -0
  787. package/supervisor/paths.ts +18 -0
  788. package/supervisor/protocol.ts +202 -0
  789. package/supervisor/server.ts +404 -0
  790. package/supervisor/store.ts +1451 -0
  791. package/supervisor/tsconfig.json +8 -0
  792. package/supervisor/types.ts +255 -0
  793. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  794. package/docs/architecture/history/README.md +0 -7
  795. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  796. package/docs/architecture/history/global-hook-runtime.md +0 -166
  797. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  798. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  799. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  800. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  801. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  802. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  803. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  804. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  805. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  806. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  807. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  808. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  809. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  810. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  811. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  812. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  813. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  814. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  815. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  816. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  817. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  818. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  819. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  820. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  821. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  822. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  823. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  824. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  825. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  826. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  827. package/docs/architecture/requests/archive/2026/root.md +0 -68
  828. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  829. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  830. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  831. package/scripts/archive-architecture-request.sh +0 -4
  832. package/scripts/benchmark-background-check-overlap.ts +0 -228
  833. package/scripts/benchmark-controller-hotpath.ts +0 -212
  834. package/scripts/benchmark-controller-round2.ts +0 -139
  835. package/scripts/benchmark-ios-agent-device.ts +0 -514
  836. package/scripts/benchmark-mcp-host.sh +0 -117
  837. package/scripts/benchmark-mcp-transport.ts +0 -170
  838. package/scripts/benchmark-quality-harness.ts +0 -146
  839. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  840. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  841. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  842. package/scripts/benchmark-thin-harness.ts +0 -251
  843. package/scripts/benchmark-typescript-navigation.ts +0 -186
  844. package/scripts/benchmark-work-efficiency.ts +0 -334
  845. package/scripts/build-controller-ui.ts +0 -28
  846. package/scripts/check-npm-release.sh +0 -32
  847. package/scripts/check-public-release-surface.sh +0 -19
  848. package/scripts/check-release-published.sh +0 -57
  849. package/scripts/check-release-readiness.sh +0 -37
  850. package/scripts/check-release-version.mjs +0 -74
  851. package/scripts/check-tarball-install-smoke.sh +0 -67
  852. package/scripts/package-source-archive.sh +0 -147
  853. package/scripts/public-release-files.txt +0 -39
  854. package/scripts/public-release.ts +0 -174
  855. package/scripts/publish-release-tarball.sh +0 -27
  856. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  857. package/scripts/run-skill-evals.ts +0 -1022
  858. package/scripts/stage-runtime-release.ts +0 -39
  859. package/src/cli/controller/progress.ts +0 -656
  860. package/src/cli/controller/work-mode.ts +0 -181
  861. package/src/cli/local-bridge/console-assets.ts +0 -17
  862. package/src/cli/local-bridge/dashboard.ts +0 -17
  863. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  864. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  865. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  866. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  867. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  868. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  869. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  870. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  871. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  872. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  873. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  874. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  875. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  876. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  877. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  878. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  879. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  880. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  881. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  882. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  883. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  884. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  885. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  886. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  887. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  888. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  889. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  890. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  891. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -385
  892. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -1913
  893. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  894. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -449
  895. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -1118
  896. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -93
  897. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  898. package/src/runtime/control-plane/routing/index.ts +0 -1
  899. package/src/runtime/control-plane/routing/route-policy.ts +0 -382
  900. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -1,35 +1,42 @@
1
- import { randomUUID } from 'crypto';
1
+ import { createHash, randomUUID } from 'crypto';
2
2
  import {
3
3
  createHandoffItem,
4
- listHandoffItems,
5
4
  type HandoffInboxStoreOptions,
6
5
  } from './handoff-inbox-store';
7
- import { getControllerSession } from './controller-session-store';
6
+ import { executionPlacement, readForgeInstanceIdentity } from '../../../../packages/kernel/identity/api/index';
8
7
  import {
9
- appendVerificationRecord,
8
+ applyEngineeringBlockerDisposition,
9
+ buildEngineeringBlockerDispositionReceipt,
10
+ buildEngineeringContextReceipt,
11
+ engineeringWorkProfileForRisk,
10
12
  appendWorkEvidence,
11
13
  appendWorkHandoffRef,
12
14
  createWorkContract,
15
+ failWorkContract,
13
16
  getWorkContract,
17
+ semanticWorkState,
14
18
  listWorkContracts,
19
+ readActiveWorkCandidates,
20
+ recordWorkEvidenceState,
15
21
  recordWorkScopeEvidence,
22
+ reviseWorkSemanticContext,
16
23
  summarizeWorkContract,
17
24
  transitionWorkContractPhase,
18
25
  updateWorkContract,
26
+ type EngineeringAdmissionEvidence,
27
+ type EvaluationPromotionReceipt,
19
28
  type WorkContractStoreOptions,
20
- } from './work-contract-store';
29
+ } from '../../../../packages/kernel/work/api/index';
21
30
  import {
22
- claimPlanStepForWork,
23
- completePlanStepForWork,
24
31
  getPlanContract,
32
+ currentPlanSemanticRevision,
25
33
  type PlanContractStoreOptions,
26
34
  } from './plan-contract-store';
27
35
  import { withPrimaryWorkAdmissionLock } from './semantic-admission';
28
- import { completeWorkWithReceipt } from '../execution/work-completion-authority';
29
- import { evaluateReadOnlyReviewSourceIdentity, evaluateWorkCompletionEvidence, evaluateWorkImplementationEvidence, verificationRecordAppliesToCurrentWorkspace } from '../execution/work-evidence-policy';
30
- import { readRequirement } from '../persistence/requirement-store';
36
+ import { effectiveCurrentWorkVerificationRecords, evaluateWorkCompletionEvidence, evaluateWorkImplementationEvidence } from '../execution/work-evidence-policy';
37
+ import { hasSettledWorkDeliveryReceipt } from '../execution/work-completion-authority';
38
+ import { currentRequirementSemanticRevision, readRequirement } from '../persistence/requirement-store';
31
39
  import {
32
- classifyVerificationOutcome,
33
40
  normalizeCheckIds,
34
41
  reconcileVerificationHistory,
35
42
  type CheckDefinitionLike,
@@ -41,20 +48,17 @@ import { buildWorkContinuationSnapshot } from './work-continuation';
41
48
  import type {
42
49
  CapabilityRisk,
43
50
  EvidenceRef,
44
- ExecutionModeSelectionInput,
45
51
  FacadeResult,
46
52
  PlanContract,
47
53
  PolicyDecision,
48
54
  SuggestedNextAction,
49
- VerificationRecord,
50
55
  WorkContract,
56
+ WorkStartFacts,
51
57
  WorkKind,
52
58
  WorkRisk,
53
59
  } from './types';
54
- import { selectExecutionMode } from './types';
55
- import { resolveWorkspaceAdmissionConstraint } from '../routing/workspace-admission';
56
60
 
57
- export type GoalWorkloopOperation = 'start' | 'continue' | 'verify' | 'finalize' | 'stop';
61
+ export type GoalWorkloopOperation = 'start' | 'continue' | 'stop';
58
62
 
59
63
  export interface GoalWorkloopContext {
60
64
  workStore: WorkContractStoreOptions;
@@ -63,10 +67,16 @@ export interface GoalWorkloopContext {
63
67
  availableChecks?: readonly CheckDefinitionLike[];
64
68
  planStore?: PlanContractStoreOptions;
65
69
  sourceRevision?: string;
70
+ /** Explicit only when repository observation proves the Work started from a revision or an unborn HEAD. */
71
+ sourceBaseState?: 'revision' | 'unborn';
66
72
  checkoutId?: string;
67
73
  principalId?: string;
68
74
  controllerInstanceId?: string;
69
75
  workspaceFingerprint?: string;
76
+ /** Trusted repository observation for placement admission. Never accept this as caller-supplied semantic authority. */
77
+ workspaceDirty?: boolean;
78
+ /** Stage-insensitive Git-canonical identity of the exact current review candidate. Derived by the trusted repository adapter. */
79
+ implementationReviewWorkspaceFingerprint?: string;
70
80
  /** Current net repository paths changed from the Work base plus dirty checkout paths. */
71
81
  workspaceChangedPaths?: readonly string[];
72
82
  /** Durable terminal repository Process ids explicitly bound to this Work + checkout. */
@@ -81,6 +91,8 @@ export type WorkAdmissionRelation = 'continue' | 'extend' | 'parallel' | 'new_go
81
91
 
82
92
  export interface GoalWorkloopStartInput {
83
93
  objective: string;
94
+ /** Optional exact durable identity reserved by the admission authority before creation. */
95
+ workId?: string;
84
96
  acceptanceCriteria?: string[];
85
97
  allowedPaths?: string[];
86
98
  /** Non-authoritative first-pass discovery candidates. */
@@ -88,8 +100,11 @@ export interface GoalWorkloopStartInput {
88
100
  forbiddenPaths?: string[];
89
101
  checks?: string[];
90
102
  constraints?: WorkContract['constraints'];
91
- modeInput: ExecutionModeSelectionInput;
103
+ /** Concrete mechanical request facts. This capability is itself the explicit durable-Work choice. */
104
+ request: WorkStartFacts;
92
105
  requestedBy?: WorkContract['requestedBy'];
106
+ /** Stable durable admission identity for system-originated Work such as recurrent incident repair. */
107
+ requestId?: string;
93
108
  relatedWorkId?: string;
94
109
  workRelation?: WorkAdmissionRelation;
95
110
  requirementId?: string;
@@ -97,11 +112,12 @@ export interface GoalWorkloopStartInput {
97
112
  issueId?: string;
98
113
  approvalConfirmed?: boolean;
99
114
  dryRun?: boolean;
100
- forceMode?: WorkContract['mode'];
101
115
  planId?: string;
102
116
  planStepId?: string;
103
117
  /** Explicit technical evidence shape chosen by the semantic Controller. Never inferred from objective/check text. */
104
118
  workKind?: Extract<WorkKind, 'repository_change' | 'completed_no_change' | 'read_only_review' | 'investigation' | 'local_effect' | 'remote_effect' | 'reconciliation'>;
119
+ /** Trusted source-bound evidence resolved by Forge evidence authorities before admission. Never populate this field from raw caller-supplied receipt ids. */
120
+ verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
105
121
  }
106
122
 
107
123
  export interface GoalWorkloopAcceptanceEvidenceBinding {
@@ -124,32 +140,17 @@ export interface GoalWorkloopContinueInput {
124
140
  acceptanceEvidence?: GoalWorkloopAcceptanceEvidenceBinding[];
125
141
  /** Explicit Controller decision after deterministic acceptance failure; never inferred from note text. */
126
142
  acceptanceFailureDecision?: 'repair' | 'rescope';
127
- }
128
-
129
- export interface GoalWorkloopVerifyInput {
130
- workId: string;
131
- checkId: string;
132
- /** Exact Git revision observed by the authoritative Work-bound verification Process. */
133
- sourceRevision?: string;
134
- /** Exact dirty-workspace content identity observed by the authoritative check. */
135
- workspaceFingerprint?: string;
136
- /** Stable semantic check-input identity used for exact evidence reuse. */
137
- verificationInputFingerprint?: string;
138
- /** Bounded command/invocation audit identity; not part of reusable semantic inputs. */
139
- commandFingerprint?: string;
140
- /** Persistence-safe Process receipt. Accepted only when its Work/repo/check identity matches this verification. */
141
- receipt?: VerificationRecord['receipt'];
142
- /** When true, simulate infrastructure failure rather than acceptance fail. */
143
- infrastructureFailed?: boolean;
144
- /** When true and check is valid, record acceptance failure. */
145
- checkFailed?: boolean;
146
- /** When true, skip without acceptance implication. */
147
- skipped?: boolean;
148
- }
149
-
150
- export interface GoalWorkloopFinalizeInput {
151
- workId: string;
152
- forceFailed?: boolean;
143
+ /** Trusted refreshed engineering evidence minted by Runtime composition, never copied from raw caller receipt ids. */
144
+ verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
145
+ /** Semantic Controller blocker classification; Forge derives the permitted action and persists the receipt. */
146
+ engineeringBlocker?: {
147
+ blockerId: string;
148
+ classification: 'same_root_cause' | 'same_root_cause_scope_extension' | 'unrelated';
149
+ rationale: string;
150
+ semanticScopeKeys?: string[];
151
+ /** Required for classification=unrelated: the exact Work that already owns the blocker. */
152
+ linkedWorkId?: string;
153
+ };
153
154
  }
154
155
 
155
156
  export interface GoalWorkloopStopInput {
@@ -178,44 +179,41 @@ function handoffIdFor(prefix: string): string {
178
179
 
179
180
  function workRiskFor(input: GoalWorkloopStartInput): WorkRisk {
180
181
  if (input.workKind === 'read_only_review') return 'readonly';
181
- const risk = input.modeInput.risk;
182
- if (input.modeInput.secretAccess === true || input.modeInput.destructive === true) return 'destructive';
183
- if (input.modeInput.remoteWrite === true) return 'high';
182
+ const risk = input.request.risk;
183
+ if (input.request.secretAccess === true || input.request.destructive === true) return 'destructive';
184
+ if (input.request.remoteWrite === true) return 'high';
184
185
  if (risk === 'readonly') return 'readonly';
185
186
  if (risk === 'destructive' || risk === 'destructive_remote' || risk === 'raw_secret_config') return 'destructive';
186
187
  if (risk === 'remote_write') return 'high';
187
188
  if (risk === 'local_repo_write') return 'low';
189
+ if (risk === undefined) return 'low';
188
190
  return 'medium';
189
191
  }
190
192
 
193
+ function resolvedWorkKindFor(input: GoalWorkloopStartInput): WorkKind {
194
+ if (input.workKind) return input.workKind;
195
+ if (input.request.requiresExternalEffect === true) {
196
+ // Mechanical effect classification only: a remote effect differs from a local
197
+ // effect in receipt identity. Predicted scope size is model strategy and is
198
+ // never consulted; callers declare `work_kind` when a repository change is
199
+ // also involved.
200
+ return input.request.remoteWrite === true ? 'remote_effect' : 'local_effect';
201
+ }
202
+ return 'repository_change';
203
+ }
204
+
191
205
  function suggestedForWorkIdentity(workId: string, checks: string[], extras: SuggestedNextAction[] = []): SuggestedNextAction[] {
192
- const base: SuggestedNextAction[] = [
193
- {
194
- label: 'Continue workloop',
195
- tool: 'rh_work',
196
- operation: 'continue',
197
- payload: { work_id: workId },
198
- risk: 'readonly',
199
- confidence: 'high',
200
- },
201
- {
202
- label: 'Verify registered checks',
203
- tool: 'rh_work',
204
- operation: 'verify',
205
- payload: { work_id: workId, check_id: checks[0] },
206
- risk: 'workspace_write',
207
- confidence: checks[0] ? 'high' : 'low',
208
- },
209
- {
210
- label: 'Finalize when ready',
211
- tool: 'rh_work',
212
- operation: 'finalize',
213
- payload: { work_id: workId },
214
- risk: 'readonly',
215
- confidence: 'medium',
216
- },
217
- ];
218
- return validateSuggestedNextActions([...extras, ...base], {
206
+ const semanticRead: SuggestedNextAction = {
207
+ label: 'Read current Work semantic context',
208
+ tool: 'rh_work',
209
+ operation: 'get',
210
+ payload: { work_id: workId },
211
+ risk: 'readonly',
212
+ confidence: 'high',
213
+ };
214
+ // Suggested actions expose facts/capabilities only. The model decides whether
215
+ // implementation, checks, review, delivery, or semantic completion is next.
216
+ return validateSuggestedNextActions([...extras, semanticRead], {
219
217
  validCheckIds: checks,
220
218
  }).actions;
221
219
  }
@@ -240,31 +238,36 @@ export function routeWorkStart(
240
238
  ctx: GoalWorkloopContext,
241
239
  input: GoalWorkloopStartInput,
242
240
  ): FacadeResult {
243
- const placementResolution = resolveWorkspaceAdmissionConstraint(input.constraints);
244
- if (placementResolution.ok === false) {
245
- return buildFacadeResult({
246
- status: 'blocked',
247
- summary: `${placementResolution.code}: ${placementResolution.message}`,
248
- data: { executionStarted: false, workContractCreated: false, placementConstraintConflict: true },
249
- rawAvailable: false,
250
- });
251
- }
252
- const placementConstraint = placementResolution.constraint;
241
+ const workspaceMode: 'current' | 'isolated' = input.constraints?.workspaceMode === 'isolated' || input.constraints?.workspaceMode === 'auto' ? 'isolated' : 'current';
253
242
  const canonicalConstraints: WorkContract['constraints'] = {
254
243
  ...(input.constraints ?? {}),
255
- workspaceMode: placementConstraint.workspaceMode,
256
- requireWorktree: placementConstraint.requireWorktree,
257
- directMainProhibited: placementConstraint.directMainProhibited,
244
+ workspaceMode,
258
245
  };
259
246
  const strategyConflictRequiresApproval = input.constraints?.architectureStrategyChange === true
260
247
  || input.constraints?.conflictsWithThinHarnessPolicy === true
261
248
  || input.constraints?.changesDefaultExecutionStrategy === true;
262
- const relatedLifecycleSource = input.relatedWorkId ? getWorkContract(ctx.workStore, input.relatedWorkId) : undefined;
249
+ let relatedLifecycleSource: WorkContract | undefined;
250
+ if (input.relatedWorkId) {
251
+ try {
252
+ relatedLifecycleSource = getWorkContract(ctx.workStore, input.relatedWorkId);
253
+ } catch (error) {
254
+ return buildFacadeResult({
255
+ status: 'blocked',
256
+ summary: `WORK_RELATED_CONTRACT_INVALID: ${input.relatedWorkId} is malformed and cannot authorize a Work relationship.`,
257
+ data: {
258
+ executionStarted: false,
259
+ workContractCreated: false,
260
+ invalidWorkId: input.relatedWorkId,
261
+ invalidWorkError: error instanceof Error ? error.message : String(error),
262
+ },
263
+ });
264
+ }
265
+ }
263
266
  const inheritedReadOnlyReview = Boolean(
264
267
  input.workKind === undefined
265
268
  && input.workRelation === 'new_goal'
266
269
  && relatedLifecycleSource?.workKind === 'read_only_review'
267
- && (relatedLifecycleSource.status === 'cancelled' || relatedLifecycleSource.status === 'completed' || relatedLifecycleSource.status === 'failed'),
270
+ && semanticWorkState(relatedLifecycleSource) !== 'open',
268
271
  );
269
272
  if (
270
273
  inheritedReadOnlyReview
@@ -279,19 +282,23 @@ export function routeWorkStart(
279
282
  });
280
283
  }
281
284
  const requestedWorkKind = input.workKind ?? (inheritedReadOnlyReview ? 'read_only_review' : undefined);
282
- const typedReadOnlyReviewRequested = requestedWorkKind === undefined
283
- && (input.modeInput.mutation === false || (input.modeInput.mutation === undefined && input.modeInput.risk === 'readonly'))
284
- && input.modeInput.requiresInvestigation === true
285
- && input.modeInput.requiresRecovery === true;
285
+ const explicitSourceMutationFence = input.allowedPaths !== undefined
286
+ && input.allowedPaths.length === 0
287
+ && (input.forbiddenPaths ?? []).some((value) => {
288
+ const normalized = value.trim().replace(/\\/g, '/');
289
+ return normalized === '*' || normalized === '**' || normalized === '**/*';
290
+ });
291
+ // A read-only review is an explicit `work_kind`, or an explicit source-mutation
292
+ // fence that proves no repository path may change. Forge never infers it from
293
+ // caller hints about investigation depth or recovery needs.
294
+ const typedReadOnlyReviewRequested = requestedWorkKind === undefined && explicitSourceMutationFence;
286
295
  const effectiveWorkKind = requestedWorkKind ?? (typedReadOnlyReviewRequested ? 'read_only_review' : undefined);
287
296
  const readOnlyReviewRequested = effectiveWorkKind === 'read_only_review';
288
297
  const readOnlyMutationConflict = readOnlyReviewRequested && (
289
- input.modeInput.mutation === true
290
- || (input.modeInput.expectedFiles ?? 0) > 0
291
- || (input.modeInput.expectedChangedLines ?? 0) > 0
292
- || input.modeInput.requiresExternalEffect === true
293
- || input.modeInput.remoteWrite === true
294
- || input.modeInput.destructive === true
298
+ input.request.mutation === true
299
+ || input.request.requiresExternalEffect === true
300
+ || input.request.remoteWrite === true
301
+ || input.request.destructive === true
295
302
  );
296
303
  if (readOnlyMutationConflict) {
297
304
  return buildFacadeResult({
@@ -300,151 +307,49 @@ export function routeWorkStart(
300
307
  data: { executionStarted: false, workContractCreated: false, workKind: effectiveWorkKind },
301
308
  });
302
309
  }
303
- const effectiveModeInput: ExecutionModeSelectionInput = {
304
- ...input.modeInput,
305
- objective: input.objective,
306
- knownPaths: input.allowedPaths,
307
- workspacePlacement: placementConstraint.workspaceMode,
308
- directMainProhibited: placementConstraint.directMainProhibited,
309
- mutation: readOnlyReviewRequested ? false : input.modeInput.mutation ?? input.modeInput.risk !== 'readonly',
310
- risk: readOnlyReviewRequested ? 'readonly' : input.modeInput.risk,
310
+ // A terminal continuation is not a new semantic request. Its durable predecessor
311
+ // and Plan lineage already define the goal scope, while the successor Plan step
312
+ // becomes the Work objective later in startGoalWorkloop.
313
+ const routeObjective = input.objective.trim()
314
+ || (input.workRelation === 'continue' && relatedLifecycleSource && semanticWorkState(relatedLifecycleSource) === 'completed'
315
+ ? relatedLifecycleSource.objective.trim()
316
+ : '');
317
+ const effectiveRequest: WorkStartFacts = {
318
+ ...input.request,
319
+ objective: routeObjective,
320
+ workspaceDirty: ctx.workspaceDirty ?? input.request.workspaceDirty,
321
+ mutation: readOnlyReviewRequested ? false : input.request.mutation ?? input.request.risk !== 'readonly',
322
+ risk: readOnlyReviewRequested ? 'readonly' : input.request.risk,
311
323
  approvalConfirmed: input.approvalConfirmed === true,
312
324
  requiresUserApproval: input.approvalConfirmed === true
313
325
  ? false
314
- : input.modeInput.requiresUserApproval === true || strategyConflictRequiresApproval,
326
+ : input.request.requiresUserApproval === true || strategyConflictRequiresApproval,
315
327
  };
316
- if (effectiveModeInput.explicitMode === 'scale' && (!input.planId || !input.planStepId || !ctx.planStore)) {
317
- return buildFacadeResult({
318
- status: 'blocked',
319
- summary: 'SCALE_PLAN_REQUIRED: explicit Scale execution requires a bound approved PlanContract step.',
320
- data: { executionStarted: false, workContractCreated: false, planRequired: true, explicitMode: 'scale' },
321
- });
322
- }
323
- const applyForcedMode = (selected: ReturnType<typeof selectExecutionMode>) => input.forceMode
324
- ? {
325
- ...selected,
326
- mode: input.forceMode,
327
- reason: `Forced mode: ${input.forceMode}. ${selected.reason}`,
328
- createWorkContract: input.forceMode === 'handoff_only' ? false : selected.requiresWork,
329
- createHandoff: input.forceMode === 'handoff_only',
330
- }
331
- : selected;
332
- const evaluateAccessPolicy = (selected: ReturnType<typeof selectExecutionMode>) => evaluatePolicyGate({
333
- capabilityId: selected.mode === 'direct_control' ? 'repository.direct_edit' : selected.mode === 'goal_workloop' ? 'controller.goal_workloop' : 'controller.handoff_inbox',
334
- risk: effectiveModeInput.risk
335
- ?? (input.modeInput.secretAccess === true ? 'raw_secret_config'
336
- : input.modeInput.destructive === true ? 'destructive'
337
- : input.modeInput.remoteWrite === true ? 'remote_write'
338
- : input.modeInput.requiresApproval === true || input.modeInput.requiresUserApproval === true ? 'workspace_write'
339
- : selected.mode === 'direct_control' ? 'local_repo_write'
340
- : selected.mode === 'goal_workloop' ? 'workspace_write'
341
- : 'readonly'),
328
+ const evaluateAccessPolicy = () => evaluatePolicyGate({
329
+ capabilityId: 'controller.work_contract',
330
+ risk: effectiveRequest.risk
331
+ ?? (input.request.secretAccess === true ? 'raw_secret_config'
332
+ : input.request.destructive === true ? 'destructive'
333
+ : input.request.remoteWrite === true ? 'remote_write'
334
+ : input.request.requiresApproval === true || input.request.requiresUserApproval === true ? 'workspace_write'
335
+ : 'workspace_write'),
342
336
  accessMode: input.constraints?.accessMode,
343
337
  approvalConfirmed: input.approvalConfirmed === true,
344
338
  dryRun: input.dryRun === true,
345
- directEditBoundary: {
346
- scopeClear: effectiveModeInput.scopeClear,
347
- maxChangedFiles: effectiveModeInput.expectedFiles,
348
- maxChangedLines: effectiveModeInput.expectedChangedLines,
349
- pathsExplicit: effectiveModeInput.scopeClear,
350
- },
351
339
  });
340
+ // Missing mechanical admission fields are reported to the caller verbatim.
341
+ // Forge does not translate them into an engineering-mode recommendation.
342
+ const missingContractFields: string[] = [];
343
+ if (!input.request.scopeClear) missingContractFields.push('scopeSummary', 'acceptanceCriteria', 'allowedPaths');
344
+ if (input.objective.trim().length === 0) missingContractFields.push('objective');
352
345
 
353
- let selectedMode = selectExecutionMode(effectiveModeInput);
354
- if (input.forceMode === 'direct_control' && selectedMode.routeDecision.requiresIsolation) {
355
- return buildFacadeResult({
356
- status: 'blocked',
357
- summary: 'WORKSPACE_PLACEMENT_DIRECT_CONTROL_FORBIDDEN: typed placement requires isolation, so force_mode=direct_control cannot override admission policy.',
358
- data: { executionStarted: false, workContractCreated: false, routeDecision: selectedMode.routeDecision },
359
- rawAvailable: false,
360
- });
361
- }
362
- let policy = evaluateAccessPolicy(selectedMode);
363
- if (policy.decision === 'allowed' && effectiveModeInput.requiresUserApproval !== true) {
364
- selectedMode = selectExecutionMode({ ...effectiveModeInput, approvalConfirmed: true });
365
- policy = evaluateAccessPolicy(selectedMode);
366
- }
367
- let mode = applyForcedMode(selectedMode);
368
- const routeApprovalRequired = mode.routeDecision.requiresApproval
369
- && mode.routeDecision.waitForUser
370
- && mode.routeDecision.approvalState !== 'blocked_by_policy';
371
- const approvalRequired = policy.decision === 'approval_required' || routeApprovalRequired;
372
-
373
- // Route Policy is the sole execution-depth authority. Existing Work is
374
- // ownership context, not a reason to upgrade an otherwise Direct operation
375
- // into Durable Work. Explicit Work/Plan ownership is resolved only after a
376
- // durable route has already been selected; checkout writer conflicts affect
377
- // placement, not semantic task identity.
378
-
379
- if (mode.mode === 'direct_control') {
380
- const available = ctx.availableChecks ?? [];
381
- const normalized = normalizeCheckIds(input.checks ?? [], available);
382
- // Explicit ownership may annotate a Direct operation, but it never changes
383
- // the RouteDecision. The actual repository mutation can pass this Work id
384
- // to repository_safe_patch_apply / repository_command_execute for evidence
385
- // attribution while staying on the Direct/Process path.
386
- const directOwnershipCandidates = input.relatedWorkId || (input.planId && input.planStepId)
387
- ? listWorkContracts({ ...ctx.workStore, status: 'active', limit: 100 })
388
- .filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary')
389
- : [];
390
- const directPlan = input.planId && ctx.planStore ? getPlanContract(ctx.planStore, input.planId) : undefined;
391
- const directPlanStep = directPlan && input.planStepId ? directPlan.steps.find((step) => step.id === input.planStepId) : undefined;
392
- const directOwner = (input.relatedWorkId
393
- ? directOwnershipCandidates.find((candidate) => candidate.workId === input.relatedWorkId)
394
- : undefined)
395
- ?? (directPlanStep?.workId
396
- ? directOwnershipCandidates.find((candidate) => candidate.workId === directPlanStep.workId)
397
- : undefined);
398
- const suggested = validateSuggestedNextActions([
399
- {
400
- label: 'Apply bounded direct edit',
401
- tool: 'rh_work',
402
- operation: 'start',
403
- payload: { mode: 'direct_control', objective: input.objective.slice(0, 200) },
404
- risk: 'local_repo_write',
405
- confidence: 'high',
406
- reason: 'Small supervised task stays on Direct Control; no WorkContract created.',
407
- },
408
- ...normalized.suggestedNextActions,
409
- {
410
- label: 'Read repository context',
411
- tool: 'rh_context',
412
- operation: 'get',
413
- risk: 'readonly',
414
- confidence: 'medium',
415
- },
416
- ], { validCheckIds: normalized.validCheckIds }).actions;
417
-
418
- return buildFacadeResult({
419
- status: policy.decision === 'denied' ? 'blocked' : 'ok',
420
- summary: `Direct control recommended. No WorkContract created. ${mode.reason}`,
421
- data: {
422
- mode,
423
- policy,
424
- workContractCreated: false,
425
- directControlPreserved: true,
426
- objective: input.objective.slice(0, 1_000),
427
- normalizedChecks: normalized,
428
- ...(directOwner ? {
429
- ownership: {
430
- workId: directOwner.workId,
431
- relation: input.workRelation ?? 'continue',
432
- executionDepthPreserved: true,
433
- },
434
- } : {}),
435
- },
436
- warnings: [...policy.warnings, ...normalized.warnings],
437
- suggestedNextActions: suggested,
438
- rawAvailable: false,
439
- });
440
- }
346
+ const policy = evaluateAccessPolicy();
347
+ const approvalRequired = policy.decision === 'approval_required';
441
348
 
442
- // Policy approval decisions stop before Work creation. Ordinary host-managed local work
443
- // reaches this point only after the same Route Policy has been replayed with the Access
444
- // Policy's explicit allowed decision as authorization evidence.
349
+ // Policy approval decisions stop before Work creation. Ordinary host-managed
350
+ // local work reaches this point only after the Access Policy authorized it.
445
351
  const blockForHandoff =
446
- mode.mode === 'handoff_only'
447
- || policy.decision === 'denied'
352
+ policy.decision === 'denied'
448
353
  || policy.decision === 'approval_required';
449
354
 
450
355
  if (blockForHandoff) {
@@ -453,20 +358,19 @@ export function routeWorkStart(
453
358
  repoId: ctx.repoId,
454
359
  title: 'Work blocked pending decision',
455
360
  severity: policy.decision === 'denied' ? 'blocked' : 'needs_review',
456
- creationReason: !input.modeInput.scopeClear
361
+ creationReason: !input.request.scopeClear
457
362
  ? 'invalid_objective'
458
363
  : approvalRequired
459
- ? (input.modeInput.destructive ? 'destructive_action_requires_confirmation' : 'policy_approval_required')
364
+ ? (input.request.destructive ? 'destructive_action_requires_confirmation' : 'policy_approval_required')
460
365
  : 'missing_authorization',
461
- reason: mode.reason,
462
- summary: `Handoff-only routing: ${mode.reason}`,
366
+ reason: policy.reason,
367
+ summary: `Execution blocked by access policy: ${policy.reason}`,
463
368
  currentState: {
464
369
  repoId: ctx.repoId,
465
- mode: 'handoff_only',
466
370
  statusSummary: 'waiting for ChatGPT or user decision; no execution started',
467
- blockedBy: mode.missingContractFields,
371
+ blockedBy: missingContractFields,
468
372
  },
469
- attemptedActions: ['route_execution_mode'],
373
+ attemptedActions: ['work_start'],
470
374
  evidenceRefs: [],
471
375
  blockingDecision: approvalRequired
472
376
  ? 'Approve side effects or restate a safer objective.'
@@ -479,27 +383,19 @@ export function routeWorkStart(
479
383
  operation: 'start',
480
384
  label: 'Approve and start work',
481
385
  summary: 'Create the work contract with the original scope and explicit approval.',
482
- risk: input.modeInput.destructive ? 'destructive' : 'workspace_write',
386
+ risk: input.request.destructive ? 'destructive' : 'workspace_write',
483
387
  payload: {
484
388
  objective: input.objective,
485
389
  acceptanceCriteria: input.acceptanceCriteria,
486
390
  allowedPaths: input.allowedPaths,
487
391
  forbiddenPaths: input.forbiddenPaths,
488
392
  checkIds: input.checks,
489
- expectedFiles: input.modeInput.expectedFiles,
490
- expectedChangedLines: input.modeInput.expectedChangedLines,
491
- scopeClear: input.modeInput.scopeClear,
492
- requiresInvestigation: input.modeInput.requiresInvestigation,
493
- requiresLongRunningChecks: input.modeInput.requiresLongRunningChecks,
494
- requiresWorker: input.modeInput.requiresWorker,
495
- requiresApproval: input.modeInput.requiresApproval === true || input.modeInput.requiresUserApproval === true,
496
- destructive: input.modeInput.destructive === true,
393
+ scopeClear: input.request.scopeClear,
394
+ requiresApproval: input.request.requiresApproval === true || input.request.requiresUserApproval === true,
395
+ destructive: input.request.destructive === true,
497
396
  accessMode: input.constraints?.accessMode,
498
- workspaceMode: placementConstraint.workspaceMode,
499
- requireWorktree: placementConstraint.requireWorktree,
500
- directMainProhibited: placementConstraint.directMainProhibited,
397
+ workspaceMode,
501
398
  approvalConfirmed: true,
502
- forceMode: 'goal_workloop',
503
399
  },
504
400
  }
505
401
  : undefined,
@@ -516,9 +412,8 @@ export function routeWorkStart(
516
412
 
517
413
  return buildFacadeResult({
518
414
  status: policy.decision === 'denied' ? 'blocked' : approvalRequired ? 'approval_required' : 'blocked',
519
- summary: `Handoff-only: no WorkContract created and no execution started. ${mode.reason}`,
415
+ summary: 'Blocked: no WorkContract created and no execution started until the policy decision is resolved.',
520
416
  data: {
521
- mode,
522
417
  policy,
523
418
  workContractCreated: false,
524
419
  handoffId: handoff.id,
@@ -548,33 +443,20 @@ export function routeWorkStart(
548
443
  return startGoalWorkloop(ctx, {
549
444
  ...input,
550
445
  constraints: canonicalConstraints,
551
- modeInput: effectiveModeInput,
446
+ request: effectiveRequest,
552
447
  workKind: effectiveWorkKind,
553
- }, policy, 'goal_workloop', mode.routeDecision);
448
+ }, policy);
554
449
  }
555
450
 
556
451
  export function startGoalWorkloop(
557
452
  ctx: GoalWorkloopContext,
558
453
  input: GoalWorkloopStartInput,
559
454
  policy?: PolicyDecision,
560
- executionMode: 'direct_control' | 'goal_workloop' = 'goal_workloop',
561
- routeDecision = selectExecutionMode({ ...input.modeInput, objective: input.objective, knownPaths: input.allowedPaths }).routeDecision,
562
455
  ): FacadeResult {
563
- const placementResolution = resolveWorkspaceAdmissionConstraint(input.constraints);
564
- if (placementResolution.ok === false) {
565
- return buildFacadeResult({
566
- status: 'blocked',
567
- summary: `${placementResolution.code}: ${placementResolution.message}`,
568
- data: { executionStarted: false, workContractCreated: false, placementConstraintConflict: true },
569
- rawAvailable: false,
570
- });
571
- }
572
- const placementConstraint = placementResolution.constraint;
456
+ const workspaceMode: 'current' | 'isolated' = input.constraints?.workspaceMode === 'isolated' || input.constraints?.workspaceMode === 'auto' ? 'isolated' : 'current';
573
457
  const canonicalConstraints: WorkContract['constraints'] = {
574
458
  ...(input.constraints ?? {}),
575
- workspaceMode: placementConstraint.workspaceMode,
576
- requireWorktree: placementConstraint.requireWorktree,
577
- directMainProhibited: placementConstraint.directMainProhibited,
459
+ workspaceMode,
578
460
  };
579
461
  const hasStrongAdmissionBinding = Boolean(input.planId || input.planStepId || input.requirementId || input.relatedWorkId || input.workRelation);
580
462
  if (hasStrongAdmissionBinding && !ctx.semanticAdmissionLocked) {
@@ -582,38 +464,134 @@ export function startGoalWorkloop(
582
464
  { ...ctx, semanticAdmissionLocked: true },
583
465
  input,
584
466
  policy,
585
- executionMode,
586
- routeDecision,
587
467
  ));
588
468
  }
589
469
  const at = nowIso(ctx);
470
+ let resolvedWorkKind: WorkKind;
471
+ try {
472
+ resolvedWorkKind = resolvedWorkKindFor(input);
473
+ } catch (error) {
474
+ return buildFacadeResult({
475
+ status: 'blocked',
476
+ summary: error instanceof Error ? error.message : 'WORK_KIND_RESOLUTION_INVALID',
477
+ data: { executionStarted: false, workContractCreated: false },
478
+ rawAvailable: false,
479
+ });
480
+ }
481
+ const workRisk = workRiskFor(input);
482
+ // External-effect risk is governed by authorization/confirmation/receipt
483
+ // semantics. Engineering admission is specifically for repository mutation.
484
+ const engineeringRisk: WorkRisk = resolvedWorkKind === 'repository_change' ? workRisk : 'readonly';
485
+ const engineeringProfile = engineeringWorkProfileForRisk(engineeringRisk);
486
+ const engineeringSourceIdentity = ctx.sourceRevision?.trim()
487
+ ? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
488
+ : ctx.sourceBaseState === 'unborn'
489
+ ? { kind: 'unborn' as const }
490
+ : { kind: 'unknown' as const };
491
+ let engineeringContext;
492
+ try {
493
+ engineeringContext = buildEngineeringContextReceipt({
494
+ risk: engineeringRisk,
495
+ sourceIdentity: engineeringSourceIdentity,
496
+ evidence: input.verifiedEngineeringEvidence,
497
+ recordedAt: at,
498
+ });
499
+ } catch (error) {
500
+ return buildFacadeResult({
501
+ status: 'blocked',
502
+ summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
503
+ data: { executionStarted: false, workContractCreated: false, engineeringProfile },
504
+ });
505
+ }
506
+ const engineeringMutation = resolvedWorkKind === 'repository_change'
507
+ && (input.request.mutation ?? input.request.risk !== 'readonly');
508
+ // Engineering context is durable model/project guidance only. Safety and
509
+ // capability admission is enforced by the concrete executor, never here.
510
+ void engineeringMutation;
590
511
  const available = ctx.availableChecks ?? [];
591
- const workspaceMode = placementConstraint.workspaceMode;
592
- const activeWorks = listWorkContracts({ ...ctx.workStore, status: 'active', limit: 100 })
512
+ const activeAdmissionSnapshot = readActiveWorkCandidates({ ...ctx.workStore, limit: 100 });
513
+ const activeWorks = activeAdmissionSnapshot.contracts
593
514
  .filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary');
594
- const workspaceOwner = ctx.checkoutId
595
- ? activeWorks.find((candidate) => candidate.checkoutId === ctx.checkoutId
596
- && candidate.worktreePolicy.required !== true
597
- && !candidate.worktreeRef)
515
+ const invalidRelatedWork = input.relatedWorkId
516
+ ? activeAdmissionSnapshot.invalid.find((candidate) => candidate.workId === input.relatedWorkId)
598
517
  : undefined;
518
+ if (invalidRelatedWork) {
519
+ return buildFacadeResult({
520
+ status: 'blocked',
521
+ summary: `WORK_RELATED_CONTRACT_INVALID: ${invalidRelatedWork.workId} is malformed and cannot authorize a Work relationship.`,
522
+ data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidRelatedWork.workId, invalidWorkError: invalidRelatedWork.error },
523
+ });
524
+ }
525
+ const relatedLifecycleSource = input.relatedWorkId
526
+ ? getWorkContract(ctx.workStore, input.relatedWorkId)
527
+ : undefined;
528
+ const terminalContinuationSource = input.workRelation === 'continue'
529
+ && relatedLifecycleSource
530
+ && semanticWorkState(relatedLifecycleSource) !== 'open'
531
+ ? relatedLifecycleSource
532
+ : undefined;
533
+ if (terminalContinuationSource && !terminalContinuationSource.requirementId && !terminalContinuationSource.planId) {
534
+ return buildFacadeResult({
535
+ status: 'blocked',
536
+ summary: `WORK_SUCCESSOR_SEMANTIC_SCOPE_REQUIRED: ${terminalContinuationSource.workId} is terminal but has no Requirement/Plan lineage. Use new_goal or bind an explicit durable semantic authority instead of guessing continuity.`,
537
+ data: { executionStarted: false, workContractCreated: false, relatedWorkId: terminalContinuationSource.workId },
538
+ });
539
+ }
599
540
  const explicitRelatedWork = input.relatedWorkId
600
541
  ? activeWorks.find((candidate) => candidate.workId === input.relatedWorkId)
601
542
  : undefined;
602
- const plan = input.planId && ctx.planStore ? getPlanContract(ctx.planStore, input.planId) : undefined;
603
- const planStep = plan && input.planStepId ? plan.steps.find((step) => step.id === input.planStepId) : undefined;
543
+
544
+ let resolvedPlanId = input.planId?.trim() || undefined;
545
+ const resolvedPlanStepId = input.planStepId?.trim() || undefined;
546
+ if (terminalContinuationSource?.planId) {
547
+ if (resolvedPlanId && resolvedPlanId !== terminalContinuationSource.planId) {
548
+ return buildFacadeResult({
549
+ status: 'blocked',
550
+ summary: `WORK_SUCCESSOR_PLAN_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${terminalContinuationSource.planId}, not ${resolvedPlanId}. Supersede/replan explicitly instead of silently moving continuation authority.`,
551
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorPlanId: terminalContinuationSource.planId, requestedPlanId: resolvedPlanId },
552
+ });
553
+ }
554
+ resolvedPlanId = terminalContinuationSource.planId;
555
+ }
556
+ // Plan and Plan item references are recorded provenance for this Work. They
557
+ // never select the successor, own the scope, gate admission or require
558
+ // acceptance; only the Plan's existence is validated so a Work cannot record a
559
+ // reference to a Plan authority that does not exist.
560
+ const plan = resolvedPlanId && ctx.planStore ? getPlanContract(ctx.planStore, resolvedPlanId) : undefined;
561
+ if (resolvedPlanId && !plan) {
562
+ return buildFacadeResult({
563
+ status: 'blocked',
564
+ summary: terminalContinuationSource?.planId
565
+ ? `WORK_SUCCESSOR_PLAN_NOT_FOUND: ${terminalContinuationSource.planId}. Recover the durable Plan authority before creating a successor Work.`
566
+ : `PLAN_NOT_FOUND: ${resolvedPlanId}. Recover or create the semantic Plan before using it as Work provenance.`,
567
+ data: {
568
+ executionStarted: false,
569
+ workContractCreated: false,
570
+ planId: resolvedPlanId,
571
+ ...(terminalContinuationSource?.planId ? { predecessorWorkId: terminalContinuationSource.workId } : {}),
572
+ },
573
+ });
574
+ }
604
575
  const requestedRequirementId = input.requirementId?.trim() || undefined;
605
- if (plan && requestedRequirementId && requestedRequirementId !== plan.requirementId) {
576
+ const predecessorRequirementId = terminalContinuationSource?.requirementId;
577
+ if (predecessorRequirementId && requestedRequirementId && predecessorRequirementId !== requestedRequirementId) {
606
578
  return buildFacadeResult({
607
579
  status: 'blocked',
608
- summary: `PLAN_REQUIREMENT_MISMATCH: ${plan.planId} is bound to ${plan.requirementId ?? 'no Requirement'}, not ${requestedRequirementId}. Use the Plan relationship authority or replan explicitly.`,
609
- data: { executionStarted: false, workContractCreated: false, planId: plan.planId, planRequirementId: plan.requirementId, requestedRequirementId },
580
+ summary: `WORK_SUCCESSOR_REQUIREMENT_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${predecessorRequirementId}, not ${requestedRequirementId}.`,
581
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorRequirementId, requestedRequirementId },
610
582
  });
611
583
  }
612
- const effectiveRequirementId = requestedRequirementId || plan?.requirementId;
584
+ // A selected Plan supplies its Requirement when no explicit/predecessor link exists.
585
+ // Requirement membership alone never selects sibling Plans or successor Works.
586
+ const effectiveRequirementId = requestedRequirementId || predecessorRequirementId || plan?.requirementId;
587
+ if (plan?.requirementId && effectiveRequirementId && plan.requirementId !== effectiveRequirementId) {
588
+ return buildFacadeResult({ status: 'blocked', summary: `WORK_PLAN_REQUIREMENT_MISMATCH: ${resolvedPlanId} belongs to ${plan.requirementId}, not ${effectiveRequirementId}.`, data: { executionStarted: false, workContractCreated: false } });
589
+ }
590
+ const effectiveRequirementRecord = effectiveRequirementId && ctx.workStore.controllerHome ? readRequirement({ controllerHome: ctx.workStore.controllerHome }, effectiveRequirementId) : undefined;
613
591
  if (
614
592
  effectiveRequirementId
615
593
  && ctx.workStore.controllerHome
616
- && !readRequirement({ controllerHome: ctx.workStore.controllerHome }, effectiveRequirementId)
594
+ && !effectiveRequirementRecord
617
595
  ) {
618
596
  return buildFacadeResult({
619
597
  status: 'blocked',
@@ -621,49 +599,55 @@ export function startGoalWorkloop(
621
599
  data: { executionStarted: false, workContractCreated: false, requirementId: effectiveRequirementId, planId: plan?.planId },
622
600
  });
623
601
  }
624
- const sameStringSet = (provided: string[] | undefined, frozen: string[]): boolean => {
625
- if (provided === undefined) return true;
626
- const actual = [...new Set(provided)].sort();
627
- const expected = [...new Set(frozen)].sort();
628
- return actual.length === expected.length && actual.every((value, index) => value === expected[index]);
629
- };
630
- if (planStep) {
631
- const mismatches = [
632
- !sameStringSet(input.acceptanceCriteria, planStep.acceptanceCriteria) && 'acceptance_criteria',
633
- !sameStringSet(input.allowedPaths, planStep.allowedPaths) && 'allowed_paths',
634
- !sameStringSet(input.forbiddenPaths, planStep.forbiddenPaths) && 'forbidden_paths',
635
- !sameStringSet(input.checks, planStep.checks) && 'check_ids',
636
- ].filter((value): value is string => Boolean(value));
637
- if (mismatches.length > 0) {
638
- return buildFacadeResult({
639
- status: 'blocked',
640
- summary: `PLAN_STEP_WORK_CONTRACT_MISMATCH: ${input.planId}/${input.planStepId} conflicts with frozen Plan step field(s): ${mismatches.join(', ')}. Replan instead of widening or narrowing Work admission.`,
641
- data: { executionStarted: false, workContractCreated: false, planId: input.planId, planStepId: input.planStepId, mismatches },
642
- });
643
- }
644
- }
645
- const effectiveAcceptanceCriteria = planStep?.acceptanceCriteria ?? input.acceptanceCriteria ?? [];
646
- const effectiveAllowedPaths = planStep?.allowedPaths ?? input.allowedPaths ?? [];
647
- const effectiveForbiddenPaths = planStep?.forbiddenPaths ?? input.forbiddenPaths ?? [];
648
- const effectiveChecks = planStep?.checks ?? input.checks ?? [];
602
+ // Work scope is authored by the caller. A referenced Plan item contributes no
603
+ // objective, paths, checks or acceptance criteria.
604
+ const effectiveObjective = input.objective;
605
+ const effectiveAcceptanceCriteria = input.acceptanceCriteria ?? [];
606
+ const effectiveAllowedPaths = input.allowedPaths ?? [];
607
+ const effectiveForbiddenPaths = input.forbiddenPaths ?? [];
608
+ const effectiveChecks = input.checks ?? [];
649
609
  const normalized = normalizeCheckIds(effectiveChecks, available);
650
- const boundPlanStepWorks = input.planId && input.planStepId
651
- ? activeWorks.filter((candidate) => candidate.planId === input.planId && candidate.planStepId === input.planStepId)
652
- : [];
653
- const explicitPlanStepWork = planStep?.workId ? activeWorks.find((candidate) => candidate.workId === planStep.workId) : undefined;
654
- const planStepWork = explicitPlanStepWork ?? (boundPlanStepWorks.length === 1 ? boundPlanStepWorks[0] : undefined);
655
- const requirementWorks = effectiveRequirementId
656
- ? activeWorks.filter((candidate) => candidate.requirementId === effectiveRequirementId)
657
- : [];
658
- const deterministicTarget = input.relatedWorkId
659
- ? explicitRelatedWork
660
- : planStepWork ?? (requirementWorks.length === 1 ? requirementWorks[0] : undefined);
661
- const requestedRelation = input.workRelation ?? (input.modeInput.requiresParallelism === true ? 'parallel' : undefined);
610
+ const invalidLineageWork = activeAdmissionSnapshot.invalid.find((candidate) =>
611
+ (input.relatedWorkId && candidate.workId === input.relatedWorkId));
612
+ if (invalidLineageWork) {
613
+ return buildFacadeResult({
614
+ status: 'blocked',
615
+ summary: `WORK_ADMISSION_INVALID_SEMANTIC_AUTHORITY: ${invalidLineageWork.workId} is malformed but still claims the requested durable Plan/Requirement lineage. Repair that exact authority before creating a sibling Work.`,
616
+ data: {
617
+ executionStarted: false,
618
+ workContractCreated: false,
619
+ invalidWorkId: invalidLineageWork.workId,
620
+ invalidWorkError: invalidLineageWork.error,
621
+ planId: resolvedPlanId,
622
+ planStepId: resolvedPlanStepId,
623
+ requirementId: effectiveRequirementId,
624
+ },
625
+ });
626
+ }
627
+ const newWorkWillBeIsolated = workspaceMode === 'isolated';
628
+ const invalidSharedWorkspaceOwner = !newWorkWillBeIsolated
629
+ ? activeAdmissionSnapshot.invalid.find((candidate) => candidate.isolation === 'shared'
630
+ && (!candidate.checkoutId || candidate.checkoutId === ctx.checkoutId))
631
+ : undefined;
632
+ if (invalidSharedWorkspaceOwner) {
633
+ return buildFacadeResult({
634
+ status: 'blocked',
635
+ summary: `WORK_ADMISSION_INVALID_WORKSPACE_AUTHORITY: ${invalidSharedWorkspaceOwner.workId} is malformed and may still own the shared checkout; isolated placement or exact authority repair is required.`,
636
+ data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidSharedWorkspaceOwner.workId, invalidWorkError: invalidSharedWorkspaceOwner.error },
637
+ });
638
+ }
639
+ // Requirement membership is portfolio ownership, not semantic Work identity.
640
+ // Only an explicit related Work may select an existing Work authority. A Plan
641
+ // item never selects, owns or reuses Work. Siblings under one Requirement
642
+ // remain unrelated for semantic admission and meet only in
643
+ // placement/resource arbitration.
644
+ const deterministicTarget = input.relatedWorkId ? explicitRelatedWork : undefined;
645
+ const requestedRelation = input.workRelation;
662
646
  // Only strong semantic bindings participate in ownership resolution. An
663
647
  // unrelated active Work or a checkout writer is a placement fact, not a
664
648
  // semantic candidate for continue/extend/parallel/new_goal.
665
649
  const candidateWorks = [...new Map(
666
- [explicitRelatedWork, planStepWork, ...boundPlanStepWorks, ...requirementWorks]
650
+ [explicitRelatedWork]
667
651
  .filter((candidate): candidate is WorkContract => Boolean(candidate))
668
652
  .map((candidate) => [candidate.workId, candidate]),
669
653
  ).values()].slice(0, 8);
@@ -684,39 +668,18 @@ export function startGoalWorkloop(
684
668
  evidenceRefs: target?.evidenceRefs ?? [],
685
669
  suggestedNextActions: target
686
670
  ? [{
687
- label: 'Continue existing Work',
671
+ label: 'Inspect existing Work',
688
672
  tool: 'rh_work',
689
- operation: 'continue',
673
+ operation: 'get',
690
674
  payload: { work_id: target.workId },
691
675
  risk: 'readonly',
692
- confidence: planStepWork || explicitRelatedWork ? 'high' : 'medium',
693
- reason: 'Resolve intent before execution; do not create a second Work until the relationship is explicit.',
676
+ confidence: explicitRelatedWork ? 'high' : 'medium',
677
+ reason: 'Resolve intent before execution; semantic Work inspection does not progress lifecycle state.',
694
678
  }]
695
679
  : [],
696
680
  rawAvailable: false,
697
681
  });
698
682
 
699
- if (boundPlanStepWorks.length > 1) {
700
- return resolutionRequired(`PLAN_STEP_MULTIPLE_PRIMARY_WORKS: ${input.planId}/${input.planStepId} has ${boundPlanStepWorks.length} active primary Work records and requires repair before execution.`);
701
- }
702
- if (planStep?.workId && !explicitPlanStepWork) {
703
- return buildFacadeResult({
704
- status: 'blocked',
705
- summary: `PLAN_STEP_BOUND_WORK_MISSING: ${input.planId}/${input.planStepId} is bound to ${planStep.workId}, but that Work is not active. Repair the exact binding; do not create a replacement Work.`,
706
- data: { executionStarted: false, workContractCreated: false, planId: input.planId, planStepId: input.planStepId, boundWorkId: planStep.workId, repairRequired: true },
707
- suggestedNextActions: [{
708
- label: 'Diagnose exact Plan step binding',
709
- tool: 'rh_work',
710
- operation: 'repair',
711
- payload: { plan_id: input.planId, plan_step_id: input.planStepId, repair_operation: 'diagnose', dry_run: true },
712
- risk: 'readonly',
713
- confidence: 'high',
714
- reason: 'The Plan step has an exact Work identity but the active Work view cannot resolve it. Diagnose that binding before any replacement Work can be admitted.',
715
- }],
716
- rawAvailable: false,
717
- });
718
- }
719
-
720
683
  if ((input.requestedBy ?? 'chatgpt') === 'scheduler') {
721
684
  return resolutionRequired(
722
685
  deterministicTarget
@@ -725,42 +688,17 @@ export function startGoalWorkloop(
725
688
  );
726
689
  }
727
690
 
728
- if (planStepWork && !requestedRelation) {
729
- return buildFacadeResult({
730
- status: 'ok',
731
- summary: `PLAN_STEP_REUSES_ACTIVE_WORK: ${input.planId}/${input.planStepId} already executes as ${planStepWork.workId}; reuse that Work instead of creating another.`,
732
- data: {
733
- executionStarted: false,
734
- workContractCreated: false,
735
- admissionDecision: 'reuse_existing',
736
- resolutionRequired: false,
737
- work: summarizeWorkContract(planStepWork),
738
- },
739
- evidenceRefs: planStepWork.evidenceRefs,
740
- suggestedNextActions: [{ label: 'Continue existing Work', tool: 'rh_work', operation: 'continue', payload: { work_id: planStepWork.workId }, risk: 'readonly', confidence: 'high' }],
741
- rawAvailable: false,
742
- });
743
- }
744
-
745
- if (requestedRelation === 'continue' || requestedRelation === 'extend') {
691
+ if ((requestedRelation === 'continue' || requestedRelation === 'extend') && !terminalContinuationSource) {
746
692
  if (!deterministicTarget) {
747
- return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id or bind the request to an active Plan/Requirement before execution.`);
748
- }
749
- if (requestedRelation === 'extend' && deterministicTarget.planId) {
750
- return resolutionRequired(
751
- `PLAN_EXTENSION_REQUIRES_REPLAN: Work ${deterministicTarget.workId} is governed by Plan ${deterministicTarget.planId}; record additional discovery in scopeEvidence, but update the authoritative Plan before widening policy or acceptance scope.`,
752
- deterministicTarget,
753
- );
693
+ return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id before execution.`);
754
694
  }
755
695
  const selected = requestedRelation === 'extend'
756
696
  ? updateWorkContract(ctx.workStore, deterministicTarget.workId, {
757
697
  acceptanceCriteria: [...new Set([...deterministicTarget.acceptanceCriteria, ...(input.acceptanceCriteria ?? [])])].slice(0, 30),
758
- allowedPaths: [...new Set([...deterministicTarget.allowedPaths, ...(input.allowedPaths ?? [])])].slice(0, 50),
759
- forbiddenPaths: [...new Set([...deterministicTarget.forbiddenPaths, ...(input.forbiddenPaths ?? [])])].slice(0, 50),
760
698
  checks: [...new Set([...deterministicTarget.checks, ...normalized.validCheckIds])].slice(0, 30),
761
699
  scopeEvidence: {
762
700
  initialLikelyPaths: [...new Set([
763
- ...(deterministicTarget.scopeEvidence?.initialLikelyPaths ?? deterministicTarget.allowedPaths),
701
+ ...(deterministicTarget.scopeEvidence?.initialLikelyPaths ?? []),
764
702
  ...(input.initialLikelyPaths ?? input.allowedPaths ?? []),
765
703
  ])].slice(0, 100),
766
704
  inspectedPaths: deterministicTarget.scopeEvidence?.inspectedPaths ?? [],
@@ -782,7 +720,7 @@ export function startGoalWorkloop(
782
720
  work: summarizeWorkContract(selected),
783
721
  },
784
722
  evidenceRefs: selected.evidenceRefs,
785
- suggestedNextActions: [{ label: 'Continue existing Work', tool: 'rh_work', operation: 'continue', payload: { work_id: selected.workId }, risk: 'readonly', confidence: 'high' }],
723
+ suggestedNextActions: [{ label: 'Read existing Work context', tool: 'rh_work', operation: 'get', payload: { work_id: selected.workId }, risk: 'readonly', confidence: 'high' }],
786
724
  rawAvailable: false,
787
725
  });
788
726
  }
@@ -793,112 +731,72 @@ export function startGoalWorkloop(
793
731
  );
794
732
  }
795
733
 
796
- const placementConflict = Boolean(workspaceOwner);
797
- const needsWorktree = executionMode === 'goal_workloop' && (placementConstraint.requireWorktree
798
- || placementConflict
799
- || input.modeInput.requiresParallelism === true
800
- || routeDecision.requiresIsolation === true);
801
- if (!needsWorktree && workspaceOwner) {
734
+ // Repository topology is caller-owned. Forge records the selected placement
735
+ // and lets concrete WorkHandle/Lease/Git authorities report real conflicts at
736
+ // the mutation boundary instead of inferring isolation from semantic context.
737
+ const needsWorktree = workspaceMode === 'isolated';
738
+ const requestedWorkId = input.workId?.trim();
739
+ if (requestedWorkId && !/^work-[a-z0-9][a-z0-9-]{0,199}$/i.test(requestedWorkId)) {
802
740
  return buildFacadeResult({
803
741
  status: 'blocked',
804
- summary: `WORKSPACE_OWNERSHIP_INVARIANT: unresolved admission reached checkout ${ctx.checkoutId} while active Work ${workspaceOwner.workId} owns it. This is an internal routing invariant, not a normal fallback path.`,
805
- data: {
806
- executionStarted: false,
807
- workContractCreated: false,
808
- conflictType: 'workspace_single_writer_invariant',
809
- existingWork: summarizeWorkContract(workspaceOwner),
810
- },
811
- evidenceRefs: workspaceOwner.evidenceRefs,
812
- rawAvailable: false,
742
+ summary: 'WORK_ID_INVALID: explicit Work ids must use the canonical work-<slug> form.',
743
+ data: { executionStarted: false, workContractCreated: false },
813
744
  });
814
745
  }
815
- const generatedWorkId = workIdFor(input.objective);
816
- if (input.planId || input.planStepId) {
817
- if (!input.planId || !input.planStepId || !ctx.planStore || !ctx.sourceRevision || !plan || !planStep) {
818
- return buildFacadeResult({ status: 'blocked', summary: 'PLAN_CONTEXT_REQUIRED: plan_id, plan_step_id, an executable Plan step and a current source revision are required.', data: { executionStarted: false } });
819
- }
820
- if (plan.status !== 'approved' && plan.status !== 'executing') {
821
- return buildFacadeResult({ status: 'blocked', summary: `PLAN_NOT_EXECUTABLE: ${plan.planId} is ${plan.status}`, data: { executionStarted: false, planId: plan.planId } });
822
- }
823
- if (plan.sourceRevision !== ctx.sourceRevision) {
824
- const invalidated = claimPlanStepForWork(ctx.planStore, {
825
- planId: input.planId,
826
- stepId: input.planStepId,
827
- workId: generatedWorkId,
828
- sourceRevision: ctx.sourceRevision,
829
- });
830
- return buildFacadeResult({
831
- status: 'blocked',
832
- summary: `PLAN_SOURCE_DRIFT: ${invalidated.planId} was invalidated because its source revision no longer matches. Replan before execution.`,
833
- data: { planId: invalidated.planId, executionStarted: false, workContractCreated: false, replanRequired: true },
834
- });
835
- }
836
- const unresolved = planStep.dependencies.filter((dependency) => plan.steps.find((candidate) => candidate.id === dependency)?.status !== 'completed');
837
- if (unresolved.length > 0) {
838
- return buildFacadeResult({ status: 'blocked', summary: `PLAN_STEP_DEPENDENCIES_PENDING: ${unresolved.join(', ')}`, data: { executionStarted: false, workContractCreated: false, planId: plan.planId, planStepId: planStep.id } });
839
- }
840
- if (planStep.status === 'completed') {
841
- return buildFacadeResult({ status: 'blocked', summary: `PLAN_STEP_ALREADY_COMPLETED: ${planStep.id}`, data: { executionStarted: false, workContractCreated: false, planId: plan.planId, planStepId: planStep.id } });
842
- }
843
- }
746
+ const generatedWorkId = requestedWorkId ?? workIdFor(effectiveObjective);
844
747
  const initialSuggestedNextActions = suggestedForWorkIdentity(
845
748
  generatedWorkId,
846
749
  normalized.validCheckIds,
847
750
  normalized.suggestedNextActions,
848
751
  );
849
- // allowed_paths and initial_likely_paths are policy/discovery fences, not proof
850
- // that this Work owns a repository mutation. External-effect Work therefore
851
- // needs either an explicit repository_change semantic choice or concrete
852
- // expected source-change evidence before Forge treats it as repository_change.
853
- const repositoryChangeIntent = input.workKind === 'repository_change'
854
- || (input.modeInput.expectedFiles ?? 0) > 0
855
- || (input.modeInput.expectedChangedLines ?? 0) > 0;
856
- const typedRecoverableReadOnlyReview = input.workKind === undefined
857
- && input.modeInput.mutation === false
858
- && input.modeInput.requiresInvestigation === true
859
- && input.modeInput.requiresRecovery === true;
860
- const resolvedWorkKind = input.workKind ?? (
861
- typedRecoverableReadOnlyReview
862
- ? 'read_only_review'
863
- : input.modeInput.requiresExternalEffect === true
864
- && !repositoryChangeIntent
865
- ? (input.modeInput.remoteWrite === true ? 'remote_effect' : 'local_effect')
866
- : 'repository_change'
867
- );
868
752
  const remoteDeliveryRequired = resolvedWorkKind === 'repository_change'
869
- && input.modeInput.requiresExternalEffect === true
870
- && input.modeInput.remoteWrite === true;
753
+ && input.request.requiresExternalEffect === true
754
+ && input.request.remoteWrite === true;
755
+ const effectiveConstraints: WorkContract['constraints'] = {
756
+ ...canonicalConstraints,
757
+ ...(remoteDeliveryRequired ? { remoteDeliveryRequired: true } : {}),
758
+ };
759
+ const worktreeReason = needsWorktree
760
+ ? 'Caller explicitly selected isolated repository placement.'
761
+ : 'Caller selected the current repository checkout.';
762
+ const forgeInstanceId = ctx.workStore.controllerHome
763
+ ? readForgeInstanceIdentity(ctx.workStore.controllerHome)?.instanceId
764
+ : undefined;
871
765
  const work = createWorkContract(ctx.workStore, {
872
766
  workId: generatedWorkId,
873
767
  repoId: ctx.repoId,
874
768
  checkoutId: needsWorktree ? undefined : ctx.checkoutId,
769
+ executionPlacement: executionPlacement({
770
+ ...(forgeInstanceId ? { forgeInstanceId } : {}),
771
+ repositoryId: ctx.repoId,
772
+ ...(!needsWorktree && ctx.checkoutId ? { checkoutId: ctx.checkoutId } : {}),
773
+ }),
875
774
  principalId: ctx.principalId,
876
775
  controllerInstanceId: ctx.controllerInstanceId,
877
776
  baseRevision: ctx.sourceRevision,
777
+ repositoryBaseState: ctx.sourceBaseState ?? (ctx.sourceRevision ? 'revision' : undefined),
878
778
  workspaceFingerprint: needsWorktree ? undefined : ctx.workspaceFingerprint,
879
- routeDecisionFingerprint: routeDecision.inputFingerprint,
880
- routeDecision,
881
- mode: executionMode,
882
- objective: input.objective,
779
+ objective: effectiveObjective,
883
780
  acceptanceCriteria: effectiveAcceptanceCriteria,
884
- constraints: {
885
- ...canonicalConstraints,
886
- ...(remoteDeliveryRequired ? { remoteDeliveryRequired: true } : {}),
887
- },
888
- risk: workRiskFor(input),
781
+ constraints: effectiveConstraints,
782
+ risk: workRisk,
783
+ engineeringContext,
889
784
  // Remote delivery is a risk/effect dimension, not a reason to erase a
890
785
  // repository-change Work's semantic identity. Pure external actions with no
891
786
  // repository-change signal remain remote_effect and keep plugin receipt semantics.
892
787
  workKind: resolvedWorkKind,
893
- status: 'running',
788
+ dispatchState: 'running',
894
789
  phase: 'implementation',
895
790
  issueId: input.issueId,
896
791
  taskId: input.taskId,
897
792
  requirementId: effectiveRequirementId,
898
- planId: input.planId,
899
- planStepId: input.planStepId,
900
- planSourceRevision: input.planId ? ctx.sourceRevision : undefined,
901
- scopeSummary: input.modeInput.scopeClear ? 'scope declared at start' : 'scope incomplete',
793
+ requirementRevision: effectiveRequirementRecord ? currentRequirementSemanticRevision(effectiveRequirementRecord.value) : undefined,
794
+ predecessorWorkId: terminalContinuationSource?.workId,
795
+ planId: resolvedPlanId,
796
+ planRevision: plan ? currentPlanSemanticRevision(plan) : undefined,
797
+ planStepId: resolvedPlanStepId,
798
+ planSourceRevision: resolvedPlanStepId ? plan?.sourceRevision : undefined,
799
+ scopeSummary: input.request.scopeClear ? 'scope declared at start' : 'scope incomplete',
902
800
  scopeEvidence: {
903
801
  initialLikelyPaths: [...new Set(input.initialLikelyPaths ?? effectiveAllowedPaths)].slice(0, 100),
904
802
  inspectedPaths: [],
@@ -908,18 +806,9 @@ export function startGoalWorkloop(
908
806
  allowedPaths: effectiveAllowedPaths,
909
807
  forbiddenPaths: effectiveForbiddenPaths,
910
808
  checks: normalized.validCheckIds,
911
- driver: {
912
- preferred: executionMode === 'direct_control'
913
- ? 'direct_edit'
914
- : input.modeInput.requiresWorker === true ? 'external_controller' : needsWorktree ? 'isolated_worktree' : 'direct_edit',
915
- allowWorker: false,
916
- allowDirectEdit: executionMode === 'direct_control' || (input.modeInput.requiresWorker !== true && !needsWorktree),
917
- },
918
809
  worktreePolicy: {
919
810
  required: needsWorktree,
920
- reason: needsWorktree
921
- ? 'Isolation was explicitly requested or required for parallel execution.'
922
- : 'Current workspace is the stability-first default; isolation remains opt-in.',
811
+ reason: worktreeReason,
923
812
  },
924
813
  worktreeRef: undefined,
925
814
  evidencePolicy: {
@@ -928,8 +817,8 @@ export function startGoalWorkloop(
928
817
  maxEvidenceRefs: 20,
929
818
  },
930
819
  approvalPolicy: {
931
- required: input.modeInput.requiresApproval === true || input.modeInput.requiresUserApproval === true,
932
- reasons: input.modeInput.requiresApproval || input.modeInput.requiresUserApproval ? ['approval requested at start'] : [],
820
+ required: input.request.requiresApproval === true || input.request.requiresUserApproval === true,
821
+ reasons: input.request.requiresApproval || input.request.requiresUserApproval ? ['approval requested at start'] : [],
933
822
  confirmed: input.approvalConfirmed === true,
934
823
  },
935
824
  recoveryPolicy: {
@@ -938,52 +827,26 @@ export function startGoalWorkloop(
938
827
  handoffOnAmbiguity: true,
939
828
  },
940
829
  requestedBy: input.requestedBy ?? 'chatgpt',
941
- evidenceRefs: [initialEvidence(input.objective)],
830
+ requestId: input.requestId?.trim() || undefined,
831
+ evidenceRefs: [initialEvidence(effectiveObjective)],
942
832
  policyDecisions: policy ? [policy] : [],
943
833
  suggestedNextActions: initialSuggestedNextActions,
944
- continuationPrompt: `Continue work ${ctx.repoId}: ${input.objective.slice(0, 200)}`,
834
+ continuationPrompt: `Continue work ${ctx.repoId}: ${effectiveObjective.slice(0, 200)}`,
945
835
  });
946
836
 
947
- if (input.planId && input.planStepId && ctx.planStore && ctx.sourceRevision) {
948
- try {
949
- const claimed = claimPlanStepForWork(ctx.planStore, {
950
- planId: input.planId,
951
- stepId: input.planStepId,
952
- workId: work.workId,
953
- sourceRevision: ctx.sourceRevision,
954
- });
955
- if (claimed.status === 'invalidated_by_drift') {
956
- return buildFacadeResult({
957
- status: 'blocked',
958
- summary: `PLAN_SOURCE_DRIFT: ${claimed.planId} was invalidated because its source revision no longer matches. Repair or replan before execution.`,
959
- data: { executionStarted: false, workContractCreated: true, work: summarizeWorkContract(work), planId: claimed.planId, canonicalWorkRetained: true, replanRequired: true },
960
- });
961
- }
962
- } catch (error) {
963
- return buildFacadeResult({
964
- status: 'blocked',
965
- summary: error instanceof Error ? error.message : 'PLAN_STEP_CLAIM_FAILED',
966
- data: { planId: input.planId, planStepId: input.planStepId, executionStarted: false, workContractCreated: true, work: summarizeWorkContract(work), canonicalWorkRetained: true },
967
- });
968
- }
969
- }
970
-
971
837
  return buildFacadeResult({
972
838
  status: 'ok',
973
- summary: executionMode === 'direct_control'
974
- ? `Direct-control Work lineage started as ${work.workId}.`
975
- : `Goal workloop started as ${work.workId}.`,
839
+ summary: terminalContinuationSource
840
+ ? `Successor Work ${work.workId} continues semantic lineage from terminal ${terminalContinuationSource.workId}${resolvedPlanId && resolvedPlanStepId ? ` at ${resolvedPlanId}/${resolvedPlanStepId}` : ''}.`
841
+ : `Work started as ${work.workId}.`,
976
842
  data: {
977
- mode: {
978
- mode: executionMode,
979
- reason: executionMode === 'direct_control'
980
- ? 'A lightweight WorkContract records the mutation while execution stays on Direct Control.'
981
- : 'WorkContract created for multi-step recoverable work.',
982
- createWorkContract: true,
983
- createHandoff: false,
984
- missingContractFields: [],
985
- requiresWork: true,
986
- routeDecision,
843
+ // Caller-visible placement facts only. Execution mode tokens, route reasons
844
+ // and RouteDecision are legacy orchestration metadata, not Work semantics.
845
+ placement: {
846
+ workContractCreated: true,
847
+ worktreeRequired: work.worktreePolicy.required,
848
+ isolated: work.worktreePolicy.required,
849
+ requiresRecovery: work.recoveryPolicy.allowSelfHealing === true || false,
987
850
  },
988
851
  workContractCreated: true,
989
852
  work: summarizeWorkContract(work),
@@ -1009,10 +872,10 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1009
872
  });
1010
873
  }
1011
874
 
1012
- if (work.status === 'cancelled' || work.status === 'completed' || work.status === 'failed') {
875
+ if (semanticWorkState(work) !== 'open') {
1013
876
  return buildFacadeResult({
1014
877
  status: 'blocked',
1015
- summary: `WorkContract ${work.workId} is terminal (${work.status}); continue is not allowed.`,
878
+ summary: `WorkContract ${work.workId} is terminal (${semanticWorkState(work)}); continue is not allowed.`,
1016
879
  data: { work: summarizeWorkContract(work) },
1017
880
  suggestedNextActions: [
1018
881
  {
@@ -1026,35 +889,123 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1026
889
  });
1027
890
  }
1028
891
 
1029
- if ((input.reviewFindings?.length ?? 0) > 0 && work.workKind !== 'read_only_review') {
892
+ if (semanticWorkState(work) === 'open' && hasSettledWorkDeliveryReceipt(work)) {
1030
893
  return buildFacadeResult({
1031
- status: 'blocked',
1032
- summary: `READ_ONLY_REVIEW_FINDINGS_KIND_REQUIRED: ${work.workId} is ${work.workKind}, not read_only_review.`,
1033
- data: { work: summarizeWorkContract(work), reviewFindingsRecorded: false },
894
+ status: 'ok',
895
+ summary: `Continue: physical delivery is settled for ${work.workId}; semantic Work remains open for explicit Controller judgment.`,
896
+ data: {
897
+ work: summarizeWorkContract(work),
898
+ backgroundCompleted: false,
899
+ nextStep: 'semantic_decision',
900
+ deliverySettled: true,
901
+ semanticWorkState: 'open',
902
+ },
903
+ evidenceRefs: work.evidenceRefs.slice(0, 5),
1034
904
  });
1035
905
  }
1036
- if (work.workKind === 'read_only_review') {
1037
- const sourceIdentity = evaluateReadOnlyReviewSourceIdentity(work, ctx.sourceRevision, ctx.workspaceChangedPaths);
1038
- if (sourceIdentity.status !== 'complete') {
906
+
907
+ if (input.verifiedEngineeringEvidence) {
908
+ const sourceIdentity = ctx.sourceRevision?.trim()
909
+ ? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
910
+ : ctx.sourceBaseState === 'unborn'
911
+ ? { kind: 'unborn' as const }
912
+ : { kind: 'unknown' as const };
913
+ let refreshedEngineeringContext;
914
+ try {
915
+ refreshedEngineeringContext = buildEngineeringContextReceipt({
916
+ risk: work.risk,
917
+ sourceIdentity,
918
+ evidence: input.verifiedEngineeringEvidence,
919
+ recordedAt: nowIso(ctx),
920
+ });
921
+ } catch (error) {
1039
922
  return buildFacadeResult({
1040
923
  status: 'blocked',
1041
- summary: `READ_ONLY_REVIEW_SOURCE_IDENTITY_REQUIRED: ${sourceIdentity.reasons.join(' ')}`,
1042
- data: { work: summarizeWorkContract(work), sourceIdentityProven: false },
924
+ summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
925
+ data: { work: summarizeWorkContract(work), engineeringEvidenceUpdated: false },
1043
926
  });
1044
927
  }
928
+ work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: refreshedEngineeringContext });
1045
929
  }
1046
930
 
931
+ if (input.engineeringBlocker) {
932
+ if (!work.engineeringContext || work.engineeringContext.sourceIdentity.kind !== 'revision') {
933
+ return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_SOURCE_IDENTITY_REQUIRED', data: { work: summarizeWorkContract(work) } });
934
+ }
935
+ let blocker;
936
+ let linkedWork: WorkContract | undefined;
937
+ try {
938
+ let linkedWorkId: string | undefined;
939
+ if (input.engineeringBlocker.classification === 'unrelated') {
940
+ // Forge does not decompose work on the model's behalf. An unrelated blocker
941
+ // must name the Work that already owns it; the caller creates that Work
942
+ // explicitly (rh_work start) and passes its exact id here.
943
+ const declaredLinkedWorkId = input.engineeringBlocker.linkedWorkId?.trim();
944
+ if (!declaredLinkedWorkId) {
945
+ return buildFacadeResult({
946
+ status: 'blocked',
947
+ summary: 'ENGINEERING_BLOCKER_LINKED_WORK_REQUIRED: an unrelated blocker must name the Work that owns it via linked_work_id; Forge never creates a child Work automatically.',
948
+ data: { work: summarizeWorkContract(work), linkedWorkCreated: false },
949
+ suggestedNextActions: [{
950
+ label: 'Start the owning Work explicitly',
951
+ tool: 'rh_work',
952
+ operation: 'start',
953
+ payload: { objective: `Resolve unrelated blocker ${input.engineeringBlocker.blockerId}: ${input.engineeringBlocker.rationale}` },
954
+ risk: 'readonly',
955
+ confidence: 'medium',
956
+ }],
957
+ });
958
+ }
959
+ linkedWork = getWorkContract(ctx.workStore, declaredLinkedWorkId);
960
+ if (!linkedWork) {
961
+ return buildFacadeResult({
962
+ status: 'blocked',
963
+ summary: `ENGINEERING_BLOCKER_LINKED_WORK_UNKNOWN: ${declaredLinkedWorkId} is not a readable Work.`,
964
+ data: { work: summarizeWorkContract(work), linkedWorkId: declaredLinkedWorkId, linkedWorkCreated: false },
965
+ });
966
+ }
967
+ linkedWorkId = linkedWork.workId;
968
+ }
969
+ blocker = buildEngineeringBlockerDispositionReceipt({
970
+ sourceRevision: work.engineeringContext.sourceIdentity.revision,
971
+ blockerId: input.engineeringBlocker.blockerId,
972
+ classification: input.engineeringBlocker.classification,
973
+ semanticScopeKeys: input.engineeringBlocker.semanticScopeKeys ?? work.engineeringContext.semanticScope?.keys ?? [],
974
+ ...(linkedWork ? { linkedWorkId: linkedWork.workId } : {}),
975
+ rationale: input.engineeringBlocker.rationale,
976
+ recordedAt: nowIso(ctx),
977
+ });
978
+ const nextEngineering = applyEngineeringBlockerDisposition(work.engineeringContext, blocker);
979
+ work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: nextEngineering });
980
+ } catch (error) {
981
+ return buildFacadeResult({ status: 'blocked', summary: error instanceof Error ? error.message : 'ENGINEERING_BLOCKER_INVALID', data: { work: summarizeWorkContract(work) } });
982
+ }
983
+ if (blocker.action === 'extend_candidate') {
984
+ work = appendWorkEvidence(ctx.workStore, work.workId, {
985
+ title: 'same-root candidate scope extension',
986
+ summary: `Blocker ${blocker.blockerId} remains inside the current architecture authority; continue the exact candidate without Design re-entry or sibling Work.`,
987
+ detailLevel: 'summary',
988
+ });
989
+ } else {
990
+ work = appendWorkEvidence(ctx.workStore, work.workId, {
991
+ title: 'engineering blocker observation',
992
+ summary: `Recorded ${blocker.classification} blocker ${blocker.blockerId}; it is model guidance, not a Work continuation gate.`,
993
+ detailLevel: 'summary',
994
+ });
995
+ }
996
+ }
997
+
998
+ if ((input.reviewFindings?.length ?? 0) > 0 && work.workKind !== 'read_only_review') {
999
+ return buildFacadeResult({
1000
+ status: 'blocked',
1001
+ summary: `READ_ONLY_REVIEW_FINDINGS_KIND_REQUIRED: ${work.workId} is ${work.workKind}, not read_only_review.`,
1002
+ data: { work: summarizeWorkContract(work), reviewFindingsRecorded: false },
1003
+ });
1004
+ }
1047
1005
  const explicitPolicyScope = input.allowedPaths !== undefined
1048
1006
  || input.forbiddenPaths !== undefined
1049
1007
  || input.checks !== undefined;
1050
1008
  if (explicitPolicyScope) {
1051
- if (work.planId) {
1052
- return buildFacadeResult({
1053
- status: 'blocked',
1054
- summary: `PLAN_EXTENSION_REQUIRES_REPLAN: Work ${work.workId} is governed by Plan ${work.planId}; continue cannot widen frozen policy scope. Replan the authoritative Plan instead.`,
1055
- data: { work: summarizeWorkContract(work), planId: work.planId, policyScopeUpdated: false },
1056
- });
1057
- }
1058
1009
  const normalizedChecks = normalizeCheckIds(input.checks ?? [], ctx.availableChecks ?? []);
1059
1010
  if (normalizedChecks.invalidCheckIds.length > 0) {
1060
1011
  return buildFacadeResult({
@@ -1064,12 +1015,10 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1064
1015
  });
1065
1016
  }
1066
1017
  work = updateWorkContract(ctx.workStore, work.workId, {
1067
- allowedPaths: [...new Set([...work.allowedPaths, ...(input.allowedPaths ?? [])])].slice(0, 50),
1068
- forbiddenPaths: [...new Set([...work.forbiddenPaths, ...(input.forbiddenPaths ?? [])])].slice(0, 50),
1069
1018
  checks: [...new Set([...work.checks, ...normalizedChecks.validCheckIds])].slice(0, 30),
1070
1019
  scopeEvidence: {
1071
1020
  initialLikelyPaths: [...new Set([
1072
- ...(work.scopeEvidence?.initialLikelyPaths ?? work.allowedPaths),
1021
+ ...(work.scopeEvidence?.initialLikelyPaths ?? []),
1073
1022
  ...(input.allowedPaths ?? []),
1074
1023
  ])].slice(0, 100),
1075
1024
  inspectedPaths: work.scopeEvidence?.inspectedPaths ?? [],
@@ -1185,8 +1134,11 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1185
1134
  }
1186
1135
  }
1187
1136
 
1188
- const currentCheckRefs = work.checkRefs.filter((record) =>
1189
- verificationRecordAppliesToCurrentWorkspace(record, ctx.sourceRevision, ctx.workspaceFingerprint));
1137
+ const currentCheckRefs = effectiveCurrentWorkVerificationRecords(
1138
+ work,
1139
+ ctx.sourceRevision,
1140
+ ctx.workspaceFingerprint,
1141
+ );
1190
1142
  const history = reconcileVerificationHistory(
1191
1143
  currentCheckRefs.map((record) => ({ checkId: record.checkId, outcome: record.outcome, recordedAt: record.recordedAt })),
1192
1144
  );
@@ -1201,7 +1153,6 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1201
1153
  detailLevel: 'summary',
1202
1154
  });
1203
1155
  transitionWorkContractPhase(ctx.workStore, work.workId, {
1204
- status: 'running',
1205
1156
  phase: 'implementation',
1206
1157
  state: 'active',
1207
1158
  summary: `Controller chose bounded ${decision} after acceptance failure; implementation may resume before re-verification.`,
@@ -1224,148 +1175,36 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1224
1175
  });
1225
1176
  }
1226
1177
 
1227
- // Ambiguous acceptance failure without an explicit bounded Controller decision →
1228
- // ask once per distinct failure evidence. Repeated continue calls must not
1229
- // manufacture an unbounded handoff loop. A resolved handoff remains the decision
1230
- // authority until newer valid-fail evidence; an unresolved matching handoff is reused.
1231
- if (history.acceptanceFailures.length > 0 && !explicitAcceptanceRepair && work.recoveryPolicy.handoffOnAmbiguity) {
1178
+ // Acceptance failure is a model/controller decision point, not a human blocker.
1179
+ // Do not manufacture Handoff/UserRequest state for repair vs re-scope.
1180
+ if (history.acceptanceFailures.length > 0 && !explicitAcceptanceRepair) {
1232
1181
  const failureReason = `Acceptance checks failed: ${history.acceptanceFailures.join(', ')}`;
1233
- const latestFailureAt = work.checkRefs
1234
- .filter((record) => record.outcome === 'valid_fail' && history.acceptanceFailures.includes(record.checkId))
1235
- .map((record) => record.recordedAt)
1236
- .sort((left, right) => right.localeCompare(left))[0];
1237
- const matchingHandoffs = listHandoffItems({ ...ctx.handoffStore, status: 'all', limit: 100 })
1238
- .filter((item) => (
1239
- item.workId === input.workId
1240
- && item.creationReason === 'ambiguous_outcome'
1241
- && item.title === 'Acceptance failure needs review'
1242
- && item.reason === failureReason
1243
- && (!latestFailureAt || item.createdAt >= latestFailureAt)
1244
- ));
1245
- const activeHandoff = matchingHandoffs.find((item) => item.status === 'pending' || item.status === 'acknowledged');
1246
- if (activeHandoff) {
1247
- return buildFacadeResult({
1248
- status: 'blocked',
1249
- summary: `Continue remains paused for ChatGPT review through existing handoff ${activeHandoff.id}.`,
1250
- data: {
1251
- work: summarizeWorkContract(work),
1252
- handoffId: activeHandoff.id,
1253
- acceptanceFailures: history.acceptanceFailures,
1254
- infrastructureIssues: history.infrastructureIssues,
1255
- backgroundCompleted: false,
1256
- },
1257
- evidenceRefs: work.evidenceRefs.slice(0, 5),
1258
- suggestedNextActions: [{
1259
- label: 'Get handoff',
1260
- tool: 'rh_inbox',
1261
- operation: 'get',
1262
- payload: { handoff_id: activeHandoff.id },
1263
- risk: 'readonly',
1264
- confidence: 'high',
1265
- }],
1266
- });
1267
- }
1268
-
1269
- const resolvedHandoff = matchingHandoffs.find((item) => item.status === 'resolved');
1270
- if (!resolvedHandoff) {
1271
- const continuation = buildWorkContinuationSnapshot(work);
1272
- const handoff = createHandoffItem(ctx.handoffStore, {
1273
- id: handoffIdFor('continue'),
1274
- repoId: ctx.repoId,
1275
- workId: work.workId,
1276
- title: 'Acceptance failure needs review',
1277
- severity: 'needs_review',
1278
- creationReason: 'ambiguous_outcome',
1279
- reason: failureReason,
1280
- summary: 'Continue paused; ChatGPT must decide repair vs re-scope.',
1281
- currentState: {
1282
- repoId: ctx.repoId,
1283
- workId: work.workId,
1284
- mode: work.mode,
1285
- statusSummary: 'waiting_for_review after acceptance failure',
1286
- checks: history.acceptanceFailures.map((checkId) => ({ checkId, ok: false, outcome: 'valid_fail' as const })),
1287
- workSemantics: continuation.semantics,
1288
- reconciliationRequired: continuation.reconciliationRequired,
1289
- nextSafeAction: continuation.nextSafeAction,
1290
- },
1291
- attemptedActions: ['continue'],
1292
- evidenceRefs: work.evidenceRefs.slice(0, 5),
1293
- blockingDecision: 'Decide whether to repair code, adjust acceptance criteria, or stop.',
1294
- recommendedDecision: 'Inspect evidence and either repair or stop the workloop.',
1295
- recommendedPrompt: work.continuationPrompt ?? `Continue from work ${work.workId}.`,
1296
- recommendedContinuationPrompt: continuation.continuationPrompt,
1297
- suggestedNextActions: [
1298
- {
1299
- label: 'Read work context',
1300
- tool: 'rh_context',
1301
- operation: 'get',
1302
- payload: { work_id: work.workId },
1303
- risk: 'readonly',
1304
- },
1305
- ],
1306
- });
1307
- transitionWorkContractPhase(ctx.workStore, work.workId, {
1308
- status: 'ready',
1309
- phase: 'verification',
1310
- state: 'blocked',
1311
- summary: `Acceptance failure requires review through handoff ${handoff.id}.`,
1312
- evidenceRefs: work.evidenceRefs,
1313
- });
1314
- const updated = appendWorkHandoffRef(ctx.workStore, work.workId, handoff.id);
1315
-
1316
- return buildFacadeResult({
1317
- status: 'blocked',
1318
- summary: `Continue paused for ChatGPT review; handoff ${handoff.id} created. No background execution pretended.`,
1319
- data: {
1320
- work: summarizeWorkContract(updated),
1321
- handoffId: handoff.id,
1322
- acceptanceFailures: history.acceptanceFailures,
1323
- infrastructureIssues: history.infrastructureIssues,
1324
- backgroundCompleted: false,
1325
- },
1326
- evidenceRefs: work.evidenceRefs.slice(0, 5),
1327
- suggestedNextActions: [
1328
- {
1329
- label: 'Get handoff',
1330
- tool: 'rh_inbox',
1331
- operation: 'get',
1332
- payload: { handoff_id: handoff.id },
1333
- risk: 'readonly',
1334
- confidence: 'high',
1335
- },
1336
- ],
1337
- });
1338
- }
1339
-
1340
- transitionWorkContractPhase(ctx.workStore, work.workId, {
1341
- status: 'running',
1342
- phase: 'verification',
1343
- state: 'active',
1344
- summary: `Resolved acceptance-failure handoff ${resolvedHandoff.id} authorizes bounded continuation to re-verification.`,
1345
- evidenceRefs: work.evidenceRefs,
1346
- });
1347
- work = getWorkContract(ctx.workStore, work.workId) ?? work;
1348
1182
  return buildFacadeResult({
1349
- status: 'ok',
1350
- summary: `Continue accepted resolved acceptance-failure handoff ${resolvedHandoff.id}; re-verification may resume.`,
1183
+ status: 'blocked',
1184
+ summary: `${failureReason}. The model must explicitly choose bounded repair or re-scope before continuing; no human Handoff was created.`,
1351
1185
  data: {
1352
1186
  work: summarizeWorkContract(work),
1353
- handoffId: resolvedHandoff.id,
1354
1187
  acceptanceFailures: history.acceptanceFailures,
1355
1188
  infrastructureIssues: history.infrastructureIssues,
1356
1189
  backgroundCompleted: false,
1357
- nextStep: 'verify',
1358
- remainingChecks: history.acceptanceFailures,
1190
+ acceptanceFailureDecisionRequired: true,
1359
1191
  },
1360
1192
  evidenceRefs: work.evidenceRefs.slice(0, 5),
1361
- suggestedNextActions: suggestedForWork(work),
1193
+ suggestedNextActions: [{
1194
+ label: 'Inspect Work before repair or re-scope',
1195
+ tool: 'rh_context',
1196
+ operation: 'get',
1197
+ payload: { work_id: work.workId },
1198
+ risk: 'readonly',
1199
+ confidence: 'high',
1200
+ reason: 'Acceptance evidence is factual input; the model chooses the next capability without a Work lifecycle transition.',
1201
+ }],
1362
1202
  });
1363
1203
  }
1364
1204
 
1365
1205
  // Infrastructure issues: suggest self-healing, not acceptance failure.
1366
1206
  if (history.infrastructureIssues.length > 0) {
1367
1207
  transitionWorkContractPhase(ctx.workStore, work.workId, {
1368
- status: 'running',
1369
1208
  phase: 'implementation',
1370
1209
  state: 'active',
1371
1210
  summary: `Infrastructure issues require repair: ${history.infrastructureIssues.join(', ')}.`,
@@ -1414,7 +1253,6 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1414
1253
  },
1415
1254
  ]).actions;
1416
1255
  transitionWorkContractPhase(ctx.workStore, work.workId, {
1417
- status: 'running',
1418
1256
  phase: 'implementation',
1419
1257
  state: 'active',
1420
1258
  summary: implementationEvidence.reasons.join(' '),
@@ -1443,24 +1281,15 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1443
1281
 
1444
1282
  if (work.checks.length > 0 && history.validPasses.length < work.checks.length) {
1445
1283
  const remaining = work.checks.filter((checkId) => !history.validPasses.includes(checkId));
1446
- const suggested = validateSuggestedNextActions(
1447
- remaining.map((checkId) => ({
1448
- label: `Verify ${checkId}`,
1449
- tool: 'rh_work' as const,
1450
- operation: 'verify',
1451
- payload: { work_id: work.workId, check_id: checkId },
1452
- risk: 'workspace_write' as const,
1453
- confidence: 'high' as const,
1454
- })),
1455
- { validCheckIds: work.checks },
1456
- ).actions;
1457
- transitionWorkContractPhase(ctx.workStore, work.workId, {
1458
- status: 'running',
1459
- phase: 'verification',
1460
- state: 'active',
1461
- summary: `Verification remains for: ${remaining.join(', ')}.`,
1462
- evidenceRefs: work.evidenceRefs,
1463
- });
1284
+ const suggested = validateSuggestedNextActions([{
1285
+ label: 'Inspect Work and check evidence',
1286
+ tool: 'rh_context',
1287
+ operation: 'get',
1288
+ payload: { work_id: work.workId },
1289
+ risk: 'readonly',
1290
+ confidence: 'high',
1291
+ reason: 'Registered checks are independent execution evidence; they do not progress Work lifecycle state.',
1292
+ }]).actions;
1464
1293
  const updated = updateWorkContract(ctx.workStore, work.workId, {
1465
1294
  suggestedNextActions: suggested,
1466
1295
  continuationPrompt: input.note
@@ -1469,12 +1298,12 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1469
1298
  });
1470
1299
  return buildFacadeResult({
1471
1300
  status: 'ok',
1472
- summary: `Continue: next step is verification of ${remaining[0]}. No background work was completed.`,
1301
+ summary: `Registered check evidence is still missing for: ${remaining.join(', ')}. Run checks through the check capability; Work semantic state is unchanged.`,
1473
1302
  data: {
1474
1303
  work: summarizeWorkContract(updated),
1475
1304
  remainingChecks: remaining,
1476
1305
  backgroundCompleted: false,
1477
- nextStep: 'verify',
1306
+ nextStep: 'run_check',
1478
1307
  },
1479
1308
  suggestedNextActions: suggested,
1480
1309
  });
@@ -1500,7 +1329,6 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1500
1329
  },
1501
1330
  ]).actions;
1502
1331
  transitionWorkContractPhase(ctx.workStore, work.workId, {
1503
- status: 'running',
1504
1332
  phase: work.workKind === 'read_only_review' ? 'verification' : 'implementation',
1505
1333
  state: 'active',
1506
1334
  summary: work.workKind === 'read_only_review'
@@ -1530,200 +1358,13 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1530
1358
  });
1531
1359
  }
1532
1360
 
1533
- const suggested = validateSuggestedNextActions([
1534
- {
1535
- label: 'Finalize work',
1536
- tool: 'rh_work',
1537
- operation: 'finalize',
1538
- payload: { work_id: work.workId },
1539
- risk: 'readonly',
1540
- confidence: 'high',
1541
- },
1542
- ]).actions;
1543
- transitionWorkContractPhase(ctx.workStore, work.workId, {
1544
- status: 'running',
1545
- phase: 'delivery',
1546
- state: 'active',
1547
- summary: work.workKind === 'read_only_review'
1548
- ? 'Read-only review evidence is source-bound and clean; semantic no-change finalization is next.'
1549
- : 'Implementation and verification evidence are sufficient; exact delivery and cleanup receipt is next.',
1550
- evidenceRefs: work.evidenceRefs,
1551
- });
1552
- const updated = updateWorkContract(ctx.workStore, work.workId, {
1553
- suggestedNextActions: suggested,
1554
- });
1555
- return buildFacadeResult({
1556
- status: 'ok',
1557
- summary: `Continue: checks satisfied or none required; ready to finalize after ChatGPT review.`,
1558
- data: {
1559
- work: summarizeWorkContract(updated),
1560
- backgroundCompleted: false,
1561
- nextStep: 'finalize',
1562
- },
1563
- suggestedNextActions: suggested,
1564
- });
1565
- }
1566
-
1567
- export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopVerifyInput): FacadeResult {
1568
- const work = getWorkContract(ctx.workStore, input.workId);
1569
- if (!work) {
1570
- return buildFacadeResult({
1571
- status: 'not_found',
1572
- summary: `WorkContract ${input.workId} not found.`,
1573
- data: { workId: input.workId },
1574
- });
1575
- }
1576
-
1577
- const available = ctx.availableChecks ?? work.checks.map((id) => ({ id }));
1578
- const classified = classifyVerificationOutcome({
1579
- checkId: input.checkId,
1580
- available,
1581
- infrastructureFailed: input.infrastructureFailed,
1582
- checkFailed: input.checkFailed,
1583
- skipped: input.skipped,
1584
- });
1585
- if (work.status === 'completed' || work.status === 'cancelled' || work.status === 'failed') {
1586
- const resolvedCheckId = classified.normalizedCheckId ?? classified.checkId;
1587
- const existing = [...work.checkRefs].reverse().find((record) => record.checkId === resolvedCheckId);
1588
- const completed = work.status === 'completed';
1589
- return buildFacadeResult({
1590
- status: completed ? 'ok' : 'blocked',
1591
- summary: `WorkContract ${work.workId} is terminal (${work.status}); verification was not re-executed.`,
1592
- data: {
1593
- work: summarizeWorkContract(work),
1594
- verification: {
1595
- checkId: resolvedCheckId,
1596
- ...(existing ? { outcome: existing.outcome } : {}),
1597
- terminal: true,
1598
- idempotent: true,
1599
- reexecuted: false,
1600
- isAcceptanceFailure: existing?.outcome === 'valid_fail',
1601
- isInfrastructureIssue: existing?.outcome === 'invalid_check_id' || existing?.outcome === 'infrastructure_failure',
1602
- doesNotRequestTaskChanges: existing?.outcome !== 'valid_fail',
1603
- },
1604
- backgroundCompleted: false,
1605
- },
1606
- evidenceRefs: existing?.evidenceRef ? [existing.evidenceRef] : [],
1607
- warnings: classified.warnings,
1608
- suggestedNextActions: [{ label: 'Inspect work via context', tool: 'rh_context', operation: 'get', payload: { work_id: work.workId }, risk: 'readonly', confidence: 'high' }],
1609
- });
1610
- }
1611
-
1612
- const at = nowIso(ctx);
1613
- const resolvedCheckId = classified.normalizedCheckId ?? classified.checkId;
1614
- const receipt = input.receipt
1615
- && input.receipt.repoId === work.repoId
1616
- && input.receipt.workId === work.workId
1617
- && input.receipt.checkId === resolvedCheckId
1618
- ? input.receipt
1619
- : undefined;
1620
- const sourceRevision = receipt && input.sourceRevision?.trim() ? input.sourceRevision.trim() : undefined;
1621
- const verificationSummary = receipt
1622
- ? `${classified.summary} Durable Process receipt ${receipt.receiptId}.`
1623
- : classified.summary;
1624
- const record: VerificationRecord = {
1625
- checkId: resolvedCheckId,
1626
- outcome: classified.outcome,
1627
- summary: verificationSummary,
1628
- recordedAt: at,
1629
- sourceRevision,
1630
- workspaceFingerprint: receipt && sourceRevision ? input.workspaceFingerprint : undefined,
1631
- verificationInputFingerprint: receipt && sourceRevision ? input.verificationInputFingerprint : undefined,
1632
- commandFingerprint: receipt && sourceRevision ? input.commandFingerprint : undefined,
1633
- startedAt: receipt?.startedAt,
1634
- completedAt: receipt?.finishedAt,
1635
- receipt,
1636
- evidenceRef: {
1637
- title: `verification:${classified.outcome}`,
1638
- summary: verificationSummary,
1639
- detailLevel: 'summary',
1640
- },
1641
- };
1642
-
1643
- // Supersede prior invalid/infrastructure noise when a valid outcome arrives for the same check.
1644
- let checkRefs = work.checkRefs;
1645
- if (classified.outcome === 'valid_pass' || classified.outcome === 'valid_fail') {
1646
- checkRefs = work.checkRefs.map((existing) => {
1647
- if (
1648
- existing.checkId === record.checkId
1649
- && (existing.outcome === 'invalid_check_id' || existing.outcome === 'infrastructure_failure')
1650
- ) {
1651
- return { ...existing, outcome: 'superseded' as const, summary: `Superseded by ${classified.outcome} at ${at}` };
1652
- }
1653
- return existing;
1654
- });
1655
- updateWorkContract(ctx.workStore, work.workId, { checkRefs });
1656
- }
1657
-
1658
- const updated = appendVerificationRecord(ctx.workStore, work.workId, record);
1659
- if (record.evidenceRef) appendWorkEvidence(ctx.workStore, work.workId, record.evidenceRef);
1660
-
1661
- const status =
1662
- classified.outcome === 'invalid_check_id' || classified.outcome === 'infrastructure_failure'
1663
- ? 'ok'
1664
- : classified.outcome === 'valid_fail'
1665
- ? 'failed'
1666
- : 'ok';
1667
-
1668
- const completionEvidence = classified.outcome === 'valid_pass'
1669
- ? evaluateWorkCompletionEvidence(updated, sourceRevision, input.workspaceFingerprint)
1670
- : undefined;
1671
- const validPassReadyToFinalize = completionEvidence?.status === 'complete';
1672
- const suggested = validateSuggestedNextActions(
1673
- classified.outcome === 'valid_pass'
1674
- ? [{
1675
- label: validPassReadyToFinalize ? 'Finalize work' : 'Continue workloop',
1676
- tool: 'rh_work',
1677
- operation: validPassReadyToFinalize ? 'finalize' : 'continue',
1678
- payload: { work_id: work.workId },
1679
- risk: 'readonly',
1680
- confidence: 'high',
1681
- }]
1682
- : classified.outcome === 'valid_fail'
1683
- ? [{
1684
- label: 'Continue for review handoff',
1685
- tool: 'rh_work',
1686
- operation: 'continue',
1687
- payload: { work_id: work.workId },
1688
- risk: 'readonly',
1689
- confidence: 'high',
1690
- }]
1691
- : [{
1692
- label: 'Diagnose infrastructure (dry-run)',
1693
- tool: 'rh_work',
1694
- operation: 'repair',
1695
- payload: { work_id: work.workId, repair_operation: 'diagnose', dry_run: true },
1696
- risk: 'readonly',
1697
- confidence: 'high',
1698
- }],
1699
- { validCheckIds: work.checks },
1700
- ).actions;
1701
-
1702
- return buildFacadeResult({
1703
- status: status === 'failed' ? 'failed' : 'ok',
1704
- summary: classified.summary,
1705
- data: {
1706
- work: summarizeWorkContract(updated),
1707
- verification: {
1708
- checkId: record.checkId,
1709
- outcome: classified.outcome,
1710
- isAcceptanceFailure: classified.isAcceptanceFailure,
1711
- isInfrastructureIssue: classified.isInfrastructureIssue,
1712
- // Explicitly separate pollution classes for ChatGPT.
1713
- doesNotRequestTaskChanges: !classified.isAcceptanceFailure,
1714
- },
1715
- backgroundCompleted: false,
1716
- ...(classified.outcome === 'valid_pass'
1717
- ? { nextStep: validPassReadyToFinalize ? 'finalize' : 'continue' }
1718
- : {}),
1719
- },
1720
- warnings: classified.warnings,
1721
- evidenceRefs: record.evidenceRef ? [record.evidenceRef] : [],
1722
- suggestedNextActions: suggested,
1723
- });
1361
+ // Completion evidence is observational. It cannot choose a review/finalize
1362
+ // transition or tell the model which capability to invoke next.
1363
+ const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: [] });
1364
+ return buildFacadeResult({ status: 'ok', summary: 'Continue: current Work evidence recorded.', data: { work: summarizeWorkContract(updated), backgroundCompleted: false }, suggestedNextActions: [] });
1724
1365
  }
1725
1366
 
1726
- export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopFinalizeInput): FacadeResult {
1367
+ export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopStopInput): FacadeResult {
1727
1368
  const work = getWorkContract(ctx.workStore, input.workId);
1728
1369
  if (!work) {
1729
1370
  return buildFacadeResult({
@@ -1733,326 +1374,38 @@ export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1733
1374
  });
1734
1375
  }
1735
1376
 
1736
- if (work.status === 'cancelled') {
1377
+ const destructiveCleanup = input.authorizeDestructiveCleanup === true;
1378
+ if (semanticWorkState(work) === 'completed') {
1737
1379
  return buildFacadeResult({
1738
1380
  status: 'blocked',
1739
- summary: `WorkContract ${work.workId} was cancelled; finalize is not allowed.`,
1381
+ summary: `WORK_CANCEL_COMPLETED: ${work.workId}`,
1740
1382
  data: { work: summarizeWorkContract(work) },
1741
1383
  });
1742
1384
  }
1743
-
1744
- // A Work-owned completion receipt is the strongest delivery/cleanup authority.
1745
- // Physical finalization records status=completed + receipt atomically. A facade
1746
- // retry must be idempotent and must never re-run weaker pre-delivery evidence
1747
- // evaluation that could attempt to demote an already completed Work.
1748
- if (work.status === 'completed' && work.completionReceipt) {
1749
- if (work.planId && work.planStepId && ctx.planStore) {
1750
- completePlanStepForWork(ctx.planStore, { planId: work.planId, stepId: work.planStepId, work });
1751
- }
1752
- return buildFacadeResult({
1753
- status: 'ok',
1754
- summary: `Finalize result: succeeded for ${work.workId}.`,
1755
- data: {
1756
- work: summarizeWorkContract(work),
1757
- finalStatus: 'completed',
1758
- completionReceipt: work.completionReceipt,
1759
- idempotent: true,
1760
- hiddenFailure: false,
1761
- },
1762
- evidenceRefs: work.evidenceRefs.slice(0, 5),
1763
- suggestedNextActions: [{ label: 'Read controller status', tool: 'rh_status', operation: 'get', risk: 'readonly' }],
1764
- });
1765
- }
1766
-
1767
- const completionEvidence = evaluateWorkCompletionEvidence(
1768
- work,
1769
- ctx.sourceRevision,
1770
- ctx.workspaceFingerprint,
1771
- ctx.workBoundProcessEvidenceIds,
1772
- ctx.workspaceChangedPaths,
1773
- );
1774
- const history = completionEvidence.history;
1775
-
1776
- if (input.forceFailed || completionEvidence.status === 'failed') {
1777
- const updated = transitionWorkContractPhase(ctx.workStore, work.workId, {
1778
- status: 'failed',
1779
- phase: 'cleanup',
1780
- state: 'failed',
1781
- summary: `Work failed acceptance/finalization: ${history.acceptanceFailures.join(', ') || 'forced failure'}.`,
1782
- evidenceRefs: work.evidenceRefs,
1783
- });
1784
- if (updated.planId && updated.planStepId && ctx.planStore) {
1785
- completePlanStepForWork(ctx.planStore, { planId: updated.planId, stepId: updated.planStepId, work: updated });
1786
- }
1787
- return buildFacadeResult({
1788
- status: 'failed',
1789
- summary: `Finalize result: failed. Acceptance failures: ${history.acceptanceFailures.join(', ') || 'forced'}.`,
1790
- data: {
1791
- work: summarizeWorkContract(updated),
1792
- finalStatus: 'failed',
1793
- acceptanceFailures: history.acceptanceFailures,
1794
- infrastructureIssues: history.infrastructureIssues,
1795
- invalidCheckIds: history.invalidCheckIds,
1796
- // Failures are not hidden.
1797
- hiddenFailure: false,
1798
- },
1799
- suggestedNextActions: [
1800
- {
1801
- label: 'List handoffs',
1802
- tool: 'rh_inbox',
1803
- operation: 'list',
1804
- risk: 'readonly',
1805
- },
1806
- ],
1807
- });
1808
- }
1809
-
1810
- // Weak refs, partial checks, invalid ids, and infrastructure issues never imply successful completion.
1811
- if (completionEvidence.status === 'incomplete') {
1812
- const updated = transitionWorkContractPhase(ctx.workStore, work.workId, {
1813
- status: 'ready',
1814
- phase: 'verification',
1815
- state: 'blocked',
1816
- summary: `Completion evidence remains incomplete: ${completionEvidence.reasons.join(' ')}`,
1817
- evidenceRefs: work.evidenceRefs,
1818
- });
1819
- return buildFacadeResult({
1820
- status: 'blocked',
1821
- summary: `Finalize result: waiting_for_review. ${completionEvidence.reasons.join(' ')}`,
1822
- data: {
1823
- work: summarizeWorkContract(updated),
1824
- finalStatus: 'ready',
1825
- infrastructureIssues: history.infrastructureIssues,
1826
- invalidCheckIds: history.invalidCheckIds,
1827
- validPasses: history.validPasses,
1828
- missingChecks: completionEvidence.missingChecks,
1829
- durableResultEvidence: completionEvidence.durableResultEvidence,
1830
- ignoredWeakReferences: {
1831
- workerRef: Boolean(work.workerRef),
1832
- worktreeRef: Boolean(work.worktreeRef),
1833
- },
1834
- hiddenFailure: false,
1835
- },
1836
- suggestedNextActions: [
1837
- {
1838
- label: 'Continue workloop',
1839
- tool: 'rh_work',
1840
- operation: 'continue',
1841
- payload: { work_id: work.workId },
1842
- risk: 'readonly',
1843
- },
1844
- ],
1845
- });
1846
- }
1847
-
1848
- // Read-only review completion is semantic no-change authority, not Git delivery.
1849
- // The exact source/workspace fence is re-evaluated above at finalize time; only
1850
- // a clean review with persisted inspected paths and zero findings can close.
1851
- if (work.workKind === 'read_only_review' && !work.completionReceipt) {
1852
- const reviewEvidence = work.readOnlyReviewEvidence!;
1853
- const recordedAt = nowIso(ctx);
1854
- const completed = completeWorkWithReceipt(
1855
- ctx.workStore,
1856
- work.workId,
1857
- {
1858
- schemaVersion: 1,
1859
- receiptId: `ROR-WORK-${randomUUID()}`,
1860
- source: 'read_only_review',
1861
- workId: work.workId,
1862
- baseRevision: work.baseRevision!,
1863
- sourceRevision: ctx.sourceRevision!,
1864
- ...(ctx.workspaceFingerprint ? { workspaceFingerprint: ctx.workspaceFingerprint } : {}),
1865
- workspaceChangedPaths: [],
1866
- inspectedPaths: reviewEvidence.inspectedPaths,
1867
- findingCount: 0,
1868
- recordedAt,
1869
- },
1870
- 'completed_no_change',
1871
- 'read_only_review',
1872
- );
1873
- if (completed.planId && completed.planStepId && ctx.planStore) {
1874
- completePlanStepForWork(ctx.planStore, { planId: completed.planId, stepId: completed.planStepId, work: completed });
1875
- }
1876
- return buildFacadeResult({
1877
- status: 'ok',
1878
- summary: `Finalize result: clean read-only review completed with no source change for ${work.workId}.`,
1879
- data: {
1880
- work: summarizeWorkContract(completed),
1881
- finalStatus: 'completed',
1882
- completionOutcome: 'completed_no_change',
1883
- completionReceipt: completed.completionReceipt,
1884
- inspectedPathCount: reviewEvidence.inspectedPaths.length,
1885
- hiddenFailure: false,
1886
- },
1887
- evidenceRefs: completed.evidenceRefs.slice(0, 5),
1888
- suggestedNextActions: [{ label: 'Read controller status', tool: 'rh_status', operation: 'get', risk: 'readonly' }],
1889
- });
1890
- }
1891
-
1892
- // For a controller-local effect Work, the semantic Controller's explicit
1893
- // finalize call is the terminalization decision, but it is admissible only
1894
- // after durable result evidence exists. Record one canonical Work receipt here
1895
- // instead of routing an effect-only Work through Git delivery. Remote effects
1896
- // remain bound to their durable plugin action receipt in the plugin layer.
1897
- if (work.workKind === 'local_effect' && !work.completionReceipt && completionEvidence.durableResultEvidence) {
1898
- const recordedAt = nowIso(ctx);
1899
- const completed = completeWorkWithReceipt(
1900
- ctx.workStore,
1901
- work.workId,
1902
- {
1903
- schemaVersion: 1,
1904
- receiptId: `LFX-WORK-${randomUUID()}`,
1905
- source: 'local_effect',
1906
- workId: work.workId,
1907
- operation: 'controller_work/local_effect',
1908
- target: { kind: 'controller_local', id: work.workId },
1909
- changed: true,
1910
- recordedAt,
1911
- },
1912
- 'completed_local',
1913
- 'local_effect',
1914
- );
1915
- if (completed.planId && completed.planStepId && ctx.planStore) {
1916
- completePlanStepForWork(ctx.planStore, { planId: completed.planId, stepId: completed.planStepId, work: completed });
1917
- }
1918
- return buildFacadeResult({
1919
- status: 'ok',
1920
- summary: `Finalize result: succeeded for ${work.workId}.`,
1921
- data: {
1922
- work: summarizeWorkContract(completed),
1923
- finalStatus: 'completed',
1924
- completionReceipt: completed.completionReceipt,
1925
- validPasses: history.validPasses,
1926
- hiddenFailure: false,
1927
- },
1928
- evidenceRefs: completed.evidenceRefs.slice(0, 5),
1929
- suggestedNextActions: [{ label: 'Read controller status', tool: 'rh_status', operation: 'get', risk: 'readonly' }],
1930
- });
1931
- }
1932
-
1933
- // Checks and result evidence are necessary but not sufficient. Delivery and
1934
- // cleanup must produce the exact Work-owned receipt before Work can become
1935
- // terminal; a Run exit or a missing receipt is never completion authority.
1936
- if (!work.completionReceipt) {
1937
- const updated = transitionWorkContractPhase(ctx.workStore, work.workId, {
1938
- status: 'ready',
1939
- phase: 'delivery',
1940
- state: 'blocked',
1941
- summary: 'Exact delivery and cleanup completion receipt is required.',
1942
- evidenceRefs: work.evidenceRefs,
1943
- });
1944
- return buildFacadeResult({
1945
- status: 'blocked',
1946
- summary: `Finalize blocked for ${work.workId}: an exact delivery and cleanup completion receipt is required.`,
1947
- data: { work: summarizeWorkContract(updated), finalStatus: 'ready', completionReceiptRequired: true },
1948
- suggestedNextActions: [{ label: 'Complete Work delivery and cleanup', tool: 'rh_work', operation: 'finalize', payload: { work_id: work.workId }, risk: 'local_repo_write' }],
1949
- });
1950
- }
1951
-
1952
- const updated = getWorkContract(ctx.workStore, work.workId)!;
1953
- if (updated.planId && updated.planStepId && ctx.planStore) {
1954
- completePlanStepForWork(ctx.planStore, { planId: updated.planId, stepId: updated.planStepId, work: updated });
1955
- }
1956
- return buildFacadeResult({
1957
- status: 'ok',
1958
- summary: `Finalize result: succeeded for ${work.workId}.`,
1959
- data: {
1960
- work: summarizeWorkContract(updated),
1961
- finalStatus: 'completed',
1962
- validPasses: history.validPasses,
1963
- hiddenFailure: false,
1964
- },
1965
- evidenceRefs: work.evidenceRefs.slice(0, 5),
1966
- suggestedNextActions: [
1967
- {
1968
- label: 'Read controller status',
1969
- tool: 'rh_status',
1970
- operation: 'get',
1971
- risk: 'readonly',
1972
- },
1973
- ],
1974
- });
1975
- }
1976
-
1977
- export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopStopInput): FacadeResult {
1978
- const work = getWorkContract(ctx.workStore, input.workId);
1979
- if (!work) {
1980
- return buildFacadeResult({
1981
- status: 'not_found',
1982
- summary: `WorkContract ${input.workId} not found.`,
1983
- data: { workId: input.workId },
1984
- });
1985
- }
1986
-
1987
- const owner = ctx.workStore.controllerHome
1988
- ? getControllerSession({ controllerHome: ctx.workStore.controllerHome, repoId: ctx.repoId }, work.workId)
1989
- : undefined;
1990
- if (owner) {
1991
- const ownerPrincipal = owner.principalId?.trim() || owner.controllerId;
1992
- const ownerInstanceId = owner.controllerInstanceId?.trim();
1993
- const callerPrincipal = ctx.principalId?.trim();
1994
- const callerInstanceId = ctx.controllerInstanceId?.trim();
1995
- const sameAuthority = Boolean(
1996
- callerPrincipal
1997
- && ownerPrincipal === callerPrincipal
1998
- && (!ownerInstanceId || ownerInstanceId === callerInstanceId),
1999
- );
2000
- if (!sameAuthority) {
2001
- return buildFacadeResult({
2002
- status: 'blocked',
2003
- summary: `WORK_TERMINALIZATION_ACTIVE_CONTROLLER_FENCE: ${work.workId} has a newer active Controller claim generation ${owner.claimGeneration ?? 1}.`,
2004
- data: {
2005
- work: summarizeWorkContract(work),
2006
- activeController: {
2007
- controllerId: owner.controllerId,
2008
- principalId: ownerPrincipal,
2009
- controllerInstanceId: owner.controllerInstanceId,
2010
- sessionId: owner.sessionId,
2011
- claimGeneration: owner.claimGeneration ?? 1,
2012
- },
2013
- finalStatus: work.status,
2014
- },
2015
- });
2016
- }
2017
- }
2018
-
2019
- const destructiveCleanup = input.authorizeDestructiveCleanup === true;
2020
- transitionWorkContractPhase(ctx.workStore, work.workId, {
2021
- status: 'cancelled',
2022
- phase: 'cleanup',
2023
- state: 'skipped',
2024
- summary: input.reason ? `Stopped: ${input.reason}` : 'Work stopped without destructive cleanup.',
2025
- evidenceRefs: work.evidenceRefs,
2026
- });
2027
- const updated = updateWorkContract(ctx.workStore, work.workId, {
2028
- continuationPrompt: input.reason
2029
- ? `Stopped: ${input.reason}`.slice(0, 2_000)
2030
- : work.continuationPrompt,
2031
- // Authorization is not proof of cleanup. Preserve the reference until a real
2032
- // cleanup handler verifies ownership, cleanliness and successful removal.
2033
- worktreeRef: work.worktreeRef,
1385
+ const semanticRevision = Number.isInteger(work.semanticRevision) && Number(work.semanticRevision) > 0
1386
+ ? Number(work.semanticRevision)
1387
+ : 1;
1388
+ const updated = reviseWorkSemanticContext(ctx.workStore, work.workId, {
1389
+ expectedRevision: semanticRevision,
1390
+ state: 'cancelled',
1391
+ resultRefs: work.evidenceRefs
1392
+ .flatMap((evidence) => [evidence.evidenceId, evidence.artifactId])
1393
+ .map((value) => value?.trim())
1394
+ .filter((value): value is string => Boolean(value)),
2034
1395
  });
2035
1396
  const planId = updated.planId;
2036
1397
  const planStepId = updated.planStepId;
2037
1398
  let plan = planId && ctx.planStore
2038
1399
  ? getPlanContract(ctx.planStore, planId)
2039
1400
  : undefined;
2040
- if (plan && planId && planStepId && ctx.planStore) {
2041
- const step = plan.steps.find((entry) => entry.id === planStepId);
2042
- if (step?.workId === updated.workId) {
2043
- plan = completePlanStepForWork(ctx.planStore, { planId, stepId: planStepId, work: updated });
2044
- }
2045
- }
2046
1401
 
2047
1402
  return buildFacadeResult({
2048
1403
  status: 'ok',
2049
- summary: `WorkContract ${work.workId} cancelled/stopped. Evidence retained. Worktree cleanup ${destructiveCleanup ? 'authorized but pending verification' : 'not performed'}.`,
1404
+ summary: `WorkContract ${work.workId} cancelled/stopped. Evidence retained. Managed resource cleanup/retention is settled by the canonical Work finalization service.`,
2050
1405
  data: {
2051
1406
  work: summarizeWorkContract(updated),
2052
1407
  finalStatus: 'cancelled',
2053
1408
  evidenceRetained: true,
2054
- worktreeDeleted: false,
2055
- cleanupPending: destructiveCleanup && Boolean(work.worktreeRef),
2056
1409
  destructiveCleanupAuthorized: destructiveCleanup,
2057
1410
  ...(plan ? { plan } : {}),
2058
1411
  },
@@ -2068,32 +1421,36 @@ export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopSt
2068
1421
  });
2069
1422
  }
2070
1423
 
1424
+ export interface GoalWorkloopTrustedInput {
1425
+ /** Runtime-composed source-bound evidence. This channel is intentionally separate from raw MCP/tool arguments. */
1426
+ verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
1427
+ /**
1428
+ * Evaluator-minted evidence for one exact candidate. Kept off the raw MCP
1429
+ * argument surface so callers cannot manufacture architecture evidence.
1430
+ */
1431
+ evaluationPromotionReceipt?: EvaluationPromotionReceipt;
1432
+ }
1433
+
2071
1434
  export function runGoalWorkloop(
2072
1435
  ctx: GoalWorkloopContext,
2073
1436
  operation: GoalWorkloopOperation,
2074
1437
  args: Record<string, unknown>,
1438
+ trusted: GoalWorkloopTrustedInput = {},
2075
1439
  ): FacadeResult {
2076
1440
  switch (operation) {
2077
1441
  case 'start':
2078
1442
  return routeWorkStart(ctx, {
2079
1443
  objective: String(args.objective ?? ''),
1444
+ workId: typeof args.work_id === 'string' ? args.work_id : undefined,
2080
1445
  acceptanceCriteria: Array.isArray(args.acceptance_criteria) ? args.acceptance_criteria.map(String) : undefined,
2081
1446
  allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
2082
1447
  initialLikelyPaths: Array.isArray(args.initial_likely_paths) ? args.initial_likely_paths.map(String) : undefined,
2083
1448
  forbiddenPaths: Array.isArray(args.forbidden_paths) ? args.forbidden_paths.map(String) : undefined,
2084
1449
  checks: Array.isArray(args.check_ids) ? args.check_ids.map(String) : undefined,
2085
- modeInput: {
1450
+ request: {
2086
1451
  objective: typeof args.objective === 'string' ? args.objective : undefined,
2087
- expectedFiles: typeof args.expected_files === 'number' ? args.expected_files : undefined,
2088
- expectedChangedLines: typeof args.expected_changed_lines === 'number' ? args.expected_changed_lines : undefined,
2089
1452
  scopeClear: args.scope_clear === undefined ? true : args.scope_clear === true,
2090
- requiresInvestigation: args.requires_investigation === true,
2091
- requiresLongRunningChecks: args.requires_long_running_checks === true,
2092
- requiresParallelism: args.requires_parallelism === true,
2093
- explicitMode: args.mode === 'scale' ? 'scale' : undefined,
2094
- needsDependencies: args.needs_dependencies === true,
2095
1453
  requiresRecovery: args.requires_recovery === true,
2096
- requiresWorker: args.requires_worker === true,
2097
1454
  requiresExternalEffect: args.requires_external_effect === true,
2098
1455
  requiresApproval: args.requires_approval === true,
2099
1456
  requiresUserApproval: args.requires_user_approval === true,
@@ -2107,9 +1464,6 @@ export function runGoalWorkloop(
2107
1464
  issueId: typeof args.issue_id === 'string' ? args.issue_id : undefined,
2108
1465
  approvalConfirmed: args.approval_confirmed === true,
2109
1466
  dryRun: args.dry_run === true,
2110
- forceMode: args.force_mode === 'direct_control' || args.force_mode === 'goal_workloop' || args.force_mode === 'handoff_only'
2111
- ? args.force_mode
2112
- : undefined,
2113
1467
  relatedWorkId: typeof args.related_work_id === 'string' ? args.related_work_id : undefined,
2114
1468
  workRelation: args.work_relation === 'continue' || args.work_relation === 'extend' || args.work_relation === 'parallel' || args.work_relation === 'new_goal'
2115
1469
  ? args.work_relation
@@ -2117,6 +1471,7 @@ export function runGoalWorkloop(
2117
1471
  requirementId: typeof args.requirement_id === 'string' ? args.requirement_id : undefined,
2118
1472
  planId: typeof args.plan_id === 'string' ? args.plan_id : undefined,
2119
1473
  planStepId: typeof args.plan_step_id === 'string' ? args.plan_step_id : undefined,
1474
+ verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
2120
1475
  workKind: args.work_kind === 'repository_change'
2121
1476
  || args.work_kind === 'completed_no_change'
2122
1477
  || args.work_kind === 'read_only_review'
@@ -2127,7 +1482,25 @@ export function runGoalWorkloop(
2127
1482
  ? args.work_kind
2128
1483
  : undefined,
2129
1484
  });
2130
- case 'continue':
1485
+ case 'continue': {
1486
+ let engineeringBlocker: GoalWorkloopContinueInput['engineeringBlocker'];
1487
+ if (args.engineering_blocker !== undefined) {
1488
+ if (!args.engineering_blocker || typeof args.engineering_blocker !== 'object' || Array.isArray(args.engineering_blocker)) {
1489
+ return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_INVALID', data: { workId: String(args.work_id ?? '') } });
1490
+ }
1491
+ const rawBlocker = args.engineering_blocker as Record<string, unknown>;
1492
+ const classification = rawBlocker.classification;
1493
+ if (classification !== 'same_root_cause' && classification !== 'same_root_cause_scope_extension' && classification !== 'unrelated') {
1494
+ return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_CLASSIFICATION_INVALID', data: { workId: String(args.work_id ?? '') } });
1495
+ }
1496
+ engineeringBlocker = {
1497
+ blockerId: String(rawBlocker.blocker_id ?? ''),
1498
+ classification,
1499
+ rationale: String(rawBlocker.rationale ?? ''),
1500
+ semanticScopeKeys: Array.isArray(rawBlocker.semantic_scope_keys) ? rawBlocker.semantic_scope_keys.map(String) : undefined,
1501
+ linkedWorkId: typeof rawBlocker.linked_work_id === 'string' ? rawBlocker.linked_work_id : undefined,
1502
+ };
1503
+ }
2131
1504
  return continueGoalWorkloop(ctx, {
2132
1505
  workId: String(args.work_id ?? ''),
2133
1506
  note: typeof args.note === 'string' ? args.note : undefined,
@@ -2147,20 +1520,10 @@ export function runGoalWorkloop(
2147
1520
  acceptanceFailureDecision: args.acceptance_failure_decision === 'repair' || args.acceptance_failure_decision === 'rescope'
2148
1521
  ? args.acceptance_failure_decision
2149
1522
  : undefined,
1523
+ verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
1524
+ engineeringBlocker,
2150
1525
  });
2151
- case 'verify':
2152
- return verifyGoalWorkloop(ctx, {
2153
- workId: String(args.work_id ?? ''),
2154
- checkId: String(args.check_id ?? args.checkId ?? ''),
2155
- infrastructureFailed: args.infrastructure_failed === true,
2156
- checkFailed: args.check_failed === true,
2157
- skipped: args.skipped === true,
2158
- });
2159
- case 'finalize':
2160
- return finalizeGoalWorkloop(ctx, {
2161
- workId: String(args.work_id ?? ''),
2162
- forceFailed: args.force_failed === true,
2163
- });
1526
+ }
2164
1527
  case 'stop':
2165
1528
  return stopGoalWorkloop(ctx, {
2166
1529
  workId: String(args.work_id ?? ''),