@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,7 +1,7 @@
1
1
  import { listControllerChecks } from '../../../cli/controller/check-runner';
2
2
  import type { ControllerCheck } from '../../../cli/controller/check-runner';
3
3
  import type { CompletionReceipt } from '../../../cli/controller/types';
4
- import type { MultiRepositoryMcpToolContext } from '../../../cli/mcp/multi-repository';
4
+ import type { McpExecutionContext } from '../../../../packages/protocols/mcp/execution-context';
5
5
  import { globMatches } from '../../../cli/mcp/paths';
6
6
  import { withControllerLock } from '../../../cli/repositories/locks';
7
7
  import { getRepository, selectRepositoryCheckout, setRepositoryCheckoutLifecycle } from '../../../cli/repositories/registry';
@@ -9,17 +9,28 @@ import { repositoryGitCommit, repositoryGitDeleteBranch, repositoryGitFinishWork
9
9
  import type { RepositoryRecord } from '../../../cli/repositories/types';
10
10
  import { hasCurrentWorkValidationAuthority, markWorkValidationPending, projectWorkValidationOutcome } from './work-validation-reconciler';
11
11
  import { markRepositoryProjectionDirty } from '../../projections/invalidation';
12
- import { withControllerSessionTerminalizationFence } from '../facade/controller-session-store';
12
+ import { withControllerSessionTerminalizationFence } from '../../../../packages/kernel/controller/api/index';
13
13
  import type { VerificationRecord } from '../facade/types';
14
- import { appendVerificationRecord, appendWorkEvidence, listWorkContracts, transitionWorkContractPhase, updateWorkContract } from '../facade/work-contract-store';
14
+ import { appendVerificationRecord, appendWorkEvidence, listWorkContracts, reconcileApprovedWorkImplementationReviewProjection, transitionWorkContractPhase, updateWorkContract } from '../../../../packages/kernel/work/api/index';
15
15
  import { readRepositoryAccessPolicy } from '../governance/access-policy';
16
16
  import { assertResolvedAuthorization, decideAuthorization } from '../governance/authorization';
17
17
  import { updateExecutionSession } from './session-store';
18
- import { validateWorkHandle } from './validation';
18
+ import { validateWorkHandle, WorkHandleValidationError } from './validation';
19
+ import { implementationReviewContentFingerprint } from './implementation-review-content';
20
+ import { transferReviewedWorkAuthorityAcrossContentEquivalentCommit } from './content-equivalent-commit-authority';
21
+ import {
22
+ assertImplementationReviewPreDeliveryBoundary,
23
+ authoritativeImplementationReviewVerificationEvidence,
24
+ latestImplementationReview,
25
+ implementationReviewChangedPathDigest,
26
+ normalizeImplementationReviewChangedPaths,
27
+ workRequiresImplementationReview,
28
+ type ImplementationReviewCandidateIdentity,
29
+ } from '../../../../packages/kernel/work/api/index';
19
30
  import { effectiveVerificationEvidence, verificationInputFingerprint, workspaceValidationFingerprint, workValidationInputFingerprint } from './verification-evidence';
20
31
  import { completeWorkWithReceipt } from './work-completion-authority';
21
- import { markWorkHandleFailed, readWorkHandle, transitionWorkHandle, workDeliveryBaseRevision, writeWorkHandle } from './work-handle-store';
22
- import type { WorkFinalizationStages, WorkHandleState } from './work-handle-store';
32
+ import { adoptWorkHandleSuccessorCandidate, readWorkHandle, resolveWorkDeliveryTargetBranch, transitionWorkHandle, workDeliveryBaseRevision, writeWorkHandle } from './work-handle-store';
33
+ import type { WorkFinalizationFailureCode, WorkFinalizationStages, WorkHandleState } from './work-handle-store';
23
34
  import { findWorkPathScopeViolation } from './work-path-scope';
24
35
  import type { WorkRemoteDeliveryReceipt } from './work-remote-delivery';
25
36
  import { pushExactWorkRemoteDelivery } from './work-remote-delivery';
@@ -28,16 +39,7 @@ import { createHash } from 'crypto';
28
39
  import { existsSync, realpathSync } from 'fs';
29
40
  import { basename, resolve } from 'path';
30
41
  import { assertWorkControllerOwnership, compactHandle, contractFor, gitChangedPaths, gitCommit, gitHead, gitMergeBase, gitRevision, identityFor, reconcileTerminalCleanup, releasePreparedWorkOwnership, requireSession, selectWorkFinalizationTarget, terminalCleanupOutcome, workForSession, workReturnCheckoutId } from './work-execution-support';
31
-
32
- function gitIsAncestor(root: string, ancestor: string, descendant: string): boolean {
33
- const result = spawnSync('git', ['-C', root, 'merge-base', '--is-ancestor', ancestor, descendant], {
34
- encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
35
- });
36
- if (result.error || (result.status !== 0 && result.status !== 1)) {
37
- throw new Error(`WORK_TARGET_ADVANCE_ANCESTRY_UNAVAILABLE: ${ancestor} -> ${descendant}`);
38
- }
39
- return result.status === 0;
40
- }
42
+ import { gitIsAncestor } from './direct-canonical-work-reconciliation';
41
43
 
42
44
  export interface WorkTargetAdvanceInspection {
43
45
  relation: 'candidate_contains_target' | 'target_contains_candidate' | 'diverged_clean' | 'diverged_conflict';
@@ -97,6 +99,69 @@ export function targetAdvanceLinearMergeCommits(root: string, targetRevision: st
97
99
  return output.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
98
100
  }
99
101
 
102
+ export interface FailedNonLinearTargetAdvanceRepairInspection {
103
+ eligible: boolean;
104
+ reason:
105
+ | 'equivalent_linear_repair'
106
+ | 'same_revision'
107
+ | 'target_not_ancestor_of_failed'
108
+ | 'failed_candidate_is_linear'
109
+ | 'target_not_ancestor_of_repair'
110
+ | 'repair_contains_merge'
111
+ | 'tree_mismatch';
112
+ failedHead: string;
113
+ repairedHead: string;
114
+ targetHead: string;
115
+ failedTree: string;
116
+ repairedTree: string;
117
+ failedMergeCommits: string[];
118
+ repairedMergeCommits: string[];
119
+ }
120
+
121
+ function gitTree(root: string, revision: string, label: string): string {
122
+ const result = spawnSync('git', ['-C', root, 'rev-parse', '--verify', `${revision}^{tree}`], {
123
+ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
124
+ });
125
+ if (result.status !== 0 || result.error || typeof result.stdout !== 'string' || !result.stdout.trim()) {
126
+ throw new Error(`WORK_TARGET_ADVANCE_${label}_TREE_UNAVAILABLE: ${revision}`);
127
+ }
128
+ return result.stdout.trim();
129
+ }
130
+
131
+ /**
132
+ * Inspect the only safe history-repair case after target-advance finalization
133
+ * rejected a controller-created merge commit. The repaired candidate may move
134
+ * backwards in commit ancestry only when it is a clean linear representation
135
+ * of the exact same Git tree above the same reconciled target. This proves a
136
+ * history-shape repair, not a source-content rewrite.
137
+ */
138
+ export function inspectFailedNonLinearTargetAdvanceRepair(
139
+ root: string,
140
+ failedRevision: string,
141
+ repairedRevision: string,
142
+ targetRevision: string,
143
+ ): FailedNonLinearTargetAdvanceRepairInspection {
144
+ const failedHead = gitCommit(root, failedRevision, 'FAILED_NON_LINEAR_CANDIDATE');
145
+ const repairedHead = gitCommit(root, repairedRevision, 'REPAIRED_LINEAR_CANDIDATE');
146
+ const targetHead = gitCommit(root, targetRevision, 'REPAIRED_LINEAR_TARGET');
147
+ const failedTree = gitTree(root, failedHead, 'FAILED_NON_LINEAR');
148
+ const repairedTree = gitTree(root, repairedHead, 'REPAIRED_LINEAR');
149
+ const failedMergeCommits = gitIsAncestor(root, targetHead, failedHead)
150
+ ? targetAdvanceLinearMergeCommits(root, targetHead, failedHead)
151
+ : [];
152
+ const repairedMergeCommits = gitIsAncestor(root, targetHead, repairedHead)
153
+ ? targetAdvanceLinearMergeCommits(root, targetHead, repairedHead)
154
+ : [];
155
+ const base = { failedHead, repairedHead, targetHead, failedTree, repairedTree, failedMergeCommits, repairedMergeCommits };
156
+ if (failedHead === repairedHead) return { ...base, eligible: false, reason: 'same_revision' };
157
+ if (!gitIsAncestor(root, targetHead, failedHead)) return { ...base, eligible: false, reason: 'target_not_ancestor_of_failed' };
158
+ if (failedMergeCommits.length === 0) return { ...base, eligible: false, reason: 'failed_candidate_is_linear' };
159
+ if (!gitIsAncestor(root, targetHead, repairedHead)) return { ...base, eligible: false, reason: 'target_not_ancestor_of_repair' };
160
+ if (repairedMergeCommits.length > 0) return { ...base, eligible: false, reason: 'repair_contains_merge' };
161
+ if (failedTree !== repairedTree) return { ...base, eligible: false, reason: 'tree_mismatch' };
162
+ return { ...base, eligible: true, reason: 'equivalent_linear_repair' };
163
+ }
164
+
100
165
  function normalizedReadScope(scope: string): string {
101
166
  return scope.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/, '') || '.';
102
167
  }
@@ -195,7 +260,7 @@ export function targetAdvanceWorkScopeViolation(
195
260
  }
196
261
 
197
262
  function replaceTargetAdvanceScopeEvidence(
198
- ctx: MultiRepositoryMcpToolContext,
263
+ ctx: McpExecutionContext,
199
264
  contract: NonNullable<ReturnType<typeof contractFor>>,
200
265
  actualChangedPaths: string[],
201
266
  ): void {
@@ -283,6 +348,292 @@ function workOwnedDirtyPaths(
283
348
  return { dirtyPaths, ownedPaths };
284
349
  }
285
350
 
351
+
352
+ export function implementationReviewCommittedBaseRevision(
353
+ repository: Pick<RepositoryRecord, 'canonicalRoot' | 'defaultBranch'>,
354
+ handle: Pick<WorkHandleState, 'workId' | 'managedWorktree' | 'deliveryTargetBranch' | 'baseCommit' | 'deliveryBaseCommit'> & Partial<Pick<WorkHandleState, 'state'>>,
355
+ fallbackBaseRevision: string | undefined,
356
+ head: string | undefined,
357
+ explicitTargetBranch?: string,
358
+ ): string | undefined {
359
+ const base = workDeliveryBaseRevision(handle) ?? fallbackBaseRevision;
360
+ if (!base || !head || !handle.managedWorktree) return base;
361
+ const targetBranch = resolveWorkDeliveryTargetBranch(handle, repository.defaultBranch, explicitTargetBranch);
362
+ const targetHead = gitRevision(repository.canonicalRoot, targetBranch);
363
+ if (!targetHead || targetHead === base) return base;
364
+ // Once the delivery target has caught up to the exact candidate HEAD, using
365
+ // that target as the review baseline would erase the Work delta and make an
366
+ // unchanged approved review appear stale. Preserve only the already-durable
367
+ // delivery base in this exact catch-up state; without that durable fence, keep
368
+ // the previous fail-closed behavior below.
369
+ if (
370
+ targetHead === head
371
+ && (
372
+ handle.state === 'merged'
373
+ || (
374
+ handle.deliveryBaseCommit?.trim()
375
+ && handle.deliveryBaseCommit.trim() !== (handle.baseCommit?.trim() || fallbackBaseRevision?.trim())
376
+ )
377
+ )
378
+ ) return base;
379
+ // Review may exclude canonical target-only history only after proving that the
380
+ // recorded delivery base advances linearly to the exact target and that this
381
+ // target is already contained by the managed candidate. This is a read-only
382
+ // review baseline; deliveryBaseCommit remains persisted later by the existing
383
+ // target-advance delivery transition.
384
+ if (!gitIsAncestor(repository.canonicalRoot, base, targetHead)) return base;
385
+ if (!gitIsAncestor(repository.canonicalRoot, targetHead, head)) return base;
386
+ return targetHead;
387
+ }
388
+
389
+ function implementationReviewCommittedChangedPaths(input: {
390
+ repository: Pick<RepositoryRecord, 'canonicalRoot' | 'defaultBranch'>;
391
+ handle: WorkHandleState;
392
+ contract: NonNullable<ReturnType<typeof contractFor>>;
393
+ head: string | undefined;
394
+ explicitTargetBranch?: string;
395
+ review?: NonNullable<ReturnType<typeof latestImplementationReview>>;
396
+ }): string[] {
397
+ const committedBase = implementationReviewCommittedBaseRevision(
398
+ input.repository,
399
+ input.handle,
400
+ input.contract.baseRevision,
401
+ input.head,
402
+ input.explicitTargetBranch,
403
+ );
404
+ if (!input.head || !committedBase) return [];
405
+ const pathsFrom = (base: string) => normalizeImplementationReviewChangedPaths(
406
+ input.head === base ? [] : gitChangedPaths(input.repository.canonicalRoot, base, input.head!),
407
+ );
408
+ const committedPaths = pathsFrom(committedBase);
409
+ if (!input.review) return committedPaths;
410
+
411
+ // Review preparation, pre-delivery finalization, branch-cleanup retry, and
412
+ // cleaned-completion catch-up must share one committed review-baseline
413
+ // authority. The canonical target may legitimately move after review because
414
+ // this candidate was merged (making the current helper base equal HEAD), or
415
+ // it may already have contained the candidate before review. Only select
416
+ // between the current proven helper base and the durable delivery base when
417
+ // one exactly reproduces the immutable reviewed changed-path identity. A real
418
+ // source/path drift therefore still fails closed in the caller.
419
+ const reviewedDigest = implementationReviewChangedPathDigest(
420
+ normalizeImplementationReviewChangedPaths(input.review.changedPaths),
421
+ );
422
+ if (implementationReviewChangedPathDigest(committedPaths) === reviewedDigest) return committedPaths;
423
+
424
+ const recordedBase = workDeliveryBaseRevision(input.handle) ?? input.contract.baseRevision;
425
+ if (recordedBase && recordedBase !== committedBase) {
426
+ const recordedPaths = pathsFrom(recordedBase);
427
+ if (implementationReviewChangedPathDigest(recordedPaths) === reviewedDigest) return recordedPaths;
428
+ }
429
+
430
+ // New reviews intentionally preserve the durable delivery base once target has
431
+ // caught the exact candidate (implementationReviewCommittedBaseRevision). A
432
+ // review already persisted by an older Runtime may, however, legitimately
433
+ // carry the former exact-target-relative empty identity. Recovery may honor
434
+ // that historical identity only when the current canonical target is exactly
435
+ // the immutable reviewed source HEAD and its path digest matches. This branch
436
+ // is review-only compatibility; it can never create a new empty review.
437
+ const targetBranch = resolveWorkDeliveryTargetBranch(
438
+ input.handle,
439
+ input.repository.defaultBranch,
440
+ input.explicitTargetBranch,
441
+ );
442
+ const targetHead = gitRevision(input.repository.canonicalRoot, targetBranch);
443
+ if (targetHead === input.head && input.review.sourceRevision === input.head) {
444
+ const exactTargetPaths = pathsFrom(targetHead);
445
+ if (implementationReviewChangedPathDigest(exactTargetPaths) === reviewedDigest) return exactTargetPaths;
446
+ }
447
+ return committedPaths;
448
+ }
449
+
450
+ function currentWorkReviewChangedPaths(
451
+ repository: RepositoryRecord,
452
+ handle: WorkHandleState,
453
+ contract: NonNullable<ReturnType<typeof contractFor>>,
454
+ explicitTargetBranch?: string,
455
+ ): string[] {
456
+ const status = repositoryGitStatus(repository);
457
+ const { dirtyPaths, ownedPaths } = workOwnedDirtyPaths(contract, status);
458
+ if (dirtyPaths.length !== ownedPaths.length) {
459
+ const unowned = dirtyPaths.filter((path) => !ownedPaths.includes(path));
460
+ throw new Error(`WORK_IMPLEMENTATION_REVIEW_UNOWNED_DIRTY_PATH: ${unowned.join(', ')}`);
461
+ }
462
+ const head = status.head?.trim();
463
+ const committedPaths = implementationReviewCommittedChangedPaths({
464
+ repository, handle, contract, head, explicitTargetBranch,
465
+ });
466
+ const observed = normalizeImplementationReviewChangedPaths([...committedPaths, ...ownedPaths]);
467
+ const scopeViolation = findWorkPathScopeViolation(contract, observed);
468
+ if (scopeViolation) {
469
+ throw new Error(`WORK_IMPLEMENTATION_REVIEW_SCOPE_VIOLATION: ${scopeViolation.kind}:${scopeViolation.path}`);
470
+ }
471
+ return observed;
472
+ }
473
+
474
+ function assertPhysicalImplementationReviewGate(input: {
475
+ ctx: McpExecutionContext;
476
+ repository: RepositoryRecord;
477
+ handle: WorkHandleState;
478
+ contract: NonNullable<ReturnType<typeof contractFor>>;
479
+ /** Use the exact strict verification identity observed before repository staging. */
480
+ verificationWorkspaceFingerprint?: string;
481
+ targetBranch?: string;
482
+ }): ImplementationReviewCandidateIdentity {
483
+ const status = repositoryGitStatus(input.repository);
484
+ const sourceRevision = status.head?.trim();
485
+ if (!sourceRevision) throw new Error('WORK_IMPLEMENTATION_REVIEW_SOURCE_IDENTITY_REQUIRED');
486
+ const changedPaths = currentWorkReviewChangedPaths(input.repository, input.handle, input.contract, input.targetBranch);
487
+ const verificationWorkspaceFingerprint = input.verificationWorkspaceFingerprint?.trim()
488
+ || workspaceValidationFingerprint(input.repository.canonicalRoot, status);
489
+ const workspaceFingerprint = implementationReviewContentFingerprint(input.repository.canonicalRoot, changedPaths);
490
+ const verification = authoritativeImplementationReviewVerificationEvidence({
491
+ repoId: input.contract.repoId,
492
+ workId: input.contract.workId,
493
+ requiredCheckIds: input.contract.checks,
494
+ records: input.contract.checkRefs,
495
+ sourceRevision,
496
+ workspaceFingerprint: verificationWorkspaceFingerprint,
497
+ });
498
+ if (verification.missingCheckIds.length > 0) {
499
+ throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
500
+ }
501
+ const candidate: ImplementationReviewCandidateIdentity = {
502
+ sourceRevision,
503
+ workspaceFingerprint,
504
+ verificationWorkspaceFingerprint,
505
+ changedPaths,
506
+ verificationEvidence: verification.evidence,
507
+ architectureEvidence: [],
508
+ };
509
+ assertImplementationReviewPreDeliveryBoundary({
510
+ repoId: input.contract.repoId,
511
+ workId: input.contract.workId,
512
+ workKind: input.contract.workKind,
513
+ reviews: input.contract.implementationReviews,
514
+ candidate,
515
+ requiredCheckIds: input.contract.checks,
516
+ verificationRecords: input.contract.checkRefs,
517
+ });
518
+ return candidate;
519
+ }
520
+
521
+ /**
522
+ * A successful branch-cleanup retry can run after the reviewed worktree has
523
+ * already been removed. In that state filesystem content cannot be re-hashed,
524
+ * but the Work branch commit is immutable. Re-bind the shared review boundary
525
+ * to that exact branch HEAD and recompute current Work-bound verification
526
+ * evidence before deleting the ref. Cancelled/failed cleanup uses separate
527
+ * recovery authority and never calls this successful-delivery gate.
528
+ */
529
+ function assertPhysicalBranchCleanupImplementationReviewGate(input: {
530
+ target: RepositoryRecord;
531
+ handle: WorkHandleState;
532
+ contract: NonNullable<ReturnType<typeof contractFor>>;
533
+ targetBranch?: string;
534
+ }): void {
535
+ const branchHead = gitRevision(input.target.canonicalRoot, input.handle.branch);
536
+ if (!branchHead) throw new Error('WORK_IMPLEMENTATION_REVIEW_BRANCH_SOURCE_REQUIRED');
537
+ if (input.handle.expectedHead && branchHead !== input.handle.expectedHead) {
538
+ throw new Error('WORK_IMPLEMENTATION_REVIEW_BRANCH_SOURCE_CHANGED');
539
+ }
540
+ const review = latestImplementationReview(input.contract.implementationReviews);
541
+ const changedPaths = implementationReviewCommittedChangedPaths({
542
+ repository: input.target,
543
+ handle: input.handle,
544
+ contract: input.contract,
545
+ head: branchHead,
546
+ explicitTargetBranch: input.targetBranch,
547
+ review,
548
+ });
549
+ if (!workRequiresImplementationReview(input.contract.workKind, changedPaths)) return;
550
+ if (!review) throw new Error('WORK_IMPLEMENTATION_REVIEW_REQUIRED');
551
+ if (review.sourceRevision !== branchHead) throw new Error('WORK_IMPLEMENTATION_REVIEW_STALE: branch source revision changed');
552
+ const verification = authoritativeImplementationReviewVerificationEvidence({
553
+ repoId: input.contract.repoId,
554
+ workId: input.contract.workId,
555
+ requiredCheckIds: input.contract.checks,
556
+ records: input.contract.checkRefs,
557
+ sourceRevision: branchHead,
558
+ workspaceFingerprint: review.verificationWorkspaceFingerprint,
559
+ });
560
+ if (verification.missingCheckIds.length > 0) {
561
+ throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
562
+ }
563
+ assertImplementationReviewPreDeliveryBoundary({
564
+ repoId: input.contract.repoId,
565
+ workId: input.contract.workId,
566
+ workKind: input.contract.workKind,
567
+ reviews: input.contract.implementationReviews,
568
+ candidate: {
569
+ sourceRevision: branchHead,
570
+ workspaceFingerprint: review.workspaceFingerprint,
571
+ verificationWorkspaceFingerprint: review.verificationWorkspaceFingerprint,
572
+ changedPaths,
573
+ verificationEvidence: verification.evidence,
574
+ architectureEvidence: review.architectureEvidence,
575
+ },
576
+ requiredCheckIds: input.contract.checks,
577
+ verificationRecords: input.contract.checkRefs,
578
+ });
579
+ }
580
+
581
+ /**
582
+ * Re-prove review authority from immutable Git + durable review/check identity
583
+ * after successful managed-worktree cleanup. This path never recreates a
584
+ * checkout and never authorizes commit/merge; it only allows a missing semantic
585
+ * completion receipt to catch up with already-durable physical finalization.
586
+ */
587
+ function assertCleanedCompletionImplementationReviewGate(input: {
588
+ target: RepositoryRecord;
589
+ handle: WorkHandleState;
590
+ contract: NonNullable<ReturnType<typeof contractFor>>;
591
+ }): NonNullable<ReturnType<typeof latestImplementationReview>> {
592
+ const deliveryRevision = input.handle.expectedHead ?? input.handle.baseCommit;
593
+ if (!deliveryRevision) throw new Error('WORK_IMPLEMENTATION_REVIEW_SOURCE_IDENTITY_REQUIRED');
594
+ const review = latestImplementationReview(input.contract.implementationReviews);
595
+ if (!review) throw new Error('WORK_IMPLEMENTATION_REVIEW_REQUIRED');
596
+ if (review.sourceRevision !== deliveryRevision) {
597
+ throw new Error('WORK_IMPLEMENTATION_REVIEW_STALE: cleaned delivery source revision changed');
598
+ }
599
+ const changedPaths = implementationReviewCommittedChangedPaths({
600
+ repository: input.target, handle: input.handle, contract: input.contract, head: deliveryRevision, review,
601
+ });
602
+ const reviewedPaths = normalizeImplementationReviewChangedPaths(review.changedPaths);
603
+ if (implementationReviewChangedPathDigest(reviewedPaths) !== implementationReviewChangedPathDigest(changedPaths)) {
604
+ throw new Error('WORK_IMPLEMENTATION_REVIEW_STALE: cleaned delivery changed-path identity changed');
605
+ }
606
+ const verification = authoritativeImplementationReviewVerificationEvidence({
607
+ repoId: input.contract.repoId,
608
+ workId: input.contract.workId,
609
+ requiredCheckIds: input.contract.checks,
610
+ records: input.contract.checkRefs,
611
+ sourceRevision: deliveryRevision,
612
+ workspaceFingerprint: review.verificationWorkspaceFingerprint,
613
+ });
614
+ if (verification.missingCheckIds.length > 0) {
615
+ throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
616
+ }
617
+ assertImplementationReviewPreDeliveryBoundary({
618
+ repoId: input.contract.repoId,
619
+ workId: input.contract.workId,
620
+ workKind: input.contract.workKind,
621
+ reviews: input.contract.implementationReviews,
622
+ candidate: {
623
+ sourceRevision: deliveryRevision,
624
+ workspaceFingerprint: review.workspaceFingerprint,
625
+ verificationWorkspaceFingerprint: review.verificationWorkspaceFingerprint,
626
+ changedPaths,
627
+ verificationEvidence: verification.evidence,
628
+ architectureEvidence: review.architectureEvidence,
629
+ },
630
+ requiredCheckIds: input.contract.checks,
631
+ verificationRecords: input.contract.checkRefs,
632
+ });
633
+ return review;
634
+ }
635
+
636
+
286
637
  export interface TargetDirtyWorkOwnershipInspection {
287
638
  owned: boolean;
288
639
  dirtyPaths: string[];
@@ -338,6 +689,26 @@ export function inspectTargetDirtyWorkOwnership(input: {
338
689
  };
339
690
  }
340
691
 
692
+ export interface TargetDirtyPreservationPlan {
693
+ preserveDirtyTargetPaths: string[];
694
+ ownership: TargetDirtyWorkOwnershipInspection;
695
+ }
696
+
697
+ /**
698
+ * Dirty target content is a Git preservation concern first. Semantic Work
699
+ * ownership remains useful provenance, but it is not required when the
700
+ * integration candidate is disjoint. repositoryGitFinishWorkflow owns the
701
+ * exact overlap fence and fails before target mutation when a preserved path
702
+ * conflicts with the candidate.
703
+ */
704
+ export function planTargetDirtyPreservation(input: Parameters<typeof inspectTargetDirtyWorkOwnership>[0]): TargetDirtyPreservationPlan {
705
+ const ownership = inspectTargetDirtyWorkOwnership(input);
706
+ return {
707
+ preserveDirtyTargetPaths: ownership.dirtyPaths,
708
+ ownership,
709
+ };
710
+ }
711
+
341
712
  export interface DirectCanonicalTargetAdvanceInspection {
342
713
  reconcilable: boolean;
343
714
  reason:
@@ -474,6 +845,169 @@ export function inspectDirectCanonicalTargetAdvanceReconciliation(input: {
474
845
  };
475
846
  }
476
847
 
848
+ export interface DirectCanonicalTargetAdvanceReconciliationCommandResult {
849
+ reconciled: boolean;
850
+ handle: WorkHandleState;
851
+ inspection: DirectCanonicalTargetAdvanceInspection;
852
+ }
853
+
854
+ /**
855
+ * Application command for advancing Direct canonical Work delivery identity.
856
+ * The pure inspector remains the admission authority; this command is the only
857
+ * place shared review/finalization callers persist the approved identity move
858
+ * and its evidence. Gateway callers consume the returned handle and never write
859
+ * lifecycle/evidence records themselves.
860
+ */
861
+ export function reconcileDirectCanonicalTargetAdvanceCommand(input: {
862
+ controllerHome: string;
863
+ repoId: string;
864
+ workId: string;
865
+ handle: WorkHandleState;
866
+ root: string;
867
+ targetBranch: string;
868
+ status: ReturnType<typeof repositoryGitStatus>;
869
+ scope?: { allowedPaths: string[]; forbiddenPaths: string[] };
870
+ checkIds: string[];
871
+ checkRefs: VerificationRecord[];
872
+ persistHandle?: (patch: { deliveryBaseCommit: string; expectedHead: string; failureReason: undefined }) => WorkHandleState;
873
+ evidenceTitle?: string;
874
+ }): DirectCanonicalTargetAdvanceReconciliationCommandResult {
875
+ if (input.handle.repositoryId !== input.repoId) {
876
+ throw new Error(`WORK_DIRECT_TARGET_ADVANCE_REPOSITORY_MISMATCH: expected ${input.handle.repositoryId}, found ${input.repoId}`);
877
+ }
878
+ const boundWorkId = input.handle.workContractId ?? input.handle.workId;
879
+ if (boundWorkId !== input.workId) {
880
+ throw new Error(`WORK_DIRECT_TARGET_ADVANCE_IDENTITY_MISMATCH: expected ${boundWorkId}, found ${input.workId}`);
881
+ }
882
+ const inspection = inspectDirectCanonicalTargetAdvanceReconciliation({
883
+ root: input.root,
884
+ worktreePath: input.handle.worktreePath,
885
+ managedWorktree: input.handle.managedWorktree,
886
+ workBranch: input.handle.branch,
887
+ targetBranch: input.targetBranch,
888
+ expectedRevision: input.handle.expectedHead,
889
+ status: input.status,
890
+ scope: input.scope,
891
+ checkIds: input.checkIds,
892
+ checkRefs: input.checkRefs,
893
+ });
894
+ if (!inspection.reconcilable || !inspection.targetHead) {
895
+ return { reconciled: false, handle: input.handle, inspection };
896
+ }
897
+
898
+ const previousHead = input.handle.expectedHead;
899
+ const patch = {
900
+ deliveryBaseCommit: inspection.targetHead,
901
+ expectedHead: inspection.targetHead,
902
+ failureReason: undefined,
903
+ } as const;
904
+ const handle = input.persistHandle
905
+ ? input.persistHandle(patch)
906
+ : transitionWorkHandle(input.controllerHome, input.handle, input.handle.state, patch);
907
+ appendWorkEvidence({ controllerHome: input.controllerHome, repoId: input.repoId }, input.workId, {
908
+ title: input.evidenceTitle ?? 'direct canonical target advancement reconciled',
909
+ summary: `Canonical ${input.targetBranch} advanced linearly ${previousHead} -> ${inspection.targetHead}. Delivery identity adopted only the target revision after proving all ${inspection.dirtyPaths.length} dirty path(s) are Work-owned, ${inspection.targetChangedPaths.length} target-only path(s) are disjoint, and fresh exact verification bound [${inspection.freshCheckIds.join(', ')}] to source ${inspection.targetHead} plus workspace ${inspection.workspaceFingerprint?.slice(0, 16) ?? 'unknown'}.`,
910
+ detailLevel: 'summary',
911
+ });
912
+ return { reconciled: true, handle, inspection };
913
+ }
914
+
915
+ export interface ManagedWorkSuccessorAdoptionInspection {
916
+ adoptable: boolean;
917
+ reason:
918
+ | 'not_managed_worktree'
919
+ | 'path_mismatch'
920
+ | 'branch_mismatch'
921
+ | 'workspace_dirty'
922
+ | 'revision_unavailable'
923
+ | 'no_head_change'
924
+ | 'descendant_progress'
925
+ | 'target_not_advanced'
926
+ | 'target_history_rewritten'
927
+ | 'target_not_candidate_ancestor'
928
+ | 'non_linear_candidate'
929
+ | 'scope_violation'
930
+ | 'adoptable';
931
+ previousHead?: string;
932
+ previousDeliveryBase?: string;
933
+ candidateHead?: string;
934
+ targetHead?: string;
935
+ candidateChangedPaths: string[];
936
+ detail?: string;
937
+ }
938
+
939
+ /**
940
+ * Inspect a manually conflict-repaired managed Work candidate before changing
941
+ * WorkHandle authority. This is intentionally narrower than ordinary
942
+ * descendant progress: canonical target must have advanced linearly from the
943
+ * recorded delivery base, and the rewritten clean candidate must contain that
944
+ * exact target with only Work-scoped target-relative changes.
945
+ */
946
+ export function inspectManagedWorkSuccessorAdoption(input: {
947
+ root: string;
948
+ worktreePath: string;
949
+ managedWorktree: boolean;
950
+ workBranch: string;
951
+ targetBranch: string;
952
+ expectedRevision?: string;
953
+ deliveryBaseRevision?: string;
954
+ status: ReturnType<typeof repositoryGitStatus>;
955
+ scope?: { allowedPaths: string[]; forbiddenPaths: string[] };
956
+ }): ManagedWorkSuccessorAdoptionInspection {
957
+ const empty = (reason: ManagedWorkSuccessorAdoptionInspection['reason'], detail?: string): ManagedWorkSuccessorAdoptionInspection => ({
958
+ adoptable: false,
959
+ reason,
960
+ candidateChangedPaths: [],
961
+ ...(detail ? { detail } : {}),
962
+ });
963
+ if (!input.managedWorktree) return empty('not_managed_worktree');
964
+ try {
965
+ if (realpathSync(input.root) !== realpathSync(input.worktreePath)) return empty('path_mismatch');
966
+ } catch {
967
+ return empty('path_mismatch');
968
+ }
969
+ const branch = spawnSync('git', ['-C', input.root, 'branch', '--show-current'], {
970
+ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
971
+ });
972
+ if (branch.status !== 0 || String(branch.stdout ?? '').trim() !== input.workBranch) return empty('branch_mismatch');
973
+ if (!input.status.clean) return empty('workspace_dirty');
974
+
975
+ const previousHead = input.expectedRevision ? gitRevision(input.root, input.expectedRevision) : undefined;
976
+ const previousDeliveryBase = input.deliveryBaseRevision ? gitRevision(input.root, input.deliveryBaseRevision) : undefined;
977
+ const candidateHead = input.status.head ? gitRevision(input.root, input.status.head) : undefined;
978
+ const targetHead = gitRevision(input.root, input.targetBranch);
979
+ if (!previousHead || !previousDeliveryBase || !candidateHead || !targetHead) return empty('revision_unavailable');
980
+ const base = { previousHead, previousDeliveryBase, candidateHead, targetHead, candidateChangedPaths: [] as string[] };
981
+ if (candidateHead === previousHead) return { ...base, adoptable: false, reason: 'no_head_change' };
982
+ if (gitIsAncestor(input.root, previousHead, candidateHead)) return { ...base, adoptable: false, reason: 'descendant_progress' };
983
+ if (targetHead === previousDeliveryBase) return { ...base, adoptable: false, reason: 'target_not_advanced' };
984
+ if (!gitIsAncestor(input.root, previousDeliveryBase, targetHead)) {
985
+ return { ...base, adoptable: false, reason: 'target_history_rewritten' };
986
+ }
987
+ if (!gitIsAncestor(input.root, targetHead, candidateHead)) {
988
+ return { ...base, adoptable: false, reason: 'target_not_candidate_ancestor' };
989
+ }
990
+ const mergeCommits = targetAdvanceLinearMergeCommits(input.root, targetHead, candidateHead);
991
+ if (mergeCommits.length > 0) {
992
+ return { ...base, adoptable: false, reason: 'non_linear_candidate', detail: mergeCommits.slice(0, 8).join(', ') };
993
+ }
994
+ const candidateChangedPaths = gitChangedPaths(input.root, targetHead, candidateHead);
995
+ if (!input.scope) {
996
+ return { ...base, candidateChangedPaths, adoptable: false, reason: 'scope_violation', detail: 'WorkContract scope unavailable' };
997
+ }
998
+ const scopeViolation = findWorkPathScopeViolation(input.scope, candidateChangedPaths);
999
+ if (scopeViolation) {
1000
+ return {
1001
+ ...base,
1002
+ candidateChangedPaths,
1003
+ adoptable: false,
1004
+ reason: 'scope_violation',
1005
+ detail: `${scopeViolation.kind}:${scopeViolation.path}`,
1006
+ };
1007
+ }
1008
+ return { ...base, candidateChangedPaths, adoptable: true, reason: 'adoptable' };
1009
+ }
1010
+
477
1011
  export function completionReceiptChangedPaths(repoRoot: string, baseRevision: string, deliveryRevision: string): string[] {
478
1012
  const result = spawnSync('git', ['-C', repoRoot, 'diff', '--name-only', `${baseRevision}..${deliveryRevision}`], {
479
1013
  encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
@@ -486,16 +1020,18 @@ export function completionReceiptChangedPaths(repoRoot: string, baseRevision: st
486
1020
  }
487
1021
 
488
1022
  function completionReceiptForFinalizedWork(
489
- ctx: MultiRepositoryMcpToolContext,
1023
+ ctx: McpExecutionContext,
490
1024
  handle: WorkHandleState,
491
1025
  contract: ReturnType<typeof contractFor>,
492
1026
  args: Record<string, unknown>,
493
1027
  ): CompletionReceipt {
494
1028
  const repository = getRepository(handle.repositoryId, ctx.controllerHome, { includeRemoved: true });
495
1029
  const target = selectWorkFinalizationTarget(repository, handle);
496
- const targetBranch = typeof args.target_branch === 'string' && args.target_branch.trim()
497
- ? args.target_branch.trim()
498
- : repository.defaultBranch || 'main';
1030
+ const targetBranch = resolveWorkDeliveryTargetBranch(
1031
+ handle,
1032
+ repository.defaultBranch,
1033
+ typeof args.target_branch === 'string' ? args.target_branch : undefined,
1034
+ );
499
1035
  const targetRevision = gitRevision(target.canonicalRoot, targetBranch);
500
1036
  if (!targetRevision) throw new Error(`WORK_COMPLETION_RECEIPT_TARGET_REQUIRED: target branch ${targetBranch} is unavailable`);
501
1037
  const noChange = args.completion_outcome === 'completed_no_change';
@@ -577,7 +1113,7 @@ function runCleanup(targetRoot: string, worktreePath: string): { ok: boolean; me
577
1113
  * crash without turning a missing checkout into proof of safety.
578
1114
  */
579
1115
  export function inspectCleanupOnlyMergedHead(
580
- ctx: MultiRepositoryMcpToolContext,
1116
+ ctx: McpExecutionContext,
581
1117
  current: WorkHandleState,
582
1118
  args: Record<string, unknown>,
583
1119
  ): { currentHead: string; cancelledContract: boolean; worktreeMissing: boolean } | undefined {
@@ -587,9 +1123,11 @@ export function inspectCleanupOnlyMergedHead(
587
1123
 
588
1124
  const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
589
1125
  const target = selectWorkFinalizationTarget(repository, current);
590
- const targetBranch = typeof args.target_branch === 'string' && args.target_branch.trim()
591
- ? args.target_branch.trim()
592
- : repository.defaultBranch || 'main';
1126
+ const targetBranch = resolveWorkDeliveryTargetBranch(
1127
+ current,
1128
+ repository.defaultBranch,
1129
+ typeof args.target_branch === 'string' ? args.target_branch : undefined,
1130
+ );
593
1131
  const branchHeadResult = spawnSync('git', ['-C', target.canonicalRoot, 'rev-parse', '--verify', `refs/heads/${current.branch}`], {
594
1132
  encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
595
1133
  });
@@ -624,7 +1162,11 @@ export function inspectCleanupOnlyMergedHead(
624
1162
  encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
625
1163
  });
626
1164
  if (currentBranch.status !== 0 || String(currentBranch.stdout ?? '').trim() !== current.branch) return undefined;
627
- if (current.expectedHead && currentHead !== current.expectedHead && cancelledContract) return undefined;
1165
+ // Cleanup reconciliation proves the exact Work-owned revision, regardless of
1166
+ // terminal contract status. A same-named branch may be advanced later by a
1167
+ // different controller or manual Git operation; reachability from the target
1168
+ // is not authority to adopt that newer HEAD as this Work's delivery.
1169
+ if (current.expectedHead && currentHead !== current.expectedHead) return undefined;
628
1170
  return { currentHead, cancelledContract, worktreeMissing: false };
629
1171
  }
630
1172
 
@@ -640,7 +1182,7 @@ interface FailedCleanupProof {
640
1182
  * cleanup only; it never proves successful verification or delivery.
641
1183
  */
642
1184
  function failedCleanupOnlyHead(
643
- ctx: MultiRepositoryMcpToolContext,
1185
+ ctx: McpExecutionContext,
644
1186
  current: WorkHandleState,
645
1187
  args: Record<string, unknown>,
646
1188
  ): FailedCleanupProof | undefined {
@@ -658,9 +1200,11 @@ function failedCleanupOnlyHead(
658
1200
  if (!expectedHead) return undefined;
659
1201
  const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
660
1202
  const target = selectWorkFinalizationTarget(repository, current);
661
- const targetBranch = typeof args.target_branch === 'string' && args.target_branch.trim()
662
- ? args.target_branch.trim()
663
- : repository.defaultBranch || 'main';
1203
+ const targetBranch = resolveWorkDeliveryTargetBranch(
1204
+ current,
1205
+ repository.defaultBranch,
1206
+ typeof args.target_branch === 'string' ? args.target_branch : undefined,
1207
+ );
664
1208
  const merged = spawnSync('git', ['-C', target.canonicalRoot, 'merge-base', '--is-ancestor', expectedHead, targetBranch], {
665
1209
  encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
666
1210
  });
@@ -708,6 +1252,98 @@ function retryHandleStateForFinalization(stages: WorkFinalizationStages): WorkHa
708
1252
  return 'validating';
709
1253
  }
710
1254
 
1255
+ function reconcileFailedNonLinearTargetAdvanceRepair(
1256
+ ctx: McpExecutionContext,
1257
+ current: WorkHandleState,
1258
+ args: Record<string, unknown>,
1259
+ ): WorkHandleState {
1260
+ if (
1261
+ current.state !== 'failed'
1262
+ || current.finalization.merge !== 'failed'
1263
+ || current.finalization.failureCode !== 'WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION'
1264
+ || args.merge !== true
1265
+ || !current.managedWorktree
1266
+ || !current.expectedHead
1267
+ ) return current;
1268
+
1269
+ const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
1270
+ const worktree = selectRepositoryCheckout(repository, current.checkoutId);
1271
+ const status = repositoryGitStatus(worktree);
1272
+ const repairedHead = status.head;
1273
+ if (!status.clean || !repairedHead || repairedHead === current.expectedHead) return current;
1274
+ const branch = spawnSync('git', ['-C', worktree.canonicalRoot, 'branch', '--show-current'], {
1275
+ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
1276
+ });
1277
+ if (branch.status !== 0 || String(branch.stdout ?? '').trim() !== current.branch) {
1278
+ throw new Error('WORK_TARGET_ADVANCE_REPAIR_BRANCH_MISMATCH');
1279
+ }
1280
+
1281
+ const targetRevision = workDeliveryBaseRevision(current);
1282
+ if (!targetRevision) throw new Error('WORK_TARGET_ADVANCE_REPAIR_TARGET_MISSING');
1283
+ const inspection = inspectFailedNonLinearTargetAdvanceRepair(
1284
+ worktree.canonicalRoot,
1285
+ current.expectedHead,
1286
+ repairedHead,
1287
+ targetRevision,
1288
+ );
1289
+ if (!inspection.eligible) {
1290
+ throw new Error(`WORK_TARGET_ADVANCE_REPAIR_UNSAFE: ${inspection.reason}`);
1291
+ }
1292
+
1293
+ const contract = contractFor(ctx, current);
1294
+ if (!contract) throw new Error(`WORK_TARGET_ADVANCE_REPAIR_CONTRACT_MISSING: ${current.workContractId ?? current.workId}`);
1295
+ const changedPaths = gitChangedPaths(worktree.canonicalRoot, inspection.targetHead, inspection.repairedHead);
1296
+ const scopeViolation = targetAdvanceWorkScopeViolation(contract, changedPaths);
1297
+ if (scopeViolation) {
1298
+ throw new Error(`WORK_TARGET_ADVANCE_REPAIR_SCOPE_VIOLATION: Work-owned ${scopeViolation.kind} path ${scopeViolation.path}`);
1299
+ }
1300
+
1301
+ const repaired = withControllerLock(
1302
+ ctx.controllerHome,
1303
+ { scope: 'worktree', repoId: current.repositoryId, worktreeId: current.checkoutId },
1304
+ `work-finalize:${current.workId}:repair-non-linear-target-advance`,
1305
+ () => {
1306
+ const fresh = readWorkHandle(ctx.controllerHome, current.repositoryId, current.workId) ?? current;
1307
+ if (fresh.state !== 'failed' || fresh.expectedHead !== inspection.failedHead || fresh.finalization.merge !== 'failed') {
1308
+ throw new Error('WORK_TARGET_ADVANCE_REPAIR_STALE_HANDLE');
1309
+ }
1310
+ return transitionWorkHandle(ctx.controllerHome, fresh, 'validating', {
1311
+ expectedHead: inspection.repairedHead,
1312
+ deliveryBaseCommit: inspection.targetHead,
1313
+ failureReason: undefined,
1314
+ validationRun: undefined,
1315
+ validatedInputFingerprint: undefined,
1316
+ finalization: {
1317
+ ...fresh.finalization,
1318
+ validation: 'pending',
1319
+ commit: fresh.finalization.commit === 'failed' ? 'pending' : fresh.finalization.commit,
1320
+ merge: 'pending',
1321
+ failureCode: undefined, lastError: undefined,
1322
+ },
1323
+ });
1324
+ },
1325
+ 10_000,
1326
+ );
1327
+ replaceTargetAdvanceScopeEvidence(ctx, contract, changedPaths);
1328
+ transitionWorkContractPhase(
1329
+ { controllerHome: ctx.controllerHome, repoId: current.repositoryId },
1330
+ contract.workId,
1331
+ {
1332
+ phase: 'delivery',
1333
+ status: 'running',
1334
+ state: 'active',
1335
+ summary: `Repaired non-linear target-advance history ${inspection.failedHead} -> ${inspection.repairedHead} with identical Git tree; exact validation is required again before delivery.`,
1336
+ },
1337
+ );
1338
+ appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, contract.workId, {
1339
+ title: 'non-linear target-advance candidate repaired',
1340
+ summary: `Replaced failed merge candidate ${inspection.failedHead} with linear candidate ${inspection.repairedHead} above target ${inspection.targetHead} only after exact Git-tree equality, zero repaired merge commits, clean exact Work checkout/branch ownership, and Work path-scope verification. Prior validation identity was invalidated and must be re-established.`,
1341
+ detailLevel: 'summary',
1342
+ });
1343
+ markRepositoryProjectionDirty(ctx.controllerHome, current.repositoryId, `finalize:${current.workId}:non-linear-target-repair`);
1344
+ return repaired;
1345
+ }
1346
+
711
1347
  export function resetFinalizationStagesForRequest(
712
1348
  stages: WorkFinalizationStages,
713
1349
  wants: { commit: boolean; merge: boolean; cleanup: boolean },
@@ -761,6 +1397,48 @@ export function resetFinalizationStagesForRequest(
761
1397
  return next;
762
1398
  }
763
1399
 
1400
+ function completeFinalizedWorkContract(input: {
1401
+ ctx: McpExecutionContext;
1402
+ handle: WorkHandleState;
1403
+ contract: NonNullable<ReturnType<typeof contractFor>>;
1404
+ args: Record<string, unknown>;
1405
+ terminalizationOwner: ReturnType<typeof assertWorkControllerOwnership>;
1406
+ outcome: 'completed_changed' | 'completed_no_change';
1407
+ }): CompletionReceipt {
1408
+ const receipt = completionReceiptForFinalizedWork(input.ctx, input.handle, input.contract, input.args);
1409
+ const ownerPrincipal = input.terminalizationOwner.principalId?.trim() || input.terminalizationOwner.controllerId;
1410
+ const ownerInstanceId = input.terminalizationOwner.controllerInstanceId?.trim() || '';
1411
+ const ownerClaimGeneration = input.terminalizationOwner.claimGeneration;
1412
+ if (!ownerInstanceId || typeof ownerClaimGeneration !== 'number' || ownerClaimGeneration < 1) {
1413
+ throw new Error(`WORK_CONTROLLER_TERMINALIZATION_AUTHORITY_INVALID: ${input.contract.workId}`);
1414
+ }
1415
+ const fencedCompletion = withControllerSessionTerminalizationFence(
1416
+ { controllerHome: input.ctx.controllerHome, repoId: input.handle.repositoryId },
1417
+ {
1418
+ workId: input.contract.workId,
1419
+ actor: `work-finalize-completion:${input.terminalizationOwner.controllerId}:${ownerInstanceId}`,
1420
+ authority: {
1421
+ controllerId: input.terminalizationOwner.controllerId,
1422
+ controllerType: input.terminalizationOwner.controllerType,
1423
+ principalId: ownerPrincipal,
1424
+ controllerInstanceId: ownerInstanceId,
1425
+ claimGeneration: ownerClaimGeneration,
1426
+ },
1427
+ },
1428
+ () => completeWorkWithReceipt(
1429
+ { controllerHome: input.ctx.controllerHome, repoId: input.handle.repositoryId },
1430
+ input.contract.workId,
1431
+ receipt,
1432
+ input.outcome,
1433
+ input.outcome === 'completed_changed' ? 'repository_change' : input.contract.workKind,
1434
+ ),
1435
+ );
1436
+ if (!fencedCompletion.allowed) {
1437
+ throw new Error(`WORK_TERMINALIZATION_AUTHORITY_FENCED: ${input.contract.workId}:${fencedCompletion.reason}`);
1438
+ }
1439
+ return receipt;
1440
+ }
1441
+
764
1442
  function finalizationComplete(stages: WorkFinalizationStages): boolean {
765
1443
  return stages.validation === 'done'
766
1444
  && stages.commit !== 'pending'
@@ -797,17 +1475,15 @@ function currentWorkValidationInput(
797
1475
  };
798
1476
  }
799
1477
 
800
- export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Record<string, unknown>): Promise<Record<string, unknown>> {
1478
+ export async function finalizeWork(ctx: McpExecutionContext, args: Record<string, unknown>): Promise<Record<string, unknown>> {
801
1479
  const session = requireSession(ctx, args);
802
1480
  let current = workForSession(ctx, session, args, { allowClaimedTerminalCleanup: args.cleanup !== false });
803
1481
  const requestedWants = { commit: args.commit === true, merge: args.merge === true, cleanup: args.cleanup === true };
804
- const retryStage = current.state === 'failed'
805
- ? requestedFailedFinalizationRetry(current.finalization, requestedWants)
806
- : undefined;
1482
+ let retryStage = requestedFailedFinalizationRetry(current.finalization, requestedWants);
807
1483
  const retryContract = retryStage ? contractFor(ctx, current) : undefined;
808
1484
  if (
809
1485
  retryStage
810
- && retryContract?.status === 'failed'
1486
+ && (retryContract?.status === 'failed' || retryContract?.status === 'blocked')
811
1487
  && !retryContract.completionOutcome
812
1488
  && !retryContract.completionReceipt
813
1489
  ) {
@@ -827,6 +1503,8 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
827
1503
  return await reconcileTerminalCleanup(ctx, session, current, args, terminalOutcome);
828
1504
  }
829
1505
  const terminalizationOwner = assertWorkControllerOwnership(ctx, session, current, args);
1506
+ current = reconcileFailedNonLinearTargetAdvanceRepair(ctx, current, args);
1507
+ if (retryStage && current.finalization[retryStage] !== 'failed') retryStage = undefined;
830
1508
  if (terminalOutcome && args.cleanup === false) {
831
1509
  const recordedAt = new Date().toISOString();
832
1510
  current = withControllerLock(
@@ -844,6 +1522,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
844
1522
  merge: fresh.finalization.merge === 'pending' ? 'skipped' : fresh.finalization.merge,
845
1523
  branchCleanup: 'skipped',
846
1524
  worktreeCleanup: 'skipped',
1525
+ failureCode: fresh.finalization.validation === 'failed' ? fresh.finalization.failureCode : undefined,
847
1526
  lastError: fresh.finalization.validation === 'failed' ? fresh.finalization.lastError : undefined,
848
1527
  },
849
1528
  terminalResourceDisposition: {
@@ -901,6 +1580,53 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
901
1580
  failurePreserved: true,
902
1581
  };
903
1582
  }
1583
+ if (terminalContract?.status === 'completed' && terminalContract.completionReceipt) {
1584
+ updateExecutionSession(ctx.controllerHome, identityFor(ctx, args), {
1585
+ activeWorkId: undefined,
1586
+ activeCheckoutId: workReturnCheckoutId(ctx, current, session.activeCheckoutId),
1587
+ });
1588
+ return { idempotent: true, work: compactHandle(current), stages: current.finalization, completed: true };
1589
+ }
1590
+ const cleanedCompletionRecovery = finalizationComplete(current.finalization)
1591
+ && args.cleanup === true
1592
+ && args.commit !== true
1593
+ && args.merge !== true
1594
+ && args.completion_outcome === 'completed_changed'
1595
+ && terminalContract
1596
+ && terminalContract.status !== 'cancelled'
1597
+ && terminalContract.status !== 'failed'
1598
+ && !terminalContract.completionReceipt;
1599
+ if (cleanedCompletionRecovery && terminalContract) {
1600
+ const terminalizationOwner = assertWorkControllerOwnership(ctx, session, current, args);
1601
+ const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
1602
+ const target = selectWorkFinalizationTarget(repository, current);
1603
+ const durableReview = assertCleanedCompletionImplementationReviewGate({ target, handle: current, contract: terminalContract });
1604
+ const reconciledContract = reconcileApprovedWorkImplementationReviewProjection(
1605
+ { controllerHome: ctx.controllerHome, repoId: current.repositoryId },
1606
+ terminalContract.workId,
1607
+ {
1608
+ reviewId: durableReview.reviewId,
1609
+ sourceRevision: durableReview.sourceRevision,
1610
+ changedPathDigest: durableReview.changedPathDigest,
1611
+ },
1612
+ );
1613
+ completeFinalizedWorkContract({
1614
+ ctx, handle: current, contract: reconciledContract, args, terminalizationOwner, outcome: 'completed_changed',
1615
+ });
1616
+ releasePreparedWorkOwnership(ctx, current);
1617
+ updateExecutionSession(ctx.controllerHome, identityFor(ctx, args), {
1618
+ activeWorkId: undefined,
1619
+ activeCheckoutId: workReturnCheckoutId(ctx, current, session.activeCheckoutId),
1620
+ });
1621
+ return {
1622
+ idempotent: true,
1623
+ recoveredCompletionReceipt: true,
1624
+ work: compactHandle(current),
1625
+ stages: current.finalization,
1626
+ completed: true,
1627
+ cleanupCompleted: true,
1628
+ };
1629
+ }
904
1630
  validateWorkHandle(ctx.controllerHome, current, identityFor(ctx, args), 'none', 'finalize');
905
1631
  return { idempotent: true, work: compactHandle(current) };
906
1632
  }
@@ -931,14 +1657,36 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
931
1657
  return update(fresh);
932
1658
  }, 10_000);
933
1659
 
934
- const failStage = (stage: keyof WorkFinalizationStages, reason: string): Record<string, unknown> => {
1660
+ const failStage = (
1661
+ stage: keyof WorkFinalizationStages,
1662
+ reason: string,
1663
+ failureCode?: WorkFinalizationFailureCode,
1664
+ ): Record<string, unknown> => {
935
1665
  current = transact(`fail:${String(stage)}`, (fresh) => {
936
- const finalization = { ...fresh.finalization, [stage]: 'failed', lastError: reason } as WorkFinalizationStages;
937
- return markWorkHandleFailed(ctx.controllerHome, { ...fresh, finalization }, reason);
1666
+ const finalization = { ...fresh.finalization, [stage]: 'failed', failureCode, lastError: reason } as WorkFinalizationStages;
1667
+ const retryableState = stage === 'validation'
1668
+ ? 'validating'
1669
+ : retryHandleStateForFinalization(finalization);
1670
+ return transitionWorkHandle(ctx.controllerHome, fresh, retryableState, {
1671
+ failureReason: reason,
1672
+ finalization,
1673
+ ...(stage === 'validation' ? { validationRun: undefined, validatedInputFingerprint: undefined } : {}),
1674
+ });
938
1675
  });
939
1676
  if (current.workContractId) {
940
1677
  if (stage === 'validation') {
941
- updateWorkContract({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId, { status: 'failed' });
1678
+ transitionWorkContractPhase(
1679
+ { controllerHome: ctx.controllerHome, repoId: current.repositoryId },
1680
+ current.workContractId,
1681
+ {
1682
+ phase: 'verification',
1683
+ status: 'blocked',
1684
+ state: 'blocked',
1685
+ dispatchState: 'blocked',
1686
+ summary: `Retryable Work finalization validation failed; exact verification/review authority must be re-established: ${reason}`,
1687
+ },
1688
+ );
1689
+ markWorkValidationPending(ctx.controllerHome, current);
942
1690
  } else if (stage === 'commit' || stage === 'merge' || stage === 'branchCleanup' || stage === 'worktreeCleanup') {
943
1691
  transitionWorkContractPhase(
944
1692
  { controllerHome: ctx.controllerHome, repoId: current.repositoryId },
@@ -947,6 +1695,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
947
1695
  phase: retryPhaseForFinalizationStage(stage),
948
1696
  status: 'blocked',
949
1697
  state: 'blocked',
1698
+ dispatchState: 'blocked',
950
1699
  summary: `Retryable Work finalization stage ${stage} failed: ${reason}`,
951
1700
  },
952
1701
  );
@@ -1006,44 +1755,92 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1006
1755
  });
1007
1756
  if (gitAuthorization.decision !== 'allow') return { authorization: gitAuthorization, work: compactHandle(current), stages: current.finalization };
1008
1757
 
1009
- // A direct Work can share canonical main with another Work's preserved dirty
1010
- // delta. If main advanced after this Work was prepared, keep the global
1011
- // execution-identity fence strict and reconcile only here, where delivery
1012
- // ownership plus exact current verification can be proven together.
1013
- if (wants.commit && !current.managedWorktree && current.expectedHead) {
1758
+ // A conflict-repaired managed candidate rewrites commit identity, so the
1759
+ // normal descendant-HEAD fence cannot authorize it. Re-adopt only an exact,
1760
+ // clean, scope-contained candidate that has reconciled the linearly advanced
1761
+ // target. The transition returns before delivery, so the successor must gain
1762
+ // current-source verification/review authority before merge can resume.
1763
+ if (wants.merge && current.managedWorktree && current.expectedHead) {
1014
1764
  const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
1015
1765
  const worktree = selectRepositoryCheckout(repository, current.checkoutId, { allowArchived: true });
1016
1766
  const target = selectWorkFinalizationTarget(repository, current);
1017
- const targetBranch = explicitTargetBranch ?? repository.defaultBranch ?? 'main';
1767
+ const targetBranch = resolveWorkDeliveryTargetBranch(current, target.defaultBranch, explicitTargetBranch);
1018
1768
  const contract = contractFor(ctx, current);
1019
- const advance = inspectDirectCanonicalTargetAdvanceReconciliation({
1020
- root: target.canonicalRoot,
1769
+ const adoption = inspectManagedWorkSuccessorAdoption({
1770
+ root: worktree.canonicalRoot,
1021
1771
  worktreePath: current.worktreePath,
1022
1772
  managedWorktree: current.managedWorktree,
1023
1773
  workBranch: current.branch,
1024
1774
  targetBranch,
1025
1775
  expectedRevision: current.expectedHead,
1776
+ deliveryBaseRevision: workDeliveryBaseRevision(current),
1026
1777
  status: repositoryGitStatus(worktree),
1027
1778
  scope: contract ? { allowedPaths: contract.allowedPaths, forbiddenPaths: contract.forbiddenPaths } : undefined,
1028
- checkIds: contract?.checks ?? [],
1029
- checkRefs: contract?.checkRefs ?? [],
1030
1779
  });
1031
- if (advance.reconcilable && advance.targetHead) {
1780
+ if (adoption.adoptable && adoption.candidateHead && adoption.targetHead) {
1032
1781
  const previousHead = current.expectedHead;
1033
- current = transact('direct-canonical-target-advance-reconciled', (fresh) => writeWorkHandle(ctx.controllerHome, {
1034
- ...fresh,
1035
- deliveryBaseCommit: advance.targetHead,
1036
- expectedHead: advance.targetHead,
1037
- failureReason: undefined,
1038
- }));
1782
+ current = transact('managed-successor-adopted', (fresh) => adoptWorkHandleSuccessorCandidate(
1783
+ ctx.controllerHome,
1784
+ fresh,
1785
+ { candidateHead: adoption.candidateHead!, targetHead: adoption.targetHead! },
1786
+ ));
1787
+ if (contract) {
1788
+ transitionWorkContractPhase(
1789
+ { controllerHome: ctx.controllerHome, repoId: current.repositoryId },
1790
+ contract.workId,
1791
+ {
1792
+ phase: 'verification',
1793
+ status: 'running',
1794
+ state: 'active',
1795
+ summary: `Conflict-repaired successor ${adoption.candidateHead} adopted after canonical ${targetBranch} advanced to ${adoption.targetHead}; current-source verification is required before delivery resumes.`,
1796
+ },
1797
+ );
1798
+ }
1039
1799
  appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
1040
- title: 'direct canonical target advancement reconciled',
1041
- summary: `Canonical ${targetBranch} advanced linearly ${previousHead} -> ${advance.targetHead}. ${advance.targetChangedPaths.length} target-only path(s) were disjoint from all ${advance.dirtyPaths.length} positively Work-owned dirty path(s), and fresh exact verification bound [${advance.freshCheckIds.join(', ')}] to source ${advance.targetHead} plus workspace ${advance.workspaceFingerprint?.slice(0, 16) ?? 'unknown'}. Delivery identity advanced without adopting target-only commits or unrelated dirty paths.`,
1800
+ title: 'managed Work successor candidate adopted',
1801
+ summary: `WorkHandle authority moved ${previousHead} -> ${adoption.candidateHead} only after proving clean exact checkout ownership, canonical target ancestry at ${adoption.targetHead}, linear candidate history, and ${adoption.candidateChangedPaths.length} scope-contained target-relative path(s). Validation/delivery authority was re-armed; no canonical target mutation occurred.`,
1042
1802
  detailLevel: 'summary',
1043
1803
  });
1804
+ markWorkValidationPending(ctx.controllerHome, current);
1805
+ return {
1806
+ work: compactHandle(current),
1807
+ stages: current.finalization,
1808
+ completed: false,
1809
+ successorAdopted: true,
1810
+ continuation: `WORK_SUCCESSOR_REVALIDATION_REQUIRED: successor ${adoption.candidateHead} is now the exact Work candidate after target ${adoption.targetHead}; verify/review this source revision before retrying delivery`,
1811
+ };
1044
1812
  }
1045
1813
  }
1046
1814
 
1815
+ // A direct Work can share canonical main with another Work's preserved dirty
1816
+ // delta. If main advanced after this Work was prepared, keep the global
1817
+ // execution-identity fence strict and reconcile only here, where delivery
1818
+ // ownership plus exact current verification can be proven together.
1819
+ if (wants.commit && !current.managedWorktree && current.expectedHead) {
1820
+ const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
1821
+ const worktree = selectRepositoryCheckout(repository, current.checkoutId, { allowArchived: true });
1822
+ const target = selectWorkFinalizationTarget(repository, current);
1823
+ const targetBranch = resolveWorkDeliveryTargetBranch(current, repository.defaultBranch, explicitTargetBranch);
1824
+ const contract = contractFor(ctx, current);
1825
+ const reconciliation = reconcileDirectCanonicalTargetAdvanceCommand({
1826
+ controllerHome: ctx.controllerHome,
1827
+ repoId: current.repositoryId,
1828
+ workId: current.workContractId ?? current.workId,
1829
+ handle: current,
1830
+ root: target.canonicalRoot,
1831
+ targetBranch,
1832
+ status: repositoryGitStatus(worktree),
1833
+ scope: contract ? { allowedPaths: contract.allowedPaths, forbiddenPaths: contract.forbiddenPaths } : undefined,
1834
+ checkIds: contract?.checks ?? [],
1835
+ checkRefs: contract?.checkRefs ?? [],
1836
+ persistHandle: (patch) => transact('direct-canonical-target-advance-reconciled', (fresh) => writeWorkHandle(ctx.controllerHome, {
1837
+ ...fresh,
1838
+ ...patch,
1839
+ })),
1840
+ });
1841
+ if (reconciliation.reconciled) current = reconciliation.handle;
1842
+ }
1843
+
1047
1844
  if (failedCleanupProof) {
1048
1845
  const preservedFailure = current.failureReason ?? current.finalization.lastError ?? 'Work validation failed.';
1049
1846
  current = transact('failed-cleanup-begin', (fresh) => writeWorkHandle(ctx.controllerHome, {
@@ -1171,6 +1968,16 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1171
1968
  && wants.cleanup
1172
1969
  && !wants.commit
1173
1970
  && !wants.merge;
1971
+ const noChangeCleanupReconciliation = requestedOutcome === 'completed_no_change'
1972
+ ? inspectCleanupOnlyMergedHead(ctx, current, args)
1973
+ : undefined;
1974
+ const noChangeRemovedWorktreeRecovery = Boolean(
1975
+ noChangeCleanupReconciliation?.worktreeMissing
1976
+ && current.finalization.worktreeCleanup === 'done'
1977
+ && current.finalization.validation === 'done'
1978
+ && current.validatedInputFingerprint
1979
+ && (current.state === 'merged' || current.state === 'cleaned'),
1980
+ );
1174
1981
  const cleanupReconciliation = requestedOutcome === 'completed_no_change'
1175
1982
  ? undefined
1176
1983
  : inspectCleanupOnlyMergedHead(ctx, current, args);
@@ -1194,7 +2001,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1194
2001
  branchCleanup: deleteBranchRequested
1195
2002
  ? (fresh.finalization.branchCleanup === 'done' ? 'done' : 'pending')
1196
2003
  : 'skipped',
1197
- lastError: undefined,
2004
+ failureCode: undefined, lastError: undefined,
1198
2005
  },
1199
2006
  }));
1200
2007
  }
@@ -1214,14 +2021,22 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1214
2021
  if (terminalCleanupOnly) {
1215
2022
  current = transact('terminal-cleanup-validation-skipped', (fresh) => writeWorkHandle(ctx.controllerHome, {
1216
2023
  ...fresh,
1217
- finalization: { ...fresh.finalization, validation: 'done', lastError: undefined },
2024
+ finalization: { ...fresh.finalization, validation: 'done', failureCode: undefined, lastError: undefined },
1218
2025
  }));
2026
+ } else if (noChangeRemovedWorktreeRecovery) {
2027
+ // A prior no-change finalize may have durably recorded validation and physical
2028
+ // worktree cleanup before terminal Work completion. Target containment above
2029
+ // re-proves the exact expected HEAD; do not recreate mutable workspace state
2030
+ // merely to repeat a validation whose fingerprint is already durable.
1219
2031
  } else {
1220
2032
  let validatedRepository: RepositoryRecord;
1221
2033
  try {
1222
2034
  validatedRepository = validateWorkHandle(ctx.controllerHome, current, identity, 'full', 'finalize').worktreeRepository;
1223
2035
  } catch (error) {
1224
- return failStage('validation', error instanceof Error ? error.message : String(error));
2036
+ const failureCode = error instanceof WorkHandleValidationError && error.code === 'WORK_HANDLE_HEAD_CHANGED'
2037
+ ? error.code
2038
+ : undefined;
2039
+ return failStage('validation', error instanceof Error ? error.message : String(error), failureCode);
1225
2040
  }
1226
2041
  const validationContract = contractFor(ctx, current);
1227
2042
  if (!validationContract) throw new Error(`WORK_VALIDATION_CONTRACT_MISSING: ${current.workContractId ?? current.workId}`);
@@ -1232,7 +2047,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1232
2047
  current = transact('validation-no-checks', (fresh) => writeWorkHandle(ctx.controllerHome, {
1233
2048
  ...fresh,
1234
2049
  validatedInputFingerprint: validationInput.fingerprint,
1235
- finalization: { ...fresh.finalization, validation: 'done', lastError: undefined },
2050
+ finalization: { ...fresh.finalization, validation: 'done', failureCode: undefined, lastError: undefined },
1236
2051
  }));
1237
2052
  }
1238
2053
  projectWorkValidationOutcome(ctx.controllerHome, current, 'passed', 'No validation checks were required.');
@@ -1245,7 +2060,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1245
2060
  current = transact('validation-required', (fresh) => writeWorkHandle(ctx.controllerHome, {
1246
2061
  ...fresh,
1247
2062
  validatedInputFingerprint: undefined,
1248
- finalization: { ...fresh.finalization, validation: 'pending', lastError: undefined },
2063
+ finalization: { ...fresh.finalization, validation: 'pending', failureCode: undefined, lastError: undefined },
1249
2064
  }));
1250
2065
  markWorkValidationPending(ctx.controllerHome, current);
1251
2066
  throw new Error('WORK_VALIDATION_REQUIRED: run work_validate against the exact current workspace before finalization');
@@ -1269,16 +2084,20 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1269
2084
  }
1270
2085
 
1271
2086
  if (noChangeFastPath) {
1272
- if (!exactValidationInput?.clean) {
2087
+ if (!exactValidationInput?.clean && !noChangeRemovedWorktreeRecovery) {
1273
2088
  throw new Error('WORK_NO_CHANGE_DIRTY: completed_no_change cannot retain an owned dirty worktree');
1274
2089
  }
2090
+ const noChangeValidationFingerprint = exactValidationInput?.fingerprint ?? current.validatedInputFingerprint;
2091
+ if (!noChangeValidationFingerprint) {
2092
+ throw new Error('WORK_NO_CHANGE_VALIDATION_AUTHORITY_REQUIRED: completed_no_change requires retained exact validation authority');
2093
+ }
1275
2094
  const noChangeFinalization: WorkFinalizationStages = {
1276
2095
  ...current.finalization,
1277
2096
  validation: 'done',
1278
2097
  commit: 'skipped',
1279
2098
  merge: 'skipped',
1280
2099
  branchCleanup: wants.cleanup && deleteBranchRequested ? 'pending' : 'skipped',
1281
- lastError: undefined,
2100
+ failureCode: undefined, lastError: undefined,
1282
2101
  };
1283
2102
  // Collapse the no-op Git stages into the same durable lifecycle write used
1284
2103
  // to enter/establish the delivery boundary. Physical cleanup remains
@@ -1286,13 +2105,13 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1286
2105
  if (current.state === 'prepared') {
1287
2106
  current = transact('no-change-validated', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'validating', {
1288
2107
  finalization: noChangeFinalization,
1289
- validatedInputFingerprint: exactValidationInput.fingerprint,
2108
+ validatedInputFingerprint: noChangeValidationFingerprint,
1290
2109
  failureReason: undefined,
1291
2110
  }));
1292
2111
  } else if (current.state === 'editing' || current.state === 'validating' || current.state === 'committed') {
1293
2112
  current = transact('no-change-delivery-integrated', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'merged', {
1294
2113
  finalization: noChangeFinalization,
1295
- validatedInputFingerprint: exactValidationInput.fingerprint,
2114
+ validatedInputFingerprint: noChangeValidationFingerprint,
1296
2115
  failureReason: undefined,
1297
2116
  }));
1298
2117
  }
@@ -1307,28 +2126,53 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1307
2126
  if (dirtyPaths.length > 0 && commitPaths.length === 0) {
1308
2127
  throw new Error(`WORK_COMMIT_NO_OWNED_DIRTY_PATHS: ${current.workId} has ${dirtyPaths.length} dirty path(s), but none are owned by its WorkContract path scope`);
1309
2128
  }
2129
+ if (!contract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
2130
+ const preCommitReviewCandidate = assertPhysicalImplementationReviewGate({
2131
+ ctx,
2132
+ repository: validated.worktreeRepository,
2133
+ handle: current,
2134
+ contract,
2135
+ verificationWorkspaceFingerprint: exactValidationInput?.workspaceFingerprint,
2136
+ targetBranch: resolveWorkDeliveryTargetBranch(current, validated.worktreeRepository.defaultBranch, explicitTargetBranch),
2137
+ });
2138
+ const reviewedCommitPaths = normalizeImplementationReviewChangedPaths(preCommitReviewCandidate.changedPaths);
2139
+ const exactCommitPaths = normalizeImplementationReviewChangedPaths(commitPaths);
2140
+ if (JSON.stringify(reviewedCommitPaths) !== JSON.stringify(exactCommitPaths)) {
2141
+ throw new Error('WORK_IMPLEMENTATION_REVIEW_TRANSFER_COMMIT_SCOPE_MISMATCH: commit must materialize the complete reviewed Work path set');
2142
+ }
1310
2143
  const committed = repositoryGitCommit(ctx.controllerHome, validated.worktreeRepository, { message: String(args.message ?? `Complete ${current.workId}`), paths: commitPaths, allowEmpty: false, authorizationDecision: gitAuthorization, sessionId: session.sessionId, principalId: session.principalId, workId: current.workId, goalId: current.goalId });
1311
2144
  const pendingAuthorization = [committed.stage, committed.commit].find((execution) => execution?.authorizationDecision?.decision === 'user_confirmation_required')?.authorizationDecision;
1312
2145
  if (pendingAuthorization) return { authorization: pendingAuthorization, work: compactHandle(current), stages: current.finalization };
1313
2146
  if (!committed.committed) return { ...failStage('commit', committed.error?.message ?? 'commit failed'), commit: committed };
1314
2147
  const checks = contract?.checks ?? [];
1315
- const committedValidatedWorkspaceFingerprint = exactValidationInput && committed.after.clean
1316
- ? workspaceValidationFingerprint(validated.worktreeRepository.canonicalRoot, committed.before)
1317
- : undefined;
2148
+ const committedHead = gitHead(validated.worktreeRepository.canonicalRoot);
2149
+ if (!committedHead) throw new Error('WORK_COMMIT_HEAD_REQUIRED: committed revision is unavailable');
2150
+ const postCommitInput = currentWorkValidationInput(validated.worktreeRepository, { ...current, expectedHead: committedHead }, checks);
2151
+ const postCommitContentFingerprint = implementationReviewContentFingerprint(
2152
+ validated.worktreeRepository.canonicalRoot,
2153
+ preCommitReviewCandidate.changedPaths,
2154
+ );
2155
+ const committedPaths = exactCommitChangedPaths(validated.worktreeRepository.canonicalRoot, committedHead);
2156
+ const reviewedCommitPathsAfterCommit = normalizeImplementationReviewChangedPaths(preCommitReviewCandidate.changedPaths);
2157
+ const exactCommittedPaths = normalizeImplementationReviewChangedPaths(committedPaths);
2158
+ const reviewedContentPreservedAcrossCommit = JSON.stringify(reviewedCommitPathsAfterCommit) === JSON.stringify(exactCommittedPaths)
2159
+ && postCommitContentFingerprint === preCommitReviewCandidate.workspaceFingerprint;
2160
+ // Git staging/commit changes workspace-status shape even when the reviewed bytes
2161
+ // are identical. Preserve validation from exact reviewed content equivalence;
2162
+ // transferWorkVerificationAcrossContentEquivalentCommit remains responsible
2163
+ // for invalidating checks whose semantics are genuinely Git-sensitive.
1318
2164
  const validationPreservedAcrossCommit = Boolean(
1319
2165
  checks.length > 0
1320
2166
  && exactValidationInput
1321
- && committedValidatedWorkspaceFingerprint === exactValidationInput.workspaceFingerprint
2167
+ && reviewedContentPreservedAcrossCommit
1322
2168
  );
1323
- const committedHead = gitHead(validated.worktreeRepository.canonicalRoot);
1324
- const postCommitInput = currentWorkValidationInput(validated.worktreeRepository, { ...current, expectedHead: committedHead }, checks);
1325
2169
  current = transact('commit-done', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'committed', {
1326
2170
  expectedHead: committedHead,
1327
2171
  finalization: {
1328
2172
  ...fresh.finalization,
1329
2173
  validation: checks.length ? (validationPreservedAcrossCommit ? 'done' : 'pending') : 'done',
1330
2174
  commit: 'done',
1331
- lastError: undefined,
2175
+ failureCode: undefined, lastError: undefined,
1332
2176
  },
1333
2177
  validatedInputFingerprint: checks.length
1334
2178
  ? (validationPreservedAcrossCommit ? postCommitInput.fingerprint : undefined)
@@ -1344,55 +2188,64 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1344
2188
  continuation: 'WORK_COMMITTED_REVALIDATION_REQUIRED: committed content changed from the exact validated workspace; run work_validate on the committed HEAD before merge or completion',
1345
2189
  };
1346
2190
  }
1347
- if (validationPreservedAcrossCommit && exactValidationInput) {
1348
- const checkDefinitions = listControllerChecks(validated.worktreeRepository.canonicalRoot);
1349
- for (const checkId of checks) {
1350
- const check = checkDefinitions.find((entry) => entry.id === checkId);
1351
- if (check?.effects?.git !== undefined) continue;
1352
- const sourcePass = contract
1353
- ? effectiveVerificationEvidence(contract.checkRefs, {
1354
- sourceRevision: exactValidationInput.head,
1355
- workspaceFingerprint: exactValidationInput.workspaceFingerprint,
1356
- checkId,
1357
- requestedChecks: checks,
1358
- }).find((entry) => entry.current && entry.record.outcome === 'valid_pass' && Boolean(entry.record.receipt))?.record
1359
- : undefined;
1360
- if (!sourcePass) continue;
1361
- appendVerificationRecord({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
1362
- ...sourcePass,
1363
- summary: `Validation authority transferred across content-equivalent commit for ${checkId}.`,
1364
- recordedAt: new Date().toISOString(),
1365
- sourceRevision: postCommitInput.head,
1366
- workspaceFingerprint: postCommitInput.workspaceFingerprint,
1367
- verificationInputFingerprint: verificationInputFingerprint({
1368
- sourceRevision: postCommitInput.head,
1369
- workspaceFingerprint: postCommitInput.workspaceFingerprint,
1370
- checkId,
1371
- requestedChecks: checks,
1372
- }),
1373
- evidenceRef: {
1374
- title: checkId,
1375
- summary: 'Reused prior valid receipt after proving the validated filesystem content was committed without drift.',
1376
- detailLevel: 'summary',
1377
- },
1378
- });
1379
- }
1380
- appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
1381
- title: 'validation authority preserved across content-equivalent commit',
1382
- summary: `Exact validated workspace ${exactValidationInput.workspaceFingerprint.slice(0, 16)} was committed without content/status drift; non-Git-sensitive per-check authority transferred to committed HEAD ${postCommitInput.head}.`,
1383
- detailLevel: 'summary',
1384
- });
1385
- } else if (checks.length === 0) {
1386
- projectWorkValidationOutcome(ctx.controllerHome, current, 'passed', 'No validation checks were required after commit.');
2191
+ const authorityTransfer = transferReviewedWorkAuthorityAcrossContentEquivalentCommit({
2192
+ controllerHome: ctx.controllerHome,
2193
+ repository: validated.worktreeRepository,
2194
+ workId: current.workContractId ?? current.workId,
2195
+ preCommitCandidate: preCommitReviewCandidate,
2196
+ preCommitDirtyPaths: commitPaths,
2197
+ committedPaths,
2198
+ preCommitContentDigest: preCommitReviewCandidate.workspaceFingerprint,
2199
+ postCommitSourceRevision: postCommitInput.head,
2200
+ postCommitContentDigest: postCommitContentFingerprint,
2201
+ postCommitVerificationWorkspaceFingerprint: postCommitInput.workspaceFingerprint,
2202
+ postCommitChangedPaths: preCommitReviewCandidate.changedPaths,
2203
+ });
2204
+ if (authorityTransfer.invalidatedCheckIds.length > 0) {
2205
+ current = transact('commit-review-revalidation-required', (fresh) => writeWorkHandle(ctx.controllerHome, {
2206
+ ...fresh,
2207
+ finalization: { ...fresh.finalization, validation: 'pending', failureCode: undefined, lastError: undefined },
2208
+ validatedInputFingerprint: undefined,
2209
+ }));
2210
+ markWorkValidationPending(ctx.controllerHome, current);
2211
+ return {
2212
+ work: compactHandle(current),
2213
+ stages: current.finalization,
2214
+ completed: false,
2215
+ continuation: `WORK_COMMITTED_REVALIDATION_REQUIRED: content-equivalent verification transfer invalidated [${authorityTransfer.invalidatedCheckIds.join(', ')}]`,
2216
+ };
2217
+ }
2218
+ if (!authorityTransfer.transferred || !authorityTransfer.derivedReview) {
2219
+ throw new Error('WORK_IMPLEMENTATION_REVIEW_TRANSFER_REQUIRED');
1387
2220
  }
2221
+ appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
2222
+ title: 'verification and implementation-review authority preserved across content-equivalent commit',
2223
+ summary: `The exact reviewed Work content was committed as ${postCommitInput.head}; ${authorityTransfer.reusableCheckIds.length}/${checks.length} non-Git-sensitive Process receipt(s) and review ${authorityTransfer.derivedReview.reviewId} were transferred atomically.`,
2224
+ detailLevel: 'summary',
2225
+ });
1388
2226
  } else if (!wants.commit && current.finalization.commit === 'pending') {
1389
- current = transact('commit-skipped', (fresh) => writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, commit: 'skipped' } }));
2227
+ const committedRepository = validateWorkHandle(ctx.controllerHome, current, identity, 'full', 'finalize').worktreeRepository;
2228
+ const committedStatus = repositoryGitStatus(committedRepository);
2229
+ const alreadyMaterializedCommit = Boolean(
2230
+ committedStatus.clean
2231
+ && committedStatus.head
2232
+ && current.expectedHead
2233
+ && current.baseCommit
2234
+ && committedStatus.head === current.expectedHead
2235
+ && committedStatus.head !== current.baseCommit,
2236
+ );
2237
+ current = alreadyMaterializedCommit
2238
+ ? transact('commit-already-materialized', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'committed', {
2239
+ finalization: { ...fresh.finalization, commit: 'done', failureCode: undefined, lastError: undefined },
2240
+ failureReason: undefined,
2241
+ }))
2242
+ : transact('commit-skipped', (fresh) => writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, commit: 'skipped' } }));
1390
2243
  }
1391
2244
 
1392
2245
  if (wants.merge && current.finalization.merge === 'pending' && current.finalization.commit === 'done') {
1393
2246
  const repository = getRepository(current.repositoryId, ctx.controllerHome);
1394
2247
  const target = selectWorkFinalizationTarget(repository, current);
1395
- const targetBranch = explicitTargetBranch ?? repository.defaultBranch ?? 'main';
2248
+ const targetBranch = resolveWorkDeliveryTargetBranch(current, repository.defaultBranch, explicitTargetBranch);
1396
2249
  const directTarget = inspectDirectTargetDelivery(
1397
2250
  target.canonicalRoot,
1398
2251
  current.worktreePath,
@@ -1403,7 +2256,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1403
2256
  );
1404
2257
  if (directTarget.integrated) {
1405
2258
  current = transact('direct-target-delivery-integrated', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'merged', {
1406
- finalization: { ...fresh.finalization, merge: 'done', branchCleanup: 'skipped', lastError: undefined },
2259
+ finalization: { ...fresh.finalization, merge: 'done', branchCleanup: 'skipped', failureCode: undefined, lastError: undefined },
1407
2260
  failureReason: undefined,
1408
2261
  }));
1409
2262
  appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
@@ -1425,9 +2278,16 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1425
2278
  }
1426
2279
  const contract = contractFor(ctx, current);
1427
2280
  if (contract?.constraints.allowMerge === false) throw new Error('WORK_MERGE_NOT_ALLOWED: WorkContract disallows merge');
2281
+ if (!contract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
1428
2282
  const target = selectWorkFinalizationTarget(getRepository(current.repositoryId, ctx.controllerHome), current);
1429
2283
  const deleteAfterWorktreeCleanup = current.managedWorktree && deleteBranchRequested;
1430
- const targetBranch = explicitTargetBranch ?? target.defaultBranch ?? 'main';
2284
+ const targetBranch = resolveWorkDeliveryTargetBranch(current, target.defaultBranch, explicitTargetBranch);
2285
+ // Re-read the exact Work candidate immediately before any merge-path mutation
2286
+ // (including target-advance rebase). The finalizer is an independent physical
2287
+ // gate; semantic finalize authority is not trusted as a cached boolean.
2288
+ assertPhysicalImplementationReviewGate({
2289
+ ctx, repository: mergeValidated.worktreeRepository, handle: current, contract, targetBranch,
2290
+ });
1431
2291
  const activeMergeHead = retryStage === 'merge' ? gitRevision(target.canonicalRoot, 'MERGE_HEAD') : undefined;
1432
2292
  if (!activeMergeHead && current.managedWorktree && current.expectedHead) {
1433
2293
  const targetHead = gitRevision(target.canonicalRoot, targetBranch);
@@ -1442,6 +2302,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1442
2302
  phase: 'delivery',
1443
2303
  status: 'blocked',
1444
2304
  state: 'blocked',
2305
+ dispatchState: 'blocked',
1445
2306
  summary: `Target branch ${targetBranch} advanced to ${advance.targetHead} and conflicts with candidate ${advance.candidateHead}; canonical target was not mutated.`,
1446
2307
  },
1447
2308
  );
@@ -1466,7 +2327,11 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1466
2327
  advance.candidateHead,
1467
2328
  );
1468
2329
  if (nonLinearCommits.length > 0) {
1469
- return failStage('merge', `WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION: candidate already contains merge commit(s) above reconciled target ${advance.targetHead}: ${nonLinearCommits.slice(0, 8).join(', ')}`);
2330
+ return failStage(
2331
+ 'merge',
2332
+ `WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION: candidate already contains merge commit(s) above reconciled target ${advance.targetHead}: ${nonLinearCommits.slice(0, 8).join(', ')}`,
2333
+ 'WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION',
2334
+ );
1470
2335
  }
1471
2336
  const scopeViolation = targetAdvanceWorkScopeViolation(contract, advance.candidateChangedPaths);
1472
2337
  if (scopeViolation) {
@@ -1505,6 +2370,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1505
2370
  phase: 'delivery',
1506
2371
  status: 'blocked',
1507
2372
  state: 'blocked',
2373
+ dispatchState: 'blocked',
1508
2374
  summary: `Linear target-advance integration failed inside the isolated Work checkout; canonical ${targetBranch} was not mutated.`,
1509
2375
  },
1510
2376
  );
@@ -1537,7 +2403,11 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1537
2403
  encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
1538
2404
  });
1539
2405
  if (mergeCommits.length > 0) {
1540
- return failStage('merge', `WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION: integrated candidate contains merge commit(s): ${mergeCommits.slice(0, 8).join(', ')}`);
2406
+ return failStage(
2407
+ 'merge',
2408
+ `WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION: integrated candidate contains merge commit(s): ${mergeCommits.slice(0, 8).join(', ')}`,
2409
+ 'WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION',
2410
+ );
1541
2411
  }
1542
2412
  if (
1543
2413
  !advance.mergedTree
@@ -1585,7 +2455,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1585
2455
  deliveryBaseCommit: advance.targetHead,
1586
2456
  expectedHead: integratedHead,
1587
2457
  failureReason: undefined,
1588
- finalization: { ...fresh.finalization, validation: validationPreserved ? 'done' : 'pending', merge: 'pending', lastError: undefined },
2458
+ finalization: { ...fresh.finalization, validation: validationPreserved ? 'done' : 'pending', merge: 'pending', failureCode: undefined, lastError: undefined },
1589
2459
  validationRun: undefined,
1590
2460
  validatedInputFingerprint: validationPreserved ? integratedInput.fingerprint : undefined,
1591
2461
  },
@@ -1610,6 +2480,31 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1610
2480
  }
1611
2481
  }
1612
2482
  }
2483
+ const deliveryContract = contractFor(ctx, current);
2484
+ if (!deliveryContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
2485
+ try {
2486
+ const deliveryRepository = selectRepositoryCheckout(
2487
+ getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true }),
2488
+ current.checkoutId,
2489
+ { allowArchived: true },
2490
+ );
2491
+ assertPhysicalImplementationReviewGate({
2492
+ ctx, repository: deliveryRepository, handle: current, contract: deliveryContract, targetBranch,
2493
+ });
2494
+ } catch (error) {
2495
+ return {
2496
+ work: compactHandle(current),
2497
+ stages: current.finalization,
2498
+ completed: false,
2499
+ blocked: true,
2500
+ recoverable: true,
2501
+ error: {
2502
+ code: 'WORK_IMPLEMENTATION_REVIEW_REQUIRED',
2503
+ message: error instanceof Error ? error.message : String(error),
2504
+ },
2505
+ continuation: 'WORK_IMPLEMENTATION_REVIEW_REQUIRED: target integration changed the Work delivery candidate; verify/review the exact current candidate before mutating the canonical target.',
2506
+ };
2507
+ }
1613
2508
  let merged: ReturnType<typeof repositoryGitFinishWorkflow>;
1614
2509
  if (activeMergeHead) {
1615
2510
  const expectedMergeHead = current.expectedHead ? gitRevision(target.canonicalRoot, current.expectedHead) : undefined;
@@ -1691,29 +2586,26 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1691
2586
  ])].sort();
1692
2587
  let preserveDirtyTargetPaths: string[] | undefined;
1693
2588
  if (lockedDirtyPaths.length > 0) {
1694
- const ownership = inspectTargetDirtyWorkOwnership({
2589
+ const preservation = planTargetDirtyPreservation({
1695
2590
  dirtyPaths: lockedDirtyPaths,
1696
2591
  targetCheckoutId: target.activeCheckoutId,
1697
2592
  currentWorkId: current.workId,
1698
2593
  activeWorks: listWorkContracts({ controllerHome: ctx.controllerHome, repoId: current.repositoryId, status: 'active', limit: 200 }),
1699
2594
  });
1700
- if (!ownership.owned) {
1701
- const detail = [
1702
- ownership.unownedPaths.length ? `unowned=[${ownership.unownedPaths.join(', ')}]` : '',
1703
- ownership.ambiguousPaths.length ? `ambiguous=[${ownership.ambiguousPaths.join(', ')}]` : '',
1704
- ].filter(Boolean).join(' ');
1705
- throw new Error(`WORK_TARGET_DIRTY_OWNERSHIP_REQUIRED: target ${targetBranch} has dirty path(s) that cannot be attributed uniquely to another active Work. ${detail}`.trim());
1706
- }
1707
- preserveDirtyTargetPaths = ownership.dirtyPaths;
2595
+ const ownership = preservation.ownership;
2596
+ preserveDirtyTargetPaths = preservation.preserveDirtyTargetPaths;
2597
+ const attribution = ownership.owned
2598
+ ? 'all preserved paths are attributed uniquely to other active Work'
2599
+ : `preserved paths include unattributed or ambiguous content (unowned=${ownership.unownedPaths.length}, ambiguous=${ownership.ambiguousPaths.length})`;
1708
2600
  appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
1709
- title: 'concurrent target dirty ownership preserved',
1710
- summary: `Target ${targetBranch} retained ${ownership.dirtyPaths.length} dirty path(s) owned by other active Work while this Work entered only the target-branch mutation critical section.`,
2601
+ title: 'concurrent target dirty content preserved',
2602
+ summary: `Target ${targetBranch} retained ${preserveDirtyTargetPaths.length} dirty path(s) in place; ${attribution}. Git integration remains fail-closed before target mutation if the candidate overlaps any preserved path.`,
1711
2603
  detailLevel: 'summary',
1712
2604
  });
1713
2605
  }
1714
2606
  return repositoryGitFinishWorkflow(ctx.controllerHome, target, {
1715
2607
  featureBranch: current.branch,
1716
- targetBranch: explicitTargetBranch,
2608
+ targetBranch,
1717
2609
  deleteBranch: !deleteAfterWorktreeCleanup && deleteBranchRequested,
1718
2610
  noFf: args.no_ff === true,
1719
2611
  preserveDirtyTargetPaths,
@@ -1735,7 +2627,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1735
2627
  if (pendingAuthorization) return { authorization: pendingAuthorization, work: compactHandle(current), stages: current.finalization };
1736
2628
  if (!merged.completed) return { ...failStage('merge', merged.error?.message ?? 'merge failed'), merge: merged };
1737
2629
  current = transact('merge-done', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'merged', {
1738
- finalization: { ...fresh.finalization, merge: 'done', branchCleanup: !deleteBranchRequested ? 'skipped' : deleteAfterWorktreeCleanup ? 'pending' : 'done', lastError: undefined },
2630
+ finalization: { ...fresh.finalization, merge: 'done', branchCleanup: !deleteBranchRequested ? 'skipped' : deleteAfterWorktreeCleanup ? 'pending' : 'done', failureCode: undefined, lastError: undefined },
1739
2631
  failureReason: undefined,
1740
2632
  }));
1741
2633
  } else if (!wants.merge && current.finalization.merge === 'pending') {
@@ -1755,10 +2647,15 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1755
2647
  if (remoteDeliveryRequired && requestedOutcome !== 'completed_no_change') {
1756
2648
  const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
1757
2649
  const target = selectWorkFinalizationTarget(repository, current);
1758
- const targetBranch = explicitTargetBranch ?? repository.defaultBranch ?? 'main';
2650
+ const targetBranch = resolveWorkDeliveryTargetBranch(current, repository.defaultBranch, explicitTargetBranch);
1759
2651
  const targetRevision = gitRevision(target.canonicalRoot, targetBranch);
1760
2652
  if (!targetRevision) throw new Error(`WORK_REMOTE_DELIVERY_TARGET_REQUIRED: target branch ${targetBranch} is unavailable`);
1761
2653
  const authorityRepository = selectRepositoryCheckout(repository, current.checkoutId, { allowArchived: true });
2654
+ const remoteDeliveryContract = contractFor(ctx, current);
2655
+ if (!remoteDeliveryContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
2656
+ assertPhysicalImplementationReviewGate({
2657
+ ctx, repository: authorityRepository, handle: current, contract: remoteDeliveryContract, targetBranch,
2658
+ });
1762
2659
  remoteDelivery = await pushExactWorkRemoteDelivery({
1763
2660
  controllerHome: ctx.controllerHome,
1764
2661
  repository: authorityRepository,
@@ -1806,19 +2703,50 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1806
2703
  }
1807
2704
  }
1808
2705
 
2706
+ let prevalidatedNoChangeReceipt: CompletionReceipt | undefined;
2707
+ if (requestedOutcome === 'completed_no_change'
2708
+ && current.finalization.validation === 'done'
2709
+ && current.finalization.commit === 'skipped'
2710
+ && current.finalization.merge === 'skipped'
2711
+ && (current.state === 'merged' || current.state === 'cleaned')) {
2712
+ const completionContract = contractFor(ctx, current);
2713
+ if (!completionContract) throw new Error(`WORK_COMPLETION_CONTRACT_MISSING: ${current.workContractId ?? current.workId}`);
2714
+ // Delivery/target identity is semantic completion evidence. Prove it before
2715
+ // deleting any managed worktree or branch so a wrong target remains safely
2716
+ // retryable with the exact workspace intact.
2717
+ prevalidatedNoChangeReceipt = completionReceiptForFinalizedWork(ctx, current, completionContract, args);
2718
+ }
2719
+
1809
2720
  if (wants.cleanup && current.finalization.worktreeCleanup === 'pending') {
1810
2721
  const contract = contractFor(ctx, current);
1811
2722
  if (contract?.constraints.allowCleanup === false) throw new Error('WORK_CLEANUP_NOT_ALLOWED: WorkContract disallows cleanup');
1812
2723
  if (!current.managedWorktree) {
1813
2724
  current = transact('worktree-cleanup-skipped', (fresh) => writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, worktreeCleanup: 'skipped' } }));
1814
2725
  } else {
2726
+ const cleanupContract = contractFor(ctx, current);
2727
+ if (!cleanupContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
2728
+ if (cleanupContract.status !== 'cancelled' && cleanupContract.status !== 'failed') {
2729
+ const cleanupRepository = selectRepositoryCheckout(
2730
+ getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true }),
2731
+ current.checkoutId,
2732
+ { allowArchived: true },
2733
+ );
2734
+ const cleanupTargetBranch = resolveWorkDeliveryTargetBranch(
2735
+ current,
2736
+ cleanupRepository.defaultBranch,
2737
+ explicitTargetBranch,
2738
+ );
2739
+ assertPhysicalImplementationReviewGate({
2740
+ ctx, repository: cleanupRepository, handle: current, contract: cleanupContract, targetBranch: cleanupTargetBranch,
2741
+ });
2742
+ }
1815
2743
  const target = selectWorkFinalizationTarget(getRepository(current.repositoryId, ctx.controllerHome), current);
1816
2744
  const cleanup = runCleanup(target.canonicalRoot, current.worktreePath);
1817
2745
  if (!cleanup.ok) return failStage('worktreeCleanup', cleanup.message ?? 'worktree cleanup failed');
1818
2746
  current = transact('worktree-cleanup-done', (fresh) => {
1819
2747
  setRepositoryCheckoutLifecycle({ controllerHome: ctx.controllerHome, repoId: fresh.repositoryId, checkoutId: fresh.checkoutId, lifecycle: 'removed', reason: `Work ${fresh.workId} cleanup completed.` });
1820
2748
  markRepositoryProjectionDirty(ctx.controllerHome, fresh.repositoryId, `cleanup:${fresh.workId}:worktree`);
1821
- return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, worktreeCleanup: 'done', lastError: undefined } });
2749
+ return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, worktreeCleanup: 'done', failureCode: undefined, lastError: undefined } });
1822
2750
  });
1823
2751
  }
1824
2752
  } else if (!wants.cleanup && current.finalization.worktreeCleanup === 'pending') {
@@ -1831,12 +2759,23 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1831
2759
  && (current.finalization.merge === 'done' || requestedOutcome === 'completed_no_change')
1832
2760
  ) {
1833
2761
  const target = selectWorkFinalizationTarget(getRepository(current.repositoryId, ctx.controllerHome), current);
2762
+ const branchCleanupContract = contractFor(ctx, current);
2763
+ if (!branchCleanupContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
2764
+ if (branchCleanupContract.status !== 'cancelled' && branchCleanupContract.status !== 'failed') {
2765
+ const targetBranch = resolveWorkDeliveryTargetBranch(current, target.defaultBranch, explicitTargetBranch);
2766
+ assertPhysicalBranchCleanupImplementationReviewGate({
2767
+ target,
2768
+ handle: current,
2769
+ contract: branchCleanupContract,
2770
+ targetBranch,
2771
+ });
2772
+ }
1834
2773
  const deleted = repositoryGitDeleteBranch(ctx.controllerHome, target, { branch: current.branch, force: false, authorizationDecision: gitAuthorization, sessionId: session.sessionId, principalId: session.principalId, workId: current.workId, goalId: current.goalId });
1835
2774
  if (deleted.execution.authorizationDecision?.decision === 'user_confirmation_required') return { authorization: deleted.execution.authorizationDecision, work: compactHandle(current), stages: current.finalization };
1836
2775
  if (deleted.execution.status !== 'executed' || deleted.execution.ok !== true) return failStage('branchCleanup', deleted.execution.stderr || 'feature branch cleanup failed');
1837
2776
  current = transact('branch-cleanup-done', (fresh) => {
1838
2777
  markRepositoryProjectionDirty(ctx.controllerHome, fresh.repositoryId, `cleanup:${fresh.workId}:branch`);
1839
- return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, branchCleanup: 'done', lastError: undefined } });
2778
+ return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, branchCleanup: 'done', failureCode: undefined, lastError: undefined } });
1840
2779
  });
1841
2780
  }
1842
2781
 
@@ -1866,36 +2805,34 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
1866
2805
  detailLevel: 'summary',
1867
2806
  });
1868
2807
  }
1869
- const receipt = completionReceiptForFinalizedWork(ctx, current, completionContract, args);
1870
- const ownerPrincipal = terminalizationOwner.principalId?.trim() || terminalizationOwner.controllerId;
1871
- const ownerInstanceId = terminalizationOwner.controllerInstanceId?.trim() || '';
1872
- const ownerClaimGeneration = terminalizationOwner.claimGeneration;
1873
- if (!ownerInstanceId || typeof ownerClaimGeneration !== 'number' || ownerClaimGeneration < 1) {
1874
- throw new Error(`WORK_CONTROLLER_TERMINALIZATION_AUTHORITY_INVALID: ${workId}`);
1875
- }
1876
- const fencedCompletion = withControllerSessionTerminalizationFence(
1877
- { controllerHome: ctx.controllerHome, repoId: current.repositoryId },
1878
- {
1879
- workId,
1880
- actor: `work-finalize-completion:${terminalizationOwner.controllerId}:${ownerInstanceId}`,
1881
- authority: {
1882
- controllerId: terminalizationOwner.controllerId,
1883
- controllerType: terminalizationOwner.controllerType,
1884
- principalId: ownerPrincipal,
1885
- controllerInstanceId: ownerInstanceId,
1886
- claimGeneration: ownerClaimGeneration,
1887
- },
1888
- },
1889
- () => completeWorkWithReceipt(
2808
+ if (prevalidatedNoChangeReceipt) {
2809
+ const ownerPrincipal = terminalizationOwner.principalId?.trim() || terminalizationOwner.controllerId;
2810
+ const ownerInstanceId = terminalizationOwner.controllerInstanceId?.trim() || '';
2811
+ const ownerClaimGeneration = terminalizationOwner.claimGeneration;
2812
+ if (!ownerInstanceId || typeof ownerClaimGeneration !== 'number' || ownerClaimGeneration < 1) {
2813
+ throw new Error(`WORK_CONTROLLER_TERMINALIZATION_AUTHORITY_INVALID: ${workId}`);
2814
+ }
2815
+ const fencedCompletion = withControllerSessionTerminalizationFence(
1890
2816
  { controllerHome: ctx.controllerHome, repoId: current.repositoryId },
1891
- workId,
1892
- receipt,
1893
- requestedOutcome === 'completed_no_change' ? 'completed_no_change' : 'completed_changed',
1894
- requestedOutcome === 'completed_no_change' ? 'completed_no_change' : 'repository_change',
1895
- ),
1896
- );
1897
- if (!fencedCompletion.allowed) {
1898
- throw new Error(`WORK_TERMINALIZATION_AUTHORITY_FENCED: ${workId}:${fencedCompletion.reason}`);
2817
+ {
2818
+ workId,
2819
+ actor: `work-finalize-completion:${terminalizationOwner.controllerId}:${ownerInstanceId}`,
2820
+ authority: {
2821
+ controllerId: terminalizationOwner.controllerId, controllerType: terminalizationOwner.controllerType,
2822
+ principalId: ownerPrincipal, controllerInstanceId: ownerInstanceId, claimGeneration: ownerClaimGeneration,
2823
+ },
2824
+ },
2825
+ () => completeWorkWithReceipt(
2826
+ { controllerHome: ctx.controllerHome, repoId: current.repositoryId }, workId, prevalidatedNoChangeReceipt,
2827
+ 'completed_no_change', 'completed_no_change',
2828
+ ),
2829
+ );
2830
+ if (!fencedCompletion.allowed) throw new Error(`WORK_TERMINALIZATION_AUTHORITY_FENCED: ${workId}:${fencedCompletion.reason}`);
2831
+ } else {
2832
+ completeFinalizedWorkContract({
2833
+ ctx, handle: current, contract: completionContract, args, terminalizationOwner,
2834
+ outcome: requestedOutcome === 'completed_no_change' ? 'completed_no_change' : 'completed_changed',
2835
+ });
1899
2836
  }
1900
2837
  }
1901
2838
  // Successful WorkContract completion always ends controller ownership.