@webpresso/agent-kit 3.1.28 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @webpresso/agent-kit might be problematic. Click here for more details.

Files changed (329) hide show
  1. package/README.md +53 -15
  2. package/bin/_run.js +179 -19
  3. package/catalog/AGENTS.md.tpl +6 -6
  4. package/catalog/agent/agents/implementer.md +45 -0
  5. package/catalog/agent/rules/ci-cost-local-first.md +139 -0
  6. package/catalog/agent/rules/ci-test-perf.md +5 -1
  7. package/catalog/agent/rules/cmd-execution.md +9 -0
  8. package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
  9. package/catalog/agent/rules/pre-implementation.md +30 -2
  10. package/catalog/agent/rules/supported-agent-clis.md +9 -0
  11. package/catalog/agent/rules/test-scan-perf.md +80 -0
  12. package/catalog/agent/rules/typescript-version.md +50 -0
  13. package/catalog/agent/rules/workflow-skills-routing.md +4 -2
  14. package/catalog/agent/skills/codex/SKILL.md +25 -1
  15. package/catalog/agent/skills/fix/SKILL.md +3 -0
  16. package/catalog/agent/skills/verify/SKILL.md +22 -2
  17. package/catalog/docs/templates/blueprint.md +1 -1
  18. package/dist/esm/audit/agents.js +1 -0
  19. package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
  20. package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
  21. package/dist/esm/audit/blueprint-pr-coverage.js +72 -7
  22. package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
  23. package/dist/esm/audit/ci-guardrails-wiring.js +90 -0
  24. package/dist/esm/audit/ci-test-perf.js +30 -0
  25. package/dist/esm/audit/command-surface.js +4 -2
  26. package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
  27. package/dist/esm/audit/host-substitution-risk.js +256 -0
  28. package/dist/esm/audit/registry.d.ts +4 -4
  29. package/dist/esm/audit/registry.js +11 -1
  30. package/dist/esm/audit/roadmap-links.js +6 -0
  31. package/dist/esm/audit/test-scan-perf.d.ts +18 -0
  32. package/dist/esm/audit/test-scan-perf.js +275 -0
  33. package/dist/esm/audit/toolchain-isolation.js +1 -2
  34. package/dist/esm/audit/typescript-version.d.ts +2 -0
  35. package/dist/esm/audit/typescript-version.js +123 -0
  36. package/dist/esm/blueprint/core/parser.js +43 -1
  37. package/dist/esm/blueprint/core/validation/criteria.d.ts +14 -0
  38. package/dist/esm/blueprint/core/validation/criteria.js +41 -2
  39. package/dist/esm/blueprint/core/validation/state.js +25 -14
  40. package/dist/esm/blueprint/db/migrations/run.js +4 -2
  41. package/dist/esm/blueprint/lifecycle/audit.d.ts +20 -0
  42. package/dist/esm/blueprint/lifecycle/audit.js +65 -10
  43. package/dist/esm/blueprint/lifecycle/canonicalize.d.ts +6 -0
  44. package/dist/esm/blueprint/lifecycle/canonicalize.js +135 -0
  45. package/dist/esm/blueprint/lifecycle/engine.d.ts +9 -0
  46. package/dist/esm/blueprint/lifecycle/engine.js +53 -6
  47. package/dist/esm/blueprint/lifecycle/local.d.ts +2 -1
  48. package/dist/esm/blueprint/lifecycle/local.js +19 -3
  49. package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
  50. package/dist/esm/blueprint/lifecycle/review-provenance.js +118 -12
  51. package/dist/esm/blueprint/markdown/helpers.js +6 -2
  52. package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
  53. package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
  54. package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
  55. package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
  56. package/dist/esm/blueprint/trust/command-runner.js +148 -0
  57. package/dist/esm/blueprint/trust/gates.d.ts +7 -0
  58. package/dist/esm/blueprint/trust/gates.js +20 -0
  59. package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
  60. package/dist/esm/blueprint/trust/promotion.js +62 -112
  61. package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
  62. package/dist/esm/blueprint/trust/scaffold.js +64 -0
  63. package/dist/esm/blueprint/trust/validator.d.ts +1 -0
  64. package/dist/esm/blueprint/trust/validator.js +7 -1
  65. package/dist/esm/blueprint/utils/archive.js +27 -5
  66. package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
  67. package/dist/esm/blueprint/utils/document-paths.js +45 -12
  68. package/dist/esm/build/cli-mcp-parity.js +10 -0
  69. package/dist/esm/ci/act-replay.js +110 -7
  70. package/dist/esm/ci/act-runner.d.ts +4 -0
  71. package/dist/esm/ci/act-runner.js +85 -26
  72. package/dist/esm/ci/act-worktree-git.d.ts +11 -0
  73. package/dist/esm/ci/act-worktree-git.js +219 -0
  74. package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
  75. package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
  76. package/dist/esm/cli/cli.d.ts +1 -1
  77. package/dist/esm/cli/cli.js +34 -0
  78. package/dist/esm/cli/commands/agent-launch.d.ts +6 -1
  79. package/dist/esm/cli/commands/agent-launch.js +82 -4
  80. package/dist/esm/cli/commands/audit-core.d.ts +1 -1
  81. package/dist/esm/cli/commands/audit-core.js +8 -1
  82. package/dist/esm/cli/commands/audit.js +20 -5
  83. package/dist/esm/cli/commands/blueprint/execution.js +18 -6
  84. package/dist/esm/cli/commands/blueprint/mutations.js +83 -47
  85. package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
  86. package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
  87. package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
  88. package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
  89. package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
  90. package/dist/esm/cli/commands/blueprint/router.js +40 -3
  91. package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
  92. package/dist/esm/cli/commands/ci-preflight.js +6 -3
  93. package/dist/esm/cli/commands/ci.d.ts +8 -1
  94. package/dist/esm/cli/commands/ci.js +80 -21
  95. package/dist/esm/cli/commands/compile.js +5 -31
  96. package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
  97. package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
  98. package/dist/esm/cli/commands/dash/keymap.js +1 -0
  99. package/dist/esm/cli/commands/dash/launch-preferences.d.ts +15 -0
  100. package/dist/esm/cli/commands/dash/launch-preferences.js +46 -17
  101. package/dist/esm/cli/commands/dash/plan-once-label.d.ts +7 -0
  102. package/dist/esm/cli/commands/dash/plan-once-label.js +5 -0
  103. package/dist/esm/cli/commands/dash/plan-once-membership.d.ts +67 -0
  104. package/dist/esm/cli/commands/dash/plan-once-membership.js +151 -0
  105. package/dist/esm/cli/commands/dash/plan-once-merge.d.ts +143 -0
  106. package/dist/esm/cli/commands/dash/plan-once-merge.js +238 -0
  107. package/dist/esm/cli/commands/dash/plan-once.d.ts +22 -2
  108. package/dist/esm/cli/commands/dash/plan-once.js +41 -4
  109. package/dist/esm/cli/commands/dash/tui/attention-hud.d.ts +9 -0
  110. package/dist/esm/cli/commands/dash/tui/attention-hud.js +9 -0
  111. package/dist/esm/cli/commands/dash/tui/chat-bridge.d.ts +41 -0
  112. package/dist/esm/cli/commands/dash/tui/chat-bridge.js +73 -0
  113. package/dist/esm/cli/commands/dash/tui/chat-mode.d.ts +47 -0
  114. package/dist/esm/cli/commands/dash/tui/chat-mode.js +98 -0
  115. package/dist/esm/cli/commands/dash/tui/frame.d.ts +3 -0
  116. package/dist/esm/cli/commands/dash/tui/frame.js +3 -1
  117. package/dist/esm/cli/commands/dash/tui/palette.d.ts +1 -1
  118. package/dist/esm/cli/commands/dash/tui/palette.js +14 -0
  119. package/dist/esm/cli/commands/dash/tui/runtime.js +239 -4
  120. package/dist/esm/cli/commands/dash/tui/state.d.ts +41 -0
  121. package/dist/esm/cli/commands/dash/tui/state.js +257 -4
  122. package/dist/esm/cli/commands/doctor.js +5 -0
  123. package/dist/esm/cli/commands/format.js +0 -2
  124. package/dist/esm/cli/commands/hook.d.ts +5 -0
  125. package/dist/esm/cli/commands/hook.js +15 -9
  126. package/dist/esm/cli/commands/init/config.d.ts +0 -1
  127. package/dist/esm/cli/commands/init/config.js +3 -6
  128. package/dist/esm/cli/commands/init/convergence-apply.d.ts +24 -0
  129. package/dist/esm/cli/commands/init/convergence-apply.js +221 -0
  130. package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
  131. package/dist/esm/cli/commands/init/convergence-state.js +54 -0
  132. package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
  133. package/dist/esm/cli/commands/init/convergence.js +57 -0
  134. package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
  135. package/dist/esm/cli/commands/init/git-convergence.js +168 -0
  136. package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
  137. package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
  138. package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
  139. package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
  140. package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
  141. package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
  142. package/dist/esm/cli/commands/init/host-visibility.js +94 -5
  143. package/dist/esm/cli/commands/init/index.d.ts +1 -0
  144. package/dist/esm/cli/commands/init/index.js +326 -173
  145. package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
  146. package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
  147. package/dist/esm/cli/commands/init/merge.d.ts +1 -0
  148. package/dist/esm/cli/commands/init/merge.js +30 -1
  149. package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
  150. package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
  151. package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
  152. package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
  153. package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +11 -0
  154. package/dist/esm/cli/commands/init/scaffold-base-kit.js +56 -1
  155. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.d.ts +5 -6
  156. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.js +6 -13
  157. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
  158. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
  159. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
  160. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +51 -23
  161. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.d.ts +0 -2
  162. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.js +0 -2
  163. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.d.ts +6 -3
  164. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.js +39 -26
  165. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
  166. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +297 -173
  167. package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +7 -0
  168. package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +58 -0
  169. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
  170. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
  171. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
  172. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
  173. package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
  174. package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
  175. package/dist/esm/cli/commands/logs.d.ts +15 -0
  176. package/dist/esm/cli/commands/logs.js +192 -3
  177. package/dist/esm/cli/commands/opencode-rotate.d.ts +49 -0
  178. package/dist/esm/cli/commands/opencode-rotate.js +135 -0
  179. package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
  180. package/dist/esm/cli/commands/public-readiness.js +42 -0
  181. package/dist/esm/cli/commands/quality-log-store.d.ts +1 -0
  182. package/dist/esm/cli/commands/quality-log-store.js +187 -51
  183. package/dist/esm/cli/commands/review.d.ts +35 -3
  184. package/dist/esm/cli/commands/review.js +734 -217
  185. package/dist/esm/cli/commands/run-wait.d.ts +2 -0
  186. package/dist/esm/cli/commands/run-wait.js +122 -0
  187. package/dist/esm/cli/commands/secrets.d.ts +1 -0
  188. package/dist/esm/cli/commands/secrets.js +4 -1
  189. package/dist/esm/cli/commands/setup-help.js +1 -1
  190. package/dist/esm/cli/commands/setup-repair-help.js +1 -1
  191. package/dist/esm/cli/commands/worktree/router-dispatch.js +13 -8
  192. package/dist/esm/cli/direct-provider-launch.js +31 -3
  193. package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
  194. package/dist/esm/cli/optional-tool-freshness.js +219 -9
  195. package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
  196. package/dist/esm/daemon/domains/git-actions.js +136 -0
  197. package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
  198. package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
  199. package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
  200. package/dist/esm/daemon/domains/sessions-search.js +29 -0
  201. package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
  202. package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
  203. package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
  204. package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
  205. package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
  206. package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
  207. package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
  208. package/dist/esm/daemon/handlers/git-handlers.js +27 -0
  209. package/dist/esm/daemon/handlers/product.d.ts +7 -0
  210. package/dist/esm/daemon/handlers/product.js +10 -119
  211. package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
  212. package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
  213. package/dist/esm/daemon/handlers/shared.d.ts +5 -0
  214. package/dist/esm/daemon/handlers/shared.js +16 -0
  215. package/dist/esm/daemon/methods.d.ts +8 -1
  216. package/dist/esm/daemon/methods.js +7 -0
  217. package/dist/esm/daemon/protocol/contract.d.ts +43 -0
  218. package/dist/esm/daemon/protocol/contract.js +26 -0
  219. package/dist/esm/daemon/protocol/index.d.ts +1 -1
  220. package/dist/esm/daemon/protocol/index.js +1 -1
  221. package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
  222. package/dist/esm/hooks/__conformance__/matrix.js +137 -0
  223. package/dist/esm/hooks/conformance/batch.js +5 -0
  224. package/dist/esm/hooks/doctor.d.ts +15 -0
  225. package/dist/esm/hooks/doctor.js +79 -8
  226. package/dist/esm/hooks/permission-request/index.js +9 -3
  227. package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
  228. package/dist/esm/hooks/permission-request/policy.js +13 -0
  229. package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
  230. package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
  231. package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
  232. package/dist/esm/hooks/pretool-guard/logger.js +16 -0
  233. package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
  234. package/dist/esm/hooks/pretool-guard/runner.js +68 -16
  235. package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
  236. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +58 -4
  237. package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
  238. package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
  239. package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.d.ts +0 -1
  240. package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.js +8 -5
  241. package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
  242. package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
  243. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.d.ts +9 -7
  244. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +126 -14
  245. package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
  246. package/dist/esm/hooks/shared/guard-outcome.js +215 -0
  247. package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
  248. package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
  249. package/dist/esm/hooks/shared/types.d.ts +8 -5
  250. package/dist/esm/hooks/shared/types.js +23 -8
  251. package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
  252. package/dist/esm/hooks/stop/qa-changed-files.js +74 -0
  253. package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
  254. package/dist/esm/mcp/blueprint/handlers/document-mutations.js +78 -4
  255. package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
  256. package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
  257. package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
  258. package/dist/esm/mcp/blueprint/registration.js +3 -2
  259. package/dist/esm/mcp/server.d.ts +1 -0
  260. package/dist/esm/mcp/server.js +27 -0
  261. package/dist/esm/mcp/tools/_names.d.ts +1 -1
  262. package/dist/esm/mcp/tools/_names.js +5 -0
  263. package/dist/esm/mcp/tools/_registry.js +10 -0
  264. package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
  265. package/dist/esm/mcp/tools/_shared/result.js +44 -1
  266. package/dist/esm/mcp/tools/audit.d.ts +5 -1
  267. package/dist/esm/mcp/tools/audit.js +40 -0
  268. package/dist/esm/mcp/tools/audits.d.ts +4 -0
  269. package/dist/esm/mcp/tools/ci-act.js +9 -11
  270. package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
  271. package/dist/esm/mcp/tools/ci-preflight.js +183 -0
  272. package/dist/esm/mcp/tools/pr-upsert.js +39 -1
  273. package/dist/esm/mcp/tools/review-run.d.ts +28 -0
  274. package/dist/esm/mcp/tools/review-run.js +216 -0
  275. package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
  276. package/dist/esm/mcp/tools/run-wait.js +593 -0
  277. package/dist/esm/mcp/tools/session-id.d.ts +3 -0
  278. package/dist/esm/mcp/tools/session-id.js +45 -0
  279. package/dist/esm/mcp/tools/session-info.d.ts +3 -0
  280. package/dist/esm/mcp/tools/session-info.js +49 -0
  281. package/dist/esm/output-transforms/audit.d.ts +23 -0
  282. package/dist/esm/output-transforms/audit.js +47 -0
  283. package/dist/esm/output-transforms/index.js +2 -0
  284. package/dist/esm/paths/state-root.js +3 -0
  285. package/dist/esm/review/authority.js +30 -7
  286. package/dist/esm/review/availability.d.ts +22 -0
  287. package/dist/esm/review/availability.js +123 -23
  288. package/dist/esm/review/delivery-verifier.d.ts +27 -0
  289. package/dist/esm/review/delivery-verifier.js +115 -0
  290. package/dist/esm/review/events.d.ts +3 -2
  291. package/dist/esm/review/events.js +23 -3
  292. package/dist/esm/review/execution/adapters.js +26 -9
  293. package/dist/esm/review/execution/output-policy.d.ts +2 -0
  294. package/dist/esm/review/execution/output-policy.js +12 -0
  295. package/dist/esm/review/execution/review-checkout.js +87 -6
  296. package/dist/esm/review/execution/supervisor.js +5 -0
  297. package/dist/esm/review/execution/types.d.ts +8 -0
  298. package/dist/esm/review/opencode-account.d.ts +24 -0
  299. package/dist/esm/review/opencode-account.js +68 -0
  300. package/dist/esm/review/opencode-auth.d.ts +33 -0
  301. package/dist/esm/review/opencode-auth.js +79 -0
  302. package/dist/esm/review/subject.d.ts +27 -0
  303. package/dist/esm/review/subject.js +179 -32
  304. package/dist/esm/runtime/executor.d.ts +1 -0
  305. package/dist/esm/runtime/executor.js +2 -1
  306. package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
  307. package/dist/esm/runtime/opencode-account-materializer.js +33 -0
  308. package/dist/esm/secret-gate/runner.d.ts +1 -0
  309. package/dist/esm/secret-gate/runner.js +4 -1
  310. package/dist/esm/status/snapshot.js +30 -19
  311. package/dist/esm/test/duration-sequencer.d.ts +20 -0
  312. package/dist/esm/test/duration-sequencer.js +54 -0
  313. package/dist/esm/test/shard-durations.json +922 -0
  314. package/dist/esm/test/worker-budget.d.ts +6 -0
  315. package/dist/esm/test/worker-budget.js +59 -1
  316. package/dist/esm/test-helpers/global-setup.d.ts +1 -1
  317. package/dist/esm/test-helpers/global-setup.js +68 -24
  318. package/dist/esm/ultragoal/service.d.ts +7 -0
  319. package/dist/esm/ultragoal/service.js +42 -2
  320. package/dist/esm/utils/write-json-file.js +3 -1
  321. package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
  322. package/dist/esm/worktrees/codex-project-trust.js +56 -0
  323. package/dist/esm/worktrees/current.d.ts +1 -0
  324. package/dist/esm/worktrees/current.js +1 -0
  325. package/dist/esm/worktrees/project-agent-surfaces.d.ts +1 -0
  326. package/dist/esm/worktrees/project-agent-surfaces.js +2 -1
  327. package/dist/esm/worktrees/registry.d.ts +8 -0
  328. package/dist/esm/worktrees/registry.js +18 -3
  329. package/package.json +18 -15
@@ -0,0 +1,23 @@
1
+ import type { TransformContext, TransformResult } from "./index.js";
2
+ /**
3
+ * `wp audit <kind>` writes the full `formatRepoAuditReport` output (title +
4
+ * one bullet per violation) into its CLI log. Every audit kind normalizes to
5
+ * the shared `"audit"` tool name (see `normalizeToolName`), so without a
6
+ * dedicated transform that output fell through to `genericTransform`, whose
7
+ * error/fail keyword heuristic drops any violation bullet that doesn't
8
+ * literally contain "error"/"fail"/"✗"/"✘" — silently discarding most
9
+ * violation detail even when the full report is a handful of short lines.
10
+ *
11
+ * This transform recognizes the bullet-list shape directly so violation text
12
+ * survives regardless of wording, then defers to the same size-based
13
+ * clip/elision policy every other transform uses (`createTransformResult` /
14
+ * `clipRawOutput`'s existing byte threshold) to decide whether the (now
15
+ * correctly extracted) violation list is short enough to print inline or
16
+ * long enough to still defer to `wp logs audit`.
17
+ *
18
+ * Audit output that isn't shaped like a violation-bullet report (for example
19
+ * raw subprocess output from `mutation`/`bundle-budget`) has no matching
20
+ * bullet lines, so this falls back to the previous generic keyword-based
21
+ * behavior unchanged.
22
+ */
23
+ export declare function auditTransform(rawOutput: string | undefined, context: TransformContext): TransformResult;
@@ -0,0 +1,47 @@
1
+ import { createTransformResult } from "./metadata.js";
2
+ import { genericTransform } from "./generic.js";
3
+ // `formatRepoAuditReport` (src/audit/repo-guardrails.ts) always renders one
4
+ // violation per line as `- <location><metadata><message><fixability>`. That
5
+ // bullet marker is the one structural signal every repo-audit report shares,
6
+ // regardless of which audit kind produced it or whether the violation text
7
+ // happens to contain the words "error"/"fail" that the generic fallback
8
+ // keys off of.
9
+ const VIOLATION_LINE_RE = /^-\s+(.+)$/u;
10
+ /**
11
+ * `wp audit <kind>` writes the full `formatRepoAuditReport` output (title +
12
+ * one bullet per violation) into its CLI log. Every audit kind normalizes to
13
+ * the shared `"audit"` tool name (see `normalizeToolName`), so without a
14
+ * dedicated transform that output fell through to `genericTransform`, whose
15
+ * error/fail keyword heuristic drops any violation bullet that doesn't
16
+ * literally contain "error"/"fail"/"✗"/"✘" — silently discarding most
17
+ * violation detail even when the full report is a handful of short lines.
18
+ *
19
+ * This transform recognizes the bullet-list shape directly so violation text
20
+ * survives regardless of wording, then defers to the same size-based
21
+ * clip/elision policy every other transform uses (`createTransformResult` /
22
+ * `clipRawOutput`'s existing byte threshold) to decide whether the (now
23
+ * correctly extracted) violation list is short enough to print inline or
24
+ * long enough to still defer to `wp logs audit`.
25
+ *
26
+ * Audit output that isn't shaped like a violation-bullet report (for example
27
+ * raw subprocess output from `mutation`/`bundle-budget`) has no matching
28
+ * bullet lines, so this falls back to the previous generic keyword-based
29
+ * behavior unchanged.
30
+ */
31
+ export function auditTransform(rawOutput, context) {
32
+ if (!rawOutput)
33
+ return {};
34
+ const failures = extractViolationBullets(rawOutput);
35
+ if (failures.length === 0)
36
+ return genericTransform(rawOutput, context);
37
+ return createTransformResult(rawOutput, failures.map((failure) => failure.message).join("\n"), context, { tier: 1, failures });
38
+ }
39
+ function extractViolationBullets(rawOutput) {
40
+ const failures = [];
41
+ for (const line of rawOutput.split(/\r?\n/u)) {
42
+ const message = VIOLATION_LINE_RE.exec(line)?.[1]?.trim();
43
+ if (message)
44
+ failures.push({ message });
45
+ }
46
+ return failures;
47
+ }
@@ -1,3 +1,4 @@
1
+ import { auditTransform } from "./audit.js";
1
2
  import { genericTransform } from "./generic.js";
2
3
  import { oxlintTransform } from "./oxlint.js";
3
4
  import { tscTransform } from "./tsc.js";
@@ -8,6 +9,7 @@ const builtInTransforms = new Map([
8
9
  ["lint-oxlint", oxlintTransform],
9
10
  ["typecheck", tscTransform],
10
11
  ["test", vitestTransform],
12
+ ["audit", auditTransform],
11
13
  ]);
12
14
  const transforms = new Map(builtInTransforms);
13
15
  export function registerTransform(toolName, transform) {
@@ -247,6 +247,9 @@ export async function getSurfacePathAsync(name, scope, cwd = resolveCwd()) {
247
247
  export async function withLock(scope, fn, cwd) {
248
248
  const lockTarget = await getSurfacePathAsync(".lock", scope, cwd);
249
249
  mkdirSync(dirname(lockTarget), { recursive: true });
250
+ // Keep retries short so lock-contention probes (review gate contender tests)
251
+ // still fail closed in a few seconds. Dual-writer create loops retry at the
252
+ // call site when they need longer budgets under CI load.
250
253
  const release = await lockfile.lock(lockTarget, {
251
254
  realpath: false,
252
255
  retries: { retries: 5, minTimeout: 50, maxTimeout: 500, factor: 2 },
@@ -9,7 +9,7 @@ import { createHash } from "node:crypto";
9
9
  import { existsSync, lstatSync, readFileSync } from "node:fs";
10
10
  import path from "node:path";
11
11
  import { evaluateReviewEvents, parseReviewEventLog } from "./events.js";
12
- import { createDeliverySubjectAtRef, createPlanSubjectAtRef, resolveReviewCommit, } from "./subject.js";
12
+ import { createDeliverySubjectAtRef, createLegacyPlanSubjectAtRef, createPlanSubjectAtRef, resolveReviewCommit, } from "./subject.js";
13
13
  const GIT_TIMEOUT_MS = 5_000;
14
14
  const GIT_MAX_BUFFER_BYTES = 128 * 1024 * 1024;
15
15
  const REGULAR_MODE_PATTERN = /^100(?:644|755)$/u;
@@ -60,7 +60,7 @@ function assertStableSlug(slug) {
60
60
  }
61
61
  function locateBlueprint(entries, stableSlug) {
62
62
  assertStableSlug(stableSlug);
63
- const suffixPattern = new RegExp(`(?:^|/)${LIFECYCLE_SEGMENTS}/${stableSlug.replaceAll("/", "\\/")}/_overview\\.md$`, "u");
63
+ const suffixPattern = new RegExp(`(?:^|/)${LIFECYCLE_SEGMENTS}/${stableSlug.replaceAll("/", "\\/")}(?:/_overview\\.md|\\.md)$`, "u");
64
64
  const matches = entries.filter((entry) => suffixPattern.test(entry.path) &&
65
65
  REGULAR_MODE_PATTERN.test(entry.mode) &&
66
66
  entry.type === "blob");
@@ -69,6 +69,9 @@ function locateBlueprint(entries, stableSlug) {
69
69
  }
70
70
  return matches[0];
71
71
  }
72
+ function ledgerDirectoryForBlueprintPath(blueprintPath) {
73
+ return blueprintPath.endsWith("/_overview.md") ? path.posix.dirname(blueprintPath) : null;
74
+ }
72
75
  function readBlob(cwd, entry) {
73
76
  if (!REGULAR_MODE_PATTERN.test(entry.mode) || entry.type !== "blob") {
74
77
  throw new Error(`Review authority path is not a regular blob: ${entry.path}`);
@@ -115,23 +118,40 @@ export function readReviewAuthorityAtRef(cwd, ref, stableSlug, purpose) {
115
118
  const commit = resolveReviewCommit(cwd, ref);
116
119
  const entries = parseRefTree(cwd, commit);
117
120
  const overview = locateBlueprint(entries, stableSlug);
118
- const blueprintDirectory = path.posix.dirname(overview.path);
119
- const ledgerPath = path.posix.join(blueprintDirectory, "review-events.jsonl");
120
- const ledger = entries.find((entry) => entry.path === ledgerPath);
121
+ const blueprintDirectory = ledgerDirectoryForBlueprintPath(overview.path);
122
+ const ledgerPath = blueprintDirectory === null ? null : path.posix.join(blueprintDirectory, "review-events.jsonl");
123
+ const ledger = ledgerPath === null ? undefined : entries.find((entry) => entry.path === ledgerPath);
121
124
  const events = ledger ? parseReviewEventLog(readBlob(cwd, ledger).toString("utf8")) : [];
122
125
  if (events.some((event) => event.blueprintSlug !== stableSlug)) {
123
126
  throw new Error(`Review event ledger contains an event for a different blueprint: ${ledgerPath}`);
124
127
  }
125
- const { compromisedIds, issues } = markCompromisedArtifactEvents(cwd, entries, blueprintDirectory, events);
128
+ const { compromisedIds, issues } = blueprintDirectory === null
129
+ ? { compromisedIds: new Set(), issues: [] }
130
+ : markCompromisedArtifactEvents(cwd, entries, blueprintDirectory, events);
126
131
  const subject = purpose === "plan"
127
132
  ? createPlanSubjectAtRef(cwd, commit, stableSlug, overview.path)
128
133
  : createDeliverySubjectAtRef(cwd, commit, stableSlug, overview.path);
129
- const evaluation = evaluateReviewEvents(events, {
134
+ let evaluation = evaluateReviewEvents(events, {
130
135
  purpose,
131
136
  blueprintSlug: stableSlug,
132
137
  subjectScheme: subject.scheme,
133
138
  subjectDigest: subject.digest,
134
139
  }, compromisedIds);
140
+ if (purpose === "plan" && evaluation.approvals.length === 0) {
141
+ // Digest-compatibility fallback: events recorded before the lifecycle
142
+ // stripped-key extension bound their subjectDigest to the legacy
143
+ // normalization. Accept those for the same tracked content; new events
144
+ // always record the current digest (see legacyNormalizePlanSubjectContent).
145
+ const legacySubject = createLegacyPlanSubjectAtRef(cwd, commit, stableSlug, overview.path);
146
+ if (legacySubject.digest !== subject.digest) {
147
+ evaluation = evaluateReviewEvents(events, {
148
+ purpose,
149
+ blueprintSlug: stableSlug,
150
+ subjectScheme: legacySubject.scheme,
151
+ subjectDigest: legacySubject.digest,
152
+ }, compromisedIds);
153
+ }
154
+ }
135
155
  return {
136
156
  commit,
137
157
  blueprintPath: overview.path,
@@ -145,6 +165,9 @@ export function readReviewAuthorityAtRef(cwd, ref, stableSlug, purpose) {
145
165
  };
146
166
  }
147
167
  export function readWorkingReviewEvents(blueprintPath, stableSlug) {
168
+ if (path.basename(blueprintPath) !== "_overview.md") {
169
+ return { events: [], compromisedIds: new Set(), issues: [] };
170
+ }
148
171
  const ledgerPath = path.join(path.dirname(blueprintPath), "review-events.jsonl");
149
172
  if (!existsSync(ledgerPath))
150
173
  return { events: [], compromisedIds: new Set(), issues: [] };
@@ -49,3 +49,25 @@ export declare function pruneExpiredAvailabilitySuppressions(state: ReviewAvaila
49
49
  state: ReviewAvailabilityState;
50
50
  pruned: string[];
51
51
  };
52
+ export declare const OPENCODE_ACCOUNT_AVAILABILITY_SCHEMA_VERSION = 1;
53
+ export interface OpencodeAccountAvailabilityState {
54
+ readonly schemaVersion: 1;
55
+ readonly updatedAt: string;
56
+ readonly accounts: Record<string, ReviewAvailabilityRecord>;
57
+ }
58
+ export interface OpencodeAccountAvailabilityEvent {
59
+ readonly limitWindow?: ReviewLimitWindow;
60
+ readonly retryAfterMs?: number;
61
+ readonly failureCode?: string;
62
+ readonly terminalClassification?: string;
63
+ readonly at?: string;
64
+ }
65
+ export declare function opencodeAccountAvailabilityPath(): string;
66
+ export declare function loadOpencodeAccountAvailability(): OpencodeAccountAvailabilityState;
67
+ export declare function isOpencodeAccountSuppressed(state: OpencodeAccountAvailabilityState, canonicalId: string): boolean;
68
+ export declare function recordOpencodeAccountAvailability(canonicalId: string, event: OpencodeAccountAvailabilityEvent): OpencodeAccountAvailabilityState;
69
+ export declare function recordOpencodeAccountAvailabilityWithLock(canonicalId: string, event: OpencodeAccountAvailabilityEvent): Promise<OpencodeAccountAvailabilityState>;
70
+ export declare function pruneExpiredOpencodeAccountSuppressions(state: OpencodeAccountAvailabilityState, nowMs?: number): {
71
+ state: OpencodeAccountAvailabilityState;
72
+ pruned: string[];
73
+ };
@@ -1,13 +1,9 @@
1
1
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
- import { NotInGitRepoError, withLock } from "#paths/state-root.js";
3
+ import { getStateRoot, NotInGitRepoError, withLock } from "#paths/state-root.js";
4
4
  import { writeJsonFileAtomic } from "#shared-utils/write-json-file.js";
5
5
  export const REVIEW_AVAILABILITY_SCHEMA_VERSION = 1;
6
- const FAILURE_CLASSIFICATIONS = new Set([
7
- "provider-limit-reached",
8
- "provider-unavailable",
9
- "provider-failed",
10
- ]);
6
+ const FAILURE_CLASSIFICATIONS = new Set(["provider-limit-reached", "provider-unavailable"]);
11
7
  const SUCCESS_CLASSIFICATIONS = new Set([
12
8
  "complete",
13
9
  "approved",
@@ -117,14 +113,15 @@ export function saveReviewAvailability(projectRoot, state) {
117
113
  function shouldSuppress(event) {
118
114
  return FAILURE_CLASSIFICATIONS.has(event.terminalClassification);
119
115
  }
116
+ function suppressedRecordStillApplies(record) {
117
+ return (record.terminalClassification !== undefined &&
118
+ FAILURE_CLASSIFICATIONS.has(record.terminalClassification));
119
+ }
120
120
  function isAvailabilitySuccess(event) {
121
121
  return SUCCESS_CLASSIFICATIONS.has(event.terminalClassification);
122
122
  }
123
123
  function isProviderWideOpenCodeFailure(event) {
124
- return (event.provider === "opencode" &&
125
- (event.failureCode === "opencode-go-usage-limit" ||
126
- event.reviewer === undefined ||
127
- event.reviewer === "opencode"));
124
+ return (event.provider === "opencode" && (event.reviewer === undefined || event.reviewer === "opencode"));
128
125
  }
129
126
  function nextRecord(previous, event, input) {
130
127
  const at = event.at ?? new Date().toISOString();
@@ -155,6 +152,15 @@ function nextRecord(previous, event, input) {
155
152
  const success = isAvailabilitySuccess(event);
156
153
  if (!input.suppress) {
157
154
  const preserveSuppression = !success && previous?.suppressed === true;
155
+ if (preserveSuppression) {
156
+ return {
157
+ ...previous,
158
+ ...(event.model ? { lastModel: event.model } : {}),
159
+ ...(event.effort ? { lastEffort: event.effort } : {}),
160
+ ...(event.stage ? { lastStage: event.stage } : {}),
161
+ suppressed: true,
162
+ };
163
+ }
158
164
  return {
159
165
  ...common,
160
166
  ...(success
@@ -241,22 +247,35 @@ export function suppressedReviewers(state) {
241
247
  .filter((record) => record.suppressed)
242
248
  .map((record) => record.canonicalId));
243
249
  }
244
- export function pruneExpiredAvailabilitySuppressions(state, nowMs = Date.now()) {
245
- const pruned = [];
246
- const pruneMap = (records) => Object.fromEntries(Object.entries(records).map(([key, record]) => {
247
- if (record.suppressed &&
248
- record.retryAfterMs !== undefined &&
249
- record.lastFailureAt !== undefined) {
250
- const failureAt = Date.parse(record.lastFailureAt);
251
- if (Number.isFinite(failureAt) && failureAt + record.retryAfterMs <= nowMs) {
252
- pruned.push(key);
253
- return [key, { ...record, suppressed: false }];
254
- }
250
+ /**
251
+ * Shared expiry predicate for a suppressed availability record: applies to
252
+ * both the reviewer/provider store and the opencode-go account store below.
253
+ * A record clears when its terminal classification is no longer a failure
254
+ * classification, or when its retry window has elapsed.
255
+ */
256
+ function suppressionExpired(record, nowMs) {
257
+ if (!record.suppressed)
258
+ return false;
259
+ if (!suppressedRecordStillApplies(record))
260
+ return true;
261
+ if (record.retryAfterMs === undefined || record.lastFailureAt === undefined)
262
+ return false;
263
+ const failureAt = Date.parse(record.lastFailureAt);
264
+ return Number.isFinite(failureAt) && failureAt + record.retryAfterMs <= nowMs;
265
+ }
266
+ function pruneRecordMap(records, nowMs, pruned) {
267
+ return Object.fromEntries(Object.entries(records).map(([key, record]) => {
268
+ if (suppressionExpired(record, nowMs)) {
269
+ pruned.push(key);
270
+ return [key, { ...record, suppressed: false }];
255
271
  }
256
272
  return [key, record];
257
273
  }));
258
- const providers = pruneMap(state.providers);
259
- const reviewers = pruneMap(state.reviewers);
274
+ }
275
+ export function pruneExpiredAvailabilitySuppressions(state, nowMs = Date.now()) {
276
+ const pruned = [];
277
+ const providers = pruneRecordMap(state.providers, nowMs, pruned);
278
+ const reviewers = pruneRecordMap(state.reviewers, nowMs, pruned);
260
279
  if (pruned.length === 0)
261
280
  return { state, pruned };
262
281
  return {
@@ -269,3 +288,84 @@ export function pruneExpiredAvailabilitySuppressions(state, nowMs = Date.now())
269
288
  pruned,
270
289
  };
271
290
  }
291
+ // --- opencode-go multi-account cooldown store (user-scoped, cross-repo) ---
292
+ export const OPENCODE_ACCOUNT_AVAILABILITY_SCHEMA_VERSION = 1;
293
+ export function opencodeAccountAvailabilityPath() {
294
+ return path.join(getStateRoot(), "opencode-account-availability.json");
295
+ }
296
+ function emptyOpencodeAccountAvailability(now = new Date().toISOString()) {
297
+ return {
298
+ schemaVersion: OPENCODE_ACCOUNT_AVAILABILITY_SCHEMA_VERSION,
299
+ updatedAt: now,
300
+ accounts: {},
301
+ };
302
+ }
303
+ export function loadOpencodeAccountAvailability() {
304
+ const file = opencodeAccountAvailabilityPath();
305
+ if (!existsSync(file))
306
+ return emptyOpencodeAccountAvailability();
307
+ try {
308
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
309
+ if (!isRecord(parsed) ||
310
+ parsed.schemaVersion !== OPENCODE_ACCOUNT_AVAILABILITY_SCHEMA_VERSION) {
311
+ return emptyOpencodeAccountAvailability();
312
+ }
313
+ return {
314
+ schemaVersion: OPENCODE_ACCOUNT_AVAILABILITY_SCHEMA_VERSION,
315
+ updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : new Date().toISOString(),
316
+ accounts: normalizeRecordMap(parsed.accounts),
317
+ };
318
+ }
319
+ catch {
320
+ return emptyOpencodeAccountAvailability();
321
+ }
322
+ }
323
+ function saveOpencodeAccountAvailability(state) {
324
+ const file = opencodeAccountAvailabilityPath();
325
+ mkdirSync(path.dirname(file), { recursive: true });
326
+ writeJsonFileAtomic(file, state);
327
+ }
328
+ export function isOpencodeAccountSuppressed(state, canonicalId) {
329
+ return state.accounts[canonicalId]?.suppressed === true;
330
+ }
331
+ export function recordOpencodeAccountAvailability(canonicalId, event) {
332
+ const at = event.at ?? new Date().toISOString();
333
+ const current = loadOpencodeAccountAvailability();
334
+ const accounts = { ...current.accounts };
335
+ accounts[canonicalId] = {
336
+ canonicalId,
337
+ provider: "opencode",
338
+ terminalClassification: event.terminalClassification ?? "provider-limit-reached",
339
+ ...(event.failureCode ? { failureCode: event.failureCode } : {}),
340
+ ...(event.limitWindow ? { limitWindow: event.limitWindow } : {}),
341
+ ...(event.retryAfterMs === undefined ? {} : { retryAfterMs: event.retryAfterMs }),
342
+ lastFailureAt: at,
343
+ suppressed: true,
344
+ suppressionScope: "provider",
345
+ suppressedAt: at,
346
+ };
347
+ const next = {
348
+ schemaVersion: OPENCODE_ACCOUNT_AVAILABILITY_SCHEMA_VERSION,
349
+ updatedAt: at,
350
+ accounts,
351
+ };
352
+ saveOpencodeAccountAvailability(next);
353
+ return next;
354
+ }
355
+ export function recordOpencodeAccountAvailabilityWithLock(canonicalId, event) {
356
+ return withLock("user", () => recordOpencodeAccountAvailability(canonicalId, event));
357
+ }
358
+ export function pruneExpiredOpencodeAccountSuppressions(state, nowMs = Date.now()) {
359
+ const pruned = [];
360
+ const accounts = pruneRecordMap(state.accounts, nowMs, pruned);
361
+ if (pruned.length === 0)
362
+ return { state, pruned };
363
+ return {
364
+ state: {
365
+ ...state,
366
+ updatedAt: new Date(nowMs).toISOString(),
367
+ accounts,
368
+ },
369
+ pruned,
370
+ };
371
+ }
@@ -0,0 +1,27 @@
1
+ import { executeAllowedWpCommand } from "#trust/command-runner.js";
2
+ import type { ReviewTargetCheckout } from "#review/execution/review-checkout.js";
3
+ export interface ReviewGateVerificationCommand {
4
+ readonly command: string;
5
+ readonly outcome: "passed" | "failed" | "timed-out" | "deferred";
6
+ readonly durationMs: number;
7
+ readonly exitCode: number | null;
8
+ readonly signal?: string;
9
+ }
10
+ export interface ReviewGateVerification {
11
+ readonly status: "passed" | "failed";
12
+ readonly commands: readonly ReviewGateVerificationCommand[];
13
+ readonly failureCode?: string;
14
+ readonly logPath?: string;
15
+ }
16
+ export interface VerifyDeliveryGatesInput {
17
+ readonly purpose: "plan" | "delivery";
18
+ readonly blueprintContent: string;
19
+ readonly projectRoot: string;
20
+ readonly gateId: string;
21
+ readonly checkout: ReviewTargetCheckout;
22
+ readonly signal?: AbortSignal;
23
+ readonly executeCommand?: typeof executeAllowedWpCommand;
24
+ }
25
+ export declare function verifyDeliveryPromotionGates(input: VerifyDeliveryGatesInput): ReviewGateVerification | undefined;
26
+ export declare function verificationPromptSummary(verification: ReviewGateVerification | undefined): string | undefined;
27
+ export declare function verificationArtifactSection(verification: ReviewGateVerification | undefined): string;
@@ -0,0 +1,115 @@
1
+ import path from "node:path";
2
+ import { executeAllowedWpCommand, relativeCommandLogPath, } from "#trust/command-runner.js";
3
+ import { parseTrustDossier } from "#trust/dossier.js";
4
+ import { parseAllowedWpCommand } from "#trust/gates.js";
5
+ export function verifyDeliveryPromotionGates(input) {
6
+ if (input.purpose !== "delivery")
7
+ return undefined;
8
+ const parsed = parseTrustDossier(input.blueprintContent);
9
+ if (parsed.violations.length > 0) {
10
+ if (parsed.violations.length === 1 &&
11
+ parsed.violations[0]?.section === "Trust Dossier" &&
12
+ parsed.violations[0]?.message === "missing Trust Dossier section") {
13
+ return undefined;
14
+ }
15
+ return {
16
+ status: "failed",
17
+ commands: [],
18
+ failureCode: "malformed-trust-dossier",
19
+ };
20
+ }
21
+ const gates = parsed.dossier?.gates ?? [];
22
+ if (gates.length === 0)
23
+ return undefined;
24
+ const commands = [];
25
+ const executeCommand = input.executeCommand ?? executeAllowedWpCommand;
26
+ const logRoot = path.join(input.projectRoot, ".webpresso", "logs", "delivery-verification", input.gateId);
27
+ for (const gate of gates) {
28
+ if (input.signal?.aborted) {
29
+ return {
30
+ status: "failed",
31
+ commands,
32
+ failureCode: "verification-cancelled",
33
+ };
34
+ }
35
+ if (isApprovalDependentLifecycleGate(gate.command)) {
36
+ commands.push({
37
+ command: gate.command,
38
+ outcome: "deferred",
39
+ durationMs: 0,
40
+ exitCode: null,
41
+ });
42
+ continue;
43
+ }
44
+ const result = executeCommand({
45
+ command: gate.command,
46
+ executionRoot: input.checkout.cwd,
47
+ logRoot,
48
+ env: { CI: "1", NO_COLOR: "1", WP_FORCE_SOURCE: "1" },
49
+ });
50
+ commands.push(publicCommandResult(result));
51
+ if (result.outcome !== "passed") {
52
+ return {
53
+ status: "failed",
54
+ commands,
55
+ failureCode: result.failureCode ?? "verification-command-failed",
56
+ logPath: relativeCommandLogPath(input.projectRoot, result.logPath),
57
+ };
58
+ }
59
+ const changes = input.checkout.changes();
60
+ if (changes.length > 0) {
61
+ return {
62
+ status: "failed",
63
+ commands,
64
+ failureCode: "verification-worktree-contaminated",
65
+ logPath: relativeCommandLogPath(input.projectRoot, result.logPath),
66
+ };
67
+ }
68
+ }
69
+ return {
70
+ status: "passed",
71
+ commands,
72
+ };
73
+ }
74
+ export function verificationPromptSummary(verification) {
75
+ if (!verification)
76
+ return undefined;
77
+ if (verification.status !== "passed")
78
+ return undefined;
79
+ return [
80
+ "Host verification before outside review: passed.",
81
+ ...verification.commands.map((command, index) => `${index + 1}. ${command.command}: ${command.outcome === "deferred" ? "deferred until review publication" : "passed"}`),
82
+ ].join("\n");
83
+ }
84
+ function isApprovalDependentLifecycleGate(command) {
85
+ try {
86
+ const [, subcommand, kind] = parseAllowedWpCommand(command);
87
+ return subcommand === "audit" && kind === "blueprint-lifecycle";
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ }
93
+ export function verificationArtifactSection(verification) {
94
+ if (!verification)
95
+ return "";
96
+ const lines = [
97
+ "## Host Verification",
98
+ "",
99
+ `Status: ${verification.status}`,
100
+ ...verification.commands.map((command, index) => `${index + 1}. ${command.command} -> ${command.outcome} (${command.durationMs}ms, exit ${command.exitCode ?? "null"})`),
101
+ ...(verification.failureCode ? [`Failure code: ${verification.failureCode}`] : []),
102
+ ...(verification.logPath ? [`Log: ${verification.logPath}`] : []),
103
+ "",
104
+ ];
105
+ return `${lines.join("\n")}\n`;
106
+ }
107
+ function publicCommandResult(result) {
108
+ return {
109
+ command: result.command,
110
+ outcome: result.outcome,
111
+ durationMs: result.durationMs,
112
+ exitCode: result.exitCode,
113
+ ...(result.signal ? { signal: result.signal } : {}),
114
+ };
115
+ }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ export declare const MAINTAINER_OVERRIDE_REVIEWER = "maintainer-override";
2
3
  export declare const reviewEventSchema: z.ZodObject<{
3
4
  schemaVersion: z.ZodLiteral<2>;
4
5
  id: z.ZodString;
@@ -16,12 +17,12 @@ export declare const reviewEventSchema: z.ZodObject<{
16
17
  gateId: z.ZodString;
17
18
  promptHash: z.ZodString;
18
19
  reviewer: z.ZodString;
19
- provider: z.ZodEnum<{
20
+ provider: z.ZodOptional<z.ZodEnum<{
20
21
  claude: "claude";
21
22
  codex: "codex";
22
23
  grok: "grok";
23
24
  opencode: "opencode";
24
- }>;
25
+ }>>;
25
26
  model: z.ZodOptional<z.ZodString>;
26
27
  effort: z.ZodOptional<z.ZodEnum<{
27
28
  high: "high";
@@ -9,6 +9,14 @@ const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
9
9
  const COMMIT_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u;
10
10
  const BLUEPRINT_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)*$/u;
11
11
  const ARTIFACT_PATH_PATTERN = /^review-artifacts\/[a-zA-Z0-9._-]+\.md$/u;
12
+ // The honest, auditable escape hatch reviewer id. A maintainer override is a
13
+ // HUMAN adjudication (not a machine backend), recorded only by `wp review
14
+ // override` after a real strong-reviewer rejection of the same subject. It
15
+ // satisfies the promotion gate DISTINCTLY (never folded into a strong or
16
+ // OpenCode-Go approval). Defined here — the lowest review-core module — so both
17
+ // the ledger read-time policy below and the lifecycle approval gate share one
18
+ // source of truth without an import cycle.
19
+ export const MAINTAINER_OVERRIDE_REVIEWER = "maintainer-override";
12
20
  export const reviewEventSchema = z
13
21
  .object({
14
22
  schemaVersion: z.literal(2),
@@ -21,7 +29,10 @@ export const reviewEventSchema = z
21
29
  gateId: z.string().min(1).max(120),
22
30
  promptHash: z.string().regex(SHA256_PATTERN),
23
31
  reviewer: z.string().min(1).max(120),
24
- provider: z.enum(["claude", "codex", "opencode", "grok"]),
32
+ // Optional: present for machine-backend reviews (always set by `wp review
33
+ // gate`/`log`); absent for a human maintainer-override, which has no AI
34
+ // backend. Descriptive metadata only — no policy keys off `provider`.
35
+ provider: z.enum(["claude", "codex", "opencode", "grok"]).optional(),
25
36
  model: z.string().min(1).max(240).optional(),
26
37
  effort: z.enum(["medium", "high"]).optional(),
27
38
  stage: z.string().min(1).max(120),
@@ -81,7 +92,7 @@ function orderedEvent(event) {
81
92
  gateId: event.gateId,
82
93
  promptHash: event.promptHash,
83
94
  reviewer: event.reviewer,
84
- provider: event.provider,
95
+ ...(event.provider ? { provider: event.provider } : {}),
85
96
  ...(event.model ? { model: event.model } : {}),
86
97
  ...(event.effort ? { effort: event.effort } : {}),
87
98
  stage: event.stage,
@@ -164,10 +175,19 @@ function isManualMarker(value) {
164
175
  // dodge the policy while real gate ids (content hashes) can never collide.
165
176
  return value.trim().toLowerCase() === "manual";
166
177
  }
178
+ // Reviewers whose manual-stage approvals are legitimate governance decisions,
179
+ // not disguised machine approvals: a plain `human` review and a
180
+ // `maintainer-override` (a maintainer's hand-recorded adjudication). Both are
181
+ // human authority by construction, so a manual marker on their events is
182
+ // expected rather than a policy dodge.
183
+ const HUMAN_LIKE_REVIEWERS = new Set(["human", MAINTAINER_OVERRIDE_REVIEWER]);
184
+ function isHumanLikeReviewer(reviewer) {
185
+ return HUMAN_LIKE_REVIEWERS.has(canonicalReviewer(reviewer));
186
+ }
167
187
  function isManualMachineApproval(event) {
168
188
  return (isPolicyApprovalVerdict(event.verdict) &&
169
189
  (isManualMarker(event.gateId) || isManualMarker(event.stage)) &&
170
- canonicalReviewer(event.reviewer) !== "human");
190
+ !isHumanLikeReviewer(event.reviewer));
171
191
  }
172
192
  // `compromisedIds` marks events whose artifact evidence failed integrity
173
193
  // verification. They still PARTICIPATE in reviewer supersession — a later