@moretea-labs/forge 1.5.0-rc.1

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 (903) hide show
  1. package/AGENTS.md +120 -0
  2. package/CHANGELOG.md +36 -0
  3. package/CLAUDE.md +120 -0
  4. package/CODE_OF_CONDUCT.md +23 -0
  5. package/CONTRIBUTING.md +77 -0
  6. package/LICENSE +22 -0
  7. package/NOTICE +9 -0
  8. package/README.en.md +5 -0
  9. package/README.es.md +12 -0
  10. package/README.fr.md +12 -0
  11. package/README.ja.md +12 -0
  12. package/README.md +132 -0
  13. package/README.zh-CN.md +84 -0
  14. package/SECURITY.md +50 -0
  15. package/SKILL.md +431 -0
  16. package/SUPPORT.md +31 -0
  17. package/THIRD_PARTY_NOTICES.md +18 -0
  18. package/agents/openai.yaml +4 -0
  19. package/assets/AGENTS.md +68 -0
  20. package/assets/CLAUDE.md +68 -0
  21. package/assets/branding/README.md +10 -0
  22. package/assets/branding/forge-controller-flow.mmd +8 -0
  23. package/assets/hooks/AGENTS.md +75 -0
  24. package/assets/hooks/CLAUDE.md +75 -0
  25. package/assets/hooks/anti-simplification.sh +12 -0
  26. package/assets/hooks/changelog-guard.sh +80 -0
  27. package/assets/hooks/codex.hooks.template.json +60 -0
  28. package/assets/hooks/first-principles-guard.sh +72 -0
  29. package/assets/hooks/hook-input.sh +618 -0
  30. package/assets/hooks/lib/session-state.sh +106 -0
  31. package/assets/hooks/lib/workflow-state.sh +1730 -0
  32. package/assets/hooks/post-bash.sh +215 -0
  33. package/assets/hooks/post-edit-guard.sh +256 -0
  34. package/assets/hooks/post-tool-observer.sh +99 -0
  35. package/assets/hooks/pre-edit-guard.sh +240 -0
  36. package/assets/hooks/prompt-guard.sh +1077 -0
  37. package/assets/hooks/run-hook.sh +67 -0
  38. package/assets/hooks/security-sentinel.sh +115 -0
  39. package/assets/hooks/session-start-context.sh +593 -0
  40. package/assets/hooks/settings.template.json +60 -0
  41. package/assets/hooks/stop-orchestrator.sh +171 -0
  42. package/assets/hooks/subagent-return-channel-guard.sh +107 -0
  43. package/assets/hooks/worktree-guard.sh +39 -0
  44. package/assets/initializer-question-pack.v1.json +132 -0
  45. package/assets/initializer-question-pack.v1.schema.json +46 -0
  46. package/assets/initializer-question-pack.v2.json +174 -0
  47. package/assets/initializer-question-pack.v3.json +206 -0
  48. package/assets/initializer-question-pack.v3.schema.json +92 -0
  49. package/assets/initializer-question-pack.v4.json +557 -0
  50. package/assets/initializer-question-pack.v4.schema.json +169 -0
  51. package/assets/partials/01-header.partial.md +10 -0
  52. package/assets/partials/02-iron-rules.partial.md +30 -0
  53. package/assets/partials/03-philosophy.partial.md +32 -0
  54. package/assets/partials/04-project-structure.partial.md +56 -0
  55. package/assets/partials/05-workflow.partial.md +79 -0
  56. package/assets/partials/06-cloudflare.partial.md +20 -0
  57. package/assets/partials/07-footer.partial.md +38 -0
  58. package/assets/partials/08-orchestration.partial.md +49 -0
  59. package/assets/partials/09-compact-instructions.partial.md +11 -0
  60. package/assets/partials/_assembly-order.md +40 -0
  61. package/assets/partials-agents/01-header.partial.md +8 -0
  62. package/assets/partials-agents/02-operating-mode.partial.md +44 -0
  63. package/assets/partials-agents/03-orchestration.partial.md +45 -0
  64. package/assets/partials-agents/04-task-protocol.partial.md +69 -0
  65. package/assets/partials-agents/05-coding-constraints.partial.md +20 -0
  66. package/assets/partials-agents/06-quality-safety.partial.md +23 -0
  67. package/assets/partials-agents/07-cloudflare.partial.md +9 -0
  68. package/assets/partials-agents/08-deep-docs.partial.md +32 -0
  69. package/assets/plan-map.json +301 -0
  70. package/assets/plugin-registry.v1.json +8 -0
  71. package/assets/project-structures/ai-native-collaborative-editor.txt +25 -0
  72. package/assets/project-structures/ai-native-product-copilot.txt +21 -0
  73. package/assets/project-structures/ai-native-runtime-console.txt +25 -0
  74. package/assets/project-structures/ai-native-sidecar-kernel.txt +19 -0
  75. package/assets/project-structures/astro-ssr.txt +25 -0
  76. package/assets/project-structures/expo-nativewind.txt +52 -0
  77. package/assets/project-structures/monorepo.txt +63 -0
  78. package/assets/project-structures/remix.txt +50 -0
  79. package/assets/project-structures/tanstack-start-workers.txt +51 -0
  80. package/assets/project-structures/vite-tanstack.txt +58 -0
  81. package/assets/reference-configs/AGENTS.md +13 -0
  82. package/assets/reference-configs/CLAUDE.md +13 -0
  83. package/assets/reference-configs/agentic-development-flow.md +94 -0
  84. package/assets/reference-configs/ai-workflows.md +14 -0
  85. package/assets/reference-configs/changelog-versioning.md +13 -0
  86. package/assets/reference-configs/coding-standards.md +14 -0
  87. package/assets/reference-configs/development-protocol.md +21 -0
  88. package/assets/reference-configs/document-generation.md +38 -0
  89. package/assets/reference-configs/evaluator-rubric.md +19 -0
  90. package/assets/reference-configs/external-tooling.md +374 -0
  91. package/assets/reference-configs/git-strategy.md +14 -0
  92. package/assets/reference-configs/global-working-rules.md +64 -0
  93. package/assets/reference-configs/handoff-protocol.md +38 -0
  94. package/assets/reference-configs/harness-overview.md +124 -0
  95. package/assets/reference-configs/heartbeat-triage.md +57 -0
  96. package/assets/reference-configs/hook-operations.md +96 -0
  97. package/assets/reference-configs/release-deploy.md +40 -0
  98. package/assets/reference-configs/spa-day-protocol.md +14 -0
  99. package/assets/reference-configs/sprint-contracts.md +89 -0
  100. package/assets/reference-configs/workflow-orchestration.md +14 -0
  101. package/assets/skill-hooks.json +41 -0
  102. package/assets/skill-version.json +189 -0
  103. package/assets/skills/claude-review/SKILL.md +231 -0
  104. package/assets/skills/codex-review/SKILL.md +103 -0
  105. package/assets/templates/AGENTS.md +13 -0
  106. package/assets/templates/CLAUDE.md +13 -0
  107. package/assets/templates/architecture.template.md +80 -0
  108. package/assets/templates/brief.template.md +38 -0
  109. package/assets/templates/contract.template.md +104 -0
  110. package/assets/templates/decisions.template.md +85 -0
  111. package/assets/templates/factor-factory/factor-backtest-report.template.md +36 -0
  112. package/assets/templates/factor-factory/factor-hypothesis.template.md +30 -0
  113. package/assets/templates/factor-factory/factor-registry.template.json +5 -0
  114. package/assets/templates/gitignore.template +53 -0
  115. package/assets/templates/guides/metro-esm-gotchas.template.md +46 -0
  116. package/assets/templates/helpers/architecture-event.ts +822 -0
  117. package/assets/templates/helpers/architecture-queue.sh +647 -0
  118. package/assets/templates/helpers/archive-architecture-request.sh +244 -0
  119. package/assets/templates/helpers/archive-workflow.sh +295 -0
  120. package/assets/templates/helpers/capability-config.ts +394 -0
  121. package/assets/templates/helpers/capability-resolver.ts +505 -0
  122. package/assets/templates/helpers/capture-plan.sh +335 -0
  123. package/assets/templates/helpers/check-agent-tooling.sh +1533 -0
  124. package/assets/templates/helpers/check-architecture-sync.sh +287 -0
  125. package/assets/templates/helpers/check-brain-manifest.sh +255 -0
  126. package/assets/templates/helpers/check-context-files.sh +49 -0
  127. package/assets/templates/helpers/check-deploy-sql-order.sh +117 -0
  128. package/assets/templates/helpers/check-skill-version.ts +193 -0
  129. package/assets/templates/helpers/check-task-sync.sh +65 -0
  130. package/assets/templates/helpers/check-task-workflow.sh +1002 -0
  131. package/assets/templates/helpers/codex-handoff-resume.sh +269 -0
  132. package/assets/templates/helpers/context-contract-sync.sh +516 -0
  133. package/assets/templates/helpers/contract-run.ts +465 -0
  134. package/assets/templates/helpers/contract-worktree.sh +725 -0
  135. package/assets/templates/helpers/ensure-task-workflow.sh +1218 -0
  136. package/assets/templates/helpers/harness-trace-grade.sh +150 -0
  137. package/assets/templates/helpers/heartbeat-triage.sh +361 -0
  138. package/assets/templates/helpers/inspect-project-state.ts +280 -0
  139. package/assets/templates/helpers/maintenance-triage.sh +167 -0
  140. package/assets/templates/helpers/migrate-project-template.sh +54 -0
  141. package/assets/templates/helpers/migrate-workflow-docs.ts +413 -0
  142. package/assets/templates/helpers/new-plan.sh +183 -0
  143. package/assets/templates/helpers/new-spec.sh +50 -0
  144. package/assets/templates/helpers/new-sprint.sh +49 -0
  145. package/assets/templates/helpers/plan-to-todo.sh +799 -0
  146. package/assets/templates/helpers/prepare-codex-handoff.sh +203 -0
  147. package/assets/templates/helpers/prepare-handoff.sh +81 -0
  148. package/assets/templates/helpers/refresh-current-status.sh +469 -0
  149. package/assets/templates/helpers/select-agent-context-blocks.sh +66 -0
  150. package/assets/templates/helpers/ship-worktrees.sh +561 -0
  151. package/assets/templates/helpers/sprint-backlog.sh +837 -0
  152. package/assets/templates/helpers/summarize-failures.sh +125 -0
  153. package/assets/templates/helpers/switch-plan.sh +153 -0
  154. package/assets/templates/helpers/sync-brain-docs.sh +381 -0
  155. package/assets/templates/helpers/verify-contract.sh +705 -0
  156. package/assets/templates/helpers/verify-sprint.sh +680 -0
  157. package/assets/templates/helpers/workflow-contract.ts +202 -0
  158. package/assets/templates/helpers/workstream-sync.sh +263 -0
  159. package/assets/templates/implementation-notes.template.md +37 -0
  160. package/assets/templates/packages.template.md +45 -0
  161. package/assets/templates/plan.template.md +77 -0
  162. package/assets/templates/prd.template.md +165 -0
  163. package/assets/templates/research.template.md +25 -0
  164. package/assets/templates/review.template.md +78 -0
  165. package/assets/templates/spec.template.md +31 -0
  166. package/assets/templates/sprint.template.md +69 -0
  167. package/assets/templates/tech-stack.template.md +57 -0
  168. package/assets/versions.json +77 -0
  169. package/assets/workflow-contract.v1.json +671 -0
  170. package/bin/forge-desktop-helper.mjs +204 -0
  171. package/bin/forge-hook.mjs +32 -0
  172. package/bin/forge-runtime-service.mjs +34 -0
  173. package/bin/forge-runtime.mjs +30 -0
  174. package/bin/forge.mjs +32 -0
  175. package/docs/CHANGELOG.md +978 -0
  176. package/docs/README.md +72 -0
  177. package/docs/README.zh-CN.md +58 -0
  178. package/docs/architecture/chatgpt-handoff-facade.md +393 -0
  179. package/docs/architecture/current/README.md +158 -0
  180. package/docs/architecture/current/approved-target-architecture.zh-CN.md +1501 -0
  181. package/docs/architecture/current/architecture-invariants.md +331 -0
  182. package/docs/architecture/current/automation-and-schedule-engine.md +453 -0
  183. package/docs/architecture/current/codegraph-context-provider.md +226 -0
  184. package/docs/architecture/current/control-plane-authority-inventory.md +82 -0
  185. package/docs/architecture/current/controller-repository-decoupling.md +148 -0
  186. package/docs/architecture/current/dispatch-and-agent-strategy.md +354 -0
  187. package/docs/architecture/current/entity-model.md +619 -0
  188. package/docs/architecture/current/external-plugin-governance.md +396 -0
  189. package/docs/architecture/current/failure-recovery.md +471 -0
  190. package/docs/architecture/current/governance.md +213 -0
  191. package/docs/architecture/current/human-interaction-plane.md +87 -0
  192. package/docs/architecture/current/implementation-status.md +128 -0
  193. package/docs/architecture/current/job-and-run-lifecycle.md +413 -0
  194. package/docs/architecture/current/local-workspace-target-grants.md +70 -0
  195. package/docs/architecture/current/migration-roadmap.md +105 -0
  196. package/docs/architecture/current/multi-repository-execution.md +371 -0
  197. package/docs/architecture/current/operations-runbook.md +45 -0
  198. package/docs/architecture/current/peer-supercontroller-runtime.md +32 -0
  199. package/docs/architecture/current/personal-assistant-plugin-baseline.md +207 -0
  200. package/docs/architecture/current/repository-access-modes.md +102 -0
  201. package/docs/architecture/current/route-policy.md +15 -0
  202. package/docs/architecture/current/route-session-concurrency.md +85 -0
  203. package/docs/architecture/current/runtime-architecture-simplification.md +214 -0
  204. package/docs/architecture/current/runtime-directory-map.md +49 -0
  205. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +85 -0
  206. package/docs/architecture/current/scheduler-and-resource-claims.md +432 -0
  207. package/docs/architecture/current/session-aware-execution-and-authorization.md +77 -0
  208. package/docs/architecture/current/system-overview.md +144 -0
  209. package/docs/architecture/current/target-requirements-traceability.md +202 -0
  210. package/docs/architecture/current/thin-harness-governance-policy.md +32 -0
  211. package/docs/architecture/current/thin-harness-v1.md +313 -0
  212. package/docs/architecture/current/verification-and-release-gates.md +391 -0
  213. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +100 -0
  214. package/docs/architecture/decisions/20260803-canonical-skill-surface.md +40 -0
  215. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +53 -0
  216. package/docs/architecture/diagrams/.gitkeep +1 -0
  217. package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +186 -0
  218. package/docs/architecture/domains/.gitkeep +0 -0
  219. package/docs/architecture/domains/public-surface.md +30 -0
  220. package/docs/architecture/domains/runtime-harness.md +33 -0
  221. package/docs/architecture/domains/verification.md +33 -0
  222. package/docs/architecture/domains/workflow-engine.md +30 -0
  223. package/docs/architecture/global-hook-runtime.md +162 -0
  224. package/docs/architecture/history.md +38 -0
  225. package/docs/architecture/index.md +114 -0
  226. package/docs/architecture/ios-semantic-automation-provider-v2.md +166 -0
  227. package/docs/architecture/modules/.gitkeep +0 -0
  228. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +24 -0
  229. package/docs/architecture/requests/.gitkeep +1 -0
  230. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
  231. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +59 -0
  232. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +59 -0
  233. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +59 -0
  234. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +59 -0
  235. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
  236. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
  237. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
  238. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
  239. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +59 -0
  240. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  241. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  242. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
  243. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  244. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
  245. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  246. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
  247. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  248. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
  249. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +59 -0
  250. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +59 -0
  251. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +63 -0
  252. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +60 -0
  253. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +60 -0
  254. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +60 -0
  255. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +60 -0
  256. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +60 -0
  257. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +60 -0
  258. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +68 -0
  259. package/docs/architecture/requests/archive/2026/root.md +68 -0
  260. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +76 -0
  261. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +68 -0
  262. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +69 -0
  263. package/docs/architecture/reviews/2026-07-11-access-surface-review.md +99 -0
  264. package/docs/architecture/snapshots/.gitkeep +1 -0
  265. package/docs/forge-autonomous-goal-loop.md +7 -0
  266. package/docs/forge-chatgpt-browser-engine.md +11 -0
  267. package/docs/forge-chatgpt-mcp-setup.md +239 -0
  268. package/docs/forge-chatgpt-supervised-automation.md +11 -0
  269. package/docs/forge-github-issue-launcher.md +7 -0
  270. package/docs/forge-plugin-management.md +95 -0
  271. package/docs/forge-provider-configuration.md +7 -0
  272. package/docs/forge-runtime-self-healing-loop.md +7 -0
  273. package/docs/images/forge-banner-cn.svg +1 -0
  274. package/docs/images/forge-banner.svg +1 -0
  275. package/docs/images/forge-controller-flow.svg +1 -0
  276. package/docs/images/image.png +0 -0
  277. package/docs/images/repo-harness-gptpro-cn.png +0 -0
  278. package/docs/images/repo-harness-gptpro.png +0 -0
  279. package/docs/images/repo-harness-hook-carrot.png +0 -0
  280. package/docs/operations/chatgpt-personal-assistant-runtime.md +83 -0
  281. package/docs/operations/checkout-route-integrity.md +22 -0
  282. package/docs/operations/controller-browser-plugin.md +299 -0
  283. package/docs/operations/controller-desktop-plugin.md +112 -0
  284. package/docs/operations/controller-performance-and-502.md +126 -0
  285. package/docs/operations/controller-reliability-runbook.md +134 -0
  286. package/docs/operations/environment-hardening.md +50 -0
  287. package/docs/operations/features.md +45 -0
  288. package/docs/operations/features.zh-CN.md +45 -0
  289. package/docs/operations/github-repository.md +60 -0
  290. package/docs/operations/gmail-assistant-routines.md +186 -0
  291. package/docs/operations/homebrew.md +26 -0
  292. package/docs/operations/local-system-assistant.md +46 -0
  293. package/docs/operations/local-system-assistant.zh-CN.md +46 -0
  294. package/docs/operations/mcp-tool-exposure.md +58 -0
  295. package/docs/operations/mobile-intents-shortcuts.md +117 -0
  296. package/docs/operations/open-source-release-hygiene.md +31 -0
  297. package/docs/operations/personal-assistant-operator-guide.md +61 -0
  298. package/docs/operations/platform-support.md +62 -0
  299. package/docs/operations/platform-support.zh-CN.md +61 -0
  300. package/docs/operations/provider-configuration.md +31 -0
  301. package/docs/operations/releasing.md +86 -0
  302. package/docs/operations/releasing.zh-CN.md +86 -0
  303. package/docs/operations/route-session-concurrency-baseline.json +893 -0
  304. package/docs/operations/route-session-concurrency-baseline.md +94 -0
  305. package/docs/operations/runtime-performance-diagnostics.md +14 -0
  306. package/docs/operations/sqlite-control-plane-recovery.md +65 -0
  307. package/docs/operations/standalone-disaster-recovery.md +136 -0
  308. package/docs/operations/troubleshooting.md +72 -0
  309. package/docs/operations/troubleshooting.zh-CN.md +70 -0
  310. package/docs/public-usage-guide.md +33 -0
  311. package/docs/public-usage-guide.zh-CN.md +33 -0
  312. package/docs/tutorials/01-install-and-start.md +93 -0
  313. package/docs/tutorials/01-install-and-start.zh-CN.md +93 -0
  314. package/docs/tutorials/02-connect-chatgpt.md +82 -0
  315. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +80 -0
  316. package/docs/tutorials/03-first-repository-task.md +57 -0
  317. package/docs/tutorials/03-first-repository-task.zh-CN.md +57 -0
  318. package/docs/tutorials/README.md +13 -0
  319. package/docs/tutorials/README.zh-CN.md +13 -0
  320. package/docs/wiki/Architecture.md +85 -0
  321. package/docs/wiki/Core-Concepts.md +27 -0
  322. package/docs/wiki/Home.md +36 -0
  323. package/docs/wiki/Implementation.md +172 -0
  324. package/docs/wiki/Installation.md +50 -0
  325. package/docs/wiki/Integrations.md +29 -0
  326. package/docs/wiki/Operations.md +26 -0
  327. package/docs/wiki/Quick-Start.md +46 -0
  328. package/docs/wiki/Releases-and-Upgrades.md +34 -0
  329. package/docs/wiki/Runtime-Architecture.md +50 -0
  330. package/docs/wiki/Security-Model.md +31 -0
  331. package/docs/wiki/Troubleshooting.md +38 -0
  332. package/docs/wiki/Work-Lifecycle.md +31 -0
  333. package/docs/wiki/_Sidebar.md +21 -0
  334. package/install.ps1 +48 -0
  335. package/install.sh +66 -0
  336. package/package.json +156 -0
  337. package/scripts/activate-source-baseline.ts +335 -0
  338. package/scripts/architecture-event.ts +822 -0
  339. package/scripts/architecture-queue.sh +647 -0
  340. package/scripts/archive-architecture-request.sh +244 -0
  341. package/scripts/archive-workflow.sh +295 -0
  342. package/scripts/assemble-template.ts +1172 -0
  343. package/scripts/benchmark-controller-hotpath.ts +212 -0
  344. package/scripts/benchmark-controller-round2.ts +139 -0
  345. package/scripts/benchmark-ios-agent-device.ts +514 -0
  346. package/scripts/benchmark-route-session-concurrency.ts +714 -0
  347. package/scripts/benchmark-thin-harness-gateway-ab.ts +632 -0
  348. package/scripts/benchmark-thin-harness-mcp-e2e.ts +479 -0
  349. package/scripts/benchmark-thin-harness.ts +251 -0
  350. package/scripts/bootstrap-runtime-maintenance-recovery.sh +183 -0
  351. package/scripts/canary-global-hook.sh +263 -0
  352. package/scripts/capability-config.ts +394 -0
  353. package/scripts/capability-resolver.ts +505 -0
  354. package/scripts/capture-plan.sh +335 -0
  355. package/scripts/check-agent-tooling.sh +1549 -0
  356. package/scripts/check-architecture-sync.sh +373 -0
  357. package/scripts/check-brain-manifest.sh +255 -0
  358. package/scripts/check-ci.sh +16 -0
  359. package/scripts/check-context-files.sh +49 -0
  360. package/scripts/check-deploy-sql-order.sh +117 -0
  361. package/scripts/check-mcp-compatibility.ts +93 -0
  362. package/scripts/check-npm-release.sh +32 -0
  363. package/scripts/check-open-source-tracked-surface.sh +146 -0
  364. package/scripts/check-package-identity.mjs +117 -0
  365. package/scripts/check-platform-support.mjs +79 -0
  366. package/scripts/check-public-docs.sh +105 -0
  367. package/scripts/check-public-release-surface.sh +19 -0
  368. package/scripts/check-release-published.sh +57 -0
  369. package/scripts/check-release-readiness.sh +43 -0
  370. package/scripts/check-release-version.mjs +74 -0
  371. package/scripts/check-runtime-architecture.mjs +593 -0
  372. package/scripts/check-runtime-env.mjs +89 -0
  373. package/scripts/check-skill-version.ts +193 -0
  374. package/scripts/check-tarball-install-smoke.sh +66 -0
  375. package/scripts/check-task-sync.sh +65 -0
  376. package/scripts/check-task-workflow.sh +1002 -0
  377. package/scripts/check-third-party-notices.mjs +36 -0
  378. package/scripts/codex-handoff-resume.sh +269 -0
  379. package/scripts/complete-control-plane-cutover.ts +126 -0
  380. package/scripts/context-contract-sync.sh +516 -0
  381. package/scripts/contract-run.ts +465 -0
  382. package/scripts/contract-worktree.sh +725 -0
  383. package/scripts/controller-ngrok-rotation.sh +678 -0
  384. package/scripts/create-project-dirs.sh +282 -0
  385. package/scripts/ensure-codegraph.sh +16 -0
  386. package/scripts/ensure-task-workflow.sh +1218 -0
  387. package/scripts/export-requirement-portfolio.ts +28 -0
  388. package/scripts/factor-lab-check.sh +73 -0
  389. package/scripts/factor-lab-new.sh +96 -0
  390. package/scripts/factor-lab-promote.sh +86 -0
  391. package/scripts/factor-lab-reject.sh +68 -0
  392. package/scripts/harness-trace-grade.sh +150 -0
  393. package/scripts/heartbeat-triage.sh +361 -0
  394. package/scripts/hook-dispatch-diet-report.ts +234 -0
  395. package/scripts/hook-shim.sh +93 -0
  396. package/scripts/init-project.sh +505 -0
  397. package/scripts/initializer-question-pack.ts +200 -0
  398. package/scripts/inspect-project-state.ts +280 -0
  399. package/scripts/install-desktop-operator-registration.ts +45 -0
  400. package/scripts/install-standalone-recovery.ts +74 -0
  401. package/scripts/lib/AGENTS.md +13 -0
  402. package/scripts/lib/CLAUDE.md +13 -0
  403. package/scripts/lib/controller-home-env.sh +69 -0
  404. package/scripts/lib/project-init-lib.sh +2759 -0
  405. package/scripts/load-standalone-recovery.sh +17 -0
  406. package/scripts/loop-engine-cutover-gate.ts +324 -0
  407. package/scripts/maintenance-triage.sh +167 -0
  408. package/scripts/migrate-project-template.sh +1189 -0
  409. package/scripts/migrate-requirement-portfolio.ts +86 -0
  410. package/scripts/migrate-workflow-docs.ts +413 -0
  411. package/scripts/new-plan.sh +183 -0
  412. package/scripts/new-spec.sh +50 -0
  413. package/scripts/new-sprint.sh +49 -0
  414. package/scripts/open-source-audit-allowlist.txt +46 -0
  415. package/scripts/package-source-archive.sh +147 -0
  416. package/scripts/plan-to-todo.sh +799 -0
  417. package/scripts/prepare-codex-handoff.sh +203 -0
  418. package/scripts/prepare-handoff.sh +81 -0
  419. package/scripts/public-release-files.txt +39 -0
  420. package/scripts/public-release.ts +174 -0
  421. package/scripts/publish-release-tarball.sh +27 -0
  422. package/scripts/refresh-current-status.sh +469 -0
  423. package/scripts/refresh-ios-physical-runtime.ts +196 -0
  424. package/scripts/repair-executable-modes.ts +8 -0
  425. package/scripts/rollout-all-registered-repos.sh +7 -0
  426. package/scripts/route-nl-vs-ts-eval.ts +679 -0
  427. package/scripts/run-bun-test-file.ts +182 -0
  428. package/scripts/run-governed-gate.ts +140 -0
  429. package/scripts/run-mobile-monitor-bridge.ts +71 -0
  430. package/scripts/run-skill-evals.ts +1022 -0
  431. package/scripts/run-skill-hook.ts +244 -0
  432. package/scripts/run-supervised-command.ts +24 -0
  433. package/scripts/run-tests-portable.sh +20 -0
  434. package/scripts/select-agent-context-blocks.sh +66 -0
  435. package/scripts/setup-plugins.sh +43 -0
  436. package/scripts/ship-worktrees.sh +561 -0
  437. package/scripts/smoke-browser-cdp-bridge.ts +93 -0
  438. package/scripts/smoke-chatgpt-supervised-automation.ts +208 -0
  439. package/scripts/smoke-ios-physical-device.ts +114 -0
  440. package/scripts/smoke-mcp-http-runtime.ts +216 -0
  441. package/scripts/smoke-mcp-tool-surface.ts +117 -0
  442. package/scripts/smoke-runtime-recovery.ts +220 -0
  443. package/scripts/smoke-schedule-engine.ts +283 -0
  444. package/scripts/sprint-backlog.sh +837 -0
  445. package/scripts/stage-runtime-release.ts +38 -0
  446. package/scripts/summarize-failures.sh +125 -0
  447. package/scripts/switch-plan.sh +153 -0
  448. package/scripts/sync-brain-docs.sh +381 -0
  449. package/scripts/sync-codex-installed-copies.sh +183 -0
  450. package/scripts/test-governance.ts +75 -0
  451. package/scripts/verify-contract.sh +705 -0
  452. package/scripts/verify-forge-runtime.sh +31 -0
  453. package/scripts/verify-sprint.sh +680 -0
  454. package/scripts/windows-paths.d.mts +7 -0
  455. package/scripts/windows-paths.mjs +28 -0
  456. package/scripts/workflow-contract.ts +206 -0
  457. package/scripts/workstream-sync.sh +263 -0
  458. package/src/cli/agent-jobs/executable-resolver.ts +289 -0
  459. package/src/cli/agent-jobs/executor-health.ts +252 -0
  460. package/src/cli/agent-jobs/integration.ts +963 -0
  461. package/src/cli/agent-jobs/job-manager.ts +2030 -0
  462. package/src/cli/agent-jobs/types.ts +219 -0
  463. package/src/cli/agent-jobs/worker-lifecycle.ts +288 -0
  464. package/src/cli/chatgpt-browser/bind-server.ts +504 -0
  465. package/src/cli/chatgpt-browser/binding.ts +140 -0
  466. package/src/cli/chatgpt-browser/bridge-extension.ts +221 -0
  467. package/src/cli/chatgpt-browser/bridge-provider.ts +268 -0
  468. package/src/cli/chatgpt-browser/engine.ts +569 -0
  469. package/src/cli/chatgpt-browser/file-policy.ts +158 -0
  470. package/src/cli/chatgpt-browser/native-provider.ts +553 -0
  471. package/src/cli/chatgpt-browser/oracle-provider.ts +437 -0
  472. package/src/cli/chatgpt-browser/prompt-assembler.ts +29 -0
  473. package/src/cli/chatgpt-browser/session-store.ts +301 -0
  474. package/src/cli/chatgpt-browser/types.ts +172 -0
  475. package/src/cli/commands/adopt-plan.ts +186 -0
  476. package/src/cli/commands/brain-root.ts +132 -0
  477. package/src/cli/commands/brain.ts +609 -0
  478. package/src/cli/commands/capability-context.ts +613 -0
  479. package/src/cli/commands/chatgpt.ts +424 -0
  480. package/src/cli/commands/controller.ts +680 -0
  481. package/src/cli/commands/docs.ts +103 -0
  482. package/src/cli/commands/doctor.ts +476 -0
  483. package/src/cli/commands/global-runtime.ts +323 -0
  484. package/src/cli/commands/hook.ts +34 -0
  485. package/src/cli/commands/init-hook.ts +870 -0
  486. package/src/cli/commands/init.ts +829 -0
  487. package/src/cli/commands/install.ts +86 -0
  488. package/src/cli/commands/mcp.ts +315 -0
  489. package/src/cli/commands/migrate.ts +154 -0
  490. package/src/cli/commands/plugin.ts +74 -0
  491. package/src/cli/commands/prompt-guard-decision.ts +238 -0
  492. package/src/cli/commands/recovery.ts +667 -0
  493. package/src/cli/commands/repository.ts +224 -0
  494. package/src/cli/commands/run.ts +23 -0
  495. package/src/cli/commands/runtime.ts +191 -0
  496. package/src/cli/commands/security.ts +440 -0
  497. package/src/cli/commands/status.ts +137 -0
  498. package/src/cli/commands/tools.ts +167 -0
  499. package/src/cli/controller/check-runner.ts +1022 -0
  500. package/src/cli/controller/codex-continuation.ts +153 -0
  501. package/src/cli/controller/completion-backlog.ts +380 -0
  502. package/src/cli/controller/completion-orchestrator.ts +1807 -0
  503. package/src/cli/controller/completion-target.ts +96 -0
  504. package/src/cli/controller/composite-operations.ts +196 -0
  505. package/src/cli/controller/composite-result.ts +130 -0
  506. package/src/cli/controller/context-pack.ts +652 -0
  507. package/src/cli/controller/execution-completion.ts +175 -0
  508. package/src/cli/controller/execution-policy.ts +392 -0
  509. package/src/cli/controller/governance.ts +594 -0
  510. package/src/cli/controller/issue-store.ts +1421 -0
  511. package/src/cli/controller/launch-agents.ts +410 -0
  512. package/src/cli/controller/legacy-issue-cutover.ts +415 -0
  513. package/src/cli/controller/lifecycle-authority.ts +16 -0
  514. package/src/cli/controller/migrated-issue-decision.ts +237 -0
  515. package/src/cli/controller/operational-plan.ts +226 -0
  516. package/src/cli/controller/postcondition.ts +134 -0
  517. package/src/cli/controller/progress.ts +656 -0
  518. package/src/cli/controller/project-state.ts +147 -0
  519. package/src/cli/controller/requirement-portfolio-export.ts +146 -0
  520. package/src/cli/controller/requirement-portfolio-migration.ts +613 -0
  521. package/src/cli/controller/run-evidence.ts +43 -0
  522. package/src/cli/controller/runtime-config.ts +92 -0
  523. package/src/cli/controller/stuck-state-migration.ts +396 -0
  524. package/src/cli/controller/task-ledger.ts +632 -0
  525. package/src/cli/controller/task-status-resolver.ts +403 -0
  526. package/src/cli/controller/types.ts +273 -0
  527. package/src/cli/controller/work-mode.ts +186 -0
  528. package/src/cli/controller/worklog.ts +201 -0
  529. package/src/cli/diagnostic-entry.ts +39 -0
  530. package/src/cli/editing/edit-session.ts +1606 -0
  531. package/src/cli/editing/executable-modes.ts +70 -0
  532. package/src/cli/github/github.ts +274 -0
  533. package/src/cli/github/plugin.ts +345 -0
  534. package/src/cli/github/session.ts +104 -0
  535. package/src/cli/hook/prompt-guard-decision.ts +238 -0
  536. package/src/cli/hook/prompt-intents.ts +534 -0
  537. package/src/cli/hook/route-registry.ts +107 -0
  538. package/src/cli/hook/runtime.ts +303 -0
  539. package/src/cli/hook/state-snapshot.ts +388 -0
  540. package/src/cli/hook-entry.ts +51 -0
  541. package/src/cli/index.ts +574 -0
  542. package/src/cli/installer/managed-entries.ts +78 -0
  543. package/src/cli/installer/shared.ts +65 -0
  544. package/src/cli/installer/targets/claude.ts +136 -0
  545. package/src/cli/installer/targets/codex.ts +174 -0
  546. package/src/cli/installer/targets/registry.ts +28 -0
  547. package/src/cli/installer/types.ts +72 -0
  548. package/src/cli/local-bridge/connector-freshness.ts +529 -0
  549. package/src/cli/local-bridge/console-view-models.ts +324 -0
  550. package/src/cli/local-bridge/dashboard.ts +1312 -0
  551. package/src/cli/local-bridge/facade-api.ts +1877 -0
  552. package/src/cli/local-bridge/job-store.ts +1451 -0
  553. package/src/cli/local-bridge/mobile-intents.ts +344 -0
  554. package/src/cli/local-bridge/mobile-monitor.ts +209 -0
  555. package/src/cli/local-bridge/server.ts +3183 -0
  556. package/src/cli/local-bridge/types.ts +176 -0
  557. package/src/cli/mcp/access-mode.ts +132 -0
  558. package/src/cli/mcp/access-tools.ts +387 -0
  559. package/src/cli/mcp/audit.ts +32 -0
  560. package/src/cli/mcp/auth.ts +402 -0
  561. package/src/cli/mcp/instructions.ts +33 -0
  562. package/src/cli/mcp/legacy-context.ts +16 -0
  563. package/src/cli/mcp/legacy-tool-service.ts +5516 -0
  564. package/src/cli/mcp/multi-repository.ts +423 -0
  565. package/src/cli/mcp/oauth.ts +226 -0
  566. package/src/cli/mcp/paths.ts +134 -0
  567. package/src/cli/mcp/policy.ts +230 -0
  568. package/src/cli/mcp/proxy-env.ts +62 -0
  569. package/src/cli/mcp/readiness-projection.ts +24 -0
  570. package/src/cli/mcp/redaction.ts +21 -0
  571. package/src/cli/mcp/repo.ts +24 -0
  572. package/src/cli/mcp/repository-tools.ts +1295 -0
  573. package/src/cli/mcp/server.ts +341 -0
  574. package/src/cli/mcp/setup.ts +793 -0
  575. package/src/cli/mcp/tool-capability-cache.ts +87 -0
  576. package/src/cli/mcp/tools.ts +9 -0
  577. package/src/cli/mcp/toolset-names.ts +70 -0
  578. package/src/cli/mcp/toolset-selection.ts +41 -0
  579. package/src/cli/mcp/toolset.ts +300 -0
  580. package/src/cli/mcp/transports/AGENTS.md +39 -0
  581. package/src/cli/mcp/transports/CLAUDE.md +39 -0
  582. package/src/cli/mcp/transports/http.ts +1234 -0
  583. package/src/cli/mcp/transports/session-registry.ts +428 -0
  584. package/src/cli/mcp/transports/stdio.ts +8 -0
  585. package/src/cli/mcp/types.ts +41 -0
  586. package/src/cli/mcp/umbrella-tools.ts +117 -0
  587. package/src/cli/repo-adoption/reclaim-runtime.ts +681 -0
  588. package/src/cli/repositories/branch-name-policy.ts +57 -0
  589. package/src/cli/repositories/command-classifier.ts +600 -0
  590. package/src/cli/repositories/command-executor.ts +1066 -0
  591. package/src/cli/repositories/command-normalization.ts +53 -0
  592. package/src/cli/repositories/command-scope.ts +264 -0
  593. package/src/cli/repositories/controller-home.ts +88 -0
  594. package/src/cli/repositories/entity-migration.ts +199 -0
  595. package/src/cli/repositories/ephemeral-workspace.ts +62 -0
  596. package/src/cli/repositories/git-command-executor.ts +301 -0
  597. package/src/cli/repositories/goal-registry.ts +239 -0
  598. package/src/cli/repositories/identity.ts +64 -0
  599. package/src/cli/repositories/local-project-onboarding.ts +539 -0
  600. package/src/cli/repositories/locks.ts +262 -0
  601. package/src/cli/repositories/registry.ts +1057 -0
  602. package/src/cli/repositories/runtime-storage.ts +411 -0
  603. package/src/cli/repositories/safe-patch.ts +421 -0
  604. package/src/cli/repositories/selected-path-actions.ts +341 -0
  605. package/src/cli/repositories/structured-git.ts +257 -0
  606. package/src/cli/repositories/types.ts +155 -0
  607. package/src/cli/repositories/umbrella.ts +134 -0
  608. package/src/cli/repositories/workbench.ts +140 -0
  609. package/src/cli/repositories/worktree-storage.ts +137 -0
  610. package/src/cli/repository/inspector.ts +637 -0
  611. package/src/cli/repository/session-cache.ts +453 -0
  612. package/src/cli/runtime/helper-runner.ts +172 -0
  613. package/src/cli/runtime-invocation.ts +318 -0
  614. package/src/cli/tools/codegraph.ts +676 -0
  615. package/src/cli/v81-entry.ts +29 -0
  616. package/src/core/adoption/gitignore-plan.ts +127 -0
  617. package/src/core/adoption/helper-wrapper-plan.ts +159 -0
  618. package/src/core/adoption/manifest-templates.ts +60 -0
  619. package/src/core/adoption/modes.ts +7 -0
  620. package/src/core/adoption/operations.ts +126 -0
  621. package/src/core/adoption/plan.ts +149 -0
  622. package/src/core/adoption/render.ts +70 -0
  623. package/src/core/adoption/rollback.ts +62 -0
  624. package/src/core/adoption/summary.ts +43 -0
  625. package/src/core/adoption/workflow-contract-asset.ts +14 -0
  626. package/src/core/adoption/workflow-contract-plan.ts +31 -0
  627. package/src/effects/fs-transaction.ts +540 -0
  628. package/src/effects/git-executable.ts +59 -0
  629. package/src/effects/managed-block.ts +86 -0
  630. package/src/effects/path-safety.ts +47 -0
  631. package/src/effects/process-runner.ts +192 -0
  632. package/src/runtime/assistant/action-proposals.ts +279 -0
  633. package/src/runtime/assistant/intent.ts +395 -0
  634. package/src/runtime/assistant/model-provider.ts +460 -0
  635. package/src/runtime/assistant/openapi.ts +223 -0
  636. package/src/runtime/assistant/policy.ts +89 -0
  637. package/src/runtime/assistant/readiness.ts +258 -0
  638. package/src/runtime/assistant/routine-runtime.ts +477 -0
  639. package/src/runtime/assistant/schedule-binding.ts +171 -0
  640. package/src/runtime/assistant/standing-grants.ts +375 -0
  641. package/src/runtime/assistant/store.ts +243 -0
  642. package/src/runtime/assistant/types.ts +135 -0
  643. package/src/runtime/context/codegraph-read-provider.ts +347 -0
  644. package/src/runtime/context/codegraph-sidecar.cjs +207 -0
  645. package/src/runtime/control-plane/dispatch-priority.ts +67 -0
  646. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +151 -0
  647. package/src/runtime/control-plane/execution/execution-identity.ts +456 -0
  648. package/src/runtime/control-plane/execution/session-store.ts +175 -0
  649. package/src/runtime/control-plane/execution/validation.ts +117 -0
  650. package/src/runtime/control-plane/execution/verification-evidence.ts +191 -0
  651. package/src/runtime/control-plane/execution/work-handle-store.ts +225 -0
  652. package/src/runtime/control-plane/execution/work-prepare-request-store.ts +159 -0
  653. package/src/runtime/control-plane/execution/work-task-receipt.ts +445 -0
  654. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +821 -0
  655. package/src/runtime/control-plane/facade/capability-registry.ts +279 -0
  656. package/src/runtime/control-plane/facade/check-normalization.ts +233 -0
  657. package/src/runtime/control-plane/facade/codex-delegation.ts +196 -0
  658. package/src/runtime/control-plane/facade/controller-session-store.ts +199 -0
  659. package/src/runtime/control-plane/facade/facade-result.ts +41 -0
  660. package/src/runtime/control-plane/facade/goal-workloop-access.ts +177 -0
  661. package/src/runtime/control-plane/facade/goal-workloop.ts +1216 -0
  662. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +221 -0
  663. package/src/runtime/control-plane/facade/index.ts +16 -0
  664. package/src/runtime/control-plane/facade/operation-digest.ts +528 -0
  665. package/src/runtime/control-plane/facade/plan-contract-store.ts +403 -0
  666. package/src/runtime/control-plane/facade/policy-gate.ts +149 -0
  667. package/src/runtime/control-plane/facade/requirement-board.ts +280 -0
  668. package/src/runtime/control-plane/facade/self-healing-loop.ts +555 -0
  669. package/src/runtime/control-plane/facade/suggested-actions.ts +108 -0
  670. package/src/runtime/control-plane/facade/types.ts +729 -0
  671. package/src/runtime/control-plane/facade/work-admission-policy.ts +93 -0
  672. package/src/runtime/control-plane/facade/work-continuation.ts +104 -0
  673. package/src/runtime/control-plane/facade/work-contract-store.ts +1118 -0
  674. package/src/runtime/control-plane/facade/work-task-projection.ts +66 -0
  675. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +365 -0
  676. package/src/runtime/control-plane/global-scheduler/scheduler.ts +920 -0
  677. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +89 -0
  678. package/src/runtime/control-plane/goal-loop/config-facade.ts +700 -0
  679. package/src/runtime/control-plane/goal-loop/config-store.ts +570 -0
  680. package/src/runtime/control-plane/goal-loop/config-types.ts +213 -0
  681. package/src/runtime/control-plane/goal-loop/executor-router.ts +109 -0
  682. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +149 -0
  683. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +304 -0
  684. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +1001 -0
  685. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +160 -0
  686. package/src/runtime/control-plane/goal-loop/index.ts +13 -0
  687. package/src/runtime/control-plane/goal-loop/local-tools.ts +290 -0
  688. package/src/runtime/control-plane/goal-loop/policy-gates.ts +203 -0
  689. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +93 -0
  690. package/src/runtime/control-plane/goal-loop/provider-registry.ts +545 -0
  691. package/src/runtime/control-plane/goal-loop/types.ts +378 -0
  692. package/src/runtime/control-plane/governance/access-policy.ts +147 -0
  693. package/src/runtime/control-plane/governance/authorization.ts +195 -0
  694. package/src/runtime/control-plane/governance/external-effects.ts +43 -0
  695. package/src/runtime/control-plane/launcher/thin-launcher.ts +97 -0
  696. package/src/runtime/control-plane/persistence/metadata-payload-policy.ts +53 -0
  697. package/src/runtime/control-plane/persistence/requirement-store.ts +241 -0
  698. package/src/runtime/control-plane/persistence/sqlite-store.ts +539 -0
  699. package/src/runtime/control-plane/release-retention.ts +447 -0
  700. package/src/runtime/control-plane/repo-actor/actor.ts +181 -0
  701. package/src/runtime/control-plane/repo-actor/registry.ts +20 -0
  702. package/src/runtime/control-plane/routing/index.ts +1 -0
  703. package/src/runtime/control-plane/routing/route-policy.ts +379 -0
  704. package/src/runtime/control-plane/runtime-cleanup.ts +727 -0
  705. package/src/runtime/control-plane/runtime-generation.ts +509 -0
  706. package/src/runtime/control-plane/runtime-pid-ownership.ts +11 -0
  707. package/src/runtime/control-plane/runtime-status-client.ts +90 -0
  708. package/src/runtime/control-plane/startup-recovery.ts +198 -0
  709. package/src/runtime/diagnostics/connectivity-observability.ts +475 -0
  710. package/src/runtime/diagnostics/mcp-timing.ts +64 -0
  711. package/src/runtime/diagnostics/performance.ts +379 -0
  712. package/src/runtime/diagnostics/process-facade.ts +350 -0
  713. package/src/runtime/diagnostics/read-only-tool.ts +226 -0
  714. package/src/runtime/evidence/artifact-store.ts +154 -0
  715. package/src/runtime/evidence/event-ledger.ts +80 -0
  716. package/src/runtime/evidence/evidence-store.ts +68 -0
  717. package/src/runtime/evidence/process-check-receipt.ts +43 -0
  718. package/src/runtime/evidence/result-store.ts +209 -0
  719. package/src/runtime/evidence/sensitive-output.ts +353 -0
  720. package/src/runtime/execution/jobs/child-reference.ts +124 -0
  721. package/src/runtime/execution/jobs/legacy-adapter.ts +22 -0
  722. package/src/runtime/execution/jobs/receipt-recovery.ts +119 -0
  723. package/src/runtime/execution/jobs/receipt-store.ts +243 -0
  724. package/src/runtime/execution/jobs/store.ts +737 -0
  725. package/src/runtime/execution/jobs/timeouts.ts +109 -0
  726. package/src/runtime/execution/jobs/types.ts +230 -0
  727. package/src/runtime/execution/jobs/wait.ts +45 -0
  728. package/src/runtime/execution/process-runtime/canary.ts +26 -0
  729. package/src/runtime/execution/process-runtime/check-facade.ts +272 -0
  730. package/src/runtime/execution/process-runtime/check-receipt.ts +185 -0
  731. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +60 -0
  732. package/src/runtime/execution/process-runtime/command-facade.ts +321 -0
  733. package/src/runtime/execution/process-runtime/gc.ts +171 -0
  734. package/src/runtime/execution/process-runtime/index.ts +9 -0
  735. package/src/runtime/execution/process-runtime/interactive-admission.ts +23 -0
  736. package/src/runtime/execution/process-runtime/process-runner-entry.ts +466 -0
  737. package/src/runtime/execution/process-runtime/resource-claims.ts +440 -0
  738. package/src/runtime/execution/process-runtime/runtime.ts +1816 -0
  739. package/src/runtime/execution/process-runtime/store.ts +687 -0
  740. package/src/runtime/execution/process-runtime/types.ts +352 -0
  741. package/src/runtime/execution/thin-harness/async-process.ts +181 -0
  742. package/src/runtime/execution/thin-harness/batch-executor.ts +671 -0
  743. package/src/runtime/execution/thin-harness/execution-router.ts +700 -0
  744. package/src/runtime/execution/thin-harness/fast-executor.ts +1263 -0
  745. package/src/runtime/execution/thin-harness/fast-receipt.ts +235 -0
  746. package/src/runtime/execution/thin-harness/fingerprint-worker.ts +191 -0
  747. package/src/runtime/execution/thin-harness/index.ts +13 -0
  748. package/src/runtime/execution/thin-harness/latency-trace.ts +107 -0
  749. package/src/runtime/execution/thin-harness/lightweight-lanes.ts +745 -0
  750. package/src/runtime/execution/thin-harness/mutation-gate.ts +578 -0
  751. package/src/runtime/execution/thin-harness/proposal-store.ts +409 -0
  752. package/src/runtime/execution/thin-harness/request-ledger.ts +595 -0
  753. package/src/runtime/execution/thin-harness/search-worker.ts +114 -0
  754. package/src/runtime/execution/thin-harness/types.ts +313 -0
  755. package/src/runtime/execution/thin-harness/workspace-savepoint.ts +333 -0
  756. package/src/runtime/execution/workers/executor.ts +687 -0
  757. package/src/runtime/execution/workers/ownership.ts +94 -0
  758. package/src/runtime/execution/workers/worker-entry.ts +277 -0
  759. package/src/runtime/gateway/mcp/execution-tools.ts +2009 -0
  760. package/src/runtime/gateway/mcp/persisted-check-process.ts +255 -0
  761. package/src/runtime/gateway/mcp/process-tools.ts +230 -0
  762. package/src/runtime/gateway/mcp/resource-policy.ts +195 -0
  763. package/src/runtime/gateway/mcp/router.ts +1088 -0
  764. package/src/runtime/gateway/mcp/runtime-tools.ts +5994 -0
  765. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +219 -0
  766. package/src/runtime/health/evaluator.ts +474 -0
  767. package/src/runtime/health/index.ts +2 -0
  768. package/src/runtime/health/operational-view.ts +198 -0
  769. package/src/runtime/maintenance/cleanup.ts +419 -0
  770. package/src/runtime/model-clients/deepseek-adapter.ts +312 -0
  771. package/src/runtime/model-clients/index.ts +2 -0
  772. package/src/runtime/model-clients/types.ts +101 -0
  773. package/src/runtime/observability/gateway-contention-metrics.ts +129 -0
  774. package/src/runtime/observability/route-session-benchmark.ts +110 -0
  775. package/src/runtime/personal-assistant/gmail-triage-manager.ts +231 -0
  776. package/src/runtime/personal-assistant/reporting-runtime.ts +344 -0
  777. package/src/runtime/personal-assistant/triage-runtime.ts +748 -0
  778. package/src/runtime/plugins/app-store-connect-adapter.ts +868 -0
  779. package/src/runtime/plugins/browser-adapter.ts +2812 -0
  780. package/src/runtime/plugins/browser-automation-service.ts +46 -0
  781. package/src/runtime/plugins/browser-handoff-host.ts +304 -0
  782. package/src/runtime/plugins/browser-handoff.ts +371 -0
  783. package/src/runtime/plugins/browser-macos-bridge.ts +865 -0
  784. package/src/runtime/plugins/browser-node-bridge-host.ts +64 -0
  785. package/src/runtime/plugins/browser-node-bridge.ts +249 -0
  786. package/src/runtime/plugins/desktop-adapter.ts +374 -0
  787. package/src/runtime/plugins/desktop-operator-registration.ts +418 -0
  788. package/src/runtime/plugins/errors.ts +39 -0
  789. package/src/runtime/plugins/execution-origin.ts +26 -0
  790. package/src/runtime/plugins/external-adapter.ts +322 -0
  791. package/src/runtime/plugins/external-registration.ts +308 -0
  792. package/src/runtime/plugins/external-unix-socket-probe.cjs +79 -0
  793. package/src/runtime/plugins/external-unix-socket.ts +238 -0
  794. package/src/runtime/plugins/first-party-registry.ts +37 -0
  795. package/src/runtime/plugins/github-adapter.ts +266 -0
  796. package/src/runtime/plugins/gmail-adapter.ts +729 -0
  797. package/src/runtime/plugins/google-calendar-adapter.ts +468 -0
  798. package/src/runtime/plugins/google-shared.ts +578 -0
  799. package/src/runtime/plugins/google-tasks-adapter.ts +535 -0
  800. package/src/runtime/plugins/interaction-session.ts +252 -0
  801. package/src/runtime/plugins/ios/agent-device-capabilities.ts +258 -0
  802. package/src/runtime/plugins/ios/agent-device-failures.ts +95 -0
  803. package/src/runtime/plugins/ios/agent-device-provider.ts +63 -0
  804. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +268 -0
  805. package/src/runtime/plugins/ios/app-adapters.ts +122 -0
  806. package/src/runtime/plugins/ios-adapter.ts +509 -0
  807. package/src/runtime/plugins/ios-agent-device.ts +2311 -0
  808. package/src/runtime/plugins/ios-physical-device.ts +955 -0
  809. package/src/runtime/plugins/local-system-adapter.ts +1016 -0
  810. package/src/runtime/plugins/macos-capability-broker.ts +73 -0
  811. package/src/runtime/plugins/managed-process-adapter.ts +366 -0
  812. package/src/runtime/plugins/resend-adapter.ts +506 -0
  813. package/src/runtime/plugins/store.ts +1025 -0
  814. package/src/runtime/plugins/types.ts +138 -0
  815. package/src/runtime/projections/controller-context.ts +596 -0
  816. package/src/runtime/projections/git-status-sampler.ts +84 -0
  817. package/src/runtime/projections/invalidation.ts +250 -0
  818. package/src/runtime/projections/materialized-view.ts +694 -0
  819. package/src/runtime/recovery/actions.ts +158 -0
  820. package/src/runtime/recovery/audit.ts +38 -0
  821. package/src/runtime/recovery/classifier.ts +163 -0
  822. package/src/runtime/recovery/index.ts +9 -0
  823. package/src/runtime/recovery/local-jobs-repair.ts +393 -0
  824. package/src/runtime/recovery/maintenance-executor.ts +822 -0
  825. package/src/runtime/recovery/patch-handoff.ts +50 -0
  826. package/src/runtime/recovery/probe.ts +308 -0
  827. package/src/runtime/recovery/store.ts +35 -0
  828. package/src/runtime/recovery/types.ts +175 -0
  829. package/src/runtime/release/identity.ts +101 -0
  830. package/src/runtime/release/release-gate.ts +137 -0
  831. package/src/runtime/resources/claims/conflicts.ts +205 -0
  832. package/src/runtime/resources/index.ts +1 -0
  833. package/src/runtime/resources/leases/store.ts +539 -0
  834. package/src/runtime/resources/leases/types.ts +50 -0
  835. package/src/runtime/resources/ownership.ts +72 -0
  836. package/src/runtime/root/controller-services.ts +42 -0
  837. package/src/runtime/root/entry.ts +70 -0
  838. package/src/runtime/root/gateway-adapter.ts +46 -0
  839. package/src/runtime/root/local-bridge.ts +72 -0
  840. package/src/runtime/root/mcp-transport.ts +198 -0
  841. package/src/runtime/root/ownership.ts +94 -0
  842. package/src/runtime/root/readiness.ts +69 -0
  843. package/src/runtime/root/release-manifest.ts +147 -0
  844. package/src/runtime/root/release-materialize.ts +509 -0
  845. package/src/runtime/root/release-store.ts +236 -0
  846. package/src/runtime/root/runtime.ts +318 -0
  847. package/src/runtime/root/scheduler.ts +58 -0
  848. package/src/runtime/root/service-runner-entry.ts +20 -0
  849. package/src/runtime/root/service-runner.ts +69 -0
  850. package/src/runtime/root/service.ts +291 -0
  851. package/src/runtime/root/status.ts +127 -0
  852. package/src/runtime/root/types.ts +112 -0
  853. package/src/runtime/root/write-fence.ts +309 -0
  854. package/src/runtime/safe-tooling/browser-safe.ts +133 -0
  855. package/src/runtime/safe-tooling/external-filesystem.ts +304 -0
  856. package/src/runtime/safe-tooling/google-credential-store.ts +95 -0
  857. package/src/runtime/safe-tooling/google-oauth-broker.ts +286 -0
  858. package/src/runtime/safe-tooling/index.ts +11 -0
  859. package/src/runtime/safe-tooling/ios-development.ts +1039 -0
  860. package/src/runtime/safe-tooling/job-summary.ts +361 -0
  861. package/src/runtime/safe-tooling/plugin-summary.ts +53 -0
  862. package/src/runtime/safe-tooling/resend-oauth.ts +387 -0
  863. package/src/runtime/safe-tooling/review-artifacts.ts +133 -0
  864. package/src/runtime/safe-tooling/types.ts +103 -0
  865. package/src/runtime/safe-tooling/workspace-auth.ts +116 -0
  866. package/src/runtime/shared/bounded-child-supervisor.ts +235 -0
  867. package/src/runtime/shared/idempotent-retry.ts +128 -0
  868. package/src/runtime/shared/json-files.ts +43 -0
  869. package/src/runtime/shared/local-bridge-surface.ts +231 -0
  870. package/src/runtime/shared/managed-env.ts +154 -0
  871. package/src/runtime/shared/node-ts-loader.mjs +50 -0
  872. package/src/runtime/shared/process-environment.ts +97 -0
  873. package/src/runtime/shared/process-identity.ts +72 -0
  874. package/src/runtime/shared/process-tree.ts +227 -0
  875. package/src/runtime/shared/response-budget.ts +246 -0
  876. package/src/runtime/standalone-recovery/core.ts +2275 -0
  877. package/src/runtime/standalone-recovery/entry.ts +686 -0
  878. package/src/runtime/standalone-recovery/http-transport.ts +317 -0
  879. package/src/runtime/standalone-recovery/installer.ts +747 -0
  880. package/src/runtime/standalone-recovery/release.ts +241 -0
  881. package/src/runtime/watchdog/workflow-watchdog.ts +118 -0
  882. package/src/runtime/workflow/campaigns/cleanup.ts +263 -0
  883. package/src/runtime/workflow/campaigns/engine.ts +112 -0
  884. package/src/runtime/workflow/campaigns/normalize.ts +100 -0
  885. package/src/runtime/workflow/campaigns/review.ts +315 -0
  886. package/src/runtime/workflow/campaigns/store.ts +526 -0
  887. package/src/runtime/workflow/campaigns/supervisor.ts +112 -0
  888. package/src/runtime/workflow/campaigns/types.ts +290 -0
  889. package/src/runtime/workflow/campaigns/workspace-agent.ts +155 -0
  890. package/src/runtime/workflow/campaigns/workspace.ts +200 -0
  891. package/src/runtime/workflow/findings/store.ts +165 -0
  892. package/src/runtime/workflow/findings/types.ts +36 -0
  893. package/src/runtime/workflow/portfolio/engine.ts +20 -0
  894. package/src/runtime/workflow/portfolio/store.ts +185 -0
  895. package/src/runtime/workflow/portfolio/types.ts +29 -0
  896. package/src/runtime/workflow/schedules/engine.ts +620 -0
  897. package/src/runtime/workflow/schedules/settlement.ts +144 -0
  898. package/src/runtime/workflow/schedules/store.ts +417 -0
  899. package/src/runtime/workflow/schedules/types.ts +124 -0
  900. package/src/runtime/workspace-targets/index.ts +24 -0
  901. package/src/runtime/workspace-targets/target-grant.ts +721 -0
  902. package/src/testing/test-governance.ts +626 -0
  903. package/src/version.ts +21 -0
@@ -0,0 +1,1730 @@
1
+ #!/bin/bash
2
+ # Shared workflow state helpers for plan/todo/contract-aware hooks.
3
+
4
+ WORKFLOW_CHANGED_PATHS=""
5
+ WORKFLOW_CHANGED_PATHS_READY=0
6
+
7
+ workflow_strip_quotes() {
8
+ local value="$1"
9
+ value="$(printf '%s' "$value" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')"
10
+ if [[ "$value" =~ ^\".*\"$ ]]; then
11
+ value="${value:1:${#value}-2}"
12
+ elif [[ "$value" =~ ^\'.*\'$ ]]; then
13
+ value="${value:1:${#value}-2}"
14
+ fi
15
+ printf '%s' "$value"
16
+ }
17
+
18
+ workflow_json_escape() {
19
+ local value="$1"
20
+ value="${value//\\/\\\\}"
21
+ value="${value//\"/\\\"}"
22
+ value="${value//$'\n'/\\n}"
23
+ value="${value//$'\r'/\\r}"
24
+ value="${value//$'\t'/\\t}"
25
+ printf '%s' "$value"
26
+ }
27
+
28
+ workflow_policy_file() {
29
+ printf '.ai/harness/policy.json'
30
+ }
31
+
32
+ workflow_policy_get() {
33
+ local jq_path="$1"
34
+ local default_value="${2:-}"
35
+ local policy_file value
36
+
37
+ policy_file="$(workflow_policy_file)"
38
+ if [[ -f "$policy_file" ]] && command -v jq >/dev/null 2>&1; then
39
+ value="$(jq -r "$jq_path // empty" "$policy_file" 2>/dev/null || true)"
40
+ if [[ -n "$value" ]]; then
41
+ printf '%s' "$value"
42
+ return 0
43
+ fi
44
+ fi
45
+
46
+ printf '%s' "$default_value"
47
+ }
48
+
49
+ workflow_repo_relative_path() {
50
+ local value="$1"
51
+ local default_value="$2"
52
+ local allowed_prefix="${3:-}"
53
+
54
+ if [[ -z "$value" || "$value" == /* || "$value" == *$'\n'* || "$value" == *$'\r'* ]]; then
55
+ printf '%s' "$default_value"
56
+ return 0
57
+ fi
58
+
59
+ case "$value" in
60
+ ..|../*|*/..|*/../*)
61
+ printf '%s' "$default_value"
62
+ ;;
63
+ *)
64
+ if [[ -n "$allowed_prefix" && "$value" != "$allowed_prefix"* ]]; then
65
+ printf '%s' "$default_value"
66
+ return 0
67
+ fi
68
+ printf '%s' "$value"
69
+ ;;
70
+ esac
71
+ }
72
+
73
+ workflow_context_map_file() {
74
+ workflow_repo_relative_path "$(workflow_policy_get '.context.map_file' '.ai/context/context-map.json')" '.ai/context/context-map.json' '.ai/context/'
75
+ }
76
+
77
+ workflow_failure_log_file() {
78
+ workflow_repo_relative_path "$(workflow_policy_get '.harness.failure_log_file' '.ai/harness/failures/latest.jsonl')" '.ai/harness/failures/latest.jsonl' '.ai/harness/'
79
+ }
80
+
81
+ workflow_events_file() {
82
+ workflow_repo_relative_path "$(workflow_policy_get '.harness.events_file' '.ai/harness/events.jsonl')" '.ai/harness/events.jsonl' '.ai/harness/'
83
+ }
84
+
85
+ workflow_trace_file() {
86
+ printf '%s' ".claude/.trace.jsonl"
87
+ }
88
+
89
+ workflow_runs_dir() {
90
+ workflow_repo_relative_path "$(workflow_policy_get '.harness.runs_dir' '.ai/harness/runs')" '.ai/harness/runs' '.ai/harness/'
91
+ }
92
+
93
+ workflow_resume_packet_file() {
94
+ workflow_repo_relative_path "$(workflow_policy_get '.handoff_resume.resume_packet_file' '.ai/harness/handoff/resume.md')" '.ai/harness/handoff/resume.md' '.ai/harness/'
95
+ }
96
+
97
+ workflow_pending_orchestration_file() {
98
+ workflow_repo_relative_path "$(workflow_policy_get '.planning.pending_orchestration_file' '.ai/harness/planning/pending.json')" '.ai/harness/planning/pending.json' '.ai/harness/'
99
+ }
100
+
101
+ workflow_ensure_harness_surface() {
102
+ mkdir -p \
103
+ "tasks/notes" \
104
+ "$(dirname "$(workflow_context_map_file)")" \
105
+ "$(dirname "$(workflow_policy_file)")" \
106
+ "$(dirname "$(workflow_checks_file)")" \
107
+ "$(dirname "$(workflow_handoff_file)")" \
108
+ "$(dirname "$(workflow_resume_packet_file)")" \
109
+ "$(dirname "$(workflow_failure_log_file)")" \
110
+ "$(dirname "$(workflow_pending_orchestration_file)")" \
111
+ "$(workflow_runs_dir)"
112
+
113
+ [[ -f "$(workflow_checks_file)" ]] || printf "{}\n" > "$(workflow_checks_file)"
114
+ [[ -f "$(workflow_handoff_file)" ]] || printf "# Harness Handoff\n\n> **Reason**: bootstrap\n" > "$(workflow_handoff_file)"
115
+ [[ -f "$(workflow_resume_packet_file)" ]] || printf "# Codex Resume Packet\n\n> **Reason**: bootstrap\n" > "$(workflow_resume_packet_file)"
116
+ [[ -f "$(workflow_failure_log_file)" ]] || : > "$(workflow_failure_log_file)"
117
+ [[ -f "$(workflow_events_file)" ]] || : > "$(workflow_events_file)"
118
+ }
119
+
120
+ is_git_repo() {
121
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1
122
+ }
123
+
124
+ load_changed_paths() {
125
+ if [[ "$WORKFLOW_CHANGED_PATHS_READY" -eq 1 ]]; then
126
+ return
127
+ fi
128
+
129
+ WORKFLOW_CHANGED_PATHS_READY=1
130
+ if ! is_git_repo; then
131
+ return
132
+ fi
133
+
134
+ WORKFLOW_CHANGED_PATHS="$(
135
+ git status --porcelain=v1 --untracked-files=no 2>/dev/null \
136
+ | awk '{
137
+ path = substr($0, 4)
138
+ rename_idx = index(path, " -> ")
139
+ if (rename_idx > 0) {
140
+ path = substr(path, rename_idx + 4)
141
+ }
142
+ print path
143
+ }'
144
+ )"
145
+ }
146
+
147
+ has_changes() {
148
+ local file="$1"
149
+
150
+ load_changed_paths
151
+
152
+ if [[ -n "$WORKFLOW_CHANGED_PATHS" ]] && printf '%s\n' "$WORKFLOW_CHANGED_PATHS" | grep -Fxq -- "$file"; then
153
+ return 0
154
+ fi
155
+ return 1
156
+ }
157
+
158
+ has_changes_glob() {
159
+ local pattern="$1"
160
+ local changed
161
+
162
+ load_changed_paths
163
+
164
+ changed="$(printf '%s\n' "$WORKFLOW_CHANGED_PATHS" | grep -E "$pattern" | head -1)"
165
+
166
+ if [[ -n "$changed" ]]; then
167
+ printf '%s' "$changed"
168
+ return 0
169
+ fi
170
+ return 1
171
+ }
172
+
173
+ get_latest_plan() {
174
+ local latest
175
+ latest="$(find plans -maxdepth 1 -type f -name 'plan-*.md' 2>/dev/null | sort | tail -1)"
176
+ if [[ -n "$latest" ]]; then
177
+ printf '%s' "$latest"
178
+ return 0
179
+ fi
180
+ return 1
181
+ }
182
+
183
+ ACTIVE_PLAN_MARKER=".ai/harness/active-plan"
184
+ LEGACY_ACTIVE_PLAN_MARKER=".claude/.active-plan"
185
+ ACTIVE_WORKTREE_MARKER=".ai/harness/active-worktree"
186
+
187
+ # Records why get_active_plan returned empty. Empty string means the marker
188
+ # state was clean. Possible values:
189
+ # "deleted" - marker exists but points at a plan file that no longer exists
190
+ # "foreign" - active-worktree marker is owned by a different worktree
191
+ # Hooks can inspect this to differentiate "no marker at all" (hard error,
192
+ # user has not started a plan) from "marker is stale" (advisory + self-heal,
193
+ # the marker rotted underneath the user).
194
+ ACTIVE_PLAN_MARKER_STALE_REASON=""
195
+
196
+ read_active_plan_marker() {
197
+ local marker_file="$1"
198
+ local marker_plan
199
+
200
+ if [[ -f "$marker_file" ]]; then
201
+ marker_plan="$(cat "$marker_file" 2>/dev/null | xargs)"
202
+ if [[ -n "$marker_plan" ]]; then
203
+ if [[ -f "$marker_plan" ]]; then
204
+ printf '%s' "$marker_plan"
205
+ return 0
206
+ fi
207
+ ACTIVE_PLAN_MARKER_STALE_REASON="deleted"
208
+ fi
209
+ fi
210
+
211
+ return 1
212
+ }
213
+
214
+ active_plan_marker_matches_cwd() {
215
+ local owner current
216
+
217
+ [[ -f "$ACTIVE_WORKTREE_MARKER" ]] || return 0
218
+ owner="$(cat "$ACTIVE_WORKTREE_MARKER" 2>/dev/null | head -n 1 | xargs)"
219
+ [[ -n "$owner" ]] || return 0
220
+
221
+ current="$(pwd -P 2>/dev/null)"
222
+ [[ -n "$current" ]] || return 0
223
+
224
+ if [[ "$owner" != "$current" ]]; then
225
+ ACTIVE_PLAN_MARKER_STALE_REASON="foreign"
226
+ return 1
227
+ fi
228
+ return 0
229
+ }
230
+
231
+ get_active_plan() {
232
+ ACTIVE_PLAN_MARKER_STALE_REASON=""
233
+
234
+ if ! active_plan_marker_matches_cwd; then
235
+ return 1
236
+ fi
237
+
238
+ read_active_plan_marker "$ACTIVE_PLAN_MARKER" \
239
+ || read_active_plan_marker "$LEGACY_ACTIVE_PLAN_MARKER"
240
+ }
241
+
242
+ write_active_plan_marker() {
243
+ local plan_file="$1"
244
+ mkdir -p "$(dirname "$ACTIVE_PLAN_MARKER")" "$(dirname "$LEGACY_ACTIVE_PLAN_MARKER")" "$(dirname "$ACTIVE_WORKTREE_MARKER")"
245
+ printf '%s' "$plan_file" > "$ACTIVE_PLAN_MARKER"
246
+ printf '%s' "$plan_file" > "$LEGACY_ACTIVE_PLAN_MARKER"
247
+ pwd -P > "$ACTIVE_WORKTREE_MARKER"
248
+ }
249
+
250
+ clear_active_plan_marker() {
251
+ rm -f "$ACTIVE_PLAN_MARKER" "$LEGACY_ACTIVE_PLAN_MARKER" "$ACTIVE_WORKTREE_MARKER"
252
+ }
253
+
254
+ set_active_plan() {
255
+ local plan_file="$1"
256
+ write_active_plan_marker "$plan_file"
257
+ }
258
+
259
+ clear_active_plan() {
260
+ clear_active_plan_marker
261
+ }
262
+
263
+ get_plan_status() {
264
+ local plan_file="$1"
265
+ awk '/\*\*Status\*\*:/ {sub(/^.*\*\*Status\*\*: */, ""); gsub(/\r/, ""); print; exit}' "$plan_file" | xargs
266
+ }
267
+
268
+ get_todo_source_plan() {
269
+ if [[ ! -f "tasks/todos.md" ]]; then
270
+ return 1
271
+ fi
272
+
273
+ awk -F': ' '/^\> \*\*Source Plan\*\*:/ {print $2; exit}' tasks/todos.md | xargs
274
+ }
275
+
276
+ workflow_plan_slug_from_path() {
277
+ local plan_file="$1"
278
+ local base slug
279
+
280
+ base="$(basename "$plan_file")"
281
+ slug="$(printf '%s' "$base" | sed -E 's/^plan-[0-9]{8}-[0-9]{4}-//; s/\.md$//')"
282
+
283
+ if [[ -z "$slug" ]] || [[ "$slug" == "$base" ]]; then
284
+ return 1
285
+ fi
286
+
287
+ printf '%s' "$slug"
288
+ }
289
+
290
+ workflow_plan_original_artifact_stem_from_path() {
291
+ local plan_file="$1"
292
+ local base stem
293
+
294
+ base="$(basename "$plan_file")"
295
+ stem="$(printf '%s' "$base" | sed -E 's/^plan-//; s/\.md$//')"
296
+ if [[ "$stem" =~ ^[0-9]{8}-[0-9]{4}-.+ ]]; then
297
+ printf '%s' "$stem"
298
+ return 0
299
+ fi
300
+
301
+ workflow_plan_slug_from_path "$plan_file"
302
+ }
303
+
304
+ workflow_is_transient_plan_slug() {
305
+ case "$1" in
306
+ think-plan-[0-9]*|codex-plan-[0-9]*|approved-plan-[0-9]*)
307
+ return 0
308
+ ;;
309
+ esac
310
+ return 1
311
+ }
312
+
313
+ workflow_plan_title_slug_from_file() {
314
+ local plan_file="$1"
315
+ local title slug
316
+
317
+ [[ -f "$plan_file" ]] || return 1
318
+ title="$(awk '
319
+ /^# Plan:[[:space:]]*/ {
320
+ sub(/^# Plan:[[:space:]]*/, "")
321
+ print
322
+ exit
323
+ }
324
+ ' "$plan_file" | xargs)"
325
+ [[ -n "$title" ]] || return 1
326
+
327
+ slug="$(printf '%s' "$title" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//; s/-{2,}/-/g')"
328
+ [[ -n "$slug" ]] || return 1
329
+ printf '%s' "$slug"
330
+ }
331
+
332
+ workflow_plan_artifact_stem_from_path() {
333
+ local plan_file="$1"
334
+ local stem stamp slug title_slug
335
+
336
+ stem="$(workflow_plan_original_artifact_stem_from_path "$plan_file" || true)"
337
+ if [[ "$stem" =~ ^[0-9]{8}-[0-9]{4}-.+ ]]; then
338
+ stamp="$(printf '%s' "$stem" | sed -E 's/^([0-9]{8}-[0-9]{4})-.+$/\1/')"
339
+ slug="$(printf '%s' "$stem" | sed -E 's/^[0-9]{8}-[0-9]{4}-//')"
340
+ if workflow_is_transient_plan_slug "$slug"; then
341
+ title_slug="$(workflow_plan_title_slug_from_file "$plan_file" || true)"
342
+ if [[ -n "$title_slug" && "$title_slug" != "$slug" ]]; then
343
+ printf '%s-%s' "$stamp" "$title_slug"
344
+ return 0
345
+ fi
346
+ fi
347
+ printf '%s' "$stem"
348
+ return 0
349
+ fi
350
+
351
+ workflow_plan_slug_from_path "$plan_file"
352
+ }
353
+
354
+ workflow_preferred_or_legacy_path() {
355
+ local preferred="$1"
356
+ local legacy="$2"
357
+
358
+ if [[ -f "$preferred" ]] || [[ ! -f "$legacy" ]]; then
359
+ printf '%s' "$preferred"
360
+ else
361
+ printf '%s' "$legacy"
362
+ fi
363
+ }
364
+
365
+ workflow_plan_declared_path() {
366
+ local plan_file="$1"
367
+ local label="$2"
368
+ [[ -f "$plan_file" ]] || return 1
369
+ awk -v label="$label" '
370
+ BEGIN { pattern = "^> \\*\\*" label "\\*\\*:" }
371
+ $0 ~ pattern {
372
+ sub(pattern "[[:space:]]*", "")
373
+ gsub(/`/, "")
374
+ gsub(/\r/, "")
375
+ print
376
+ exit
377
+ }
378
+ ' "$plan_file" | xargs
379
+ }
380
+
381
+ derive_contract_path() {
382
+ local plan_file="$1"
383
+ local stem slug explicit
384
+
385
+ explicit="$(workflow_plan_declared_path "$plan_file" "Task Contract" || workflow_plan_declared_path "$plan_file" "Sprint Contract" || true)"
386
+ if [[ -n "$explicit" ]]; then
387
+ printf '%s' "$explicit"
388
+ return 0
389
+ fi
390
+
391
+ stem="$(workflow_plan_artifact_stem_from_path "$plan_file" || true)"
392
+ slug="$(workflow_plan_slug_from_path "$plan_file" || true)"
393
+ [[ -n "$stem" && -n "$slug" ]] || return 1
394
+
395
+ workflow_preferred_or_legacy_path "tasks/contracts/${stem}.contract.md" "tasks/contracts/${slug}.contract.md"
396
+ }
397
+
398
+ workflow_plan_slug() {
399
+ local active_plan slug
400
+ active_plan="$(get_active_plan || true)"
401
+ if [[ -z "$active_plan" ]]; then
402
+ return 1
403
+ fi
404
+
405
+ slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
406
+ if [[ -n "$slug" ]]; then
407
+ printf '%s' "$slug"
408
+ return 0
409
+ fi
410
+ return 1
411
+ }
412
+
413
+ workflow_todo_total() {
414
+ if [[ ! -f "tasks/todos.md" ]]; then
415
+ printf '0'
416
+ return
417
+ fi
418
+
419
+ grep -E '^[[:space:]]*-[[:space:]]\[[ xX]\][[:space:]]+' tasks/todos.md | wc -l | tr -d ' '
420
+ }
421
+
422
+ workflow_todo_done() {
423
+ if [[ ! -f "tasks/todos.md" ]]; then
424
+ printf '0'
425
+ return
426
+ fi
427
+
428
+ grep -E '^[[:space:]]*-[[:space:]]\[[xX]\][[:space:]]+' tasks/todos.md | wc -l | tr -d ' '
429
+ }
430
+
431
+ workflow_is_linked_worktree() {
432
+ local git_dir
433
+ git_dir="$(git rev-parse --git-dir 2>/dev/null || true)"
434
+ [[ "$git_dir" == *".git/worktrees/"* ]]
435
+ }
436
+
437
+ workflow_current_branch() {
438
+ git branch --show-current 2>/dev/null || true
439
+ }
440
+
441
+ workflow_target_branch() {
442
+ local target
443
+ target="$(workflow_policy_get '.worktree_strategy.merge_back.target' '')"
444
+ if [[ -z "$target" ]]; then
445
+ target="$(workflow_policy_get '.worktree_strategy.base_branch' 'main')"
446
+ fi
447
+ printf '%s' "${target:-main}"
448
+ }
449
+
450
+ workflow_branch_prefix() {
451
+ workflow_policy_get '.worktree_strategy.branch_prefix' 'codex/'
452
+ }
453
+
454
+ workflow_find_worktree_for_branch() {
455
+ local branch="$1"
456
+ git worktree list --porcelain 2>/dev/null | awk -v branch_ref="refs/heads/${branch}" '
457
+ $1 == "worktree" { path = $2; next }
458
+ $1 == "branch" && $2 == branch_ref { print path; exit }
459
+ '
460
+ }
461
+
462
+ workflow_branch_exists() {
463
+ local branch="$1"
464
+ git show-ref --verify --quiet "refs/heads/$branch"
465
+ }
466
+
467
+ workflow_branch_merged_to_target() {
468
+ local branch="$1"
469
+ local target="$2"
470
+ [[ -n "$branch" && -n "$target" ]] || return 1
471
+ workflow_branch_exists "$branch" || return 1
472
+ git rev-parse --verify --quiet "$target" >/dev/null 2>&1 || return 1
473
+ git merge-base --is-ancestor "$branch" "$target" >/dev/null 2>&1
474
+ }
475
+
476
+ workflow_iterate_plan_tasks() {
477
+ local plan_file="${1:-}"
478
+ [[ -n "$plan_file" && -f "$plan_file" ]] || return 0
479
+
480
+ awk '
481
+ BEGIN { in_section = 0; task_index = 0 }
482
+ /^## Task Breakdown[[:space:]]*$/ { in_section = 1; next }
483
+ in_section && /^## / { exit }
484
+ in_section && /^[[:space:]]*-[[:space:]]\[[ xX]\][[:space:]]+/ {
485
+ task_index += 1
486
+ status = ($0 ~ /\[[xX]\]/) ? "completed" : "pending"
487
+ desc = $0
488
+ sub(/^[[:space:]]*-[[:space:]]\[[ xX]\][[:space:]]+/, "", desc)
489
+ gsub(/\r/, "", desc)
490
+ print task_index "\t" status "\t" desc
491
+ }
492
+ ' "$plan_file"
493
+ }
494
+
495
+ workflow_plan_task_state_from_stream() {
496
+ local total=0
497
+ local done=0
498
+ local next_pending=""
499
+ local idx status desc
500
+
501
+ while IFS=$'\t' read -r idx status desc; do
502
+ [[ -n "$idx" ]] || continue
503
+ total=$((total + 1))
504
+ if [[ "$status" == "completed" ]]; then
505
+ done=$((done + 1))
506
+ elif [[ -z "$next_pending" ]]; then
507
+ next_pending="$desc"
508
+ fi
509
+ done
510
+
511
+ printf '%s\t%s\t%s\n' "$total" "$done" "$next_pending"
512
+ }
513
+
514
+ workflow_plan_task_state() {
515
+ local plan_file="${1:-}"
516
+ local state total done next_pending
517
+
518
+ if [[ -z "$plan_file" ]]; then
519
+ plan_file="$(get_active_plan || true)"
520
+ fi
521
+
522
+ if [[ -n "$plan_file" && -f "$plan_file" ]]; then
523
+ state="$(workflow_iterate_plan_tasks "$plan_file" | workflow_plan_task_state_from_stream)"
524
+ IFS=$'\t' read -r total done next_pending <<< "$state"
525
+ if [[ "${total:-0}" -gt 0 ]]; then
526
+ printf '%s\n' "$state"
527
+ return 0
528
+ fi
529
+ fi
530
+
531
+ # Legacy compatibility only: current repositories keep execution in the
532
+ # active plan, but older generated repos may still carry a todo checklist.
533
+ if [[ -f "tasks/todos.md" ]] && ! grep -Eq '^> \*\*Status\*\*:[[:space:]]*Backlog[[:space:]]*$' tasks/todos.md; then
534
+ workflow_iterate_todo_tasks "tasks/todos.md" | workflow_plan_task_state_from_stream
535
+ return 0
536
+ fi
537
+
538
+ printf '0\t0\t\n'
539
+ }
540
+
541
+ workflow_cleanup_candidate() {
542
+ local target branch_prefix branch worktree slug metadata
543
+ target="$(workflow_target_branch)"
544
+ branch_prefix="$(workflow_branch_prefix)"
545
+
546
+ if is_git_repo; then
547
+ while IFS=$'\t' read -r branch worktree; do
548
+ [[ -n "$branch" ]] || continue
549
+ slug="${branch#${branch_prefix}}"
550
+ if workflow_branch_merged_to_target "$branch" "$target"; then
551
+ printf '%s\t%s\t%s\n' "$slug" "$branch" "$worktree"
552
+ return 0
553
+ fi
554
+ done < <(
555
+ git worktree list --porcelain 2>/dev/null | awk -v prefix="refs/heads/${branch_prefix}" '
556
+ $1 == "worktree" { path = $2; next }
557
+ $1 == "branch" && index($2, prefix) == 1 {
558
+ branch = substr($2, length("refs/heads/") + 1)
559
+ print branch "\t" path
560
+ }
561
+ '
562
+ )
563
+
564
+ while IFS= read -r branch; do
565
+ [[ -n "$branch" ]] || continue
566
+ slug="${branch#${branch_prefix}}"
567
+ if workflow_branch_merged_to_target "$branch" "$target"; then
568
+ printf '%s\t%s\t\n' "$slug" "$branch"
569
+ return 0
570
+ fi
571
+ done < <(git for-each-ref --format='%(refname:short)' "refs/heads/${branch_prefix}" 2>/dev/null || true)
572
+ fi
573
+
574
+ for metadata in .ai/harness/worktrees/*.json; do
575
+ [[ -e "$metadata" ]] || continue
576
+ slug="$(basename "$metadata" .json)"
577
+ [[ -n "$slug" ]] || continue
578
+ printf '%s\t%s\t\n' "$slug" "${branch_prefix}${slug}"
579
+ return 0
580
+ done
581
+
582
+ return 1
583
+ }
584
+
585
+ workflow_next_action() {
586
+ local active_plan task_state total done next_pending contract_file review_file checks_file checks_error
587
+ local external_status external_state external_reviewer external_source external_message expected_source
588
+ local target current_branch slug candidate branch worktree command message
589
+
590
+ active_plan="$(get_active_plan || true)"
591
+ if [[ -n "$active_plan" && -f "$active_plan" ]]; then
592
+ task_state="$(workflow_plan_task_state "$active_plan")"
593
+ IFS=$'\t' read -r total done next_pending <<< "$task_state"
594
+ total="${total:-0}"
595
+ done="${done:-0}"
596
+
597
+ if [[ "$total" -gt "$done" ]]; then
598
+ message="${next_pending:-continue active plan Task Breakdown}"
599
+ message="If a major module was just completed, stage its coherent diff first; then continue the next Task Breakdown item: ${message}"
600
+ printf 'task\t-\t%s\n' "$message"
601
+ return 0
602
+ fi
603
+
604
+ contract_file="$(workflow_active_contract || true)"
605
+ review_file="$(workflow_active_review || true)"
606
+ checks_file="$(workflow_checks_file)"
607
+
608
+ if [[ -z "$review_file" || ! -f "$review_file" ]]; then
609
+ printf 'check\t/check\tStage the completed module diff first; then run /check and record a sprint review before finishing this worktree.\n'
610
+ return 0
611
+ fi
612
+
613
+ if ! workflow_review_recommends_pass "$review_file"; then
614
+ printf 'check\t/check\tStage the completed module diff first; then run /check until %s records Recommendation: pass.\n' "$review_file"
615
+ return 0
616
+ fi
617
+
618
+ if [[ -z "$contract_file" || ! -f "$contract_file" ]]; then
619
+ printf 'check\t/check\tStage the completed module diff first; then regenerate the active sprint contract and run /check.\n'
620
+ return 0
621
+ fi
622
+
623
+ external_status="$(workflow_external_acceptance_status "$review_file")"
624
+ IFS=$'\t' read -r external_state external_reviewer external_source external_message <<< "$external_status"
625
+ if [[ "$external_state" != "pass" && "$external_state" != "manual_override" ]]; then
626
+ expected_source="$(workflow_external_acceptance_expected_source)"
627
+ printf 'check\t/check\tStage the completed module diff first; then %s Run external acceptance via %s and record ## External Acceptance Advice in %s.\n' "${external_message:-External acceptance is missing.}" "$expected_source" "$review_file"
628
+ return 0
629
+ fi
630
+
631
+ if [[ ! -f "$checks_file" ]]; then
632
+ printf 'check\t/check\tStage the completed module diff first; then run /check and verify-sprint so %s exists.\n' "$checks_file"
633
+ return 0
634
+ fi
635
+
636
+ if ! checks_error="$(workflow_checks_pass "$checks_file" "$contract_file" "$review_file")"; then
637
+ printf 'check\t/check\tStage the completed module diff first; then resolve check evidence: %s\n' "$checks_error"
638
+ return 0
639
+ fi
640
+
641
+ target="$(workflow_target_branch)"
642
+ current_branch="$(workflow_current_branch)"
643
+ if workflow_is_linked_worktree && [[ -n "$current_branch" && "$current_branch" != "$target" ]]; then
644
+ printf 'finish\tbash scripts/contract-worktree.sh finish\tReview/checks pass; finish and fast-forward merge this contract worktree.\n'
645
+ return 0
646
+ fi
647
+ fi
648
+
649
+ if candidate="$(workflow_cleanup_candidate)"; then
650
+ IFS=$'\t' read -r slug branch worktree <<< "$candidate"
651
+ target="$(workflow_target_branch)"
652
+ command="bash scripts/contract-worktree.sh cleanup --slug ${slug} --target ${target}"
653
+ printf 'cleanup\t%s\tClean up merged contract worktree %s.\n' "$command" "${branch:-$slug}"
654
+ return 0
655
+ fi
656
+
657
+ printf 'none\t-\t(none)\n'
658
+ }
659
+
660
+ workflow_task_state_file() {
661
+ printf '.claude/.task-state.json'
662
+ }
663
+
664
+ workflow_read_state_field() {
665
+ local state_file="$1"
666
+ local field="$2"
667
+ local value=""
668
+
669
+ if [[ ! -f "$state_file" ]]; then
670
+ return 1
671
+ fi
672
+
673
+ if command -v jq >/dev/null 2>&1; then
674
+ value="$(jq -r ".$field // empty" "$state_file" 2>/dev/null || true)"
675
+ else
676
+ value="$(
677
+ awk -v field="$field" '
678
+ $0 ~ "\"" field "\"" {
679
+ line = $0
680
+ sub(/^[^:]*:[[:space:]]*/, "", line)
681
+ sub(/[[:space:]]*,?[[:space:]]*$/, "", line)
682
+ gsub(/^"/, "", line)
683
+ gsub(/"$/, "", line)
684
+ print line
685
+ exit
686
+ }
687
+ ' "$state_file"
688
+ )"
689
+ fi
690
+
691
+ [[ -n "$value" ]] || return 1
692
+ printf '%s' "$value"
693
+ }
694
+
695
+ workflow_iterate_todo_tasks() {
696
+ local todo_file="${1:-tasks/todos.md}"
697
+ [[ -f "$todo_file" ]] || return 0
698
+
699
+ awk '
700
+ BEGIN { task_index = 0 }
701
+ /^[[:space:]]*-[[:space:]]\[[ xX]\][[:space:]]+/ {
702
+ task_index += 1
703
+ status = ($0 ~ /\[[xX]\]/) ? "completed" : "pending"
704
+ desc = $0
705
+ sub(/^[[:space:]]*-[[:space:]]\[[ xX]\][[:space:]]+/, "", desc)
706
+ gsub(/\r/, "", desc)
707
+ print task_index "\t" status "\t" desc
708
+ }
709
+ ' "$todo_file"
710
+ }
711
+
712
+ workflow_sync_task_state_from_todo() {
713
+ local todo_file="${1:-tasks/todos.md}"
714
+ local state_file="${2:-.claude/.task-state.json}"
715
+ local source_plan="${3:-}"
716
+ local run_id="${HOOK_RUN_ID:-${CLAUDE_RUN_ID:-${CODEX_RUN_ID:-}}}"
717
+ local timestamp
718
+ local tmp_state
719
+ local total=0
720
+ local done=0
721
+ local promoted_in_progress=0
722
+ local idx status desc next_status passes first=1
723
+
724
+ if [[ -z "$source_plan" ]]; then
725
+ source_plan="$(get_todo_source_plan || true)"
726
+ fi
727
+
728
+ mkdir -p "$(dirname "$state_file")"
729
+ timestamp="$(date '+%Y-%m-%dT%H:%M:%S%z')"
730
+ if [[ -z "$run_id" ]]; then
731
+ run_id="run-$(date '+%Y%m%dT%H%M%S')-$$"
732
+ fi
733
+
734
+ {
735
+ echo "{"
736
+ printf ' "done_tasks": 0,\n'
737
+ printf ' "total_tasks": 0,\n'
738
+ printf ' "source_plan": "%s",\n' "$(workflow_json_escape "${source_plan:-}")"
739
+ printf ' "run_id": "%s",\n' "$(workflow_json_escape "$run_id")"
740
+ printf ' "updated_at": "%s",\n' "$(workflow_json_escape "$timestamp")"
741
+ echo ' "tasks": ['
742
+
743
+ while IFS=$'\t' read -r idx status desc; do
744
+ [[ -n "$idx" ]] || continue
745
+ total=$((total + 1))
746
+ next_status="$status"
747
+ if [[ "$status" == "completed" ]]; then
748
+ done=$((done + 1))
749
+ elif [[ "$promoted_in_progress" -eq 0 ]]; then
750
+ next_status="in_progress"
751
+ promoted_in_progress=1
752
+ fi
753
+
754
+ if [[ "$next_status" == "completed" ]]; then
755
+ passes="true"
756
+ else
757
+ passes="false"
758
+ fi
759
+
760
+ if [[ "$first" -eq 0 ]]; then
761
+ echo ","
762
+ fi
763
+ first=0
764
+
765
+ printf ' {"id":"task-%s","desc":"%s","status":"%s","passes":%s,"verification_evidence":[]}' \
766
+ "$idx" \
767
+ "$(workflow_json_escape "$desc")" \
768
+ "$next_status" \
769
+ "$passes"
770
+ done < <(workflow_iterate_todo_tasks "$todo_file")
771
+
772
+ echo
773
+ echo " ]"
774
+ echo "}"
775
+ } > "$state_file"
776
+
777
+ tmp_state="$(mktemp)"
778
+ awk -v done="$done" -v total="$total" '
779
+ {
780
+ if ($0 ~ /"done_tasks":/) {
781
+ printf " \"done_tasks\": %s,\n", done
782
+ } else if ($0 ~ /"total_tasks":/) {
783
+ printf " \"total_tasks\": %s,\n", total
784
+ } else {
785
+ print
786
+ }
787
+ }
788
+ ' "$state_file" > "$tmp_state"
789
+ mv "$tmp_state" "$state_file"
790
+ }
791
+
792
+ workflow_read_file_mtime() {
793
+ local file="$1"
794
+ [[ -e "$file" ]] || return 1
795
+
796
+ if stat -f '%m' "$file" >/dev/null 2>&1; then
797
+ stat -f '%m' "$file"
798
+ return 0
799
+ fi
800
+
801
+ stat -c '%Y' "$file"
802
+ }
803
+
804
+ workflow_pending_orchestration_field() {
805
+ local field="$1"
806
+ local pending_file value
807
+ pending_file="$(workflow_pending_orchestration_file)"
808
+ [[ -f "$pending_file" ]] || return 1
809
+
810
+ if command -v jq >/dev/null 2>&1; then
811
+ value="$(jq -r ".$field // empty" "$pending_file" 2>/dev/null || true)"
812
+ else
813
+ value="$(
814
+ awk -v field="$field" '
815
+ $0 ~ "\"" field "\"" {
816
+ line = $0
817
+ sub(/^[^:]*:[[:space:]]*/, "", line)
818
+ sub(/[[:space:]]*,?[[:space:]]*$/, "", line)
819
+ gsub(/^"/, "", line)
820
+ gsub(/"$/, "", line)
821
+ print line
822
+ exit
823
+ }
824
+ ' "$pending_file"
825
+ )"
826
+ fi
827
+
828
+ [[ -n "$value" && "$value" != "null" ]] || return 1
829
+ printf '%s' "$value"
830
+ }
831
+
832
+ workflow_write_pending_orchestration() {
833
+ local kind="${1:-host-plan}"
834
+ local host="${2:-${HOOK_HOST:-unknown}}"
835
+ local prompt_slug="${3:-planning}"
836
+ local draft_plan_path="${4:-}"
837
+ local source_ref="${5:-}"
838
+ local expected_artifact="${6:-plan}"
839
+ local pending_file timestamp cwd
840
+
841
+ pending_file="$(workflow_pending_orchestration_file)"
842
+ timestamp="$(date '+%Y-%m-%dT%H:%M:%S%z')"
843
+ cwd="$(pwd -P 2>/dev/null || pwd)"
844
+ mkdir -p "$(dirname "$pending_file")"
845
+
846
+ cat > "$pending_file" <<EOF_PENDING_ORCHESTRATION
847
+ {
848
+ "version": 1,
849
+ "kind": "$(workflow_json_escape "$kind")",
850
+ "host": "$(workflow_json_escape "$host")",
851
+ "prompt_slug": "$(workflow_json_escape "$prompt_slug")",
852
+ "draft_plan_path": "$(workflow_json_escape "$draft_plan_path")",
853
+ "source_ref": "$(workflow_json_escape "$source_ref")",
854
+ "expected_artifact": "$(workflow_json_escape "$expected_artifact")",
855
+ "cwd": "$(workflow_json_escape "$cwd")",
856
+ "created_at": "$(workflow_json_escape "$timestamp")"
857
+ }
858
+ EOF_PENDING_ORCHESTRATION
859
+ }
860
+
861
+ workflow_clear_pending_orchestration() {
862
+ rm -f "$(workflow_pending_orchestration_file)"
863
+ }
864
+
865
+ workflow_pending_orchestration_is_fresh() {
866
+ local max_age="${1:-259200}"
867
+ local pending_file mtime now age draft_path status
868
+ pending_file="$(workflow_pending_orchestration_file)"
869
+ [[ -s "$pending_file" ]] || return 1
870
+
871
+ mtime="$(workflow_read_file_mtime "$pending_file" 2>/dev/null || true)"
872
+ now="$(date +%s)"
873
+ if [[ -n "$mtime" ]]; then
874
+ age=$((now - mtime))
875
+ [[ "$age" -le "$max_age" ]] && return 0
876
+ fi
877
+
878
+ draft_path="$(workflow_pending_orchestration_field draft_plan_path 2>/dev/null || true)"
879
+ if [[ -n "$draft_path" && -f "$draft_path" ]]; then
880
+ status="$(get_plan_status "$draft_path" | tr '[:upper:]' '[:lower:]')"
881
+ case "$status" in
882
+ draft|annotating|"")
883
+ [[ -n "$mtime" ]] && [[ "$((now - mtime))" -le 604800 ]] && return 0
884
+ ;;
885
+ esac
886
+ fi
887
+
888
+ return 1
889
+ }
890
+
891
+ workflow_pending_orchestration_summary() {
892
+ local kind host prompt_slug draft_path source_ref expected_artifact cwd
893
+
894
+ kind="$(workflow_pending_orchestration_field kind 2>/dev/null || true)"
895
+ host="$(workflow_pending_orchestration_field host 2>/dev/null || true)"
896
+ prompt_slug="$(workflow_pending_orchestration_field prompt_slug 2>/dev/null || true)"
897
+ draft_path="$(workflow_pending_orchestration_field draft_plan_path 2>/dev/null || true)"
898
+ source_ref="$(workflow_pending_orchestration_field source_ref 2>/dev/null || true)"
899
+ expected_artifact="$(workflow_pending_orchestration_field expected_artifact 2>/dev/null || true)"
900
+ cwd="$(workflow_pending_orchestration_field cwd 2>/dev/null || true)"
901
+
902
+ printf 'kind=%s host=%s expected=%s slug=%s' "${kind:-unknown}" "${host:-unknown}" "${expected_artifact:-plan}" "${prompt_slug:-planning}"
903
+ [[ -n "$draft_path" ]] && printf ' draft=%s' "$draft_path"
904
+ [[ -n "$source_ref" ]] && printf ' source_ref=%s' "$source_ref"
905
+ [[ -n "$cwd" ]] && printf ' cwd=%s' "$cwd"
906
+ printf '\n'
907
+ }
908
+
909
+ latest_research_report() {
910
+ local research_dir="${1:-docs/researches}"
911
+ local file mtime latest_file="" latest_mtime=0
912
+
913
+ [[ -d "$research_dir" ]] || return 1
914
+
915
+ while IFS= read -r -d '' file; do
916
+ case "$(basename "$file")" in
917
+ README.md)
918
+ continue
919
+ ;;
920
+ esac
921
+
922
+ mtime="$(workflow_read_file_mtime "$file" || true)"
923
+ if [[ -n "$mtime" && "$mtime" -gt "$latest_mtime" ]]; then
924
+ latest_mtime="$mtime"
925
+ latest_file="$file"
926
+ fi
927
+ done < <(find "$research_dir" -type f -name '*.md' -print0 2>/dev/null)
928
+
929
+ [[ -n "$latest_file" ]] || return 1
930
+ printf '%s' "$latest_file"
931
+ }
932
+
933
+ has_research_for_new_plan() {
934
+ local latest_plan latest_report research_mtime plan_mtime
935
+
936
+ latest_report="$(latest_research_report || true)"
937
+ [[ -n "$latest_report" ]] || return 1
938
+
939
+ latest_plan="$(get_latest_plan || true)"
940
+ if [[ -z "$latest_plan" ]]; then
941
+ return 0
942
+ fi
943
+
944
+ research_mtime="$(workflow_read_file_mtime "$latest_report" || true)"
945
+ plan_mtime="$(workflow_read_file_mtime "$latest_plan" || true)"
946
+
947
+ [[ -n "$research_mtime" && -n "$plan_mtime" && "$research_mtime" -gt "$plan_mtime" ]]
948
+ }
949
+
950
+ workflow_extract_status_from_text() {
951
+ local text="${1:-}"
952
+ printf '%s' "$text" | awk '/\*\*Status\*\*:/ {sub(/^.*\*\*Status\*\*: */, ""); gsub(/\r/, ""); print; exit}' | xargs
953
+ }
954
+
955
+ workflow_plan_note_count_in_text() {
956
+ local text="${1:-}"
957
+ printf '%s\n' "$text" | grep -c '\[NOTE\]:' || true
958
+ }
959
+
960
+ workflow_plan_note_count() {
961
+ local plan_file="$1"
962
+ [[ -f "$plan_file" ]] || { printf '0'; return; }
963
+ grep -c '\[NOTE\]:' "$plan_file" || true
964
+ }
965
+
966
+ validate_plan_transition() {
967
+ local current_status="$1"
968
+ local next_status="$2"
969
+ local note_count="$3"
970
+
971
+ case "${current_status}:${next_status}" in
972
+ Draft:Annotating)
973
+ if [[ "$note_count" -lt 1 ]]; then
974
+ echo "Draft -> Annotating requires at least one [NOTE]: annotation."
975
+ return 1
976
+ fi
977
+ ;;
978
+ Annotating:Approved)
979
+ if [[ "$note_count" -gt 0 ]]; then
980
+ echo "Annotating -> Approved requires all [NOTE]: annotations to be resolved."
981
+ return 1
982
+ fi
983
+ ;;
984
+ Annotating:Draft)
985
+ echo "[PlanState] Rollback: Annotating -> Draft (plan direction rethink)."
986
+ return 0
987
+ ;;
988
+ Draft:Approved|Draft:Executing|Annotating:Executing)
989
+ echo "Status jump ${current_status} -> ${next_status} skips required workflow gates."
990
+ return 1
991
+ ;;
992
+ Approved:Draft|Approved:Annotating|Executing:Draft|Executing:Annotating|Executing:Approved)
993
+ echo "Backward transition ${current_status} -> ${next_status} is not allowed."
994
+ return 1
995
+ ;;
996
+ esac
997
+
998
+ return 0
999
+ }
1000
+
1001
+ read_contract_status() {
1002
+ local file="$1"
1003
+ awk '/^\> \*\*Status\*\*:/ {sub(/^.*\> \*\*Status\*\*: */, ""); gsub(/\r/, ""); print; exit}' "$file" | xargs
1004
+ }
1005
+
1006
+ contract_references_path() {
1007
+ local contract_file="$1"
1008
+ local file_path="$2"
1009
+ local yaml_block section pending_path trimmed item
1010
+
1011
+ [[ -f "$contract_file" ]] || return 1
1012
+ [[ "$file_path" == "$contract_file" ]] && return 0
1013
+
1014
+ yaml_block="$(
1015
+ awk '
1016
+ BEGIN { in_block = 0; printed = 0 }
1017
+ /^```yaml[[:space:]]*$/ && printed == 0 { in_block = 1; next }
1018
+ /^```[[:space:]]*$/ && in_block == 1 { printed = 1; in_block = 0; exit }
1019
+ in_block == 1 { print }
1020
+ ' "$contract_file"
1021
+ )"
1022
+
1023
+ section=""
1024
+ pending_path=""
1025
+
1026
+ while IFS= read -r line; do
1027
+ trimmed="$(printf '%s' "$line" | sed -E 's/[[:space:]]+$//; s/^[[:space:]]+//')"
1028
+ [[ -z "$trimmed" ]] && continue
1029
+
1030
+ case "$trimmed" in
1031
+ files_exist:|tests_pass:|files_contain:|files_not_exist:|files_not_contain:)
1032
+ section="${trimmed%:}"
1033
+ pending_path=""
1034
+ continue
1035
+ ;;
1036
+ esac
1037
+
1038
+ case "$section" in
1039
+ files_exist|files_not_exist)
1040
+ if [[ "$trimmed" =~ ^-[[:space:]]*(.+)$ ]]; then
1041
+ item="$(workflow_strip_quotes "${BASH_REMATCH[1]}")"
1042
+ [[ "$item" == "$file_path" ]] && return 0
1043
+ fi
1044
+ ;;
1045
+ tests_pass|files_contain|files_not_contain)
1046
+ if [[ "$trimmed" =~ ^-[[:space:]]*path:[[:space:]]*(.+)$ ]]; then
1047
+ pending_path="$(workflow_strip_quotes "${BASH_REMATCH[1]}")"
1048
+ [[ "$pending_path" == "$file_path" ]] && return 0
1049
+ elif [[ "$trimmed" =~ ^path:[[:space:]]*(.+)$ ]]; then
1050
+ pending_path="$(workflow_strip_quotes "${BASH_REMATCH[1]}")"
1051
+ [[ "$pending_path" == "$file_path" ]] && return 0
1052
+ fi
1053
+ ;;
1054
+ esac
1055
+ done <<< "$yaml_block"
1056
+
1057
+ return 1
1058
+ }
1059
+
1060
+ workflow_contract_slug() {
1061
+ local active_plan slug
1062
+ active_plan="$(get_active_plan || true)"
1063
+ [[ -n "$active_plan" ]] || return 1
1064
+ slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
1065
+ [[ -n "$slug" ]] || return 1
1066
+ printf '%s' "$slug"
1067
+ }
1068
+
1069
+ workflow_active_contract() {
1070
+ local active_plan contract_file
1071
+ active_plan="$(get_active_plan || true)"
1072
+ [[ -n "$active_plan" ]] || return 1
1073
+ contract_file="$(derive_contract_path "$active_plan" || true)"
1074
+ [[ -n "$contract_file" ]] || return 1
1075
+ printf '%s' "$contract_file"
1076
+ }
1077
+
1078
+ workflow_active_review() {
1079
+ local active_plan stem slug reviews_dir explicit
1080
+ active_plan="$(get_active_plan || true)"
1081
+ [[ -n "$active_plan" ]] || return 1
1082
+ explicit="$(workflow_plan_declared_path "$active_plan" "Task Review" || workflow_plan_declared_path "$active_plan" "Sprint Review" || true)"
1083
+ if [[ -n "$explicit" ]]; then
1084
+ printf '%s' "$explicit"
1085
+ return 0
1086
+ fi
1087
+ stem="$(workflow_plan_artifact_stem_from_path "$active_plan" || true)"
1088
+ slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
1089
+ [[ -n "$stem" && -n "$slug" ]] || return 1
1090
+ reviews_dir="$(workflow_repo_relative_path "$(workflow_policy_get '.tasks.reviews_dir' 'tasks/reviews')" 'tasks/reviews' 'tasks/')"
1091
+ workflow_preferred_or_legacy_path "${reviews_dir}/${stem}.review.md" "${reviews_dir}/${slug}.review.md"
1092
+ }
1093
+
1094
+ workflow_active_notes() {
1095
+ local active_plan stem slug notes_dir explicit
1096
+ active_plan="$(get_active_plan || true)"
1097
+ [[ -n "$active_plan" ]] || return 1
1098
+ explicit="$(workflow_plan_declared_path "$active_plan" "Implementation Notes" || workflow_plan_declared_path "$active_plan" "Notes File" || true)"
1099
+ if [[ -n "$explicit" ]]; then
1100
+ printf '%s' "$explicit"
1101
+ return 0
1102
+ fi
1103
+ stem="$(workflow_plan_artifact_stem_from_path "$active_plan" || true)"
1104
+ slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
1105
+ [[ -n "$stem" && -n "$slug" ]] || return 1
1106
+ notes_dir="$(workflow_repo_relative_path "$(workflow_policy_get '.tasks.notes_dir' 'tasks/notes')" 'tasks/notes' 'tasks/')"
1107
+ workflow_preferred_or_legacy_path "${notes_dir}/${stem}.notes.md" "${notes_dir}/${slug}.notes.md"
1108
+ }
1109
+
1110
+ workflow_checks_file() {
1111
+ workflow_repo_relative_path "$(workflow_policy_get '.harness.checks_file' '.ai/harness/checks/latest.json')" '.ai/harness/checks/latest.json' '.ai/harness/'
1112
+ }
1113
+
1114
+ workflow_handoff_file() {
1115
+ workflow_repo_relative_path "$(workflow_policy_get '.harness.handoff_file' '.ai/harness/handoff/current.md')" '.ai/harness/handoff/current.md' '.ai/harness/'
1116
+ }
1117
+
1118
+ # mkdir-based mutual exclusion (macOS ships no flock). Spins ~2s, breaks locks
1119
+ # older than 60s (crashed holder), and as a last resort runs the command
1120
+ # unlocked rather than wedging an advisory hook.
1121
+ workflow_with_lock() {
1122
+ local name="$1"
1123
+ shift
1124
+ local lock_root lock_dir waited=0 now mtime status=0
1125
+ lock_root="$(dirname "$(workflow_events_file)")/.locks"
1126
+ lock_dir="$lock_root/${name}.lock"
1127
+ if ! mkdir -p "$lock_root" 2>/dev/null; then
1128
+ "$@" || status=$?
1129
+ return "$status"
1130
+ fi
1131
+
1132
+ while ! mkdir "$lock_dir" 2>/dev/null; do
1133
+ if [[ "$waited" -ge 40 ]]; then
1134
+ now="$(date +%s)"
1135
+ mtime="$(stat -c '%Y' "$lock_dir" 2>/dev/null || stat -f '%m' "$lock_dir" 2>/dev/null || echo 0)"
1136
+ if [[ "${mtime:-0}" =~ ^[0-9]+$ && "${mtime:-0}" -gt 0 && $((now - mtime)) -ge 60 ]]; then
1137
+ rmdir "$lock_dir" 2>/dev/null || true
1138
+ waited=0
1139
+ continue
1140
+ fi
1141
+ "$@" || status=$?
1142
+ return "$status"
1143
+ fi
1144
+ sleep 0.05
1145
+ waited=$((waited + 1))
1146
+ done
1147
+
1148
+ "$@" || status=$?
1149
+ rmdir "$lock_dir" 2>/dev/null || true
1150
+ return "$status"
1151
+ }
1152
+
1153
+ workflow_locked_append_line() {
1154
+ printf '%s\n' "$2" >> "$1"
1155
+ }
1156
+
1157
+ workflow_locked_increment_file() {
1158
+ local file="$1" value
1159
+ value="$(cat "$file" 2>/dev/null | tr -cd '0-9')"
1160
+ value=$(( ${value:-0} + 1 ))
1161
+ printf '%s\n' "$value" > "$file"
1162
+ printf '%s' "$value"
1163
+ }
1164
+
1165
+ # Atomic read-increment-write for small counter files (concurrent PostToolUse
1166
+ # hooks used to lose increments via unlocked read-modify-write).
1167
+ workflow_increment_counter() {
1168
+ local file="$1"
1169
+ workflow_with_lock "counter-$(basename "$file")" workflow_locked_increment_file "$file"
1170
+ }
1171
+
1172
+ # Rotate an events JSONL file once it exceeds limits. Cold-path only (session
1173
+ # start); holds the same lock as appends so no event line is lost mid-rotate.
1174
+ workflow_rotate_events_file() {
1175
+ local file="$1" max_lines="${2:-2000}" max_bytes="${3:-524288}" keep="${4:-500}"
1176
+ [[ -f "$file" ]] || return 0
1177
+ local lines bytes
1178
+ lines="$(wc -l < "$file" 2>/dev/null | tr -cd '0-9')"
1179
+ bytes="$(wc -c < "$file" 2>/dev/null | tr -cd '0-9')"
1180
+ if [[ "${lines:-0}" -le "$max_lines" && "${bytes:-0}" -le "$max_bytes" ]]; then
1181
+ return 0
1182
+ fi
1183
+ [[ "${lines:-0}" -gt "$keep" ]] || return 0
1184
+ workflow_with_lock "evt-$(basename "$file")" workflow_rotate_events_file_locked "$file" "$lines" "$keep"
1185
+ }
1186
+
1187
+ workflow_rotate_events_file_locked() {
1188
+ local file="$1" lines="$2" keep="$3"
1189
+ local archive_dir archive_file tmp cut
1190
+ archive_dir="$(dirname "$file")/archive"
1191
+ archive_file="$archive_dir/$(basename "$file" .jsonl)-$(date '+%Y%m').jsonl"
1192
+ cut=$((lines - keep))
1193
+ mkdir -p "$archive_dir" 2>/dev/null || return 0
1194
+ tmp="$(mktemp 2>/dev/null)" || return 0
1195
+ if head -n "$cut" "$file" >> "$archive_file" 2>/dev/null && tail -n "$keep" "$file" > "$tmp" 2>/dev/null; then
1196
+ mv "$tmp" "$file"
1197
+ echo "[WorkflowState] Rotated $(basename "$file"): archived $cut lines to $archive_file" >&2
1198
+ else
1199
+ rm -f "$tmp"
1200
+ fi
1201
+ }
1202
+
1203
+ workflow_append_event() {
1204
+ local event_type="$1"
1205
+ local reason="${2:-}"
1206
+ local extra_json="${3:-{}}"
1207
+ local events_file run_id line=""
1208
+
1209
+ workflow_ensure_harness_surface
1210
+ events_file="$(workflow_events_file)"
1211
+ run_id="${HOOK_RUN_ID:-${CLAUDE_RUN_ID:-${CODEX_RUN_ID:-run-$(date '+%Y%m%dT%H%M%S')-$$}}}"
1212
+
1213
+ if command -v jq >/dev/null 2>&1; then
1214
+ line="$(jq -nc \
1215
+ --arg ts "$(date '+%Y-%m-%dT%H:%M:%S%z')" \
1216
+ --arg event_type "$event_type" \
1217
+ --arg reason "$reason" \
1218
+ --arg run_id "$run_id" \
1219
+ --arg extra_json "$extra_json" \
1220
+ '{
1221
+ ts: $ts,
1222
+ event_type: $event_type,
1223
+ reason: $reason,
1224
+ run_id: $run_id,
1225
+ extra: (try ($extra_json | fromjson) catch {})
1226
+ }')"
1227
+ else
1228
+ line="$(printf '{"ts":"%s","event_type":"%s","reason":"%s","run_id":"%s"}' \
1229
+ "$(workflow_json_escape "$(date '+%Y-%m-%dT%H:%M:%S%z')")" \
1230
+ "$(workflow_json_escape "$event_type")" \
1231
+ "$(workflow_json_escape "$reason")" \
1232
+ "$(workflow_json_escape "$run_id")")"
1233
+ fi
1234
+
1235
+ [[ -n "$line" ]] || return 0
1236
+ workflow_with_lock "evt-$(basename "$events_file")" workflow_locked_append_line "$events_file" "$line"
1237
+ }
1238
+
1239
+ workflow_write_run_summary() {
1240
+ local reason="${1:-state-update}"
1241
+ local run_id active_plan active_contract active_review active_notes output_file
1242
+
1243
+ workflow_ensure_harness_surface
1244
+ run_id="${HOOK_RUN_ID:-${CLAUDE_RUN_ID:-${CODEX_RUN_ID:-run-$(date '+%Y%m%dT%H%M%S')-$$}}}"
1245
+ active_plan="$(get_active_plan || true)"
1246
+ active_contract="$(workflow_active_contract || true)"
1247
+ active_review="$(workflow_active_review || true)"
1248
+ active_notes="$(workflow_active_notes || true)"
1249
+ output_file="$(workflow_runs_dir)/${run_id}.json"
1250
+
1251
+ if command -v jq >/dev/null 2>&1; then
1252
+ jq -nc \
1253
+ --arg generated_at "$(date '+%Y-%m-%dT%H:%M:%S%z')" \
1254
+ --arg run_id "$run_id" \
1255
+ --arg reason "$reason" \
1256
+ --arg active_plan "${active_plan:-}" \
1257
+ --arg active_contract "${active_contract:-}" \
1258
+ --arg active_review "${active_review:-}" \
1259
+ --arg active_notes "${active_notes:-}" \
1260
+ --arg checks_file "$(workflow_checks_file)" \
1261
+ --arg handoff_file "$(workflow_handoff_file)" \
1262
+ --arg policy_file "$(workflow_policy_file)" \
1263
+ --arg context_map_file "$(workflow_context_map_file)" \
1264
+ '{
1265
+ generated_at: $generated_at,
1266
+ run_id: $run_id,
1267
+ reason: $reason,
1268
+ active_plan: $active_plan,
1269
+ active_contract: $active_contract,
1270
+ active_review: $active_review,
1271
+ active_notes: $active_notes,
1272
+ checks_file: $checks_file,
1273
+ handoff_file: $handoff_file,
1274
+ policy_file: $policy_file,
1275
+ context_map_file: $context_map_file
1276
+ }' > "$output_file"
1277
+ return 0
1278
+ fi
1279
+
1280
+ cat > "$output_file" <<EOF_RUN
1281
+ {"generated_at":"$(workflow_json_escape "$(date '+%Y-%m-%dT%H:%M:%S%z')")","run_id":"$(workflow_json_escape "$run_id")","reason":"$(workflow_json_escape "$reason")","checks_file":"$(workflow_json_escape "$(workflow_checks_file)")","handoff_file":"$(workflow_json_escape "$(workflow_handoff_file)")"}
1282
+ EOF_RUN
1283
+ }
1284
+
1285
+ workflow_review_recommends_pass() {
1286
+ local review_file="${1:-}"
1287
+ [[ -n "$review_file" && -f "$review_file" ]] || return 1
1288
+ grep -Eq '^> \*\*Recommendation\*\*:[[:space:]]*pass[[:space:]]*$' "$review_file"
1289
+ }
1290
+
1291
+ workflow_external_acceptance_expected_reviewer() {
1292
+ local host="${HOOK_HOST:-}"
1293
+
1294
+ if [[ -z "$host" ]]; then
1295
+ if [[ -n "${CODEX_RUN_ID:-}${CODEX_SESSION_ID:-}${CODEX_THREAD_ID:-}${CODEX_SHELL:-}${CODEX_INTERNAL_ORIGINATOR_OVERRIDE:-}" ]]; then
1296
+ host="codex"
1297
+ elif [[ -n "${CLAUDE_RUN_ID:-}${CLAUDE_SESSION_ID:-}" ]]; then
1298
+ host="claude"
1299
+ else
1300
+ host="claude"
1301
+ fi
1302
+ fi
1303
+
1304
+ if [[ "$host" == "codex" ]]; then
1305
+ printf 'Claude'
1306
+ else
1307
+ printf 'Codex'
1308
+ fi
1309
+ }
1310
+
1311
+ workflow_external_acceptance_source_for_reviewer() {
1312
+ local reviewer="${1:-}"
1313
+ case "$(printf '%s' "$reviewer" | tr '[:upper:]' '[:lower:]')" in
1314
+ claude) printf 'claude-review' ;;
1315
+ *) printf 'codex-review' ;;
1316
+ esac
1317
+ }
1318
+
1319
+ workflow_external_acceptance_expected_source() {
1320
+ local reviewer="${1:-}"
1321
+ reviewer="${reviewer:-$(workflow_external_acceptance_expected_reviewer)}"
1322
+ workflow_external_acceptance_source_for_reviewer "$reviewer"
1323
+ }
1324
+
1325
+ workflow_external_acceptance_section() {
1326
+ local review_file="${1:-}"
1327
+ [[ -n "$review_file" && -f "$review_file" ]] || return 1
1328
+ awk '
1329
+ /^##[[:space:]]+External Acceptance Advice[[:space:]]*$/ { in_section = 1; next }
1330
+ /^##[[:space:]]+/ && in_section { exit }
1331
+ in_section { print }
1332
+ ' "$review_file"
1333
+ }
1334
+
1335
+ workflow_external_acceptance_field() {
1336
+ local section="${1:-}"
1337
+ local label="${2:-}"
1338
+ printf '%s\n' "$section" |
1339
+ sed -nE "s/^> \\*\\*${label}\\*\\*:[[:space:]]*([^[:space:]].*)[[:space:]]*$/\\1/p" |
1340
+ head -n 1 |
1341
+ sed -E 's/[[:space:]]+$//'
1342
+ }
1343
+
1344
+ workflow_external_acceptance_status() {
1345
+ local review_file="${1:-}"
1346
+ local expected_reviewer="${2:-}"
1347
+ local expected_source section acceptance reviewer source p1_blockers manual_override
1348
+ local acceptance_lc reviewer_lc source_lc expected_reviewer_lc expected_source_lc p1_lc
1349
+
1350
+ expected_reviewer="${expected_reviewer:-$(workflow_external_acceptance_expected_reviewer)}"
1351
+ expected_source="$(workflow_external_acceptance_expected_source "$expected_reviewer")"
1352
+
1353
+ if [[ -z "$review_file" || ! -f "$review_file" ]]; then
1354
+ printf 'missing\t-\t-\tExternal acceptance review file is missing: %s\n' "${review_file:-tasks/reviews/<slug>.review.md}"
1355
+ return 0
1356
+ fi
1357
+
1358
+ section="$(workflow_external_acceptance_section "$review_file" || true)"
1359
+ if [[ -z "$section" ]]; then
1360
+ printf 'missing\t-\t-\tExternal acceptance section is missing from %s.\n' "$review_file"
1361
+ return 0
1362
+ fi
1363
+
1364
+ acceptance="$(workflow_external_acceptance_field "$section" "External Acceptance")"
1365
+ reviewer="$(workflow_external_acceptance_field "$section" "External Reviewer")"
1366
+ source="$(workflow_external_acceptance_field "$section" "External Source")"
1367
+ p1_blockers="$(
1368
+ printf '%s\n' "$section" |
1369
+ sed -nE 's/^- P1 blockers:[[:space:]]*([^[:space:]].*)[[:space:]]*$/\1/p' |
1370
+ head -n 1 |
1371
+ sed -E 's/[[:space:]]+$//'
1372
+ )"
1373
+ manual_override="$(
1374
+ printf '%s\n' "$section" |
1375
+ sed -nE 's/^-?[[:space:]]*Manual Override:[[:space:]]*([^[:space:]].*)[[:space:]]*$/\1/p' |
1376
+ head -n 1 |
1377
+ sed -E 's/[[:space:]]+$//'
1378
+ )"
1379
+
1380
+ if [[ -n "$manual_override" ]]; then
1381
+ printf 'manual_override\t%s\t%s\tManual override recorded for external acceptance: %s\n' "${reviewer:--}" "${source:--}" "$manual_override"
1382
+ return 0
1383
+ fi
1384
+
1385
+ acceptance_lc="$(printf '%s' "$acceptance" | tr '[:upper:]' '[:lower:]')"
1386
+ reviewer_lc="$(printf '%s' "$reviewer" | tr '[:upper:]' '[:lower:]')"
1387
+ source_lc="$(printf '%s' "$source" | tr '[:upper:]' '[:lower:]')"
1388
+ expected_reviewer_lc="$(printf '%s' "$expected_reviewer" | tr '[:upper:]' '[:lower:]')"
1389
+ expected_source_lc="$(printf '%s' "$expected_source" | tr '[:upper:]' '[:lower:]')"
1390
+ p1_lc="$(printf '%s' "$p1_blockers" | tr '[:upper:]' '[:lower:]')"
1391
+
1392
+ if [[ "$acceptance_lc" != "pass" ]]; then
1393
+ printf 'fail\t%s\t%s\tExternal acceptance is %s; expected pass from %s via %s.\n' "${reviewer:--}" "${source:--}" "${acceptance:-missing}" "$expected_reviewer" "$expected_source"
1394
+ return 0
1395
+ fi
1396
+
1397
+ if [[ "$reviewer_lc" != "$expected_reviewer_lc" ]]; then
1398
+ printf 'fail\t%s\t%s\tExternal reviewer is %s; expected %s.\n' "${reviewer:--}" "${source:--}" "${reviewer:-missing}" "$expected_reviewer"
1399
+ return 0
1400
+ fi
1401
+
1402
+ if [[ "$source_lc" != "$expected_source_lc" ]]; then
1403
+ printf 'fail\t%s\t%s\tExternal source is %s; expected %s.\n' "${reviewer:--}" "${source:--}" "${source:-missing}" "$expected_source"
1404
+ return 0
1405
+ fi
1406
+
1407
+ if [[ "$p1_lc" != "none" ]]; then
1408
+ printf 'fail\t%s\t%s\tExternal acceptance has P1 blockers: %s\n' "${reviewer:--}" "${source:--}" "${p1_blockers:-missing}"
1409
+ return 0
1410
+ fi
1411
+
1412
+ printf 'pass\t%s\t%s\tExternal acceptance passed.\n' "$reviewer" "$source"
1413
+ }
1414
+
1415
+ workflow_external_acceptance_pass() {
1416
+ local review_file="${1:-}"
1417
+ local expected_reviewer="${2:-}"
1418
+ local row status
1419
+
1420
+ row="$(workflow_external_acceptance_status "$review_file" "$expected_reviewer")"
1421
+ status="${row%%$'\t'*}"
1422
+ [[ "$status" == "pass" || "$status" == "manual_override" ]]
1423
+ }
1424
+
1425
+ workflow_checks_pass() {
1426
+ local checks_file="${1:-}"
1427
+ local contract_file="${2:-}"
1428
+ local review_file="${3:-}"
1429
+ local status source exit_code check_contract check_review
1430
+
1431
+ if [[ -z "$checks_file" || ! -s "$checks_file" ]]; then
1432
+ echo "Structured checks file is missing or empty: ${checks_file:-"(none)"}"
1433
+ return 1
1434
+ fi
1435
+
1436
+ if command -v jq >/dev/null 2>&1; then
1437
+ status="$(jq -r '.status // empty' "$checks_file" 2>/dev/null || true)"
1438
+ source="$(jq -r '.source // empty' "$checks_file" 2>/dev/null || true)"
1439
+ exit_code="$(jq -r '.exit_code // empty' "$checks_file" 2>/dev/null || true)"
1440
+ check_contract="$(jq -r '.contract.file // .contract // empty' "$checks_file" 2>/dev/null || true)"
1441
+ check_review="$(jq -r '.review.file // .review // empty' "$checks_file" 2>/dev/null || true)"
1442
+
1443
+ if [[ "$status" != "pass" ]]; then
1444
+ echo "Structured checks are not passing in $checks_file (status=${status:-missing})."
1445
+ return 1
1446
+ fi
1447
+ if [[ "$source" != "verify-sprint" ]]; then
1448
+ echo "Structured checks must come from verify-sprint, got ${source:-missing}."
1449
+ return 1
1450
+ fi
1451
+ if [[ "$exit_code" != "0" ]]; then
1452
+ echo "Structured checks did not record a zero verify-sprint exit code (exit_code=${exit_code:-missing})."
1453
+ return 1
1454
+ fi
1455
+ if [[ -n "$contract_file" && "$check_contract" != "$contract_file" ]]; then
1456
+ echo "Structured checks are stale for contract ${check_contract:-missing}; expected $contract_file."
1457
+ return 1
1458
+ fi
1459
+ if [[ -n "$review_file" && "$check_review" != "$review_file" ]]; then
1460
+ echo "Structured checks are stale for review ${check_review:-missing}; expected $review_file."
1461
+ return 1
1462
+ fi
1463
+ return 0
1464
+ fi
1465
+
1466
+ if ! grep -Eq '"status"[[:space:]]*:[[:space:]]*"pass"' "$checks_file"; then
1467
+ echo "Structured checks are not passing in $checks_file."
1468
+ return 1
1469
+ fi
1470
+ if ! grep -Eq '"source"[[:space:]]*:[[:space:]]*"verify-sprint"' "$checks_file"; then
1471
+ echo "Structured checks must come from verify-sprint."
1472
+ return 1
1473
+ fi
1474
+ if ! grep -Eq '"exit_code"[[:space:]]*:[[:space:]]*0' "$checks_file"; then
1475
+ echo "Structured checks did not record a zero verify-sprint exit code."
1476
+ return 1
1477
+ fi
1478
+ if [[ -n "$contract_file" ]] && ! grep -Fq "\"file\":\"$contract_file\"" "$checks_file" && ! grep -Fq "\"file\": \"$contract_file\"" "$checks_file"; then
1479
+ echo "Structured checks do not reference current contract $contract_file."
1480
+ return 1
1481
+ fi
1482
+ if [[ -n "$review_file" ]] && ! grep -Fq "\"file\":\"$review_file\"" "$checks_file" && ! grep -Fq "\"file\": \"$review_file\"" "$checks_file"; then
1483
+ echo "Structured checks do not reference current review $review_file."
1484
+ return 1
1485
+ fi
1486
+ }
1487
+
1488
+ workflow_contract_allows_path() {
1489
+ local contract_file="$1"
1490
+ local file_path="$2"
1491
+ local yaml_block section trimmed item pattern
1492
+
1493
+ [[ -f "$contract_file" ]] || return 1
1494
+ [[ "$file_path" == "$contract_file" ]] && return 0
1495
+
1496
+ yaml_block="$(
1497
+ awk '
1498
+ BEGIN { in_block = 0; printed = 0 }
1499
+ /^```yaml[[:space:]]*$/ && printed == 0 { in_block = 1; next }
1500
+ /^```[[:space:]]*$/ && in_block == 1 { printed = 1; in_block = 0; exit }
1501
+ in_block == 1 { print }
1502
+ ' "$contract_file"
1503
+ )"
1504
+
1505
+ section=""
1506
+ while IFS= read -r line; do
1507
+ trimmed="$(printf '%s' "$line" | sed -E 's/[[:space:]]+$//; s/^[[:space:]]+//')"
1508
+ [[ -z "$trimmed" ]] && continue
1509
+
1510
+ case "$trimmed" in
1511
+ allowed_paths:)
1512
+ section="allowed_paths"
1513
+ continue
1514
+ ;;
1515
+ exit_criteria:|files_exist:|tests_pass:|commands_succeed:|files_contain:|artifacts_exist:|qa_scores:|manual_checks:)
1516
+ section=""
1517
+ continue
1518
+ ;;
1519
+ esac
1520
+
1521
+ if [[ "$section" == "allowed_paths" && "$trimmed" =~ ^-[[:space:]]*(.+)$ ]]; then
1522
+ item="$(workflow_strip_quotes "${BASH_REMATCH[1]}")"
1523
+ pattern="$item"
1524
+ if [[ "$pattern" == */ ]]; then
1525
+ [[ "$file_path" == "$pattern"* ]] && return 0
1526
+ elif [[ "$file_path" == $pattern ]]; then
1527
+ return 0
1528
+ fi
1529
+ fi
1530
+ done <<< "$yaml_block"
1531
+
1532
+ return 1
1533
+ }
1534
+ workflow_write_handoff() {
1535
+ local reason="${1:-session-stop}"
1536
+ local handoff_file active_plan active_contract active_review active_notes checks_file next_task changed_files diff_stat spec_file source_plan parent_run_id supersedes
1537
+ local next_action next_stage next_command next_message
1538
+ local resume_file trace_file recent_commands blockers decisions goal latest_trace_file
1539
+ local active_sprint active_sprint_row
1540
+ local changed_count untracked_count
1541
+
1542
+ workflow_ensure_harness_surface
1543
+ handoff_file="$(workflow_handoff_file)"
1544
+ checks_file="$(workflow_checks_file)"
1545
+ resume_file="$(workflow_resume_packet_file)"
1546
+ spec_file="docs/spec.md"
1547
+ active_plan="$(get_active_plan || true)"
1548
+ active_contract="$(workflow_active_contract || true)"
1549
+ active_review="$(workflow_active_review || true)"
1550
+ active_notes="$(workflow_active_notes || true)"
1551
+ active_sprint=""
1552
+ if [[ -f ".ai/harness/sprint/active-sprint" ]]; then
1553
+ active_sprint="$(cat ".ai/harness/sprint/active-sprint" 2>/dev/null | xargs)"
1554
+ fi
1555
+ active_sprint_row="(none)"
1556
+ if [[ -n "$active_sprint" && -f "$active_sprint" ]]; then
1557
+ active_sprint_row="$(
1558
+ awk -v plan="$active_plan" '
1559
+ /^\|[[:space:]]*[0-9]+[[:space:]]*\|/ {
1560
+ if (plan != "" && index($0, plan) > 0) {
1561
+ print
1562
+ found = 1
1563
+ exit
1564
+ }
1565
+ }
1566
+ END { if (!found) exit 1 }
1567
+ ' "$active_sprint" 2>/dev/null || true
1568
+ )"
1569
+ active_sprint_row="${active_sprint_row:-Active sprint: ${active_sprint}}"
1570
+ fi
1571
+ source_plan="$(get_todo_source_plan || true)"
1572
+ if [[ "$source_plan" == "(none)" ]]; then
1573
+ source_plan=""
1574
+ fi
1575
+ parent_run_id="${HOOK_RUN_ID:-${CLAUDE_RUN_ID:-${CODEX_RUN_ID:-run-$(date '+%Y%m%dT%H%M%S')-$$}}}"
1576
+ supersedes="$(workflow_read_state_field "$(workflow_task_state_file)" 'source_plan' || true)"
1577
+
1578
+ next_action="$(workflow_next_action)"
1579
+ next_stage="$(printf '%s\n' "$next_action" | cut -f1)"
1580
+ next_command="$(printf '%s\n' "$next_action" | cut -f2)"
1581
+ next_message="$(printf '%s\n' "$next_action" | cut -f3-)"
1582
+ [[ "${next_command:-}" == "-" ]] && next_command=""
1583
+ next_stage="${next_stage:-none}"
1584
+ next_message="${next_message:-(none)}"
1585
+ if [[ -n "${next_command:-}" ]]; then
1586
+ next_task="${next_message} Command: ${next_command}"
1587
+ else
1588
+ next_task="$next_message"
1589
+ fi
1590
+
1591
+ if is_git_repo; then
1592
+ changed_files="$(
1593
+ {
1594
+ git diff --name-only HEAD 2>/dev/null || true
1595
+ git ls-files --others --exclude-standard 2>/dev/null || true
1596
+ } | sed '/^[[:space:]]*$/d' | sort -u
1597
+ )"
1598
+ changed_files="${changed_files:-(none)}"
1599
+ changed_count="$(printf '%s\n' "$changed_files" | sed '/^(none)$/d; /^[[:space:]]*$/d' | wc -l | tr -d ' ')"
1600
+ if [[ "$changed_count" -gt 80 ]]; then
1601
+ changed_files="$(
1602
+ {
1603
+ printf '%s\n' "$changed_files" | head -80
1604
+ printf '... (%s total changed/untracked paths; inspect git status --short)\n' "$changed_count"
1605
+ }
1606
+ )"
1607
+ fi
1608
+
1609
+ diff_stat="$( (git diff --shortstat HEAD 2>/dev/null || true) | tr -d '\n' )"
1610
+ untracked_count="$(git ls-files --others --exclude-standard 2>/dev/null | wc -l | tr -d ' ')"
1611
+ if [[ "$untracked_count" -gt 0 ]]; then
1612
+ diff_stat="${diff_stat:-no tracked diff}; ${untracked_count} untracked files"
1613
+ fi
1614
+ diff_stat="${diff_stat:-no uncommitted diff against HEAD}"
1615
+ else
1616
+ changed_files="(none)"
1617
+ diff_stat="git repository not detected"
1618
+ fi
1619
+
1620
+ trace_file="$(workflow_trace_file)"
1621
+ if [[ -f "$trace_file" ]]; then
1622
+ recent_commands="$(
1623
+ tail -5 "$trace_file" 2>/dev/null \
1624
+ | sed -E 's/^/- /'
1625
+ )"
1626
+ fi
1627
+ recent_commands="${recent_commands:-- (none captured)}"
1628
+
1629
+ if [[ -n "$source_plan" ]]; then
1630
+ goal="Continue task checklist sourced from ${source_plan}."
1631
+ elif [[ -n "$active_plan" ]]; then
1632
+ goal="Continue active plan ${active_plan}."
1633
+ elif [[ "$next_stage" == "cleanup" ]]; then
1634
+ goal="Clean up completed contract worktree."
1635
+ elif [[ "$next_task" != "(none)" && "$next_task" != "No active execution checklist" ]]; then
1636
+ goal="$next_task"
1637
+ else
1638
+ goal="No active plan. Continue from the latest user request and filesystem state."
1639
+ fi
1640
+ decisions="Use filesystem artifacts as source of truth; treat SQLite/thread state as a rebuildable read model only."
1641
+ blockers="(none recorded)"
1642
+ if [[ -f "$checks_file" ]] && command -v jq >/dev/null 2>&1; then
1643
+ latest_trace_file="$(jq -r '.run_file // empty' "$checks_file" 2>/dev/null || true)"
1644
+ else
1645
+ latest_trace_file=""
1646
+ fi
1647
+ latest_trace_file="${latest_trace_file:-$checks_file}"
1648
+
1649
+ cat > "$handoff_file" <<EOF_HANDOFF
1650
+ # Harness Handoff
1651
+
1652
+ > **Generated**: $(date '+%Y-%m-%d %H:%M:%S')
1653
+ > **Reason**: ${reason}
1654
+
1655
+ ## Goal
1656
+
1657
+ ${goal}
1658
+
1659
+ ## Decisions
1660
+
1661
+ - ${decisions}
1662
+
1663
+ ## Files Touched
1664
+
1665
+ \`\`\`
1666
+ ${changed_files}
1667
+ \`\`\`
1668
+
1669
+ ## Commands Run
1670
+
1671
+ ${recent_commands}
1672
+
1673
+ ## Checks
1674
+
1675
+ - Checks file: ${checks_file}
1676
+ - Latest trace: ${latest_trace_file}
1677
+
1678
+ ## Blockers
1679
+
1680
+ - ${blockers}
1681
+
1682
+ ## Active Artifacts
1683
+
1684
+ - Active plan: ${active_plan:-(none)}
1685
+ - Active contract: ${active_contract:-(none)}
1686
+ - Active sprint row: ${active_sprint_row}
1687
+ - Review file: ${active_review:-(none)}
1688
+ - Latest trace/checks file: ${latest_trace_file}
1689
+ - Resume packet: ${resume_file}
1690
+
1691
+ ## Exact Next Step
1692
+
1693
+ - ${next_task}
1694
+
1695
+ ## Resume Prompt
1696
+
1697
+ - Resume packet: ${resume_file}
1698
+ - Start a fresh Codex session and read source artifacts first, then this handoff, before continuing; do not rely on auto-compact.
1699
+
1700
+ ## Source Artifacts
1701
+
1702
+ - Spec: ${spec_file}
1703
+ - Plan: ${active_plan:-(none)}
1704
+ - Todo Source Plan: ${source_plan:-(none)}
1705
+ - Contract: ${active_contract:-(none)}
1706
+ - Review: ${active_review:-(none)}
1707
+ - Notes: ${active_notes:-(none)}
1708
+ - Checks: ${checks_file}
1709
+ - Resume Packet: ${resume_file}
1710
+ - Policy: $(workflow_policy_file)
1711
+ - Context Map: $(workflow_context_map_file)
1712
+
1713
+ ## Current Status
1714
+
1715
+ - Next action stage: ${next_stage}
1716
+ - Next recommended action: ${next_task}
1717
+ - Working tree: ${diff_stat}
1718
+ - Parent Run ID: ${parent_run_id}
1719
+ - Supersedes: ${supersedes:-(none)}
1720
+
1721
+ ## Changed Files
1722
+
1723
+ \`\`\`
1724
+ ${changed_files}
1725
+ \`\`\`
1726
+ EOF_HANDOFF
1727
+
1728
+ workflow_append_event "handoff_refresh" "$reason" "{\"source_plan\":\"$(workflow_json_escape "${source_plan:-}")\",\"parent_run_id\":\"$(workflow_json_escape "$parent_run_id")\"}"
1729
+ workflow_write_run_summary "$reason"
1730
+ }