@moretea-labs/forge 1.7.2 → 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 +26 -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 -1613
  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 +369 -99
  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 +219 -22
  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 -6655
  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 +85 -18
  609. package/src/runtime/root/package-runtime-service.ts +60 -97
  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,5 +1,6 @@
1
- import { createHash } from 'crypto';
1
+ import { createHash, randomUUID } from 'crypto';
2
2
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from 'fs';
3
+ import { tmpdir } from 'os';
3
4
  import { basename, dirname, join, normalize, relative, resolve } from 'path';
4
5
  import {
5
6
  capProcessOutput,
@@ -12,6 +13,21 @@ import { runBoundedChild } from '../../runtime/shared/bounded-child-supervisor';
12
13
  import { signalProcessTree } from '../../runtime/shared/process-tree';
13
14
  import { repositoryChildProcessEnvironment, resolveBunExecutable } from '../../runtime/shared/process-environment';
14
15
  import { materializeManagedWorkspaceCheckDependencies } from '../../runtime/execution/managed-workspace';
16
+ import {
17
+ isStructuredCheckFailureEvidence,
18
+ STRUCTURED_CHECK_RESULT_PATH_ENV,
19
+ type StructuredCheckFailureEvidence,
20
+ } from '../../runtime/execution/process-runtime/check-result';
21
+ import { readRuntimeReleaseAuthority } from '../../runtime/root/release-store';
22
+ import { observeRuntimeStatus } from '../../runtime/root/status';
23
+ import { readCurrentRecoveryRelease } from '../../runtime/standalone-recovery/release';
24
+ import { acquireRecoveryOperationLock, recoveryOperationLockPath } from '../../runtime/standalone-recovery/operation-lock';
25
+ import {
26
+ ensureRepositoryCheckStorage,
27
+ resolveRepositoryCheckStorage,
28
+ type RepositoryCheckStorageAuthority,
29
+ type ResolvedRepositoryCheckStorage,
30
+ } from '../../runtime/execution/process-runtime/check-storage';
15
31
 
16
32
  export interface ControllerCheckEffects {
17
33
  /** Repository-relative read scopes. Use [\".\"] for the whole checkout. */
@@ -29,6 +45,7 @@ export interface ControllerCheckEffects {
29
45
  export type ControllerCheckCostClass = 'L0' | 'L1' | 'L2' | 'L3' | 'L4';
30
46
  export type ControllerCheckRiskFloor = 'readonly' | 'low' | 'medium' | 'high' | 'destructive';
31
47
  export type ControllerCheckPhase = 'pre_edit' | 'post_edit' | 'pre_finalize' | 'release';
48
+ export type ControllerCheckExecutionAuthority = 'isolated_controller_home' | 'live_controller_home';
32
49
 
33
50
  export interface ControllerCheckSelection {
34
51
  /** Execution cost only; it does not lower severity or automatically run the check. */
@@ -45,6 +62,8 @@ export interface ControllerCheck {
45
62
  cwd: string;
46
63
  timeoutMs: number;
47
64
  source: 'repo-config' | 'package-script';
65
+ /** Where authority-sensitive child state is resolved. Ordinary checks stay isolated. */
66
+ executionAuthority?: ControllerCheckExecutionAuthority;
48
67
  effects?: ControllerCheckEffects;
49
68
  /** Selection/preflight metadata; intentionally excluded from physical execution identity. */
50
69
  selection?: ControllerCheckSelection;
@@ -89,8 +108,8 @@ interface CheckConfig {
89
108
 
90
109
  const CHECK_CONFIG = '.forge/checks.json';
91
110
  const LEGACY_TRACKED_CHECK_CONFIG = '.repo-harness/checks.json';
92
- const CHECK_EVIDENCE_ROOT = '.ai/harness/checks/controller';
93
- const HEAVY_CHECK_LOCK = '.ai/harness/controller/heavy-check.lock';
111
+ const CHECK_EVIDENCE_SUBDIR = 'controller';
112
+ const HEAVY_CHECK_LOCK = 'heavy-check.lock';
94
113
  const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
95
114
  const MAX_TIMEOUT_MS = 30 * 60 * 1000;
96
115
  const SAFE_PACKAGE_SCRIPT = /^(test(?::|$)|check(?::|$)|lint(?::|$)|typecheck(?::|$)|format:check$)/;
@@ -253,6 +272,14 @@ export function controllerCheckSelection(check: Pick<ControllerCheck, 'id' | 'se
253
272
 
254
273
  function inferredPackageCheckEffects(name: string): ControllerCheckEffects | undefined {
255
274
  const normalized = name.trim().toLowerCase();
275
+ if (normalized === 'check:stable-baseline') {
276
+ return { reads: ['.'], hostServices: ['canonical-runtime', 'forge-recovery'] };
277
+ }
278
+ if (normalized === 'check:task' || normalized === 'check:main') {
279
+ // Governed gates reject any candidate workspace mutation after every step
280
+ // and persist only content-bound receipts/caches outside repository source.
281
+ return { reads: ['.'], cache: 'write', temp: 'isolated', git: 'read' };
282
+ }
256
283
  const staticAnalysis = /(?:^|:)(?:type|typecheck|lint|format:check|runtime-architecture|mcp-compatibility|forge-runtime)$/.test(normalized);
257
284
  if (staticAnalysis) return { reads: ['.'], cache: 'write' };
258
285
  const isolatedReadOnlyCheck = /(?:^|:)(?:quality-harness|evaluation-framework|background-check-overlap|typescript-navigation|check-scheduling|bootstrap-files)$/.test(normalized);
@@ -312,6 +339,9 @@ function packageChecks(repoRoot: string): ControllerCheck[] {
312
339
  cwd: '.',
313
340
  timeoutMs: packageScriptTimeoutMs(name),
314
341
  source: 'package-script' as const,
342
+ executionAuthority: name.trim().toLowerCase() === 'check:stable-baseline'
343
+ ? 'live_controller_home' as const
344
+ : 'isolated_controller_home' as const,
315
345
  effects: inferredPackageCheckEffects(name),
316
346
  selection: inferredCheckSelection(name),
317
347
  }];
@@ -340,8 +370,9 @@ export interface ControllerCheckResult {
340
370
  validatedRevision?: string;
341
371
  /** Original process execution timestamp; retained when a result is served from cache. */
342
372
  originalExecutedAt?: string;
343
- /** A non-zero check result is acceptance failure; runtime/process defects are infrastructure. */
373
+ /** Non-zero repository failures are acceptance failures unless bounded infrastructure evidence proves otherwise. */
344
374
  failureClass?: 'acceptance_failure' | 'infrastructure_failure';
375
+ failureEvidence?: StructuredCheckFailureEvidence;
345
376
  }
346
377
 
347
378
  export interface ControllerCheckEvidence {
@@ -365,54 +396,190 @@ export interface ControllerCheckEvidence {
365
396
  validatedRevision?: string;
366
397
  originalExecutedAt?: string;
367
398
  failureClass?: 'acceptance_failure' | 'infrastructure_failure';
399
+ failureEvidence?: StructuredCheckFailureEvidence;
400
+ }
401
+
402
+ const STRONG_TRANSPORT_FAILURE_PATTERNS = [
403
+ /\bjava\.net\.(?:SocketTimeoutException|UnknownHostException|ConnectException|NoRouteToHostException)\b/i,
404
+ /\b(?:EAI_AGAIN|ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENETUNREACH|EHOSTUNREACH)\b/i,
405
+ /\bCould not resolve host\b/i,
406
+ /\b(?:connection|connect|read) timed out\b/i,
407
+ /\bNetwork is unreachable\b/i,
408
+ /\b(?:SSLHandshakeException|TLS handshake (?:failed|timeout|timed out)|CERTIFICATE_VERIFY_FAILED)\b/i,
409
+ ] as const;
410
+
411
+ function classifyControllerCheckFailure(input: {
412
+ ok: boolean;
413
+ stale?: boolean;
414
+ timedOut: boolean;
415
+ runtimeFailure: boolean;
416
+ stdout: string;
417
+ stderr: string;
418
+ }): 'acceptance_failure' | 'infrastructure_failure' | undefined {
419
+ if (input.ok && !input.stale) return undefined;
420
+ if (input.stale || input.timedOut || input.runtimeFailure) return 'infrastructure_failure';
421
+ const output = `${input.stdout}\n${input.stderr}`;
422
+ if (STRONG_TRANSPORT_FAILURE_PATTERNS.some((pattern) => pattern.test(output))) return 'infrastructure_failure';
423
+ return 'acceptance_failure';
424
+ }
425
+
426
+ const GOVERNED_TEST_GATES = new Map<string, string>([
427
+ ['package:test', 'affected'],
428
+ ['package:test:core', 'core'],
429
+ ['package:test:integration', 'integration'],
430
+ ['package:test:infrastructure', 'infrastructure'],
431
+ ['package:test:fault', 'fault'],
432
+ ['package:test:full', 'full'],
433
+ ['package:test:bun', 'full'],
434
+ ]);
435
+ const MAX_STRUCTURED_CHECK_RESULT_BYTES = 64 * 1024;
436
+
437
+ interface StructuredCheckObservation {
438
+ evidence?: StructuredCheckFailureEvidence;
439
+ error?: string;
440
+ }
441
+
442
+ function structuredCheckResultPath(check: ControllerCheck): string | undefined {
443
+ if (!GOVERNED_TEST_GATES.has(check.id)) return undefined;
444
+ return join(tmpdir(), `forge-check-result-${process.pid}-${randomUUID()}.json`);
445
+ }
446
+
447
+ function readStructuredCheckObservation(check: ControllerCheck, path: string | undefined): StructuredCheckObservation {
448
+ const expectedGate = GOVERNED_TEST_GATES.get(check.id);
449
+ if (!expectedGate) return {};
450
+ if (!path || !existsSync(path)) return { error: 'CHECK_STRUCTURED_RESULT_MISSING' };
451
+ try {
452
+ const bytes = readFileSync(path);
453
+ if (bytes.byteLength > MAX_STRUCTURED_CHECK_RESULT_BYTES) return { error: 'CHECK_STRUCTURED_RESULT_TOO_LARGE' };
454
+ const value = JSON.parse(bytes.toString('utf8')) as unknown;
455
+ if (!isStructuredCheckFailureEvidence(value) || value.gate !== expectedGate) return { error: 'CHECK_STRUCTURED_RESULT_INVALID' };
456
+ return { evidence: value };
457
+ } catch {
458
+ return { error: 'CHECK_STRUCTURED_RESULT_INVALID' };
459
+ }
460
+ }
461
+
462
+ function consumeStructuredCheckObservation(check: ControllerCheck, path: string | undefined): StructuredCheckObservation {
463
+ try {
464
+ return readStructuredCheckObservation(check, path);
465
+ } finally {
466
+ if (path) rmSync(path, { force: true });
467
+ }
468
+ }
469
+
470
+ function applyStructuredCheckObservation(processOk: boolean, observation: StructuredCheckObservation): {
471
+ ok: boolean;
472
+ failureClass?: 'acceptance_failure' | 'infrastructure_failure';
473
+ failureEvidence?: StructuredCheckFailureEvidence;
474
+ error?: string;
475
+ } {
476
+ if (!observation.error && !observation.evidence) return { ok: processOk };
477
+ if (observation.error) return { ok: false, failureClass: 'infrastructure_failure', error: observation.error };
478
+ const evidence = observation.evidence!;
479
+ const reportOk = evidence.status === 'passed';
480
+ if (reportOk !== processOk) {
481
+ return { ok: false, failureClass: 'infrastructure_failure', failureEvidence: evidence, error: 'CHECK_STRUCTURED_RESULT_STATUS_MISMATCH' };
482
+ }
483
+ if (reportOk) return { ok: true, failureEvidence: evidence };
484
+ const sourceOnly = evidence.failureClasses.length > 0 && evidence.failureClasses.every((entry) => entry === 'source');
485
+ return {
486
+ ok: false,
487
+ failureClass: sourceOnly ? 'acceptance_failure' : 'infrastructure_failure',
488
+ failureEvidence: evidence,
489
+ };
368
490
  }
369
491
 
370
492
  function artifactSlug(id: string): string {
371
493
  return id.toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'check';
372
494
  }
373
495
 
374
- function evidencePath(repoRoot: string, id: string): string {
375
- return join(repoRoot, CHECK_EVIDENCE_ROOT, `latest-${artifactSlug(id)}.json`);
496
+ function evidencePath(storage: ResolvedRepositoryCheckStorage, id: string): string {
497
+ return join(storage.physicalRoot, CHECK_EVIDENCE_SUBDIR, `latest-${artifactSlug(id)}.json`);
498
+ }
499
+
500
+ function historicalEvidencePath(storage: ResolvedRepositoryCheckStorage, id: string, cacheKey: string): string {
501
+ return join(storage.physicalRoot, CHECK_EVIDENCE_SUBDIR, artifactSlug(id), `${cacheKey}.json`);
376
502
  }
377
503
 
378
- function historicalEvidencePath(repoRoot: string, id: string, cacheKey: string): string {
379
- return join(repoRoot, CHECK_EVIDENCE_ROOT, artifactSlug(id), `${cacheKey}.json`);
504
+ function logicalEvidenceArtifactPath(id: string): string {
505
+ return `controller-home://checks/controller/latest-${artifactSlug(id)}.json`;
380
506
  }
381
507
 
382
508
  const CHECK_REVISION_EXCLUDES = [
383
- '.ai/harness/jobs/**',
384
- '.ai/harness/local-jobs/**',
385
- '.ai/harness/checks/controller/**',
386
- '.ai/harness/edit-sessions/**',
387
- '.ai/harness/worktrees/**',
388
- '.ai/harness/controller/**',
389
- '.ai/harness/artifacts/**',
390
- '.ai/harness/local-bridge/**',
391
- '.ai/harness/ephemeral-issues/**',
509
+ // `.ai/harness` is Forge runtime/check state, not repository source. Checks
510
+ // are allowed to materialize artifacts such as design-system audit reports
511
+ // there; hashing those outputs would make a stable check invalidate itself.
512
+ '.ai/harness/**',
392
513
  ];
393
514
 
394
515
  function checkRevisionPathspecs(): string[] {
395
516
  return ['.', ...CHECK_REVISION_EXCLUDES.map((path) => `:(exclude)${path}`)];
396
517
  }
397
518
 
398
- export function currentControllerCheckRevision(repoRoot: string): string {
519
+ interface ControllerCheckContentObservation {
520
+ revision: string;
521
+ fileDigests?: Map<string, string>;
522
+ listingAvailable: boolean;
523
+ }
524
+
525
+ function observeControllerCheckContent(
526
+ repoRoot: string,
527
+ options: { captureFileDigests?: boolean } = {},
528
+ ): ControllerCheckContentObservation {
399
529
  const files = runProcess('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z', '--', ...checkRevisionPathspecs()], {
400
530
  cwd: repoRoot,
401
531
  timeoutMs: 10_000,
402
532
  maxOutputBytes: 8 * 1024 * 1024,
403
533
  });
404
534
  const revision = createHash('sha256').update('controller-check-content-v2\n');
405
- if (!files.ok) return revision.update(`git-error:${files.error || files.stderr}`).digest('hex').slice(0, 24);
535
+ const fileDigests = options.captureFileDigests ? new Map<string, string>() : undefined;
536
+ if (!files.ok) {
537
+ return {
538
+ revision: revision.update(`git-error:${files.error || files.stderr}`).digest('hex').slice(0, 24),
539
+ ...(fileDigests ? { fileDigests } : {}),
540
+ listingAvailable: false,
541
+ };
542
+ }
406
543
  for (const relativePath of files.stdout.split('\0').filter(Boolean).sort()) {
407
544
  if (relativePath.startsWith('tasks/') || relativePath.startsWith('plans/')) continue;
408
545
  revision.update(`${relativePath}\0`);
409
546
  try {
410
- revision.update(readFileSync(resolve(repoRoot, relativePath)));
547
+ const content = readFileSync(resolve(repoRoot, relativePath));
548
+ revision.update(content);
549
+ fileDigests?.set(relativePath, createHash('sha256').update(content).digest('hex'));
411
550
  } catch (_error) {
412
551
  revision.update('missing');
552
+ fileDigests?.set(relativePath, 'missing');
413
553
  }
414
554
  }
415
- return revision.digest('hex').slice(0, 24);
555
+ return {
556
+ revision: revision.digest('hex').slice(0, 24),
557
+ ...(fileDigests ? { fileDigests } : {}),
558
+ listingAvailable: true,
559
+ };
560
+ }
561
+
562
+ export function currentControllerCheckRevision(repoRoot: string): string {
563
+ return observeControllerCheckContent(repoRoot).revision;
564
+ }
565
+
566
+ function controllerCheckWriteScopeContainsPath(scope: string, relativePath: string): boolean {
567
+ return scope === '.' || relativePath === scope || relativePath.startsWith(`${scope}/`);
568
+ }
569
+
570
+ function controllerCheckInputIntegrityChanged(
571
+ check: ControllerCheck,
572
+ input: ControllerCheckContentObservation,
573
+ completed: ControllerCheckContentObservation,
574
+ ): boolean {
575
+ if (!input.listingAvailable || !completed.listingAvailable || !input.fileDigests || !completed.fileDigests) return true;
576
+ const writeScopes = check.effects?.writes ?? [];
577
+ const observedPaths = new Set([...input.fileDigests.keys(), ...completed.fileDigests.keys()]);
578
+ for (const relativePath of observedPaths) {
579
+ if (writeScopes.some((scope) => controllerCheckWriteScopeContainsPath(scope, relativePath))) continue;
580
+ if (input.fileDigests.get(relativePath) !== completed.fileDigests.get(relativePath)) return true;
581
+ }
582
+ return false;
416
583
  }
417
584
 
418
585
  function checkDefinitionDigest(check: ControllerCheck): string {
@@ -423,6 +590,7 @@ function checkDefinitionDigest(check: ControllerCheck): string {
423
590
  cwd: check.cwd,
424
591
  timeoutMs: check.timeoutMs,
425
592
  source: check.source,
593
+ executionAuthority: check.executionAuthority ?? 'isolated_controller_home',
426
594
  effects: check.effects,
427
595
  })).digest('hex');
428
596
  }
@@ -450,6 +618,7 @@ function validateControllerCheckSnapshot(repoRoot: string, snapshot: ControllerC
450
618
  cwd: normalizeCwd(repoRoot, snapshot.cwd),
451
619
  timeoutMs: boundedTimeout(snapshot.timeoutMs),
452
620
  source: snapshot.source,
621
+ executionAuthority: snapshot.executionAuthority ?? 'isolated_controller_home',
453
622
  effects: normalizeCheckEffects(repoRoot, snapshot.effects),
454
623
  selection: normalizeCheckSelection(snapshot.selection, snapshot.id),
455
624
  };
@@ -457,7 +626,72 @@ function validateControllerCheckSnapshot(repoRoot: string, snapshot: ControllerC
457
626
 
458
627
  const CONTROLLER_CHECK_EXECUTION_IDENTITY_VERSION = 'controller-check-execution-v2';
459
628
 
460
- function checkEnvironmentFingerprint(check: ControllerCheck): string {
629
+ /**
630
+ * Bounded live-state identity for certification checks. This is read-only
631
+ * evidence, not lifecycle authority. It includes current release authority and
632
+ * observed Runtime liveness so a dead/replaced Runtime cannot reuse a prior
633
+ * green Stable Baseline receipt merely because its status file remained.
634
+ */
635
+ export function controllerCheckLiveExecutionStateFingerprint(controllerHome: string, recoveryFenceInstanceId?: string): string {
636
+ const mutationLockPath = recoveryOperationLockPath(controllerHome);
637
+ try {
638
+ const raw = readFileSync(mutationLockPath, 'utf8');
639
+ if (recoveryFenceInstanceId?.trim()) {
640
+ try {
641
+ const owner = JSON.parse(raw) as { instanceId?: unknown };
642
+ if (owner?.instanceId === recoveryFenceInstanceId.trim()) {
643
+ // This check owns the real Recovery mutation fence; its own lock is not drift.
644
+ } else {
645
+ throw new Error('LIVE_CHECK_RECOVERY_MUTATION_IN_PROGRESS');
646
+ }
647
+ } catch (error) {
648
+ if (error instanceof Error && error.message === 'LIVE_CHECK_RECOVERY_MUTATION_IN_PROGRESS') throw error;
649
+ throw new Error('LIVE_CHECK_RECOVERY_MUTATION_STATE_UNREADABLE');
650
+ }
651
+ } else {
652
+ throw new Error('LIVE_CHECK_RECOVERY_MUTATION_IN_PROGRESS');
653
+ }
654
+ } catch (error) {
655
+ if (error instanceof Error && (
656
+ error.message === 'LIVE_CHECK_RECOVERY_MUTATION_IN_PROGRESS'
657
+ || error.message === 'LIVE_CHECK_RECOVERY_MUTATION_STATE_UNREADABLE'
658
+ )) throw error;
659
+ if ((error as NodeJS.ErrnoException)?.code !== 'ENOENT') {
660
+ throw new Error('LIVE_CHECK_RECOVERY_MUTATION_STATE_UNREADABLE');
661
+ }
662
+ }
663
+ const release = readRuntimeReleaseAuthority(controllerHome);
664
+ const recoveryRelease = readCurrentRecoveryRelease(controllerHome);
665
+ const runtime = observeRuntimeStatus(controllerHome);
666
+ return createHash('sha256')
667
+ .update(JSON.stringify({
668
+ release: release ? {
669
+ revision: release.revision,
670
+ releaseId: release.active.releaseId,
671
+ artifactIdentity: release.active.artifactIdentity,
672
+ manifestSha256: release.active.manifestSha256,
673
+ } : null,
674
+ recoveryRelease: recoveryRelease ? {
675
+ releaseRevision: recoveryRelease.releaseRevision,
676
+ sourceCommit: recoveryRelease.sourceCommit,
677
+ manifestSha256: recoveryRelease.manifestSha256,
678
+ } : null,
679
+ runtime: {
680
+ running: runtime.running,
681
+ ready: runtime.ready,
682
+ stale: runtime.stale,
683
+ reasonCodes: [...runtime.reasonCodes].sort(),
684
+ runtimeInstanceId: runtime.snapshot?.runtimeInstanceId ?? null,
685
+ pid: runtime.snapshot?.pid ?? null,
686
+ releaseId: runtime.snapshot?.releaseId ?? null,
687
+ artifactIdentity: runtime.snapshot?.artifactIdentity ?? null,
688
+ },
689
+ }))
690
+ .digest('hex')
691
+ .slice(0, 24);
692
+ }
693
+
694
+ export function controllerCheckEnvironmentFingerprint(check: ControllerCheck, executionStateFingerprint?: string): string {
461
695
  const env = repositoryChildProcessEnvironment(process.env);
462
696
  return createHash('sha256')
463
697
  .update(JSON.stringify({
@@ -470,6 +704,8 @@ function checkEnvironmentFingerprint(check: ControllerCheck): string {
470
704
  // for the parent and forge-check-runner for the sidecar; process.execPath
471
705
  // therefore differs even though both execute the exact same check/toolchain.
472
706
  commandExecutable: check.command[0],
707
+ executionAuthority: check.executionAuthority ?? 'isolated_controller_home',
708
+ executionStateFingerprint: executionStateFingerprint?.trim() || '',
473
709
  path: env.PATH ?? '',
474
710
  home: env.HOME ?? env.USERPROFILE ?? '',
475
711
  bunInstall: env.BUN_INSTALL ?? '',
@@ -514,7 +750,7 @@ function buildCheckCacheKey(
514
750
  timeoutMs: number,
515
751
  revision: string,
516
752
  definitionDigest = checkDefinitionDigest(check),
517
- environmentFingerprint = checkEnvironmentFingerprint(check),
753
+ environmentFingerprint = controllerCheckEnvironmentFingerprint(check),
518
754
  ): string {
519
755
  return createHash('sha256')
520
756
  .update(JSON.stringify({
@@ -531,11 +767,13 @@ function buildCheckCacheKey(
531
767
  .slice(0, 24);
532
768
  }
533
769
 
534
- export function controllerCheckExecutionIdentity(
770
+ function controllerCheckExecutionIdentityForRevision(
535
771
  repoRoot: string,
536
772
  id: string,
773
+ revision: string,
537
774
  requestedTimeoutMs?: number,
538
775
  snapshot?: ControllerCheckSnapshot,
776
+ executionStateFingerprint?: string,
539
777
  ): ControllerCheckExecutionIdentity {
540
778
  const check = snapshot
541
779
  ? validateControllerCheckSnapshot(repoRoot, snapshot)
@@ -544,9 +782,8 @@ export function controllerCheckExecutionIdentity(
544
782
  const timeoutMs = requestedTimeoutMs === undefined
545
783
  ? check.timeoutMs
546
784
  : Math.min(check.timeoutMs, boundedTimeout(requestedTimeoutMs));
547
- const revision = currentControllerCheckRevision(repoRoot);
548
785
  const definitionDigest = checkDefinitionDigest(check);
549
- const environmentFingerprint = checkEnvironmentFingerprint(check);
786
+ const environmentFingerprint = controllerCheckEnvironmentFingerprint(check, executionStateFingerprint);
550
787
  const checkoutClean = checkWorkspaceClean(repoRoot);
551
788
  const crossCheckoutReusable = checkoutClean
552
789
  && checkEffectsAllowCrossCheckoutReuse(check)
@@ -565,8 +802,25 @@ export function controllerCheckExecutionIdentity(
565
802
  };
566
803
  }
567
804
 
568
- function persistCheckEvidence(
805
+ export function controllerCheckExecutionIdentity(
569
806
  repoRoot: string,
807
+ id: string,
808
+ requestedTimeoutMs?: number,
809
+ snapshot?: ControllerCheckSnapshot,
810
+ executionStateFingerprint?: string,
811
+ ): ControllerCheckExecutionIdentity {
812
+ return controllerCheckExecutionIdentityForRevision(
813
+ repoRoot,
814
+ id,
815
+ currentControllerCheckRevision(repoRoot),
816
+ requestedTimeoutMs,
817
+ snapshot,
818
+ executionStateFingerprint,
819
+ );
820
+ }
821
+
822
+ function persistCheckEvidence(
823
+ storage: ResolvedRepositoryCheckStorage,
570
824
  result: Omit<ControllerCheckResult, 'artifactPath'>,
571
825
  meta: {
572
826
  revision?: string;
@@ -577,7 +831,7 @@ function persistCheckEvidence(
577
831
  validatedRevision?: string;
578
832
  } = {},
579
833
  ): string {
580
- const path = evidencePath(repoRoot, result.check.id);
834
+ const path = evidencePath(storage, result.check.id);
581
835
  mkdirSync(dirname(path), { recursive: true });
582
836
  const evidence: ControllerCheckEvidence = {
583
837
  schemaVersion: 2,
@@ -600,30 +854,40 @@ function persistCheckEvidence(
600
854
  validatedRevision: meta.validatedRevision,
601
855
  originalExecutedAt: result.originalExecutedAt ?? result.executedAt,
602
856
  failureClass: result.failureClass,
857
+ failureEvidence: result.failureEvidence,
603
858
  };
604
859
  const serialized = `${JSON.stringify(evidence, null, 2)}\n`;
605
860
  atomicWriteFileSync(path, serialized);
606
861
  if (meta.cacheKey) {
607
- const historicalPath = historicalEvidencePath(repoRoot, result.check.id, meta.cacheKey);
862
+ const historicalPath = historicalEvidencePath(storage, result.check.id, meta.cacheKey);
608
863
  mkdirSync(dirname(historicalPath), { recursive: true });
609
864
  atomicWriteFileSync(historicalPath, serialized);
610
865
  }
611
- return relative(repoRoot, path).replace(/\\/g, '/');
866
+ return logicalEvidenceArtifactPath(result.check.id);
612
867
  }
613
868
 
614
- export function readLatestControllerCheckEvidence(repoRoot: string, id: string): ControllerCheckEvidence | undefined {
615
- const path = evidencePath(repoRoot, id);
869
+ export function readLatestControllerCheckEvidence(
870
+ repoRoot: string,
871
+ id: string,
872
+ storageAuthority?: RepositoryCheckStorageAuthority | ResolvedRepositoryCheckStorage,
873
+ ): ControllerCheckEvidence | undefined {
874
+ const storage = storageAuthority && 'physicalRoot' in storageAuthority
875
+ ? storageAuthority as ResolvedRepositoryCheckStorage
876
+ : resolveRepositoryCheckStorage(repoRoot, storageAuthority as RepositoryCheckStorageAuthority | undefined);
877
+ const path = evidencePath(storage, id);
616
878
  if (!existsSync(path)) return undefined;
617
879
  try {
618
880
  const value = JSON.parse(readFileSync(path, 'utf-8')) as ControllerCheckEvidence;
619
- return value.schemaVersion === 2 && value.checkId === id ? value : undefined;
881
+ if (value.schemaVersion !== 2 || value.checkId !== id) return undefined;
882
+ if (value.failureEvidence !== undefined && !isStructuredCheckFailureEvidence(value.failureEvidence)) return undefined;
883
+ return value;
620
884
  } catch (_error) {
621
885
  return undefined;
622
886
  }
623
887
  }
624
888
 
625
- function readControllerCheckEvidenceByKey(repoRoot: string, id: string, cacheKey: string): ControllerCheckEvidence | undefined {
626
- const path = historicalEvidencePath(repoRoot, id, cacheKey);
889
+ function readControllerCheckEvidenceByKey(storage: ResolvedRepositoryCheckStorage, id: string, cacheKey: string): ControllerCheckEvidence | undefined {
890
+ const path = historicalEvidencePath(storage, id, cacheKey);
627
891
  if (!existsSync(path)) return undefined;
628
892
  try {
629
893
  const value = JSON.parse(readFileSync(path, 'utf-8')) as ControllerCheckEvidence;
@@ -638,16 +902,27 @@ function prepareControllerCheckDependencies(repoRoot: string, check: ControllerC
638
902
  materializeManagedWorkspaceCheckDependencies(repoRoot);
639
903
  }
640
904
 
641
- export function runControllerCheck(repoRoot: string, id: string, requestedTimeoutMs?: number, snapshot?: ControllerCheckSnapshot): ControllerCheckResult {
905
+ export function runControllerCheck(
906
+ repoRoot: string,
907
+ id: string,
908
+ requestedTimeoutMs?: number,
909
+ snapshot?: ControllerCheckSnapshot,
910
+ storageAuthority?: RepositoryCheckStorageAuthority,
911
+ ): ControllerCheckResult {
642
912
  const check = snapshot ? validateControllerCheckSnapshot(repoRoot, snapshot) : listControllerChecks(repoRoot).find((entry) => entry.id === id);
643
913
  if (!check || check.id !== id) throw new Error(`check not found: ${id}`);
914
+ if (check.executionAuthority === 'live_controller_home') {
915
+ throw new Error('LIVE_CHECK_DURABLE_EXECUTION_REQUIRED');
916
+ }
917
+ const storage = ensureRepositoryCheckStorage(repoRoot, storageAuthority);
644
918
  prepareControllerCheckDependencies(repoRoot, check);
645
- const identity = controllerCheckExecutionIdentity(repoRoot, id, requestedTimeoutMs, snapshot);
919
+ const inputIntegrity = observeControllerCheckContent(repoRoot, { captureFileDigests: true });
920
+ const identity = controllerCheckExecutionIdentityForRevision(repoRoot, id, inputIntegrity.revision, requestedTimeoutMs, snapshot);
646
921
  const timeoutMs = identity.timeoutMs;
647
922
  const revision = identity.revision;
648
923
  const cacheKey = identity.cacheKey;
649
- const cached = readControllerCheckEvidenceByKey(repoRoot, id, cacheKey)
650
- ?? readLatestControllerCheckEvidence(repoRoot, id);
924
+ const cached = readControllerCheckEvidenceByKey(storage, id, cacheKey)
925
+ ?? readLatestControllerCheckEvidence(repoRoot, id, storage);
651
926
  if (cached?.cacheKey === cacheKey && cached.ok) {
652
927
  return {
653
928
  check,
@@ -658,20 +933,23 @@ export function runControllerCheck(repoRoot: string, id: string, requestedTimeou
658
933
  stderr: cached.stderr,
659
934
  command: cached.command,
660
935
  executedAt: cached.executedAt,
661
- artifactPath: relative(repoRoot, evidencePath(repoRoot, id)).replace(/\\/g, '/'),
936
+ artifactPath: logicalEvidenceArtifactPath(id),
662
937
  cacheHit: true,
663
938
  validatedRevision: cached.validatedRevision ?? cached.completedRevision ?? cached.revision ?? revision,
664
939
  originalExecutedAt: cached.originalExecutedAt ?? cached.executedAt,
665
940
  failureClass: cached.failureClass,
941
+ failureEvidence: cached.failureEvidence,
666
942
  };
667
943
  }
668
944
  const heavy = controllerCheckConcurrencyClass(id) === 'heavy';
669
- const lease = heavy ? tryAcquireHeavyCheckLock(repoRoot, id) : undefined;
945
+ const lease = heavy ? tryAcquireHeavyCheckLock(storage, id) : undefined;
670
946
  if (heavy && !lease) throw new Error(`heavy check already running for repository: ${id}`);
947
+ const structuredResultPath = structuredCheckResultPath(check);
671
948
  let result: ProcessRunResult;
672
949
  try {
673
950
  const bridgeRuntime = resolveSyncSupervisorBridgeRuntime();
674
951
  const childEnvironment = repositoryChildProcessEnvironment();
952
+ if (structuredResultPath) childEnvironment[STRUCTURED_CHECK_RESULT_PATH_ENV] = structuredResultPath;
675
953
  delete childEnvironment[CHECK_BRIDGE_RUNTIME_ENV];
676
954
  childEnvironment.FORGE_SUPERVISED_REQUEST = Buffer.from(JSON.stringify({
677
955
  command: check.command[0],
@@ -736,17 +1014,23 @@ export function runControllerCheck(repoRoot: string, id: string, requestedTimeou
736
1014
  } finally {
737
1015
  lease?.release();
738
1016
  }
739
- const completedRevision = currentControllerCheckRevision(repoRoot);
740
- const stale = completedRevision !== revision;
1017
+ const structuredObservation = consumeStructuredCheckObservation(check, structuredResultPath);
1018
+ const structuredDecision = applyStructuredCheckObservation(result.ok, structuredObservation);
1019
+ const completedContent = observeControllerCheckContent(repoRoot, { captureFileDigests: true });
1020
+ const completedRevision = completedContent.revision;
1021
+ const stale = controllerCheckInputIntegrityChanged(check, inputIntegrity, completedContent);
741
1022
  const executedAt = new Date().toISOString();
1023
+ const effectiveOk = structuredDecision.ok && !stale;
1024
+ const hardInfrastructureFailure = stale || result.timedOut || Boolean(result.error) || Boolean(result.signal);
742
1025
  const withoutPath = {
743
1026
  check,
744
- ok: result.ok && !stale,
745
- status: stale ? 1 : result.status,
1027
+ ok: effectiveOk,
1028
+ status: stale || (!structuredDecision.ok && result.status === 0) ? 1 : result.status,
746
1029
  timedOut: result.timedOut,
747
1030
  stdout: result.stdout,
748
1031
  stderr: [
749
1032
  result.stderr || result.error,
1033
+ structuredDecision.error,
750
1034
  stale ? 'repository revision changed while the check was running; evidence is stale and the check must be rerun' : '',
751
1035
  ].filter(Boolean).join('\n'),
752
1036
  command: result.command,
@@ -754,13 +1038,21 @@ export function runControllerCheck(repoRoot: string, id: string, requestedTimeou
754
1038
  cacheHit: false,
755
1039
  validatedRevision: stale ? completedRevision : revision,
756
1040
  originalExecutedAt: executedAt,
757
- failureClass: stale || result.timedOut || Boolean(result.error) || Boolean(result.signal)
1041
+ failureClass: hardInfrastructureFailure
758
1042
  ? 'infrastructure_failure' as const
759
- : result.ok ? undefined : 'acceptance_failure' as const,
1043
+ : structuredDecision.failureClass ?? classifyControllerCheckFailure({
1044
+ ok: effectiveOk,
1045
+ stale,
1046
+ timedOut: result.timedOut,
1047
+ runtimeFailure: Boolean(result.error) || Boolean(result.signal),
1048
+ stdout: result.stdout,
1049
+ stderr: [result.stderr, result.error, structuredDecision.error].filter(Boolean).join('\n'),
1050
+ }),
1051
+ failureEvidence: structuredDecision.failureEvidence,
760
1052
  };
761
1053
  return {
762
1054
  ...withoutPath,
763
- artifactPath: persistCheckEvidence(repoRoot, withoutPath, {
1055
+ artifactPath: persistCheckEvidence(storage, withoutPath, {
764
1056
  revision,
765
1057
  completedRevision,
766
1058
  stale,
@@ -773,6 +1065,11 @@ export function runControllerCheck(repoRoot: string, id: string, requestedTimeou
773
1065
 
774
1066
  export interface AsyncControllerCheckOptions {
775
1067
  snapshot?: ControllerCheckSnapshot;
1068
+ /** Exact live-state identity supplied by the persisted Work verifier for live certification checks. */
1069
+ executionStateFingerprint?: string;
1070
+ /** Explicit installed Controller Home; never inferred from ambient child environment. */
1071
+ liveControllerHome?: string;
1072
+ storageAuthority?: RepositoryCheckStorageAuthority;
776
1073
  /** Explicit isolated authority for Candidate verification child processes. */
777
1074
  isolatedControllerHome?: string;
778
1075
  requestedTimeoutMs?: number;
@@ -823,8 +1120,8 @@ interface HeavyCheckLease {
823
1120
  release(): void;
824
1121
  }
825
1122
 
826
- function tryAcquireHeavyCheckLock(repoRoot: string, checkId: string): HeavyCheckLease | undefined {
827
- const path = join(repoRoot, HEAVY_CHECK_LOCK);
1123
+ function tryAcquireHeavyCheckLock(storage: ResolvedRepositoryCheckStorage, checkId: string): HeavyCheckLease | undefined {
1124
+ const path = join(storage.lockRoot, HEAVY_CHECK_LOCK);
828
1125
  mkdirSync(dirname(path), { recursive: true });
829
1126
  const lockId = `${process.pid}:${Date.now()}:${checkId}`;
830
1127
  const record: HeavyCheckLockRecord = {
@@ -851,7 +1148,7 @@ function tryAcquireHeavyCheckLock(repoRoot: string, checkId: string): HeavyCheck
851
1148
  const orphaned = !isProcessAlive(existing?.controllerPid ?? existing?.pid) && !isProcessAlive(existing?.childPid);
852
1149
  if (!existing || orphaned) {
853
1150
  rmSync(path, { force: true });
854
- return tryAcquireHeavyCheckLock(repoRoot, checkId);
1151
+ return tryAcquireHeavyCheckLock(storage, checkId);
855
1152
  }
856
1153
  return undefined;
857
1154
  }
@@ -873,10 +1170,10 @@ function tryAcquireHeavyCheckLock(repoRoot: string, checkId: string): HeavyCheck
873
1170
  };
874
1171
  }
875
1172
 
876
- async function acquireHeavyCheckLock(repoRoot: string, checkId: string): Promise<HeavyCheckLease> {
1173
+ async function acquireHeavyCheckLock(storage: ResolvedRepositoryCheckStorage, checkId: string): Promise<HeavyCheckLease> {
877
1174
  const deadline = Date.now() + MAX_TIMEOUT_MS * 2;
878
1175
  while (true) {
879
- const lease = tryAcquireHeavyCheckLock(repoRoot, checkId);
1176
+ const lease = tryAcquireHeavyCheckLock(storage, checkId);
880
1177
  if (lease) return lease;
881
1178
  if (Date.now() >= deadline) throw new Error(`timed out waiting for repository heavy-check lock before ${checkId}`);
882
1179
  await new Promise((resolvePromise) => setTimeout(resolvePromise, 50));
@@ -913,13 +1210,19 @@ async function executeControllerCheckAsync(
913
1210
  repoRoot: string,
914
1211
  check: ControllerCheck,
915
1212
  timeoutMs: number,
1213
+ storage: ResolvedRepositoryCheckStorage,
916
1214
  onSpawn?: (pid: number) => void,
917
1215
  isolatedControllerHome?: string,
1216
+ liveControllerHome?: string,
918
1217
  ): Promise<ControllerCheckResult> {
919
1218
  const maxOutputBytes = 256 * 1024;
920
1219
  const command = [check.command[0], ...check.command.slice(1)];
921
1220
  const childEnvironment = repositoryChildProcessEnvironment();
922
- if (isolatedControllerHome?.trim()) childEnvironment.FORGE_CONTROLLER_HOME = resolve(isolatedControllerHome);
1221
+ const structuredResultPath = structuredCheckResultPath(check);
1222
+ if (structuredResultPath) childEnvironment[STRUCTURED_CHECK_RESULT_PATH_ENV] = structuredResultPath;
1223
+ if (isolatedControllerHome?.trim() && liveControllerHome?.trim()) throw new Error('CHECK_CONTROLLER_HOME_AUTHORITY_CONFLICT');
1224
+ if (liveControllerHome?.trim()) childEnvironment.FORGE_CONTROLLER_HOME = resolve(liveControllerHome);
1225
+ else if (isolatedControllerHome?.trim()) childEnvironment.FORGE_CONTROLLER_HOME = resolve(isolatedControllerHome);
923
1226
  const supervised = await runBoundedChild(check.command[0], check.command.slice(1), {
924
1227
  cwd: resolve(repoRoot, check.cwd),
925
1228
  env: childEnvironment,
@@ -935,19 +1238,34 @@ async function executeControllerCheckAsync(
935
1238
  const timeoutMessage = supervised.timedOut
936
1239
  ? `process timed out after ${timeoutMs}ms: ${command.join(' ')}`
937
1240
  : '';
1241
+ const processOk = supervised.status === 0 && !supervised.failureCode;
1242
+ const structuredObservation = consumeStructuredCheckObservation(check, structuredResultPath);
1243
+ const structuredDecision = applyStructuredCheckObservation(processOk, structuredObservation);
1244
+ const hardInfrastructureFailure = supervised.timedOut
1245
+ || Boolean(supervised.failureCode)
1246
+ || Boolean(supervised.error)
1247
+ || Boolean(supervised.signal);
938
1248
  const result = {
939
- ok: supervised.status === 0 && !supervised.failureCode,
940
- status: supervised.status,
1249
+ ok: structuredDecision.ok,
1250
+ status: !structuredDecision.ok && supervised.status === 0 ? 1 : supervised.status,
941
1251
  timedOut: supervised.timedOut,
942
1252
  stdout: capProcessOutput(redactProcessOutput(supervised.stdout), maxOutputBytes),
943
1253
  stderr: capProcessOutput(redactProcessOutput([
944
1254
  supervised.stderr,
945
1255
  timeoutMessage || supervised.error || '',
946
1256
  processTreeError,
1257
+ structuredDecision.error,
947
1258
  ].filter(Boolean).join('\n')), maxOutputBytes),
948
- failureClass: supervised.failureCode
1259
+ failureClass: hardInfrastructureFailure
949
1260
  ? 'infrastructure_failure' as const
950
- : supervised.status === 0 ? undefined : 'acceptance_failure' as const,
1261
+ : structuredDecision.failureClass ?? classifyControllerCheckFailure({
1262
+ ok: structuredDecision.ok,
1263
+ timedOut: supervised.timedOut,
1264
+ runtimeFailure: Boolean(supervised.failureCode) || Boolean(supervised.error) || Boolean(supervised.signal),
1265
+ stdout: supervised.stdout,
1266
+ stderr: [supervised.stderr, timeoutMessage, supervised.error, processTreeError, structuredDecision.error].filter(Boolean).join('\n'),
1267
+ }),
1268
+ failureEvidence: structuredDecision.failureEvidence,
951
1269
  };
952
1270
 
953
1271
  const executedAt = new Date().toISOString();
@@ -964,8 +1282,9 @@ async function executeControllerCheckAsync(
964
1282
  validatedRevision: undefined,
965
1283
  originalExecutedAt: executedAt,
966
1284
  failureClass: result.failureClass,
1285
+ failureEvidence: result.failureEvidence,
967
1286
  };
968
- return { ...withoutPath, artifactPath: relative(repoRoot, evidencePath(repoRoot, check.id)).replace(/\\/g, '/') };
1287
+ return { ...withoutPath, artifactPath: logicalEvidenceArtifactPath(check.id) };
969
1288
  }
970
1289
 
971
1290
  export function runControllerCheckAsync(
@@ -980,19 +1299,68 @@ export function runControllerCheckAsync(
980
1299
  return Promise.reject(error);
981
1300
  }
982
1301
  if (!check || check.id !== id) return Promise.reject(new Error(`check not found: ${id}`));
1302
+ const liveCertification = check.executionAuthority === 'live_controller_home';
1303
+ const liveControllerHome = options.liveControllerHome?.trim();
1304
+ const expectedLiveState = options.executionStateFingerprint?.trim();
1305
+ if (liveCertification) {
1306
+ if (!liveControllerHome || !expectedLiveState) {
1307
+ return Promise.reject(new Error('LIVE_CHECK_EXPLICIT_AUTHORITY_REQUIRED'));
1308
+ }
1309
+ } else if (liveControllerHome) {
1310
+ return Promise.reject(new Error('LIVE_CHECK_AUTHORITY_NOT_DECLARED'));
1311
+ }
1312
+ let storage: ResolvedRepositoryCheckStorage;
983
1313
  try {
1314
+ storage = ensureRepositoryCheckStorage(repoRoot, options.storageAuthority);
984
1315
  prepareControllerCheckDependencies(repoRoot, check);
985
1316
  } catch (error) {
986
1317
  return Promise.reject(error);
987
1318
  }
988
- const identity = controllerCheckExecutionIdentity(repoRoot, id, options.requestedTimeoutMs, options.snapshot);
1319
+ const inputIntegrity = observeControllerCheckContent(repoRoot, { captureFileDigests: true });
1320
+ const identity = controllerCheckExecutionIdentityForRevision(
1321
+ repoRoot,
1322
+ id,
1323
+ inputIntegrity.revision,
1324
+ options.requestedTimeoutMs,
1325
+ options.snapshot,
1326
+ options.executionStateFingerprint,
1327
+ );
989
1328
  const timeoutMs = identity.timeoutMs;
990
1329
  const revision = identity.revision;
991
1330
  const cacheKey = identity.cacheKey;
992
- const cached = readControllerCheckEvidenceByKey(repoRoot, id, cacheKey)
993
- ?? readLatestControllerCheckEvidence(repoRoot, id);
1331
+ const cached = readControllerCheckEvidenceByKey(storage, id, cacheKey)
1332
+ ?? readLatestControllerCheckEvidence(repoRoot, id, storage);
1333
+ const withLiveRecoveryFence = async <T>(action: (recoveryFenceInstanceId?: string) => Promise<T> | T): Promise<T> => {
1334
+ if (!liveCertification) return await action(undefined);
1335
+ let attempt;
1336
+ try {
1337
+ attempt = acquireRecoveryOperationLock({
1338
+ controllerHome: liveControllerHome!,
1339
+ action: 'certify_stable_baseline',
1340
+ requestId: `check:${id}:${cacheKey}`,
1341
+ instanceIdPrefix: 'live-certification-',
1342
+ });
1343
+ } catch (error) {
1344
+ const message = error instanceof Error ? error.message : String(error);
1345
+ if (message === 'RECOVERY_OPERATION_LOCK_BUSY') throw new Error('LIVE_CHECK_RECOVERY_MUTATION_IN_PROGRESS');
1346
+ throw new Error(`LIVE_CHECK_RECOVERY_MUTATION_STATE_UNREADABLE: ${message}`);
1347
+ }
1348
+ if (!attempt.acquired) throw new Error('LIVE_CHECK_RECOVERY_MUTATION_IN_PROGRESS');
1349
+ try {
1350
+ const observedLiveState = controllerCheckLiveExecutionStateFingerprint(
1351
+ liveControllerHome!,
1352
+ attempt.handle.record.instanceId,
1353
+ );
1354
+ if (observedLiveState !== expectedLiveState) {
1355
+ throw new Error('LIVE_CHECK_STATE_CHANGED_BEFORE_EXECUTION');
1356
+ }
1357
+ return await action(attempt.handle.record.instanceId);
1358
+ } finally {
1359
+ attempt.handle.close();
1360
+ }
1361
+ };
994
1362
  if (cached?.cacheKey === cacheKey && cached.ok) {
995
- return Promise.resolve({
1363
+ return withLiveRecoveryFence(() => ({
996
1364
  check,
997
1365
  ok: cached.ok,
998
1366
  status: cached.status,
@@ -1001,12 +1369,12 @@ export function runControllerCheckAsync(
1001
1369
  stderr: cached.stderr,
1002
1370
  command: cached.command,
1003
1371
  executedAt: cached.executedAt,
1004
- artifactPath: relative(repoRoot, evidencePath(repoRoot, id)).replace(/\\/g, '/'),
1372
+ artifactPath: logicalEvidenceArtifactPath(id),
1005
1373
  cacheHit: true,
1006
1374
  validatedRevision: cached.validatedRevision ?? cached.completedRevision ?? cached.revision ?? revision,
1007
1375
  originalExecutedAt: cached.originalExecutedAt ?? cached.executedAt,
1008
1376
  failureClass: cached.failureClass,
1009
- });
1377
+ }));
1010
1378
  }
1011
1379
  const key = `${resolve(repoRoot)}\u0000${id}\u0000${cacheKey}`;
1012
1380
  const existing = activeAsyncChecks.get(key);
@@ -1052,48 +1420,69 @@ export function runControllerCheckAsync(
1052
1420
  };
1053
1421
  const execute = async (lease?: HeavyCheckLease) => {
1054
1422
  assertExecutionStillRequested();
1055
- const result = await executeControllerCheckAsync(repoRoot, check, timeoutMs, (pid) => {
1056
- lease?.setChildPid(pid);
1057
- notifySpawn(pid);
1058
- }, options.isolatedControllerHome);
1059
- const completedRevision = currentControllerCheckRevision(repoRoot);
1060
- const stale = completedRevision !== revision;
1061
- const finalized = {
1062
- ...result,
1063
- ...(stale ? {
1064
- ok: false,
1065
- status: 1,
1066
- stderr: [
1067
- result.stderr,
1068
- 'repository revision changed while the check was running; evidence is stale and the check must be rerun',
1069
- ].filter(Boolean).join('\n'),
1070
- failureClass: 'infrastructure_failure' as const,
1071
- } : {}),
1072
- cacheHit: false,
1073
- validatedRevision: stale ? completedRevision : revision,
1074
- };
1075
- const { artifactPath: _artifactPath, ...withoutPath } = finalized;
1076
- const artifactPath = persistCheckEvidence(repoRoot, withoutPath, {
1077
- revision,
1078
- completedRevision,
1079
- stale,
1080
- cacheKey: stale ? undefined : cacheKey,
1081
- cacheHit: false,
1082
- validatedRevision: stale ? completedRevision : revision,
1423
+ return withLiveRecoveryFence(async (recoveryFenceInstanceId) => {
1424
+ assertExecutionStillRequested();
1425
+ const result = await executeControllerCheckAsync(repoRoot, check, timeoutMs, storage, (pid) => {
1426
+ lease?.setChildPid(pid);
1427
+ notifySpawn(pid);
1428
+ }, options.isolatedControllerHome, options.liveControllerHome);
1429
+ const completedContent = observeControllerCheckContent(repoRoot, { captureFileDigests: true });
1430
+ const completedRevision = completedContent.revision;
1431
+ let liveStateStale = false;
1432
+ if (liveCertification) {
1433
+ try {
1434
+ liveStateStale = controllerCheckLiveExecutionStateFingerprint(
1435
+ liveControllerHome!,
1436
+ recoveryFenceInstanceId,
1437
+ ) !== expectedLiveState;
1438
+ } catch {
1439
+ liveStateStale = true;
1440
+ }
1441
+ }
1442
+ const repositoryStale = controllerCheckInputIntegrityChanged(check, inputIntegrity, completedContent);
1443
+ const stale = repositoryStale || liveStateStale;
1444
+ const finalized = {
1445
+ ...result,
1446
+ ...(stale ? {
1447
+ ok: false,
1448
+ status: 1,
1449
+ stderr: [
1450
+ result.stderr,
1451
+ repositoryStale
1452
+ ? 'repository revision changed while the check was running; evidence is stale and the check must be rerun'
1453
+ : '',
1454
+ liveStateStale
1455
+ ? 'live Runtime/Recovery authority changed while the check was running; evidence is stale and the check must be rerun'
1456
+ : '',
1457
+ ].filter(Boolean).join('\n'),
1458
+ failureClass: 'infrastructure_failure' as const,
1459
+ } : {}),
1460
+ cacheHit: false,
1461
+ validatedRevision: stale ? completedRevision : revision,
1462
+ };
1463
+ const { artifactPath: _artifactPath, ...withoutPath } = finalized;
1464
+ const artifactPath = persistCheckEvidence(storage, withoutPath, {
1465
+ revision,
1466
+ completedRevision,
1467
+ stale,
1468
+ cacheKey: stale ? undefined : cacheKey,
1469
+ cacheHit: false,
1470
+ validatedRevision: stale ? completedRevision : revision,
1471
+ });
1472
+ return { ...finalized, artifactPath };
1083
1473
  });
1084
- return { ...finalized, artifactPath };
1085
1474
  };
1086
1475
  const executeHeavy = async (): Promise<ControllerCheckResult> => {
1087
1476
  assertExecutionStillRequested();
1088
- const lease = await acquireHeavyCheckLock(repoRoot, id);
1477
+ const lease = await acquireHeavyCheckLock(storage, id);
1089
1478
  try {
1090
1479
  assertExecutionStillRequested();
1091
1480
  const currentRevision = currentControllerCheckRevision(repoRoot);
1092
1481
  if (currentRevision !== revision) {
1093
1482
  throw new Error(`repository revision changed while heavy check ${id} was queued; resubmit the check`);
1094
1483
  }
1095
- const refreshed = readControllerCheckEvidenceByKey(repoRoot, id, cacheKey)
1096
- ?? readLatestControllerCheckEvidence(repoRoot, id);
1484
+ const refreshed = readControllerCheckEvidenceByKey(storage, id, cacheKey)
1485
+ ?? readLatestControllerCheckEvidence(repoRoot, id, storage);
1097
1486
  if (refreshed?.cacheKey === cacheKey && refreshed.ok) {
1098
1487
  return {
1099
1488
  check,
@@ -1104,7 +1493,7 @@ export function runControllerCheckAsync(
1104
1493
  stderr: refreshed.stderr,
1105
1494
  command: refreshed.command,
1106
1495
  executedAt: refreshed.executedAt,
1107
- artifactPath: relative(repoRoot, evidencePath(repoRoot, id)).replace(/\\/g, '/'),
1496
+ artifactPath: logicalEvidenceArtifactPath(id),
1108
1497
  cacheHit: true,
1109
1498
  validatedRevision: refreshed.validatedRevision ?? refreshed.completedRevision ?? refreshed.revision ?? revision,
1110
1499
  originalExecutedAt: refreshed.originalExecutedAt ?? refreshed.executedAt,