@moretea-labs/forge 1.6.0 → 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 (396) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +2 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  17. package/assets/plugin-registry.v1.json +1 -1
  18. package/assets/reference-configs/document-generation.md +1 -1
  19. package/assets/reference-configs/harness-overview.md +1 -1
  20. package/assets/reference-configs/sprint-contracts.md +3 -3
  21. package/assets/skill-version.json +2 -2
  22. package/assets/templates/contract.template.md +2 -7
  23. package/assets/templates/helpers/capture-plan.sh +2 -2
  24. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  25. package/assets/templates/helpers/contract-worktree.sh +1 -21
  26. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  27. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  28. package/assets/templates/helpers/new-plan.sh +1 -1
  29. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  30. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  31. package/assets/templates/helpers/verify-contract.sh +1 -111
  32. package/assets/templates/helpers/verify-sprint.sh +3 -180
  33. package/assets/templates/plan.template.md +1 -1
  34. package/assets/templates/review.template.md +9 -34
  35. package/assets/workflow-contract.v1.json +10 -19
  36. package/docs/README.md +10 -10
  37. package/docs/architecture/CURRENT.md +170 -0
  38. package/docs/architecture/EVOLUTION.md +42 -0
  39. package/docs/architecture/current/README.md +4 -156
  40. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  41. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  42. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  43. package/docs/architecture/history/README.md +7 -0
  44. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  45. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  46. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  47. package/docs/architecture/history.md +3 -34
  48. package/docs/architecture/index.md +15 -98
  49. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  50. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  51. package/docs/architecture/versions/1.6.md +38 -0
  52. package/docs/architecture/versions/1.7.md +41 -0
  53. package/docs/forge-chatgpt-browser-engine.md +2 -2
  54. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  55. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  56. package/docs/forge-github-issue-launcher.md +1 -1
  57. package/docs/forge-runtime-self-healing-loop.md +1 -1
  58. package/docs/operations/controller-browser-plugin.md +26 -6
  59. package/docs/operations/controller-performance-and-502.md +124 -0
  60. package/docs/operations/controller-reliability-runbook.md +1 -1
  61. package/docs/operations/features.md +2 -2
  62. package/docs/operations/github-repository.md +1 -1
  63. package/docs/operations/homebrew.md +2 -2
  64. package/docs/operations/mcp-tool-exposure.md +5 -3
  65. package/docs/wiki/Architecture.md +6 -6
  66. package/docs/wiki/Implementation.md +2 -2
  67. package/docs/wiki/Integrations.md +2 -2
  68. package/docs/wiki/Work-Lifecycle.md +1 -1
  69. package/package.json +29 -10
  70. package/scripts/activate-source-baseline.ts +17 -13
  71. package/scripts/benchmark-background-check-overlap.ts +228 -0
  72. package/scripts/benchmark-mcp-host.sh +117 -0
  73. package/scripts/benchmark-mcp-transport.ts +170 -0
  74. package/scripts/benchmark-quality-harness.ts +146 -0
  75. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  76. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  77. package/scripts/benchmark-typescript-navigation.ts +186 -0
  78. package/scripts/benchmark-work-efficiency.ts +334 -0
  79. package/scripts/capture-plan.sh +3 -3
  80. package/scripts/check-agent-tooling.sh +8 -35
  81. package/scripts/check-architecture-sync.sh +17 -86
  82. package/scripts/check-mcp-compatibility.ts +14 -0
  83. package/scripts/check-runtime-architecture.mjs +107 -64
  84. package/scripts/check-stable-baseline.ts +92 -0
  85. package/scripts/check-task-workflow.sh +4 -4
  86. package/scripts/contract-worktree.sh +1 -21
  87. package/scripts/ensure-task-workflow.sh +22 -55
  88. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  89. package/scripts/install-standalone-recovery.ts +24 -0
  90. package/scripts/lib/project-init-lib.sh +15 -44
  91. package/scripts/migrate-project-template.sh +23 -1179
  92. package/scripts/new-plan.sh +1 -1
  93. package/scripts/plan-to-todo.sh +11 -36
  94. package/scripts/run-governed-gate.ts +2 -1
  95. package/scripts/ship-worktrees.sh +2 -29
  96. package/scripts/smoke-ios-physical-device.ts +4 -2
  97. package/scripts/smoke-runtime-recovery.ts +1 -35
  98. package/scripts/smoke-schedule-engine.ts +1 -20
  99. package/scripts/verify-contract.sh +1 -111
  100. package/scripts/verify-forge-runtime.sh +0 -1
  101. package/scripts/verify-sprint.sh +3 -180
  102. package/scripts/workflow-contract.ts +1 -5
  103. package/src/cli/commands/init-hook.ts +3 -3
  104. package/src/cli/commands/mcp.ts +9 -5
  105. package/src/cli/commands/plugin.ts +1 -1
  106. package/src/cli/commands/recovery.ts +25 -3
  107. package/src/cli/commands/runtime.ts +55 -3
  108. package/src/cli/commands/security.ts +12 -1
  109. package/src/cli/commands/setup-profile.ts +31 -21
  110. package/src/cli/controller/check-runner.ts +106 -3
  111. package/src/cli/controller/composite-result.ts +1 -1
  112. package/src/cli/controller/context/focus.ts +51 -0
  113. package/src/cli/controller/context/instruction-context.ts +114 -0
  114. package/src/cli/controller/context/known-paths.ts +76 -0
  115. package/src/cli/controller/context/query-planning.ts +110 -0
  116. package/src/cli/controller/context/rule-resolution.ts +532 -0
  117. package/src/cli/controller/context/source-materializer.ts +278 -0
  118. package/src/cli/controller/context/structural.ts +16 -0
  119. package/src/cli/controller/context/types.ts +286 -0
  120. package/src/cli/controller/context-pack.ts +785 -469
  121. package/src/cli/controller/launch-agents.ts +77 -2
  122. package/src/cli/controller/operational-plan.ts +2 -2
  123. package/src/cli/controller/work-mode.ts +6 -4
  124. package/src/cli/editing/edit-session.ts +60 -7
  125. package/src/cli/github/github.ts +133 -0
  126. package/src/cli/installer/managed-entries.ts +15 -1
  127. package/src/cli/local-bridge/console-automations.ts +54 -78
  128. package/src/cli/local-bridge/console-view-models.ts +1 -35
  129. package/src/cli/local-bridge/facade-api.ts +0 -217
  130. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  131. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  132. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  133. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  134. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  135. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  136. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  137. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  138. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  139. package/src/cli/local-bridge/job-store.ts +1 -490
  140. package/src/cli/local-bridge/server.ts +0 -513
  141. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  142. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  143. package/src/cli/mcp/auth.ts +5 -5
  144. package/src/cli/mcp/instructions.ts +3 -2
  145. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  146. package/src/cli/mcp/multi-repository.ts +16 -5
  147. package/src/cli/mcp/oauth.ts +175 -26
  148. package/src/cli/mcp/repository-tools.ts +247 -107
  149. package/src/cli/mcp/server.ts +537 -111
  150. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  151. package/src/cli/mcp/toolset-selection.ts +1 -1
  152. package/src/cli/mcp/toolset.ts +42 -5
  153. package/src/cli/mcp/transports/http.ts +101 -14
  154. package/src/cli/mcp/transports/session-registry.ts +43 -5
  155. package/src/cli/mcp/types.ts +3 -3
  156. package/src/cli/repositories/command-classifier.ts +101 -19
  157. package/src/cli/repositories/command-executor.ts +55 -521
  158. package/src/cli/repositories/command-process.ts +108 -0
  159. package/src/cli/repositories/command-scope.ts +99 -41
  160. package/src/cli/repositories/controller-home.ts +7 -8
  161. package/src/cli/repositories/git-command-executor.ts +2 -0
  162. package/src/cli/repositories/locks.ts +53 -16
  163. package/src/cli/repositories/repository-snapshot.ts +121 -0
  164. package/src/cli/repositories/selected-path-actions.ts +17 -9
  165. package/src/cli/repositories/structured-git.ts +61 -3
  166. package/src/cli/repository/inspector.ts +553 -63
  167. package/src/cli/repository/session-cache.ts +58 -17
  168. package/src/cli/v81-entry.ts +1 -4
  169. package/src/effects/fs-transaction.ts +85 -17
  170. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  171. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  172. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  173. package/src/runtime/context/lsp-client.ts +283 -0
  174. package/src/runtime/context/semantic-navigation.ts +275 -0
  175. package/src/runtime/context/swift-navigation.ts +565 -0
  176. package/src/runtime/context/typescript-navigation.ts +226 -0
  177. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  178. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +47 -8
  179. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  180. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  181. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  182. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  183. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  184. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  185. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  186. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +143 -8
  187. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  188. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
  189. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  190. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  191. package/src/runtime/control-plane/facade/controller-session-store.ts +132 -1
  192. package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
  193. package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
  194. package/src/runtime/control-plane/facade/index.ts +1 -0
  195. package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
  196. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  197. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  198. package/src/runtime/control-plane/facade/types.ts +22 -5
  199. package/src/runtime/control-plane/facade/work-contract-store.ts +175 -73
  200. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  201. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  202. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  203. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  204. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  205. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  206. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  207. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  208. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  209. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  210. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  211. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  212. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  213. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  214. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  215. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  216. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  217. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  218. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  219. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +355 -116
  220. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  221. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  222. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  223. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  224. package/src/runtime/control-plane/release-retention.ts +62 -2
  225. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  226. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  227. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  228. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  229. package/src/runtime/diagnostics/performance.ts +24 -1
  230. package/src/runtime/evidence/event-ledger.ts +1 -1
  231. package/src/runtime/execution/jobs/store.ts +58 -4
  232. package/src/runtime/execution/jobs/types.ts +1 -1
  233. package/src/runtime/execution/managed-workspace.ts +203 -1
  234. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  235. package/src/runtime/execution/process-runtime/check-result.ts +53 -0
  236. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  237. package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
  238. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  239. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  240. package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
  241. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  242. package/src/runtime/execution/process-runtime/runtime.ts +43 -3
  243. package/src/runtime/execution/process-runtime/store.ts +1 -0
  244. package/src/runtime/execution/process-runtime/types.ts +2 -0
  245. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  246. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  247. package/src/runtime/execution/workers/executor.ts +0 -48
  248. package/src/runtime/gateway/mcp/execution-tools.ts +1192 -114
  249. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  250. package/src/runtime/gateway/mcp/router.ts +184 -82
  251. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  252. package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
  253. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  254. package/src/runtime/plugins/browser-adapter.ts +2901 -241
  255. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  256. package/src/runtime/plugins/browser-handoff.ts +92 -2
  257. package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
  258. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  259. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  260. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  261. package/src/runtime/plugins/browser-runtime.ts +183 -0
  262. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  263. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  264. package/src/runtime/plugins/desktop-operator-registration.ts +8 -2
  265. package/src/runtime/plugins/external-adapter.ts +324 -3
  266. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  267. package/src/runtime/plugins/first-party-registry.ts +2 -0
  268. package/src/runtime/plugins/github-adapter.ts +125 -0
  269. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  270. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  271. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  272. package/src/runtime/plugins/ios-adapter.ts +106 -16
  273. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  274. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  275. package/src/runtime/plugins/lightweight-action.ts +153 -0
  276. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  277. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  278. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  279. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  280. package/src/runtime/plugins/resend-adapter.ts +1 -1
  281. package/src/runtime/plugins/store.ts +269 -29
  282. package/src/runtime/plugins/types.ts +35 -2
  283. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  284. package/src/runtime/projections/git-status-sampler.ts +16 -16
  285. package/src/runtime/projections/materialized-view.ts +28 -10
  286. package/src/runtime/recovery/maintenance-executor.ts +393 -28
  287. package/src/runtime/root/mcp-transport.ts +180 -10
  288. package/src/runtime/root/package-connector-service.ts +186 -8
  289. package/src/runtime/root/package-runtime-release.ts +143 -17
  290. package/src/runtime/root/package-runtime-service.ts +430 -22
  291. package/src/runtime/root/release-manifest.ts +7 -2
  292. package/src/runtime/root/release-materialize.ts +19 -0
  293. package/src/runtime/root/release-store.ts +48 -11
  294. package/src/runtime/root/runtime.ts +23 -5
  295. package/src/runtime/root/service.ts +100 -29
  296. package/src/runtime/root/types.ts +2 -0
  297. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  298. package/src/runtime/shared/trusted-executable.ts +62 -0
  299. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  300. package/src/runtime/standalone-recovery/core.ts +700 -92
  301. package/src/runtime/standalone-recovery/entry.ts +99 -23
  302. package/src/runtime/standalone-recovery/installer.ts +19 -3
  303. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  304. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  305. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  306. package/src/runtime/workflow/schedules/engine.ts +287 -151
  307. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  308. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  309. package/src/runtime/workflow/schedules/store.ts +131 -10
  310. package/src/runtime/workflow/schedules/types.ts +2 -4
  311. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  312. package/src/testing/test-governance.ts +8 -40
  313. package/assets/reference-configs/heartbeat-triage.md +0 -57
  314. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  315. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  316. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  317. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  318. package/docs/architecture/current/architecture-invariants.md +0 -331
  319. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  320. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  321. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  322. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  323. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  324. package/docs/architecture/current/entity-model.md +0 -612
  325. package/docs/architecture/current/external-plugin-governance.md +0 -396
  326. package/docs/architecture/current/failure-recovery.md +0 -447
  327. package/docs/architecture/current/governance.md +0 -213
  328. package/docs/architecture/current/human-interaction-plane.md +0 -87
  329. package/docs/architecture/current/implementation-status.md +0 -128
  330. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  331. package/docs/architecture/current/legacy-governance.md +0 -58
  332. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  333. package/docs/architecture/current/migration-roadmap.md +0 -105
  334. package/docs/architecture/current/multi-repository-execution.md +0 -371
  335. package/docs/architecture/current/operations-runbook.md +0 -45
  336. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  337. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  338. package/docs/architecture/current/repository-access-modes.md +0 -102
  339. package/docs/architecture/current/route-policy.md +0 -15
  340. package/docs/architecture/current/route-session-concurrency.md +0 -85
  341. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  342. package/docs/architecture/current/runtime-directory-map.md +0 -49
  343. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  344. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  345. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  346. package/docs/architecture/current/system-overview.md +0 -169
  347. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  348. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  349. package/docs/architecture/current/thin-harness-v1.md +0 -321
  350. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  351. package/docs/forge-autonomous-goal-loop.md +0 -9
  352. package/docs/forge-provider-configuration.md +0 -7
  353. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  354. package/docs/operations/gmail-assistant-routines.md +0 -186
  355. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  356. package/docs/operations/provider-configuration.md +0 -31
  357. package/scripts/harness-trace-grade.sh +0 -150
  358. package/scripts/heartbeat-triage.sh +0 -361
  359. package/scripts/maintenance-triage.sh +0 -167
  360. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  361. package/src/cli/repositories/goal-registry.ts +0 -239
  362. package/src/runtime/assistant/action-proposals.ts +0 -279
  363. package/src/runtime/assistant/intent.ts +0 -395
  364. package/src/runtime/assistant/model-provider.ts +0 -460
  365. package/src/runtime/assistant/openapi.ts +0 -223
  366. package/src/runtime/assistant/policy.ts +0 -89
  367. package/src/runtime/assistant/readiness.ts +0 -258
  368. package/src/runtime/assistant/routine-runtime.ts +0 -477
  369. package/src/runtime/assistant/schedule-binding.ts +0 -171
  370. package/src/runtime/assistant/standing-grants.ts +0 -375
  371. package/src/runtime/assistant/store.ts +0 -243
  372. package/src/runtime/assistant/types.ts +0 -135
  373. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  374. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  375. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  376. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  377. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  378. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  379. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  380. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  381. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  382. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  383. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  384. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  385. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  386. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  387. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  388. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  389. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  390. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  391. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  392. package/src/runtime/workflow/findings/store.ts +0 -204
  393. package/src/runtime/workflow/findings/types.ts +0 -58
  394. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  395. package/src/runtime/workflow/portfolio/store.ts +0 -185
  396. 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
 
@@ -313,12 +581,49 @@ export async function executeRepositoryCommandViaProcessRuntime(
313
581
  stdout: handle.stdout,
314
582
  stderr: handle.stderr,
315
583
  durableSideEffects: handle.durableSideEffects,
584
+ executionMetrics: {
585
+ lane: 'durable_process',
586
+ interactiveReturnMs: interactiveWaitMs,
587
+ durableWrites: 1,
588
+ leaseOperations: claims.length,
589
+ },
316
590
  };
317
591
  }
318
592
 
319
- export {
320
- getProcessHandle as getRepositoryCommandProcess,
321
- waitForProcess as waitRepositoryCommandProcess,
322
- cancelProcess as cancelRepositoryCommandProcess,
323
- readProcessLogs as readRepositoryCommandProcessLogs,
324
- };
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
+ }
@@ -9,8 +9,9 @@
9
9
  import { existsSync, readFileSync, readdirSync, unlinkSync, statSync } from 'fs';
10
10
  import { join } from 'path';
11
11
  import { ensureRepositoryControllerLayout, repositoryControllerRoot } from '../../../cli/repositories/controller-home';
12
- import { listActiveProcessIds } from './store';
13
- import type { ProcessRuntimeStatus } from './types';
12
+ import { getProcessRecord, listActiveProcessIds } from './store';
13
+ import { reconcileStaleManagedProcessForMaintenance } from './runtime';
14
+ import { isManagedProcessActive, type ProcessRuntimeStatus } from './types';
14
15
  import { assertRuntimeMayWrite } from '../../root/write-fence';
15
16
  import { isProcessAlive } from '../../shared/process-tree';
16
17
 
@@ -21,6 +22,7 @@ const TERMINAL: ReadonlySet<ProcessRuntimeStatus> = new Set([
21
22
  'cancelled',
22
23
  'orphaned',
23
24
  'completed_unknown',
25
+ 'unknown',
24
26
  ]);
25
27
 
26
28
  export interface ProcessGcOptions {
@@ -32,6 +34,10 @@ export interface ProcessGcOptions {
32
34
  maxTerminalRecords?: number;
33
35
  /** Also delete associated log/receipt files. */
34
36
  deleteLogs?: boolean;
37
+ /** Minimum age before an active record is eligible for evidence-based stale reconciliation (default 5m). */
38
+ staleActiveMinAgeMs?: number;
39
+ /** Max stale active records to reconcile in one selected-repository GC pass (default 100). */
40
+ maxStaleReconciliations?: number;
35
41
  }
36
42
 
37
43
  export interface ProcessGcResult {
@@ -39,6 +45,7 @@ export interface ProcessGcResult {
39
45
  removedRecords: number;
40
46
  removedLogs: number;
41
47
  skippedActive: number;
48
+ reconciledStaleActive: number;
42
49
  /** Legacy/corrupt records without enough terminal proof are preserved. */
43
50
  skippedInvalid?: number;
44
51
  error?: string;
@@ -125,28 +132,52 @@ export function gcTerminalProcesses(options: ProcessGcOptions): ProcessGcResult
125
132
  removedRecords: 0,
126
133
  removedLogs: 0,
127
134
  skippedActive: 0,
135
+ reconciledStaleActive: 0,
128
136
  error: `writer_fenced:${fence.reason ?? 'denied'}`,
129
137
  };
130
138
  }
131
139
 
132
140
  const maxAgeMs = options.maxAgeMs ?? 7 * 24 * 60 * 60_000;
133
141
  const maxTerminal = options.maxTerminalRecords ?? 500;
142
+ const staleActiveMinAgeMs = Math.max(1_000, options.staleActiveMinAgeMs ?? 5 * 60_000);
143
+ const maxStaleReconciliations = Math.max(0, options.maxStaleReconciliations ?? 100);
134
144
  const root = processesDir(options.controllerHome, options.repoId);
135
145
  if (!existsSync(root)) {
136
- return { ok: true, removedRecords: 0, removedLogs: 0, skippedActive: 0 };
146
+ return { ok: true, removedRecords: 0, removedLogs: 0, skippedActive: 0, reconciledStaleActive: 0 };
137
147
  }
138
148
 
139
149
  const active = new Set(listActiveProcessIds(options.controllerHome, options.repoId));
140
150
  const terminal: Array<{ processId: string; finishedAt: number; path: string; reusableCheckEvidence: boolean }> = [];
141
151
  let skippedActive = 0;
142
152
  let skippedInvalid = 0;
153
+ let reconciledStaleActive = 0;
154
+ const nowMs = Date.now();
143
155
 
144
156
  for (const name of readdirSync(root)) {
145
157
  if (!name.endsWith('.json') || name === 'active-index.json') continue;
146
158
  const processId = name.slice(0, -'.json'.length);
147
159
  if (active.has(processId)) {
148
- skippedActive += 1;
149
- continue;
160
+ let record = getProcessRecord(options.controllerHome, options.repoId, processId);
161
+ if (!record) {
162
+ skippedActive += 1;
163
+ continue;
164
+ }
165
+ if (isManagedProcessActive(record) && reconciledStaleActive < maxStaleReconciliations) {
166
+ const reconciled = reconcileStaleManagedProcessForMaintenance(
167
+ options.controllerHome,
168
+ options.repoId,
169
+ processId,
170
+ { nowMs, minAgeMs: staleActiveMinAgeMs },
171
+ );
172
+ if (reconciled) {
173
+ if (isManagedProcessActive(record) && !isManagedProcessActive(reconciled)) reconciledStaleActive += 1;
174
+ record = reconciled;
175
+ }
176
+ }
177
+ if (isManagedProcessActive(record)) {
178
+ skippedActive += 1;
179
+ continue;
180
+ }
150
181
  }
151
182
  const metadata = readProcessGcMetadata(join(root, name));
152
183
  if (!metadata?.status) {
@@ -194,13 +225,14 @@ export function gcTerminalProcesses(options: ProcessGcOptions): ProcessGcResult
194
225
  }
195
226
  }
196
227
 
197
- return { ok: true, removedRecords, removedLogs, skippedActive, skippedInvalid };
228
+ return { ok: true, removedRecords, removedLogs, skippedActive, reconciledStaleActive, skippedInvalid };
198
229
  } catch (error) {
199
230
  return {
200
231
  ok: false,
201
232
  removedRecords: 0,
202
233
  removedLogs: 0,
203
234
  skippedActive: 0,
235
+ reconciledStaleActive: 0,
204
236
  error: error instanceof Error ? error.message : String(error),
205
237
  };
206
238
  }