@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
@@ -5,6 +5,7 @@ export * from './work-task-projection';
5
5
  export * from './work-continuation';
6
6
  export * from './controller-session-store';
7
7
  export * from './plan-contract-store';
8
+ export * from './semantic-admission';
8
9
  export * from './facade-result';
9
10
  export * from './suggested-actions';
10
11
  export * from './capability-registry';
@@ -3,12 +3,21 @@ import { join } from 'path';
3
3
  import { repositoryControllerRoot } from '../../../cli/repositories/controller-home';
4
4
  import { withControllerLock } from '../../../cli/repositories/locks';
5
5
  import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../../shared/json-files';
6
+ import {
7
+ normalizePlanScopeKey,
8
+ resolvePlanAdmission,
9
+ withPlanAdmissionLock,
10
+ withPlanAdmissionLockAsync,
11
+ type PlanAdmissionRelation,
12
+ type PlanAdmissionResolution,
13
+ } from './semantic-admission';
6
14
  import {
7
15
  listControlPlaneRecords,
8
16
  readControlPlaneRecordWithinTransaction,
9
17
  withControlPlaneTransaction,
10
18
  writeControlPlaneRecordWithinTransaction,
11
19
  } from '../persistence/sqlite-store';
20
+ import { readRequirement } from '../persistence/requirement-store';
12
21
  import {
13
22
  isTerminalPlanContractStatus,
14
23
  type EvidenceRef,
@@ -46,6 +55,13 @@ export interface CreatePlanContractInput {
46
55
  evidenceRefs?: EvidenceRef[];
47
56
  }
48
57
 
58
+ export interface AdmitPlanContractInput extends CreatePlanContractInput {
59
+ planRelation?: PlanAdmissionRelation;
60
+ relatedPlanId?: string;
61
+ }
62
+
63
+ export type AdmitPlanContractResult = PlanAdmissionResolution;
64
+
49
65
  export interface PlanContractSummary {
50
66
  planId: string;
51
67
  repoId: string;
@@ -68,7 +84,7 @@ function bounded(values: readonly string[] | undefined, limit: number, maxLength
68
84
  }
69
85
 
70
86
  function normalizeScopeKey(value: string): string {
71
- return sanitizeFileComponent(value).toLowerCase().slice(0, 160);
87
+ return normalizePlanScopeKey(value) || 'unknown';
72
88
  }
73
89
 
74
90
  function normalizeStep(step: CreatePlanContractInput['steps'][number]): PlanStep {
@@ -115,6 +131,14 @@ function updatePlanContract(
115
131
  );
116
132
  }
117
133
 
134
+ function assertRequirementReference(options: PlanContractStoreOptions, requirementId: string | undefined): void {
135
+ const normalized = requirementId?.trim();
136
+ if (!normalized || !options.controllerHome) return;
137
+ if (!readRequirement({ controllerHome: options.controllerHome, now: options.now }, normalized)) {
138
+ throw new Error(`PLAN_REQUIREMENT_NOT_FOUND: ${normalized}`);
139
+ }
140
+ }
141
+
118
142
  export function planContractRoot(location: PlanContractStoreLocation): string {
119
143
  if (location.root) {
120
144
  mkdirSync(location.root, { recursive: true });
@@ -205,11 +229,10 @@ function writePlanContractStore(options: PlanContractStoreOptions, store: PlanCo
205
229
  return store;
206
230
  }
207
231
 
208
- export function createPlanContract(options: PlanContractStoreOptions, input: CreatePlanContractInput): PlanContract {
209
- const at = nowIso(options);
232
+ function buildPlanContract(input: CreatePlanContractInput, at: string): PlanContract {
210
233
  const planId = sanitizeFileComponent(input.planId);
211
234
  if (!String(input.planId ?? '').trim() || planId === 'unknown') throw new Error('plan_id is required');
212
- const plan: PlanContract = {
235
+ return {
213
236
  schemaVersion: 1,
214
237
  planId,
215
238
  repoId: input.repoId,
@@ -229,6 +252,12 @@ export function createPlanContract(options: PlanContractStoreOptions, input: Cre
229
252
  createdAt: at,
230
253
  updatedAt: at,
231
254
  };
255
+ }
256
+
257
+ function createPlanContractUnlocked(options: PlanContractStoreOptions, input: CreatePlanContractInput): PlanContract {
258
+ assertRequirementReference(options, input.requirementId);
259
+ const at = nowIso(options);
260
+ const plan = buildPlanContract(input, at);
232
261
  const store = readPlanContractStore(options);
233
262
  if (store.contracts.some((existing) => existing.planId === plan.planId)) {
234
263
  throw new Error(`plan contract already exists: ${plan.planId}`);
@@ -237,6 +266,77 @@ export function createPlanContract(options: PlanContractStoreOptions, input: Cre
237
266
  return plan;
238
267
  }
239
268
 
269
+ function replacePlanContractUnlocked(
270
+ options: PlanContractStoreOptions,
271
+ predecessorId: string,
272
+ input: CreatePlanContractInput,
273
+ ): PlanContract {
274
+ assertRequirementReference(options, input.requirementId);
275
+ const at = nowIso(options);
276
+ const store = readPlanContractStore(options);
277
+ const predecessorKey = sanitizeFileComponent(predecessorId);
278
+ const predecessorIndex = store.contracts.findIndex((contract) => contract.planId === predecessorKey);
279
+ if (predecessorIndex < 0) throw new Error(`plan contract not found: ${predecessorKey}`);
280
+ const predecessor = store.contracts[predecessorIndex]!;
281
+ if (isTerminalPlanContractStatus(predecessor.status)) throw new Error(`plan contract ${predecessor.planId} is terminal (${predecessor.status})`);
282
+ const successor = buildPlanContract(input, at);
283
+ if (successor.planId === predecessor.planId) throw new Error('PLAN_SUCCESSOR_ID_MUST_CHANGE');
284
+ if (successor.repoId !== predecessor.repoId) throw new Error('PLAN_SUCCESSOR_REPOSITORY_MISMATCH');
285
+ if (store.contracts.some((existing) => existing.planId === successor.planId)) {
286
+ throw new Error(`plan contract already exists: ${successor.planId}`);
287
+ }
288
+ const conflictingScope = store.contracts.find((existing, index) => index !== predecessorIndex
289
+ && !isTerminalPlanContractStatus(existing.status)
290
+ && existing.scopeKey === successor.scopeKey);
291
+ if (conflictingScope) throw new Error(`PLAN_SCOPE_ALREADY_OWNED: ${successor.scopeKey}:${conflictingScope.planId}`);
292
+ const contracts = [...store.contracts];
293
+ contracts[predecessorIndex] = { ...predecessor, status: 'superseded', supersededBy: successor.planId, updatedAt: at };
294
+ writePlanContractStore(options, { schemaVersion: 1, updatedAt: at, contracts: [successor, ...contracts] });
295
+ return successor;
296
+ }
297
+
298
+ export function createPlanContract(options: PlanContractStoreOptions, input: CreatePlanContractInput): PlanContract {
299
+ return withPlanAdmissionLock(options, () => {
300
+ const store = readPlanContractStore(options);
301
+ const planId = sanitizeFileComponent(input.planId);
302
+ if (store.contracts.some((existing) => existing.planId === planId)) {
303
+ throw new Error(`plan contract already exists: ${planId}`);
304
+ }
305
+ const scopeKey = normalizeScopeKey(input.scopeKey);
306
+ const existingScopeAuthority = scopeKey === 'unknown' ? undefined : store.contracts.find((existing) =>
307
+ !isTerminalPlanContractStatus(existing.status) && existing.scopeKey === scopeKey);
308
+ if (existingScopeAuthority) {
309
+ throw new Error(`PLAN_SCOPE_ALREADY_OWNED: ${scopeKey}:${existingScopeAuthority.planId}`);
310
+ }
311
+ return createPlanContractUnlocked(options, input);
312
+ });
313
+ }
314
+
315
+ function admitPlanContractUnlocked(options: PlanContractStoreOptions, input: AdmitPlanContractInput): AdmitPlanContractResult {
316
+ assertRequirementReference(options, input.requirementId);
317
+ const activePlans = listPlanContracts({ ...options, status: 'active', limit: 100 });
318
+ const resolution = resolvePlanAdmission(activePlans, {
319
+ requirementId: input.requirementId,
320
+ scopeKey: input.scopeKey,
321
+ planRelation: input.planRelation,
322
+ relatedPlanId: input.relatedPlanId,
323
+ });
324
+ if (resolution.admissionDecision === 'extend_existing' && resolution.plan) {
325
+ const successor = replacePlanContractUnlocked(options, resolution.plan.planId, input);
326
+ return { ...resolution, admissionDecision: 'create_new', plan: successor };
327
+ }
328
+ if (resolution.admissionDecision !== 'create_new') return resolution;
329
+ return { ...resolution, plan: createPlanContractUnlocked(options, input) };
330
+ }
331
+
332
+ export function admitPlanContract(options: PlanContractStoreOptions, input: AdmitPlanContractInput): AdmitPlanContractResult {
333
+ return withPlanAdmissionLock(options, () => admitPlanContractUnlocked(options, input));
334
+ }
335
+
336
+ export async function admitPlanContractAsync(options: PlanContractStoreOptions, input: AdmitPlanContractInput): Promise<AdmitPlanContractResult> {
337
+ return await withPlanAdmissionLockAsync(options, () => admitPlanContractUnlocked(options, input));
338
+ }
339
+
240
340
  export function getPlanContract(options: PlanContractStoreOptions, planId: string): PlanContract | undefined {
241
341
  return readPlanContractStore(options).contracts.find((contract) => contract.planId === sanitizeFileComponent(planId));
242
342
  }
@@ -301,11 +401,12 @@ function approvalErrors(plan: PlanContract, allPlans: readonly PlanContract[]):
301
401
  return [...new Set(errors)];
302
402
  }
303
403
 
304
- export function approvePlanContract(options: PlanContractStoreOptions, planId: string): PlanContract {
404
+ function approvePlanContractUnlocked(options: PlanContractStoreOptions, planId: string): PlanContract {
305
405
  const store = readPlanContractStore(options);
306
406
  const index = store.contracts.findIndex((contract) => contract.planId === sanitizeFileComponent(planId));
307
407
  if (index < 0) throw new Error(`plan contract not found: ${sanitizeFileComponent(planId)}`);
308
408
  const current = store.contracts[index];
409
+ assertRequirementReference(options, current.requirementId);
309
410
  if (current.status !== 'draft' && current.status !== 'reviewing') throw new Error(`plan contract ${current.planId} cannot be approved from ${current.status}`);
310
411
  const errors = approvalErrors(current, store.contracts);
311
412
  if (errors.length > 0) throw new Error(`plan contract ${current.planId} cannot be approved: ${errors.join('; ')}`);
@@ -317,20 +418,33 @@ export function approvePlanContract(options: PlanContractStoreOptions, planId: s
317
418
  return next;
318
419
  }
319
420
 
421
+ export function approvePlanContract(options: PlanContractStoreOptions, planId: string): PlanContract {
422
+ return withPlanAdmissionLock(options, () => approvePlanContractUnlocked(options, planId));
423
+ }
424
+
425
+ export async function approvePlanContractAsync(options: PlanContractStoreOptions, planId: string): Promise<PlanContract> {
426
+ return await withPlanAdmissionLockAsync(options, () => approvePlanContractUnlocked(options, planId));
427
+ }
428
+
320
429
  export function supersedePlanContract(options: PlanContractStoreOptions, planId: string, supersededBy: string): PlanContract {
321
- const store = readPlanContractStore(options);
322
- const index = store.contracts.findIndex((contract) => contract.planId === sanitizeFileComponent(planId));
323
- if (index < 0) throw new Error(`plan contract not found: ${sanitizeFileComponent(planId)}`);
324
- const current = store.contracts[index];
325
- if (isTerminalPlanContractStatus(current.status)) throw new Error(`plan contract ${current.planId} is terminal (${current.status})`);
326
- const replacement = sanitizeFileComponent(supersededBy);
327
- if (!replacement || replacement === 'unknown') throw new Error('superseded_by is required');
328
- const at = nowIso(options);
329
- const next = { ...current, status: 'superseded' as const, supersededBy: replacement, updatedAt: at };
330
- const contracts = [...store.contracts];
331
- contracts[index] = next;
332
- writePlanContractStore(options, { schemaVersion: 1, updatedAt: at, contracts });
333
- return next;
430
+ return withPlanAdmissionLock(options, () => {
431
+ const store = readPlanContractStore(options);
432
+ const index = store.contracts.findIndex((contract) => contract.planId === sanitizeFileComponent(planId));
433
+ if (index < 0) throw new Error(`plan contract not found: ${sanitizeFileComponent(planId)}`);
434
+ const current = store.contracts[index]!;
435
+ if (isTerminalPlanContractStatus(current.status)) throw new Error(`plan contract ${current.planId} is terminal (${current.status})`);
436
+ const replacement = sanitizeFileComponent(supersededBy);
437
+ if (!replacement || replacement === 'unknown') throw new Error('superseded_by is required');
438
+ if (replacement === current.planId) throw new Error('PLAN_SUCCESSOR_ID_MUST_CHANGE');
439
+ const successor = store.contracts.find((contract) => contract.planId === replacement);
440
+ if (!successor) throw new Error(`PLAN_SUCCESSOR_NOT_FOUND: ${replacement}`);
441
+ const at = nowIso(options);
442
+ const next = { ...current, status: 'superseded' as const, supersededBy: successor.planId, updatedAt: at };
443
+ const contracts = [...store.contracts];
444
+ contracts[index] = next;
445
+ writePlanContractStore(options, { schemaVersion: 1, updatedAt: at, contracts });
446
+ return next;
447
+ });
334
448
  }
335
449
 
336
450
  /**
@@ -343,6 +457,7 @@ export function claimPlanStepForWork(
343
457
  input: { planId: string; stepId: string; workId: string; sourceRevision: string },
344
458
  ): PlanContract {
345
459
  return updatePlanContract(options, input.planId, (current) => {
460
+ assertRequirementReference(options, current.requirementId);
346
461
  if (current.status !== 'approved' && current.status !== 'executing') {
347
462
  throw new Error(`PLAN_NOT_EXECUTABLE: ${current.planId} is ${current.status}`);
348
463
  }
@@ -363,6 +478,45 @@ export function claimPlanStepForWork(
363
478
  });
364
479
  }
365
480
 
481
+ export function repairDanglingPlanStepWorkBinding(
482
+ options: PlanContractStoreOptions,
483
+ input: { planId: string; stepId: string; expectedWorkId: string; reason: string },
484
+ ): PlanContract {
485
+ const expectedWorkId = input.expectedWorkId.trim();
486
+ const reason = input.reason.trim();
487
+ if (!expectedWorkId) throw new Error('PLAN_STEP_REPAIR_EXPECTED_WORK_REQUIRED');
488
+ if (!reason) throw new Error('PLAN_STEP_REPAIR_REASON_REQUIRED');
489
+ return updatePlanContract(options, input.planId, (current) => {
490
+ if (isTerminalPlanContractStatus(current.status)) throw new Error(`PLAN_STEP_REPAIR_PLAN_TERMINAL: ${current.planId} is ${current.status}`);
491
+ const stepIndex = current.steps.findIndex((step) => step.id === sanitizeFileComponent(input.stepId));
492
+ if (stepIndex < 0) throw new Error(`PLAN_STEP_NOT_FOUND: ${input.stepId}`);
493
+ const step = current.steps[stepIndex];
494
+ if (step.workId !== expectedWorkId) {
495
+ throw new Error(`PLAN_STEP_REPAIR_BINDING_CHANGED: ${step.id} is bound to ${step.workId ?? 'no Work'}, expected ${expectedWorkId}`);
496
+ }
497
+ if (step.status !== 'executing') {
498
+ throw new Error(`PLAN_STEP_REPAIR_STATUS_INVALID: ${step.id} is ${step.status}`);
499
+ }
500
+ const at = nowIso(options);
501
+ const steps = [...current.steps];
502
+ steps[stepIndex] = {
503
+ ...step,
504
+ status: 'ready',
505
+ workId: undefined,
506
+ evidenceRefs: [{
507
+ title: 'dangling Work binding repaired',
508
+ summary: `${expectedWorkId}: ${reason}`.slice(0, 2_000),
509
+ detailLevel: 'summary' as const,
510
+ }, ...step.evidenceRefs].slice(0, 20),
511
+ };
512
+ // This is admission-state repair, not semantic replanning: the Plan and its
513
+ // acceptance criteria remain authoritative. Return the exact step to ready
514
+ // so a later start can atomically admit one replacement for the missing
515
+ // record without forcing a second Plan authority.
516
+ return { ...current, status: 'executing', steps, updatedAt: at };
517
+ });
518
+ }
519
+
366
520
  export function completePlanStepForWork(
367
521
  options: PlanContractStoreOptions,
368
522
  input: {
@@ -392,6 +546,10 @@ export function completePlanStepForWork(
392
546
  // semantic validation until an explicit Controller acceptance records it.
393
547
  // Failed/cancelled Work returns the slice to ready for replanning.
394
548
  status: delivered ? 'validating' : 'ready',
549
+ // Successful delivery keeps the exact Work identity through semantic
550
+ // validation. Failed/cancelled Work releases the step authority so the
551
+ // replanning path cannot retain a ghost binding to a terminal Work.
552
+ workId: delivered ? step.workId : undefined,
395
553
  evidenceRefs: input.work.evidenceRefs.length > 0 ? input.work.evidenceRefs.slice(0, 20) : step.evidenceRefs,
396
554
  };
397
555
  return { ...current, status: delivered ? 'verifying' : 'replanning', steps, updatedAt: at };
@@ -400,7 +558,7 @@ export function completePlanStepForWork(
400
558
 
401
559
  export function acceptPlanStepEvidence(
402
560
  options: PlanContractStoreOptions,
403
- input: { planId: string; stepId: string; reviewer: string; rationale: string },
561
+ input: { planId: string; stepId: string; reviewer: string; rationale: string; acceptedSourceRevision?: string },
404
562
  ): PlanContract {
405
563
  const reviewer = input.reviewer.trim();
406
564
  const rationale = input.rationale.trim();
@@ -418,6 +576,13 @@ export function acceptPlanStepEvidence(
418
576
  evidenceRefs: [{ title: 'semantic acceptance', summary: `${reviewer}: ${rationale}`, detailLevel: 'summary' as const }, ...step.evidenceRefs].slice(0, 20),
419
577
  };
420
578
  const allCompleted = steps.every((candidate) => candidate.status === 'completed');
421
- return { ...current, status: allCompleted ? 'finalized' : 'executing', steps, updatedAt: nowIso(options) };
579
+ const acceptedSourceRevision = input.acceptedSourceRevision?.trim();
580
+ return {
581
+ ...current,
582
+ ...(acceptedSourceRevision ? { sourceRevision: acceptedSourceRevision } : {}),
583
+ status: allCompleted ? 'finalized' : 'executing',
584
+ steps,
585
+ updatedAt: nowIso(options),
586
+ };
422
587
  });
423
588
  }
@@ -0,0 +1,205 @@
1
+ import { withControllerLock, withControllerLockAsync } from '../../../cli/repositories/locks';
2
+ import { sanitizeFileComponent } from '../../shared/json-files';
3
+ import { isTerminalPlanContractStatus, type PlanContract } from './types';
4
+
5
+ export const PLAN_ADMISSION_RELATIONS = ['extend', 'parallel'] as const;
6
+ export type PlanAdmissionRelation = (typeof PLAN_ADMISSION_RELATIONS)[number];
7
+
8
+ export type PlanAdmissionReason =
9
+ | 'exact_scope_authority'
10
+ | 'requirement_relation_required'
11
+ | 'extension_target_required'
12
+ | 'extend_existing'
13
+ | 'create_new';
14
+
15
+ export interface PlanAdmissionInput {
16
+ requirementId?: string;
17
+ scopeKey: string;
18
+ planRelation?: PlanAdmissionRelation;
19
+ relatedPlanId?: string;
20
+ }
21
+
22
+ export interface PlanAdmissionResolution {
23
+ admissionDecision: 'reuse_existing' | 'resolution_required' | 'extend_existing' | 'create_new';
24
+ resolutionRequired: boolean;
25
+ reason: PlanAdmissionReason;
26
+ normalizedScopeKey: string;
27
+ plan?: PlanContract;
28
+ candidates: PlanContract[];
29
+ allowedPlanRelations?: readonly PlanAdmissionRelation[];
30
+ }
31
+
32
+ export function normalizePlanScopeKey(value: string): string {
33
+ const raw = String(value ?? '').trim();
34
+ if (!raw) return '';
35
+ const normalized = sanitizeFileComponent(raw).toLowerCase().slice(0, 160);
36
+ return normalized === 'unknown' ? '' : normalized;
37
+ }
38
+
39
+ export function resolvePlanAdmission(
40
+ plans: readonly PlanContract[],
41
+ input: PlanAdmissionInput,
42
+ ): PlanAdmissionResolution {
43
+ const activePlans = plans.filter((plan) => !isTerminalPlanContractStatus(plan.status));
44
+ const normalizedScopeKey = normalizePlanScopeKey(input.scopeKey);
45
+ const requirementId = input.requirementId?.trim() || undefined;
46
+ const requirementPlans = requirementId
47
+ ? activePlans.filter((candidate) => candidate.requirementId === requirementId)
48
+ : [];
49
+ const relatedPlanId = input.relatedPlanId?.trim() || undefined;
50
+ const explicitlyRelatedPlan = relatedPlanId
51
+ ? activePlans.find((candidate) => candidate.planId === relatedPlanId)
52
+ : undefined;
53
+ const relatedPlan = explicitlyRelatedPlan && (!requirementId || explicitlyRelatedPlan.requirementId === requirementId)
54
+ ? explicitlyRelatedPlan
55
+ : !relatedPlanId && requirementPlans.length === 1 ? requirementPlans[0] : undefined;
56
+ const exactScopeAuthority = normalizedScopeKey
57
+ ? activePlans.find((candidate) => candidate.scopeKey === normalizedScopeKey)
58
+ : undefined;
59
+
60
+ // An explicit extension is a serial replan, not a second active Plan. Resolve
61
+ // it before exact-scope reuse so a successor may retain the predecessor scope.
62
+ // If another Plan owns the requested successor scope, preserve that authority.
63
+ if (input.planRelation === 'extend') {
64
+ if (!relatedPlan) {
65
+ return {
66
+ admissionDecision: 'resolution_required',
67
+ resolutionRequired: true,
68
+ reason: 'extension_target_required',
69
+ normalizedScopeKey,
70
+ candidates: (requirementPlans.length > 0 ? requirementPlans : activePlans).slice(0, 8),
71
+ allowedPlanRelations: PLAN_ADMISSION_RELATIONS,
72
+ };
73
+ }
74
+ if (exactScopeAuthority && exactScopeAuthority.planId !== relatedPlan.planId) {
75
+ return {
76
+ admissionDecision: 'reuse_existing',
77
+ resolutionRequired: false,
78
+ reason: 'exact_scope_authority',
79
+ normalizedScopeKey,
80
+ plan: exactScopeAuthority,
81
+ candidates: [exactScopeAuthority],
82
+ };
83
+ }
84
+ return {
85
+ admissionDecision: 'extend_existing',
86
+ resolutionRequired: false,
87
+ reason: 'extend_existing',
88
+ normalizedScopeKey,
89
+ plan: relatedPlan,
90
+ candidates: requirementPlans.length > 0 ? requirementPlans.slice(0, 8) : [relatedPlan],
91
+ };
92
+ }
93
+
94
+ if (exactScopeAuthority) {
95
+ return {
96
+ admissionDecision: 'reuse_existing',
97
+ resolutionRequired: false,
98
+ reason: 'exact_scope_authority',
99
+ normalizedScopeKey,
100
+ plan: exactScopeAuthority,
101
+ candidates: [exactScopeAuthority],
102
+ };
103
+ }
104
+
105
+ if (requirementPlans.length > 0 && !input.planRelation) {
106
+ return {
107
+ admissionDecision: 'resolution_required',
108
+ resolutionRequired: true,
109
+ reason: 'requirement_relation_required',
110
+ normalizedScopeKey,
111
+ candidates: requirementPlans.slice(0, 8),
112
+ allowedPlanRelations: PLAN_ADMISSION_RELATIONS,
113
+ };
114
+ }
115
+
116
+ return {
117
+ admissionDecision: 'create_new',
118
+ resolutionRequired: false,
119
+ reason: 'create_new',
120
+ normalizedScopeKey,
121
+ candidates: requirementPlans.slice(0, 8),
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Short per-repository semantic admission critical section. The fixed task key
127
+ * keeps admission independent from repository/worktree execution locks while
128
+ * still coordinating competing controller processes for the same repository.
129
+ */
130
+ const planAdmissionLockKey = (repoId: string) => ({
131
+ scope: 'task' as const,
132
+ repoId,
133
+ taskId: 'semantic-plan-admission',
134
+ });
135
+
136
+ export function withPlanAdmissionLock<T>(
137
+ options: { controllerHome?: string; repoId?: string },
138
+ operation: () => T,
139
+ ): T {
140
+ if (!options.controllerHome?.trim() || !options.repoId?.trim()) return operation();
141
+ return withControllerLock(
142
+ options.controllerHome,
143
+ planAdmissionLockKey(options.repoId),
144
+ 'semantic-plan-admission',
145
+ operation,
146
+ );
147
+ }
148
+
149
+ /** Gateway-facing admission waits briefly for a competing controller to finish
150
+ * its tiny create/approve decision, then re-reads canonical state under lock. */
151
+ export async function withPlanAdmissionLockAsync<T>(
152
+ options: { controllerHome?: string; repoId?: string },
153
+ operation: () => T | Promise<T>,
154
+ ): Promise<T> {
155
+ if (!options.controllerHome?.trim() || !options.repoId?.trim()) return await operation();
156
+ return await withControllerLockAsync(
157
+ options.controllerHome,
158
+ planAdmissionLockKey(options.repoId),
159
+ 'semantic-plan-admission',
160
+ async () => await operation(),
161
+ undefined,
162
+ 500,
163
+ );
164
+ }
165
+
166
+ const primaryWorkAdmissionLockKey = (repoId: string) => ({
167
+ scope: 'task' as const,
168
+ repoId,
169
+ taskId: 'semantic-primary-work-admission',
170
+ });
171
+
172
+ /**
173
+ * Primary Work admission protects only canonical ownership resolution and
174
+ * persistence. It must never span workspace materialization, dependency prep,
175
+ * checks, worker launch, or the Work execution lifetime.
176
+ */
177
+ export function withPrimaryWorkAdmissionLock<T>(
178
+ options: { controllerHome?: string; repoId?: string },
179
+ operation: () => T,
180
+ ): T {
181
+ if (!options.controllerHome?.trim() || !options.repoId?.trim()) return operation();
182
+ return withControllerLock(
183
+ options.controllerHome,
184
+ primaryWorkAdmissionLockKey(options.repoId),
185
+ 'semantic-primary-work-admission',
186
+ operation,
187
+ );
188
+ }
189
+
190
+ /** Gateway callers wait briefly for a competing admission, then re-read the
191
+ * canonical Plan/Work state while holding the same short critical section. */
192
+ export async function withPrimaryWorkAdmissionLockAsync<T>(
193
+ options: { controllerHome?: string; repoId?: string },
194
+ operation: () => T | Promise<T>,
195
+ ): Promise<T> {
196
+ if (!options.controllerHome?.trim() || !options.repoId?.trim()) return await operation();
197
+ return await withControllerLockAsync(
198
+ options.controllerHome,
199
+ primaryWorkAdmissionLockKey(options.repoId),
200
+ 'semantic-primary-work-admission',
201
+ async () => await operation(),
202
+ undefined,
203
+ 500,
204
+ );
205
+ }
@@ -11,10 +11,13 @@ function expectedActionRisk(action: SuggestedNextAction): SuggestedNextAction['r
11
11
  case 'delegate':
12
12
  case 'controller_claim':
13
13
  case 'controller_release':
14
+ case 'controller_disposition':
14
15
  case 'launcher_start':
16
+ case 'requirement_create':
15
17
  case 'schedule_create':
16
18
  case 'schedule_pause':
17
19
  case 'schedule_resume':
20
+ case 'schedule_delete':
18
21
  case 'schedule_trigger':
19
22
  return 'workspace_write';
20
23
  case 'controller_get_owner':
@@ -26,6 +29,7 @@ function expectedActionRisk(action: SuggestedNextAction): SuggestedNextAction['r
26
29
  case 'plan_list':
27
30
  return 'readonly';
28
31
  case 'plan_approve':
32
+ case 'plan_accept_step':
29
33
  case 'plan_supersede':
30
34
  return 'workspace_write';
31
35
  case 'finalize':
@@ -40,7 +44,7 @@ const ALLOWED_FACADE_OPERATIONS: Record<FacadeTool, readonly string[]> = {
40
44
  rh_status: ['list', 'get', 'repair'],
41
45
  rh_inbox: ['list', 'get', 'ack', 'accept', 'resolve', 'dismiss', 'create'],
42
46
  rh_context: ['list', 'get', 'search'],
43
- rh_work: ['start', 'continue', 'verify', 'repair', 'finalize', 'stop', 'delegate', 'controller_claim', 'controller_release', 'controller_get_owner', 'launcher_start', 'plan_create', 'plan_get', 'plan_list', 'plan_approve', 'plan_supersede', 'schedule_create', 'schedule_list', 'schedule_get', 'schedule_pause', 'schedule_resume', 'schedule_trigger'],
47
+ rh_work: ['start', 'continue', 'verify', 'repair', 'finalize', 'stop', 'delegate', 'controller_claim', 'controller_release', 'controller_disposition', 'controller_get_owner', 'launcher_start', 'requirement_create', 'plan_create', 'plan_get', 'plan_list', 'plan_approve', 'plan_accept_step', 'plan_supersede', 'schedule_create', 'schedule_list', 'schedule_get', 'schedule_pause', 'schedule_resume', 'schedule_delete', 'schedule_trigger'],
44
48
  };
45
49
 
46
50
  export interface SuggestedActionValidationOptions {
@@ -1,13 +1,14 @@
1
1
  import type { ProcessCheckReceiptEvidence } from '../../evidence/process-check-receipt';
2
2
  import type { CompletionReceipt as RepositoryCompletionReceipt } from '../../../cli/controller/types';
3
3
  import type { AccessMode } from '../governance/access-policy';
4
- import { decideRoute, type RouteDecision, type RoutePolicyInput } from '../routing/route-policy';
4
+ import { decideRoute, type ExplicitTaskMode, type RouteDecision, type RoutePolicyInput } from '../routing/route-policy';
5
5
 
6
6
  export const EXECUTION_MODES = ['direct_control', 'goal_workloop', 'handoff_only'] as const;
7
7
  export type ExecutionMode = (typeof EXECUTION_MODES)[number];
8
8
 
9
9
  export const FACADE_TOOLS = ['rh_access', 'rh_status', 'rh_inbox', 'rh_context', 'rh_work'] as const;
10
10
  export type FacadeTool = (typeof FACADE_TOOLS)[number];
11
+ export type CapabilityExecutionSurface = FacadeTool | 'plugin_action_execute';
11
12
 
12
13
  export const FACADE_STATUSES = ['ok', 'blocked', 'failed', 'approval_required', 'not_found'] as const;
13
14
  export type FacadeStatus = (typeof FACADE_STATUSES)[number];
@@ -353,6 +354,8 @@ export interface WorkContractConstraints {
353
354
  conflictsWithThinHarnessPolicy?: boolean;
354
355
  /** True when the change alters the default routing or execution mode visible to users. */
355
356
  changesDefaultExecutionStrategy?: boolean;
357
+ /** Repository-change delivery must publish the exact integrated revision before Work terminalization. */
358
+ remoteDeliveryRequired?: boolean;
356
359
  }
357
360
 
358
361
  export interface WorkContractDriverPolicy {
@@ -476,6 +479,18 @@ export interface WorkContract {
476
479
  planStepId?: string;
477
480
  planSourceRevision?: string;
478
481
  scopeSummary?: string;
482
+ /**
483
+ * Discovery evidence, never an authorization boundary. Initial guesses,
484
+ * inspected paths, and observed changes remain distinct so early retrieval
485
+ * cannot masquerade as complete implementation scope.
486
+ */
487
+ scopeEvidence?: {
488
+ initialLikelyPaths: string[];
489
+ inspectedPaths: string[];
490
+ actualChangedPaths: string[];
491
+ recordedAt: string;
492
+ };
493
+ /** Explicit policy fence. This is not semantic completeness evidence. */
479
494
  allowedPaths: string[];
480
495
  forbiddenPaths: string[];
481
496
  checks: string[];
@@ -495,13 +510,12 @@ export interface WorkContract {
495
510
  continuationPrompt?: string;
496
511
  worktreeRef?: string;
497
512
  workerRef?: string;
498
- /** Stable idempotency key when this Work was accepted via work_submit. */
499
513
  requestId?: string;
500
- /** Accepted Kernel operation envelope. Does not imply ExecutionJob creation. */
514
+ /** Typed execution metadata; it does not imply ExecutionJob creation. */
501
515
  submittedOperation?: SubmittedWorkOperation;
502
516
  }
503
517
 
504
- /** Operation metadata retained on WorkContracts accepted through work_submit. */
518
+ /** Typed execution metadata retained on execution-child WorkContracts. */
505
519
  export interface SubmittedWorkOperation {
506
520
  name: string;
507
521
  semanticKey: string;
@@ -630,7 +644,7 @@ export interface CapabilityDescriptor {
630
644
  group: CapabilityGroup;
631
645
  operationClass: CapabilityOperationClass;
632
646
  risk: CapabilityRisk;
633
- exposedVia: FacadeTool;
647
+ exposedVia: CapabilityExecutionSurface;
634
648
  schemaExposure: CapabilitySchemaExposure;
635
649
  summary: string;
636
650
  }
@@ -639,6 +653,7 @@ export interface CapabilityGroupSummary {
639
653
  group: CapabilityGroup;
640
654
  capabilityCount: number;
641
655
  domains: CapabilityDomain[];
656
+ executionSurfaces: CapabilityExecutionSurface[];
642
657
  facadeTools: FacadeTool[];
643
658
  operationClasses: CapabilityOperationClass[];
644
659
  risks: CapabilityRisk[];
@@ -667,6 +682,7 @@ export interface ExecutionModeSelectionInput {
667
682
  requiresInvestigation?: boolean;
668
683
  requiresLongRunningChecks?: boolean;
669
684
  requiresParallelism?: boolean;
685
+ explicitMode?: ExplicitTaskMode;
670
686
  needsDependencies?: boolean;
671
687
  requiresRecovery?: boolean;
672
688
  /** True only when the user explicitly requested an agent/worker executor. Complexity alone must not enable workers. */
@@ -717,6 +733,7 @@ export function selectExecutionMode(input: ExecutionModeSelectionInput): Executi
717
733
  requiresInvestigation: input.requiresInvestigation,
718
734
  requiresLongRunningChecks: input.requiresLongRunningChecks,
719
735
  requiresParallelism: input.requiresParallelism,
736
+ explicitMode: input.explicitMode,
720
737
  needsDependencies: input.needsDependencies,
721
738
  agentRequested: input.requiresWorker,
722
739
  },