@moretea-labs/forge 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +4 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  17. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  18. package/assets/plugin-registry.v1.json +3 -3
  19. package/assets/reference-configs/document-generation.md +1 -1
  20. package/assets/reference-configs/harness-overview.md +1 -1
  21. package/assets/reference-configs/sprint-contracts.md +3 -3
  22. package/assets/skill-version.json +2 -2
  23. package/assets/templates/contract.template.md +2 -7
  24. package/assets/templates/helpers/capture-plan.sh +2 -2
  25. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  26. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  27. package/assets/templates/helpers/contract-worktree.sh +1 -21
  28. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  29. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  32. package/assets/templates/helpers/new-plan.sh +1 -1
  33. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  34. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/helpers/workflow-contract.ts +5 -1
  38. package/assets/templates/plan.template.md +1 -1
  39. package/assets/templates/review.template.md +9 -34
  40. package/assets/workflow-contract.v1.json +10 -19
  41. package/docs/README.md +10 -10
  42. package/docs/architecture/CURRENT.md +178 -0
  43. package/docs/architecture/EVOLUTION.md +42 -0
  44. package/docs/architecture/current/README.md +4 -156
  45. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  46. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  47. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  48. package/docs/architecture/history/README.md +7 -0
  49. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  50. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  51. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  52. package/docs/architecture/history.md +3 -34
  53. package/docs/architecture/index.md +15 -98
  54. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  55. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  56. package/docs/architecture/versions/1.6.md +38 -0
  57. package/docs/architecture/versions/1.7.md +41 -0
  58. package/docs/forge-chatgpt-browser-engine.md +2 -2
  59. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  60. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  61. package/docs/forge-github-issue-launcher.md +1 -1
  62. package/docs/forge-plugin-management.md +1 -1
  63. package/docs/forge-runtime-self-healing-loop.md +1 -1
  64. package/docs/operations/controller-browser-plugin.md +26 -6
  65. package/docs/operations/controller-performance-and-502.md +143 -0
  66. package/docs/operations/controller-reliability-runbook.md +1 -1
  67. package/docs/operations/features.md +2 -2
  68. package/docs/operations/github-repository.md +1 -1
  69. package/docs/operations/homebrew.md +2 -2
  70. package/docs/operations/mcp-tool-exposure.md +5 -3
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Integrations.md +2 -2
  74. package/docs/wiki/Work-Lifecycle.md +1 -1
  75. package/package.json +33 -11
  76. package/scripts/activate-source-baseline.ts +17 -13
  77. package/scripts/architecture-event.ts +2 -820
  78. package/scripts/architecture-queue.sh +2 -645
  79. package/scripts/archive-architecture-request.sh +2 -242
  80. package/scripts/archive-workflow.sh +1 -291
  81. package/scripts/benchmark-background-check-overlap.ts +228 -0
  82. package/scripts/benchmark-mcp-host.sh +117 -0
  83. package/scripts/benchmark-mcp-transport.ts +170 -0
  84. package/scripts/benchmark-quality-harness.ts +146 -0
  85. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  86. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  87. package/scripts/benchmark-typescript-navigation.ts +186 -0
  88. package/scripts/benchmark-work-efficiency.ts +334 -0
  89. package/scripts/capability-config.ts +2 -392
  90. package/scripts/capability-resolver.ts +2 -503
  91. package/scripts/capture-plan.sh +1 -332
  92. package/scripts/check-agent-tooling.sh +2 -1558
  93. package/scripts/check-architecture-sync.sh +2 -371
  94. package/scripts/check-brain-manifest.sh +2 -253
  95. package/scripts/check-context-files.sh +2 -47
  96. package/scripts/check-deploy-sql-order.sh +2 -115
  97. package/scripts/check-mcp-compatibility.ts +14 -0
  98. package/scripts/check-runtime-architecture.mjs +349 -79
  99. package/scripts/check-skill-version.ts +3 -191
  100. package/scripts/check-source-duplication.mjs +61 -0
  101. package/scripts/check-stable-baseline.ts +92 -0
  102. package/scripts/check-task-sync.sh +2 -63
  103. package/scripts/check-task-workflow.sh +2 -1000
  104. package/scripts/codex-handoff-resume.sh +2 -267
  105. package/scripts/context-contract-sync.sh +2 -514
  106. package/scripts/contract-run.ts +2 -463
  107. package/scripts/contract-worktree.sh +1 -722
  108. package/scripts/ensure-task-workflow.sh +1 -1225
  109. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  110. package/scripts/inspect-project-state.ts +4 -279
  111. package/scripts/install-standalone-recovery.ts +24 -0
  112. package/scripts/lib/project-init-lib.sh +22 -46
  113. package/scripts/lib/run-forge-helper.sh +19 -0
  114. package/scripts/lib/run-forge-helper.ts +26 -0
  115. package/scripts/migrate-project-template.sh +1 -1189
  116. package/scripts/migrate-workflow-docs.ts +4 -412
  117. package/scripts/new-plan.sh +1 -180
  118. package/scripts/new-spec.sh +1 -47
  119. package/scripts/new-sprint.sh +1 -46
  120. package/scripts/plan-to-todo.sh +1 -796
  121. package/scripts/prepare-codex-handoff.sh +2 -201
  122. package/scripts/prepare-handoff.sh +1 -78
  123. package/scripts/refresh-current-status.sh +2 -467
  124. package/scripts/run-governed-gate.ts +5 -1
  125. package/scripts/select-agent-context-blocks.sh +2 -64
  126. package/scripts/ship-worktrees.sh +1 -558
  127. package/scripts/smoke-ios-physical-device.ts +4 -2
  128. package/scripts/smoke-runtime-recovery.ts +1 -35
  129. package/scripts/smoke-schedule-engine.ts +1 -20
  130. package/scripts/sprint-backlog.sh +1 -834
  131. package/scripts/stage-runtime-release.ts +2 -1
  132. package/scripts/summarize-failures.sh +2 -123
  133. package/scripts/switch-plan.sh +1 -150
  134. package/scripts/sync-brain-docs.sh +2 -379
  135. package/scripts/sync-generated-authority.mjs +175 -0
  136. package/scripts/verify-contract.sh +2 -703
  137. package/scripts/verify-forge-runtime.sh +0 -1
  138. package/scripts/verify-sprint.sh +1 -677
  139. package/scripts/workflow-contract.ts +4 -205
  140. package/scripts/workstream-sync.sh +2 -261
  141. package/src/cli/agent-jobs/executor-health.ts +1 -1
  142. package/src/cli/commands/init-hook.ts +3 -3
  143. package/src/cli/commands/mcp.ts +9 -5
  144. package/src/cli/commands/plugin.ts +12 -3
  145. package/src/cli/commands/recovery.ts +25 -3
  146. package/src/cli/commands/runtime.ts +55 -3
  147. package/src/cli/commands/security.ts +12 -1
  148. package/src/cli/commands/setup-profile.ts +31 -21
  149. package/src/cli/controller/check-runner.ts +113 -5
  150. package/src/cli/controller/composite-result.ts +1 -1
  151. package/src/cli/controller/context/focus.ts +51 -0
  152. package/src/cli/controller/context/instruction-context.ts +114 -0
  153. package/src/cli/controller/context/known-paths.ts +76 -0
  154. package/src/cli/controller/context/query-planning.ts +110 -0
  155. package/src/cli/controller/context/rule-resolution.ts +532 -0
  156. package/src/cli/controller/context/source-materializer.ts +278 -0
  157. package/src/cli/controller/context/structural.ts +16 -0
  158. package/src/cli/controller/context/types.ts +286 -0
  159. package/src/cli/controller/context-pack.ts +828 -487
  160. package/src/cli/controller/launch-agents.ts +77 -2
  161. package/src/cli/controller/operational-plan.ts +2 -2
  162. package/src/cli/controller/work-mode.ts +6 -4
  163. package/src/cli/editing/edit-session.ts +60 -7
  164. package/src/cli/github/contracts.ts +16 -0
  165. package/src/cli/github/github.ts +135 -16
  166. package/src/cli/github/session.ts +2 -1
  167. package/src/cli/installer/managed-entries.ts +15 -1
  168. package/src/cli/local-bridge/console-automations.ts +54 -78
  169. package/src/cli/local-bridge/console-view-models.ts +1 -35
  170. package/src/cli/local-bridge/facade-api.ts +37 -264
  171. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  172. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  173. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  174. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  175. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  176. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  177. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  178. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  179. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  180. package/src/cli/local-bridge/job-store.ts +1 -490
  181. package/src/cli/local-bridge/server.ts +2 -515
  182. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  183. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  184. package/src/cli/mcp/auth.ts +5 -5
  185. package/src/cli/mcp/instructions.ts +3 -2
  186. package/src/cli/mcp/legacy-tool-service.ts +10 -222
  187. package/src/cli/mcp/multi-repository.ts +16 -5
  188. package/src/cli/mcp/oauth.ts +175 -26
  189. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  190. package/src/cli/mcp/repository-tools.ts +282 -146
  191. package/src/cli/mcp/server.ts +586 -116
  192. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  193. package/src/cli/mcp/tool-contract.ts +14 -0
  194. package/src/cli/mcp/toolset-selection.ts +1 -1
  195. package/src/cli/mcp/toolset.ts +44 -7
  196. package/src/cli/mcp/transports/http.ts +124 -14
  197. package/src/cli/mcp/transports/session-registry.ts +43 -5
  198. package/src/cli/mcp/types.ts +3 -3
  199. package/src/cli/repositories/command-classifier.ts +103 -21
  200. package/src/cli/repositories/command-executor.ts +78 -526
  201. package/src/cli/repositories/command-process.ts +108 -0
  202. package/src/cli/repositories/command-scope.ts +99 -41
  203. package/src/cli/repositories/controller-home.ts +7 -8
  204. package/src/cli/repositories/git-command-executor.ts +2 -0
  205. package/src/cli/repositories/locks.ts +53 -16
  206. package/src/cli/repositories/repository-snapshot.ts +124 -0
  207. package/src/cli/repositories/selected-path-actions.ts +17 -9
  208. package/src/cli/repositories/structured-git.ts +118 -6
  209. package/src/cli/repository/inspector.ts +563 -65
  210. package/src/cli/repository/session-cache.ts +58 -17
  211. package/src/cli/v81-entry.ts +1 -4
  212. package/src/effects/fs-transaction.ts +85 -17
  213. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  214. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  215. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  216. package/src/runtime/context/lsp-client.ts +283 -0
  217. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  218. package/src/runtime/context/semantic-navigation.ts +223 -0
  219. package/src/runtime/context/swift-navigation.ts +565 -0
  220. package/src/runtime/context/typescript-navigation.ts +226 -0
  221. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  222. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
  223. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  224. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  225. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  226. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  227. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  228. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  229. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  230. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  231. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  232. package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
  233. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  234. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  235. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  236. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  237. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  238. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  239. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  240. package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
  241. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
  242. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  243. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  244. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  245. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
  246. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  247. package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
  248. package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
  249. package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
  250. package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
  251. package/src/runtime/control-plane/facade/index.ts +3 -0
  252. package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
  253. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  254. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  255. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  256. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  257. package/src/runtime/control-plane/facade/types.ts +90 -10
  258. package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
  259. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  260. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  261. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  262. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  263. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  264. package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
  265. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  266. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  267. package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
  268. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  269. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  270. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  271. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  272. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  273. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  274. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  275. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  276. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  277. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  278. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
  279. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
  280. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
  281. package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
  282. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  283. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  284. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  285. package/src/runtime/control-plane/release-retention.ts +62 -2
  286. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  287. package/src/runtime/control-plane/routing/route-policy.ts +24 -21
  288. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  289. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  290. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  291. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  292. package/src/runtime/diagnostics/performance.ts +24 -1
  293. package/src/runtime/evidence/event-ledger.ts +1 -1
  294. package/src/runtime/evidence/process-check-execution.ts +18 -0
  295. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  296. package/src/runtime/execution/jobs/store.ts +58 -4
  297. package/src/runtime/execution/jobs/types.ts +1 -1
  298. package/src/runtime/execution/managed-workspace.ts +203 -1
  299. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  300. package/src/runtime/execution/process-runtime/check-result.ts +88 -0
  301. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  302. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  303. package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
  304. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  305. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
  306. package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
  307. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  308. package/src/runtime/execution/process-runtime/runtime.ts +53 -3
  309. package/src/runtime/execution/process-runtime/store.ts +1 -0
  310. package/src/runtime/execution/process-runtime/types.ts +4 -12
  311. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  312. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  313. package/src/runtime/execution/work-activity.ts +21 -0
  314. package/src/runtime/execution/workers/executor.ts +18 -61
  315. package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
  316. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  317. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  318. package/src/runtime/gateway/mcp/router.ts +186 -84
  319. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
  320. package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
  321. package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
  322. package/src/runtime/plugins/browser-adapter.ts +2662 -829
  323. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  324. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  325. package/src/runtime/plugins/browser-handoff.ts +110 -5
  326. package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
  327. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  328. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  329. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  330. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  331. package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
  332. package/src/runtime/plugins/browser-runtime.ts +193 -0
  333. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  334. package/src/runtime/plugins/browser-session-store.ts +121 -0
  335. package/src/runtime/plugins/browser-session-types.ts +98 -0
  336. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  337. package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
  338. package/src/runtime/plugins/external-adapter.ts +358 -3
  339. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  340. package/src/runtime/plugins/first-party-registry.ts +2 -0
  341. package/src/runtime/plugins/github-adapter.ts +125 -0
  342. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  343. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  344. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  345. package/src/runtime/plugins/ios-adapter.ts +106 -16
  346. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  347. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  348. package/src/runtime/plugins/lightweight-action.ts +153 -0
  349. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  350. package/src/runtime/plugins/macos-capability-broker.ts +47 -9
  351. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  352. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  353. package/src/runtime/plugins/resend-adapter.ts +1 -1
  354. package/src/runtime/plugins/store.ts +330 -33
  355. package/src/runtime/plugins/types.ts +38 -3
  356. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  357. package/src/runtime/projections/git-status-sampler.ts +16 -16
  358. package/src/runtime/projections/materialized-view.ts +28 -10
  359. package/src/runtime/recovery/maintenance-executor.ts +426 -25
  360. package/src/runtime/root/mcp-transport.ts +180 -10
  361. package/src/runtime/root/package-connector-service.ts +230 -10
  362. package/src/runtime/root/package-runtime-release.ts +143 -17
  363. package/src/runtime/root/package-runtime-service.ts +461 -22
  364. package/src/runtime/root/release-manifest.ts +144 -18
  365. package/src/runtime/root/release-materialize.ts +125 -32
  366. package/src/runtime/root/release-store.ts +48 -11
  367. package/src/runtime/root/runtime.ts +23 -5
  368. package/src/runtime/root/service.ts +100 -29
  369. package/src/runtime/root/types.ts +12 -0
  370. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  371. package/src/runtime/shared/trusted-executable.ts +62 -0
  372. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  373. package/src/runtime/standalone-recovery/core.ts +1079 -240
  374. package/src/runtime/standalone-recovery/entry.ts +100 -24
  375. package/src/runtime/standalone-recovery/installer.ts +20 -4
  376. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  377. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  378. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  379. package/src/runtime/workflow/schedules/engine.ts +364 -202
  380. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  381. package/src/runtime/workflow/schedules/settlement.ts +64 -17
  382. package/src/runtime/workflow/schedules/store.ts +209 -19
  383. package/src/runtime/workflow/schedules/types.ts +2 -4
  384. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  385. package/src/testing/test-governance.ts +8 -40
  386. package/assets/reference-configs/heartbeat-triage.md +0 -57
  387. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  388. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  389. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  390. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  391. package/docs/architecture/current/architecture-invariants.md +0 -331
  392. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  393. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  394. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  395. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  396. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  397. package/docs/architecture/current/entity-model.md +0 -612
  398. package/docs/architecture/current/external-plugin-governance.md +0 -396
  399. package/docs/architecture/current/failure-recovery.md +0 -447
  400. package/docs/architecture/current/governance.md +0 -213
  401. package/docs/architecture/current/human-interaction-plane.md +0 -87
  402. package/docs/architecture/current/implementation-status.md +0 -128
  403. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  404. package/docs/architecture/current/legacy-governance.md +0 -58
  405. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  406. package/docs/architecture/current/migration-roadmap.md +0 -105
  407. package/docs/architecture/current/multi-repository-execution.md +0 -371
  408. package/docs/architecture/current/operations-runbook.md +0 -45
  409. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  410. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  411. package/docs/architecture/current/repository-access-modes.md +0 -102
  412. package/docs/architecture/current/route-policy.md +0 -15
  413. package/docs/architecture/current/route-session-concurrency.md +0 -85
  414. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  415. package/docs/architecture/current/runtime-directory-map.md +0 -49
  416. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  417. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  418. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  419. package/docs/architecture/current/system-overview.md +0 -169
  420. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  421. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  422. package/docs/architecture/current/thin-harness-v1.md +0 -321
  423. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  424. package/docs/forge-autonomous-goal-loop.md +0 -9
  425. package/docs/forge-provider-configuration.md +0 -7
  426. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  427. package/docs/operations/gmail-assistant-routines.md +0 -186
  428. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  429. package/docs/operations/provider-configuration.md +0 -31
  430. package/scripts/harness-trace-grade.sh +0 -150
  431. package/scripts/heartbeat-triage.sh +0 -361
  432. package/scripts/maintenance-triage.sh +0 -167
  433. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  434. package/src/cli/repositories/goal-registry.ts +0 -239
  435. package/src/runtime/assistant/action-proposals.ts +0 -279
  436. package/src/runtime/assistant/intent.ts +0 -395
  437. package/src/runtime/assistant/model-provider.ts +0 -460
  438. package/src/runtime/assistant/openapi.ts +0 -223
  439. package/src/runtime/assistant/policy.ts +0 -89
  440. package/src/runtime/assistant/readiness.ts +0 -258
  441. package/src/runtime/assistant/routine-runtime.ts +0 -477
  442. package/src/runtime/assistant/schedule-binding.ts +0 -171
  443. package/src/runtime/assistant/standing-grants.ts +0 -375
  444. package/src/runtime/assistant/store.ts +0 -243
  445. package/src/runtime/assistant/types.ts +0 -135
  446. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  447. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  448. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  449. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  450. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  451. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  452. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  453. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  454. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  455. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  456. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  457. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  458. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  459. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  460. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  461. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  462. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  463. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  464. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  465. package/src/runtime/workflow/findings/store.ts +0 -204
  466. package/src/runtime/workflow/findings/types.ts +0 -58
  467. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  468. package/src/runtime/workflow/portfolio/store.ts +0 -185
  469. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -0,0 +1,226 @@
1
+ import { statSync } from 'fs';
2
+ import { dirname, relative, resolve } from 'path';
3
+ import * as ts from 'typescript';
4
+
5
+ export type TypeScriptNavigationKind = 'definition' | 'references' | 'implementations';
6
+
7
+ export interface TypeScriptNavigationRequest {
8
+ navigation: TypeScriptNavigationKind;
9
+ path: string;
10
+ line: number;
11
+ column: number;
12
+ tsconfigPath?: string;
13
+ }
14
+
15
+ export interface TypeScriptNavigationLocation {
16
+ path: string;
17
+ line: number;
18
+ column: number;
19
+ name?: string;
20
+ kind?: string;
21
+ }
22
+
23
+ export interface TypeScriptNavigationResult {
24
+ navigation: TypeScriptNavigationKind;
25
+ target: { path: string; line: number; column: number };
26
+ locations: TypeScriptNavigationLocation[];
27
+ }
28
+
29
+ export interface TypeScriptNavigationAccess {
30
+ /** Stable identity for one read-policy scope. Restricted and unrestricted projects must never share a Language Service. */
31
+ cacheScope: string;
32
+ /** Source identity observed by the caller. A different identity must not reuse project membership. */
33
+ sourceIdentity?: string;
34
+ /** Return true only for repository-relative paths that this navigation call may read. */
35
+ allowRepositoryPath(relativePath: string): boolean;
36
+ }
37
+
38
+ interface CachedProject {
39
+ repoRoot: string;
40
+ configPath: string;
41
+ service: ts.LanguageService;
42
+ }
43
+
44
+ const projects = new Map<string, CachedProject>();
45
+ const MAX_CACHED_TYPESCRIPT_PROJECTS = 32;
46
+
47
+ function normalizePath(path: string): string {
48
+ return path.replace(/\\/g, '/');
49
+ }
50
+
51
+ function scriptVersion(path: string): string {
52
+ try {
53
+ const stat = statSync(path);
54
+ return `${stat.mtimeMs}:${stat.size}`;
55
+ } catch {
56
+ return 'missing';
57
+ }
58
+ }
59
+
60
+ function repositoryRelativePath(repoRoot: string, fileName: string): string | undefined {
61
+ const normalized = normalizePath(relative(repoRoot, resolve(fileName)));
62
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('../')) return undefined;
63
+ return normalized;
64
+ }
65
+
66
+ function loadProject(repoRoot: string, tsconfigPath = 'tsconfig.json', access?: TypeScriptNavigationAccess): CachedProject {
67
+ const root = resolve(repoRoot);
68
+ const configPath = resolve(root, tsconfigPath);
69
+ const configRelative = repositoryRelativePath(root, configPath);
70
+ if (!configRelative) throw new Error('TypeScript navigation tsconfig must be inside the repository.');
71
+ if (access && !access.allowRepositoryPath(configRelative)) {
72
+ throw new Error(`TypeScript navigation tsconfig is denied by read policy: ${configRelative}.`);
73
+ }
74
+ const cacheKey = `${root}\0${configPath}\0${scriptVersion(configPath)}\0${access?.cacheScope ?? 'unrestricted'}\0${access?.sourceIdentity ?? 'unbound-source'}`;
75
+ const cached = projects.get(cacheKey);
76
+ if (cached) {
77
+ projects.delete(cacheKey);
78
+ projects.set(cacheKey, cached);
79
+ return cached;
80
+ }
81
+
82
+ const canReadAbsolute = (fileName: string): boolean => {
83
+ if (!access) return true;
84
+ const repoRelative = repositoryRelativePath(root, fileName);
85
+ return repoRelative === undefined || access.allowRepositoryPath(repoRelative);
86
+ };
87
+ const readAllowedFile = (fileName: string): string | undefined => canReadAbsolute(fileName) ? ts.sys.readFile(fileName) : undefined;
88
+ const fileExistsAllowed = (fileName: string): boolean => canReadAbsolute(fileName) && ts.sys.fileExists(fileName);
89
+ const readDirectoryAllowed: typeof ts.sys.readDirectory = (path, extensions, exclude, include, depth) =>
90
+ ts.sys.readDirectory(path, extensions, exclude, include, depth).filter(canReadAbsolute);
91
+
92
+ const config = ts.readConfigFile(configPath, readAllowedFile);
93
+ if (config.error) {
94
+ throw new Error(ts.flattenDiagnosticMessageText(config.error.messageText, '\n'));
95
+ }
96
+ const parseHost: ts.ParseConfigHost = {
97
+ useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
98
+ readDirectory: readDirectoryAllowed,
99
+ fileExists: fileExistsAllowed,
100
+ readFile: readAllowedFile,
101
+ };
102
+ const parsed = ts.parseJsonConfigFileContent(config.config, parseHost, dirname(configPath), undefined, configPath);
103
+ if (parsed.errors.length > 0) {
104
+ throw new Error(parsed.errors.map((entry) => ts.flattenDiagnosticMessageText(entry.messageText, '\n')).join('\n'));
105
+ }
106
+ const scriptFileNames = parsed.fileNames.filter(canReadAbsolute);
107
+
108
+ const host: ts.LanguageServiceHost = {
109
+ getCompilationSettings: () => parsed.options,
110
+ getScriptFileNames: () => scriptFileNames,
111
+ getScriptVersion: scriptVersion,
112
+ getScriptSnapshot: (fileName) => {
113
+ if (!fileExistsAllowed(fileName)) return undefined;
114
+ const content = readAllowedFile(fileName);
115
+ return content === undefined ? undefined : ts.ScriptSnapshot.fromString(content);
116
+ },
117
+ getCurrentDirectory: () => root,
118
+ getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),
119
+ fileExists: fileExistsAllowed,
120
+ readFile: readAllowedFile,
121
+ readDirectory: readDirectoryAllowed,
122
+ directoryExists: ts.sys.directoryExists,
123
+ getDirectories: ts.sys.getDirectories,
124
+ realpath: ts.sys.realpath,
125
+ useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
126
+ getNewLine: () => ts.sys.newLine,
127
+ };
128
+
129
+ const project = {
130
+ repoRoot: root,
131
+ configPath,
132
+ service: ts.createLanguageService(host, ts.createDocumentRegistry()),
133
+ };
134
+ projects.set(cacheKey, project);
135
+ while (projects.size > MAX_CACHED_TYPESCRIPT_PROJECTS) {
136
+ const oldestKey = projects.keys().next().value as string | undefined;
137
+ if (!oldestKey) break;
138
+ const oldest = projects.get(oldestKey);
139
+ projects.delete(oldestKey);
140
+ oldest?.service.dispose();
141
+ }
142
+ return project;
143
+ }
144
+
145
+ function sourcePosition(project: CachedProject, program: ts.Program, fileName: string, line: number, column: number): number {
146
+ if (!Number.isInteger(line) || line < 1 || !Number.isInteger(column) || column < 1) {
147
+ throw new Error('TypeScript navigation line and column must be positive 1-based integers.');
148
+ }
149
+ const source = program.getSourceFile(fileName);
150
+ if (!source) throw new Error(`TypeScript project does not include ${normalizePath(relative(project.repoRoot, fileName))}.`);
151
+ if (line > source.getLineAndCharacterOfPosition(source.getEnd()).line + 1) {
152
+ throw new Error(`TypeScript navigation line ${line} is outside ${normalizePath(relative(project.repoRoot, fileName))}.`);
153
+ }
154
+ return source.getPositionOfLineAndCharacter(line - 1, column - 1);
155
+ }
156
+
157
+ function location(
158
+ project: CachedProject,
159
+ program: ts.Program,
160
+ fileName: string,
161
+ textSpan: ts.TextSpan,
162
+ name?: string,
163
+ kind?: ts.ScriptElementKind,
164
+ ): TypeScriptNavigationLocation {
165
+ const source = program.getSourceFile(fileName);
166
+ if (!source) {
167
+ return { path: normalizePath(relative(project.repoRoot, fileName)), line: 1, column: 1, ...(name ? { name } : {}), ...(kind ? { kind } : {}) };
168
+ }
169
+ const point = source.getLineAndCharacterOfPosition(textSpan.start);
170
+ return {
171
+ path: normalizePath(relative(project.repoRoot, fileName)),
172
+ line: point.line + 1,
173
+ column: point.character + 1,
174
+ ...(name ? { name } : {}),
175
+ ...(kind ? { kind } : {}),
176
+ };
177
+ }
178
+
179
+ function dedupe(locations: TypeScriptNavigationLocation[]): TypeScriptNavigationLocation[] {
180
+ const seen = new Set<string>();
181
+ return locations.filter((entry) => {
182
+ const key = `${entry.path}:${entry.line}:${entry.column}:${entry.name ?? ''}:${entry.kind ?? ''}`;
183
+ if (seen.has(key)) return false;
184
+ seen.add(key);
185
+ return true;
186
+ });
187
+ }
188
+
189
+ export function navigateTypeScriptSymbol(
190
+ repoRoot: string,
191
+ request: TypeScriptNavigationRequest,
192
+ access?: TypeScriptNavigationAccess,
193
+ ): TypeScriptNavigationResult {
194
+ const project = loadProject(repoRoot, request.tsconfigPath, access);
195
+ const fileName = resolve(project.repoRoot, request.path);
196
+ // A references response may materialize hundreds of locations. Acquire the
197
+ // Language Service Program once per navigation so that location formatting
198
+ // does not repeatedly re-enter the service while the result is built.
199
+ const program = project.service.getProgram();
200
+ if (!program) throw new Error('TypeScript Language Service did not produce a Program.');
201
+ const position = sourcePosition(project, program, fileName, request.line, request.column);
202
+ let locations: TypeScriptNavigationLocation[] = [];
203
+
204
+ if (request.navigation === 'definition') {
205
+ locations = (project.service.getDefinitionAtPosition(fileName, position) ?? [])
206
+ .map((entry) => location(project, program, entry.fileName, entry.textSpan, entry.name, entry.kind));
207
+ } else if (request.navigation === 'implementations') {
208
+ locations = (project.service.getImplementationAtPosition(fileName, position) ?? [])
209
+ .map((entry) => location(project, program, entry.fileName, entry.textSpan, undefined, entry.kind));
210
+ } else {
211
+ locations = (project.service.findReferences(fileName, position) ?? []).flatMap((group) =>
212
+ group.references.map((entry) => location(project, program, entry.fileName, entry.textSpan, group.definition.name, group.definition.kind)),
213
+ );
214
+ }
215
+
216
+ return {
217
+ navigation: request.navigation,
218
+ target: { path: normalizePath(request.path), line: request.line, column: request.column },
219
+ locations: dedupe(locations),
220
+ };
221
+ }
222
+
223
+ export function clearTypeScriptNavigationCache(): void {
224
+ for (const project of projects.values()) project.service.dispose();
225
+ projects.clear();
226
+ }
@@ -65,3 +65,31 @@ export function compareExecutionJobDispatchRanks(
65
65
  || left.queuedAtMs - right.queuedAtMs
66
66
  || left.jobId.localeCompare(right.jobId);
67
67
  }
68
+
69
+ export function selectExecutionJobDispatchRepositories(
70
+ activeJobs: readonly ExecutionJob[],
71
+ scheduleNow: number,
72
+ lastRepoDispatch: ReadonlyMap<string, number>,
73
+ ): string[] {
74
+ const waiting = activeJobs.filter(isExecutionJobDispatchCandidate);
75
+ const rankByJobId = new Map(
76
+ waiting.map((job) => [job.jobId, rankExecutionJobForDispatch(job, scheduleNow)] as const),
77
+ );
78
+ const compareWaiting = (left: ExecutionJob, right: ExecutionJob): number =>
79
+ compareExecutionJobDispatchRanks(rankByJobId.get(left.jobId)!, rankByJobId.get(right.jobId)!);
80
+ const topByRepo = new Map<string, ExecutionJob>();
81
+ for (const job of waiting.slice().sort(compareWaiting)) {
82
+ if (!topByRepo.has(job.repoId)) topByRepo.set(job.repoId, job);
83
+ }
84
+ return [...topByRepo.keys()].sort((left, right) => {
85
+ const leftRank = rankByJobId.get(topByRepo.get(left)!.jobId)!;
86
+ const rightRank = rankByJobId.get(topByRepo.get(right)!.jobId)!;
87
+ const priority = leftRank.effectivePriority - rightRank.effectivePriority;
88
+ if (priority !== 0) return priority;
89
+ const fairness = (lastRepoDispatch.get(left) ?? 0) - (lastRepoDispatch.get(right) ?? 0);
90
+ return fairness
91
+ || leftRank.queuedAtMs - rightRank.queuedAtMs
92
+ || leftRank.jobId.localeCompare(rightRank.jobId)
93
+ || left.localeCompare(right);
94
+ });
95
+ }
@@ -1,11 +1,12 @@
1
1
  import { createHash } from 'crypto';
2
2
  import { getEditSession, listEditSessions, type EditSession } from '../../../cli/editing/edit-session';
3
- import { globMatches } from '../../../cli/mcp/paths';
4
3
  import { runProcess } from '../../../effects/process-runner';
5
- import { completeRequirementFromWork } from '../persistence/requirement-store';
6
- import { getWorkContract, recordWorkCompletionReceipt, updateWorkContract } from '../facade/work-contract-store';
4
+ import { getWorkContract, updateWorkContract } from '../facade/work-contract-store';
5
+ import { completeWorkWithReceipt } from './work-completion-authority';
7
6
  import { isDirectEditWorkCompletionReceipt, isTerminalWorkContractStatus, type DirectEditWorkCompletionReceipt, type WorkReconciliationRecord } from '../facade/types';
8
- import { effectiveVerificationEvidence } from './verification-evidence';
7
+ import { historicalVerificationEvidenceAtRevision } from './verification-evidence';
8
+ import { readWorkHandle } from './work-handle-store';
9
+ import { assertWorkPathsWithinScope, findWorkPathScopeViolation } from './work-path-scope';
9
10
 
10
11
  export interface DirectEditWorkCompletionReconciliation {
11
12
  completedWorkIds: string[];
@@ -101,6 +102,15 @@ export function reconcileFinalizedDirectEditWorksAfterCommit(input: {
101
102
  skipped.push({ sessionId: session.sessionId, workId: session.workId, reason: 'configured_checks_not_passed' });
102
103
  continue;
103
104
  }
105
+ const scopeViolation = findWorkPathScopeViolation(work, paths);
106
+ if (scopeViolation) {
107
+ skipped.push({
108
+ sessionId: session.sessionId,
109
+ workId: session.workId,
110
+ reason: `${scopeViolation.kind === 'forbidden' ? 'work_scope_forbidden' : 'work_scope_out_of_scope'}:${scopeViolation.path}`,
111
+ });
112
+ continue;
113
+ }
104
114
  const status = git(input.repoRoot, ['status', '--porcelain=v1', '--', ...paths]);
105
115
  if (!status.ok || status.stdout.trim()) {
106
116
  skipped.push({ sessionId: session.sessionId, workId: session.workId, reason: 'owned_paths_not_clean' });
@@ -140,7 +150,7 @@ export function reconcileFinalizedDirectEditWorksAfterCommit(input: {
140
150
  verifiedAt: session.verifiedAt ?? session.finalizedAt ?? recordedAt,
141
151
  recordedAt,
142
152
  };
143
- recordWorkCompletionReceipt(
153
+ completeWorkWithReceipt(
144
154
  { controllerHome: input.controllerHome, repoId: input.repoId },
145
155
  session.workId,
146
156
  receipt,
@@ -209,7 +219,16 @@ export function acceptReviewedDirectEditWorkReconciliation(input: ReviewedDirect
209
219
  }
210
220
  throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_WORK_TERMINAL: ${input.workId}`);
211
221
  }
212
- if (isTerminalWorkContractStatus(work.status) || work.workKind !== 'repository_change') {
222
+ const failedHandle = work.status === 'failed'
223
+ ? readWorkHandle(input.controllerHome, input.repoId, input.workId)
224
+ : undefined;
225
+ const failedReviewedRecovery = work.workKind === 'repository_change'
226
+ && work.status === 'failed'
227
+ && failedHandle?.managedWorktree === false
228
+ && failedHandle.state === 'failed'
229
+ && failedHandle.finalization.validation === 'failed'
230
+ && String(failedHandle.finalization.lastError ?? failedHandle.failureReason ?? '').includes('WORK_HANDLE_HEAD_CHANGED');
231
+ if ((isTerminalWorkContractStatus(work.status) && !failedReviewedRecovery) || work.workKind !== 'repository_change') {
213
232
  throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_WORK_NOT_ELIGIBLE: ${input.workId}`);
214
233
  }
215
234
  if (!work.baseRevision?.trim()) throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_BASE_REVISION_MISSING');
@@ -226,25 +245,39 @@ export function acceptReviewedDirectEditWorkReconciliation(input: ReviewedDirect
226
245
  }
227
246
 
228
247
  const comparedPaths = normalizedComparedPaths(input.comparedPaths);
229
- const actualPaths = comparedRevisionPaths(input.repoRoot, baseRevision, targetRevision);
248
+ let comparisonBaseRevision = baseRevision;
249
+ if (failedReviewedRecovery) {
250
+ const targetParent = exactCommit(input.repoRoot, `${targetRevision}^`, 'TARGET_PARENT_REVISION');
251
+ if (!git(input.repoRoot, ['merge-base', '--is-ancestor', baseRevision, targetParent]).ok) {
252
+ throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_TARGET_PARENT_NOT_DESCENDANT');
253
+ }
254
+ comparisonBaseRevision = targetParent;
255
+ }
256
+ const actualPaths = comparedRevisionPaths(input.repoRoot, comparisonBaseRevision, targetRevision);
230
257
  if (comparedPaths.length !== actualPaths.length || comparedPaths.some((path, index) => path !== actualPaths[index])) {
231
258
  throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_PATH_COMPARISON_MISMATCH');
232
259
  }
233
- for (const path of comparedPaths) {
234
- if (work.forbiddenPaths.some((pattern) => globMatches(pattern, path))) throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_FORBIDDEN_PATH: ${path}`);
235
- if (work.allowedPaths.length > 0 && !work.allowedPaths.some((pattern) => globMatches(pattern, path))) throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_PATH_OUT_OF_SCOPE: ${path}`);
236
- }
260
+ assertWorkPathsWithinScope(work, comparedPaths, {
261
+ forbidden: 'DIRECT_EDIT_WORK_RECONCILIATION_FORBIDDEN_PATH',
262
+ outOfScope: 'DIRECT_EDIT_WORK_RECONCILIATION_PATH_OUT_OF_SCOPE',
263
+ });
237
264
  const dirty = git(input.repoRoot, ['status', '--porcelain=v1', '--', ...comparedPaths]);
238
265
  if (!dirty.ok || dirty.stdout.trim()) throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_OWNED_PATHS_DIRTY');
239
266
 
240
267
  let verifiedAt = new Date().toISOString();
241
268
  for (const checkId of work.checks) {
242
- const current = effectiveVerificationEvidence(work.checkRefs, {
269
+ const candidates = historicalVerificationEvidenceAtRevision(work.checkRefs, {
243
270
  sourceRevision: targetRevision,
271
+ repoId: input.repoId,
272
+ workId: work.workId,
273
+ checkoutId: work.checkoutId,
244
274
  checkId,
245
275
  requestedChecks: work.checks,
246
- }).find((entry) => entry.current && entry.record.outcome === 'valid_pass');
247
- if (!current) throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_CHECK_EVIDENCE_STALE: ${checkId}`);
276
+ }).filter((entry) => entry.current && entry.record.outcome === 'valid_pass');
277
+ const distinctReceipts = new Map(candidates.map((entry) => [entry.record.receipt!.receiptId, entry]));
278
+ if (distinctReceipts.size === 0) throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_CHECK_EVIDENCE_STALE: ${checkId}`);
279
+ if (distinctReceipts.size > 1) throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_CHECK_EVIDENCE_AMBIGUOUS: ${checkId}`);
280
+ const current = [...distinctReceipts.values()][0]!;
248
281
  verifiedAt = current.record.completedAt ?? current.record.recordedAt ?? verifiedAt;
249
282
  }
250
283
 
@@ -294,13 +327,12 @@ export function acceptReviewedDirectEditWorkReconciliation(input: ReviewedDirect
294
327
  verifiedAt,
295
328
  recordedAt,
296
329
  };
297
- const recorded = recordWorkCompletionReceipt(
330
+ completeWorkWithReceipt(
298
331
  { controllerHome: input.controllerHome, repoId: input.repoId },
299
332
  input.workId,
300
333
  receipt,
301
334
  'completed_changed',
302
335
  'repository_change',
303
336
  );
304
- if (recorded.requirementId) completeRequirementFromWork({ controllerHome: input.controllerHome }, { requirementId: recorded.requirementId, work: recorded });
305
337
  return { workId: input.workId, reconciliation, receipt };
306
338
  }
@@ -9,6 +9,7 @@ import {
9
9
  listControllerChecks,
10
10
  } from '../../../cli/controller/check-runner';
11
11
  import type { RepositoryRecord } from '../../../cli/repositories/types';
12
+ import { selectRepositoryCheckout } from '../../../cli/repositories/registry';
12
13
  import { executionIdentityForRepository } from './execution-identity';
13
14
  import {
14
15
  claimsForCheck,
@@ -266,6 +267,57 @@ function failedRun(
266
267
  return coordinatorResult(controllerHome, failed);
267
268
  }
268
269
 
270
+ export function resolveEditValidationRepository(
271
+ repository: RepositoryRecord,
272
+ editSessionId: string,
273
+ ): { repository: RepositoryRecord; session: EditSession } {
274
+ const checkoutIds = [...new Set([
275
+ repository.activeCheckoutId,
276
+ ...repository.checkouts.map((checkout) => checkout.checkoutId),
277
+ ].filter(Boolean))];
278
+ for (const checkoutId of checkoutIds) {
279
+ let candidate: RepositoryRecord;
280
+ try {
281
+ candidate = selectRepositoryCheckout(repository, checkoutId);
282
+ } catch {
283
+ continue;
284
+ }
285
+ let session: EditSession;
286
+ try {
287
+ session = getEditSession(candidate.canonicalRoot, editSessionId);
288
+ } catch (error) {
289
+ if (error instanceof Error && error.message === `edit session not found: ${editSessionId}`) continue;
290
+ throw error;
291
+ }
292
+ if (session.repoId && session.repoId !== candidate.repoId) {
293
+ throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: repoId ${session.repoId} != ${candidate.repoId}`);
294
+ }
295
+ if (session.checkoutId && session.checkoutId !== candidate.activeCheckoutId) {
296
+ throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: checkoutId ${session.checkoutId} != ${candidate.activeCheckoutId}`);
297
+ }
298
+ return { repository: candidate, session };
299
+ }
300
+ throw new Error(`edit session not found: ${editSessionId}`);
301
+ }
302
+
303
+ function validationRunRepository(
304
+ repository: RepositoryRecord,
305
+ run: EditValidationRunState,
306
+ ): RepositoryRecord {
307
+ if (run.repositoryId !== repository.repoId) {
308
+ throw new Error(`EDIT_VALIDATION_REPOSITORY_MISMATCH: ${run.repositoryId} != ${repository.repoId}`);
309
+ }
310
+ const candidate = selectRepositoryCheckout(repository, run.checkoutId);
311
+ const session = getEditSession(candidate.canonicalRoot, run.editSessionId);
312
+ if (session.repoId && session.repoId !== run.repositoryId) {
313
+ throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: repoId ${session.repoId} != ${run.repositoryId}`);
314
+ }
315
+ if (session.checkoutId && session.checkoutId !== run.checkoutId) {
316
+ throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: checkoutId ${session.checkoutId} != ${run.checkoutId}`);
317
+ }
318
+ return candidate;
319
+ }
320
+
269
321
  function receiptFor(
270
322
  controllerHome: string,
271
323
  repository: RepositoryRecord,
@@ -312,10 +364,18 @@ export async function reconcileEditValidationRun(
312
364
  ): Promise<EditValidationCoordinatorResult> {
313
365
  let run = readRun(controllerHome, repository.repoId, runInput.validationId) ?? runInput;
314
366
  if (run.status !== 'running') return coordinatorResult(controllerHome, run);
315
- if (run.checkoutId !== repository.activeCheckoutId) {
316
- return failedRun(controllerHome, run, 'EDIT_VALIDATION_CHECKOUT_CHANGED', `Validation checkout ${run.checkoutId} no longer matches ${repository.activeCheckoutId}.`);
367
+ let validationRepository: RepositoryRecord;
368
+ try {
369
+ validationRepository = validationRunRepository(repository, run);
370
+ } catch (error) {
371
+ return failedRun(
372
+ controllerHome,
373
+ run,
374
+ 'EDIT_VALIDATION_CHECKOUT_UNAVAILABLE',
375
+ error instanceof Error ? error.message : String(error),
376
+ );
317
377
  }
318
- const session = getEditSession(repository.canonicalRoot, run.editSessionId);
378
+ const session = getEditSession(validationRepository.canonicalRoot, run.editSessionId);
319
379
  if (session.currentRevision !== run.editRevision) {
320
380
  return failedRun(controllerHome, run, 'EDIT_VALIDATION_STALE_REVISION', `Validation targets revision ${run.editRevision}, current edit revision is ${session.currentRevision}.`);
321
381
  }
@@ -334,10 +394,10 @@ export async function reconcileEditValidationRun(
334
394
  const checkRequestId = `${run.requestId}:check:${index + 1}:${createHash('sha256').update(checkId).digest('hex').slice(0, 12)}`;
335
395
  const facade = await runPersistedCheckViaProcessRuntime({
336
396
  controllerHome,
337
- repoId: repository.repoId,
338
- checkoutId: repository.activeCheckoutId,
339
- repoRoot: repository.canonicalRoot,
340
- executionIdentity: executionIdentityForRepository(repository),
397
+ repoId: validationRepository.repoId,
398
+ checkoutId: validationRepository.activeCheckoutId,
399
+ repoRoot: validationRepository.canonicalRoot,
400
+ executionIdentity: executionIdentityForRepository(validationRepository),
341
401
  checkId,
342
402
  timeoutMs: run.timeoutMs,
343
403
  interactiveWaitMs: 0,
@@ -384,10 +444,10 @@ export async function reconcileEditValidationRun(
384
444
  }
385
445
 
386
446
  try {
387
- const receipts = run.checkIds.map((checkId) => receiptFor(controllerHome, repository, run, checkId));
388
- const checked = recordEditSessionProcessCheckReceipts(repository.canonicalRoot, run.editSessionId, {
389
- repoId: repository.repoId,
390
- checkoutId: repository.activeCheckoutId,
447
+ const receipts = run.checkIds.map((checkId) => receiptFor(controllerHome, validationRepository, run, checkId));
448
+ const checked = recordEditSessionProcessCheckReceipts(validationRepository.canonicalRoot, run.editSessionId, {
449
+ repoId: validationRepository.repoId,
450
+ checkoutId: validationRepository.activeCheckoutId,
391
451
  receipts,
392
452
  reviewer: run.reviewer,
393
453
  note: run.note,
@@ -415,7 +475,9 @@ export async function startOrJoinEditValidation(
415
475
  repository: RepositoryRecord,
416
476
  input: EditValidationStartInput,
417
477
  ): Promise<EditValidationCoordinatorResult> {
418
- const session = getEditSession(repository.canonicalRoot, input.editSessionId);
478
+ const resolved = resolveEditValidationRepository(repository, input.editSessionId);
479
+ const validationRepository = resolved.repository;
480
+ const session = resolved.session;
419
481
  const checkIds = normalized(input.checkIds ?? session.requestedChecks);
420
482
  const requestId = input.validationRequestId?.trim()
421
483
  || input.requestId?.trim()
@@ -426,14 +488,14 @@ export async function startOrJoinEditValidation(
426
488
  if (existing.checkIds.join('\n') !== checkIds.join('\n')) {
427
489
  return failedRun(controllerHome, existing, 'EDIT_VALIDATION_REQUEST_CONFLICT', 'The validation request id is already bound to a different check set.');
428
490
  }
429
- return reconcileEditValidationRun(controllerHome, repository, existing);
491
+ return reconcileEditValidationRun(controllerHome, validationRepository, existing);
430
492
  }
431
493
 
432
494
  if (checkIds.length === 0) {
433
495
  try {
434
- const checked = recordEditSessionProcessCheckReceipts(repository.canonicalRoot, session.sessionId, {
435
- repoId: repository.repoId,
436
- checkoutId: repository.activeCheckoutId,
496
+ const checked = recordEditSessionProcessCheckReceipts(validationRepository.canonicalRoot, session.sessionId, {
497
+ repoId: validationRepository.repoId,
498
+ checkoutId: validationRepository.activeCheckoutId,
437
499
  receipts: [],
438
500
  reviewer: input.reviewer,
439
501
  note: input.note,
@@ -441,8 +503,8 @@ export async function startOrJoinEditValidation(
441
503
  const completed = saveRun(controllerHome, {
442
504
  schemaVersion: 1,
443
505
  validationId: id,
444
- repositoryId: repository.repoId,
445
- checkoutId: repository.activeCheckoutId,
506
+ repositoryId: validationRepository.repoId,
507
+ checkoutId: validationRepository.activeCheckoutId,
446
508
  editSessionId: session.sessionId,
447
509
  editRevision: session.currentRevision,
448
510
  requestId,
@@ -464,8 +526,8 @@ export async function startOrJoinEditValidation(
464
526
  const run: EditValidationRunState = {
465
527
  schemaVersion: 1,
466
528
  validationId: id,
467
- repositoryId: repository.repoId,
468
- checkoutId: repository.activeCheckoutId,
529
+ repositoryId: validationRepository.repoId,
530
+ checkoutId: validationRepository.activeCheckoutId,
469
531
  editSessionId: session.sessionId,
470
532
  editRevision: session.currentRevision,
471
533
  requestId,
@@ -487,13 +549,13 @@ export async function startOrJoinEditValidation(
487
549
  const run = saveRun(controllerHome, {
488
550
  schemaVersion: 1,
489
551
  validationId: id,
490
- repositoryId: repository.repoId,
491
- checkoutId: repository.activeCheckoutId,
552
+ repositoryId: validationRepository.repoId,
553
+ checkoutId: validationRepository.activeCheckoutId,
492
554
  editSessionId: session.sessionId,
493
555
  editRevision: session.currentRevision,
494
556
  requestId,
495
557
  checkIds,
496
- lanes: checkLanes(repository, checkIds),
558
+ lanes: checkLanes(validationRepository, checkIds),
497
559
  processes: {},
498
560
  reviewer: input.reviewer,
499
561
  note: input.note,
@@ -505,7 +567,7 @@ export async function startOrJoinEditValidation(
505
567
  createdAt: now(),
506
568
  updatedAt: now(),
507
569
  });
508
- return reconcileEditValidationRun(controllerHome, repository, run);
570
+ return reconcileEditValidationRun(controllerHome, validationRepository, run);
509
571
  }
510
572
 
511
573
  export interface PendingEditValidationSummary {