@moretea-labs/forge 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +46 -0
  3. package/CLAUDE.md +2 -0
  4. package/DESIGN.md +7 -2
  5. package/README.md +4 -4
  6. package/README.zh-CN.md +2 -2
  7. package/SKILL.md +2 -2
  8. package/THIRD_PARTY_NOTICES.md +1 -1
  9. package/agents/openai.yaml +1 -1
  10. package/assets/hooks/hook-input.sh +11 -8
  11. package/assets/hooks/lib/session-state.sh +10 -7
  12. package/assets/hooks/lib/workflow-state.sh +13 -177
  13. package/assets/hooks/post-tool-observer.sh +8 -2
  14. package/assets/hooks/pre-edit-guard.sh +8 -39
  15. package/assets/hooks/prompt-guard.sh +7 -83
  16. package/assets/hooks/session-start-context.sh +56 -1
  17. package/assets/partials/05-workflow.partial.md +1 -1
  18. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  19. package/assets/plugin-registry.v1.json +54 -3
  20. package/assets/reference-configs/document-generation.md +1 -1
  21. package/assets/reference-configs/harness-overview.md +1 -1
  22. package/assets/reference-configs/hook-operations.md +1 -1
  23. package/assets/reference-configs/sprint-contracts.md +3 -3
  24. package/assets/skill-version.json +2 -2
  25. package/assets/templates/contract.template.md +2 -7
  26. package/assets/templates/helpers/capture-plan.sh +2 -2
  27. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  28. package/assets/templates/helpers/contract-worktree.sh +1 -21
  29. package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/new-plan.sh +1 -1
  32. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  33. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  34. package/assets/templates/helpers/sprint-backlog.sh +6 -6
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/plan.template.md +1 -1
  38. package/assets/templates/review.template.md +9 -34
  39. package/assets/workflow-contract.v1.json +10 -19
  40. package/docs/README.md +10 -10
  41. package/docs/architecture/CURRENT.md +170 -0
  42. package/docs/architecture/EVOLUTION.md +42 -0
  43. package/docs/architecture/current/README.md +4 -156
  44. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  45. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  46. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  47. package/docs/architecture/history/README.md +7 -0
  48. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  49. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  50. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  51. package/docs/architecture/history.md +3 -34
  52. package/docs/architecture/index.md +15 -98
  53. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  54. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  55. package/docs/architecture/versions/1.6.md +38 -0
  56. package/docs/architecture/versions/1.7.md +41 -0
  57. package/docs/forge-chatgpt-browser-engine.md +2 -2
  58. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  59. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  60. package/docs/forge-github-issue-launcher.md +1 -1
  61. package/docs/forge-runtime-self-healing-loop.md +1 -1
  62. package/docs/operations/controller-browser-plugin.md +38 -22
  63. package/docs/operations/controller-performance-and-502.md +124 -0
  64. package/docs/operations/controller-reliability-runbook.md +1 -1
  65. package/docs/operations/features.md +3 -3
  66. package/docs/operations/github-repository.md +1 -1
  67. package/docs/operations/homebrew.md +2 -2
  68. package/docs/operations/mcp-tool-exposure.md +5 -3
  69. package/docs/tutorials/01-install-and-start.md +1 -1
  70. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Installation.md +1 -1
  74. package/docs/wiki/Integrations.md +2 -2
  75. package/docs/wiki/Quick-Start.md +1 -1
  76. package/docs/wiki/Work-Lifecycle.md +1 -1
  77. package/package.json +29 -10
  78. package/scripts/activate-source-baseline.ts +17 -13
  79. package/scripts/benchmark-background-check-overlap.ts +228 -0
  80. package/scripts/benchmark-mcp-host.sh +117 -0
  81. package/scripts/benchmark-mcp-transport.ts +170 -0
  82. package/scripts/benchmark-quality-harness.ts +146 -0
  83. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  84. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  85. package/scripts/benchmark-typescript-navigation.ts +186 -0
  86. package/scripts/benchmark-work-efficiency.ts +334 -0
  87. package/scripts/capture-plan.sh +3 -3
  88. package/scripts/check-agent-tooling.sh +8 -35
  89. package/scripts/check-architecture-sync.sh +17 -86
  90. package/scripts/check-mcp-compatibility.ts +14 -0
  91. package/scripts/check-public-docs.sh +14 -1
  92. package/scripts/check-runtime-architecture.mjs +107 -64
  93. package/scripts/check-stable-baseline.ts +92 -0
  94. package/scripts/check-task-workflow.sh +4 -4
  95. package/scripts/contract-worktree.sh +1 -21
  96. package/scripts/ensure-task-workflow.sh +22 -55
  97. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  98. package/scripts/install-standalone-recovery.ts +24 -0
  99. package/scripts/lib/project-init-lib.sh +15 -44
  100. package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
  101. package/scripts/migrate-project-template.sh +23 -1179
  102. package/scripts/new-plan.sh +1 -1
  103. package/scripts/plan-to-todo.sh +11 -36
  104. package/scripts/run-governed-gate.ts +2 -1
  105. package/scripts/ship-worktrees.sh +2 -29
  106. package/scripts/smoke-ios-physical-device.ts +4 -2
  107. package/scripts/smoke-runtime-recovery.ts +1 -35
  108. package/scripts/smoke-schedule-engine.ts +1 -20
  109. package/scripts/sprint-backlog.sh +1 -1
  110. package/scripts/verify-contract.sh +1 -111
  111. package/scripts/verify-forge-runtime.sh +0 -1
  112. package/scripts/verify-sprint.sh +3 -180
  113. package/scripts/workflow-contract.ts +1 -5
  114. package/src/cli/commands/chatgpt.ts +31 -2
  115. package/src/cli/commands/init-hook.ts +3 -3
  116. package/src/cli/commands/mcp.ts +9 -5
  117. package/src/cli/commands/plugin.ts +37 -10
  118. package/src/cli/commands/recovery.ts +25 -3
  119. package/src/cli/commands/runtime.ts +55 -3
  120. package/src/cli/commands/security.ts +12 -1
  121. package/src/cli/commands/setup-profile.ts +31 -21
  122. package/src/cli/controller/check-runner.ts +106 -3
  123. package/src/cli/controller/composite-result.ts +1 -1
  124. package/src/cli/controller/context/focus.ts +51 -0
  125. package/src/cli/controller/context/instruction-context.ts +114 -0
  126. package/src/cli/controller/context/known-paths.ts +76 -0
  127. package/src/cli/controller/context/query-planning.ts +110 -0
  128. package/src/cli/controller/context/rule-resolution.ts +532 -0
  129. package/src/cli/controller/context/source-materializer.ts +278 -0
  130. package/src/cli/controller/context/structural.ts +16 -0
  131. package/src/cli/controller/context/types.ts +286 -0
  132. package/src/cli/controller/context-pack.ts +898 -430
  133. package/src/cli/controller/launch-agents.ts +77 -2
  134. package/src/cli/controller/operational-plan.ts +2 -2
  135. package/src/cli/controller/work-mode.ts +6 -4
  136. package/src/cli/editing/edit-session.ts +60 -7
  137. package/src/cli/github/github.ts +133 -0
  138. package/src/cli/hook/prompt-guard-decision.ts +17 -18
  139. package/src/cli/hook/prompt-intents.ts +1 -1
  140. package/src/cli/installer/managed-entries.ts +15 -1
  141. package/src/cli/local-bridge/console-automations.ts +225 -67
  142. package/src/cli/local-bridge/console-view-models.ts +3 -35
  143. package/src/cli/local-bridge/facade-api.ts +2 -217
  144. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  145. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
  146. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  147. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
  148. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
  149. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
  150. package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
  151. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  152. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
  153. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
  154. package/src/cli/local-bridge/frontend/src/styles.css +27 -11
  155. package/src/cli/local-bridge/frontend/src/types.ts +8 -7
  156. package/src/cli/local-bridge/job-store.ts +1 -490
  157. package/src/cli/local-bridge/server.ts +29 -532
  158. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  159. package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
  160. package/src/cli/mcp/auth.ts +5 -5
  161. package/src/cli/mcp/instructions.ts +3 -2
  162. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  163. package/src/cli/mcp/multi-repository.ts +16 -5
  164. package/src/cli/mcp/oauth.ts +175 -26
  165. package/src/cli/mcp/paths.ts +8 -2
  166. package/src/cli/mcp/repository-tools.ts +356 -101
  167. package/src/cli/mcp/server.ts +625 -54
  168. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  169. package/src/cli/mcp/toolset-selection.ts +1 -1
  170. package/src/cli/mcp/toolset.ts +42 -5
  171. package/src/cli/mcp/transports/http.ts +125 -15
  172. package/src/cli/mcp/transports/session-registry.ts +67 -5
  173. package/src/cli/mcp/types.ts +3 -3
  174. package/src/cli/repositories/command-classifier.ts +209 -14
  175. package/src/cli/repositories/command-executor.ts +55 -521
  176. package/src/cli/repositories/command-process.ts +108 -0
  177. package/src/cli/repositories/command-scope.ts +99 -41
  178. package/src/cli/repositories/controller-home.ts +7 -8
  179. package/src/cli/repositories/git-command-executor.ts +2 -0
  180. package/src/cli/repositories/locks.ts +53 -16
  181. package/src/cli/repositories/repository-snapshot.ts +121 -0
  182. package/src/cli/repositories/safe-patch.ts +6 -4
  183. package/src/cli/repositories/selected-path-actions.ts +17 -9
  184. package/src/cli/repositories/structured-git.ts +110 -10
  185. package/src/cli/repository/inspector.ts +565 -62
  186. package/src/cli/repository/session-cache.ts +58 -17
  187. package/src/cli/v81-entry.ts +1 -4
  188. package/src/effects/fs-transaction.ts +85 -17
  189. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  190. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  191. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  192. package/src/runtime/context/lsp-client.ts +283 -0
  193. package/src/runtime/context/semantic-navigation.ts +275 -0
  194. package/src/runtime/context/swift-navigation.ts +565 -0
  195. package/src/runtime/context/typescript-navigation.ts +226 -0
  196. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  197. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
  198. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  199. package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
  200. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  201. package/src/runtime/control-plane/execution/validation.ts +15 -2
  202. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  203. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  204. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  205. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  206. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  207. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
  208. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
  209. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
  210. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  211. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  212. package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
  213. package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
  214. package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
  215. package/src/runtime/control-plane/facade/index.ts +1 -0
  216. package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
  217. package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
  218. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  219. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  220. package/src/runtime/control-plane/facade/types.ts +55 -11
  221. package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
  222. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  223. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  224. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  225. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  226. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  227. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  228. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  229. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  230. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  231. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  232. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  233. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  234. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  235. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  236. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  237. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  238. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  239. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  240. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  241. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
  242. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  243. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  244. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  245. package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
  246. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  247. package/src/runtime/control-plane/release-retention.ts +62 -2
  248. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  249. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  250. package/src/runtime/diagnostics/mcp-timing.ts +19 -0
  251. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  252. package/src/runtime/diagnostics/performance.ts +24 -1
  253. package/src/runtime/evidence/event-ledger.ts +1 -1
  254. package/src/runtime/execution/jobs/store.ts +58 -4
  255. package/src/runtime/execution/jobs/types.ts +1 -1
  256. package/src/runtime/execution/managed-workspace.ts +203 -1
  257. package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
  258. package/src/runtime/execution/process-runtime/check-result.ts +63 -2
  259. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  260. package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
  261. package/src/runtime/execution/process-runtime/gc.ts +98 -12
  262. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  263. package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
  264. package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
  265. package/src/runtime/execution/process-runtime/runtime.ts +59 -4
  266. package/src/runtime/execution/process-runtime/store.ts +7 -0
  267. package/src/runtime/execution/process-runtime/types.ts +2 -0
  268. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  269. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  270. package/src/runtime/execution/workers/executor.ts +0 -48
  271. package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
  272. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  273. package/src/runtime/gateway/mcp/router.ts +185 -83
  274. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  275. package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
  276. package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
  277. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  278. package/src/runtime/plugins/browser-adapter.ts +2925 -318
  279. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  280. package/src/runtime/plugins/browser-handoff-host.ts +6 -31
  281. package/src/runtime/plugins/browser-handoff.ts +92 -5
  282. package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
  283. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  284. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  285. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  286. package/src/runtime/plugins/browser-runtime.ts +183 -0
  287. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  288. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  289. package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
  290. package/src/runtime/plugins/external-adapter.ts +324 -3
  291. package/src/runtime/plugins/external-registration.ts +84 -5
  292. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  293. package/src/runtime/plugins/first-party-registry.ts +4 -2
  294. package/src/runtime/plugins/github-adapter.ts +125 -0
  295. package/src/runtime/plugins/gmail-adapter.ts +28 -2
  296. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  297. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  298. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  299. package/src/runtime/plugins/ios-adapter.ts +106 -16
  300. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  301. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  302. package/src/runtime/plugins/lightweight-action.ts +153 -0
  303. package/src/runtime/plugins/local-system-adapter.ts +125 -8
  304. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  305. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  306. package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
  307. package/src/runtime/plugins/resend-adapter.ts +1 -1
  308. package/src/runtime/plugins/store.ts +366 -24
  309. package/src/runtime/plugins/types.ts +39 -2
  310. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  311. package/src/runtime/projections/git-status-sampler.ts +16 -16
  312. package/src/runtime/projections/materialized-view.ts +28 -10
  313. package/src/runtime/recovery/actions.ts +0 -10
  314. package/src/runtime/recovery/classifier.ts +0 -9
  315. package/src/runtime/recovery/maintenance-executor.ts +415 -18
  316. package/src/runtime/recovery/probe.ts +0 -1
  317. package/src/runtime/recovery/types.ts +0 -1
  318. package/src/runtime/root/mcp-transport.ts +180 -10
  319. package/src/runtime/root/package-connector-service.ts +186 -8
  320. package/src/runtime/root/package-runtime-release.ts +143 -17
  321. package/src/runtime/root/package-runtime-service.ts +430 -22
  322. package/src/runtime/root/release-manifest.ts +7 -2
  323. package/src/runtime/root/release-materialize.ts +162 -19
  324. package/src/runtime/root/release-store.ts +48 -11
  325. package/src/runtime/root/runtime.ts +23 -5
  326. package/src/runtime/root/service.ts +100 -27
  327. package/src/runtime/root/types.ts +2 -0
  328. package/src/runtime/safe-tooling/index.ts +0 -1
  329. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  330. package/src/runtime/safe-tooling/types.ts +0 -30
  331. package/src/runtime/shared/trusted-executable.ts +62 -0
  332. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  333. package/src/runtime/standalone-recovery/core.ts +702 -94
  334. package/src/runtime/standalone-recovery/entry.ts +99 -23
  335. package/src/runtime/standalone-recovery/installer.ts +19 -3
  336. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  337. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  338. package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
  339. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
  340. package/src/runtime/workflow/schedules/engine.ts +509 -80
  341. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  342. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  343. package/src/runtime/workflow/schedules/store.ts +131 -10
  344. package/src/runtime/workflow/schedules/types.ts +6 -3
  345. package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
  346. package/src/testing/test-governance.ts +8 -40
  347. package/assets/reference-configs/heartbeat-triage.md +0 -57
  348. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  349. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  350. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  351. package/bin/forge-desktop-helper.mjs +0 -204
  352. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  353. package/docs/architecture/current/architecture-invariants.md +0 -331
  354. package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
  355. package/docs/architecture/current/codegraph-context-provider.md +0 -226
  356. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  357. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  358. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
  359. package/docs/architecture/current/entity-model.md +0 -592
  360. package/docs/architecture/current/external-plugin-governance.md +0 -396
  361. package/docs/architecture/current/failure-recovery.md +0 -447
  362. package/docs/architecture/current/governance.md +0 -213
  363. package/docs/architecture/current/human-interaction-plane.md +0 -87
  364. package/docs/architecture/current/implementation-status.md +0 -128
  365. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  366. package/docs/architecture/current/legacy-governance.md +0 -58
  367. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  368. package/docs/architecture/current/migration-roadmap.md +0 -105
  369. package/docs/architecture/current/multi-repository-execution.md +0 -371
  370. package/docs/architecture/current/operations-runbook.md +0 -45
  371. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  372. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  373. package/docs/architecture/current/repository-access-modes.md +0 -102
  374. package/docs/architecture/current/route-policy.md +0 -15
  375. package/docs/architecture/current/route-session-concurrency.md +0 -85
  376. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  377. package/docs/architecture/current/runtime-directory-map.md +0 -49
  378. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  379. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  380. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  381. package/docs/architecture/current/system-overview.md +0 -144
  382. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  383. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  384. package/docs/architecture/current/thin-harness-v1.md +0 -311
  385. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  386. package/docs/forge-autonomous-goal-loop.md +0 -9
  387. package/docs/forge-provider-configuration.md +0 -7
  388. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
  389. package/docs/operations/controller-desktop-plugin.md +0 -112
  390. package/docs/operations/gmail-assistant-routines.md +0 -186
  391. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  392. package/docs/operations/provider-configuration.md +0 -31
  393. package/scripts/harness-trace-grade.sh +0 -150
  394. package/scripts/heartbeat-triage.sh +0 -361
  395. package/scripts/maintenance-triage.sh +0 -167
  396. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  397. package/src/cli/repositories/goal-registry.ts +0 -239
  398. package/src/runtime/assistant/action-proposals.ts +0 -279
  399. package/src/runtime/assistant/intent.ts +0 -395
  400. package/src/runtime/assistant/model-provider.ts +0 -460
  401. package/src/runtime/assistant/openapi.ts +0 -223
  402. package/src/runtime/assistant/policy.ts +0 -89
  403. package/src/runtime/assistant/readiness.ts +0 -258
  404. package/src/runtime/assistant/routine-runtime.ts +0 -477
  405. package/src/runtime/assistant/schedule-binding.ts +0 -171
  406. package/src/runtime/assistant/standing-grants.ts +0 -375
  407. package/src/runtime/assistant/store.ts +0 -243
  408. package/src/runtime/assistant/types.ts +0 -135
  409. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  410. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  411. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  412. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  413. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  414. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  415. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  416. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  417. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  418. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  419. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  420. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  421. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  422. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  423. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  424. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  425. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  426. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  427. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  428. package/src/runtime/plugins/desktop-adapter.ts +0 -374
  429. package/src/runtime/safe-tooling/browser-safe.ts +0 -133
  430. package/src/runtime/workflow/findings/store.ts +0 -204
  431. package/src/runtime/workflow/findings/types.ts +0 -58
  432. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  433. package/src/runtime/workflow/portfolio/store.ts +0 -185
  434. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -2,6 +2,10 @@ import { bindRepositoryEntities } from '../repositories/entity-migration';
2
2
  import { bootstrapLocalProject, diagnoseLatestLocalProjectSource } from '../repositories/local-project-onboarding';
3
3
  import { resolveEphemeralWorkspaceTarget } from '../repositories/ephemeral-workspace';
4
4
  import type { ResolvedExecutionIdentity } from '../../runtime/control-plane/execution/execution-identity';
5
+ import { readExecutionSession } from '../../runtime/control-plane/execution/session-store';
6
+ import { getWorkContract } from '../../runtime/control-plane/facade/work-contract-store';
7
+ import { getControllerSession, listControllerSessions } from '../../runtime/control-plane/facade/controller-session-store';
8
+ import { isTerminalWorkContractStatus } from '../../runtime/control-plane/facade/types';
5
9
  import { executeRepositoryCommand, previewRepositoryCommandExecution } from '../repositories/command-executor';
6
10
  import { withControllerLock } from '../repositories/locks';
7
11
  import {
@@ -9,6 +13,7 @@ import {
9
13
  findIdenticalRepositoryRegistration,
10
14
  getRepository,
11
15
  listRepositories,
16
+ reconcileRepositoryCheckouts,
12
17
  refreshRepository,
13
18
  registerRepository,
14
19
  removeRepository,
@@ -19,9 +24,8 @@ import {
19
24
  } from '../repositories/registry';
20
25
  import { buildControllerWorkbench } from '../repositories/workbench';
21
26
  import { applySafePatch, buildSafePatchPlan } from '../repositories/safe-patch';
22
- import { getEditSessionDiff } from '../editing/edit-session';
27
+ import { getEditSession, getEditSessionDiff, type EditSessionBinding } from '../editing/edit-session';
23
28
  import { buildSyncOperationDigest, classifyUserFacingError } from '../../runtime/control-plane/facade/operation-digest';
24
- import { diagnoseRepositoryStuckState, listRepositoryGoalRuns, readRepositoryGoalRegistry, runRepositoryGoal, upsertRepositoryGoal } from '../repositories/goal-registry';
25
29
  import {
26
30
  repositoryGitCommit,
27
31
  repositoryGitCreateBranch,
@@ -49,6 +53,10 @@ import {
49
53
  executeRepositoryCommandViaProcessRuntime,
50
54
  } from '../../runtime/execution/process-runtime/command-facade';
51
55
  import { assessWorkMode, parseExplicitTaskMode } from '../controller/work-mode';
56
+ import { normalizeRepositoryCommand } from '../repositories/command-normalization';
57
+ import { readRepositoryRange } from '../repository/inspector';
58
+ import { getMcpPolicy } from './policy';
59
+ import { redactMcpText } from './redaction';
52
60
  import type { CallToolResult, McpToolDefinition } from './tools';
53
61
  import {
54
62
  boundUtf8,
@@ -111,6 +119,13 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
111
119
  include_removed: { type: 'boolean' },
112
120
  detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns the complete checkout history.' },
113
121
  }, ['repo_id'], true),
122
+ definition('read_repository_file', 'Read a line range from one policy-readable repository file.', {
123
+ repo_id: repoId,
124
+ checkout_id: { type: 'string' },
125
+ path: { type: 'string' },
126
+ start_line: { type: 'number' },
127
+ end_line: { type: 'number' },
128
+ }, ['path'], true),
114
129
  definition('repository_validate', 'Validate repository identity and migrate legacy ownership.', {
115
130
  repo_id: repoId,
116
131
  detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns full migration evidence.' },
@@ -130,7 +145,7 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
130
145
  }, ['repo_id']),
131
146
  definition('repository_remove', 'Soft-remove a repository while retaining audit history.', {
132
147
  repo_id: repoId,
133
- }, ['repo_id'], true),
148
+ }, ['repo_id']),
134
149
  definition('repository_workbench', 'Return Workbench state or invoke one bounded Thin Harness operation without adding top-level MCP tools. Prefer batch_execute for multi-step status→search→read→diff or read→patch→check flows so ChatGPT pays one routing/receipt ownership cycle.', {
135
150
  repo_id: repoId,
136
151
  checkout_id: { type: 'string', description: 'Optional checkout identity for repository-scoped operations.' },
@@ -146,38 +161,6 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
146
161
  additionalProperties: true,
147
162
  },
148
163
  }),
149
-
150
-
151
-
152
- definition('repository_goal_list', 'List durable repository goals stored inside the checkout.', {
153
- repo_id: repoId,
154
- checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
155
- }, [], true),
156
- definition('repository_goal_upsert', 'Create or update one durable repository goal for repeated assistant workflows.', {
157
- repo_id: repoId,
158
- checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
159
- id: { type: 'string' },
160
- title: { type: 'string' },
161
- status: { type: 'string', enum: ['active', 'paused', 'done'] },
162
- checks: { type: 'array', items: { type: 'string' } },
163
- notes: { type: 'string' },
164
- }, []),
165
- definition('repository_stuck_diagnose', 'Diagnose likely repository workflow blockers from Git state and registered goals.', {
166
- repo_id: repoId,
167
- checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
168
- }, [], true),
169
- definition('repository_goal_run', 'Run one durable repository goal iteration, optionally executing its configured checks and recording a goal-run artifact.', {
170
- repo_id: repoId,
171
- checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
172
- goal_id: { type: 'string', description: 'Goal id. Defaults to the first active goal.' },
173
- run_checks: { type: 'boolean', description: 'When true, execute configured checks. Defaults to diagnosis-only.' },
174
- }, []),
175
- definition('repository_goal_runs', 'List recent repository goal-run artifacts.', {
176
- repo_id: repoId,
177
- checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
178
- limit: { type: 'number' },
179
- }, [], true),
180
-
181
164
  definition('repository_git_status', 'Return a structured Git status snapshot for the selected repository checkout.', {
182
165
  repo_id: repoId,
183
166
  checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
@@ -240,6 +223,7 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
240
223
  definition('repository_safe_patch_apply', 'Apply one coherent deterministic edit batch and return bounded review evidence. Checks are opt-in: pass check_ids only when the batch is stable. Long checks return managed Process handles instead of blocking the MCP call; use validation_only with the returned session/request ids to join later without replaying the patch.', {
241
224
  repo_id: repoId,
242
225
  checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
226
+ work_id: { type: 'string', description: 'Optional durable Work identity. Workflow controllers should pass the exact claimed Work id so attribution survives transient MCP transport sessions.' },
243
227
  session_id: { type: 'string', description: 'Existing edit session id. Required for validation_only; otherwise omit to create one.' },
244
228
  purpose: { type: 'string', description: 'Purpose for a newly created edit session.' },
245
229
  operations: { type: 'array', items: { type: 'object' }, description: 'Edit operations using the same shape as apply_patch. Required unless validation_only=true.' },
@@ -263,9 +247,10 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
263
247
  cwd: { type: 'string', description: 'Optional root-relative working directory.' },
264
248
  workspace_root: { type: 'string', description: 'Absolute existing local directory used as an ephemeral execution root. Mutually exclusive with repo_id/checkout_id; does not register or initialize the directory.' },
265
249
  }, ['command'], true),
266
- definition('repository_command_execute', 'Execute one repository-scoped local command through Full Access, Goal delegation, or a resumable approval request. Legacy preview-token callers remain compatible.', {
250
+ definition('repository_command_execute', 'Execute one repository-scoped command through the thinnest eligible lane: ephemeral for ordinary local work, an in-memory lightweight handle when it outlives the interaction budget, or explicit Durable handling for Work/external/release effects. Lightweight handles have no SQLite, Lease, recovery, or replay membership. Use rh_context for routine code discovery/reading; shell exploration is fallback-only.', {
267
251
  repo_id: repoId,
268
252
  checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
253
+ work_id: { type: 'string', description: 'Optional durable Work identity. Workflow controllers should pass the exact claimed Work id so attribution survives transient MCP transport sessions.' },
269
254
  command: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Legacy shell string or typed argv array. Typed argv executes without a shell.' },
270
255
  cwd: { type: 'string', description: 'Optional root-relative working directory.' },
271
256
  workspace_root: { type: 'string', description: 'Absolute existing local directory used as an ephemeral execution root. Mutually exclusive with repo_id/checkout_id; does not register or initialize the directory.' },
@@ -302,10 +287,169 @@ function withResponseMeta(payload: Record<string, unknown>, startedAt: number):
302
287
  return response;
303
288
  }
304
289
 
290
+ export interface RepositoryToolCallerContext {
291
+ sessionId?: string;
292
+ principalId?: string;
293
+ controllerInstanceId?: string;
294
+ }
295
+
296
+ function claimedExplicitWork(
297
+ controllerHome: string,
298
+ repoId: string,
299
+ caller: RepositoryToolCallerContext | undefined,
300
+ requestedWorkId: string,
301
+ ) {
302
+ if (!caller?.principalId?.trim()) return undefined;
303
+ const work = getWorkContract({ controllerHome, repoId }, requestedWorkId);
304
+ if (!work || isTerminalWorkContractStatus(work.status)) throw new Error(`WORK_ATTRIBUTION_INVALID: ${requestedWorkId}`);
305
+ const owner = getControllerSession({ controllerHome, repoId }, requestedWorkId);
306
+ if (!owner) throw new Error(`WORK_CONTROLLER_CLAIM_REQUIRED: ${requestedWorkId}`);
307
+ if ((owner.principalId?.trim() || owner.controllerId) !== caller.principalId.trim()) throw new Error(`WORK_CONTROLLER_OWNERSHIP_MISMATCH: ${requestedWorkId}`);
308
+ return work;
309
+ }
310
+
311
+ function assertNoBoundExecutionSessionMutation(
312
+ controllerHome: string,
313
+ repository: ReturnType<typeof resolveRepositorySelection>,
314
+ caller?: RepositoryToolCallerContext,
315
+ ): void {
316
+ if (!caller?.principalId?.trim() || !caller.sessionId?.trim()) return;
317
+ const executionSession = readExecutionSession(controllerHome, {
318
+ sessionId: caller.sessionId,
319
+ principalId: caller.principalId,
320
+ controllerInstanceId: caller.controllerInstanceId,
321
+ });
322
+ const workId = executionSession?.activeWorkId?.trim();
323
+ if (!workId || executionSession?.activeRepositoryId !== repository.repoId) return;
324
+ const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
325
+ if (!work) throw new Error(`WORK_ATTRIBUTION_INVALID: ${workId}`);
326
+ if (isTerminalWorkContractStatus(work.status)) {
327
+ throw new Error(`WORK_ATTRIBUTION_TERMINAL: ${work.workId}:${work.status}`);
328
+ }
329
+ throw new Error(`WORK_ATTRIBUTION_REQUIRED: ${work.workId}; active execution session mutations must pass work_id explicitly`);
330
+ }
331
+
332
+ function claimedSessionWorkId(
333
+ controllerHome: string,
334
+ repository: ReturnType<typeof resolveRepositorySelection>,
335
+ caller?: RepositoryToolCallerContext,
336
+ explicitWorkId?: unknown,
337
+ ): string | undefined {
338
+ const requestedWorkId = typeof explicitWorkId === 'string' ? explicitWorkId.trim() : '';
339
+ if (requestedWorkId) {
340
+ const work = claimedExplicitWork(controllerHome, repository.repoId, caller, requestedWorkId);
341
+ if (!work) return undefined;
342
+ if (work.checkoutId && work.checkoutId !== repository.activeCheckoutId) throw new Error(`WORK_CHECKOUT_MISMATCH: ${requestedWorkId}:${repository.activeCheckoutId}`);
343
+ return requestedWorkId;
344
+ }
345
+ if (!caller?.principalId?.trim()) return undefined;
346
+ if (caller.sessionId?.trim()) {
347
+ const executionSession = readExecutionSession(controllerHome, {
348
+ sessionId: caller.sessionId,
349
+ principalId: caller.principalId,
350
+ controllerInstanceId: caller.controllerInstanceId,
351
+ });
352
+ const workId = executionSession?.activeWorkId?.trim();
353
+ if (workId && executionSession?.activeRepositoryId === repository.repoId && (!executionSession.activeCheckoutId || executionSession.activeCheckoutId === repository.activeCheckoutId)) {
354
+ const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
355
+ if (work && isTerminalWorkContractStatus(work.status)) {
356
+ throw new Error(`WORK_ATTRIBUTION_TERMINAL: ${work.workId}:${work.status}`);
357
+ }
358
+ const owner = getControllerSession({ controllerHome, repoId: repository.repoId }, workId);
359
+ if (work && owner?.sessionId === caller.sessionId && (owner.principalId?.trim() || owner.controllerId) === caller.principalId.trim()) return workId;
360
+ }
361
+ }
362
+ const principal = caller.principalId.trim();
363
+ const candidates = listControllerSessions({ controllerHome, repoId: repository.repoId })
364
+ .filter((owner) => (owner.principalId?.trim() || owner.controllerId) === principal)
365
+ .map((owner) => ({ owner, work: getWorkContract({ controllerHome, repoId: repository.repoId }, owner.workId) }))
366
+ .filter((entry): entry is { owner: ReturnType<typeof listControllerSessions>[number]; work: NonNullable<ReturnType<typeof getWorkContract>> } => Boolean(entry.work && !isTerminalWorkContractStatus(entry.work.status) && (!entry.work.checkoutId || entry.work.checkoutId === repository.activeCheckoutId)));
367
+ if (candidates.length === 1) return candidates[0].work.workId;
368
+ if (candidates.length > 1) throw new Error(`WORK_ATTRIBUTION_AMBIGUOUS: principal ${principal} owns ${candidates.length} active Works on checkout ${repository.activeCheckoutId}`);
369
+ return undefined;
370
+ }
371
+
372
+ export function claimedSessionEditBinding(
373
+ controllerHome: string,
374
+ repository: ReturnType<typeof resolveRepositorySelection>,
375
+ caller?: RepositoryToolCallerContext,
376
+ explicitWorkId?: unknown,
377
+ ): EditSessionBinding | undefined {
378
+ const workId = claimedSessionWorkId(controllerHome, repository, caller, explicitWorkId);
379
+ if (!workId || !caller?.principalId?.trim()) return undefined;
380
+ const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
381
+ if (!work) return undefined;
382
+ return {
383
+ workId,
384
+ repoId: repository.repoId,
385
+ checkoutId: repository.activeCheckoutId,
386
+ principalId: caller.principalId.trim(),
387
+ controllerInstanceId: caller.controllerInstanceId,
388
+ routeDecisionFingerprint: work.routeDecisionFingerprint,
389
+ };
390
+ }
391
+
392
+ function safePatchEditBinding(
393
+ controllerHome: string,
394
+ repository: ReturnType<typeof resolveRepositorySelection>,
395
+ caller: RepositoryToolCallerContext | undefined,
396
+ args: Record<string, unknown>,
397
+ ): EditSessionBinding | undefined {
398
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
399
+ if (explicitWorkId) return claimedSessionEditBinding(controllerHome, repository, caller, explicitWorkId);
400
+ const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
401
+ if (sessionId) {
402
+ const existingWorkId = getEditSession(repository.canonicalRoot, sessionId).workId?.trim();
403
+ return existingWorkId
404
+ ? claimedSessionEditBinding(controllerHome, repository, caller, existingWorkId)
405
+ : undefined;
406
+ }
407
+ assertNoBoundExecutionSessionMutation(controllerHome, repository, caller);
408
+ return undefined;
409
+ }
410
+
411
+ function resolveRepositorySelectionForClaimedWork(
412
+ controllerHome: string,
413
+ args: Record<string, unknown>,
414
+ repoIdValue: string,
415
+ caller?: RepositoryToolCallerContext,
416
+ ): ReturnType<typeof resolveRepositorySelection> {
417
+ const checkoutId = typeof args.checkout_id === 'string' ? args.checkout_id.trim() : '';
418
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
419
+ const repository = resolveRepositorySelection({
420
+ repoId: repoIdValue || undefined,
421
+ checkoutId: checkoutId || undefined,
422
+ controllerHome,
423
+ allowSoleRepository: true,
424
+ });
425
+ if (!explicitWorkId || checkoutId) return repository;
426
+ const work = claimedExplicitWork(controllerHome, repository.repoId, caller, explicitWorkId);
427
+ if (!work?.checkoutId || work.checkoutId === repository.activeCheckoutId) return repository;
428
+ return resolveRepositorySelection({
429
+ repoId: repository.repoId,
430
+ checkoutId: work.checkoutId,
431
+ controllerHome,
432
+ allowSoleRepository: true,
433
+ });
434
+ }
435
+
436
+ function rawDefaultBranchMergeCommand(repository: ReturnType<typeof resolveRepositorySelection>, command: unknown): boolean {
437
+ if (!repository.defaultBranch) return false;
438
+ const checkout = repository.checkouts.find((entry) => entry.checkoutId === repository.activeCheckoutId);
439
+ if (checkout?.branch !== repository.defaultBranch) return false;
440
+ const normalized = normalizeRepositoryCommand(command);
441
+ if (normalized.kind === 'argv') {
442
+ const executable = normalized.executable?.split('/').pop();
443
+ return executable === 'git' && normalized.args?.[0] === 'merge';
444
+ }
445
+ return /(?:^|[;&|]\s*)git\s+merge\b/.test(normalized.shellCommand ?? '');
446
+ }
447
+
305
448
  function resolveRepositoryCommandTarget(
306
449
  controllerHome: string,
307
450
  args: Record<string, unknown>,
308
451
  repoIdValue: string,
452
+ caller?: RepositoryToolCallerContext,
309
453
  ): {
310
454
  repository: ReturnType<typeof resolveRepositorySelection>;
311
455
  executionIdentity: ResolvedExecutionIdentity;
@@ -330,12 +474,11 @@ function resolveRepositoryCommandTarget(
330
474
  workspace: { workspaceId: target.workspaceId, root: target.canonicalRoot, registered: false },
331
475
  };
332
476
  }
333
- const repository = resolveRepositorySelection({
334
- repoId: repoIdValue || undefined,
335
- checkoutId: checkoutId || undefined,
336
- controllerHome,
337
- allowSoleRepository: true,
338
- });
477
+ const repository = resolveRepositorySelectionForClaimedWork(controllerHome, args, repoIdValue, caller);
478
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
479
+ const workId = explicitWorkId
480
+ ? claimedSessionWorkId(controllerHome, repository, caller, explicitWorkId)
481
+ : undefined;
339
482
  return {
340
483
  repository,
341
484
  executionIdentity: {
@@ -344,6 +487,7 @@ function resolveRepositoryCommandTarget(
344
487
  repositoryId: repository.repoId,
345
488
  checkoutId: repository.activeCheckoutId,
346
489
  canonicalRoot: repository.canonicalRoot,
490
+ ...(workId ? { workId } : {}),
347
491
  },
348
492
  };
349
493
  }
@@ -434,6 +578,33 @@ function failure(error: unknown): RepositoryToolResult {
434
578
  return { ...result({ error: { code, message, ...(compactDetails !== undefined ? { details: compactDetails } : {}) } }), isError: true };
435
579
  }
436
580
 
581
+ interface RepositoryExplorationGuidance {
582
+ code: 'FRAGMENTED_REPOSITORY_EXPLORATION';
583
+ recommendedTool: 'rh_context';
584
+ recommendedOperation: 'search';
585
+ message: string;
586
+ }
587
+
588
+ function fragmentedRepositoryExplorationGuidance(command: unknown): RepositoryExplorationGuidance | undefined {
589
+ if (typeof command !== 'string') return undefined;
590
+ const segments = command.split(/(?:&&|\|\||[;|])/g);
591
+ const readPrograms = new Set<string>();
592
+ let readStepCount = 0;
593
+ for (const segment of segments) {
594
+ const match = segment.trim().match(/^(?:(?:command|env)\s+)?(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)*(rg|grep|sed|cat|head|tail)\b/i);
595
+ if (!match) continue;
596
+ readStepCount += 1;
597
+ readPrograms.add(match[1]!.toLowerCase());
598
+ }
599
+ if (readStepCount < 2) return undefined;
600
+ return {
601
+ code: 'FRAGMENTED_REPOSITORY_EXPLORATION',
602
+ recommendedTool: 'rh_context',
603
+ recommendedOperation: 'search',
604
+ message: `Detected ${readStepCount} chained repository read steps (${[...readPrograms].join(', ')}). Prefer a broad rh_context request first, then repeat rh_context with selected paths or relationships when more evidence can improve correctness; keep shell search as fallback when the Context Plane cannot supply the needed evidence.`,
605
+ };
606
+ }
607
+
437
608
  function compactProcessCommandPayload(input: {
438
609
  accepted?: boolean;
439
610
  mode: string;
@@ -456,6 +627,7 @@ function compactProcessCommandPayload(input: {
456
627
  /** summary (default) omits nested process / routing dumps; detail restores diagnostics. */
457
628
  detailLevel?: 'summary' | 'detail';
458
629
  includeFullProcess?: boolean;
630
+ guidance?: RepositoryExplorationGuidance;
459
631
  }): Record<string, unknown> {
460
632
  const output = compactCommandOutput(input.stdout, input.stderr, { ok: input.ok === true });
461
633
  const detail = input.detailLevel === 'detail' || input.includeFullProcess === true;
@@ -483,10 +655,14 @@ function compactProcessCommandPayload(input: {
483
655
  ...(input.workspace ? { workspace: input.workspace } : {}),
484
656
  ...(input.processId ? { processId: input.processId } : {}),
485
657
  ...(completed ? { exitCode: input.exitCode ?? (ok ? 0 : 1) } : {}),
658
+ // A running default response otherwise loses its only dependency-bound
659
+ // continuation advice when the detailed Process record is omitted.
660
+ ...(!completed ? { next: input.next } : {}),
486
661
  stdout: output.stdout ?? '',
487
662
  stderr: output.stderr ?? '',
488
663
  ...(output.stdoutTruncated ? { stdoutTruncated: true, stdoutBytes: output.stdoutBytes } : {}),
489
664
  ...(output.stderrTruncated ? { stderrTruncated: true, stderrBytes: output.stderrBytes } : {}),
665
+ ...(input.guidance ? { guidance: input.guidance, suggestedOperation: 'rh_context' } : {}),
490
666
  };
491
667
  if (completed && !ok) {
492
668
  payload.error = {
@@ -534,6 +710,7 @@ function compactProcessCommandPayload(input: {
534
710
  }
535
711
  : {}),
536
712
  durableSideEffects: effects,
713
+ ...(input.guidance ? { guidance: input.guidance, suggestedOperation: 'rh_context' } : {}),
537
714
  next: input.next,
538
715
  detailLevel: 'detail',
539
716
  };
@@ -551,6 +728,18 @@ function emptyRepositoryMigration(repository: ReturnType<typeof registerReposito
551
728
  };
552
729
  }
553
730
 
731
+ function registrationOnlyMetadataRefresh(
732
+ match: ReturnType<typeof findIdenticalRepositoryRegistration>,
733
+ ): boolean {
734
+ if (!match || match.identical || match.reasons.length === 0) return false;
735
+ const metadataReasons = new Set([
736
+ 'remote_identity_changed',
737
+ 'default_branch_changed',
738
+ 'display_name_changed',
739
+ ]);
740
+ return match.reasons.every((reason) => metadataReasons.has(reason));
741
+ }
742
+
554
743
  function registrationOnlyAttachedExistingWorktree(repository: ReturnType<typeof registerRepository>): boolean {
555
744
  const selected = repository.checkouts.find((checkout) => checkout.checkoutId === repository.activeCheckoutId);
556
745
  return selected?.worktree === true
@@ -561,11 +750,35 @@ export async function callRepositoryTool(
561
750
  controllerHome: string,
562
751
  name: string,
563
752
  args: Record<string, unknown>,
753
+ caller?: RepositoryToolCallerContext,
564
754
  ): Promise<RepositoryToolResult | undefined> {
565
- if (!name.startsWith('repository_')) return undefined;
755
+ if (!name.startsWith('repository_') && name !== 'read_repository_file') return undefined;
566
756
  try {
567
757
  const repoIdValue = typeof args.repo_id === 'string' ? args.repo_id.trim() : '';
568
758
  switch (name) {
759
+ case 'read_repository_file': {
760
+ const repository = resolveRepositorySelection({
761
+ repoId: typeof args.repo_id === 'string' ? args.repo_id : undefined,
762
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
763
+ controllerHome,
764
+ allowSoleRepository: true,
765
+ });
766
+ const path = String(args.path ?? '').trim();
767
+ if (!path) throw new Error('REPOSITORY_PATH_REQUIRED: path is required');
768
+ const session = caller?.sessionId
769
+ ? { sessionId: caller.sessionId, repoId: repository.repoId, checkoutId: repository.activeCheckoutId }
770
+ : undefined;
771
+ const range = readRepositoryRange(
772
+ repository.canonicalRoot,
773
+ getMcpPolicy('controller', { repoRoot: repository.canonicalRoot }),
774
+ path,
775
+ typeof args.start_line === 'number' ? args.start_line : 1,
776
+ typeof args.end_line === 'number' ? args.end_line : 200,
777
+ session,
778
+ );
779
+ const redacted = redactMcpText(range.content);
780
+ return result({ ...range, content: redacted.text, redactions: redacted.redactions });
781
+ }
569
782
  case 'repository_register': {
570
783
  const startedAt = performance.now();
571
784
  const registerInput = {
@@ -587,16 +800,17 @@ export async function callRepositoryTool(
587
800
  startedAt,
588
801
  ));
589
802
  }
803
+ const metadataRefresh = registrationOnlyMetadataRefresh(identical);
590
804
  const repository = registerRepository(registerInput);
591
- // Adding a worktree checkout to an already registered repository changes
592
- // checkout inventory only. Existing Issue/Task entities are already
593
- // bound to this repoId, so re-running historical migration is wasted work
594
- // and can produce misleading cross-repository rebinding diagnostics.
595
- const migration = registrationOnlyAttachedExistingWorktree(repository)
805
+ // Adding a worktree checkout or changing only display/remote/default-branch
806
+ // metadata preserves repository + checkout authority. Existing entities
807
+ // are already bound to the stable repoId, so a historical migration scan
808
+ // would only add latency and stale unresolved diagnostics.
809
+ const migration = registrationOnlyAttachedExistingWorktree(repository) || metadataRefresh
596
810
  ? emptyRepositoryMigration(repository)
597
811
  : bindRepositoryEntities(repository);
598
812
  return result(withResponseMeta(
599
- summarizeRepositoryRegistration(repository, migration, args.detail_level === 'detail'),
813
+ summarizeRepositoryRegistration(repository, migration, args.detail_level === 'detail', metadataRefresh),
600
814
  startedAt,
601
815
  ));
602
816
  }
@@ -623,6 +837,11 @@ export async function callRepositoryTool(
623
837
  case 'repository_list':
624
838
  return result({ repositories: listRepositories(controllerHome, { includeRemoved: args.include_removed === true }).map(repositorySummary) });
625
839
  case 'repository_get': {
840
+ // Repository inspection is also the bounded reconciliation point for
841
+ // checkout lifecycle drift caused by worktrees being removed outside
842
+ // Forge. Reuse the existing registry reconciler so stale worktrees are
843
+ // not exposed as lifecycle=active to automation callers.
844
+ reconcileRepositoryCheckouts(repoIdValue, controllerHome);
626
845
  const repository = getRepository(repoIdValue, controllerHome, { includeRemoved: args.include_removed === true });
627
846
  return result(summarizeRepositoryInspection(repository, args.detail_level === 'detail'));
628
847
  }
@@ -716,38 +935,6 @@ export async function callRepositoryTool(
716
935
  checkout_id: typeof args.checkout_id === 'string' ? args.checkout_id : payload.checkout_id,
717
936
  });
718
937
  }
719
-
720
-
721
-
722
- case 'repository_goal_list': {
723
- const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
724
- return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, registry: readRepositoryGoalRegistry(repository) });
725
- }
726
- case 'repository_goal_upsert': {
727
- const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
728
- return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...upsertRepositoryGoal(repository, { id: args.id, title: args.title, status: args.status, checks: args.checks, notes: args.notes }) as unknown as Record<string, unknown> });
729
- }
730
- case 'repository_stuck_diagnose': {
731
- const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
732
- return result({ diagnosis: diagnoseRepositoryStuckState(repository) });
733
- }
734
-
735
- case 'repository_goal_run': {
736
- const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
737
- const ran = withControllerLock(
738
- controllerHome,
739
- { scope: 'repository', repoId: repository.repoId },
740
- 'mcp:repository_goal_run',
741
- () => runRepositoryGoal(repository, { goalId: args.goal_id, runChecks: args.run_checks }),
742
- 60_000,
743
- );
744
- return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...ran as unknown as Record<string, unknown> });
745
- }
746
- case 'repository_goal_runs': {
747
- const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
748
- const limit = typeof args.limit === 'number' ? args.limit : typeof args.limit === 'string' ? Number(args.limit) : undefined;
749
- return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, runs: listRepositoryGoalRuns(repository, limit) });
750
- }
751
938
  case 'repository_git_status': {
752
939
  const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
753
940
  const status = args.refresh === true
@@ -809,12 +996,8 @@ export async function callRepositoryTool(
809
996
  });
810
997
  }
811
998
  case 'repository_safe_patch_apply': {
812
- const repository = resolveRepositorySelection({
813
- repoId: repoIdValue || undefined,
814
- checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
815
- controllerHome,
816
- allowSoleRepository: true,
817
- });
999
+ const repository = resolveRepositorySelectionForClaimedWork(controllerHome, args, repoIdValue, caller);
1000
+ const binding = safePatchEditBinding(controllerHome, repository, caller, args);
818
1001
  const applied = withControllerLock(
819
1002
  controllerHome,
820
1003
  { scope: 'repository', repoId: repository.repoId },
@@ -829,6 +1012,7 @@ export async function callRepositoryTool(
829
1012
  continueOnError: args.continue_on_error,
830
1013
  refreshFingerprints: args.refresh_fingerprints,
831
1014
  recoverStaleSession: args.recover_stale_session,
1015
+ binding,
832
1016
  }),
833
1017
  60_000,
834
1018
  );
@@ -892,7 +1076,7 @@ export async function callRepositoryTool(
892
1076
  return ok ? result(payload) : { ...result(payload), isError: true };
893
1077
  }
894
1078
  case 'repository_command_preview': {
895
- const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue);
1079
+ const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue, caller);
896
1080
  const { repository } = target;
897
1081
  const execution = withControllerLock(
898
1082
  controllerHome,
@@ -912,8 +1096,17 @@ export async function callRepositoryTool(
912
1096
  });
913
1097
  }
914
1098
  case 'repository_command_execute': {
915
- const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue);
1099
+ const explorationGuidance = fragmentedRepositoryExplorationGuidance(args.command);
1100
+ const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue, caller);
916
1101
  const { repository, executionIdentity } = target;
1102
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
1103
+ if (!explicitWorkId) assertNoBoundExecutionSessionMutation(controllerHome, repository, caller);
1104
+ const deliveryWorkId = rawDefaultBranchMergeCommand(repository, args.command)
1105
+ ? executionIdentity.workId
1106
+ : undefined;
1107
+ if (deliveryWorkId) {
1108
+ throw new Error(`WORK_DELIVERY_REQUIRES_FINALIZE: ${deliveryWorkId} must pass Work verification and use rh_work finalize before default-branch integration`);
1109
+ }
917
1110
  const timeoutMs = typeof args.timeout_ms === 'number'
918
1111
  ? args.timeout_ms
919
1112
  : typeof args.timeout_ms === 'string'
@@ -939,16 +1132,33 @@ export async function callRepositoryTool(
939
1132
  const forceDurable = fromDurableWorker
940
1133
  || args.mode === 'durable'
941
1134
  || args.force_durable === true;
942
- const routingDecision = routeExecution({
943
- operation: 'repository_command_execute',
944
- mode: forceDurable ? 'durable' : args.mode === 'fast' ? 'fast' : 'auto',
945
- command: args.command as string | string[] | undefined,
946
- timeoutMs,
947
- background: forceDurable && (args.background === true || args.async === true),
948
- defaultBranch: repository.defaultBranch,
949
- approvalContinuation: typeof args.approval_request_id === 'string'
950
- || typeof args.approval_token === 'string',
951
- });
1135
+ // repository_command_execute owns its command execution architecture.
1136
+ // Do not send ordinary command text through Thin Harness semantic risk
1137
+ // routing: that creates duplicate regex/classifier policy and turns
1138
+ // unknown CLI shapes into false durable/reject decisions. Only an
1139
+ // explicitly requested durable mode uses the generic durable router.
1140
+ const routingDecision = forceDurable
1141
+ ? routeExecution({
1142
+ operation: 'repository_command_execute',
1143
+ mode: 'durable',
1144
+ timeoutMs,
1145
+ background: args.background === true || args.async === true,
1146
+ })
1147
+ : {
1148
+ mode: 'fast' as const,
1149
+ reasons: ['repository_command_process_runtime_authoritative'],
1150
+ risk: 'unknown' as const,
1151
+ estimatedClass: 'unknown' as const,
1152
+ requiresIsolation: false,
1153
+ requiresRecovery: false,
1154
+ suggestedOperation: undefined,
1155
+ effects: {
1156
+ readsWorkspace: true,
1157
+ mutatesWorkspace: true,
1158
+ mutatesGitRefs: false,
1159
+ remoteWrite: false,
1160
+ },
1161
+ };
952
1162
  // Unified Process Runtime for local commands (Direct/Managed) when not forced durable.
953
1163
  // Ephemeral workspaces cannot persist a recoverable async Process handle,
954
1164
  // so their async requests remain promotion-required below.
@@ -959,7 +1169,43 @@ export async function callRepositoryTool(
959
1169
  defaultBranch: repository.defaultBranch,
960
1170
  timeoutMs,
961
1171
  });
962
- if (routeClass.route === 'process_direct' || routeClass.route === 'process_managed') {
1172
+ if (routeClass.route === 'reject') {
1173
+ return result({
1174
+ accepted: false,
1175
+ mode: 'reject',
1176
+ path: routeClass.reason,
1177
+ route: 'reject',
1178
+ status: 'rejected',
1179
+ repoId: repository.repoId,
1180
+ checkoutId: repository.activeCheckoutId,
1181
+ workspace: target.workspace,
1182
+ message: routeClass.reason === 'standalone_recovery_lifecycle_required'
1183
+ ? 'Canonical Runtime and connector lifecycle operations must be submitted to the standalone Forge Recovery owner, not executed as a child of repository Process Runtime.'
1184
+ : 'Repository command execution was rejected by Process Runtime routing policy.',
1185
+ suggestedOperation: routeClass.reason === 'standalone_recovery_lifecycle_required'
1186
+ ? 'Use the standalone Forge Recovery connector.'
1187
+ : routingDecision.suggestedOperation,
1188
+ durableSideEffects: {
1189
+ executionJobCount: 0,
1190
+ localJobCount: 0,
1191
+ workerSpawnCount: 0,
1192
+ projectionUpdateCount: 0,
1193
+ },
1194
+ });
1195
+ }
1196
+ if (routeClass.route === 'durable' && target.workspace) {
1197
+ return result({
1198
+ accepted: false,
1199
+ mode: 'durable',
1200
+ path: 'ephemeral_workspace_promotion_required',
1201
+ repoId: repository.repoId,
1202
+ checkoutId: repository.activeCheckoutId,
1203
+ workspace: target.workspace,
1204
+ message: 'Ephemeral workspaces must be registered before durable remote effects so Process identity and reconciliation have a stable repository authority.',
1205
+ suggestedOperation: 'repository_register',
1206
+ });
1207
+ }
1208
+ if (routeClass.route === 'process_direct' || routeClass.route === 'process_managed' || routeClass.route === 'durable') {
963
1209
  const processResult = await executeRepositoryCommandViaProcessRuntime({
964
1210
  controllerHome,
965
1211
  repository,
@@ -972,6 +1218,7 @@ export async function callRepositoryTool(
972
1218
  maxOutputBytes,
973
1219
  returnHandleImmediately,
974
1220
  requestId: typeof args.request_id === 'string' ? args.request_id : undefined,
1221
+ workId: executionIdentity.workId,
975
1222
  executionIdentity,
976
1223
  allowNonGitWorkspace: target.workspace !== undefined,
977
1224
  });
@@ -1033,13 +1280,14 @@ export async function callRepositoryTool(
1033
1280
  stdout: processResult.stdout,
1034
1281
  stderr: processResult.stderr,
1035
1282
  durableSideEffects: processResult.durableSideEffects,
1283
+ guidance: explorationGuidance,
1036
1284
  next: directWithoutHandle
1037
1285
  ? processResult.reason === 'readonly_fast_path'
1038
1286
  ? 'Bounded readonly execution completed without Process record / Lease / Local Job / ExecutionJob / Worker.'
1039
1287
  : 'Bounded ephemeral workspace execution completed without Process record / Lease / Local Job / ExecutionJob / Worker.'
1040
1288
  : processResult.route === 'process_direct' || completed
1041
1289
  ? 'Process Runtime completed without Local Job / ExecutionJob / Worker.'
1042
- : `Process Runtime is managing ${handle?.processId}; poll process_get/process_wait instead of creating a Local Job.`,
1290
+ : `Process Runtime is managing ${handle?.processId}. Continue independent work; use process_get only if an observation can change the next decision, then join with process_wait when this result becomes a dependency. Do not re-run or periodically poll.`,
1043
1291
  detailLevel,
1044
1292
  });
1045
1293
  payload.status = status;
@@ -1111,6 +1359,7 @@ export async function callRepositoryTool(
1111
1359
  timeoutMs,
1112
1360
  maxOutputBytes,
1113
1361
  requestId: typeof args.request_id === 'string' ? args.request_id : undefined,
1362
+ workId: executionIdentity.workId,
1114
1363
  executionIdentity,
1115
1364
  });
1116
1365
  if (processResult.route === 'process_direct') {
@@ -1155,6 +1404,12 @@ export async function callRepositoryTool(
1155
1404
  routing: compactRoutingSummary({ path: 'durable', mode: 'durable', reasons: [routingDecision.reasons.join(','), 'local_bridge_execution_retired'] }),
1156
1405
  message: 'This command requires explicit external Controller handling; Local Bridge Jobs are retired for repository commands.',
1157
1406
  suggestedOperation: 'Create or claim WorkContract, then use rh_work.launcher_start or a Process Runtime-compatible command.',
1407
+ externalEffect: {
1408
+ outcome: 'not_started',
1409
+ ambiguousFailureOutcome: 'outcome_unknown',
1410
+ replayPolicy: 'never_auto_retry',
1411
+ reconciliation: 'After an ambiguous dispatch failure, inspect the remote ref/registry/release state before deciding whether to retry.',
1412
+ },
1158
1413
  });
1159
1414
  }
1160
1415
  case 'repository_batch_execute': {