@moretea-labs/forge 1.7.1 → 1.8.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 (685) hide show
  1. package/AGENTS.md +58 -62
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +42 -62
  4. package/README.md +2 -2
  5. package/SKILL.md +0 -1
  6. package/THIRD_PARTY_NOTICES.md +5 -2
  7. package/adapters/browser/index.ts +2 -0
  8. package/adapters/chatgpt/browser-delivery-host.ts +71 -0
  9. package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
  10. package/adapters/chatgpt/controller-binding-store.ts +55 -0
  11. package/adapters/chatgpt/controller-host.ts +119 -0
  12. package/adapters/chatgpt/controller-round-host.ts +78 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
  14. package/adapters/chatgpt/provider-delivery.ts +113 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/desktop-operator-contract.ts +7 -0
  18. package/adapters/computer/desktop-operator-discovery.ts +201 -0
  19. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  20. package/adapters/computer/desktop-operator-provider.ts +312 -0
  21. package/adapters/computer/index.ts +1 -0
  22. package/adapters/computer/interaction-target-authority.ts +944 -0
  23. package/adapters/controller-process/binding-store.ts +34 -0
  24. package/adapters/controller-process/controller-host.ts +26 -0
  25. package/adapters/mcp/access-mode.ts +132 -0
  26. package/adapters/mcp/audit.ts +32 -0
  27. package/adapters/mcp/auth.ts +407 -0
  28. package/adapters/mcp/controller-round-compatibility.ts +184 -0
  29. package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
  30. package/adapters/mcp/instructions.ts +36 -0
  31. package/adapters/mcp/legacy-context.ts +18 -0
  32. package/adapters/mcp/multi-repository.ts +437 -0
  33. package/adapters/mcp/oauth.ts +391 -0
  34. package/adapters/mcp/paths.ts +140 -0
  35. package/adapters/mcp/policy.ts +232 -0
  36. package/adapters/mcp/proxy-env.ts +62 -0
  37. package/adapters/mcp/readiness-projection.ts +24 -0
  38. package/adapters/mcp/redaction.ts +21 -0
  39. package/adapters/mcp/repo.ts +24 -0
  40. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  41. package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
  42. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
  43. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
  44. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  45. package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
  46. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  47. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
  48. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  49. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  50. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  51. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  52. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
  53. package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
  54. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
  55. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
  56. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  57. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  58. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
  59. package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
  60. package/adapters/mcp/runtime-gateway/router.ts +728 -0
  61. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
  62. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
  63. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  64. package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
  65. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
  66. package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
  67. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
  68. package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
  69. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
  70. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  71. package/adapters/mcp/server.ts +1048 -0
  72. package/adapters/mcp/setup.ts +791 -0
  73. package/adapters/mcp/tool-capability-cache.ts +87 -0
  74. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  75. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
  76. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
  77. package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
  78. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  79. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  80. package/adapters/mcp/toolset-names.ts +74 -0
  81. package/adapters/mcp/toolset-selection.ts +41 -0
  82. package/adapters/mcp/toolset.ts +336 -0
  83. package/adapters/mcp/transports/http.ts +1456 -0
  84. package/adapters/mcp/transports/session-registry.ts +513 -0
  85. package/adapters/mcp/transports/stdio.ts +8 -0
  86. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
  87. package/adapters/mcp/types.ts +41 -0
  88. package/assets/AGENTS.md +2 -16
  89. package/assets/CLAUDE.md +2 -16
  90. package/assets/hooks/AGENTS.md +3 -14
  91. package/assets/hooks/CLAUDE.md +3 -14
  92. package/assets/hooks/hook-input.sh +9 -2
  93. package/assets/hooks/lib/workflow-state.sh +28 -10
  94. package/assets/hooks/prompt-guard.sh +63 -37
  95. package/assets/hooks/security-sentinel.sh +2 -1
  96. package/assets/hooks/session-start-context.sh +7 -5
  97. package/assets/hooks/stop-orchestrator.sh +13 -3
  98. package/assets/initializer-question-pack.v4.json +11 -7
  99. package/assets/partials/05-workflow.partial.md +18 -73
  100. package/assets/partials/07-footer.partial.md +1 -1
  101. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  102. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  103. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  104. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  105. package/assets/plan-map.json +11 -11
  106. package/assets/plugin-registry.v1.json +9 -3
  107. package/assets/plugin-trust-policy.v1.json +6 -0
  108. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  109. package/assets/recovery/forge-wsl-rescue +271 -0
  110. package/assets/reference-configs/development-protocol.md +11 -15
  111. package/assets/reference-configs/document-generation.md +1 -1
  112. package/assets/reference-configs/global-working-rules.md +9 -1
  113. package/assets/reference-configs/harness-overview.md +22 -112
  114. package/assets/reference-configs/hook-operations.md +1 -1
  115. package/assets/reference-configs/sprint-contracts.md +2 -2
  116. package/assets/skill-version.json +2 -2
  117. package/assets/templates/helpers/architecture-event.ts +3 -34
  118. package/assets/templates/helpers/architecture-queue.sh +61 -10
  119. package/assets/templates/helpers/archive-workflow.sh +90 -180
  120. package/assets/templates/helpers/capability-config.ts +8 -11
  121. package/assets/templates/helpers/capability-resolver.ts +37 -19
  122. package/assets/templates/helpers/capture-plan.sh +2 -2
  123. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  124. package/assets/templates/helpers/check-task-sync.sh +82 -11
  125. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  126. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  127. package/assets/templates/helpers/contract-worktree.sh +26 -24
  128. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  129. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  130. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  131. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  132. package/assets/templates/helpers/new-plan.sh +2 -2
  133. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  134. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  135. package/assets/templates/helpers/switch-plan.sh +1 -1
  136. package/assets/templates/helpers/workflow-contract.ts +1 -0
  137. package/assets/workflow-contract.v1.json +63 -171
  138. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  139. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  140. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  141. package/docs/architecture/CURRENT.md +148 -13
  142. package/docs/architecture/EVOLUTION.md +13 -0
  143. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  144. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  145. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  146. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  147. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
  148. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
  149. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  150. package/docs/architecture/domains/verification.md +7 -0
  151. package/docs/architecture/domains/workflow-engine.md +1 -1
  152. package/docs/architecture/index.md +3 -3
  153. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  154. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  155. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  156. package/docs/forge-plugin-management.md +20 -4
  157. package/docs/forge-runtime-self-healing-loop.md +2 -0
  158. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  159. package/docs/operations/controller-browser-plugin.md +3 -1
  160. package/docs/operations/homebrew.md +2 -2
  161. package/docs/operations/platform-support.md +16 -8
  162. package/docs/operations/platform-support.zh-CN.md +2 -2
  163. package/docs/operations/standalone-disaster-recovery.md +26 -1
  164. package/docs/operations/uu-remote-rescue.md +41 -0
  165. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  166. package/docs/public-usage-guide.md +1 -1
  167. package/docs/public-usage-guide.zh-CN.md +1 -1
  168. package/docs/tutorials/01-install-and-start.md +1 -1
  169. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  170. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  171. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  172. package/docs/wiki/Implementation.md +1 -1
  173. package/package.json +23 -14
  174. package/packages/kernel/controller/api/index.ts +2 -0
  175. package/packages/kernel/controller/application/controller-round-service.ts +29 -0
  176. package/packages/kernel/controller/application/controller-service.ts +30 -0
  177. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
  178. package/packages/kernel/controller/domain/controller-round.ts +107 -0
  179. package/packages/kernel/controller/domain/execution-quality.ts +133 -0
  180. package/packages/kernel/controller/domain/types.ts +56 -0
  181. package/packages/kernel/controller/index.ts +1 -0
  182. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  183. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
  184. package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
  185. package/packages/kernel/controller/ports/controller-host.ts +16 -0
  186. package/packages/kernel/goal/api/index.ts +1 -0
  187. package/packages/kernel/goal/domain/types.ts +40 -0
  188. package/packages/kernel/goal/index.ts +1 -0
  189. package/packages/kernel/identity/api/index.ts +5 -0
  190. package/packages/kernel/identity/application/identity-service.ts +67 -0
  191. package/packages/kernel/identity/domain/scope.ts +63 -0
  192. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  193. package/packages/kernel/identity/domain/types.ts +44 -0
  194. package/packages/kernel/identity/domain/workspace.ts +56 -0
  195. package/packages/kernel/identity/index.ts +1 -0
  196. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  197. package/packages/kernel/memory/api/index.ts +5 -0
  198. package/packages/kernel/memory/application/experience-service.ts +118 -0
  199. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  200. package/packages/kernel/memory/domain/experience.ts +103 -0
  201. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  202. package/packages/kernel/memory/index.ts +1 -0
  203. package/packages/kernel/progression/api/index.ts +2 -0
  204. package/packages/kernel/progression/application/projector.ts +211 -0
  205. package/packages/kernel/progression/domain/types.ts +116 -0
  206. package/packages/kernel/progression/index.ts +1 -0
  207. package/packages/kernel/scheduler/api/index.ts +6 -0
  208. package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
  209. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  210. package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
  211. package/packages/kernel/scheduler/application/settlement.ts +197 -0
  212. package/packages/kernel/scheduler/domain/continuation.ts +38 -0
  213. package/packages/kernel/scheduler/domain/schedule.ts +149 -0
  214. package/packages/kernel/scheduler/index.ts +1 -0
  215. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
  216. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
  217. package/packages/kernel/work/api/index.ts +15 -0
  218. package/packages/kernel/work/application/work-service.ts +148 -0
  219. package/packages/kernel/work/domain/admission-policy.ts +34 -0
  220. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  221. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  222. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  223. package/packages/kernel/work/domain/engineering-design.ts +281 -0
  224. package/packages/kernel/work/domain/engineering-profile.ts +242 -0
  225. package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
  226. package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
  227. package/packages/kernel/work/domain/implementation-review.ts +510 -0
  228. package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
  229. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  230. package/packages/kernel/work/domain/state-machine.ts +259 -0
  231. package/packages/kernel/work/domain/types.ts +528 -0
  232. package/packages/kernel/work/index.ts +1 -0
  233. package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
  234. package/packages/kernel/work/ports/work-contract-store.ts +18 -0
  235. package/packages/plugin-runtime/browser/index.ts +3 -0
  236. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  237. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  238. package/packages/plugin-runtime/browser/session-authority.ts +61 -0
  239. package/packages/plugin-runtime/computer/index.ts +6 -0
  240. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  241. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  242. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  243. package/packages/plugin-runtime/computer/provider.ts +19 -0
  244. package/packages/plugin-runtime/computer/target-authority.ts +190 -0
  245. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  246. package/packages/plugin-runtime/external/index.ts +1 -0
  247. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
  248. package/packages/plugin-runtime/index.ts +3 -0
  249. package/packages/protocols/browser/index.ts +1 -0
  250. package/packages/protocols/browser/session.ts +98 -0
  251. package/packages/protocols/computer/contract.ts +199 -0
  252. package/packages/protocols/computer/index.ts +1 -0
  253. package/packages/protocols/failure/contract.ts +158 -0
  254. package/packages/protocols/failure/index.ts +1 -0
  255. package/packages/protocols/handoff/index.ts +1 -0
  256. package/packages/protocols/handoff/status.ts +22 -0
  257. package/packages/protocols/mcp/execution-context.ts +11 -0
  258. package/packages/protocols/mcp/index.ts +2 -0
  259. package/packages/protocols/mcp/tool-contract.ts +42 -0
  260. package/packages/workflow-runtime/api/index.ts +2 -0
  261. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  262. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  263. package/packages/workflow-runtime/index.ts +1 -0
  264. package/scripts/assemble-template.ts +1 -1
  265. package/scripts/benchmark-computer-hotpath.ts +255 -0
  266. package/scripts/benchmark-controller-round2.ts +54 -2
  267. package/scripts/benchmark-ios-agent-device.ts +3 -6
  268. package/scripts/benchmark-mcp-transport.ts +3 -4
  269. package/scripts/benchmark-operational-memory-activation.ts +235 -0
  270. package/scripts/benchmark-operational-shadow.ts +28 -0
  271. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  272. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  273. package/scripts/canary-controller-semantic-wait.ts +195 -0
  274. package/scripts/canary-global-hook.sh +2 -2
  275. package/scripts/check-mcp-compatibility.ts +173 -7
  276. package/scripts/check-platform-support.mjs +28 -2
  277. package/scripts/check-public-docs.sh +1 -1
  278. package/scripts/check-release-readiness.sh +1 -1
  279. package/scripts/check-repository-hygiene.mjs +92 -0
  280. package/scripts/check-runtime-architecture.mjs +1206 -95
  281. package/scripts/check-source-duplication.mjs +9 -1
  282. package/scripts/check-tarball-install-smoke.sh +98 -15
  283. package/scripts/controller-ngrok-rotation.sh +5 -4
  284. package/scripts/create-project-dirs.sh +10 -74
  285. package/scripts/forge-local-recovery-helper.mjs +204 -0
  286. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  287. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  288. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  289. package/scripts/hook-shim.sh +2 -2
  290. package/scripts/init-project.sh +12 -70
  291. package/scripts/install-desktop-operator-registration.ts +4 -2
  292. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  293. package/scripts/install-standalone-recovery.ts +122 -10
  294. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  295. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  296. package/scripts/lib/controller-home-env.sh +6 -3
  297. package/scripts/lib/project-init-lib.sh +39 -582
  298. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  299. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  300. package/scripts/open-source-audit-allowlist.txt +12 -1
  301. package/scripts/portability-bindings.mjs +70 -0
  302. package/scripts/run-bounded-command.mjs +55 -0
  303. package/scripts/run-bun-test-file.ts +22 -6
  304. package/scripts/run-governed-gate.ts +4 -1
  305. package/scripts/smoke-mcp-http-runtime.ts +1 -1
  306. package/scripts/smoke-runtime-recovery.ts +34 -1
  307. package/scripts/smoke-schedule-engine.ts +1 -1
  308. package/scripts/stage-runtime-release.ts +6 -1
  309. package/scripts/test-governance.ts +51 -2
  310. package/src/cli/agent-jobs/job-manager.ts +10 -1
  311. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  312. package/src/cli/chatgpt-browser/binding.ts +20 -10
  313. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  314. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  315. package/src/cli/chatgpt-browser/engine.ts +10 -11
  316. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  317. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  318. package/src/cli/chatgpt-browser/types.ts +1 -0
  319. package/src/cli/commands/bootstrap-control.ts +334 -0
  320. package/src/cli/commands/brain-assistant.ts +38 -0
  321. package/src/cli/commands/brain.ts +193 -40
  322. package/src/cli/commands/chatgpt.ts +127 -0
  323. package/src/cli/commands/computer.ts +437 -0
  324. package/src/cli/commands/doctor.ts +18 -1
  325. package/src/cli/commands/init-hook.ts +71 -61
  326. package/src/cli/commands/init.ts +3 -1
  327. package/src/cli/commands/mcp.ts +16 -10
  328. package/src/cli/commands/plugin.ts +38 -12
  329. package/src/cli/commands/recovery.ts +493 -80
  330. package/src/cli/commands/runtime.ts +9 -4
  331. package/src/cli/commands/setup-profile.ts +89 -39
  332. package/src/cli/commands/status.ts +2 -1
  333. package/src/cli/controller/check-runner.ts +487 -98
  334. package/src/cli/controller/context/types.ts +7 -1
  335. package/src/cli/controller/context-pack.ts +23 -3
  336. package/src/cli/controller/issue-store.ts +1 -1
  337. package/src/cli/controller/systemd-user.ts +110 -0
  338. package/src/cli/controller/types.ts +10 -70
  339. package/src/cli/controller/worklog.ts +67 -13
  340. package/src/cli/editing/edit-operation-contract.ts +92 -0
  341. package/src/cli/editing/edit-session.ts +289 -13
  342. package/src/cli/github/plugin.ts +17 -12
  343. package/src/cli/hook/route-registry.ts +3 -5
  344. package/src/cli/hook/runtime.ts +49 -3
  345. package/src/cli/index.ts +3 -0
  346. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  347. package/src/cli/local-bridge/console-automations.ts +2 -2
  348. package/src/cli/local-bridge/facade-api.ts +41 -14
  349. package/src/cli/local-bridge/job-store.ts +1 -1
  350. package/src/cli/local-bridge/server.ts +24 -27
  351. package/src/cli/mcp/access-mode.ts +2 -132
  352. package/src/cli/mcp/access-tools.ts +2 -387
  353. package/src/cli/mcp/audit.ts +2 -32
  354. package/src/cli/mcp/auth.ts +2 -404
  355. package/src/cli/mcp/instructions.ts +2 -34
  356. package/src/cli/mcp/legacy-context.ts +2 -16
  357. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  358. package/src/cli/mcp/multi-repository.ts +2 -436
  359. package/src/cli/mcp/oauth.ts +2 -375
  360. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  361. package/src/cli/mcp/paths.ts +2 -140
  362. package/src/cli/mcp/policy.ts +2 -232
  363. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  364. package/src/cli/mcp/proxy-env.ts +2 -62
  365. package/src/cli/mcp/readiness-projection.ts +2 -24
  366. package/src/cli/mcp/redaction.ts +2 -21
  367. package/src/cli/mcp/repo.ts +2 -24
  368. package/src/cli/mcp/repository-tools.ts +2 -1585
  369. package/src/cli/mcp/server.ts +2 -951
  370. package/src/cli/mcp/setup.ts +2 -737
  371. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  372. package/src/cli/mcp/tool-contract.ts +2 -14
  373. package/src/cli/mcp/tools.ts +2 -9
  374. package/src/cli/mcp/toolset-names.ts +2 -70
  375. package/src/cli/mcp/toolset-selection.ts +2 -41
  376. package/src/cli/mcp/toolset.ts +2 -336
  377. package/src/cli/mcp/transports/AGENTS.md +1 -10
  378. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  379. package/src/cli/mcp/transports/http.ts +2 -1414
  380. package/src/cli/mcp/transports/session-registry.ts +2 -490
  381. package/src/cli/mcp/transports/stdio.ts +2 -8
  382. package/src/cli/mcp/types.ts +2 -41
  383. package/src/cli/mcp/umbrella-tools.ts +2 -117
  384. package/src/cli/repositories/command-executor.ts +64 -18
  385. package/src/cli/repositories/command-scope.ts +117 -1
  386. package/src/cli/repositories/controller-home.ts +119 -1
  387. package/src/cli/repositories/identity.ts +13 -0
  388. package/src/cli/repositories/registry.ts +68 -49
  389. package/src/cli/repositories/repository-snapshot.ts +53 -7
  390. package/src/cli/repositories/runtime-storage.ts +27 -2
  391. package/src/cli/repositories/safe-patch.ts +34 -38
  392. package/src/cli/repositories/selected-path-actions.ts +22 -2
  393. package/src/cli/repositories/structured-git.ts +105 -25
  394. package/src/cli/runtime/helper-runner.ts +7 -0
  395. package/src/cli/runtime-invocation.ts +8 -1
  396. package/src/cli/tools/codegraph.ts +44 -25
  397. package/src/core/adoption/manifest-templates.ts +1 -1
  398. package/src/core/adoption/plan.ts +3 -26
  399. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  400. package/src/effects/process-runner.ts +8 -14
  401. package/src/runtime/context/assistant-context.ts +154 -0
  402. package/src/runtime/context/assistant-work-context.ts +147 -0
  403. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  404. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  405. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  406. package/src/runtime/context/context-closure.ts +268 -0
  407. package/src/runtime/context/context-plane.ts +211 -0
  408. package/src/runtime/context/project-engineering-contract.ts +60 -0
  409. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  410. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  411. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  412. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  413. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  414. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  415. package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
  416. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  417. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
  418. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
  419. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
  420. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
  421. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +417 -100
  422. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
  423. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  424. package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
  425. package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
  426. package/src/runtime/control-plane/execution/validation.ts +28 -3
  427. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
  428. package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
  429. package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
  430. package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
  431. package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
  432. package/src/runtime/control-plane/execution/work-handle-authority.ts +267 -16
  433. package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
  434. package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
  435. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
  436. package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
  437. package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
  438. package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
  439. package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
  440. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
  441. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
  442. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  443. package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
  444. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
  445. package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
  446. package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
  447. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  448. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
  449. package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
  450. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
  451. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
  452. package/src/runtime/control-plane/facade/index.ts +4 -2
  453. package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
  454. package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
  455. package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
  456. package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
  457. package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
  458. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
  459. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  460. package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
  461. package/src/runtime/control-plane/facade/types.ts +189 -532
  462. package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
  463. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  464. package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
  465. package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
  466. package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
  467. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
  468. package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
  469. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  470. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  471. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
  472. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  473. package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
  474. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  475. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
  476. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  477. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
  478. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
  479. package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
  480. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  481. package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
  482. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  483. package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
  484. package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
  485. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  486. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  487. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  488. package/src/runtime/control-plane/release-retention.ts +56 -16
  489. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  490. package/src/runtime/control-plane/routing/route-policy.ts +19 -0
  491. package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
  492. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  493. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
  494. package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
  495. package/src/runtime/diagnostics/incident-repair.ts +406 -0
  496. package/src/runtime/diagnostics/mcp-timing.ts +125 -6
  497. package/src/runtime/diagnostics/performance.ts +6 -14
  498. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  499. package/src/runtime/evidence/artifact-store.ts +65 -2
  500. package/src/runtime/evidence/operational-shadow.ts +38 -0
  501. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  502. package/src/runtime/evidence/sensitive-output.ts +4 -4
  503. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  504. package/src/runtime/execution/jobs/store.ts +121 -2
  505. package/src/runtime/execution/managed-workspace.ts +55 -5
  506. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  507. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  508. package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
  509. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  510. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  511. package/src/runtime/execution/process-runtime/check-result.ts +73 -2
  512. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
  513. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  514. package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
  515. package/src/runtime/execution/process-runtime/index.ts +1 -0
  516. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  517. package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
  518. package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
  519. package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
  520. package/src/runtime/execution/process-runtime/runtime.ts +132 -15
  521. package/src/runtime/execution/process-runtime/types.ts +22 -0
  522. package/src/runtime/execution/workers/executor.ts +16 -13
  523. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  524. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  525. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  526. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  527. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  528. package/src/runtime/gateway/mcp/router.ts +2 -1125
  529. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  530. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6651
  531. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  532. package/src/runtime/maintenance/cleanup.ts +38 -24
  533. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  534. package/src/runtime/platform/computer-platform.ts +14 -0
  535. package/src/runtime/platform/executable-discovery.ts +54 -0
  536. package/src/runtime/platform/fetch-http-server.ts +90 -0
  537. package/src/runtime/platform/service-manager.ts +138 -0
  538. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  539. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  540. package/src/runtime/plugins/action-application.ts +135 -0
  541. package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
  542. package/src/runtime/plugins/browser-adapter.ts +140 -100
  543. package/src/runtime/plugins/browser-automation-service.ts +15 -36
  544. package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
  545. package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
  546. package/src/runtime/plugins/browser-native-session-service.ts +1 -1
  547. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  548. package/src/runtime/plugins/browser-registration.ts +16 -0
  549. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  550. package/src/runtime/plugins/browser-runtime.ts +41 -9
  551. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  552. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  553. package/src/runtime/plugins/browser-session-store.ts +229 -36
  554. package/src/runtime/plugins/browser-session-types.ts +2 -98
  555. package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
  556. package/src/runtime/plugins/catalog-trust.ts +82 -0
  557. package/src/runtime/plugins/computer-registration.ts +839 -0
  558. package/src/runtime/plugins/config-store.ts +119 -0
  559. package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
  560. package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
  561. package/src/runtime/plugins/errors.ts +8 -1
  562. package/src/runtime/plugins/external-adapter.ts +45 -322
  563. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  564. package/src/runtime/plugins/external-registration.ts +9 -1
  565. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  566. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  567. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  568. package/src/runtime/plugins/first-party-registry.ts +3 -1
  569. package/src/runtime/plugins/github-adapter.ts +1 -2
  570. package/src/runtime/plugins/gmail-adapter.ts +6 -6
  571. package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
  572. package/src/runtime/plugins/google-shared.ts +31 -46
  573. package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
  574. package/src/runtime/plugins/ios-adapter.ts +1 -2
  575. package/src/runtime/plugins/local-system-adapter.ts +180 -0
  576. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  577. package/src/runtime/plugins/resend-adapter.ts +15 -24
  578. package/src/runtime/plugins/store.ts +493 -51
  579. package/src/runtime/plugins/types.ts +8 -1
  580. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  581. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  582. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  583. package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
  584. package/src/runtime/projections/controller-context.ts +12 -6
  585. package/src/runtime/projections/git-status-sampler.ts +73 -22
  586. package/src/runtime/projections/materialized-view.ts +14 -8
  587. package/src/runtime/recovery/capability-action-application.ts +228 -0
  588. package/src/runtime/recovery/index.ts +3 -0
  589. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  590. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  591. package/src/runtime/recovery/maintenance-executor.ts +358 -76
  592. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  593. package/src/runtime/release/identity.ts +6 -0
  594. package/src/runtime/resources/leases/store.ts +1 -1
  595. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  596. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  597. package/src/runtime/root/browser-session-composition.ts +59 -0
  598. package/src/runtime/root/computer-composition.ts +191 -0
  599. package/src/runtime/root/computer-target-composition.ts +16 -0
  600. package/src/runtime/root/computer-target-persistence.ts +39 -0
  601. package/src/runtime/root/controller-round-composition.ts +107 -0
  602. package/src/runtime/root/controller-services.ts +2 -0
  603. package/src/runtime/root/entry.ts +4 -3
  604. package/src/runtime/root/gateway-adapter.ts +3 -3
  605. package/src/runtime/root/local-bridge.ts +5 -2
  606. package/src/runtime/root/mcp-transport.ts +5 -5
  607. package/src/runtime/root/package-connector-service.ts +108 -56
  608. package/src/runtime/root/package-runtime-release.ts +86 -18
  609. package/src/runtime/root/package-runtime-service.ts +63 -86
  610. package/src/runtime/root/release-execution-canary.ts +71 -0
  611. package/src/runtime/root/release-loader.ts +68 -0
  612. package/src/runtime/root/release-manifest.ts +106 -2
  613. package/src/runtime/root/release-materialize.ts +103 -4
  614. package/src/runtime/root/release-store.ts +31 -2
  615. package/src/runtime/root/runtime.ts +240 -25
  616. package/src/runtime/root/scheduled-controller-composition.ts +51 -0
  617. package/src/runtime/root/scheduler.ts +12 -4
  618. package/src/runtime/root/service-runner.ts +1 -1
  619. package/src/runtime/root/service.ts +41 -16
  620. package/src/runtime/root/status.ts +1 -0
  621. package/src/runtime/root/types.ts +24 -1
  622. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  623. package/src/runtime/shared/process-environment.ts +19 -4
  624. package/src/runtime/shared/storage-capacity.ts +68 -0
  625. package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
  626. package/src/runtime/standalone-recovery/core.ts +984 -230
  627. package/src/runtime/standalone-recovery/entry.ts +182 -51
  628. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  629. package/src/runtime/standalone-recovery/installer.ts +212 -104
  630. package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
  631. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  632. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  633. package/src/runtime/standalone-recovery/performance.ts +135 -0
  634. package/src/runtime/standalone-recovery/release.ts +5 -0
  635. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  636. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
  637. package/src/runtime/workflow/schedules/engine.ts +191 -343
  638. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  639. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  640. package/src/runtime/workflow/schedules/store.ts +2 -607
  641. package/src/runtime/workflow/schedules/types.ts +2 -127
  642. package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
  643. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  644. package/src/runtime/workflows/runtime.ts +486 -0
  645. package/src/testing/test-governance.ts +90 -29
  646. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  647. package/docs/architecture/history/README.md +0 -7
  648. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  649. package/docs/architecture/history/global-hook-runtime.md +0 -166
  650. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  651. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  652. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  653. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  654. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  655. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  656. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  657. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  658. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  659. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  660. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  661. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  662. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  663. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  664. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  665. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  666. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  667. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  668. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  669. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  670. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  671. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  672. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  673. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  674. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  675. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  676. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  677. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  678. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  679. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  680. package/docs/architecture/requests/archive/2026/root.md +0 -68
  681. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  682. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  683. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  684. package/scripts/archive-architecture-request.sh +0 -4
  685. package/scripts/run-skill-evals.ts +0 -1022
@@ -1,6 +1,5 @@
1
1
  import { spawnSync } from 'child_process';
2
2
  import {
3
- appendFileSync,
4
3
  existsSync,
5
4
  mkdirSync,
6
5
  readFileSync,
@@ -17,6 +16,7 @@ import {
17
16
  ensureControllerHome,
18
17
  ensureRepositoryControllerLayout,
19
18
  } from './controller-home';
19
+ import { withControllerLock } from './locks';
20
20
  import {
21
21
  inferDisplayName,
22
22
  newLocalRepoId,
@@ -38,7 +38,7 @@ import type {
38
38
 
39
39
  const REGISTRY_FILE = 'repositories.json';
40
40
  const FOCUS_FILE = 'focus.json';
41
- const LOCAL_CONFIG = '.ai/harness/repository.json';
41
+ const LEGACY_LOCAL_CONFIG = '.ai/harness/repository.json';
42
42
  const LEGACY_GITHUB_PLUGIN_CONFIG = '.forge/plugins/github.json';
43
43
 
44
44
  interface RegisterRepositoryInput {
@@ -218,6 +218,25 @@ function repositoryByGitCommonDirectory(records: RepositoryRecord[], root: strin
218
218
  return linkedWorktreeFallback ?? historicalCheckoutFallback;
219
219
  }
220
220
 
221
+ /**
222
+ * Resolve an already-registered repository from one concrete checkout root
223
+ * without mutating Repository Registry state. Exact canonical/active-checkout
224
+ * matches stay filesystem-only; Git common-directory probing is a bounded
225
+ * compatibility fallback for linked worktrees that predate checkout tracking.
226
+ */
227
+ export function findRegisteredRepositoryByCheckoutRoot(
228
+ root: string,
229
+ controllerHome?: string,
230
+ ): RepositoryRecord | undefined {
231
+ const candidateRoot = resolve(root);
232
+ const records = listRepositories(controllerHome).filter((record) => record.enabled !== false && !record.removedAt);
233
+ const exact = records.find((record) => (
234
+ comparablePath(record.canonicalRoot) === comparablePath(candidateRoot)
235
+ || activeCheckouts(record).some((checkout) => comparablePath(checkout.canonicalRoot) === comparablePath(candidateRoot))
236
+ ));
237
+ return exact ?? repositoryByGitCommonDirectory(records, candidateRoot);
238
+ }
239
+
221
240
  function managedCheckoutMatchesRepository(record: RepositoryRecord, checkout: RepositoryCheckout): boolean {
222
241
  return repositoryCheckoutRootMatches(record, checkout.canonicalRoot);
223
242
  }
@@ -336,9 +355,27 @@ export function loadRepositoryRegistry(controllerHome?: string): RepositoryRegis
336
355
 
337
356
  export function saveRepositoryRegistry(registry: RepositoryRegistry, controllerHome?: string): RepositoryRegistry {
338
357
  const home = ensureControllerHome(registryHome(controllerHome));
339
- const next = { ...registry, schemaVersion: 1 as const, updatedAt: now() };
340
- atomicJson(join(home, REGISTRY_FILE), next);
341
- return next;
358
+ return withControllerLock(
359
+ home,
360
+ { scope: 'global', resource: 'repository-registry' },
361
+ 'repository-registry:save',
362
+ () => {
363
+ const path = join(home, REGISTRY_FILE);
364
+ const current = readRegistryFile(path, true);
365
+ if (current && current.updatedAt !== registry.updatedAt) {
366
+ throw new Error(
367
+ `REPOSITORY_REGISTRY_STALE: expected=${registry.updatedAt}; current=${current.updatedAt}; reload canonical Repository Registry and retry`,
368
+ );
369
+ }
370
+ const previousRevision = timestampValue(registry.updatedAt);
371
+ const updatedAt = new Date(Math.max(Date.now(), previousRevision + 1)).toISOString();
372
+ const next = { ...registry, schemaVersion: 1 as const, updatedAt };
373
+ atomicJson(path, next);
374
+ return next;
375
+ },
376
+ 30_000,
377
+ 5_000,
378
+ );
342
379
  }
343
380
 
344
381
  export function consolidateRepositoryRegistry(controllerHome?: string): RepositoryRegistry {
@@ -354,7 +391,7 @@ function resolveGitRoot(inputPath: string): string {
354
391
  }
355
392
 
356
393
  function readLocalIdentity(canonicalRoot: string): { repoId?: string } {
357
- const path = join(canonicalRoot, LOCAL_CONFIG);
394
+ const path = join(canonicalRoot, LEGACY_LOCAL_CONFIG);
358
395
  if (!existsSync(path)) return {};
359
396
  try {
360
397
  const parsed = JSON.parse(readFileSync(path, 'utf-8')) as { repoId?: unknown };
@@ -395,28 +432,6 @@ function readLegacyGitHubPluginConfig(canonicalRoot: string): Partial<Repository
395
432
  }
396
433
  }
397
434
 
398
- function ensureHarnessRuntimeIgnored(canonicalRoot: string): void {
399
- const gitExclude = git(canonicalRoot, ['rev-parse', '--git-path', 'info/exclude']);
400
- if (!gitExclude) return;
401
- const path = resolve(canonicalRoot, gitExclude);
402
- const existing = existsSync(path) ? readFileSync(path, 'utf-8') : '';
403
- if (existing.split(/\r?\n/).some((line) => line.trim() === '.ai/harness/')) return;
404
- mkdirSync(dirname(path), { recursive: true });
405
- const prefix = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
406
- appendFileSync(path, `${prefix}# forge runtime metadata (controller-owned, never source)\n.ai/harness/\n`, 'utf-8');
407
- }
408
-
409
- function writeLocalIdentity(record: RepositoryRecord): void {
410
- ensureHarnessRuntimeIgnored(record.canonicalRoot);
411
- const path = join(record.canonicalRoot, LOCAL_CONFIG);
412
- atomicJson(path, {
413
- schemaVersion: 1,
414
- repoId: record.repoId,
415
- checkoutId: record.activeCheckoutId,
416
- stateStorageStrategy: record.stateStorageStrategy,
417
- });
418
- }
419
-
420
435
  function repositoryType(root: string, remoteUrl: string | undefined): RepositoryType {
421
436
  const bare = git(root, ['rev-parse', '--is-bare-repository']);
422
437
  if (bare === 'true') return 'bare';
@@ -553,6 +568,27 @@ export function getRepository(repoId: string, controllerHome?: string, options:
553
568
  return record;
554
569
  }
555
570
 
571
+ export type RepositoryCheckoutSelectionErrorCode = 'CHECKOUT_NOT_FOUND' | 'CHECKOUT_NOT_ACTIVE';
572
+
573
+ export class RepositoryCheckoutSelectionError extends Error {
574
+ readonly code: RepositoryCheckoutSelectionErrorCode;
575
+ readonly repoId: string;
576
+ readonly checkoutId: string;
577
+ readonly lifecycle?: RepositoryCheckoutLifecycle;
578
+
579
+ constructor(input: { code: RepositoryCheckoutSelectionErrorCode; repoId: string; checkoutId: string; lifecycle?: RepositoryCheckoutLifecycle }) {
580
+ const message = input.code === 'CHECKOUT_NOT_FOUND'
581
+ ? `checkout not found for ${input.repoId}: ${input.checkoutId}`
582
+ : `CHECKOUT_NOT_ACTIVE: ${input.repoId}/${input.checkoutId} is ${input.lifecycle ?? 'unknown'}`;
583
+ super(message);
584
+ this.name = 'RepositoryCheckoutSelectionError';
585
+ this.code = input.code;
586
+ this.repoId = input.repoId;
587
+ this.checkoutId = input.checkoutId;
588
+ this.lifecycle = input.lifecycle;
589
+ }
590
+ }
591
+
556
592
  export function selectRepositoryCheckout(
557
593
  record: RepositoryRecord,
558
594
  checkoutId?: string,
@@ -560,10 +596,10 @@ export function selectRepositoryCheckout(
560
596
  ): RepositoryRecord {
561
597
  if (!checkoutId?.trim()) return record;
562
598
  const checkout = record.checkouts.find((candidate) => candidate.checkoutId === checkoutId.trim());
563
- if (!checkout) throw new Error(`checkout not found for ${record.repoId}: ${checkoutId}`);
599
+ if (!checkout) throw new RepositoryCheckoutSelectionError({ code: 'CHECKOUT_NOT_FOUND', repoId: record.repoId, checkoutId: checkoutId.trim() });
564
600
  const lifecycle = repositoryCheckoutLifecycle(checkout);
565
601
  if (lifecycle !== 'active' && !(options.allowArchived === true && lifecycle === 'archived')) {
566
- throw new Error(`CHECKOUT_NOT_ACTIVE: ${record.repoId}/${checkout.checkoutId} is ${lifecycle}`);
602
+ throw new RepositoryCheckoutSelectionError({ code: 'CHECKOUT_NOT_ACTIVE', repoId: record.repoId, checkoutId: checkout.checkoutId, lifecycle });
567
603
  }
568
604
  return {
569
605
  ...record,
@@ -645,13 +681,6 @@ export function registerRepository(input: RegisterRepositoryInput): RepositoryRe
645
681
  registry.repositories[index] = next;
646
682
  saveRepositoryRegistry(registry, home);
647
683
  ensureRepositoryControllerLayout(home, next.repoId);
648
- ensureHarnessRuntimeIgnored(canonicalRoot);
649
- atomicJson(join(canonicalRoot, LOCAL_CONFIG), {
650
- schemaVersion: 1,
651
- repoId: next.repoId,
652
- checkoutId,
653
- stateStorageStrategy: next.stateStorageStrategy,
654
- });
655
684
  return selectRepositoryCheckout(next, checkoutId);
656
685
  }
657
686
 
@@ -675,7 +704,7 @@ export function registerRepository(input: RegisterRepositoryInput): RepositoryRe
675
704
  createdAt: existing?.createdAt ?? timestamp,
676
705
  updatedAt: timestamp,
677
706
  lastSeenAt: timestamp,
678
- configurationPath: join(canonicalRoot, LOCAL_CONFIG),
707
+ configurationPath: registryPath(home),
679
708
  stateStorageStrategy: input.stateStorageStrategy ?? existing?.stateStorageStrategy ?? 'hybrid',
680
709
  disabledAt: enabled ? undefined : existing?.disabledAt,
681
710
  removedAt: enabled ? undefined : existing?.removedAt,
@@ -687,7 +716,6 @@ export function registerRepository(input: RegisterRepositoryInput): RepositoryRe
687
716
  ];
688
717
  saveRepositoryRegistry(registry, home);
689
718
  ensureRepositoryControllerLayout(home, repoId);
690
- writeLocalIdentity(record);
691
719
  return record;
692
720
  }
693
721
 
@@ -771,13 +799,6 @@ export function addRepositoryCheckout(input: AddRepositoryCheckoutInput): Reposi
771
799
  };
772
800
  registry.repositories[index] = next;
773
801
  saveRepositoryRegistry(registry, home);
774
- ensureHarnessRuntimeIgnored(canonicalRoot);
775
- atomicJson(join(canonicalRoot, LOCAL_CONFIG), {
776
- schemaVersion: 1,
777
- repoId: next.repoId,
778
- checkoutId,
779
- stateStorageStrategy: next.stateStorageStrategy,
780
- });
781
802
  return next;
782
803
  }
783
804
 
@@ -868,7 +889,6 @@ export function updateRepository(repoId: string, patch: UpdateRepositoryInput, c
868
889
  };
869
890
  registry.repositories[index] = next;
870
891
  saveRepositoryRegistry(registry, controllerHome);
871
- writeLocalIdentity(next);
872
892
  return next;
873
893
  }
874
894
 
@@ -993,13 +1013,12 @@ export function refreshRepository(repoId: string, controllerHome?: string): Repo
993
1013
  repositoryType: repositoryType(canonicalRoot, rawRemote),
994
1014
  updatedAt: timestamp,
995
1015
  lastSeenAt: timestamp,
996
- configurationPath: join(canonicalRoot, LOCAL_CONFIG),
1016
+ configurationPath: registryPath(home),
997
1017
  };
998
1018
  registry.repositories[index] = refreshed;
999
1019
  registry.repositories = retireCanonicalDuplicates(registry.repositories, canonicalRoot, repoId, timestamp);
1000
1020
  saveRepositoryRegistry(registry, home);
1001
1021
  ensureRepositoryControllerLayout(home, repoId);
1002
- writeLocalIdentity(refreshed);
1003
1022
  return refreshed;
1004
1023
  }
1005
1024
 
@@ -5,7 +5,11 @@ import { spawn, spawnSync } from 'child_process';
5
5
  import { capProcessOutput, redactProcessOutput } from '../../effects/process-runner';
6
6
  import { commandEnvironment, collectOutput } from './command-process';
7
7
 
8
+ export type RepositoryCommandMutationEvidence = 'observed' | 'readonly_unobserved' | 'not_applicable';
9
+
8
10
  export interface RepositoryCommandSnapshot {
11
+ /** Whether this value contains authoritative mutation evidence for comparison. */
12
+ mutationEvidence: RepositoryCommandMutationEvidence;
9
13
  head: string | null;
10
14
  branch: string | null;
11
15
  status: string;
@@ -15,8 +19,17 @@ export interface RepositoryCommandSnapshot {
15
19
  pathFingerprints: Record<string, string>;
16
20
  }
17
21
 
22
+ function neutralSnapshot(mutationEvidence: Exclude<RepositoryCommandMutationEvidence, 'observed'>): RepositoryCommandSnapshot {
23
+ return { mutationEvidence, head: null, branch: null, status: '', dirty: false, refsHash: createHash('sha256').update('').digest('hex'), paths: [], pathFingerprints: {} };
24
+ }
25
+
18
26
  export function emptyWorkspaceSnapshot(): RepositoryCommandSnapshot {
19
- return { head: null, branch: null, status: '', dirty: false, refsHash: createHash('sha256').update('').digest('hex'), paths: [], pathFingerprints: {} };
27
+ return neutralSnapshot('not_applicable');
28
+ }
29
+
30
+ /** Proven-readonly execution deliberately owns no before/after mutation observation. */
31
+ export function readonlyUnobservedRepositorySnapshot(): RepositoryCommandSnapshot {
32
+ return neutralSnapshot('readonly_unobserved');
20
33
  }
21
34
 
22
35
  function commandOutput(command: string, args: string[], cwd: string, maxOutputBytes: number): { ok: boolean; stdout: string } {
@@ -54,7 +67,7 @@ function buildSnapshot(root: string, head: string | null, branch: string | null,
54
67
  byPath.set(path, [...(byPath.get(path) ?? []), line]);
55
68
  }
56
69
  const paths = [...byPath.keys()].sort();
57
- return { head, branch, status, dirty: paths.length > 0, refsHash: createHash('sha256').update(refs).digest('hex'), paths, pathFingerprints: Object.fromEntries(paths.map((path) => [path, pathFingerprint(root, path, byPath.get(path) ?? [])])) };
70
+ return { mutationEvidence: 'observed', head, branch, status, dirty: paths.length > 0, refsHash: createHash('sha256').update(refs).digest('hex'), paths, pathFingerprints: Object.fromEntries(paths.map((path) => [path, pathFingerprint(root, path, byPath.get(path) ?? [])])) };
58
71
  }
59
72
 
60
73
  export function repositorySnapshot(root: string): RepositoryCommandSnapshot {
@@ -84,7 +97,7 @@ const MAX_DIRTY_PATHS = 200;
84
97
  export async function repositorySnapshotAsync(
85
98
  root: string,
86
99
  signal?: AbortSignal,
87
- options: { pathFingerprints?: boolean; fingerprintTimeoutMs?: number } = {},
100
+ options: { pathFingerprints?: boolean; fingerprintTimeoutMs?: number; fingerprintPaths?: readonly string[] } = {},
88
101
  ): Promise<RepositoryCommandSnapshot> {
89
102
  const [headResult, branchResult, statusResult, refsResult] = await Promise.all([
90
103
  gitAsync(root, ['rev-parse', '--verify', 'HEAD'], signal), gitAsync(root, ['branch', '--show-current'], signal),
@@ -100,25 +113,58 @@ export async function repositorySnapshotAsync(
100
113
  if (signal?.aborted) throw new Error('CANCELLED: repository snapshot aborted');
101
114
  const lines = statusResult.stdout.split(/\r?\n/).filter((line) => line && !line.startsWith('##'));
102
115
  const includePathFingerprints = options.pathFingerprints !== false;
103
- if (includePathFingerprints && lines.length > MAX_DIRTY_PATHS) throw new Error(`SNAPSHOT_TOO_DIRTY: ${lines.length} dirty paths exceeds Fast Path cap ${MAX_DIRTY_PATHS}`);
104
116
  const byPath = new Map<string, string[]>();
105
117
  for (const line of lines) { const path = statusPath(line); if (path) byPath.set(path, [...(byPath.get(path) ?? []), line]); }
106
118
  const paths = [...byPath.keys()].sort();
119
+ const requestedFingerprintPaths = options.fingerprintPaths
120
+ ? [...new Set(options.fingerprintPaths.map((path) => path.trim().replace(/\\/g, '/').replace(/^\.\//, '')).filter(Boolean))]
121
+ .filter((path) => byPath.has(path))
122
+ .sort()
123
+ : undefined;
124
+ const fingerprintPaths = requestedFingerprintPaths ?? paths;
125
+ if (includePathFingerprints && fingerprintPaths.length > MAX_DIRTY_PATHS) {
126
+ throw new Error(`SNAPSHOT_TOO_DIRTY: ${fingerprintPaths.length} fingerprint paths exceeds Fast Path cap ${MAX_DIRTY_PATHS}`);
127
+ }
107
128
  let pathFingerprints: Record<string, string> = {};
108
129
  if (includePathFingerprints) {
109
130
  const { computePathFingerprintsAsync } = await import('../../runtime/execution/thin-harness/fingerprint-worker');
110
131
  const fingerprintTimeoutMs = typeof options.fingerprintTimeoutMs === 'number' && Number.isFinite(options.fingerprintTimeoutMs)
111
132
  ? Math.max(1, Math.trunc(options.fingerprintTimeoutMs))
112
133
  : 5_000;
113
- const fingerprint = await computePathFingerprintsAsync({ root, paths, statusByPath: Object.fromEntries(byPath), maxFileBytes: 256 * 1024, maxTotalBytes: 8 * 1024 * 1024, maxPaths: MAX_DIRTY_PATHS }, { signal, timeoutMs: fingerprintTimeoutMs });
134
+ const fingerprintStatusByPath = Object.fromEntries(fingerprintPaths.map((path) => [path, byPath.get(path) ?? []]));
135
+ const fingerprint = await computePathFingerprintsAsync({ root, paths: fingerprintPaths, statusByPath: fingerprintStatusByPath, maxFileBytes: 256 * 1024, maxTotalBytes: 8 * 1024 * 1024, maxPaths: MAX_DIRTY_PATHS }, { signal, timeoutMs: fingerprintTimeoutMs });
114
136
  pathFingerprints = fingerprint.pathFingerprints;
115
137
  }
116
- return { head, branch: branchResult.ok ? branchResult.stdout || null : null, status: statusResult.stdout, dirty: paths.length > 0, refsHash: createHash('sha256').update(refs).digest('hex'), paths, pathFingerprints };
138
+ return { mutationEvidence: 'observed', head, branch: branchResult.ok ? branchResult.stdout || null : null, status: statusResult.stdout, dirty: paths.length > 0, refsHash: createHash('sha256').update(refs).digest('hex'), paths, pathFingerprints };
139
+ }
140
+
141
+ function assertMutationEvidenceComparable(before: RepositoryCommandSnapshot, after: RepositoryCommandSnapshot): void {
142
+ if (before.mutationEvidence === 'readonly_unobserved' || after.mutationEvidence === 'readonly_unobserved') {
143
+ throw new Error('SNAPSHOT_COMPARISON_UNOBSERVED: readonly execution has no mutation snapshot to compare');
144
+ }
145
+ }
146
+
147
+ function snapshotStatusByPath(snapshot: RepositoryCommandSnapshot): Map<string, string[]> {
148
+ const byPath = new Map<string, string[]>();
149
+ for (const line of snapshot.status.split(/\r?\n/).filter((value) => value && !value.startsWith('##'))) {
150
+ const path = statusPath(line);
151
+ if (path) byPath.set(path, [...(byPath.get(path) ?? []), line]);
152
+ }
153
+ return byPath;
117
154
  }
118
155
 
119
156
  export function changedSnapshotPaths(before: RepositoryCommandSnapshot, after: RepositoryCommandSnapshot): string[] {
120
- return [...new Set([...before.paths, ...after.paths])].filter((path) => before.pathFingerprints[path] !== after.pathFingerprints[path]).sort();
157
+ assertMutationEvidenceComparable(before, after);
158
+ const beforeStatus = snapshotStatusByPath(before);
159
+ const afterStatus = snapshotStatusByPath(after);
160
+ return [...new Set([...before.paths, ...after.paths])].filter((path) => {
161
+ if ((beforeStatus.get(path) ?? []).join('\n') !== (afterStatus.get(path) ?? []).join('\n')) return true;
162
+ const beforeFingerprint = before.pathFingerprints[path];
163
+ const afterFingerprint = after.pathFingerprints[path];
164
+ return beforeFingerprint !== undefined && afterFingerprint !== undefined && beforeFingerprint !== afterFingerprint;
165
+ }).sort();
121
166
  }
122
167
  export function snapshotChanged(before: RepositoryCommandSnapshot, after: RepositoryCommandSnapshot): boolean {
168
+ assertMutationEvidenceComparable(before, after);
123
169
  return before.head !== after.head || before.branch !== after.branch || before.refsHash !== after.refsHash || changedSnapshotPaths(before, after).length > 0;
124
170
  }
@@ -49,6 +49,7 @@ interface RuntimeStorageSpec {
49
49
  name: string;
50
50
  sourceName: string;
51
51
  controllerName: string;
52
+ sourceRoot?: 'harness' | 'forge';
52
53
  preserveNonEmpty?: boolean;
53
54
  detectActiveRuns?: boolean;
54
55
  detectActiveLocalJobs?: boolean;
@@ -68,6 +69,15 @@ const RUNTIME_STORAGE_SPECS: RuntimeStorageSpec[] = [
68
69
  { name: 'mcp', sourceName: 'mcp', controllerName: 'mcp' },
69
70
  { name: 'local-bridge', sourceName: 'local-bridge', controllerName: 'local-bridge' },
70
71
  { name: 'ephemeral-issues', sourceName: 'ephemeral-issues', controllerName: 'ephemeral-issues' },
72
+ // Legacy callers may still use these repository-relative paths, but the
73
+ // physical state is Controller Home-owned. Keep them as compatibility links
74
+ // until the corresponding adapters consume Controller paths directly.
75
+ { name: 'session', sourceName: 'session', controllerName: 'session' },
76
+ { name: 'planning', sourceName: 'planning', controllerName: 'planning' },
77
+ { name: 'security', sourceName: 'security', controllerName: 'security' },
78
+ { name: 'transfers', sourceName: 'transfers', controllerName: 'transfers' },
79
+ { name: 'browser-provider', sourceName: 'browser', controllerName: 'browser', sourceRoot: 'forge' },
80
+ { name: 'interaction-sessions', sourceName: 'interactions', controllerName: 'interactions', sourceRoot: 'forge' },
71
81
  ];
72
82
 
73
83
  function directoryEntries(path: string): string[] {
@@ -257,12 +267,14 @@ function legacyRuntimeBlocker(repositoryPath: string, spec: RuntimeStorageSpec):
257
267
  }
258
268
 
259
269
  function bindRuntimeDirectory(
270
+ repositoryRoot: string,
260
271
  harnessRoot: string,
261
272
  controllerRoot: string,
262
273
  repoId: string,
263
274
  spec: RuntimeStorageSpec,
264
275
  ): RuntimeStorageBinding {
265
- const repositoryPath = join(harnessRoot, spec.sourceName);
276
+ const sourceRoot = spec.sourceRoot === 'forge' ? join(repositoryRoot, '.forge') : harnessRoot;
277
+ const repositoryPath = join(sourceRoot, spec.sourceName);
266
278
  const controllerPath = join(controllerRoot, spec.controllerName);
267
279
  mkdirSync(controllerPath, { recursive: true });
268
280
  writeOwnerMarker(controllerPath, repoId, spec.name);
@@ -378,6 +390,19 @@ function bindRuntimeDirectory(
378
390
  return { name: spec.name, repositoryPath, controllerPath, status: 'migrated' };
379
391
  }
380
392
 
393
+ export function ensureRepositoryRuntimeStorageBinding(
394
+ repository: RepositoryRecord,
395
+ name: string,
396
+ controllerHome?: string,
397
+ ): RuntimeStorageBinding {
398
+ const spec = RUNTIME_STORAGE_SPECS.find((entry) => entry.name === name);
399
+ if (!spec) throw new Error(`RUNTIME_STORAGE_BINDING_UNKNOWN: ${name}`);
400
+ const controllerRoot = ensureRepositoryControllerLayout(controllerHome ?? '', repository.repoId);
401
+ const harnessRoot = join(repository.canonicalRoot, '.ai', 'harness');
402
+ mkdirSync(harnessRoot, { recursive: true });
403
+ return bindRuntimeDirectory(repository.canonicalRoot, harnessRoot, controllerRoot, repository.repoId, spec);
404
+ }
405
+
381
406
  export function ensureRepositoryRuntimeStorage(
382
407
  repository: RepositoryRecord,
383
408
  controllerHome?: string,
@@ -386,7 +411,7 @@ export function ensureRepositoryRuntimeStorage(
386
411
  const harnessRoot = join(repository.canonicalRoot, '.ai', 'harness');
387
412
  mkdirSync(harnessRoot, { recursive: true });
388
413
 
389
- const bindings = RUNTIME_STORAGE_SPECS.map((spec) => bindRuntimeDirectory(harnessRoot, controllerRoot, repository.repoId, spec));
414
+ const bindings = RUNTIME_STORAGE_SPECS.map((spec) => bindRuntimeDirectory(repository.canonicalRoot, harnessRoot, controllerRoot, repository.repoId, spec));
390
415
  const warnings = bindings
391
416
  .filter((binding) => binding.status === 'legacy-active' || binding.status === 'conflict')
392
417
  .map((binding) => `${binding.name}: ${binding.message ?? binding.status}`);
@@ -2,16 +2,18 @@ import { createHash } from 'crypto';
2
2
  import { existsSync, readFileSync } from 'fs';
3
3
  import { join, relative, resolve } from 'path';
4
4
  import {
5
+ adoptEditSessionSuccessorHead,
5
6
  applyEditOperations,
6
7
  beginEditSession,
7
8
  getEditSession,
8
- type EditOperation,
9
9
  type EditSession,
10
10
  type EditSessionBinding,
11
11
  type EditSessionPatchError,
12
12
  } from '../editing/edit-session';
13
+ import { normalizeEditOperation, type EditOperation } from '../editing/edit-operation-contract';
13
14
  import { getMcpPolicy } from '../mcp/policy';
14
15
  import type { RepositoryRecord } from './types';
16
+ import { ensureRepositoryRuntimeStorageBinding } from './runtime-storage';
15
17
 
16
18
  export interface SafePatchFingerprint {
17
19
  path: string;
@@ -100,46 +102,15 @@ function objectEntries(value: unknown): Record<string, unknown>[] {
100
102
  return value.filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null && !Array.isArray(entry));
101
103
  }
102
104
 
103
- function normalizeReplacement(value: unknown): Array<{ oldText: string; newText: string; replaceAll?: boolean }> {
104
- return objectEntries(value).map((replacement) => ({
105
- oldText: String(replacement.old_text ?? replacement.oldText ?? ''),
106
- newText: String(replacement.new_text ?? replacement.newText ?? ''),
107
- replaceAll: replacement.replace_all === true || replacement.replaceAll === true,
108
- }));
109
- }
110
-
111
105
  function normalizeOperation(entry: Record<string, unknown>, originalIndex: number): NormalizedOperation {
112
- const type = String(entry.type ?? '');
113
106
  const path = String(entry.path ?? '').trim();
114
- const expectedSha256 = String(entry.expected_sha256 ?? entry.expectedSha256 ?? '').trim();
115
107
  if (!path) throw new Error(`SAFE_PATCH_OPERATION_INVALID: operation ${originalIndex + 1} path is required`);
116
- if (type === 'create') return { type, path, content: String(entry.content ?? ''), __originalIndex: originalIndex };
117
- if (type === 'delete') return { type, path, expectedSha256, __originalIndex: originalIndex };
118
- if (type === 'write') return { type, path, expectedSha256, content: String(entry.content ?? ''), __originalIndex: originalIndex };
119
- if (type === 'replace') {
120
- const replacements = Array.isArray(entry.replacements)
121
- ? normalizeReplacement(entry.replacements)
122
- : (entry.old_text !== undefined || entry.oldText !== undefined)
123
- ? normalizeReplacement([entry])
124
- : [];
125
- if (replacements.length === 0) {
126
- throw new Error(`SAFE_PATCH_OPERATION_INVALID: operation ${originalIndex + 1} replace requires replacements[] or old_text/new_text`);
127
- }
128
- return { type, path, expectedSha256, replacements, __originalIndex: originalIndex };
129
- }
130
- if (type === 'insert_before' || type === 'insert_after') {
131
- return {
132
- type,
133
- path,
134
- expectedSha256,
135
- anchor: String(entry.anchor ?? ''),
136
- content: String(entry.content ?? ''),
137
- occurrence: typeof entry.occurrence === 'number' ? Math.trunc(entry.occurrence) : undefined,
138
- __originalIndex: originalIndex,
139
- };
108
+ try {
109
+ return { ...normalizeEditOperation({ ...entry, path }), __originalIndex: originalIndex } as NormalizedOperation;
110
+ } catch (error) {
111
+ const message = error instanceof Error ? error.message : String(error);
112
+ throw new Error(`SAFE_PATCH_OPERATION_INVALID: operation ${originalIndex + 1} ${message}`);
140
113
  }
141
- if (type === 'prepend' || type === 'append') return { type, path, expectedSha256, content: String(entry.content ?? ''), __originalIndex: originalIndex };
142
- throw new Error(`SAFE_PATCH_OPERATION_INVALID: operation ${originalIndex + 1} has invalid type: ${type}`);
143
114
  }
144
115
 
145
116
  export function normalizeSafePatchOperations(value: unknown): EditOperation[] {
@@ -348,6 +319,10 @@ export function applySafePatch(repository: RepositoryRecord, input: {
348
319
  recoverStaleSession?: unknown;
349
320
  binding?: EditSessionBinding;
350
321
  }): SafePatchApplyResult {
322
+ const editStorage = ensureRepositoryRuntimeStorageBinding(repository, 'edit-sessions');
323
+ if (editStorage.status === 'legacy-active' || editStorage.status === 'conflict') {
324
+ throw new Error(`SAFE_PATCH_EDIT_SESSION_STORAGE_NOT_READY: ${editStorage.message ?? editStorage.status}`);
325
+ }
351
326
  const operations = normalizedSafePatchOperations(input.operations);
352
327
  if (operations.length === 0) throw new Error('SAFE_PATCH_OPERATIONS_REQUIRED: provide at least one operation');
353
328
  const size = chunkSize(input.chunkSize);
@@ -381,7 +356,28 @@ export function applySafePatch(repository: RepositoryRecord, input: {
381
356
  session = applyEditOperations(repository.canonicalRoot, policy, session.sessionId, preflight.operations.map(stripInternalIndex), { expectedRevision, maxBatchOperations: MAX_SAFE_PATCH_CHUNK_SIZE, binding: input.binding });
382
357
  } catch (error) {
383
358
  const message = error instanceof Error ? error.message : String(error);
384
- if (!sessionId && input.recoverStaleSession !== false && message.includes('EDIT_SESSION_FINGERPRINT_STALE')) {
359
+ if (sessionId && input.recoverStaleSession !== false && message.includes('EDIT_SESSION_BASE_REVISION_CHANGED')) {
360
+ const previousSessionId = session.sessionId;
361
+ try {
362
+ session = adoptEditSessionSuccessorHead(repository.canonicalRoot, session.sessionId, { binding: input.binding });
363
+ expectedRevision = session.currentRevision;
364
+ recoveredSession = { reason: 'Existing Work-bound edit session adopted a proven non-overlapping successor HEAD.', previousSessionId, newSessionId: session.sessionId };
365
+ const retry = preflightChunk(repository, group, chunkIndex, refresh);
366
+ if (retry.failures.length === 0) {
367
+ session = applyEditOperations(repository.canonicalRoot, policy, session.sessionId, retry.operations.map(stripInternalIndex), { expectedRevision, maxBatchOperations: MAX_SAFE_PATCH_CHUNK_SIZE, binding: input.binding });
368
+ } else {
369
+ failures.push(...retry.failures);
370
+ failedChunk = chunkIndex;
371
+ if (!continueOnError) break;
372
+ continue;
373
+ }
374
+ } catch (adoptionError) {
375
+ failures.push(...patchErrorFailures(adoptionError, chunkIndex));
376
+ failedChunk = chunkIndex;
377
+ if (!continueOnError) break;
378
+ continue;
379
+ }
380
+ } else if (!sessionId && input.recoverStaleSession !== false && message.includes('EDIT_SESSION_FINGERPRINT_STALE')) {
385
381
  const previousSessionId = session.sessionId;
386
382
  session = beginEditSession(repository.canonicalRoot, { purpose: `${String(input.purpose ?? 'Safe repository patch').trim() || 'Safe repository patch'} (recovered)`, allowedPaths, binding: input.binding });
387
383
  expectedRevision = session.currentRevision;
@@ -4,6 +4,7 @@ import { join, relative, resolve } from 'path';
4
4
  import { capProcessOutput, redactProcessOutput } from '../../effects/process-runner';
5
5
  import { buildPatchHandoffArtifact } from '../../runtime/recovery/patch-handoff';
6
6
  import { executeRepositoryGitCommand, type RepositoryGitExecution } from './git-command-executor';
7
+ import { resolveRepositoryGitCommitScope } from './structured-git';
7
8
  import { withControllerLock } from './locks';
8
9
  import type { RepositoryRecord } from './types';
9
10
 
@@ -32,6 +33,16 @@ export interface SelectedPathCommitResult {
32
33
  error?: { code: string; message: string };
33
34
  }
34
35
 
36
+ export interface SelectedPathBeforeCommitGuardInput {
37
+ repository: RepositoryRecord;
38
+ requestedPaths: string[];
39
+ stagedPaths: string[];
40
+ /** Exact HEAD observed after staging and immediately before the physical commit. */
41
+ currentHead: string | null;
42
+ }
43
+
44
+ export type SelectedPathBeforeCommitGuard = (input: SelectedPathBeforeCommitGuardInput) => void;
45
+
35
46
  export interface TransferArtifactPreview {
36
47
  path: string;
37
48
  exists: boolean;
@@ -249,9 +260,12 @@ export function stageSelectedPaths(
249
260
  export function commitSelectedPaths(
250
261
  controllerHome: string,
251
262
  repository: RepositoryRecord,
252
- input: { paths: unknown; message: unknown },
263
+ input: { paths: unknown; message: unknown; beforeCommitGuard?: SelectedPathBeforeCommitGuard },
253
264
  ): SelectedPathCommitResult {
254
- const paths = normalizeSelectedPaths(repository, input.paths);
265
+ const selectedPaths = normalizeSelectedPaths(repository, input.paths);
266
+ const scope = resolveRepositoryGitCommitScope(repository, { paths: selectedPaths });
267
+ if (scope.source !== 'explicit_paths') throw new Error('SELECTED_PATH_COMMIT_SCOPE_INVALID: selected-path commit requires explicit path scope');
268
+ const paths = scope.paths;
255
269
  const message = String(input.message ?? '').trim();
256
270
  if (!message) throw new Error('COMMIT_MESSAGE_REQUIRED: message is required');
257
271
 
@@ -283,6 +297,12 @@ export function commitSelectedPaths(
283
297
  };
284
298
  }
285
299
 
300
+ // The repository layer owns staging and exact staged-path discovery, but it
301
+ // does not own Work lifecycle/review policy. Callers may inject one typed
302
+ // fail-closed guard at the last safe boundary before the physical commit.
303
+ const currentHead = gitText(repository, ['rev-parse', '--verify', 'HEAD']) || null;
304
+ input.beforeCommitGuard?.({ repository, requestedPaths: paths, stagedPaths: staged, currentHead });
305
+
286
306
  const commit = executeRepositoryGitCommand(controllerHome, repository, {
287
307
  args: ['commit', '--only', '-m', message, '--', ...paths],
288
308
  authorization: 'explicit_user_request',