@webpresso/agent-kit 3.1.30 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +53 -15
  2. package/bin/_run.js +183 -20
  3. package/catalog/AGENTS.md.tpl +3 -3
  4. package/catalog/agent/agents/implementer.md +45 -0
  5. package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
  6. package/catalog/agent/rules/pre-implementation.md +28 -1
  7. package/catalog/agent/rules/supported-agent-clis.md +9 -0
  8. package/catalog/agent/rules/typescript-version.md +50 -0
  9. package/catalog/agent/skills/codex/SKILL.md +25 -1
  10. package/catalog/agent/skills/verify/SKILL.md +4 -2
  11. package/catalog/docs/templates/blueprint.md +1 -1
  12. package/dist/esm/audit/agents.js +1 -0
  13. package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
  14. package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
  15. package/dist/esm/audit/blueprint-pr-coverage.js +4 -0
  16. package/dist/esm/audit/blueprint-trust.js +14 -10
  17. package/dist/esm/audit/changeset-evidence.d.ts +23 -0
  18. package/dist/esm/audit/changeset-evidence.js +30 -0
  19. package/dist/esm/audit/changeset-required.d.ts +10 -0
  20. package/dist/esm/audit/changeset-required.js +73 -0
  21. package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
  22. package/dist/esm/audit/ci-guardrails-detection.js +68 -0
  23. package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
  24. package/dist/esm/audit/ci-guardrails-wiring.js +80 -0
  25. package/dist/esm/audit/ci-test-perf.js +30 -0
  26. package/dist/esm/audit/command-surface.js +4 -2
  27. package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
  28. package/dist/esm/audit/host-substitution-risk.js +256 -0
  29. package/dist/esm/audit/registry.d.ts +4 -4
  30. package/dist/esm/audit/registry.js +13 -1
  31. package/dist/esm/audit/toolchain-isolation.js +1 -2
  32. package/dist/esm/audit/typescript-version.d.ts +2 -0
  33. package/dist/esm/audit/typescript-version.js +123 -0
  34. package/dist/esm/blueprint/core/parser.js +101 -2
  35. package/dist/esm/blueprint/core/schema.d.ts +1 -0
  36. package/dist/esm/blueprint/core/schema.js +26 -0
  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 +87 -20
  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/local.d.ts +2 -1
  46. package/dist/esm/blueprint/lifecycle/local.js +19 -3
  47. package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
  48. package/dist/esm/blueprint/lifecycle/review-provenance.js +68 -12
  49. package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
  50. package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
  51. package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
  52. package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
  53. package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
  54. package/dist/esm/blueprint/sync/client.d.ts +2 -1
  55. package/dist/esm/blueprint/sync/client.js +1 -1
  56. package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
  57. package/dist/esm/blueprint/trust/command-runner.js +148 -0
  58. package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
  59. package/dist/esm/blueprint/trust/dossier.js +40 -6
  60. package/dist/esm/blueprint/trust/gates.d.ts +7 -0
  61. package/dist/esm/blueprint/trust/gates.js +20 -0
  62. package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
  63. package/dist/esm/blueprint/trust/promotion.js +74 -112
  64. package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
  65. package/dist/esm/blueprint/trust/scaffold.js +64 -0
  66. package/dist/esm/blueprint/trust/validator.d.ts +1 -0
  67. package/dist/esm/blueprint/trust/validator.js +7 -1
  68. package/dist/esm/blueprint/utils/archive.js +27 -5
  69. package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
  70. package/dist/esm/blueprint/utils/document-paths.js +45 -12
  71. package/dist/esm/build/cli-mcp-parity.js +15 -0
  72. package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
  73. package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
  74. package/dist/esm/cli/cli.d.ts +1 -1
  75. package/dist/esm/cli/cli.js +43 -0
  76. package/dist/esm/cli/commands/agent-launch.d.ts +8 -1
  77. package/dist/esm/cli/commands/agent-launch.js +93 -4
  78. package/dist/esm/cli/commands/audit-core.d.ts +1 -1
  79. package/dist/esm/cli/commands/audit-core.js +8 -1
  80. package/dist/esm/cli/commands/audit.js +21 -5
  81. package/dist/esm/cli/commands/blueprint/execution.js +18 -6
  82. package/dist/esm/cli/commands/blueprint/mutations.js +59 -40
  83. package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
  84. package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
  85. package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
  86. package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
  87. package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
  88. package/dist/esm/cli/commands/blueprint/router.js +13 -1
  89. package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
  90. package/dist/esm/cli/commands/ci-preflight.js +30 -6
  91. package/dist/esm/cli/commands/compile.js +5 -31
  92. package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
  93. package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
  94. package/dist/esm/cli/commands/doctor.js +5 -0
  95. package/dist/esm/cli/commands/hook.d.ts +5 -0
  96. package/dist/esm/cli/commands/hook.js +15 -9
  97. package/dist/esm/cli/commands/init/config.js +2 -2
  98. package/dist/esm/cli/commands/init/convergence-apply.d.ts +88 -0
  99. package/dist/esm/cli/commands/init/convergence-apply.js +325 -0
  100. package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
  101. package/dist/esm/cli/commands/init/convergence-state.js +54 -0
  102. package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
  103. package/dist/esm/cli/commands/init/convergence.js +57 -0
  104. package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
  105. package/dist/esm/cli/commands/init/git-convergence.js +168 -0
  106. package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
  107. package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
  108. package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
  109. package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
  110. package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
  111. package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
  112. package/dist/esm/cli/commands/init/host-visibility.js +94 -5
  113. package/dist/esm/cli/commands/init/index.d.ts +3 -0
  114. package/dist/esm/cli/commands/init/index.js +326 -173
  115. package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
  116. package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
  117. package/dist/esm/cli/commands/init/merge.d.ts +1 -0
  118. package/dist/esm/cli/commands/init/merge.js +30 -1
  119. package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
  120. package/dist/esm/cli/commands/init/package-root.js +10 -2
  121. package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
  122. package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
  123. package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
  124. package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
  125. package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +20 -0
  126. package/dist/esm/cli/commands/init/scaffold-base-kit.js +61 -5
  127. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
  128. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
  129. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
  130. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +54 -24
  131. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
  132. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +288 -202
  133. package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +22 -0
  134. package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +230 -0
  135. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
  136. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
  137. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
  138. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
  139. package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
  140. package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
  141. package/dist/esm/cli/commands/opencode-probe.d.ts +107 -0
  142. package/dist/esm/cli/commands/opencode-probe.js +323 -0
  143. package/dist/esm/cli/commands/opencode-rotate.d.ts +72 -0
  144. package/dist/esm/cli/commands/opencode-rotate.js +147 -0
  145. package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
  146. package/dist/esm/cli/commands/public-readiness.js +42 -0
  147. package/dist/esm/cli/commands/quality-runner.js +3 -3
  148. package/dist/esm/cli/commands/review.d.ts +35 -3
  149. package/dist/esm/cli/commands/review.js +751 -209
  150. package/dist/esm/cli/commands/run-wait.d.ts +2 -0
  151. package/dist/esm/cli/commands/run-wait.js +122 -0
  152. package/dist/esm/cli/commands/rust-check.d.ts +24 -0
  153. package/dist/esm/cli/commands/rust-check.js +110 -0
  154. package/dist/esm/cli/commands/setup-help.js +1 -1
  155. package/dist/esm/cli/commands/setup-repair-help.js +1 -1
  156. package/dist/esm/cli/commands/typecheck.d.ts +1 -0
  157. package/dist/esm/cli/commands/typecheck.js +30 -1
  158. package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +6 -0
  159. package/dist/esm/cli/commands/worktree/router-dispatch.js +126 -9
  160. package/dist/esm/cli/commands/worktree/router.js +10 -0
  161. package/dist/esm/cli/direct-provider-launch.js +9 -5
  162. package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
  163. package/dist/esm/cli/optional-tool-freshness.js +219 -9
  164. package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
  165. package/dist/esm/daemon/domains/git-actions.js +136 -0
  166. package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
  167. package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
  168. package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
  169. package/dist/esm/daemon/domains/sessions-search.js +29 -0
  170. package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
  171. package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
  172. package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
  173. package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
  174. package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
  175. package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
  176. package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
  177. package/dist/esm/daemon/handlers/git-handlers.js +27 -0
  178. package/dist/esm/daemon/handlers/product.d.ts +7 -0
  179. package/dist/esm/daemon/handlers/product.js +10 -119
  180. package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
  181. package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
  182. package/dist/esm/daemon/handlers/shared.d.ts +5 -0
  183. package/dist/esm/daemon/handlers/shared.js +16 -0
  184. package/dist/esm/daemon/methods.d.ts +8 -1
  185. package/dist/esm/daemon/methods.js +7 -0
  186. package/dist/esm/daemon/protocol/contract.d.ts +43 -0
  187. package/dist/esm/daemon/protocol/contract.js +26 -0
  188. package/dist/esm/daemon/protocol/index.d.ts +1 -1
  189. package/dist/esm/daemon/protocol/index.js +1 -1
  190. package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
  191. package/dist/esm/hooks/__conformance__/matrix.js +137 -0
  192. package/dist/esm/hooks/conformance/batch.js +5 -0
  193. package/dist/esm/hooks/doctor.d.ts +5 -3
  194. package/dist/esm/hooks/doctor.js +67 -16
  195. package/dist/esm/hooks/permission-request/index.js +9 -3
  196. package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
  197. package/dist/esm/hooks/permission-request/policy.js +13 -0
  198. package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
  199. package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
  200. package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
  201. package/dist/esm/hooks/pretool-guard/logger.js +16 -0
  202. package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
  203. package/dist/esm/hooks/pretool-guard/runner.js +68 -16
  204. package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
  205. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +54 -0
  206. package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
  207. package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
  208. package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
  209. package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
  210. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
  211. package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
  212. package/dist/esm/hooks/shared/guard-outcome.js +215 -0
  213. package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
  214. package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
  215. package/dist/esm/hooks/shared/types.d.ts +8 -5
  216. package/dist/esm/hooks/shared/types.js +50 -9
  217. package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
  218. package/dist/esm/hooks/stop/qa-changed-files.js +92 -0
  219. package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
  220. package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
  221. package/dist/esm/mcp/blueprint/handlers/document-mutations.js +109 -11
  222. package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
  223. package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
  224. package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
  225. package/dist/esm/mcp/blueprint/registration.js +3 -2
  226. package/dist/esm/mcp/server.d.ts +1 -0
  227. package/dist/esm/mcp/server.js +27 -0
  228. package/dist/esm/mcp/tools/_names.d.ts +1 -1
  229. package/dist/esm/mcp/tools/_names.js +5 -0
  230. package/dist/esm/mcp/tools/_registry.js +10 -0
  231. package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
  232. package/dist/esm/mcp/tools/_shared/result.js +44 -1
  233. package/dist/esm/mcp/tools/audit.d.ts +5 -1
  234. package/dist/esm/mcp/tools/audit.js +42 -0
  235. package/dist/esm/mcp/tools/audits.d.ts +4 -0
  236. package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
  237. package/dist/esm/mcp/tools/ci-preflight.js +183 -0
  238. package/dist/esm/mcp/tools/pr-upsert.js +39 -1
  239. package/dist/esm/mcp/tools/review-run.d.ts +28 -0
  240. package/dist/esm/mcp/tools/review-run.js +216 -0
  241. package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
  242. package/dist/esm/mcp/tools/run-wait.js +593 -0
  243. package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
  244. package/dist/esm/mcp/tools/session-id.d.ts +3 -0
  245. package/dist/esm/mcp/tools/session-id.js +45 -0
  246. package/dist/esm/mcp/tools/session-info.d.ts +3 -0
  247. package/dist/esm/mcp/tools/session-info.js +49 -0
  248. package/dist/esm/output-transforms/audit.d.ts +23 -0
  249. package/dist/esm/output-transforms/audit.js +47 -0
  250. package/dist/esm/output-transforms/index.js +2 -0
  251. package/dist/esm/package.json +2 -0
  252. package/dist/esm/platform/client.d.ts +7 -1
  253. package/dist/esm/platform/github-contract-source.d.ts +2 -1
  254. package/dist/esm/pr-description/contract.js +24 -4
  255. package/dist/esm/review/authority.js +27 -14
  256. package/dist/esm/review/availability.d.ts +33 -0
  257. package/dist/esm/review/availability.js +122 -20
  258. package/dist/esm/review/delivery-verifier.d.ts +27 -0
  259. package/dist/esm/review/delivery-verifier.js +115 -0
  260. package/dist/esm/review/events.d.ts +3 -2
  261. package/dist/esm/review/events.js +23 -3
  262. package/dist/esm/review/execution/adapters.d.ts +11 -1
  263. package/dist/esm/review/execution/adapters.js +36 -10
  264. package/dist/esm/review/execution/artifacts.d.ts +1 -1
  265. package/dist/esm/review/execution/output-policy.d.ts +2 -0
  266. package/dist/esm/review/execution/output-policy.js +12 -0
  267. package/dist/esm/review/execution/review-checkout.js +121 -21
  268. package/dist/esm/review/execution/supervisor.js +36 -4
  269. package/dist/esm/review/execution/types.d.ts +9 -0
  270. package/dist/esm/review/opencode-account.d.ts +24 -0
  271. package/dist/esm/review/opencode-account.js +68 -0
  272. package/dist/esm/review/opencode-auth.d.ts +33 -0
  273. package/dist/esm/review/opencode-auth.js +79 -0
  274. package/dist/esm/review/subject.d.ts +25 -0
  275. package/dist/esm/review/subject.js +116 -26
  276. package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
  277. package/dist/esm/runtime/opencode-account-materializer.js +33 -0
  278. package/dist/esm/rust/affected-manifests.d.ts +38 -0
  279. package/dist/esm/rust/affected-manifests.js +69 -0
  280. package/dist/esm/session-memory/fetch-index.d.ts +1 -1
  281. package/dist/esm/status/snapshot.js +32 -25
  282. package/dist/esm/test/duration-sequencer.d.ts +20 -0
  283. package/dist/esm/test/duration-sequencer.js +54 -0
  284. package/dist/esm/test/shard-durations.json +922 -0
  285. package/dist/esm/test/worker-budget.d.ts +6 -0
  286. package/dist/esm/test/worker-budget.js +59 -1
  287. package/dist/esm/test-helpers/global-setup.d.ts +1 -2
  288. package/dist/esm/test-helpers/global-setup.js +79 -32
  289. package/dist/esm/typecheck/planner.d.ts +9 -1
  290. package/dist/esm/typecheck/planner.js +31 -0
  291. package/dist/esm/ultragoal/service.d.ts +7 -0
  292. package/dist/esm/ultragoal/service.js +42 -2
  293. package/dist/esm/utils/write-json-file.js +3 -1
  294. package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
  295. package/dist/esm/worktrees/codex-project-trust.js +56 -0
  296. package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
  297. package/dist/esm/worktrees/orphan-scan.js +211 -0
  298. package/dist/esm/worktrees/registry.d.ts +8 -0
  299. package/dist/esm/worktrees/registry.js +18 -3
  300. package/package.json +18 -13
@@ -1,14 +1,12 @@
1
1
  import { execFileSync, spawnSync } from "node:child_process";
2
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { existsSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { validateBlueprintTrust } from "./validator.js";
5
5
  import { parseTrustDossier } from "./dossier.js";
6
- import { parseAllowedWpCommand } from "./gates.js";
7
- import { resolvePackageAssetPreferred } from "#utils/package-assets.js";
6
+ import { parseAllowedWpCommand, wpTestFileTargets } from "./gates.js";
7
+ import { ALLOWED_WP_COMMAND_TIMEOUT_MS, executeAllowedWpCommand, tailBounded, } from "./command-runner.js";
8
8
  export { parseAllowedWpCommand };
9
- const PROMOTION_GATE_TIMEOUT_MS = 30_000;
10
- const PROMOTION_GATE_STDIO_TAIL_LIMIT = 500;
11
- export function applyPromotionTrustGate(input) {
9
+ export function preparePromotionTrustGate(input) {
12
10
  const now = (input.now ?? new Date()).toISOString();
13
11
  const syntacticMarkdown = upsertReadinessValue(upsertReadinessValue(input.markdown, "verified-at", now), "verified-head", "0123456789abcdef0123456789abcdef01234567");
14
12
  const parsedBeforeHead = parseTrustDossier(syntacticMarkdown);
@@ -30,6 +28,25 @@ export function applyPromotionTrustGate(input) {
30
28
  throw new Error(`Blueprint trust gate failed: ${preflight.violations.map((v) => `${v.section}: ${v.message}`).join("; ")}`);
31
29
  const parsed = parseTrustDossier(markdown);
32
30
  for (const gate of parsed.dossier?.gates ?? []) {
31
+ if (gate.defer === "pre-implementation") {
32
+ // Explicit opt-in deferral (Trust Dossier "Defer" column): the gate's
33
+ // command is never executed nor existence-checked at promote time. This
34
+ // is deliberately narrower than the automatic `wp test --file` deferral
35
+ // below -- it only applies when a human/agent explicitly asked for it,
36
+ // so a genuinely broken/mistyped gate command still fails closed.
37
+ markdown = updateGateLastResult(markdown, gate.gate, "deferred: pre-implementation (explicit defer marker)");
38
+ continue;
39
+ }
40
+ const deferredTarget = firstMissingTestTarget(input.repoRoot, gate.command);
41
+ if (deferredTarget !== undefined) {
42
+ // A `wp test --file <path>` gate whose target file does not exist yet is
43
+ // recorded as an honest non-`pass` result instead of aborting the whole
44
+ // promotion. `deferred` is a distinct failing state: it is only tolerated
45
+ // by the post-run validation below (allowDeferredGates), and the strict
46
+ // whole-repo completion audit still rejects it, so it can never merge.
47
+ markdown = updateGateLastResult(markdown, gate.gate, `deferred: test file not yet created (${deferredTarget})`);
48
+ continue;
49
+ }
33
50
  runPromotionCommand(input.repoRoot, gate.command);
34
51
  markdown = updateGateLastResult(markdown, gate.gate, `pass at ${now}`);
35
52
  }
@@ -40,10 +57,27 @@ export function applyPromotionTrustGate(input) {
40
57
  markdown,
41
58
  promotionCandidate: true,
42
59
  scanTaskAmbiguity: true,
60
+ allowDeferredGates: true,
43
61
  });
44
62
  if (!validated.ok)
45
63
  throw new Error(`Blueprint trust gate failed: ${validated.violations.map((v) => `${v.section}: ${v.message}`).join("; ")}`);
46
- return markdown;
64
+ return {
65
+ file: input.file,
66
+ sourceMarkdown: input.markdown,
67
+ verifiedHead: head,
68
+ trustedMarkdown: markdown,
69
+ };
70
+ }
71
+ export function applyPromotionTrustGate(input) {
72
+ return preparePromotionTrustGate(input).trustedMarkdown;
73
+ }
74
+ export function assertPromotionTrustProofCurrent(input, proof) {
75
+ if (input.file !== proof.file || input.markdown !== proof.sourceMarkdown) {
76
+ throw new Error("Blueprint trust gate invalidated: the blueprint changed after external gates ran; retry promotion");
77
+ }
78
+ if (readHead(input.repoRoot) !== proof.verifiedHead) {
79
+ throw new Error("Blueprint trust gate invalidated: git HEAD changed after external gates ran; retry promotion");
80
+ }
47
81
  }
48
82
  function readHead(repoRoot) {
49
83
  const testHead = process.env["WP_BLUEPRINT_TRUST_GATE_TEST_HEAD"];
@@ -61,123 +95,48 @@ function readHead(repoRoot) {
61
95
  throw new Error("Blueprint trust gate failed: git HEAD is unavailable");
62
96
  }
63
97
  }
98
+ /**
99
+ * Returns the first `wp test --file` target of `command` that does not exist on
100
+ * disk (resolved against `repoRoot`), or undefined when the gate is not a
101
+ * `wp test --file` gate or all its targets exist. A missing target is
102
+ * indistinguishable at `existsSync` time from "renamed / wrong path / deleted",
103
+ * so callers must treat it as a non-`pass` deferral, never a success.
104
+ */
105
+ function firstMissingTestTarget(repoRoot, command) {
106
+ const targets = wpTestFileTargets(parseAllowedWpCommand(command));
107
+ return targets.find((target) => !existsSync(join(repoRoot, target)));
108
+ }
64
109
  export function runPromotionCommand(repoRoot, command, deps = {}) {
65
- const argv = parseAllowedWpCommand(command);
66
- const [binary, ...args] = argv;
67
- if (binary === undefined)
68
- throw new Error(`Promotion gate command is empty: ${command}`);
69
- const invocation = resolvePromotionGateInvocation(repoRoot, binary, args);
70
- const spawn = deps.spawn ?? spawnSync;
71
- const result = spawn(invocation.command, invocation.args, {
72
- cwd: repoRoot,
73
- encoding: "utf8",
74
- stdio: ["ignore", "pipe", "pipe"],
75
- timeout: PROMOTION_GATE_TIMEOUT_MS,
76
- env: invocation.env,
110
+ const result = executeAllowedWpCommand({
111
+ command,
112
+ executionRoot: repoRoot,
113
+ logRoot: join(repoRoot, ".webpresso", "logs", "promotion-gates"),
114
+ ...(deps.spawn ? { spawn: deps.spawn } : {}),
77
115
  });
78
- if (result.status === 0 && !result.error && result.signal === null)
116
+ if (result.outcome === "passed")
79
117
  return;
80
- const stdout = typeof result.stdout === "string" ? result.stdout : "";
81
- const stderr = typeof result.stderr === "string" ? result.stderr : "";
82
- const logPath = writePromotionGateLog(repoRoot, command, invocation, stdout, stderr, result);
83
- throw new Error(formatPromotionGateFailure(command, stdout, stderr, result, logPath));
118
+ throw new Error(formatPromotionGateFailure(result));
84
119
  }
85
- function resolvePromotionGateInvocation(repoRoot, binary, args) {
86
- if (binary === "./bin/wp" || binary === "wp") {
87
- const localLauncher = resolveRepoWpLauncher(repoRoot);
88
- if (localLauncher !== null) {
89
- return {
90
- command: localLauncher,
91
- args: [...args],
92
- env: { ...process.env, PATH: `${repoRoot}/bin:${process.env["PATH"] ?? ""}` },
93
- };
94
- }
95
- if (binary === "./bin/wp") {
96
- return {
97
- command: join(repoRoot, "bin", process.platform === "win32" ? "wp.cmd" : "wp"),
98
- args: [...args],
99
- env: { ...process.env, PATH: `${repoRoot}/bin:${process.env["PATH"] ?? ""}` },
100
- };
101
- }
102
- const packagedLauncher = resolvePackagedWpLauncher();
103
- return {
104
- command: process.execPath,
105
- args: [packagedLauncher, ...args],
106
- env: { ...process.env },
107
- };
108
- }
109
- return {
110
- command: binary,
111
- args: [...args],
112
- env: { ...process.env, PATH: `${repoRoot}/bin:${process.env["PATH"] ?? ""}` },
113
- };
114
- }
115
- function resolveRepoWpLauncher(repoRoot) {
116
- const launcher = join(repoRoot, "bin", process.platform === "win32" ? "wp.cmd" : "wp");
117
- return existsSync(launcher) ? launcher : null;
118
- }
119
- function resolvePackagedWpLauncher() {
120
- const launcher = resolvePackageAssetPreferred([
121
- process.platform === "win32" ? "bin/wp.cmd" : "bin/wp",
122
- "bin/wp",
123
- ]);
124
- if (!existsSync(launcher)) {
125
- throw new Error(`Promotion gate failed: packaged wp launcher is unavailable at ${launcher}`);
126
- }
127
- return launcher;
128
- }
129
- function formatPromotionGateFailure(command, stdout, stderr, result, logPath) {
120
+ function formatPromotionGateFailure(result) {
130
121
  const details = [];
131
- if (typeof result.status === "number")
132
- details.push(`exit=${result.status}`);
122
+ if (typeof result.exitCode === "number")
123
+ details.push(`exit=${result.exitCode}`);
133
124
  if (result.signal)
134
125
  details.push(`signal=${result.signal}`);
135
- if (result.error) {
136
- const code = typeof result.error === "object" &&
137
- result.error !== null &&
138
- "code" in result.error &&
139
- typeof result.error.code === "string"
140
- ? result.error.code
141
- : null;
142
- details.push(code ? `spawn_error=${code}` : `spawn_error=${result.error.message}`);
143
- if (code === "ETIMEDOUT")
144
- details.push(`timeout=${PROMOTION_GATE_TIMEOUT_MS}ms`);
126
+ if (result.errorMessage) {
127
+ details.push(`spawn_error=${result.failureCode ?? result.errorMessage}`);
128
+ if (result.outcome === "timed-out")
129
+ details.push(`timeout=${ALLOWED_WP_COMMAND_TIMEOUT_MS}ms`);
145
130
  }
146
- const stderrTail = tailBounded(stderr);
147
- const stdoutTail = tailBounded(stdout);
131
+ const stderrTail = tailBounded(result.stderr);
132
+ const stdoutTail = tailBounded(result.stdout);
148
133
  if (stderrTail.length > 0)
149
134
  details.push(`stderr_tail=${JSON.stringify(stderrTail)}`);
150
135
  if (stdoutTail.length > 0)
151
136
  details.push(`stdout_tail=${JSON.stringify(stdoutTail)}`);
152
- details.push(`log=${logPath}`);
153
- return `Promotion gate failed (${command}): ${details.join("; ")}`;
154
- }
155
- function tailBounded(text) {
156
- if (text.length <= PROMOTION_GATE_STDIO_TAIL_LIMIT)
157
- return text.trim();
158
- return text.slice(-PROMOTION_GATE_STDIO_TAIL_LIMIT).trim();
159
- }
160
- function writePromotionGateLog(repoRoot, command, invocation, stdout, stderr, result) {
161
- const logsDir = join(repoRoot, ".webpresso", "logs", "promotion-gates");
162
- mkdirSync(logsDir, { recursive: true });
163
- const logPath = join(logsDir, `${Date.now()}-promotion-gate.log`);
164
- const errorBlock = result.error
165
- ? `error: ${result.error.name}: ${result.error.message}\n`
166
- : "error: none\n";
167
- writeFileSync(logPath, [
168
- `command: ${command}`,
169
- `invocation: ${invocation.command} ${invocation.args.join(" ")}`,
170
- `exit: ${result.status ?? "null"}`,
171
- `signal: ${result.signal ?? "null"}`,
172
- `timeout_ms: ${PROMOTION_GATE_TIMEOUT_MS}`,
173
- errorBlock.trimEnd(),
174
- "--- stderr ---",
175
- stderr,
176
- "--- stdout ---",
177
- stdout,
178
- "",
179
- ].join("\n"), "utf8");
180
- return logPath;
137
+ if (result.logPath)
138
+ details.push(`log=${result.logPath}`);
139
+ return `Promotion gate failed (${result.command}): ${details.join("; ")}`;
181
140
  }
182
141
  function upsertReadinessValue(markdown, key, value) {
183
142
  const re = new RegExp(`^- ${key}: .*$`, "mu");
@@ -206,6 +165,9 @@ function updateGateLastResult(markdown, gate, result) {
206
165
  if (cells[0] === gate && cells.length === 4) {
207
166
  lines[i] = `| ${cells[0]} | ${cells[1]} | ${cells[2]} | ${result} |`;
208
167
  }
168
+ else if (cells[0] === gate && cells.length === 5) {
169
+ lines[i] = `| ${cells[0]} | ${cells[1]} | ${cells[2]} | ${result} | ${cells[4]} |`;
170
+ }
209
171
  }
210
172
  return lines.join("\n");
211
173
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Trust Dossier authoring scaffold.
3
+ *
4
+ * `dossier.ts` / `evidence.ts` / `gates.ts` / `ambiguity.ts` define strict
5
+ * validation for a blueprint's `## Trust Dossier` section, but nothing
6
+ * generates a starting template. `TRUST_DOSSIER_SCAFFOLD` is a complete,
7
+ * already-valid worked example — proven by `scaffold.test.ts` to pass
8
+ * `validateBlueprintTrust` under both the strict configuration
9
+ * `wp audit blueprint-trust` uses and the draft-preflight configuration
10
+ * `wp_blueprint_put` / `preparePromotionTrustGate` use — so an author can
11
+ * paste it in and edit the example values instead of hand-deriving the
12
+ * contract from source.
13
+ *
14
+ * `verified-at` / `verified-head` stay as inert, obviously-fake placeholders:
15
+ * `preparePromotionTrustGate` (./promotion.ts) overwrites both with the real
16
+ * timestamp/HEAD on every promotion attempt, so there is no need for this
17
+ * pure, dependency-free module to shell out to git.
18
+ */
19
+ export declare const TRUST_DOSSIER_SCAFFOLD: string;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Trust Dossier authoring scaffold.
3
+ *
4
+ * `dossier.ts` / `evidence.ts` / `gates.ts` / `ambiguity.ts` define strict
5
+ * validation for a blueprint's `## Trust Dossier` section, but nothing
6
+ * generates a starting template. `TRUST_DOSSIER_SCAFFOLD` is a complete,
7
+ * already-valid worked example — proven by `scaffold.test.ts` to pass
8
+ * `validateBlueprintTrust` under both the strict configuration
9
+ * `wp audit blueprint-trust` uses and the draft-preflight configuration
10
+ * `wp_blueprint_put` / `preparePromotionTrustGate` use — so an author can
11
+ * paste it in and edit the example values instead of hand-deriving the
12
+ * contract from source.
13
+ *
14
+ * `verified-at` / `verified-head` stay as inert, obviously-fake placeholders:
15
+ * `preparePromotionTrustGate` (./promotion.ts) overwrites both with the real
16
+ * timestamp/HEAD on every promotion attempt, so there is no need for this
17
+ * pure, dependency-free module to shell out to git.
18
+ */
19
+ const PLACEHOLDER_HEAD = "0".repeat(40);
20
+ export const TRUST_DOSSIER_SCAFFOLD = `## Trust Dossier
21
+
22
+ <!-- Trust Dossier scaffold — generated by \`wp blueprint trust-dossier\`. -->
23
+ <!-- Replace every example value below, then delete these comment lines before promotion. -->
24
+ <!-- Constraints enforced by \`wp audit blueprint-trust\` / \`wp_blueprint_put\`: -->
25
+ <!-- - All 5 "###" subsections below are required, verbatim, exactly once each. -->
26
+ <!-- - Material Claims "repo:<path>" evidence must be a bare, existing repo-relative path -->
27
+ <!-- (no ":line" suffix, no leading "/", no "../"). -->
28
+ <!-- - "Residual Unknowns" must render exactly \`None.\` when nothing is outstanding. -->
29
+ <!-- - Promotion Gates "Command" must be a \`wp\` facade call: \`wp typecheck\`, \`wp lint\` -->
30
+ <!-- (no extra args), \`wp test --file <path>\` (repeatable), or -->
31
+ <!-- \`wp audit <kind> [--json|--affected|--branch|--base <val>]\`. -->
32
+ <!-- - No TBD, TODO, "decide during implementation", "open question", or <placeholder> tokens -->
33
+ <!-- anywhere in the 5 subsections below. -->
34
+
35
+ ### Readiness Verdict
36
+
37
+ - promotion-ready: true
38
+ - unresolved-count: 0
39
+ - verified-at: 1970-01-01T00:00:00.000Z
40
+ - verified-head: ${PLACEHOLDER_HEAD}
41
+ - trust-gate-version: v1
42
+
43
+ ### Material Claims
44
+
45
+ | ID | Claim | Evidence |
46
+ | --- | --- | --- |
47
+ | C1 | Example material claim — replace with a real, falsifiable claim this blueprint depends on. | repo:README.md |
48
+
49
+ ### Material Decisions
50
+
51
+ | ID | Decision | Chosen option | Rejected alternatives | Rationale |
52
+ | --- | --- | --- | --- | --- |
53
+ | D1 | Example material decision — replace with a real decision this blueprint locks in. | Example chosen option. | Example rejected alternative and why it lost. | Example rationale for the chosen option. |
54
+
55
+ ### Promotion Gates
56
+
57
+ | Gate | Command | Expected outcome | Last result |
58
+ | --- | --- | --- | --- |
59
+ | typecheck | wp typecheck | Typecheck passes with zero errors. | pass (scaffold example — rerun and record the real result before promotion) |
60
+
61
+ ### Residual Unknowns
62
+
63
+ None.
64
+ `;
@@ -8,6 +8,7 @@ export type ValidateBlueprintTrustInput = {
8
8
  promotionCandidate?: boolean;
9
9
  requirePassingGates?: boolean;
10
10
  scanTaskAmbiguity?: boolean;
11
+ allowDeferredGates?: boolean;
11
12
  };
12
13
  export type BlueprintTrustViolation = TrustDossierViolation & {
13
14
  file: string;
@@ -51,7 +51,8 @@ export function validateBlueprintTrust(input) {
51
51
  message: error instanceof Error ? error.message : String(error),
52
52
  });
53
53
  }
54
- if (input.requirePassingGates !== false && !/^pass\b/iu.test(gate.lastResult))
54
+ if (input.requirePassingGates !== false &&
55
+ !isAcceptableGateResult(gate.lastResult, input.allowDeferredGates))
55
56
  violations.push({
56
57
  section: "Promotion Gates",
57
58
  claimId: gate.gate,
@@ -64,3 +65,8 @@ export function validateBlueprintTrust(input) {
64
65
  violations: violations.map((v) => ({ ...v, file: input.file })),
65
66
  };
66
67
  }
68
+ function isAcceptableGateResult(lastResult, allowDeferredGates) {
69
+ if (/^pass\b/iu.test(lastResult))
70
+ return true;
71
+ return allowDeferredGates === true && /^deferred:/iu.test(lastResult);
72
+ }
@@ -6,6 +6,7 @@
6
6
  import { access, readFile, writeFile } from "node:fs/promises";
7
7
  import path from "node:path";
8
8
  import { parseBlueprint } from "#core/parser";
9
+ import { getBlueprintDocumentCandidates } from "./document-paths.js";
9
10
  import { resolveBlueprintRoot } from "./blueprint-root.js";
10
11
  /**
11
12
  * Validates that all tasks in a plan are complete.
@@ -111,7 +112,13 @@ export async function archiveBlueprint(slug, projectPath) {
111
112
  if (isAlreadyCompleted(slug)) {
112
113
  return { success: false, error: "Plan is already completed" };
113
114
  }
114
- const paths = buildPlanPaths(projectPath, slug);
115
+ const paths = await buildPlanPaths(projectPath, slug);
116
+ if (paths.ambiguousPaths) {
117
+ return {
118
+ success: false,
119
+ error: `Plan ${slug} exists in both flat and folder forms; remove one before archiving: ${paths.ambiguousPaths.join(", ")}`,
120
+ };
121
+ }
115
122
  // Check if plan exists
116
123
  const planExists = await checkPlanExists(paths.sourcePath);
117
124
  if (!planExists) {
@@ -132,16 +139,31 @@ export async function archiveBlueprint(slug, projectPath) {
132
139
  /**
133
140
  * Builds source and target paths for archival.
134
141
  *
142
+ * Resolves the existing flat or folder document via the shared candidates;
143
+ * when neither shape exists, falls back to the canonical folder path so the
144
+ * caller reports the same "Plan not found" error as before.
145
+ *
135
146
  * @param projectPath - Root path of the project
136
147
  * @param slug - Plan slug
137
148
  * @returns Path information
138
149
  */
139
- function buildPlanPaths(projectPath, slug) {
150
+ async function buildPlanPaths(projectPath, slug) {
140
151
  const plansDir = resolveBlueprintRoot(projectPath);
141
- const sourcePath = path.join(plansDir, slug, "_overview.md");
152
+ const candidates = getBlueprintDocumentCandidates(plansDir, slug);
153
+ const existing = [];
154
+ for (const candidate of candidates) {
155
+ if (await checkPlanExists(candidate))
156
+ existing.push(candidate);
157
+ }
158
+ const sourcePath = existing[0] ?? candidates.at(-1) ?? path.join(plansDir, slug, "_overview.md");
142
159
  const sourceDir = path.dirname(sourcePath);
143
- const targetDir = sourceDir;
144
- return { sourcePath, sourceDir, targetDir, planName: slug };
160
+ return {
161
+ sourcePath,
162
+ sourceDir,
163
+ targetDir: sourceDir,
164
+ planName: slug,
165
+ ...(existing.length > 1 ? { ambiguousPaths: existing } : {}),
166
+ };
145
167
  }
146
168
  /**
147
169
  * Extracts plan name from slug (removes status prefix).
@@ -8,6 +8,13 @@ export interface BlueprintDocumentPath {
8
8
  slug: string;
9
9
  state: BlueprintStatus;
10
10
  }
11
+ export interface BlueprintDocumentIdentity extends BlueprintDocumentPath {
12
+ documentPath: string;
13
+ folderDirectory: string;
14
+ folderPath: string;
15
+ lifecycleSlug: string;
16
+ stableSlug: string;
17
+ }
11
18
  export declare function normalizeBlueprintPath(filePath: string): string;
12
19
  export declare function isBlueprintStatus(value: string | undefined): value is BlueprintStatus;
13
20
  export declare function isBlueprintSlugSegment(value: string | undefined): value is string;
@@ -18,5 +25,16 @@ export declare function getBlueprintDocumentPaths(root: string, state: Blueprint
18
25
  flat: string;
19
26
  folder: string;
20
27
  };
28
+ export declare function resolveBlueprintDocumentIdentity(root: string, documentPath: string): BlueprintDocumentIdentity | null;
29
+ export declare function folderBlueprintRequiredMessage(identity: {
30
+ readonly documentPath: string;
31
+ readonly lifecycleSlug: string;
32
+ readonly shape: BlueprintShape;
33
+ }): string;
34
+ export declare function assertFolderBlueprintIdentity(identity: {
35
+ readonly documentPath: string;
36
+ readonly lifecycleSlug: string;
37
+ readonly shape: BlueprintShape;
38
+ }): void;
21
39
  export declare function getBlueprintDocumentCandidates(root: string, slug: string): string[];
22
40
  export declare function getBlueprintAlternateDocumentPath(root: string, filePath: string): string | null;
@@ -22,7 +22,7 @@ export function isBlueprintSlugSegment(value) {
22
22
  export function parseBlueprintDocumentRelativePath(filePath) {
23
23
  const normalized = normalizeBlueprintPath(filePath);
24
24
  const parts = normalized.split("/").filter((segment) => segment.length > 0);
25
- const [state, second, third] = parts;
25
+ const [state, second] = parts;
26
26
  if (!isBlueprintStatus(state)) {
27
27
  return null;
28
28
  }
@@ -38,13 +38,16 @@ export function parseBlueprintDocumentRelativePath(filePath) {
38
38
  state,
39
39
  };
40
40
  }
41
- if (parts.length === 3 &&
42
- isBlueprintSlugSegment(second) &&
43
- third === BLUEPRINT_OVERVIEW_FILENAME) {
41
+ if (parts.length >= 3 && parts.at(-1) === BLUEPRINT_OVERVIEW_FILENAME) {
42
+ const slugSegments = parts.slice(1, -1);
43
+ if (!slugSegments.every((segment) => isBlueprintSlugSegment(segment))) {
44
+ return null;
45
+ }
46
+ const slug = slugSegments.join("/");
44
47
  return {
45
- relativePath: `${state}/${second}/${BLUEPRINT_OVERVIEW_FILENAME}`,
48
+ relativePath: `${state}/${slug}/${BLUEPRINT_OVERVIEW_FILENAME}`,
46
49
  shape: "folder",
47
- slug: second,
50
+ slug,
48
51
  state,
49
52
  };
50
53
  }
@@ -53,13 +56,13 @@ export function parseBlueprintDocumentRelativePath(filePath) {
53
56
  export function isBlueprintSupportingMarkdownRelativePath(filePath) {
54
57
  const normalized = normalizeBlueprintPath(filePath);
55
58
  const parts = normalized.split("/").filter((segment) => segment.length > 0);
56
- const [state, slug, doc] = parts;
57
- return (parts.length === 3 &&
59
+ const [state] = parts;
60
+ return (parts.length >= 3 &&
58
61
  isBlueprintStatus(state) &&
59
- isBlueprintSlugSegment(slug) &&
60
- typeof doc === "string" &&
61
- doc.endsWith(".md") &&
62
- doc !== BLUEPRINT_OVERVIEW_FILENAME);
62
+ parts.slice(1, -1).every((segment) => isBlueprintSlugSegment(segment)) &&
63
+ typeof parts.at(-1) === "string" &&
64
+ parts.at(-1).endsWith(".md") &&
65
+ parts.at(-1) !== BLUEPRINT_OVERVIEW_FILENAME);
63
66
  }
64
67
  export function getBlueprintDocumentPaths(root, state, slug) {
65
68
  const directory = path.join(root, state, slug);
@@ -69,6 +72,36 @@ export function getBlueprintDocumentPaths(root, state, slug) {
69
72
  folder: path.join(directory, BLUEPRINT_OVERVIEW_FILENAME),
70
73
  };
71
74
  }
75
+ export function resolveBlueprintDocumentIdentity(root, documentPath) {
76
+ const relativePath = normalizeBlueprintPath(path.relative(root, documentPath));
77
+ const parsed = parseBlueprintDocumentRelativePath(relativePath);
78
+ if (!parsed)
79
+ return null;
80
+ const paths = getBlueprintDocumentPaths(root, parsed.state, parsed.slug);
81
+ return {
82
+ ...parsed,
83
+ documentPath: parsed.shape === "flat" ? paths.flat : paths.folder,
84
+ folderDirectory: paths.directory,
85
+ folderPath: paths.folder,
86
+ lifecycleSlug: `${parsed.state}/${parsed.slug}`,
87
+ stableSlug: parsed.slug,
88
+ };
89
+ }
90
+ export function folderBlueprintRequiredMessage(identity) {
91
+ if (identity.shape === "folder") {
92
+ return "";
93
+ }
94
+ return [
95
+ `Blueprint evidence requires folder shape; ${identity.documentPath} is a flat blueprint.`,
96
+ `Run: wp blueprint canonicalize ${identity.lifecycleSlug}`,
97
+ "Then commit the canonicalized path and rerun the evidence command.",
98
+ ].join(" ");
99
+ }
100
+ export function assertFolderBlueprintIdentity(identity) {
101
+ const message = folderBlueprintRequiredMessage(identity);
102
+ if (message)
103
+ throw new Error(message);
104
+ }
72
105
  export function getBlueprintDocumentCandidates(root, slug) {
73
106
  const normalized = normalizeBlueprintPath(slug);
74
107
  const segments = normalized.split("/").filter((segment) => segment.length > 0);
@@ -187,6 +187,11 @@ export const CLI_MCP_PARITY_CLASSIFICATION = {
187
187
  classification: "direct-fallback-by-design",
188
188
  reason: "internal serialization wrapper spawned by the auto-update, wp update, and setup lanes; never agent-facing",
189
189
  },
190
+ "optional-tool-refresh": {
191
+ command: "optional-tool-refresh",
192
+ classification: "direct-fallback-by-design",
193
+ reason: "internal detached worker spawned by provider launch freshness; never agent-facing",
194
+ },
190
195
  init: {
191
196
  command: "init",
192
197
  classification: "unsafe-credentialed-destructive",
@@ -203,6 +208,11 @@ export const CLI_MCP_PARITY_CLASSIFICATION = {
203
208
  tools: ["wp_lint"],
204
209
  reason: "lint MCP tool exists",
205
210
  },
211
+ "rust-check": {
212
+ command: "rust-check",
213
+ classification: "follow-up-candidate",
214
+ reason: "cargo check/test verb for the ci-preflight Rust lane; CLI-only dev gate with no dedicated MCP tool",
215
+ },
206
216
  logs: {
207
217
  command: "logs",
208
218
  classification: "follow-up-candidate",
@@ -229,6 +239,11 @@ export const CLI_MCP_PARITY_CLASSIFICATION = {
229
239
  classification: "direct-fallback-by-design",
230
240
  reason: "long-running local preview server remains a direct CLI workflow",
231
241
  },
242
+ "public:readiness": {
243
+ command: "public:readiness",
244
+ classification: "direct-fallback-by-design",
245
+ reason: "public package readiness performs local package/tarball checks and is intentionally not a read-only MCP workflow",
246
+ },
232
247
  qa: {
233
248
  command: "qa",
234
249
  classification: "existing-mcp",
@@ -1,6 +1,7 @@
1
1
  import type { CliCommand } from "@webpresso/cli-contract";
2
2
  export declare const blueprintCommands: {
3
3
  audit: CliCommand<Record<string, unknown>>;
4
+ canonicalize: CliCommand<Record<string, unknown>>;
4
5
  exec: CliCommand<Record<string, unknown>>;
5
6
  finalize: CliCommand<Record<string, unknown>>;
6
7
  list: CliCommand<Record<string, unknown>>;
@@ -35,6 +35,7 @@ function createBlueprintAuditCommand() {
35
35
  }
36
36
  export const blueprintCommands = {
37
37
  audit: createBlueprintAuditCommand(),
38
+ canonicalize: placeholderCommand(SCOPE, "canonicalize", "Canonicalize a flat blueprint to folder shape"),
38
39
  exec: placeholderGroup(SCOPE, "exec", "Execute a blueprint", {
39
40
  logs: placeholderCommand(SCOPE, "logs", "Show blueprint execution logs"),
40
41
  resume: placeholderCommand(SCOPE, "resume", "Resume blueprint execution"),
@@ -5,7 +5,7 @@
5
5
  * Lazy-loads subcommand modules based on the first argv to keep startup
6
6
  * cheap. Modeled on apps/cli-wp/src/cli.ts.
7
7
  */
8
- declare const SUPPORTED_COMMANDS: readonly ["blueprint", "browser", "dash", "dash-helper-start", "dash-helper-complete", "claude", "codex", "grok", "opencode", "yolo", "config", "secrets", "roadmap", "status", "review", "sync", "audit", "qa", "ultragoal", "compile", "rule", "skill", "skills", "docs", "setup", "init", "dev", "deploy", "preview", "cleanup", "migrate", "doctor", "err", "test", "e2e", "ci", "ci-preflight", "typecheck", "lint", "format", "logs", "session-id", "session-info", "session", "tech-debt", "pr", "fleet", "worktree", "mcp", "hook", "hooks", "self-install-guard", "gain", "bench", "install", "add", "remove", "update", "exec", "run"];
8
+ declare const SUPPORTED_COMMANDS: readonly ["blueprint", "browser", "dash", "dash-helper-start", "dash-helper-complete", "claude", "codex", "grok", "opencode", "yolo", "config", "secrets", "roadmap", "status", "review", "sync", "audit", "qa", "ultragoal", "compile", "rule", "skill", "skills", "docs", "setup", "init", "dev", "deploy", "preview", "cleanup", "migrate", "doctor", "err", "test", "e2e", "ci", "ci-preflight", "public:readiness", "typecheck", "lint", "format", "rust-check", "logs", "session-id", "session-info", "session", "tech-debt", "pr", "fleet", "worktree", "mcp", "hook", "hooks", "self-install-guard", "optional-tool-refresh", "gain", "bench", "install", "add", "remove", "update", "exec", "run"];
9
9
  export type SupportedCommand = (typeof SUPPORTED_COMMANDS)[number];
10
10
  type RootHelpEntry = {
11
11
  readonly command: string;