@moretea-labs/forge 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (685) hide show
  1. package/AGENTS.md +58 -62
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +42 -62
  4. package/README.md +2 -2
  5. package/SKILL.md +0 -1
  6. package/THIRD_PARTY_NOTICES.md +5 -2
  7. package/adapters/browser/index.ts +2 -0
  8. package/adapters/chatgpt/browser-delivery-host.ts +71 -0
  9. package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
  10. package/adapters/chatgpt/controller-binding-store.ts +55 -0
  11. package/adapters/chatgpt/controller-host.ts +119 -0
  12. package/adapters/chatgpt/controller-round-host.ts +78 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
  14. package/adapters/chatgpt/provider-delivery.ts +113 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/desktop-operator-contract.ts +7 -0
  18. package/adapters/computer/desktop-operator-discovery.ts +201 -0
  19. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  20. package/adapters/computer/desktop-operator-provider.ts +312 -0
  21. package/adapters/computer/index.ts +1 -0
  22. package/adapters/computer/interaction-target-authority.ts +944 -0
  23. package/adapters/controller-process/binding-store.ts +34 -0
  24. package/adapters/controller-process/controller-host.ts +26 -0
  25. package/adapters/mcp/access-mode.ts +132 -0
  26. package/adapters/mcp/audit.ts +32 -0
  27. package/adapters/mcp/auth.ts +407 -0
  28. package/adapters/mcp/controller-round-compatibility.ts +184 -0
  29. package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
  30. package/adapters/mcp/instructions.ts +36 -0
  31. package/adapters/mcp/legacy-context.ts +18 -0
  32. package/adapters/mcp/multi-repository.ts +437 -0
  33. package/adapters/mcp/oauth.ts +391 -0
  34. package/adapters/mcp/paths.ts +140 -0
  35. package/adapters/mcp/policy.ts +232 -0
  36. package/adapters/mcp/proxy-env.ts +62 -0
  37. package/adapters/mcp/readiness-projection.ts +24 -0
  38. package/adapters/mcp/redaction.ts +21 -0
  39. package/adapters/mcp/repo.ts +24 -0
  40. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  41. package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
  42. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
  43. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
  44. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  45. package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
  46. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  47. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
  48. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  49. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  50. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  51. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  52. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
  53. package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
  54. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
  55. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
  56. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  57. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  58. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
  59. package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
  60. package/adapters/mcp/runtime-gateway/router.ts +728 -0
  61. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
  62. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
  63. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  64. package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
  65. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
  66. package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
  67. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
  68. package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
  69. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
  70. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  71. package/adapters/mcp/server.ts +1048 -0
  72. package/adapters/mcp/setup.ts +791 -0
  73. package/adapters/mcp/tool-capability-cache.ts +87 -0
  74. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  75. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
  76. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
  77. package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
  78. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  79. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  80. package/adapters/mcp/toolset-names.ts +74 -0
  81. package/adapters/mcp/toolset-selection.ts +41 -0
  82. package/adapters/mcp/toolset.ts +336 -0
  83. package/adapters/mcp/transports/http.ts +1456 -0
  84. package/adapters/mcp/transports/session-registry.ts +513 -0
  85. package/adapters/mcp/transports/stdio.ts +8 -0
  86. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
  87. package/adapters/mcp/types.ts +41 -0
  88. package/assets/AGENTS.md +2 -16
  89. package/assets/CLAUDE.md +2 -16
  90. package/assets/hooks/AGENTS.md +3 -14
  91. package/assets/hooks/CLAUDE.md +3 -14
  92. package/assets/hooks/hook-input.sh +9 -2
  93. package/assets/hooks/lib/workflow-state.sh +28 -10
  94. package/assets/hooks/prompt-guard.sh +63 -37
  95. package/assets/hooks/security-sentinel.sh +2 -1
  96. package/assets/hooks/session-start-context.sh +7 -5
  97. package/assets/hooks/stop-orchestrator.sh +13 -3
  98. package/assets/initializer-question-pack.v4.json +11 -7
  99. package/assets/partials/05-workflow.partial.md +18 -73
  100. package/assets/partials/07-footer.partial.md +1 -1
  101. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  102. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  103. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  104. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  105. package/assets/plan-map.json +11 -11
  106. package/assets/plugin-registry.v1.json +9 -3
  107. package/assets/plugin-trust-policy.v1.json +6 -0
  108. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  109. package/assets/recovery/forge-wsl-rescue +271 -0
  110. package/assets/reference-configs/development-protocol.md +11 -15
  111. package/assets/reference-configs/document-generation.md +1 -1
  112. package/assets/reference-configs/global-working-rules.md +9 -1
  113. package/assets/reference-configs/harness-overview.md +22 -112
  114. package/assets/reference-configs/hook-operations.md +1 -1
  115. package/assets/reference-configs/sprint-contracts.md +2 -2
  116. package/assets/skill-version.json +2 -2
  117. package/assets/templates/helpers/architecture-event.ts +3 -34
  118. package/assets/templates/helpers/architecture-queue.sh +61 -10
  119. package/assets/templates/helpers/archive-workflow.sh +90 -180
  120. package/assets/templates/helpers/capability-config.ts +8 -11
  121. package/assets/templates/helpers/capability-resolver.ts +37 -19
  122. package/assets/templates/helpers/capture-plan.sh +2 -2
  123. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  124. package/assets/templates/helpers/check-task-sync.sh +82 -11
  125. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  126. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  127. package/assets/templates/helpers/contract-worktree.sh +26 -24
  128. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  129. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  130. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  131. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  132. package/assets/templates/helpers/new-plan.sh +2 -2
  133. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  134. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  135. package/assets/templates/helpers/switch-plan.sh +1 -1
  136. package/assets/templates/helpers/workflow-contract.ts +1 -0
  137. package/assets/workflow-contract.v1.json +63 -171
  138. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  139. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  140. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  141. package/docs/architecture/CURRENT.md +148 -13
  142. package/docs/architecture/EVOLUTION.md +13 -0
  143. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  144. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  145. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  146. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  147. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
  148. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
  149. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  150. package/docs/architecture/domains/verification.md +7 -0
  151. package/docs/architecture/domains/workflow-engine.md +1 -1
  152. package/docs/architecture/index.md +3 -3
  153. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  154. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  155. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  156. package/docs/forge-plugin-management.md +20 -4
  157. package/docs/forge-runtime-self-healing-loop.md +2 -0
  158. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  159. package/docs/operations/controller-browser-plugin.md +3 -1
  160. package/docs/operations/homebrew.md +2 -2
  161. package/docs/operations/platform-support.md +16 -8
  162. package/docs/operations/platform-support.zh-CN.md +2 -2
  163. package/docs/operations/standalone-disaster-recovery.md +26 -1
  164. package/docs/operations/uu-remote-rescue.md +41 -0
  165. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  166. package/docs/public-usage-guide.md +1 -1
  167. package/docs/public-usage-guide.zh-CN.md +1 -1
  168. package/docs/tutorials/01-install-and-start.md +1 -1
  169. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  170. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  171. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  172. package/docs/wiki/Implementation.md +1 -1
  173. package/package.json +23 -14
  174. package/packages/kernel/controller/api/index.ts +2 -0
  175. package/packages/kernel/controller/application/controller-round-service.ts +29 -0
  176. package/packages/kernel/controller/application/controller-service.ts +30 -0
  177. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
  178. package/packages/kernel/controller/domain/controller-round.ts +107 -0
  179. package/packages/kernel/controller/domain/execution-quality.ts +133 -0
  180. package/packages/kernel/controller/domain/types.ts +56 -0
  181. package/packages/kernel/controller/index.ts +1 -0
  182. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  183. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
  184. package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
  185. package/packages/kernel/controller/ports/controller-host.ts +16 -0
  186. package/packages/kernel/goal/api/index.ts +1 -0
  187. package/packages/kernel/goal/domain/types.ts +40 -0
  188. package/packages/kernel/goal/index.ts +1 -0
  189. package/packages/kernel/identity/api/index.ts +5 -0
  190. package/packages/kernel/identity/application/identity-service.ts +67 -0
  191. package/packages/kernel/identity/domain/scope.ts +63 -0
  192. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  193. package/packages/kernel/identity/domain/types.ts +44 -0
  194. package/packages/kernel/identity/domain/workspace.ts +56 -0
  195. package/packages/kernel/identity/index.ts +1 -0
  196. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  197. package/packages/kernel/memory/api/index.ts +5 -0
  198. package/packages/kernel/memory/application/experience-service.ts +118 -0
  199. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  200. package/packages/kernel/memory/domain/experience.ts +103 -0
  201. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  202. package/packages/kernel/memory/index.ts +1 -0
  203. package/packages/kernel/progression/api/index.ts +2 -0
  204. package/packages/kernel/progression/application/projector.ts +211 -0
  205. package/packages/kernel/progression/domain/types.ts +116 -0
  206. package/packages/kernel/progression/index.ts +1 -0
  207. package/packages/kernel/scheduler/api/index.ts +6 -0
  208. package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
  209. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  210. package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
  211. package/packages/kernel/scheduler/application/settlement.ts +197 -0
  212. package/packages/kernel/scheduler/domain/continuation.ts +38 -0
  213. package/packages/kernel/scheduler/domain/schedule.ts +149 -0
  214. package/packages/kernel/scheduler/index.ts +1 -0
  215. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
  216. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
  217. package/packages/kernel/work/api/index.ts +15 -0
  218. package/packages/kernel/work/application/work-service.ts +148 -0
  219. package/packages/kernel/work/domain/admission-policy.ts +34 -0
  220. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  221. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  222. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  223. package/packages/kernel/work/domain/engineering-design.ts +281 -0
  224. package/packages/kernel/work/domain/engineering-profile.ts +242 -0
  225. package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
  226. package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
  227. package/packages/kernel/work/domain/implementation-review.ts +510 -0
  228. package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
  229. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  230. package/packages/kernel/work/domain/state-machine.ts +259 -0
  231. package/packages/kernel/work/domain/types.ts +528 -0
  232. package/packages/kernel/work/index.ts +1 -0
  233. package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
  234. package/packages/kernel/work/ports/work-contract-store.ts +18 -0
  235. package/packages/plugin-runtime/browser/index.ts +3 -0
  236. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  237. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  238. package/packages/plugin-runtime/browser/session-authority.ts +61 -0
  239. package/packages/plugin-runtime/computer/index.ts +6 -0
  240. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  241. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  242. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  243. package/packages/plugin-runtime/computer/provider.ts +19 -0
  244. package/packages/plugin-runtime/computer/target-authority.ts +190 -0
  245. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  246. package/packages/plugin-runtime/external/index.ts +1 -0
  247. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
  248. package/packages/plugin-runtime/index.ts +3 -0
  249. package/packages/protocols/browser/index.ts +1 -0
  250. package/packages/protocols/browser/session.ts +98 -0
  251. package/packages/protocols/computer/contract.ts +199 -0
  252. package/packages/protocols/computer/index.ts +1 -0
  253. package/packages/protocols/failure/contract.ts +158 -0
  254. package/packages/protocols/failure/index.ts +1 -0
  255. package/packages/protocols/handoff/index.ts +1 -0
  256. package/packages/protocols/handoff/status.ts +22 -0
  257. package/packages/protocols/mcp/execution-context.ts +11 -0
  258. package/packages/protocols/mcp/index.ts +2 -0
  259. package/packages/protocols/mcp/tool-contract.ts +42 -0
  260. package/packages/workflow-runtime/api/index.ts +2 -0
  261. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  262. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  263. package/packages/workflow-runtime/index.ts +1 -0
  264. package/scripts/assemble-template.ts +1 -1
  265. package/scripts/benchmark-computer-hotpath.ts +255 -0
  266. package/scripts/benchmark-controller-round2.ts +54 -2
  267. package/scripts/benchmark-ios-agent-device.ts +3 -6
  268. package/scripts/benchmark-mcp-transport.ts +3 -4
  269. package/scripts/benchmark-operational-memory-activation.ts +235 -0
  270. package/scripts/benchmark-operational-shadow.ts +28 -0
  271. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  272. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  273. package/scripts/canary-controller-semantic-wait.ts +195 -0
  274. package/scripts/canary-global-hook.sh +2 -2
  275. package/scripts/check-mcp-compatibility.ts +173 -7
  276. package/scripts/check-platform-support.mjs +28 -2
  277. package/scripts/check-public-docs.sh +1 -1
  278. package/scripts/check-release-readiness.sh +1 -1
  279. package/scripts/check-repository-hygiene.mjs +92 -0
  280. package/scripts/check-runtime-architecture.mjs +1206 -95
  281. package/scripts/check-source-duplication.mjs +9 -1
  282. package/scripts/check-tarball-install-smoke.sh +98 -15
  283. package/scripts/controller-ngrok-rotation.sh +5 -4
  284. package/scripts/create-project-dirs.sh +10 -74
  285. package/scripts/forge-local-recovery-helper.mjs +204 -0
  286. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  287. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  288. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  289. package/scripts/hook-shim.sh +2 -2
  290. package/scripts/init-project.sh +12 -70
  291. package/scripts/install-desktop-operator-registration.ts +4 -2
  292. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  293. package/scripts/install-standalone-recovery.ts +122 -10
  294. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  295. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  296. package/scripts/lib/controller-home-env.sh +6 -3
  297. package/scripts/lib/project-init-lib.sh +39 -582
  298. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  299. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  300. package/scripts/open-source-audit-allowlist.txt +12 -1
  301. package/scripts/portability-bindings.mjs +70 -0
  302. package/scripts/run-bounded-command.mjs +55 -0
  303. package/scripts/run-bun-test-file.ts +22 -6
  304. package/scripts/run-governed-gate.ts +4 -1
  305. package/scripts/smoke-mcp-http-runtime.ts +1 -1
  306. package/scripts/smoke-runtime-recovery.ts +34 -1
  307. package/scripts/smoke-schedule-engine.ts +1 -1
  308. package/scripts/stage-runtime-release.ts +6 -1
  309. package/scripts/test-governance.ts +51 -2
  310. package/src/cli/agent-jobs/job-manager.ts +10 -1
  311. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  312. package/src/cli/chatgpt-browser/binding.ts +20 -10
  313. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  314. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  315. package/src/cli/chatgpt-browser/engine.ts +10 -11
  316. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  317. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  318. package/src/cli/chatgpt-browser/types.ts +1 -0
  319. package/src/cli/commands/bootstrap-control.ts +334 -0
  320. package/src/cli/commands/brain-assistant.ts +38 -0
  321. package/src/cli/commands/brain.ts +193 -40
  322. package/src/cli/commands/chatgpt.ts +127 -0
  323. package/src/cli/commands/computer.ts +437 -0
  324. package/src/cli/commands/doctor.ts +18 -1
  325. package/src/cli/commands/init-hook.ts +71 -61
  326. package/src/cli/commands/init.ts +3 -1
  327. package/src/cli/commands/mcp.ts +16 -10
  328. package/src/cli/commands/plugin.ts +38 -12
  329. package/src/cli/commands/recovery.ts +493 -80
  330. package/src/cli/commands/runtime.ts +9 -4
  331. package/src/cli/commands/setup-profile.ts +89 -39
  332. package/src/cli/commands/status.ts +2 -1
  333. package/src/cli/controller/check-runner.ts +487 -98
  334. package/src/cli/controller/context/types.ts +7 -1
  335. package/src/cli/controller/context-pack.ts +23 -3
  336. package/src/cli/controller/issue-store.ts +1 -1
  337. package/src/cli/controller/systemd-user.ts +110 -0
  338. package/src/cli/controller/types.ts +10 -70
  339. package/src/cli/controller/worklog.ts +67 -13
  340. package/src/cli/editing/edit-operation-contract.ts +92 -0
  341. package/src/cli/editing/edit-session.ts +289 -13
  342. package/src/cli/github/plugin.ts +17 -12
  343. package/src/cli/hook/route-registry.ts +3 -5
  344. package/src/cli/hook/runtime.ts +49 -3
  345. package/src/cli/index.ts +3 -0
  346. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  347. package/src/cli/local-bridge/console-automations.ts +2 -2
  348. package/src/cli/local-bridge/facade-api.ts +41 -14
  349. package/src/cli/local-bridge/job-store.ts +1 -1
  350. package/src/cli/local-bridge/server.ts +24 -27
  351. package/src/cli/mcp/access-mode.ts +2 -132
  352. package/src/cli/mcp/access-tools.ts +2 -387
  353. package/src/cli/mcp/audit.ts +2 -32
  354. package/src/cli/mcp/auth.ts +2 -404
  355. package/src/cli/mcp/instructions.ts +2 -34
  356. package/src/cli/mcp/legacy-context.ts +2 -16
  357. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  358. package/src/cli/mcp/multi-repository.ts +2 -436
  359. package/src/cli/mcp/oauth.ts +2 -375
  360. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  361. package/src/cli/mcp/paths.ts +2 -140
  362. package/src/cli/mcp/policy.ts +2 -232
  363. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  364. package/src/cli/mcp/proxy-env.ts +2 -62
  365. package/src/cli/mcp/readiness-projection.ts +2 -24
  366. package/src/cli/mcp/redaction.ts +2 -21
  367. package/src/cli/mcp/repo.ts +2 -24
  368. package/src/cli/mcp/repository-tools.ts +2 -1613
  369. package/src/cli/mcp/server.ts +2 -951
  370. package/src/cli/mcp/setup.ts +2 -737
  371. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  372. package/src/cli/mcp/tool-contract.ts +2 -14
  373. package/src/cli/mcp/tools.ts +2 -9
  374. package/src/cli/mcp/toolset-names.ts +2 -70
  375. package/src/cli/mcp/toolset-selection.ts +2 -41
  376. package/src/cli/mcp/toolset.ts +2 -336
  377. package/src/cli/mcp/transports/AGENTS.md +1 -10
  378. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  379. package/src/cli/mcp/transports/http.ts +2 -1414
  380. package/src/cli/mcp/transports/session-registry.ts +2 -490
  381. package/src/cli/mcp/transports/stdio.ts +2 -8
  382. package/src/cli/mcp/types.ts +2 -41
  383. package/src/cli/mcp/umbrella-tools.ts +2 -117
  384. package/src/cli/repositories/command-executor.ts +64 -18
  385. package/src/cli/repositories/command-scope.ts +117 -1
  386. package/src/cli/repositories/controller-home.ts +119 -1
  387. package/src/cli/repositories/identity.ts +13 -0
  388. package/src/cli/repositories/registry.ts +68 -49
  389. package/src/cli/repositories/repository-snapshot.ts +53 -7
  390. package/src/cli/repositories/runtime-storage.ts +27 -2
  391. package/src/cli/repositories/safe-patch.ts +34 -38
  392. package/src/cli/repositories/selected-path-actions.ts +22 -2
  393. package/src/cli/repositories/structured-git.ts +105 -25
  394. package/src/cli/runtime/helper-runner.ts +7 -0
  395. package/src/cli/runtime-invocation.ts +8 -1
  396. package/src/cli/tools/codegraph.ts +44 -25
  397. package/src/core/adoption/manifest-templates.ts +1 -1
  398. package/src/core/adoption/plan.ts +3 -26
  399. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  400. package/src/effects/process-runner.ts +8 -14
  401. package/src/runtime/context/assistant-context.ts +154 -0
  402. package/src/runtime/context/assistant-work-context.ts +147 -0
  403. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  404. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  405. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  406. package/src/runtime/context/context-closure.ts +268 -0
  407. package/src/runtime/context/context-plane.ts +211 -0
  408. package/src/runtime/context/project-engineering-contract.ts +60 -0
  409. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  410. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  411. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  412. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  413. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  414. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  415. package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
  416. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  417. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
  418. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
  419. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
  420. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
  421. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +369 -99
  422. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
  423. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  424. package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
  425. package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
  426. package/src/runtime/control-plane/execution/validation.ts +28 -3
  427. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
  428. package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
  429. package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
  430. package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
  431. package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
  432. package/src/runtime/control-plane/execution/work-handle-authority.ts +219 -22
  433. package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
  434. package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
  435. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
  436. package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
  437. package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
  438. package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
  439. package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
  440. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
  441. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
  442. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  443. package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
  444. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
  445. package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
  446. package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
  447. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  448. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
  449. package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
  450. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
  451. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
  452. package/src/runtime/control-plane/facade/index.ts +4 -2
  453. package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
  454. package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
  455. package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
  456. package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
  457. package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
  458. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
  459. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  460. package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
  461. package/src/runtime/control-plane/facade/types.ts +189 -532
  462. package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
  463. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  464. package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
  465. package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
  466. package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
  467. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
  468. package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
  469. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  470. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  471. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
  472. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  473. package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
  474. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  475. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
  476. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  477. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
  478. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
  479. package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
  480. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  481. package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
  482. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  483. package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
  484. package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
  485. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  486. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  487. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  488. package/src/runtime/control-plane/release-retention.ts +56 -16
  489. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  490. package/src/runtime/control-plane/routing/route-policy.ts +19 -0
  491. package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
  492. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  493. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
  494. package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
  495. package/src/runtime/diagnostics/incident-repair.ts +406 -0
  496. package/src/runtime/diagnostics/mcp-timing.ts +125 -6
  497. package/src/runtime/diagnostics/performance.ts +6 -14
  498. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  499. package/src/runtime/evidence/artifact-store.ts +65 -2
  500. package/src/runtime/evidence/operational-shadow.ts +38 -0
  501. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  502. package/src/runtime/evidence/sensitive-output.ts +4 -4
  503. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  504. package/src/runtime/execution/jobs/store.ts +121 -2
  505. package/src/runtime/execution/managed-workspace.ts +55 -5
  506. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  507. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  508. package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
  509. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  510. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  511. package/src/runtime/execution/process-runtime/check-result.ts +73 -2
  512. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
  513. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  514. package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
  515. package/src/runtime/execution/process-runtime/index.ts +1 -0
  516. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  517. package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
  518. package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
  519. package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
  520. package/src/runtime/execution/process-runtime/runtime.ts +132 -15
  521. package/src/runtime/execution/process-runtime/types.ts +22 -0
  522. package/src/runtime/execution/workers/executor.ts +16 -13
  523. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  524. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  525. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  526. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  527. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  528. package/src/runtime/gateway/mcp/router.ts +2 -1125
  529. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  530. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
  531. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  532. package/src/runtime/maintenance/cleanup.ts +38 -24
  533. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  534. package/src/runtime/platform/computer-platform.ts +14 -0
  535. package/src/runtime/platform/executable-discovery.ts +54 -0
  536. package/src/runtime/platform/fetch-http-server.ts +90 -0
  537. package/src/runtime/platform/service-manager.ts +138 -0
  538. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  539. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  540. package/src/runtime/plugins/action-application.ts +135 -0
  541. package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
  542. package/src/runtime/plugins/browser-adapter.ts +140 -100
  543. package/src/runtime/plugins/browser-automation-service.ts +15 -36
  544. package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
  545. package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
  546. package/src/runtime/plugins/browser-native-session-service.ts +1 -1
  547. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  548. package/src/runtime/plugins/browser-registration.ts +16 -0
  549. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  550. package/src/runtime/plugins/browser-runtime.ts +41 -9
  551. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  552. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  553. package/src/runtime/plugins/browser-session-store.ts +229 -36
  554. package/src/runtime/plugins/browser-session-types.ts +2 -98
  555. package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
  556. package/src/runtime/plugins/catalog-trust.ts +82 -0
  557. package/src/runtime/plugins/computer-registration.ts +839 -0
  558. package/src/runtime/plugins/config-store.ts +119 -0
  559. package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
  560. package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
  561. package/src/runtime/plugins/errors.ts +8 -1
  562. package/src/runtime/plugins/external-adapter.ts +45 -322
  563. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  564. package/src/runtime/plugins/external-registration.ts +9 -1
  565. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  566. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  567. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  568. package/src/runtime/plugins/first-party-registry.ts +3 -1
  569. package/src/runtime/plugins/github-adapter.ts +1 -2
  570. package/src/runtime/plugins/gmail-adapter.ts +6 -6
  571. package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
  572. package/src/runtime/plugins/google-shared.ts +31 -46
  573. package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
  574. package/src/runtime/plugins/ios-adapter.ts +1 -2
  575. package/src/runtime/plugins/local-system-adapter.ts +180 -0
  576. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  577. package/src/runtime/plugins/resend-adapter.ts +15 -24
  578. package/src/runtime/plugins/store.ts +493 -51
  579. package/src/runtime/plugins/types.ts +8 -1
  580. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  581. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  582. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  583. package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
  584. package/src/runtime/projections/controller-context.ts +12 -6
  585. package/src/runtime/projections/git-status-sampler.ts +73 -22
  586. package/src/runtime/projections/materialized-view.ts +14 -8
  587. package/src/runtime/recovery/capability-action-application.ts +228 -0
  588. package/src/runtime/recovery/index.ts +3 -0
  589. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  590. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  591. package/src/runtime/recovery/maintenance-executor.ts +358 -76
  592. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  593. package/src/runtime/release/identity.ts +6 -0
  594. package/src/runtime/resources/leases/store.ts +1 -1
  595. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  596. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  597. package/src/runtime/root/browser-session-composition.ts +59 -0
  598. package/src/runtime/root/computer-composition.ts +191 -0
  599. package/src/runtime/root/computer-target-composition.ts +16 -0
  600. package/src/runtime/root/computer-target-persistence.ts +39 -0
  601. package/src/runtime/root/controller-round-composition.ts +107 -0
  602. package/src/runtime/root/controller-services.ts +2 -0
  603. package/src/runtime/root/entry.ts +4 -3
  604. package/src/runtime/root/gateway-adapter.ts +3 -3
  605. package/src/runtime/root/local-bridge.ts +5 -2
  606. package/src/runtime/root/mcp-transport.ts +5 -5
  607. package/src/runtime/root/package-connector-service.ts +108 -56
  608. package/src/runtime/root/package-runtime-release.ts +85 -18
  609. package/src/runtime/root/package-runtime-service.ts +60 -97
  610. package/src/runtime/root/release-execution-canary.ts +71 -0
  611. package/src/runtime/root/release-loader.ts +68 -0
  612. package/src/runtime/root/release-manifest.ts +106 -2
  613. package/src/runtime/root/release-materialize.ts +103 -4
  614. package/src/runtime/root/release-store.ts +31 -2
  615. package/src/runtime/root/runtime.ts +240 -25
  616. package/src/runtime/root/scheduled-controller-composition.ts +51 -0
  617. package/src/runtime/root/scheduler.ts +12 -4
  618. package/src/runtime/root/service-runner.ts +1 -1
  619. package/src/runtime/root/service.ts +41 -16
  620. package/src/runtime/root/status.ts +1 -0
  621. package/src/runtime/root/types.ts +24 -1
  622. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  623. package/src/runtime/shared/process-environment.ts +19 -4
  624. package/src/runtime/shared/storage-capacity.ts +68 -0
  625. package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
  626. package/src/runtime/standalone-recovery/core.ts +984 -230
  627. package/src/runtime/standalone-recovery/entry.ts +182 -51
  628. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  629. package/src/runtime/standalone-recovery/installer.ts +212 -104
  630. package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
  631. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  632. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  633. package/src/runtime/standalone-recovery/performance.ts +135 -0
  634. package/src/runtime/standalone-recovery/release.ts +5 -0
  635. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  636. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
  637. package/src/runtime/workflow/schedules/engine.ts +191 -343
  638. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  639. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  640. package/src/runtime/workflow/schedules/store.ts +2 -607
  641. package/src/runtime/workflow/schedules/types.ts +2 -127
  642. package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
  643. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  644. package/src/runtime/workflows/runtime.ts +486 -0
  645. package/src/testing/test-governance.ts +90 -29
  646. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  647. package/docs/architecture/history/README.md +0 -7
  648. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  649. package/docs/architecture/history/global-hook-runtime.md +0 -166
  650. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  651. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  652. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  653. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  654. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  655. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  656. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  657. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  658. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  659. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  660. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  661. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  662. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  663. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  664. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  665. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  666. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  667. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  668. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  669. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  670. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  671. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  672. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  673. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  674. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  675. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  676. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  677. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  678. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  679. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  680. package/docs/architecture/requests/archive/2026/root.md +0 -68
  681. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  682. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  683. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  684. package/scripts/archive-architecture-request.sh +0 -4
  685. package/scripts/run-skill-evals.ts +0 -1022
@@ -1,23 +1,39 @@
1
- import { randomUUID } from 'crypto';
1
+ import { createHash, randomUUID } from 'crypto';
2
+ import { globMatches } from '../../../cli/mcp/paths';
2
3
  import {
3
4
  createHandoffItem,
4
5
  listHandoffItems,
5
6
  type HandoffInboxStoreOptions,
6
7
  } from './handoff-inbox-store';
7
- import { getControllerSession } from './controller-session-store';
8
+ import { getControllerSession } from '../../../../packages/kernel/controller/api/index';
9
+ import { executionPlacement, readForgeInstanceIdentity } from '../../../../packages/kernel/identity/api/index';
10
+ import { projectAutonomousGoalProgression, type ProgressionWorkSnapshot } from '../../../../packages/kernel/progression/api/index';
8
11
  import {
12
+ applyEngineeringBlockerDisposition,
13
+ buildEngineeringBlockerDispositionReceipt,
14
+ buildEngineeringContextReceipt,
15
+ engineeringWorkProfileForRisk,
16
+ evaluateEngineeringAdmission,
9
17
  appendVerificationRecord,
10
18
  appendWorkEvidence,
11
19
  appendWorkHandoffRef,
20
+ cancelWorkContract,
12
21
  createWorkContract,
22
+ failWorkContract,
13
23
  getWorkContract,
24
+ isTerminalWorkContractStatus,
14
25
  listWorkContracts,
26
+ readActiveWorkCandidates,
27
+ recordWorkEvidenceState,
15
28
  recordWorkScopeEvidence,
29
+ recordWorkImplementationReview,
30
+ requestWorkImplementationReview,
16
31
  summarizeWorkContract,
17
32
  transitionWorkContractPhase,
18
33
  updateWorkContract,
34
+ type EngineeringAdmissionEvidence,
19
35
  type WorkContractStoreOptions,
20
- } from './work-contract-store';
36
+ } from '../../../../packages/kernel/work/api/index';
21
37
  import {
22
38
  claimPlanStepForWork,
23
39
  completePlanStepForWork,
@@ -26,7 +42,7 @@ import {
26
42
  } from './plan-contract-store';
27
43
  import { withPrimaryWorkAdmissionLock } from './semantic-admission';
28
44
  import { completeWorkWithReceipt } from '../execution/work-completion-authority';
29
- import { evaluateReadOnlyReviewSourceIdentity, evaluateWorkCompletionEvidence, evaluateWorkImplementationEvidence, verificationRecordAppliesToCurrentWorkspace } from '../execution/work-evidence-policy';
45
+ import { effectiveCurrentWorkVerificationRecords, evaluateReadOnlyReviewSourceIdentity, evaluateWorkCompletionEvidence, evaluateWorkImplementationEvidence } from '../execution/work-evidence-policy';
30
46
  import { readRequirement } from '../persistence/requirement-store';
31
47
  import {
32
48
  classifyVerificationOutcome,
@@ -35,6 +51,18 @@ import {
35
51
  type CheckDefinitionLike,
36
52
  } from './check-normalization';
37
53
  import { evaluatePolicyGate } from './policy-gate';
54
+ import {
55
+ assertImplementationReviewPreDeliveryBoundary,
56
+ authoritativeImplementationReviewVerificationEvidence,
57
+ implementationReviewChangedPathDigest,
58
+ latestImplementationReview,
59
+ normalizeImplementationReviewChangedPaths,
60
+ workRequiresImplementationReview,
61
+ type ImplementationReviewDecision,
62
+ type ImplementationReviewCandidateIdentity,
63
+ type WorkImplementationReviewFinding,
64
+ type WorkImplementationReviewRecord,
65
+ } from '../../../../packages/kernel/work/api/index';
38
66
  import { buildFacadeResult } from './facade-result';
39
67
  import { validateSuggestedNextActions } from './suggested-actions';
40
68
  import { buildWorkContinuationSnapshot } from './work-continuation';
@@ -53,8 +81,9 @@ import type {
53
81
  } from './types';
54
82
  import { selectExecutionMode } from './types';
55
83
  import { resolveWorkspaceAdmissionConstraint } from '../routing/workspace-admission';
84
+ import { resolveContextPlane, type ContextScope } from '../../context/context-plane';
56
85
 
57
- export type GoalWorkloopOperation = 'start' | 'continue' | 'verify' | 'finalize' | 'stop';
86
+ export type GoalWorkloopOperation = 'start' | 'continue' | 'verify' | 'review' | 'finalize' | 'stop';
58
87
 
59
88
  export interface GoalWorkloopContext {
60
89
  workStore: WorkContractStoreOptions;
@@ -63,10 +92,16 @@ export interface GoalWorkloopContext {
63
92
  availableChecks?: readonly CheckDefinitionLike[];
64
93
  planStore?: PlanContractStoreOptions;
65
94
  sourceRevision?: string;
95
+ /** Explicit only when repository observation proves the Work started from a revision or an unborn HEAD. */
96
+ sourceBaseState?: 'revision' | 'unborn';
66
97
  checkoutId?: string;
67
98
  principalId?: string;
68
99
  controllerInstanceId?: string;
69
100
  workspaceFingerprint?: string;
101
+ /** Trusted repository observation for placement admission. Never accept this as caller-supplied semantic authority. */
102
+ workspaceDirty?: boolean;
103
+ /** Stage-insensitive Git-canonical identity of the exact current review candidate. Derived by the trusted repository adapter. */
104
+ implementationReviewWorkspaceFingerprint?: string;
70
105
  /** Current net repository paths changed from the Work base plus dirty checkout paths. */
71
106
  workspaceChangedPaths?: readonly string[];
72
107
  /** Durable terminal repository Process ids explicitly bound to this Work + checkout. */
@@ -81,6 +116,8 @@ export type WorkAdmissionRelation = 'continue' | 'extend' | 'parallel' | 'new_go
81
116
 
82
117
  export interface GoalWorkloopStartInput {
83
118
  objective: string;
119
+ /** Optional exact durable identity reserved by the admission authority before creation. */
120
+ workId?: string;
84
121
  acceptanceCriteria?: string[];
85
122
  allowedPaths?: string[];
86
123
  /** Non-authoritative first-pass discovery candidates. */
@@ -90,6 +127,8 @@ export interface GoalWorkloopStartInput {
90
127
  constraints?: WorkContract['constraints'];
91
128
  modeInput: ExecutionModeSelectionInput;
92
129
  requestedBy?: WorkContract['requestedBy'];
130
+ /** Stable durable admission identity for system-originated Work such as recurrent incident repair. */
131
+ requestId?: string;
93
132
  relatedWorkId?: string;
94
133
  workRelation?: WorkAdmissionRelation;
95
134
  requirementId?: string;
@@ -102,6 +141,8 @@ export interface GoalWorkloopStartInput {
102
141
  planStepId?: string;
103
142
  /** Explicit technical evidence shape chosen by the semantic Controller. Never inferred from objective/check text. */
104
143
  workKind?: Extract<WorkKind, 'repository_change' | 'completed_no_change' | 'read_only_review' | 'investigation' | 'local_effect' | 'remote_effect' | 'reconciliation'>;
144
+ /** Trusted source-bound evidence resolved by Forge evidence authorities before admission. Never populate this field from raw caller-supplied receipt ids. */
145
+ verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
105
146
  }
106
147
 
107
148
  export interface GoalWorkloopAcceptanceEvidenceBinding {
@@ -124,6 +165,15 @@ export interface GoalWorkloopContinueInput {
124
165
  acceptanceEvidence?: GoalWorkloopAcceptanceEvidenceBinding[];
125
166
  /** Explicit Controller decision after deterministic acceptance failure; never inferred from note text. */
126
167
  acceptanceFailureDecision?: 'repair' | 'rescope';
168
+ /** Trusted refreshed engineering evidence minted by Runtime composition, never copied from raw caller receipt ids. */
169
+ verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
170
+ /** Semantic Controller blocker classification; Forge derives the permitted action and persists the receipt. */
171
+ engineeringBlocker?: {
172
+ blockerId: string;
173
+ classification: 'same_root_cause' | 'unrelated';
174
+ rationale: string;
175
+ semanticScopeKeys?: string[];
176
+ };
127
177
  }
128
178
 
129
179
  export interface GoalWorkloopVerifyInput {
@@ -147,6 +197,13 @@ export interface GoalWorkloopVerifyInput {
147
197
  skipped?: boolean;
148
198
  }
149
199
 
200
+ export interface GoalWorkloopReviewInput {
201
+ workId: string;
202
+ decision: ImplementationReviewDecision;
203
+ rationale: string;
204
+ findings?: WorkImplementationReviewFinding[];
205
+ }
206
+
150
207
  export interface GoalWorkloopFinalizeInput {
151
208
  workId: string;
152
209
  forceFailed?: boolean;
@@ -163,6 +220,57 @@ function nowIso(ctx: GoalWorkloopContext): string {
163
220
  return ctx.now?.() ?? new Date().toISOString();
164
221
  }
165
222
 
223
+ function currentImplementationReviewCandidate(
224
+ ctx: GoalWorkloopContext,
225
+ work: WorkContract,
226
+ ): ImplementationReviewCandidateIdentity {
227
+ const sourceRevision = ctx.sourceRevision?.trim() ?? '';
228
+ const verificationWorkspaceFingerprint = ctx.workspaceFingerprint?.trim() ?? '';
229
+ const workspaceFingerprint = ctx.implementationReviewWorkspaceFingerprint?.trim() ?? '';
230
+ const changedPaths = normalizeImplementationReviewChangedPaths(
231
+ ctx.workspaceChangedPaths ?? work.scopeEvidence?.actualChangedPaths ?? [],
232
+ );
233
+ if (!sourceRevision || !verificationWorkspaceFingerprint || !workspaceFingerprint) {
234
+ throw new Error('WORK_IMPLEMENTATION_REVIEW_SOURCE_IDENTITY_REQUIRED');
235
+ }
236
+ const verification = authoritativeImplementationReviewVerificationEvidence({
237
+ repoId: work.repoId,
238
+ workId: work.workId,
239
+ requiredCheckIds: work.checks,
240
+ records: work.checkRefs,
241
+ sourceRevision,
242
+ workspaceFingerprint: verificationWorkspaceFingerprint,
243
+ });
244
+ if (verification.missingCheckIds.length > 0) {
245
+ throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
246
+ }
247
+ return {
248
+ sourceRevision,
249
+ workspaceFingerprint,
250
+ verificationWorkspaceFingerprint,
251
+ changedPaths,
252
+ verificationEvidence: verification.evidence,
253
+ architectureEvidence: [],
254
+ };
255
+ }
256
+
257
+ function implementationReviewAcceptanceSummary(work: WorkContract): string {
258
+ const summary = work.acceptanceCriteria.map((criterion) => criterion.trim()).filter(Boolean).join(' | ');
259
+ return (summary || 'No explicit acceptance criteria; reviewer assessed the Work objective and exact current implementation evidence.').slice(0, 2_000);
260
+ }
261
+
262
+ function implementationReviewSuggestedAction(workId: string): SuggestedNextAction {
263
+ return {
264
+ label: 'Review implementation',
265
+ tool: 'rh_work',
266
+ operation: 'review',
267
+ payload: { work_id: workId },
268
+ risk: 'workspace_write',
269
+ confidence: 'high',
270
+ reason: 'Verification is complete; an explicit Controller implementation decision is required before delivery.',
271
+ };
272
+ }
273
+
166
274
  function workIdFor(objective: string): string {
167
275
  const slug = objective
168
276
  .toLowerCase()
@@ -172,6 +280,11 @@ function workIdFor(objective: string): string {
172
280
  return `work-${slug}-${randomUUID().slice(0, 8)}`;
173
281
  }
174
282
 
283
+ function linkedEngineeringBlockerWorkId(parentWorkId: string, blockerId: string): string {
284
+ const digest = createHash('sha256').update(`${parentWorkId}\0${blockerId.trim()}`).digest('hex').slice(0, 24);
285
+ return `work-linked-engineering-blocker-${digest}`;
286
+ }
287
+
175
288
  function handoffIdFor(prefix: string): string {
176
289
  return `hnd-${prefix}-${randomUUID().slice(0, 8)}`;
177
290
  }
@@ -188,6 +301,22 @@ function workRiskFor(input: GoalWorkloopStartInput): WorkRisk {
188
301
  return 'medium';
189
302
  }
190
303
 
304
+ function resolvedWorkKindFor(input: GoalWorkloopStartInput): WorkKind {
305
+ if (input.workKind) return input.workKind;
306
+ // allowed_paths and discovery hints do not prove repository mutation. External
307
+ // effects become repository_change only when source-change intent is explicit.
308
+ const repositoryChangeIntent = (input.modeInput.expectedFiles ?? 0) > 0
309
+ || (input.modeInput.expectedChangedLines ?? 0) > 0;
310
+ const typedRecoverableReadOnlyReview = input.modeInput.mutation === false
311
+ && input.modeInput.requiresInvestigation === true
312
+ && input.modeInput.requiresRecovery === true;
313
+ if (typedRecoverableReadOnlyReview) return 'read_only_review';
314
+ if (input.modeInput.requiresExternalEffect === true && !repositoryChangeIntent) {
315
+ return input.modeInput.remoteWrite === true ? 'remote_effect' : 'local_effect';
316
+ }
317
+ return 'repository_change';
318
+ }
319
+
191
320
  function suggestedForWorkIdentity(workId: string, checks: string[], extras: SuggestedNextAction[] = []): SuggestedNextAction[] {
192
321
  const base: SuggestedNextAction[] = [
193
322
  {
@@ -259,7 +388,23 @@ export function routeWorkStart(
259
388
  const strategyConflictRequiresApproval = input.constraints?.architectureStrategyChange === true
260
389
  || input.constraints?.conflictsWithThinHarnessPolicy === true
261
390
  || input.constraints?.changesDefaultExecutionStrategy === true;
262
- const relatedLifecycleSource = input.relatedWorkId ? getWorkContract(ctx.workStore, input.relatedWorkId) : undefined;
391
+ let relatedLifecycleSource: WorkContract | undefined;
392
+ if (input.relatedWorkId) {
393
+ try {
394
+ relatedLifecycleSource = getWorkContract(ctx.workStore, input.relatedWorkId);
395
+ } catch (error) {
396
+ return buildFacadeResult({
397
+ status: 'blocked',
398
+ summary: `WORK_RELATED_CONTRACT_INVALID: ${input.relatedWorkId} is malformed and cannot authorize a Work relationship.`,
399
+ data: {
400
+ executionStarted: false,
401
+ workContractCreated: false,
402
+ invalidWorkId: input.relatedWorkId,
403
+ invalidWorkError: error instanceof Error ? error.message : String(error),
404
+ },
405
+ });
406
+ }
407
+ }
263
408
  const inheritedReadOnlyReview = Boolean(
264
409
  input.workKind === undefined
265
410
  && input.workRelation === 'new_goal'
@@ -279,10 +424,21 @@ export function routeWorkStart(
279
424
  });
280
425
  }
281
426
  const requestedWorkKind = input.workKind ?? (inheritedReadOnlyReview ? 'read_only_review' : undefined);
427
+ const explicitSourceMutationFence = input.allowedPaths !== undefined
428
+ && input.allowedPaths.length === 0
429
+ && (input.forbiddenPaths ?? []).some((value) => {
430
+ const normalized = value.trim().replace(/\\/g, '/');
431
+ return normalized === '*' || normalized === '**' || normalized === '**/*';
432
+ });
282
433
  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;
434
+ && (
435
+ explicitSourceMutationFence
436
+ || (
437
+ (input.modeInput.mutation === false || (input.modeInput.mutation === undefined && input.modeInput.risk === 'readonly'))
438
+ && input.modeInput.requiresInvestigation === true
439
+ && input.modeInput.requiresRecovery === true
440
+ )
441
+ );
286
442
  const effectiveWorkKind = requestedWorkKind ?? (typedReadOnlyReviewRequested ? 'read_only_review' : undefined);
287
443
  const readOnlyReviewRequested = effectiveWorkKind === 'read_only_review';
288
444
  const readOnlyMutationConflict = readOnlyReviewRequested && (
@@ -300,12 +456,35 @@ export function routeWorkStart(
300
456
  data: { executionStarted: false, workContractCreated: false, workKind: effectiveWorkKind },
301
457
  });
302
458
  }
459
+ const contextScopes: ContextScope[] = [];
460
+ if (input.requirementId?.trim()) contextScopes.push({ schemaVersion: 1, kind: 'requirement', id: input.requirementId.trim() });
461
+ if (input.planId?.trim()) contextScopes.push({ schemaVersion: 1, kind: 'plan', id: input.planId.trim() });
462
+ if (input.relatedWorkId?.trim()) contextScopes.push({ schemaVersion: 1, kind: 'work', id: input.relatedWorkId.trim() });
463
+ const resolvedContext = ctx.workStore.controllerHome
464
+ ? resolveContextPlane({
465
+ controllerHome: ctx.workStore.controllerHome,
466
+ scopes: contextScopes,
467
+ intent: input.modeInput.routePolicyInput?.intent.taskIntent ?? 'implementation',
468
+ now: nowIso(ctx),
469
+ })
470
+ : undefined;
471
+ // A terminal continuation is not a new semantic request. Its durable predecessor
472
+ // and Plan lineage already define the goal scope, while the successor Plan step
473
+ // becomes the Work objective later in startGoalWorkloop. Do not force the external
474
+ // Controller to duplicate semantic text merely to satisfy generic route selection.
475
+ // This fallback is routing context only and never becomes successor Work authority.
476
+ const routeObjective = input.objective.trim()
477
+ || (input.workRelation === 'continue' && relatedLifecycleSource?.status === 'completed'
478
+ ? relatedLifecycleSource.objective.trim()
479
+ : '');
303
480
  const effectiveModeInput: ExecutionModeSelectionInput = {
304
481
  ...input.modeInput,
305
- objective: input.objective,
482
+ contextRouteHints: input.modeInput.contextRouteHints ?? resolvedContext?.routeHints,
483
+ objective: routeObjective,
306
484
  knownPaths: input.allowedPaths,
307
485
  workspacePlacement: placementConstraint.workspaceMode,
308
486
  directMainProhibited: placementConstraint.directMainProhibited,
487
+ workspaceDirty: ctx.workspaceDirty ?? input.modeInput.workspaceDirty,
309
488
  mutation: readOnlyReviewRequested ? false : input.modeInput.mutation ?? input.modeInput.risk !== 'readonly',
310
489
  risk: readOnlyReviewRequested ? 'readonly' : input.modeInput.risk,
311
490
  approvalConfirmed: input.approvalConfirmed === true,
@@ -383,10 +562,21 @@ export function routeWorkStart(
383
562
  // the RouteDecision. The actual repository mutation can pass this Work id
384
563
  // to repository_safe_patch_apply / repository_command_execute for evidence
385
564
  // 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
- : [];
565
+ const directAdmissionSnapshot = input.relatedWorkId || (input.planId && input.planStepId)
566
+ ? readActiveWorkCandidates({ ...ctx.workStore, limit: 100 })
567
+ : { contracts: [], invalid: [] };
568
+ const invalidDirectOwner = directAdmissionSnapshot.invalid.find((candidate) =>
569
+ candidate.workId === input.relatedWorkId
570
+ || (input.planId && input.planStepId && candidate.planId === input.planId && candidate.planStepId === input.planStepId));
571
+ if (invalidDirectOwner) {
572
+ return buildFacadeResult({
573
+ status: 'blocked',
574
+ summary: `WORK_ADMISSION_INVALID_OWNER: ${invalidDirectOwner.workId} is malformed and cannot be used as Direct Control ownership authority.`,
575
+ data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidDirectOwner.workId, invalidWorkError: invalidDirectOwner.error },
576
+ });
577
+ }
578
+ const directOwnershipCandidates = directAdmissionSnapshot.contracts
579
+ .filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary');
390
580
  const directPlan = input.planId && ctx.planStore ? getPlanContract(ctx.planStore, input.planId) : undefined;
391
581
  const directPlanStep = directPlan && input.planStepId ? directPlan.steps.find((step) => step.id === input.planStepId) : undefined;
392
582
  const directOwner = (input.relatedWorkId
@@ -587,21 +777,215 @@ export function startGoalWorkloop(
587
777
  ));
588
778
  }
589
779
  const at = nowIso(ctx);
780
+ const resolvedWorkKind = resolvedWorkKindFor(input);
781
+ const workRisk = workRiskFor(input);
782
+ // External-effect risk is governed by authorization/confirmation/receipt
783
+ // semantics. Engineering admission is specifically for repository mutation.
784
+ const engineeringRisk: WorkRisk = resolvedWorkKind === 'repository_change' ? workRisk : 'readonly';
785
+ const engineeringProfile = engineeringWorkProfileForRisk(engineeringRisk);
786
+ const engineeringSourceIdentity = ctx.sourceRevision?.trim()
787
+ ? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
788
+ : ctx.sourceBaseState === 'unborn'
789
+ ? { kind: 'unborn' as const }
790
+ : { kind: 'unknown' as const };
791
+ let engineeringContext;
792
+ try {
793
+ engineeringContext = buildEngineeringContextReceipt({
794
+ risk: engineeringRisk,
795
+ sourceIdentity: engineeringSourceIdentity,
796
+ evidence: input.verifiedEngineeringEvidence,
797
+ recordedAt: at,
798
+ });
799
+ } catch (error) {
800
+ return buildFacadeResult({
801
+ status: 'blocked',
802
+ summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
803
+ data: { executionStarted: false, workContractCreated: false, engineeringProfile },
804
+ });
805
+ }
806
+ const engineeringMutation = resolvedWorkKind === 'repository_change'
807
+ && (input.modeInput.mutation ?? input.modeInput.risk !== 'readonly');
808
+ const engineeringAdmission = evaluateEngineeringAdmission({
809
+ profile: engineeringProfile,
810
+ receipt: engineeringContext,
811
+ mutation: engineeringMutation,
812
+ });
813
+ if (!engineeringAdmission.allowed) {
814
+ return buildFacadeResult({
815
+ status: 'blocked',
816
+ summary: `${engineeringAdmission.code}: ${engineeringProfile.riskClass} mutation is missing required pre-mutation engineering evidence.`,
817
+ data: {
818
+ executionStarted: false,
819
+ workContractCreated: false,
820
+ engineeringProfile,
821
+ engineeringContext,
822
+ missingEngineeringEvidence: engineeringAdmission.missing,
823
+ },
824
+ });
825
+ }
590
826
  const available = ctx.availableChecks ?? [];
591
827
  const workspaceMode = placementConstraint.workspaceMode;
592
- const activeWorks = listWorkContracts({ ...ctx.workStore, status: 'active', limit: 100 })
828
+ const activeAdmissionSnapshot = readActiveWorkCandidates({ ...ctx.workStore, limit: 100 });
829
+ const activeWorks = activeAdmissionSnapshot.contracts
593
830
  .filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary');
831
+ const invalidRelatedWork = input.relatedWorkId
832
+ ? activeAdmissionSnapshot.invalid.find((candidate) => candidate.workId === input.relatedWorkId)
833
+ : undefined;
834
+ if (invalidRelatedWork) {
835
+ return buildFacadeResult({
836
+ status: 'blocked',
837
+ summary: `WORK_RELATED_CONTRACT_INVALID: ${invalidRelatedWork.workId} is malformed and cannot authorize a Work relationship.`,
838
+ data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidRelatedWork.workId, invalidWorkError: invalidRelatedWork.error },
839
+ });
840
+ }
841
+ const relatedLifecycleSource = input.relatedWorkId
842
+ ? getWorkContract(ctx.workStore, input.relatedWorkId)
843
+ : undefined;
594
844
  const workspaceOwner = ctx.checkoutId
595
845
  ? activeWorks.find((candidate) => candidate.checkoutId === ctx.checkoutId
596
846
  && candidate.worktreePolicy.required !== true
597
847
  && !candidate.worktreeRef)
598
848
  : undefined;
849
+ const terminalContinuationSource = input.workRelation === 'continue'
850
+ && relatedLifecycleSource
851
+ && isTerminalWorkContractStatus(relatedLifecycleSource.status)
852
+ ? relatedLifecycleSource
853
+ : undefined;
854
+ if (terminalContinuationSource && !terminalContinuationSource.requirementId && !terminalContinuationSource.planId) {
855
+ return buildFacadeResult({
856
+ status: 'blocked',
857
+ 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.`,
858
+ data: { executionStarted: false, workContractCreated: false, relatedWorkId: terminalContinuationSource.workId },
859
+ });
860
+ }
599
861
  const explicitRelatedWork = input.relatedWorkId
600
862
  ? activeWorks.find((candidate) => candidate.workId === input.relatedWorkId)
601
863
  : 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;
864
+
865
+ let resolvedPlanId = input.planId?.trim() || undefined;
866
+ let resolvedPlanStepId = input.planStepId?.trim() || undefined;
867
+ if (terminalContinuationSource?.planId) {
868
+ if (resolvedPlanId && resolvedPlanId !== terminalContinuationSource.planId) {
869
+ return buildFacadeResult({
870
+ status: 'blocked',
871
+ summary: `WORK_SUCCESSOR_PLAN_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${terminalContinuationSource.planId}, not ${resolvedPlanId}. Supersede/replan explicitly instead of silently moving continuation authority.`,
872
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorPlanId: terminalContinuationSource.planId, requestedPlanId: resolvedPlanId },
873
+ });
874
+ }
875
+ resolvedPlanId = terminalContinuationSource.planId;
876
+ }
877
+ const plan = resolvedPlanId && ctx.planStore ? getPlanContract(ctx.planStore, resolvedPlanId) : undefined;
878
+ if (terminalContinuationSource?.planId && !plan) {
879
+ return buildFacadeResult({
880
+ status: 'blocked',
881
+ summary: `WORK_SUCCESSOR_PLAN_NOT_FOUND: ${terminalContinuationSource.planId}. Recover the durable Plan authority before creating a successor Work.`,
882
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: terminalContinuationSource.planId },
883
+ });
884
+ }
885
+ if (terminalContinuationSource?.planStepId && plan && !resolvedPlanStepId) {
886
+ const predecessorStep = plan.steps.find((step) => step.id === terminalContinuationSource.planStepId);
887
+ if (!predecessorStep) {
888
+ return buildFacadeResult({
889
+ status: 'blocked',
890
+ summary: `WORK_SUCCESSOR_PLAN_STEP_NOT_FOUND: predecessor ${terminalContinuationSource.workId} references ${terminalContinuationSource.planStepId}.`,
891
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, predecessorPlanStepId: terminalContinuationSource.planStepId },
892
+ });
893
+ }
894
+ const requirementRecord = plan.requirementId && ctx.workStore.controllerHome
895
+ ? readRequirement({ controllerHome: ctx.workStore.controllerHome }, plan.requirementId)
896
+ : undefined;
897
+ const progressionWorks: ProgressionWorkSnapshot[] = plan.steps
898
+ .flatMap((candidate) => candidate.workId ? [getWorkContract(ctx.workStore, candidate.workId)] : [])
899
+ .filter((candidate): candidate is NonNullable<typeof candidate> => Boolean(candidate))
900
+ .map((candidate) => ({
901
+ workId: candidate.workId,
902
+ requirementId: candidate.requirementId,
903
+ planId: candidate.planId,
904
+ planStepId: candidate.planStepId,
905
+ status: candidate.status,
906
+ baseRevision: candidate.baseRevision,
907
+ completionTargetRevision: candidate.completionReceipt && 'targetRevision' in candidate.completionReceipt
908
+ ? candidate.completionReceipt.targetRevision
909
+ : undefined,
910
+ }));
911
+ const progression = projectAutonomousGoalProgression({
912
+ requirement: {
913
+ requirementId: plan.requirementId ?? terminalContinuationSource.requirementId ?? `plan:${plan.planId}`,
914
+ state: requirementRecord?.value.state ?? 'active',
915
+ revision: requirementRecord?.revision ?? 0,
916
+ },
917
+ plan: {
918
+ planId: plan.planId,
919
+ requirementId: plan.requirementId,
920
+ sourceRevision: plan.sourceRevision,
921
+ status: plan.status,
922
+ steps: plan.steps.map((candidate) => ({ id: candidate.id, dependencies: candidate.dependencies, status: candidate.status, workId: candidate.workId })),
923
+ },
924
+ currentSourceRevision: ctx.sourceRevision ?? plan.sourceRevision,
925
+ works: progressionWorks,
926
+ controllerRounds: [],
927
+ });
928
+ if (progression.kind === 'request_controller_acceptance') {
929
+ return buildFacadeResult({
930
+ status: 'blocked',
931
+ summary: `PLAN_STEP_SEMANTIC_ACCEPTANCE_REQUIRED: ${plan.planId}/${progression.planStepId ?? predecessorStep.id} has machine-complete delivery. Accept the delivered step before advancing the same semantic goal.`,
932
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, planStepId: progression.planStepId ?? predecessorStep.id, semanticAcceptanceRequired: true, progression },
933
+ });
934
+ }
935
+ if (progression.reasonCode === 'PLAN_STEP_TERMINAL_WORK_RECONCILIATION_REQUIRED') {
936
+ return buildFacadeResult({
937
+ status: 'blocked',
938
+ summary: `PLAN_STEP_TERMINAL_WORK_RECONCILIATION_REQUIRED: ${plan.planId}/${progression.planStepId ?? predecessorStep.id} must project terminal Work ${terminalContinuationSource.workId} before semantic progression.`,
939
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, planStepId: progression.planStepId ?? predecessorStep.id, repairRequired: true, progression },
940
+ });
941
+ }
942
+ if (progression.kind === 'start_next_plan_step' && progression.planStepId) {
943
+ resolvedPlanStepId = progression.planStepId;
944
+ } else if (progression.kind === 'request_requirement_acceptance') {
945
+ return buildFacadeResult({
946
+ status: 'ok',
947
+ summary: `CONTINUATION_REQUIREMENT_ACCEPTANCE_REQUIRED: ${plan.planId} is finalized; the semantic Controller must record goal_complete before the Requirement becomes done.`,
948
+ data: { executionStarted: false, workContractCreated: false, admissionDecision: 'requirement_acceptance_required', requirementAcceptanceRequired: true, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, requirementId: plan.requirementId, progression },
949
+ });
950
+ } else if (progression.kind === 'goal_complete') {
951
+ return buildFacadeResult({
952
+ status: 'ok',
953
+ summary: `CONTINUATION_GOAL_COMPLETE: ${plan.planId} Requirement is already semantically complete; no successor Work was created.`,
954
+ data: { executionStarted: false, workContractCreated: false, admissionDecision: 'goal_complete', goalComplete: true, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, requirementId: plan.requirementId, progression },
955
+ });
956
+ } else if (progression.reasonCode === 'MULTIPLE_READY_PLAN_STEPS') {
957
+ const candidates = (progression.dependencyStepIds ?? [])
958
+ .map((stepId) => plan.steps.find((candidate) => candidate.id === stepId))
959
+ .filter((candidate): candidate is NonNullable<typeof candidate> => Boolean(candidate));
960
+ return buildFacadeResult({
961
+ status: 'ok',
962
+ summary: `PLAN_SUCCESSOR_STEP_RESOLUTION_REQUIRED: ${plan.planId} has ${candidates.length} executable successor steps. The semantic Controller must select one; Forge will not invent ordering.`,
963
+ data: { executionStarted: false, workContractCreated: false, admissionDecision: 'resolution_required', resolutionRequired: true, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, candidatePlanSteps: candidates.map((candidate) => ({ id: candidate.id, objective: candidate.objective })), progression },
964
+ });
965
+ } else if (progression.kind === 'request_replan') {
966
+ return buildFacadeResult({
967
+ status: 'blocked',
968
+ summary: `PLAN_SUCCESSOR_REPLAN_REQUIRED: ${plan.planId} ${progression.reasonCode}.`,
969
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, progression },
970
+ });
971
+ } else if (progression.kind !== 'start_next_plan_step') {
972
+ return buildFacadeResult({
973
+ status: 'blocked',
974
+ summary: `PLAN_SUCCESSOR_PROGRESSION_BLOCKED: ${plan.planId} ${progression.reasonCode}.`,
975
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, progression },
976
+ });
977
+ }
978
+ }
979
+ const planStep = plan && resolvedPlanStepId ? plan.steps.find((step) => step.id === resolvedPlanStepId) : undefined;
604
980
  const requestedRequirementId = input.requirementId?.trim() || undefined;
981
+ const predecessorRequirementId = terminalContinuationSource?.requirementId;
982
+ if (predecessorRequirementId && requestedRequirementId && predecessorRequirementId !== requestedRequirementId) {
983
+ return buildFacadeResult({
984
+ status: 'blocked',
985
+ summary: `WORK_SUCCESSOR_REQUIREMENT_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${predecessorRequirementId}, not ${requestedRequirementId}.`,
986
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorRequirementId, requestedRequirementId },
987
+ });
988
+ }
605
989
  if (plan && requestedRequirementId && requestedRequirementId !== plan.requirementId) {
606
990
  return buildFacadeResult({
607
991
  status: 'blocked',
@@ -609,7 +993,14 @@ export function startGoalWorkloop(
609
993
  data: { executionStarted: false, workContractCreated: false, planId: plan.planId, planRequirementId: plan.requirementId, requestedRequirementId },
610
994
  });
611
995
  }
612
- const effectiveRequirementId = requestedRequirementId || plan?.requirementId;
996
+ if (plan?.requirementId && predecessorRequirementId && plan.requirementId !== predecessorRequirementId) {
997
+ return buildFacadeResult({
998
+ status: 'blocked',
999
+ summary: `WORK_SUCCESSOR_PLAN_REQUIREMENT_MISMATCH: predecessor ${terminalContinuationSource!.workId} belongs to ${predecessorRequirementId}, while ${plan.planId} belongs to ${plan.requirementId}.`,
1000
+ data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource!.workId, predecessorRequirementId, planId: plan.planId, planRequirementId: plan.requirementId },
1001
+ });
1002
+ }
1003
+ const effectiveRequirementId = requestedRequirementId || plan?.requirementId || predecessorRequirementId;
613
1004
  if (
614
1005
  effectiveRequirementId
615
1006
  && ctx.workStore.controllerHome
@@ -637,33 +1028,68 @@ export function startGoalWorkloop(
637
1028
  if (mismatches.length > 0) {
638
1029
  return buildFacadeResult({
639
1030
  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 },
1031
+ summary: `PLAN_STEP_WORK_CONTRACT_MISMATCH: ${resolvedPlanId}/${resolvedPlanStepId} conflicts with frozen Plan step field(s): ${mismatches.join(', ')}. Replan instead of widening or narrowing Work admission.`,
1032
+ data: { executionStarted: false, workContractCreated: false, planId: resolvedPlanId, planStepId: resolvedPlanStepId, mismatches },
642
1033
  });
643
1034
  }
644
1035
  }
1036
+ const effectiveObjective = planStep?.objective ?? input.objective;
645
1037
  const effectiveAcceptanceCriteria = planStep?.acceptanceCriteria ?? input.acceptanceCriteria ?? [];
646
1038
  const effectiveAllowedPaths = planStep?.allowedPaths ?? input.allowedPaths ?? [];
647
1039
  const effectiveForbiddenPaths = planStep?.forbiddenPaths ?? input.forbiddenPaths ?? [];
648
1040
  const effectiveChecks = planStep?.checks ?? input.checks ?? [];
649
1041
  const normalized = normalizeCheckIds(effectiveChecks, available);
650
- const boundPlanStepWorks = input.planId && input.planStepId
651
- ? activeWorks.filter((candidate) => candidate.planId === input.planId && candidate.planStepId === input.planStepId)
1042
+ const invalidLineageWork = activeAdmissionSnapshot.invalid.find((candidate) =>
1043
+ (input.relatedWorkId && candidate.workId === input.relatedWorkId)
1044
+ || (resolvedPlanId && resolvedPlanStepId && candidate.planId === resolvedPlanId && candidate.planStepId === resolvedPlanStepId));
1045
+ if (invalidLineageWork) {
1046
+ return buildFacadeResult({
1047
+ status: 'blocked',
1048
+ 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.`,
1049
+ data: {
1050
+ executionStarted: false,
1051
+ workContractCreated: false,
1052
+ invalidWorkId: invalidLineageWork.workId,
1053
+ invalidWorkError: invalidLineageWork.error,
1054
+ planId: resolvedPlanId,
1055
+ planStepId: resolvedPlanStepId,
1056
+ requirementId: effectiveRequirementId,
1057
+ },
1058
+ });
1059
+ }
1060
+ const newWorkWillBeIsolated = placementConstraint.requireWorktree
1061
+ || placementConstraint.workspaceMode === 'isolated'
1062
+ || input.modeInput.requiresParallelism === true
1063
+ || routeDecision.requiresIsolation === true;
1064
+ const invalidSharedWorkspaceOwner = !newWorkWillBeIsolated
1065
+ ? activeAdmissionSnapshot.invalid.find((candidate) => candidate.isolation === 'shared'
1066
+ && (!candidate.checkoutId || candidate.checkoutId === ctx.checkoutId))
1067
+ : undefined;
1068
+ if (invalidSharedWorkspaceOwner) {
1069
+ return buildFacadeResult({
1070
+ status: 'blocked',
1071
+ 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.`,
1072
+ data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidSharedWorkspaceOwner.workId, invalidWorkError: invalidSharedWorkspaceOwner.error },
1073
+ });
1074
+ }
1075
+ const boundPlanStepWorks = resolvedPlanId && resolvedPlanStepId
1076
+ ? activeWorks.filter((candidate) => candidate.planId === resolvedPlanId && candidate.planStepId === resolvedPlanStepId)
652
1077
  : [];
653
1078
  const explicitPlanStepWork = planStep?.workId ? activeWorks.find((candidate) => candidate.workId === planStep.workId) : undefined;
654
1079
  const planStepWork = explicitPlanStepWork ?? (boundPlanStepWorks.length === 1 ? boundPlanStepWorks[0] : undefined);
655
- const requirementWorks = effectiveRequirementId
656
- ? activeWorks.filter((candidate) => candidate.requirementId === effectiveRequirementId)
657
- : [];
1080
+ // Requirement membership is portfolio ownership, not semantic Work identity.
1081
+ // Only an explicit related Work or the exact bound Plan step may select an
1082
+ // existing Work authority. Siblings under one Requirement remain unrelated
1083
+ // for semantic admission and meet only in placement/resource arbitration.
658
1084
  const deterministicTarget = input.relatedWorkId
659
1085
  ? explicitRelatedWork
660
- : planStepWork ?? (requirementWorks.length === 1 ? requirementWorks[0] : undefined);
1086
+ : planStepWork;
661
1087
  const requestedRelation = input.workRelation ?? (input.modeInput.requiresParallelism === true ? 'parallel' : undefined);
662
1088
  // Only strong semantic bindings participate in ownership resolution. An
663
1089
  // unrelated active Work or a checkout writer is a placement fact, not a
664
1090
  // semantic candidate for continue/extend/parallel/new_goal.
665
1091
  const candidateWorks = [...new Map(
666
- [explicitRelatedWork, planStepWork, ...boundPlanStepWorks, ...requirementWorks]
1092
+ [explicitRelatedWork, planStepWork, ...boundPlanStepWorks]
667
1093
  .filter((candidate): candidate is WorkContract => Boolean(candidate))
668
1094
  .map((candidate) => [candidate.workId, candidate]),
669
1095
  ).values()].slice(0, 8);
@@ -697,18 +1123,42 @@ export function startGoalWorkloop(
697
1123
  });
698
1124
 
699
1125
  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.`);
1126
+ return resolutionRequired(`PLAN_STEP_MULTIPLE_PRIMARY_WORKS: ${resolvedPlanId}/${resolvedPlanStepId} has ${boundPlanStepWorks.length} active primary Work records and requires repair before execution.`);
701
1127
  }
1128
+ if (terminalContinuationSource && requestedRelation === 'continue' && planStepWork) {
1129
+ if (planStepWork.predecessorWorkId !== terminalContinuationSource.workId) {
1130
+ return resolutionRequired(
1131
+ `PLAN_STEP_SUCCESSOR_LINEAGE_CONFLICT: ${resolvedPlanId}/${resolvedPlanStepId} is already owned by ${planStepWork.workId}, but its predecessor is ${planStepWork.predecessorWorkId ?? 'none'} rather than ${terminalContinuationSource.workId}.`,
1132
+ planStepWork,
1133
+ );
1134
+ }
1135
+ return buildFacadeResult({
1136
+ status: 'ok',
1137
+ summary: `PLAN_STEP_REUSES_SUCCESSOR_WORK: ${resolvedPlanId}/${resolvedPlanStepId} already continues terminal ${terminalContinuationSource.workId} as ${planStepWork.workId}.`,
1138
+ data: {
1139
+ executionStarted: false,
1140
+ workContractCreated: false,
1141
+ admissionDecision: 'reuse_existing',
1142
+ resolutionRequired: false,
1143
+ predecessorWorkId: terminalContinuationSource.workId,
1144
+ work: summarizeWorkContract(planStepWork),
1145
+ },
1146
+ evidenceRefs: planStepWork.evidenceRefs,
1147
+ suggestedNextActions: [{ label: 'Continue successor Work', tool: 'rh_work', operation: 'continue', payload: { work_id: planStepWork.workId }, risk: 'readonly', confidence: 'high' }],
1148
+ rawAvailable: false,
1149
+ });
1150
+ }
1151
+
702
1152
  if (planStep?.workId && !explicitPlanStepWork) {
703
1153
  return buildFacadeResult({
704
1154
  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 },
1155
+ summary: `PLAN_STEP_BOUND_WORK_MISSING: ${resolvedPlanId}/${resolvedPlanStepId} is bound to ${planStep.workId}, but that Work is not active. Repair the exact binding; do not create a replacement Work.`,
1156
+ data: { executionStarted: false, workContractCreated: false, planId: resolvedPlanId, planStepId: resolvedPlanStepId, boundWorkId: planStep.workId, repairRequired: true },
707
1157
  suggestedNextActions: [{
708
1158
  label: 'Diagnose exact Plan step binding',
709
1159
  tool: 'rh_work',
710
1160
  operation: 'repair',
711
- payload: { plan_id: input.planId, plan_step_id: input.planStepId, repair_operation: 'diagnose', dry_run: true },
1161
+ payload: { plan_id: resolvedPlanId, plan_step_id: resolvedPlanStepId, repair_operation: 'diagnose', dry_run: true },
712
1162
  risk: 'readonly',
713
1163
  confidence: 'high',
714
1164
  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.',
@@ -728,7 +1178,7 @@ export function startGoalWorkloop(
728
1178
  if (planStepWork && !requestedRelation) {
729
1179
  return buildFacadeResult({
730
1180
  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.`,
1181
+ summary: `PLAN_STEP_REUSES_ACTIVE_WORK: ${resolvedPlanId}/${resolvedPlanStepId} already executes as ${planStepWork.workId}; reuse that Work instead of creating another.`,
732
1182
  data: {
733
1183
  executionStarted: false,
734
1184
  workContractCreated: false,
@@ -742,9 +1192,9 @@ export function startGoalWorkloop(
742
1192
  });
743
1193
  }
744
1194
 
745
- if (requestedRelation === 'continue' || requestedRelation === 'extend') {
1195
+ if ((requestedRelation === 'continue' || requestedRelation === 'extend') && !terminalContinuationSource) {
746
1196
  if (!deterministicTarget) {
747
- return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id or bind the request to an active Plan/Requirement before execution.`);
1197
+ return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id or bind the request to an exact active Plan step before execution.`);
748
1198
  }
749
1199
  if (requestedRelation === 'extend' && deterministicTarget.planId) {
750
1200
  return resolutionRequired(
@@ -793,12 +1243,38 @@ export function startGoalWorkloop(
793
1243
  );
794
1244
  }
795
1245
 
796
- const placementConflict = Boolean(workspaceOwner);
797
- const needsWorktree = executionMode === 'goal_workloop' && (placementConstraint.requireWorktree
798
- || placementConflict
1246
+ // Pure remote effects do not participate in repository workspace ownership.
1247
+ // An unrelated canonical writer, dirty checkout, or repository parallelism must
1248
+ // not manufacture a Git worktree for browser/API work that has no source authority.
1249
+ // Explicit typed isolation remains authoritative for the rare remote effect that
1250
+ // genuinely declares a repository workspace requirement.
1251
+ const repositoryWorkspaceParticipant = resolvedWorkKind !== 'remote_effect';
1252
+ const placementConflict = repositoryWorkspaceParticipant && Boolean(workspaceOwner);
1253
+ const trustedDirtyPaths = ctx.workspaceChangedPaths
1254
+ ? [...new Set(ctx.workspaceChangedPaths.map((path) => path.trim()).filter(Boolean))].sort()
1255
+ : undefined;
1256
+ const dirtyWorkspaceOwnershipConflict = repositoryWorkspaceParticipant
1257
+ && input.modeInput.workspaceDirty === true
1258
+ && (
1259
+ !trustedDirtyPaths
1260
+ || trustedDirtyPaths.length === 0
1261
+ || effectiveAllowedPaths.length === 0
1262
+ || trustedDirtyPaths.some((path) => (
1263
+ effectiveForbiddenPaths.some((pattern) => globMatches(pattern, path))
1264
+ || !effectiveAllowedPaths.some((pattern) => globMatches(pattern, path))
1265
+ ))
1266
+ );
1267
+ const automaticRepositoryIsolation = repositoryWorkspaceParticipant && (
1268
+ placementConflict
1269
+ || dirtyWorkspaceOwnershipConflict
799
1270
  || input.modeInput.requiresParallelism === true
800
- || routeDecision.requiresIsolation === true);
801
- if (!needsWorktree && workspaceOwner) {
1271
+ );
1272
+ const needsWorktree = executionMode === 'goal_workloop' && (
1273
+ placementConstraint.requireWorktree
1274
+ || automaticRepositoryIsolation
1275
+ || routeDecision.requiresIsolation === true
1276
+ );
1277
+ if (!needsWorktree && workspaceOwner && repositoryWorkspaceParticipant) {
802
1278
  return buildFacadeResult({
803
1279
  status: 'blocked',
804
1280
  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.`,
@@ -812,9 +1288,17 @@ export function startGoalWorkloop(
812
1288
  rawAvailable: false,
813
1289
  });
814
1290
  }
815
- const generatedWorkId = workIdFor(input.objective);
816
- if (input.planId || input.planStepId) {
817
- if (!input.planId || !input.planStepId || !ctx.planStore || !ctx.sourceRevision || !plan || !planStep) {
1291
+ const requestedWorkId = input.workId?.trim();
1292
+ if (requestedWorkId && !/^work-[a-z0-9][a-z0-9-]{0,199}$/i.test(requestedWorkId)) {
1293
+ return buildFacadeResult({
1294
+ status: 'blocked',
1295
+ summary: 'WORK_ID_INVALID: explicit Work ids must use the canonical work-<slug> form.',
1296
+ data: { executionStarted: false, workContractCreated: false },
1297
+ });
1298
+ }
1299
+ const generatedWorkId = requestedWorkId ?? workIdFor(effectiveObjective);
1300
+ if (resolvedPlanId || resolvedPlanStepId) {
1301
+ if (!resolvedPlanId || !resolvedPlanStepId || !ctx.planStore || !ctx.sourceRevision || !plan || !planStep) {
818
1302
  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
1303
  }
820
1304
  if (plan.status !== 'approved' && plan.status !== 'executing') {
@@ -822,8 +1306,8 @@ export function startGoalWorkloop(
822
1306
  }
823
1307
  if (plan.sourceRevision !== ctx.sourceRevision) {
824
1308
  const invalidated = claimPlanStepForWork(ctx.planStore, {
825
- planId: input.planId,
826
- stepId: input.planStepId,
1309
+ planId: resolvedPlanId,
1310
+ stepId: resolvedPlanStepId,
827
1311
  workId: generatedWorkId,
828
1312
  sourceRevision: ctx.sourceRevision,
829
1313
  });
@@ -846,46 +1330,48 @@ export function startGoalWorkloop(
846
1330
  normalized.validCheckIds,
847
1331
  normalized.suggestedNextActions,
848
1332
  );
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
1333
  const remoteDeliveryRequired = resolvedWorkKind === 'repository_change'
869
1334
  && input.modeInput.requiresExternalEffect === true
870
1335
  && input.modeInput.remoteWrite === true;
1336
+ const effectiveConstraints: WorkContract['constraints'] = {
1337
+ ...canonicalConstraints,
1338
+ ...(needsWorktree ? { workspaceMode: 'isolated' as const, requireWorktree: true } : {}),
1339
+ ...(remoteDeliveryRequired ? { remoteDeliveryRequired: true } : {}),
1340
+ };
1341
+ const worktreeReason = placementConstraint.requireWorktree || routeDecision.requiresIsolation === true
1342
+ ? 'Typed placement or Route Policy requires isolated execution.'
1343
+ : dirtyWorkspaceOwnershipConflict
1344
+ ? 'Trusted repository observation found dirty paths outside or ambiguous to the Work path fence; isolated placement prevents unrelated changes from entering Work ownership or verification.'
1345
+ : placementConflict
1346
+ ? 'The selected checkout is already owned by another active Work; this Work requires isolated placement.'
1347
+ : repositoryWorkspaceParticipant && input.modeInput.requiresParallelism === true
1348
+ ? 'Parallel Work requires isolated placement.'
1349
+ : 'Current workspace is the stability-first default; isolation remains opt-in.';
1350
+ const forgeInstanceId = ctx.workStore.controllerHome
1351
+ ? readForgeInstanceIdentity(ctx.workStore.controllerHome)?.instanceId
1352
+ : undefined;
871
1353
  const work = createWorkContract(ctx.workStore, {
872
1354
  workId: generatedWorkId,
873
1355
  repoId: ctx.repoId,
874
1356
  checkoutId: needsWorktree ? undefined : ctx.checkoutId,
1357
+ executionPlacement: executionPlacement({
1358
+ ...(forgeInstanceId ? { forgeInstanceId } : {}),
1359
+ repositoryId: ctx.repoId,
1360
+ ...(!needsWorktree && ctx.checkoutId ? { checkoutId: ctx.checkoutId } : {}),
1361
+ }),
875
1362
  principalId: ctx.principalId,
876
1363
  controllerInstanceId: ctx.controllerInstanceId,
877
1364
  baseRevision: ctx.sourceRevision,
1365
+ repositoryBaseState: ctx.sourceBaseState ?? (ctx.sourceRevision ? 'revision' : undefined),
878
1366
  workspaceFingerprint: needsWorktree ? undefined : ctx.workspaceFingerprint,
879
1367
  routeDecisionFingerprint: routeDecision.inputFingerprint,
880
1368
  routeDecision,
881
1369
  mode: executionMode,
882
- objective: input.objective,
1370
+ objective: effectiveObjective,
883
1371
  acceptanceCriteria: effectiveAcceptanceCriteria,
884
- constraints: {
885
- ...canonicalConstraints,
886
- ...(remoteDeliveryRequired ? { remoteDeliveryRequired: true } : {}),
887
- },
888
- risk: workRiskFor(input),
1372
+ constraints: effectiveConstraints,
1373
+ risk: workRisk,
1374
+ engineeringContext,
889
1375
  // Remote delivery is a risk/effect dimension, not a reason to erase a
890
1376
  // repository-change Work's semantic identity. Pure external actions with no
891
1377
  // repository-change signal remain remote_effect and keep plugin receipt semantics.
@@ -895,9 +1381,10 @@ export function startGoalWorkloop(
895
1381
  issueId: input.issueId,
896
1382
  taskId: input.taskId,
897
1383
  requirementId: effectiveRequirementId,
898
- planId: input.planId,
899
- planStepId: input.planStepId,
900
- planSourceRevision: input.planId ? ctx.sourceRevision : undefined,
1384
+ predecessorWorkId: terminalContinuationSource?.workId,
1385
+ planId: resolvedPlanId,
1386
+ planStepId: resolvedPlanStepId,
1387
+ planSourceRevision: resolvedPlanId ? ctx.sourceRevision : undefined,
901
1388
  scopeSummary: input.modeInput.scopeClear ? 'scope declared at start' : 'scope incomplete',
902
1389
  scopeEvidence: {
903
1390
  initialLikelyPaths: [...new Set(input.initialLikelyPaths ?? effectiveAllowedPaths)].slice(0, 100),
@@ -917,9 +1404,7 @@ export function startGoalWorkloop(
917
1404
  },
918
1405
  worktreePolicy: {
919
1406
  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.',
1407
+ reason: worktreeReason,
923
1408
  },
924
1409
  worktreeRef: undefined,
925
1410
  evidencePolicy: {
@@ -938,17 +1423,18 @@ export function startGoalWorkloop(
938
1423
  handoffOnAmbiguity: true,
939
1424
  },
940
1425
  requestedBy: input.requestedBy ?? 'chatgpt',
941
- evidenceRefs: [initialEvidence(input.objective)],
1426
+ requestId: input.requestId?.trim() || undefined,
1427
+ evidenceRefs: [initialEvidence(effectiveObjective)],
942
1428
  policyDecisions: policy ? [policy] : [],
943
1429
  suggestedNextActions: initialSuggestedNextActions,
944
- continuationPrompt: `Continue work ${ctx.repoId}: ${input.objective.slice(0, 200)}`,
1430
+ continuationPrompt: `Continue work ${ctx.repoId}: ${effectiveObjective.slice(0, 200)}`,
945
1431
  });
946
1432
 
947
- if (input.planId && input.planStepId && ctx.planStore && ctx.sourceRevision) {
1433
+ if (resolvedPlanId && resolvedPlanStepId && ctx.planStore && ctx.sourceRevision) {
948
1434
  try {
949
1435
  const claimed = claimPlanStepForWork(ctx.planStore, {
950
- planId: input.planId,
951
- stepId: input.planStepId,
1436
+ planId: resolvedPlanId,
1437
+ stepId: resolvedPlanStepId,
952
1438
  workId: work.workId,
953
1439
  sourceRevision: ctx.sourceRevision,
954
1440
  });
@@ -963,16 +1449,18 @@ export function startGoalWorkloop(
963
1449
  return buildFacadeResult({
964
1450
  status: 'blocked',
965
1451
  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 },
1452
+ data: { planId: resolvedPlanId, planStepId: resolvedPlanStepId, executionStarted: false, workContractCreated: true, work: summarizeWorkContract(work), canonicalWorkRetained: true },
967
1453
  });
968
1454
  }
969
1455
  }
970
1456
 
971
1457
  return buildFacadeResult({
972
1458
  status: 'ok',
973
- summary: executionMode === 'direct_control'
974
- ? `Direct-control Work lineage started as ${work.workId}.`
975
- : `Goal workloop started as ${work.workId}.`,
1459
+ summary: terminalContinuationSource
1460
+ ? `Successor Work ${work.workId} continues semantic lineage from terminal ${terminalContinuationSource.workId}${resolvedPlanId && resolvedPlanStepId ? ` at ${resolvedPlanId}/${resolvedPlanStepId}` : ''}.`
1461
+ : executionMode === 'direct_control'
1462
+ ? `Direct-control Work lineage started as ${work.workId}.`
1463
+ : `Goal workloop started as ${work.workId}.`,
976
1464
  data: {
977
1465
  mode: {
978
1466
  mode: executionMode,
@@ -1026,6 +1514,150 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1026
1514
  });
1027
1515
  }
1028
1516
 
1517
+ if (input.verifiedEngineeringEvidence) {
1518
+ const sourceIdentity = ctx.sourceRevision?.trim()
1519
+ ? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
1520
+ : ctx.sourceBaseState === 'unborn'
1521
+ ? { kind: 'unborn' as const }
1522
+ : { kind: 'unknown' as const };
1523
+ let refreshedEngineeringContext;
1524
+ try {
1525
+ refreshedEngineeringContext = buildEngineeringContextReceipt({
1526
+ risk: work.risk,
1527
+ sourceIdentity,
1528
+ evidence: input.verifiedEngineeringEvidence,
1529
+ recordedAt: nowIso(ctx),
1530
+ });
1531
+ } catch (error) {
1532
+ return buildFacadeResult({
1533
+ status: 'blocked',
1534
+ summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
1535
+ data: { work: summarizeWorkContract(work), engineeringEvidenceUpdated: false },
1536
+ });
1537
+ }
1538
+ const priorDesign = work.engineeringContext?.evidence.designDecisionReceipt;
1539
+ if (work.engineeringContext?.designState === 'revisit_required' && priorDesign) {
1540
+ const nextDesign = refreshedEngineeringContext.evidence.designDecisionReceipt;
1541
+ if (!nextDesign || nextDesign.supersedesReceiptId !== priorDesign.receiptId) {
1542
+ return buildFacadeResult({
1543
+ status: 'blocked',
1544
+ summary: 'ENGINEERING_DESIGN_SUPERSESSION_REQUIRED: same-root-cause re-entry requires a new design receipt that explicitly supersedes the prior design.',
1545
+ data: { work: summarizeWorkContract(work), priorDesignReceiptId: priorDesign.receiptId },
1546
+ });
1547
+ }
1548
+ if (refreshedEngineeringContext.evidence.independentCritiqueReceipt?.decision !== 'approved') {
1549
+ return buildFacadeResult({
1550
+ status: 'blocked',
1551
+ summary: 'ENGINEERING_DESIGN_CRITIQUE_APPROVAL_REQUIRED: same-root-cause re-entry requires an independently approved critique of the superseding design.',
1552
+ data: { work: summarizeWorkContract(work), priorDesignReceiptId: priorDesign.receiptId, nextDesignReceiptId: nextDesign.receiptId },
1553
+ });
1554
+ }
1555
+ }
1556
+ work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: refreshedEngineeringContext });
1557
+ }
1558
+
1559
+ if (work.engineeringContext?.designState === 'revisit_required') {
1560
+ return buildFacadeResult({
1561
+ status: 'blocked',
1562
+ summary: 'ENGINEERING_DESIGN_REVISIT_REQUIRED: same-root-cause design authority must be explicitly superseded and independently approved before further Work mutation.',
1563
+ data: { work: summarizeWorkContract(work) },
1564
+ suggestedNextActions: [{
1565
+ label: 'Refresh design evidence',
1566
+ tool: 'rh_context',
1567
+ operation: 'search',
1568
+ payload: { work_id: work.workId, query: 'Refresh current source and design evidence for this Work before engineering re-entry.' },
1569
+ risk: 'readonly',
1570
+ }],
1571
+ });
1572
+ }
1573
+
1574
+ if (input.engineeringBlocker) {
1575
+ if (!work.engineeringContext || work.engineeringContext.sourceIdentity.kind !== 'revision') {
1576
+ return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_SOURCE_IDENTITY_REQUIRED', data: { work: summarizeWorkContract(work) } });
1577
+ }
1578
+ let blocker;
1579
+ let linkedWork: WorkContract | undefined;
1580
+ try {
1581
+ let linkedWorkId: string | undefined;
1582
+ if (input.engineeringBlocker.classification === 'unrelated') {
1583
+ linkedWorkId = linkedEngineeringBlockerWorkId(work.workId, input.engineeringBlocker.blockerId);
1584
+ linkedWork = getWorkContract(ctx.workStore, linkedWorkId);
1585
+ if (!linkedWork) {
1586
+ const linked = routeWorkStart(ctx, {
1587
+ workId: linkedWorkId,
1588
+ objective: `Resolve unrelated blocker ${input.engineeringBlocker.blockerId}: ${input.engineeringBlocker.rationale}`,
1589
+ acceptanceCriteria: [
1590
+ `Resolve blocker ${input.engineeringBlocker.blockerId} without widening Work ${work.workId}.`,
1591
+ 'Return bounded evidence or a precise wake/blocker condition to the owning Controller.',
1592
+ ],
1593
+ allowedPaths: [],
1594
+ initialLikelyPaths: [],
1595
+ forbiddenPaths: [],
1596
+ checks: [],
1597
+ modeInput: {
1598
+ scopeClear: false,
1599
+ mutation: false,
1600
+ requiresInvestigation: true,
1601
+ requiresRecovery: true,
1602
+ requiresParallelism: true,
1603
+ },
1604
+ requestedBy: 'chatgpt',
1605
+ relatedWorkId: work.workId,
1606
+ workRelation: 'parallel',
1607
+ requirementId: work.requirementId,
1608
+ workKind: 'investigation',
1609
+ constraints: { accessMode: work.constraints.accessMode },
1610
+ });
1611
+ linkedWork = getWorkContract(ctx.workStore, linkedWorkId);
1612
+ if (linked.status !== 'ok' || !linkedWork) {
1613
+ return buildFacadeResult({
1614
+ status: 'blocked',
1615
+ summary: `ENGINEERING_LINKED_WORK_ADMISSION_FAILED: ${linked.summary}`,
1616
+ data: { work: summarizeWorkContract(work), linkedWorkId, linkedWorkCreated: false },
1617
+ });
1618
+ }
1619
+ }
1620
+ }
1621
+ blocker = buildEngineeringBlockerDispositionReceipt({
1622
+ sourceRevision: work.engineeringContext.sourceIdentity.revision,
1623
+ blockerId: input.engineeringBlocker.blockerId,
1624
+ classification: input.engineeringBlocker.classification,
1625
+ semanticScopeKeys: input.engineeringBlocker.semanticScopeKeys ?? work.engineeringContext.semanticScope?.keys ?? [],
1626
+ ...(linkedWork ? { linkedWorkId: linkedWork.workId } : {}),
1627
+ rationale: input.engineeringBlocker.rationale,
1628
+ recordedAt: nowIso(ctx),
1629
+ });
1630
+ const nextEngineering = applyEngineeringBlockerDisposition(work.engineeringContext, blocker);
1631
+ work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: nextEngineering });
1632
+ } catch (error) {
1633
+ return buildFacadeResult({ status: 'blocked', summary: error instanceof Error ? error.message : 'ENGINEERING_BLOCKER_INVALID', data: { work: summarizeWorkContract(work) } });
1634
+ }
1635
+ const returnToDesign = blocker.action === 'return_to_design';
1636
+ return buildFacadeResult({
1637
+ status: 'blocked',
1638
+ summary: returnToDesign
1639
+ ? `Same-root-cause blocker ${blocker.blockerId} requires Product/Design re-entry before further mutation.`
1640
+ : `Unrelated blocker ${blocker.blockerId} is linked to ${blocker.linkedWorkId}; current Work semantic scope is unchanged.`,
1641
+ data: {
1642
+ work: summarizeWorkContract(work),
1643
+ engineeringBlocker: blocker,
1644
+ ...(linkedWork ? { linkedWork: summarizeWorkContract(linkedWork), linkedWorkCreated: true } : {}),
1645
+ nextStep: blocker.action,
1646
+ },
1647
+ suggestedNextActions: returnToDesign
1648
+ ? [{
1649
+ label: 'Refresh design evidence',
1650
+ tool: 'rh_context',
1651
+ operation: 'search',
1652
+ payload: { work_id: work.workId, query: 'Refresh current source and design evidence for this Work before engineering re-entry.' },
1653
+ risk: 'readonly',
1654
+ }]
1655
+ : linkedWork
1656
+ ? [{ label: 'Continue linked Work', tool: 'rh_work', operation: 'continue', payload: { work_id: linkedWork.workId }, risk: 'workspace_write' }]
1657
+ : [],
1658
+ });
1659
+ }
1660
+
1029
1661
  if ((input.reviewFindings?.length ?? 0) > 0 && work.workKind !== 'read_only_review') {
1030
1662
  return buildFacadeResult({
1031
1663
  status: 'blocked',
@@ -1185,8 +1817,11 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1185
1817
  }
1186
1818
  }
1187
1819
 
1188
- const currentCheckRefs = work.checkRefs.filter((record) =>
1189
- verificationRecordAppliesToCurrentWorkspace(record, ctx.sourceRevision, ctx.workspaceFingerprint));
1820
+ const currentCheckRefs = effectiveCurrentWorkVerificationRecords(
1821
+ work,
1822
+ ctx.sourceRevision,
1823
+ ctx.workspaceFingerprint,
1824
+ );
1190
1825
  const history = reconcileVerificationHistory(
1191
1826
  currentCheckRefs.map((record) => ({ checkId: record.checkId, outcome: record.outcome, recordedAt: record.recordedAt })),
1192
1827
  );
@@ -1530,38 +2165,35 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1530
2165
  });
1531
2166
  }
1532
2167
 
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;
2168
+ const changedPaths = normalizeImplementationReviewChangedPaths(ctx.workspaceChangedPaths ?? work.scopeEvidence?.actualChangedPaths ?? []);
2169
+ if (workRequiresImplementationReview(work.workKind, changedPaths)) {
2170
+ recordWorkScopeEvidence(ctx.workStore, work.workId, { actualChangedPaths: changedPaths });
2171
+ transitionWorkContractPhase(ctx.workStore, work.workId, {
2172
+ status: 'running',
2173
+ phase: 'verification',
2174
+ state: 'satisfied',
2175
+ summary: 'Exact Work completion evidence proves the current candidate is verified and eligible for implementation review.',
2176
+ evidenceRefs: work.evidenceRefs,
2177
+ });
2178
+ requestWorkImplementationReview(ctx.workStore, work.workId, 'Implementation and verification evidence are complete; explicit Controller implementation review is required before delivery.');
2179
+ const suggested = validateSuggestedNextActions([implementationReviewSuggestedAction(work.workId)]).actions;
2180
+ const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
2181
+ return buildFacadeResult({
2182
+ status: 'ok',
2183
+ summary: 'Continue: implementation and verification evidence are complete; explicit implementation review is next.',
2184
+ data: { work: summarizeWorkContract(updated), backgroundCompleted: false, nextStep: 'review' },
2185
+ suggestedNextActions: suggested,
2186
+ });
2187
+ }
2188
+ const suggested = validateSuggestedNextActions([{
2189
+ label: 'Finalize work', tool: 'rh_work', operation: 'finalize', payload: { work_id: work.workId }, risk: 'readonly', confidence: 'high',
2190
+ }]).actions;
1543
2191
  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,
2192
+ status: 'running', phase: 'delivery', state: 'active',
2193
+ summary: 'Source-free Work evidence is complete; implementation review is not required and semantic finalization is next.', evidenceRefs: work.evidenceRefs,
1564
2194
  });
2195
+ const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
2196
+ return buildFacadeResult({ status: 'ok', summary: 'Continue: evidence is complete; ready to finalize.', data: { work: summarizeWorkContract(updated), backgroundCompleted: false, nextStep: 'finalize' }, suggestedNextActions: suggested });
1565
2197
  }
1566
2198
 
1567
2199
  export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopVerifyInput): FacadeResult {
@@ -1668,17 +2300,49 @@ export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloop
1668
2300
  const completionEvidence = classified.outcome === 'valid_pass'
1669
2301
  ? evaluateWorkCompletionEvidence(updated, sourceRevision, input.workspaceFingerprint)
1670
2302
  : undefined;
1671
- const validPassReadyToFinalize = completionEvidence?.status === 'complete';
2303
+ const validPassReadyForNextBoundary = completionEvidence?.status === 'complete';
2304
+ const currentChangedPaths = normalizeImplementationReviewChangedPaths(ctx.workspaceChangedPaths ?? updated.scopeEvidence?.actualChangedPaths ?? []);
2305
+ const latestReview = latestImplementationReview(updated.implementationReviews);
2306
+ const approvedReviewRemainsAuthoritative = Boolean(
2307
+ validPassReadyForNextBoundary
2308
+ && updated.phase === 'delivery'
2309
+ && updated.phaseEvidence.review.state === 'satisfied'
2310
+ && latestReview?.decision === 'approved'
2311
+ && sourceRevision
2312
+ && input.workspaceFingerprint
2313
+ && latestReview.sourceRevision === sourceRevision
2314
+ && latestReview.verificationWorkspaceFingerprint === input.workspaceFingerprint
2315
+ && latestReview.changedPathDigest === implementationReviewChangedPathDigest(currentChangedPaths)
2316
+ );
2317
+ const reviewRequiredAfterPass = validPassReadyForNextBoundary
2318
+ && !approvedReviewRemainsAuthoritative
2319
+ && workRequiresImplementationReview(updated.workKind, currentChangedPaths);
2320
+ if (approvedReviewRemainsAuthoritative && updated.evidenceState !== 'valid') {
2321
+ recordWorkEvidenceState(ctx.workStore, updated.workId, 'valid');
2322
+ }
2323
+ if (reviewRequiredAfterPass) {
2324
+ recordWorkScopeEvidence(ctx.workStore, updated.workId, { actualChangedPaths: [...(ctx.workspaceChangedPaths ?? [])] });
2325
+ transitionWorkContractPhase(ctx.workStore, updated.workId, {
2326
+ status: 'running',
2327
+ phase: 'verification',
2328
+ state: 'satisfied',
2329
+ summary: 'All required exact Work verification receipts are current; implementation review admission is now allowed.',
2330
+ evidenceRefs: updated.evidenceRefs,
2331
+ });
2332
+ requestWorkImplementationReview(ctx.workStore, updated.workId, 'Verification is complete; explicit Controller implementation review is required before delivery.');
2333
+ }
1672
2334
  const suggested = validateSuggestedNextActions(
1673
2335
  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
- }]
2336
+ ? [reviewRequiredAfterPass
2337
+ ? implementationReviewSuggestedAction(work.workId)
2338
+ : {
2339
+ label: validPassReadyForNextBoundary ? 'Finalize work' : 'Continue workloop',
2340
+ tool: 'rh_work',
2341
+ operation: validPassReadyForNextBoundary ? 'finalize' : 'continue',
2342
+ payload: { work_id: work.workId },
2343
+ risk: 'readonly',
2344
+ confidence: 'high',
2345
+ }]
1682
2346
  : classified.outcome === 'valid_fail'
1683
2347
  ? [{
1684
2348
  label: 'Continue for review handoff',
@@ -1714,7 +2378,7 @@ export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloop
1714
2378
  },
1715
2379
  backgroundCompleted: false,
1716
2380
  ...(classified.outcome === 'valid_pass'
1717
- ? { nextStep: validPassReadyToFinalize ? 'finalize' : 'continue' }
2381
+ ? { nextStep: reviewRequiredAfterPass ? 'review' : validPassReadyForNextBoundary ? 'finalize' : 'continue' }
1718
2382
  : {}),
1719
2383
  },
1720
2384
  warnings: classified.warnings,
@@ -1723,6 +2387,70 @@ export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloop
1723
2387
  });
1724
2388
  }
1725
2389
 
2390
+ export function reviewGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopReviewInput): FacadeResult {
2391
+ let work = getWorkContract(ctx.workStore, input.workId);
2392
+ if (!work) return buildFacadeResult({ status: 'not_found', summary: `WorkContract ${input.workId} not found.`, data: { workId: input.workId } });
2393
+ if (work.status === 'completed' || work.status === 'cancelled' || work.status === 'failed') {
2394
+ return buildFacadeResult({ status: 'blocked', summary: `WorkContract ${work.workId} is terminal (${work.status}); implementation review was not recorded.`, data: { work: summarizeWorkContract(work) } });
2395
+ }
2396
+ const reviewerPrincipalId = ctx.principalId?.trim() ?? '';
2397
+ if (!reviewerPrincipalId) {
2398
+ return buildFacadeResult({ status: 'blocked', summary: 'WORK_IMPLEMENTATION_REVIEW_REVIEWER_REQUIRED: authenticated reviewer principal is required.', data: { work: summarizeWorkContract(work) } });
2399
+ }
2400
+ if (!input.rationale?.trim()) {
2401
+ return buildFacadeResult({ status: 'blocked', summary: 'WORK_IMPLEMENTATION_REVIEW_RATIONALE_REQUIRED: explicit review rationale is required.', data: { work: summarizeWorkContract(work) } });
2402
+ }
2403
+ try {
2404
+ const candidate = currentImplementationReviewCandidate(ctx, work);
2405
+ recordWorkScopeEvidence(ctx.workStore, work.workId, { actualChangedPaths: [...candidate.changedPaths] });
2406
+ work = getWorkContract(ctx.workStore, work.workId) ?? work;
2407
+ if (work.phase !== 'review') {
2408
+ throw new Error('WORK_IMPLEMENTATION_REVIEW_PHASE_REQUIRED: verify or continue the exact candidate into review before recording a decision.');
2409
+ }
2410
+ const at = nowIso(ctx);
2411
+ const review: WorkImplementationReviewRecord = {
2412
+ schemaVersion: 1,
2413
+ reviewId: `impl-review-${randomUUID().slice(0, 12)}`,
2414
+ workId: work.workId,
2415
+ reviewerPrincipalId,
2416
+ decision: input.decision,
2417
+ rationale: input.rationale.trim().slice(0, 4_000),
2418
+ findings: (input.findings ?? []).slice(0, 100).map((finding) => ({
2419
+ severity: finding.severity,
2420
+ category: finding.category.trim().slice(0, 160),
2421
+ summary: finding.summary.trim().slice(0, 1_000),
2422
+ ...(finding.path?.trim() ? { path: finding.path.trim() } : {}),
2423
+ ...(finding.symbol?.trim() ? { symbol: finding.symbol.trim().slice(0, 240) } : {}),
2424
+ })),
2425
+ sourceRevision: candidate.sourceRevision,
2426
+ workspaceFingerprint: candidate.workspaceFingerprint,
2427
+ verificationWorkspaceFingerprint: candidate.verificationWorkspaceFingerprint,
2428
+ changedPaths: [...candidate.changedPaths],
2429
+ changedPathDigest: implementationReviewChangedPathDigest(candidate.changedPaths),
2430
+ acceptanceCriteriaSummary: implementationReviewAcceptanceSummary(work),
2431
+ verificationEvidence: [...candidate.verificationEvidence],
2432
+ architectureEvidence: [...(candidate.architectureEvidence ?? [])],
2433
+ recordedAt: at,
2434
+ };
2435
+ const updated = recordWorkImplementationReview(ctx.workStore, work.workId, review);
2436
+ const suggested = validateSuggestedNextActions(input.decision === 'approved'
2437
+ ? [{ label: 'Finalize reviewed work', tool: 'rh_work', operation: 'finalize', payload: { work_id: work.workId }, risk: 'workspace_write', confidence: 'high' }]
2438
+ : input.decision === 'changes_required'
2439
+ ? [{ label: 'Continue implementation repairs', tool: 'rh_work', operation: 'continue', payload: { work_id: work.workId }, risk: 'workspace_write', confidence: 'high' }]
2440
+ : [{ label: 'Inspect blocked review evidence', tool: 'rh_context', operation: 'get', payload: { work_id: work.workId }, risk: 'readonly', confidence: 'high' }]).actions;
2441
+ const persisted = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
2442
+ return buildFacadeResult({
2443
+ status: input.decision === 'blocked' ? 'blocked' : 'ok',
2444
+ summary: `Implementation review ${review.reviewId}: ${input.decision}.`,
2445
+ data: { work: summarizeWorkContract(persisted), review, nextStep: input.decision === 'approved' ? 'finalize' : input.decision === 'changes_required' ? 'continue' : 'blocked' },
2446
+ suggestedNextActions: suggested,
2447
+ });
2448
+ } catch (error) {
2449
+ const message = error instanceof Error ? error.message : String(error);
2450
+ return buildFacadeResult({ status: 'blocked', summary: message, data: { work: summarizeWorkContract(work) }, suggestedNextActions: suggestedForWork(work) });
2451
+ }
2452
+ }
2453
+
1726
2454
  export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopFinalizeInput): FacadeResult {
1727
2455
  const work = getWorkContract(ctx.workStore, input.workId);
1728
2456
  if (!work) {
@@ -1764,6 +2492,65 @@ export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1764
2492
  });
1765
2493
  }
1766
2494
 
2495
+ const currentChangedPaths = normalizeImplementationReviewChangedPaths(ctx.workspaceChangedPaths ?? work.scopeEvidence?.actualChangedPaths ?? []);
2496
+ if (workRequiresImplementationReview(work.workKind, currentChangedPaths)) {
2497
+ try {
2498
+ const candidate = currentImplementationReviewCandidate(ctx, work);
2499
+ assertImplementationReviewPreDeliveryBoundary({
2500
+ repoId: work.repoId,
2501
+ workId: work.workId,
2502
+ workKind: work.workKind,
2503
+ reviews: work.implementationReviews,
2504
+ candidate,
2505
+ requiredCheckIds: work.checks,
2506
+ verificationRecords: work.checkRefs,
2507
+ });
2508
+ } catch (error) {
2509
+ const message = error instanceof Error ? error.message : String(error);
2510
+ const reviewDecisionBlocked = /WORK_IMPLEMENTATION_REVIEW_(REQUIRED|STALE|CHANGES_REQUIRED|BLOCKED)/.test(message);
2511
+ if (reviewDecisionBlocked) {
2512
+ transitionWorkContractPhase(ctx.workStore, work.workId, {
2513
+ status: 'running',
2514
+ phase: 'verification',
2515
+ state: 'satisfied',
2516
+ summary: `Current exact verification remains authoritative, but implementation review must be renewed before delivery: ${message}`,
2517
+ evidenceRefs: work.evidenceRefs,
2518
+ });
2519
+ requestWorkImplementationReview(ctx.workStore, work.workId, `Pre-delivery implementation review gate requires a renewed Controller decision: ${message}`);
2520
+ const suggested = validateSuggestedNextActions([implementationReviewSuggestedAction(work.workId)]).actions;
2521
+ const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
2522
+ return buildFacadeResult({
2523
+ status: 'blocked',
2524
+ summary: message,
2525
+ data: { work: summarizeWorkContract(updated), nextStep: 'review' },
2526
+ suggestedNextActions: suggested,
2527
+ });
2528
+ }
2529
+ transitionWorkContractPhase(ctx.workStore, work.workId, {
2530
+ status: 'running',
2531
+ phase: 'verification',
2532
+ state: 'active',
2533
+ summary: `Pre-delivery review could not prove an exact verified candidate: ${message}`,
2534
+ evidenceRefs: work.evidenceRefs,
2535
+ });
2536
+ const suggested = validateSuggestedNextActions([{
2537
+ label: 'Verify exact candidate',
2538
+ tool: 'rh_work',
2539
+ operation: 'verify',
2540
+ payload: { work_id: work.workId, check_id: work.checks[0] },
2541
+ risk: 'workspace_write',
2542
+ confidence: work.checks[0] ? 'high' : 'medium',
2543
+ }], { validCheckIds: work.checks }).actions;
2544
+ const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
2545
+ return buildFacadeResult({
2546
+ status: 'blocked',
2547
+ summary: message,
2548
+ data: { work: summarizeWorkContract(updated), nextStep: 'verify' },
2549
+ suggestedNextActions: suggested,
2550
+ });
2551
+ }
2552
+ }
2553
+
1767
2554
  const completionEvidence = evaluateWorkCompletionEvidence(
1768
2555
  work,
1769
2556
  ctx.sourceRevision,
@@ -1774,11 +2561,9 @@ export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
1774
2561
  const history = completionEvidence.history;
1775
2562
 
1776
2563
  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'}.`,
2564
+ const updated = failWorkContract(ctx.workStore, work.workId, {
2565
+ phase: work.phase,
2566
+ summary: `Work failed acceptance/finalization while in ${work.phase}: ${history.acceptanceFailures.join(', ') || 'forced failure'}.`,
1782
2567
  evidenceRefs: work.evidenceRefs,
1783
2568
  });
1784
2569
  if (updated.planId && updated.planStepId && ctx.planStore) {
@@ -2017,10 +2802,7 @@ export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopSt
2017
2802
  }
2018
2803
 
2019
2804
  const destructiveCleanup = input.authorizeDestructiveCleanup === true;
2020
- transitionWorkContractPhase(ctx.workStore, work.workId, {
2021
- status: 'cancelled',
2022
- phase: 'cleanup',
2023
- state: 'skipped',
2805
+ cancelWorkContract(ctx.workStore, work.workId, {
2024
2806
  summary: input.reason ? `Stopped: ${input.reason}` : 'Work stopped without destructive cleanup.',
2025
2807
  evidenceRefs: work.evidenceRefs,
2026
2808
  });
@@ -2068,15 +2850,22 @@ export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopSt
2068
2850
  });
2069
2851
  }
2070
2852
 
2853
+ export interface GoalWorkloopTrustedInput {
2854
+ /** Runtime-composed source-bound evidence. This channel is intentionally separate from raw MCP/tool arguments. */
2855
+ verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
2856
+ }
2857
+
2071
2858
  export function runGoalWorkloop(
2072
2859
  ctx: GoalWorkloopContext,
2073
2860
  operation: GoalWorkloopOperation,
2074
2861
  args: Record<string, unknown>,
2862
+ trusted: GoalWorkloopTrustedInput = {},
2075
2863
  ): FacadeResult {
2076
2864
  switch (operation) {
2077
2865
  case 'start':
2078
2866
  return routeWorkStart(ctx, {
2079
2867
  objective: String(args.objective ?? ''),
2868
+ workId: typeof args.work_id === 'string' ? args.work_id : undefined,
2080
2869
  acceptanceCriteria: Array.isArray(args.acceptance_criteria) ? args.acceptance_criteria.map(String) : undefined,
2081
2870
  allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
2082
2871
  initialLikelyPaths: Array.isArray(args.initial_likely_paths) ? args.initial_likely_paths.map(String) : undefined,
@@ -2090,7 +2879,7 @@ export function runGoalWorkloop(
2090
2879
  requiresInvestigation: args.requires_investigation === true,
2091
2880
  requiresLongRunningChecks: args.requires_long_running_checks === true,
2092
2881
  requiresParallelism: args.requires_parallelism === true,
2093
- explicitMode: args.mode === 'scale' ? 'scale' : undefined,
2882
+ explicitMode: args.mode === 'plan' || args.mode === 'scale' ? args.mode : undefined,
2094
2883
  needsDependencies: args.needs_dependencies === true,
2095
2884
  requiresRecovery: args.requires_recovery === true,
2096
2885
  requiresWorker: args.requires_worker === true,
@@ -2117,6 +2906,7 @@ export function runGoalWorkloop(
2117
2906
  requirementId: typeof args.requirement_id === 'string' ? args.requirement_id : undefined,
2118
2907
  planId: typeof args.plan_id === 'string' ? args.plan_id : undefined,
2119
2908
  planStepId: typeof args.plan_step_id === 'string' ? args.plan_step_id : undefined,
2909
+ verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
2120
2910
  workKind: args.work_kind === 'repository_change'
2121
2911
  || args.work_kind === 'completed_no_change'
2122
2912
  || args.work_kind === 'read_only_review'
@@ -2127,7 +2917,24 @@ export function runGoalWorkloop(
2127
2917
  ? args.work_kind
2128
2918
  : undefined,
2129
2919
  });
2130
- case 'continue':
2920
+ case 'continue': {
2921
+ let engineeringBlocker: GoalWorkloopContinueInput['engineeringBlocker'];
2922
+ if (args.engineering_blocker !== undefined) {
2923
+ if (!args.engineering_blocker || typeof args.engineering_blocker !== 'object' || Array.isArray(args.engineering_blocker)) {
2924
+ return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_INVALID', data: { workId: String(args.work_id ?? '') } });
2925
+ }
2926
+ const rawBlocker = args.engineering_blocker as Record<string, unknown>;
2927
+ const classification = rawBlocker.classification;
2928
+ if (classification !== 'same_root_cause' && classification !== 'unrelated') {
2929
+ return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_CLASSIFICATION_INVALID', data: { workId: String(args.work_id ?? '') } });
2930
+ }
2931
+ engineeringBlocker = {
2932
+ blockerId: String(rawBlocker.blocker_id ?? ''),
2933
+ classification,
2934
+ rationale: String(rawBlocker.rationale ?? ''),
2935
+ semanticScopeKeys: Array.isArray(rawBlocker.semantic_scope_keys) ? rawBlocker.semantic_scope_keys.map(String) : undefined,
2936
+ };
2937
+ }
2131
2938
  return continueGoalWorkloop(ctx, {
2132
2939
  workId: String(args.work_id ?? ''),
2133
2940
  note: typeof args.note === 'string' ? args.note : undefined,
@@ -2147,7 +2954,10 @@ export function runGoalWorkloop(
2147
2954
  acceptanceFailureDecision: args.acceptance_failure_decision === 'repair' || args.acceptance_failure_decision === 'rescope'
2148
2955
  ? args.acceptance_failure_decision
2149
2956
  : undefined,
2957
+ verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
2958
+ engineeringBlocker,
2150
2959
  });
2960
+ }
2151
2961
  case 'verify':
2152
2962
  return verifyGoalWorkloop(ctx, {
2153
2963
  workId: String(args.work_id ?? ''),
@@ -2156,6 +2966,31 @@ export function runGoalWorkloop(
2156
2966
  checkFailed: args.check_failed === true,
2157
2967
  skipped: args.skipped === true,
2158
2968
  });
2969
+ case 'review': {
2970
+ if (args.review_decision !== 'approved' && args.review_decision !== 'changes_required' && args.review_decision !== 'blocked') {
2971
+ return buildFacadeResult({
2972
+ status: 'blocked',
2973
+ summary: 'WORK_IMPLEMENTATION_REVIEW_DECISION_REQUIRED: operation=review requires an explicit approved, changes_required, or blocked decision.',
2974
+ data: { workId: String(args.work_id ?? '') },
2975
+ });
2976
+ }
2977
+ return reviewGoalWorkloop(ctx, {
2978
+ workId: String(args.work_id ?? ''),
2979
+ decision: args.review_decision,
2980
+ rationale: typeof args.review_rationale === 'string' ? args.review_rationale : '',
2981
+ findings: Array.isArray(args.implementation_review_findings)
2982
+ ? args.implementation_review_findings
2983
+ .filter((value): value is Record<string, unknown> => Boolean(value) && typeof value === 'object')
2984
+ .map((value) => ({
2985
+ severity: value.severity === 'critical' || value.severity === 'high' || value.severity === 'medium' || value.severity === 'low' || value.severity === 'info' ? value.severity : 'info',
2986
+ category: typeof value.category === 'string' ? value.category : '',
2987
+ summary: typeof value.summary === 'string' ? value.summary : '',
2988
+ ...(typeof value.path === 'string' ? { path: value.path } : {}),
2989
+ ...(typeof value.symbol === 'string' ? { symbol: value.symbol } : {}),
2990
+ }))
2991
+ : undefined,
2992
+ });
2993
+ }
2159
2994
  case 'finalize':
2160
2995
  return finalizeGoalWorkloop(ctx, {
2161
2996
  workId: String(args.work_id ?? ''),