@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
@@ -1,19 +1,26 @@
1
1
  /**
2
- * repository_command_execute → Unified Process Runtime.
2
+ * repository_command_execute → three explicit execution lanes.
3
3
  *
4
- * Short readonly / focused commands: Direct (wait briefly, return result).
5
- * Longer local build/test: Managed (same spawn, return handle).
6
- * release / remote / non-idempotent: Durable (caller keeps ExecutionJob path).
4
+ * - Ephemeral Direct: bounded ordinary reads and commands that finish inside
5
+ * the interactive budget.
6
+ * - Lightweight Managed: the same local command remains alive in memory and
7
+ * returns a bounded handle, without SQLite, Lease, or recovery membership.
8
+ * - Durable: explicit Work/external/release boundaries only.
7
9
  */
8
10
 
9
11
  import type { RepositoryRecord } from '../../../cli/repositories/types';
10
- import { classifyRepositoryCommand } from '../../../cli/repositories/command-classifier';
12
+ import { repositoryGitStatus } from '../../../cli/repositories/structured-git';
13
+ import { classifyRepositoryCommand, fixedShellWrapperCommand } from '../../../cli/repositories/command-classifier';
11
14
  import {
12
15
  executeRepositoryCommandAsync,
13
16
  executeRepositoryReadOnlyCommandDirect,
14
17
  type RepositoryCommandExecution,
15
18
  } from '../../../cli/repositories/command-executor';
16
19
  import { normalizeRepositoryCommand } from '../../../cli/repositories/command-normalization';
20
+ import {
21
+ assertRepositoryCommandNoPluginExecutionBypass,
22
+ assertRepositoryCommandStableHostIdentity,
23
+ } from '../../../cli/repositories/command-scope';
17
24
  import { claimsForRepositoryCommand, scopeResourceClaims, toProcessClaims } from './resource-claims';
18
25
  import {
19
26
  spawnManagedProcess,
@@ -27,6 +34,15 @@ import { DEFAULT_INTERACTIVE_WAIT_MS } from './types';
27
34
  import { durationAwareInteractiveWaitMs } from './interactive-admission';
28
35
  import { isFocusedCheckCommand } from '../thin-harness/execution-router';
29
36
  import { assertExecutionIdentity, type ResolvedExecutionIdentity } from '../../control-plane/execution/execution-identity';
37
+ import { readWorkHandle, transitionWorkHandle } from '../../control-plane/execution/work-handle-store';
38
+ import {
39
+ cancelLightweightProcess,
40
+ getLightweightProcessHandle,
41
+ isLightweightProcessId,
42
+ readLightweightProcessLogs,
43
+ startLightweightRepositoryCommand,
44
+ waitForLightweightProcess,
45
+ } from './lightweight-managed';
30
46
 
31
47
  export type RepositoryCommandRoute =
32
48
  | 'process_direct'
@@ -76,6 +92,19 @@ export interface RepositoryCommandProcessResult {
76
92
  projectionUpdateCount: number;
77
93
  };
78
94
  suggestedOperation?: string;
95
+ externalEffect?: {
96
+ outcome: 'not_started' | 'outcome_unknown';
97
+ replayPolicy: 'never_auto_retry';
98
+ reconciliation: string;
99
+ };
100
+ executionMetrics?: {
101
+ lane: 'ephemeral_direct' | 'lightweight_managed' | 'durable_process' | 'durable_external';
102
+ preSpawnHarnessMs?: number;
103
+ childDurationMs?: number;
104
+ interactiveReturnMs?: number;
105
+ durableWrites: number;
106
+ leaseOperations: number;
107
+ };
79
108
  }
80
109
 
81
110
  const emptyEffects = {
@@ -85,6 +114,154 @@ const emptyEffects = {
85
114
  projectionUpdateCount: 0,
86
115
  };
87
116
 
117
+ export type WorkHeadSettlementReason =
118
+ | 'settled'
119
+ | 'no_work'
120
+ | 'command_not_successful'
121
+ | 'work_handle_missing'
122
+ | 'identity_mismatch'
123
+ | 'terminal_handle'
124
+ | 'branch_changed'
125
+ | 'head_unavailable'
126
+ | 'head_unchanged'
127
+ | 'concurrent_lifecycle_write';
128
+
129
+ export interface WorkHeadSettlementResult {
130
+ settled: boolean;
131
+ reason: WorkHeadSettlementReason;
132
+ previousHead?: string;
133
+ currentHead?: string;
134
+ }
135
+
136
+ /**
137
+ * Settle the one legitimate post-command Work HEAD transition without weakening
138
+ * the pre-command execution identity fence. The command has already been
139
+ * admitted only if repo/checkout/branch/expectedHead matched the WorkHandle.
140
+ * After success, adopt the exact current HEAD only while the same checkout and
141
+ * branch still own the Work. CAS failure is a bounded no-op: finalization keeps
142
+ * failing closed instead of accepting a concurrent lifecycle writer.
143
+ */
144
+ export function settleWorkHandleExpectedHeadAfterRepositoryCommand(input: {
145
+ controllerHome: string;
146
+ repository: RepositoryRecord;
147
+ executionIdentity: ResolvedExecutionIdentity;
148
+ workId?: string;
149
+ ok?: boolean;
150
+ cancelled?: boolean;
151
+ timedOut?: boolean;
152
+ }): WorkHeadSettlementResult {
153
+ const workId = input.workId?.trim();
154
+ if (!workId) return { settled: false, reason: 'no_work' };
155
+ if (input.ok !== true || input.cancelled === true || input.timedOut === true) {
156
+ return { settled: false, reason: 'command_not_successful' };
157
+ }
158
+ const handle = readWorkHandle(input.controllerHome, input.repository.repoId, workId);
159
+ if (!handle) return { settled: false, reason: 'work_handle_missing' };
160
+ if (
161
+ handle.repositoryId !== input.executionIdentity.repositoryId
162
+ || handle.checkoutId !== input.executionIdentity.checkoutId
163
+ || handle.workId !== input.executionIdentity.workId
164
+ ) return { settled: false, reason: 'identity_mismatch' };
165
+ if (handle.state === 'merged' || handle.state === 'cleaned' || handle.state === 'failed_terminal_cleanup') {
166
+ return { settled: false, reason: 'terminal_handle' };
167
+ }
168
+ const status = repositoryGitStatus(input.repository);
169
+ if (!status.branch || status.branch !== handle.branch) {
170
+ return { settled: false, reason: 'branch_changed', previousHead: handle.expectedHead, currentHead: status.head ?? undefined };
171
+ }
172
+ const currentHead = status.head?.trim();
173
+ if (!currentHead) return { settled: false, reason: 'head_unavailable', previousHead: handle.expectedHead };
174
+ if (currentHead === handle.expectedHead) {
175
+ return { settled: false, reason: 'head_unchanged', previousHead: handle.expectedHead, currentHead };
176
+ }
177
+ try {
178
+ transitionWorkHandle(input.controllerHome, handle, handle.state, { expectedHead: currentHead });
179
+ return { settled: true, reason: 'settled', previousHead: handle.expectedHead, currentHead };
180
+ } catch (error) {
181
+ if (error instanceof Error && error.message.includes('CONTROL_PLANE_REVISION_CONFLICT')) {
182
+ return { settled: false, reason: 'concurrent_lifecycle_write', previousHead: handle.expectedHead, currentHead };
183
+ }
184
+ throw error;
185
+ }
186
+ }
187
+
188
+ const STANDALONE_RECOVERY_LIFECYCLE_COMMANDS = new Set([
189
+ 'restart-runtime',
190
+ 'bootstrap-cutover',
191
+ 'restart-connector',
192
+ 'stage-and-activate-runtime',
193
+ 'recover',
194
+ 'rollback',
195
+ 'restart',
196
+ 'activate-runtime',
197
+ 'install',
198
+ ]);
199
+
200
+ function commandBasename(value: string): string {
201
+ return value.split(/[\\/]/).pop()?.toLowerCase() ?? '';
202
+ }
203
+
204
+ function recoveryLifecycleFromArgv(executable: string, args: readonly string[]): string | undefined {
205
+ const executableName = commandBasename(executable);
206
+ let recoveryArgs: readonly string[];
207
+ if (executableName === 'forge' || executableName === 'forge.mjs') {
208
+ recoveryArgs = args;
209
+ } else if (['bun', 'node', 'nodejs'].includes(executableName)) {
210
+ const scriptIndex = args.findIndex((arg) => {
211
+ const name = commandBasename(arg);
212
+ return name === 'forge' || name === 'forge.mjs';
213
+ });
214
+ if (scriptIndex < 0) return undefined;
215
+ recoveryArgs = args.slice(scriptIndex + 1);
216
+ } else {
217
+ return undefined;
218
+ }
219
+ if (recoveryArgs[0] !== 'recovery') return undefined;
220
+ const subcommand = recoveryArgs[1]?.toLowerCase();
221
+ return subcommand && STANDALONE_RECOVERY_LIFECYCLE_COMMANDS.has(subcommand) ? subcommand : undefined;
222
+ }
223
+
224
+ function recoveryLifecycleFromShell(shellCommand: string): string | undefined {
225
+ const match = /^\s*(?:exec\s+)?(?:(?:\S*[\\/])?(?:bun|node|nodejs)\s+)?(?:\S*[\\/])?forge(?:\.mjs)?\s+recovery\s+([a-z-]+)\b/i.exec(shellCommand);
226
+ const subcommand = match?.[1]?.toLowerCase();
227
+ return subcommand && STANDALONE_RECOVERY_LIFECYCLE_COMMANDS.has(subcommand) ? subcommand : undefined;
228
+ }
229
+
230
+ function repositoryCommandRecoveryLifecycle(command: string | readonly string[]): string | undefined {
231
+ const normalized = normalizeRepositoryCommand(command);
232
+ if (normalized.kind === 'shell') return recoveryLifecycleFromShell(normalized.shellCommand ?? '');
233
+ const direct = recoveryLifecycleFromArgv(normalized.executable ?? '', normalized.args ?? []);
234
+ if (direct) return direct;
235
+ const argv = normalized.value as string[];
236
+ const wrapped = fixedShellWrapperCommand(argv);
237
+ return wrapped ? recoveryLifecycleFromShell(wrapped) : undefined;
238
+ }
239
+
240
+ function rawGitCommitRequiresExplicitPathScope(command: string | readonly string[]): boolean {
241
+ const normalized = normalizeRepositoryCommand(command);
242
+ if (normalized.kind === 'shell') {
243
+ return /(?:^|[;&|]\s*)\s*git\s+commit\b/i.test(normalized.shellCommand ?? '');
244
+ }
245
+ const executable = commandBasename(normalized.executable ?? '');
246
+ const args = [...(normalized.args ?? [])];
247
+ if (executable !== 'git') {
248
+ const wrapped = fixedShellWrapperCommand(normalized.value as string[]);
249
+ return wrapped ? /(?:^|[;&|]\s*)\s*git\s+commit\b/i.test(wrapped) : false;
250
+ }
251
+ let index = 0;
252
+ while (index < args.length && args[index]?.startsWith('-')) {
253
+ const option = args[index]!;
254
+ if (['-c', '-C', '--git-dir', '--work-tree', '--namespace'].includes(option)) index += 2;
255
+ else index += 1;
256
+ }
257
+ if (args[index]?.toLowerCase() !== 'commit') return false;
258
+ const commitArgs = args.slice(index + 1);
259
+ const pathSeparator = commitArgs.indexOf('--');
260
+ const hasExplicitOnly = commitArgs.includes('--only') || commitArgs.includes('-o');
261
+ const paths = pathSeparator >= 0 ? commitArgs.slice(pathSeparator + 1).filter(Boolean) : [];
262
+ return !(hasExplicitOnly && paths.length > 0);
263
+ }
264
+
88
265
  function toProcessCommand(command: string | readonly string[], cwd: string): ProcessCommandSpec {
89
266
  const normalized = normalizeRepositoryCommand(command);
90
267
  if (normalized.kind === 'argv') {
@@ -113,37 +290,53 @@ export function classifyRepositoryCommandRoute(
113
290
  timeoutMs?: number;
114
291
  } = {},
115
292
  ): { route: RepositoryCommandRoute; reason: string } {
293
+ if (repositoryCommandRecoveryLifecycle(command)) {
294
+ return { route: 'reject', reason: 'standalone_recovery_lifecycle_required' };
295
+ }
296
+ if (rawGitCommitRequiresExplicitPathScope(command)) {
297
+ return { route: 'reject', reason: 'git_commit_requires_explicit_path_scope' };
298
+ }
116
299
  if (options.forceDurable) {
117
300
  return { route: 'durable', reason: 'force_durable_or_async' };
118
301
  }
119
302
  const classification = classifyRepositoryCommand(command, options.defaultBranch);
120
- // ExecutionJobs are retired. Authorized local destructive/remote-risk commands
121
- // still execute through Process Runtime; SuperController owns retry/replay policy.
303
+ const text = Array.isArray(command) ? command.join(' ') : String(command);
304
+ // Classification chooses scheduling/receipt granularity only; it does not
305
+ // decide whether a scoped repository command is allowed to run. Known remote
306
+ // or broadly effectful commands use managed Process Runtime so existing
307
+ // resource claims and receipts can serialize/reconcile them. Unknown commands
308
+ // still execute instead of requiring a new allow rule.
122
309
  if (classification.risk === 'remote_write' || classification.risk === 'destructive') {
123
- return {
124
- route: 'process_managed',
125
- reason: `risk_${classification.risk}_via_process_runtime`,
126
- };
310
+ return { route: 'process_managed', reason: 'effectful_command_managed' };
127
311
  }
128
- // release / rollback style commands
129
- const text = Array.isArray(command) ? command.join(' ') : String(command);
130
- if (/\b(?:gh\s+release\s+(?:create|delete|edit|upload)|git\s+push|npm\s+publish)\b/i.test(text)) {
131
- return { route: 'durable', reason: 'release_or_remote_mutation' };
312
+ const argv = Array.isArray(command) ? command : [];
313
+ const executable = argv[0]?.split(/[\\/]/).pop()?.toLowerCase();
314
+ const shellWrapped = Boolean(executable
315
+ && ['bash', 'sh', 'zsh', 'fish', 'dash', 'cmd', 'powershell', 'pwsh'].includes(executable)
316
+ && argv.slice(1).some((arg) => ['-c', '-lc', '/c', '-Command'].includes(arg)));
317
+ const wrappedShellCommand = shellWrapped ? fixedShellWrapperCommand(argv) : undefined;
318
+ const wrapsInlineInterpreter = Boolean(wrappedShellCommand
319
+ && /^\s*(?:exec\s+)?(?:node|nodejs|bun|deno|ruby|perl|python\d*)\b[^\n]*(?:\s-c\b|\s-e\b|\s--eval\b)/i.test(wrappedShellCommand));
320
+ const wrapsObviousExternalIo = Boolean(wrappedShellCommand
321
+ && /\b(?:curl|wget|ssh|scp|sftp|ftp|telnet|nc|ncat)\b|https?:\/\//i.test(wrappedShellCommand));
322
+ if (shellWrapped && classification.risk !== 'readonly') {
323
+ if (wrapsObviousExternalIo && !wrapsInlineInterpreter) {
324
+ return { route: 'process_managed', reason: 'shell_wrapper_requires_managed_boundary' };
325
+ }
326
+ return { route: 'process_direct', reason: 'lightweight_local_shell_wrapper' };
327
+ }
328
+ if (executable && ['node', 'nodejs', 'bun', 'deno', 'ruby', 'perl', 'python', 'python3'].includes(executable)
329
+ && argv.slice(1).some((arg) => ['-c', '-e', '--eval'].includes(arg))) {
330
+ return { route: 'process_direct', reason: 'lightweight_local_inline_interpreter' };
132
331
  }
133
332
  if (classification.risk === 'readonly') {
134
- // Short readonly → direct. Only an *explicit* long timeout upgrades to managed.
135
- if (typeof options.timeoutMs === 'number' && options.timeoutMs > 30_000) {
136
- return { route: 'process_managed', reason: 'readonly_long_timeout' };
137
- }
138
333
  return { route: 'process_direct', reason: 'readonly_fast_path' };
139
334
  }
140
335
  // workspace_write / build-test
141
336
  if (isFocusedCheckCommand(command) || /\b(?:test|typecheck|lint|build|check)\b/i.test(text)) {
142
- return { route: 'process_managed', reason: 'local_build_or_test' };
337
+ return { route: 'process_direct', reason: 'ephemeral_local_build_or_test' };
143
338
  }
144
- // Unknown mutating local command remains one local Managed Process. Timeout is
145
- // a process lifecycle budget and must never select a different architecture.
146
- return { route: 'process_managed', reason: 'local_workspace_mutation' };
339
+ return { route: 'process_direct', reason: 'ephemeral_local_workspace_mutation' };
147
340
  }
148
341
 
149
342
  /**
@@ -153,18 +346,38 @@ export function classifyRepositoryCommandRoute(
153
346
  export async function executeRepositoryCommandViaProcessRuntime(
154
347
  input: RepositoryCommandProcessInput,
155
348
  ): Promise<RepositoryCommandProcessResult> {
349
+ // Process Runtime intentionally supports some command forms that the legacy
350
+ // repository executor rejects (for example bounded typed eval). Keep that
351
+ // surface, but fail closed before routing any macOS TCC-sensitive host tool:
352
+ // a release-specific forge-runtime executable must never own those grants.
353
+ assertRepositoryCommandStableHostIdentity(input.command);
354
+ assertRepositoryCommandNoPluginExecutionBypass(input.command);
156
355
  const decision = classifyRepositoryCommandRoute(input.command, {
157
356
  forceDurable: input.forceDurable,
357
+ // Work identity is continuity/audit metadata only. It must not change the
358
+ // execution lane of an otherwise ordinary local repository command.
158
359
  defaultBranch: input.repository.defaultBranch,
159
360
  timeoutMs: input.timeoutMs,
160
361
  });
161
-
162
- if (decision.route === 'durable' || decision.route === 'reject') {
362
+ // Only explicit lifecycle/delivery rejection or a caller-requested durable
363
+ // mode stops here. Ordinary command effects, including remote writes, execute
364
+ // through Process Runtime; classification does not become an authorization gate.
365
+ if (decision.route === 'reject' || decision.route === 'durable') {
163
366
  return {
164
367
  route: decision.route,
165
368
  reason: decision.reason,
166
369
  durableSideEffects: emptyEffects,
167
- suggestedOperation: 'repository_command_execute via Durable Work / Local Job',
370
+ suggestedOperation: decision.reason === 'standalone_recovery_lifecycle_required'
371
+ ? 'Use the standalone Forge Recovery connector for canonical Runtime/connector lifecycle operations.'
372
+ : 'repository_command_execute via Durable Work / Local Job',
373
+ externalEffect: decision.route === 'durable'
374
+ ? {
375
+ outcome: 'not_started',
376
+ replayPolicy: 'never_auto_retry',
377
+ reconciliation: 'If a separate external controller later loses the result after dispatch, report outcome_unknown and inspect remote state before any retry.',
378
+ }
379
+ : undefined,
380
+ executionMetrics: { lane: 'durable_external', durableWrites: 0, leaseOperations: 0 },
168
381
  };
169
382
  }
170
383
 
@@ -191,12 +404,12 @@ export async function executeRepositoryCommandViaProcessRuntime(
191
404
  };
192
405
  }
193
406
 
194
- // A short readonly command owns no durable Process/Lease state. Validate the
195
- // same immutable repository identity first, then execute with the bounded
196
- // non-persistent reader. This keeps reads available while a stale/passive
197
- // Runtime remains correctly fenced from Controller writes.
407
+ // Ordinary local commands own no durable Process/Lease state. Validate the
408
+ // immutable repository identity first, then execute through the authorized
409
+ // non-persistent repository executor. Runtime/release durability is not a
410
+ // command-execution tax.
198
411
  const canonicalCommand = normalizeRepositoryCommand(input.command);
199
- if (decision.route === 'process_direct' && canonicalCommand.kind === 'argv') {
412
+ if (decision.route === 'process_direct') {
200
413
  assertExecutionIdentity({
201
414
  controllerHome: input.controllerHome,
202
415
  identity: executionIdentity,
@@ -204,14 +417,68 @@ export async function executeRepositoryCommandViaProcessRuntime(
204
417
  requestedRepoId: input.repository.repoId,
205
418
  requestedCheckoutId: input.repository.activeCheckoutId,
206
419
  });
207
- const direct = await executeRepositoryReadOnlyCommandDirect(input.repository, {
420
+ const directInput = {
208
421
  command: input.command,
209
422
  cwd: input.cwd,
210
- timeoutMs: Math.min(input.timeoutMs ?? 30_000, 30_000),
423
+ timeoutMs: input.timeoutMs,
211
424
  maxOutputBytes: input.maxOutputBytes,
212
425
  signal: input.signal,
213
426
  allowNonGitWorkspace: executionIdentity.authority === 'ephemeral_workspace',
214
- });
427
+ allowOpaqueLocalScript: decision.reason === 'lightweight_local_shell_wrapper'
428
+ || decision.reason === 'lightweight_local_inline_interpreter',
429
+ };
430
+ const readonly = canonicalCommand.kind === 'argv'
431
+ && classifyRepositoryCommand(input.command, input.repository.defaultBranch).risk === 'readonly';
432
+ if (!readonly) {
433
+ const deferLongPreparation = decision.reason === 'ephemeral_local_build_or_test';
434
+ const interactiveWaitMs = deferLongPreparation
435
+ ? 0
436
+ : durationAwareInteractiveWaitMs(
437
+ input.command,
438
+ input.returnHandleImmediately ? 0 : input.interactiveWaitMs,
439
+ Math.min(DEFAULT_INTERACTIVE_WAIT_MS, 250),
440
+ );
441
+ const timeoutMs = Math.max(
442
+ interactiveWaitMs + 1,
443
+ Math.min(input.timeoutMs ?? 15 * 60_000, 24 * 60 * 60_000),
444
+ );
445
+ const lightweight = await startLightweightRepositoryCommand({
446
+ controllerHome: input.controllerHome,
447
+ repository: input.repository,
448
+ execution: directInput,
449
+ interactiveWaitMs,
450
+ timeoutMs,
451
+ maxOutputBytes: input.maxOutputBytes,
452
+ workId: input.workId,
453
+ commandId: input.commandId ?? input.requestId,
454
+ deferStart: deferLongPreparation,
455
+ reuseActiveEquivalent: deferLongPreparation,
456
+ onCompleted: (result) => {
457
+ settleWorkHandleExpectedHeadAfterRepositoryCommand({
458
+ controllerHome: input.controllerHome,
459
+ repository: input.repository,
460
+ executionIdentity,
461
+ workId: input.workId,
462
+ ok: result.ok,
463
+ cancelled: result.cancelled,
464
+ timedOut: result.timedOut,
465
+ });
466
+ },
467
+ });
468
+ const handle = lightweight.handle;
469
+ return {
470
+ route: handle.completed ? 'process_direct' : 'process_managed',
471
+ reason: handle.completed ? decision.reason : 'interactive_budget_exceeded_lightweight_handle',
472
+ process: handle,
473
+ ok: handle.ok,
474
+ exitCode: handle.exitCode,
475
+ stdout: handle.stdout,
476
+ stderr: handle.stderr,
477
+ durableSideEffects: emptyEffects,
478
+ executionMetrics: lightweight.metrics,
479
+ };
480
+ }
481
+ const direct = await executeRepositoryReadOnlyCommandDirect(input.repository, directInput);
215
482
  return {
216
483
  route: 'process_direct',
217
484
  reason: decision.reason,
@@ -224,6 +491,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
224
491
  authorizationDecision: direct.authorizationDecision,
225
492
  approvalRequestId: direct.approvalRequestId,
226
493
  durableSideEffects: emptyEffects,
494
+ executionMetrics: { lane: 'ephemeral_direct', durableWrites: 0, leaseOperations: 0 },
227
495
  };
228
496
  }
229
497
 
@@ -273,6 +541,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
273
541
  executionIdentity.repositoryId,
274
542
  executionIdentity.checkoutId,
275
543
  input.repository.defaultBranch,
544
+ input.repository.canonicalRoot,
276
545
  ),
277
546
  executionIdentity.repositoryId,
278
547
  executionIdentity.checkoutId,
@@ -312,12 +581,49 @@ export async function executeRepositoryCommandViaProcessRuntime(
312
581
  stdout: handle.stdout,
313
582
  stderr: handle.stderr,
314
583
  durableSideEffects: handle.durableSideEffects,
584
+ executionMetrics: {
585
+ lane: 'durable_process',
586
+ interactiveReturnMs: interactiveWaitMs,
587
+ durableWrites: 1,
588
+ leaseOperations: claims.length,
589
+ },
315
590
  };
316
591
  }
317
592
 
318
- export {
319
- getProcessHandle as getRepositoryCommandProcess,
320
- waitForProcess as waitRepositoryCommandProcess,
321
- cancelProcess as cancelRepositoryCommandProcess,
322
- readProcessLogs as readRepositoryCommandProcessLogs,
323
- };
593
+ export function getRepositoryCommandProcess(controllerHome: string, repoId: string, processId: string): ProcessHandle | undefined {
594
+ return isLightweightProcessId(processId)
595
+ ? getLightweightProcessHandle(controllerHome, repoId, processId)
596
+ : getProcessHandle(controllerHome, repoId, processId);
597
+ }
598
+
599
+ export function waitRepositoryCommandProcess(
600
+ controllerHome: string,
601
+ repoId: string,
602
+ processId: string,
603
+ options?: Parameters<typeof waitForProcess>[3],
604
+ ): Promise<ProcessHandle> {
605
+ return isLightweightProcessId(processId)
606
+ ? waitForLightweightProcess(controllerHome, repoId, processId, options)
607
+ : waitForProcess(controllerHome, repoId, processId, options);
608
+ }
609
+
610
+ export function cancelRepositoryCommandProcess(
611
+ controllerHome: string,
612
+ repoId: string,
613
+ processId: string,
614
+ ): Promise<ProcessHandle> {
615
+ return isLightweightProcessId(processId)
616
+ ? cancelLightweightProcess(controllerHome, repoId, processId)
617
+ : cancelProcess(controllerHome, repoId, processId);
618
+ }
619
+
620
+ export function readRepositoryCommandProcessLogs(
621
+ controllerHome: string,
622
+ repoId: string,
623
+ processId: string,
624
+ maxBytes?: number,
625
+ ) {
626
+ return isLightweightProcessId(processId)
627
+ ? readLightweightProcessLogs(controllerHome, repoId, processId, maxBytes)
628
+ : readProcessLogs(controllerHome, repoId, processId, maxBytes);
629
+ }