@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.
- package/README.md +53 -15
- package/bin/_run.js +183 -20
- package/catalog/AGENTS.md.tpl +3 -3
- package/catalog/agent/agents/implementer.md +45 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
- package/catalog/agent/rules/pre-implementation.md +28 -1
- package/catalog/agent/rules/supported-agent-clis.md +9 -0
- package/catalog/agent/rules/typescript-version.md +50 -0
- package/catalog/agent/skills/codex/SKILL.md +25 -1
- package/catalog/agent/skills/verify/SKILL.md +4 -2
- package/catalog/docs/templates/blueprint.md +1 -1
- package/dist/esm/audit/agents.js +1 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
- package/dist/esm/audit/blueprint-pr-coverage.js +4 -0
- package/dist/esm/audit/blueprint-trust.js +14 -10
- package/dist/esm/audit/changeset-evidence.d.ts +23 -0
- package/dist/esm/audit/changeset-evidence.js +30 -0
- package/dist/esm/audit/changeset-required.d.ts +10 -0
- package/dist/esm/audit/changeset-required.js +73 -0
- package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
- package/dist/esm/audit/ci-guardrails-detection.js +68 -0
- package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +80 -0
- package/dist/esm/audit/ci-test-perf.js +30 -0
- package/dist/esm/audit/command-surface.js +4 -2
- package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
- package/dist/esm/audit/host-substitution-risk.js +256 -0
- package/dist/esm/audit/registry.d.ts +4 -4
- package/dist/esm/audit/registry.js +13 -1
- package/dist/esm/audit/toolchain-isolation.js +1 -2
- package/dist/esm/audit/typescript-version.d.ts +2 -0
- package/dist/esm/audit/typescript-version.js +123 -0
- package/dist/esm/blueprint/core/parser.js +101 -2
- package/dist/esm/blueprint/core/schema.d.ts +1 -0
- package/dist/esm/blueprint/core/schema.js +26 -0
- package/dist/esm/blueprint/core/validation/criteria.d.ts +14 -0
- package/dist/esm/blueprint/core/validation/criteria.js +41 -2
- package/dist/esm/blueprint/core/validation/state.js +25 -14
- package/dist/esm/blueprint/db/migrations/run.js +4 -2
- package/dist/esm/blueprint/lifecycle/audit.d.ts +20 -0
- package/dist/esm/blueprint/lifecycle/audit.js +87 -20
- package/dist/esm/blueprint/lifecycle/canonicalize.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/canonicalize.js +135 -0
- package/dist/esm/blueprint/lifecycle/local.d.ts +2 -1
- package/dist/esm/blueprint/lifecycle/local.js +19 -3
- package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/review-provenance.js +68 -12
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
- package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
- package/dist/esm/blueprint/sync/client.d.ts +2 -1
- package/dist/esm/blueprint/sync/client.js +1 -1
- package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
- package/dist/esm/blueprint/trust/command-runner.js +148 -0
- package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
- package/dist/esm/blueprint/trust/dossier.js +40 -6
- package/dist/esm/blueprint/trust/gates.d.ts +7 -0
- package/dist/esm/blueprint/trust/gates.js +20 -0
- package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
- package/dist/esm/blueprint/trust/promotion.js +74 -112
- package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
- package/dist/esm/blueprint/trust/scaffold.js +64 -0
- package/dist/esm/blueprint/trust/validator.d.ts +1 -0
- package/dist/esm/blueprint/trust/validator.js +7 -1
- package/dist/esm/blueprint/utils/archive.js +27 -5
- package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
- package/dist/esm/blueprint/utils/document-paths.js +45 -12
- package/dist/esm/build/cli-mcp-parity.js +15 -0
- package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
- package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +43 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +8 -1
- package/dist/esm/cli/commands/agent-launch.js +93 -4
- package/dist/esm/cli/commands/audit-core.d.ts +1 -1
- package/dist/esm/cli/commands/audit-core.js +8 -1
- package/dist/esm/cli/commands/audit.js +21 -5
- package/dist/esm/cli/commands/blueprint/execution.js +18 -6
- package/dist/esm/cli/commands/blueprint/mutations.js +59 -40
- package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
- package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
- package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
- package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
- package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
- package/dist/esm/cli/commands/blueprint/router.js +13 -1
- package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
- package/dist/esm/cli/commands/ci-preflight.js +30 -6
- package/dist/esm/cli/commands/compile.js +5 -31
- package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
- package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
- package/dist/esm/cli/commands/doctor.js +5 -0
- package/dist/esm/cli/commands/hook.d.ts +5 -0
- package/dist/esm/cli/commands/hook.js +15 -9
- package/dist/esm/cli/commands/init/config.js +2 -2
- package/dist/esm/cli/commands/init/convergence-apply.d.ts +88 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +325 -0
- package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
- package/dist/esm/cli/commands/init/convergence-state.js +54 -0
- package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
- package/dist/esm/cli/commands/init/convergence.js +57 -0
- package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
- package/dist/esm/cli/commands/init/git-convergence.js +168 -0
- package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
- package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
- package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
- package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
- package/dist/esm/cli/commands/init/host-visibility.js +94 -5
- package/dist/esm/cli/commands/init/index.d.ts +3 -0
- package/dist/esm/cli/commands/init/index.js +326 -173
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
- package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
- package/dist/esm/cli/commands/init/merge.d.ts +1 -0
- package/dist/esm/cli/commands/init/merge.js +30 -1
- package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
- package/dist/esm/cli/commands/init/package-root.js +10 -2
- package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
- package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +20 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +61 -5
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +54 -24
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +288 -202
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +22 -0
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +230 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
- package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
- package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
- package/dist/esm/cli/commands/opencode-probe.d.ts +107 -0
- package/dist/esm/cli/commands/opencode-probe.js +323 -0
- package/dist/esm/cli/commands/opencode-rotate.d.ts +72 -0
- package/dist/esm/cli/commands/opencode-rotate.js +147 -0
- package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
- package/dist/esm/cli/commands/public-readiness.js +42 -0
- package/dist/esm/cli/commands/quality-runner.js +3 -3
- package/dist/esm/cli/commands/review.d.ts +35 -3
- package/dist/esm/cli/commands/review.js +751 -209
- package/dist/esm/cli/commands/run-wait.d.ts +2 -0
- package/dist/esm/cli/commands/run-wait.js +122 -0
- package/dist/esm/cli/commands/rust-check.d.ts +24 -0
- package/dist/esm/cli/commands/rust-check.js +110 -0
- package/dist/esm/cli/commands/setup-help.js +1 -1
- package/dist/esm/cli/commands/setup-repair-help.js +1 -1
- package/dist/esm/cli/commands/typecheck.d.ts +1 -0
- package/dist/esm/cli/commands/typecheck.js +30 -1
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +6 -0
- package/dist/esm/cli/commands/worktree/router-dispatch.js +126 -9
- package/dist/esm/cli/commands/worktree/router.js +10 -0
- package/dist/esm/cli/direct-provider-launch.js +9 -5
- package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
- package/dist/esm/cli/optional-tool-freshness.js +219 -9
- package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
- package/dist/esm/daemon/domains/git-actions.js +136 -0
- package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
- package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
- package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
- package/dist/esm/daemon/domains/sessions-search.js +29 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
- package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
- package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
- package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
- package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/git-handlers.js +27 -0
- package/dist/esm/daemon/handlers/product.d.ts +7 -0
- package/dist/esm/daemon/handlers/product.js +10 -119
- package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
- package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
- package/dist/esm/daemon/handlers/shared.d.ts +5 -0
- package/dist/esm/daemon/handlers/shared.js +16 -0
- package/dist/esm/daemon/methods.d.ts +8 -1
- package/dist/esm/daemon/methods.js +7 -0
- package/dist/esm/daemon/protocol/contract.d.ts +43 -0
- package/dist/esm/daemon/protocol/contract.js +26 -0
- package/dist/esm/daemon/protocol/index.d.ts +1 -1
- package/dist/esm/daemon/protocol/index.js +1 -1
- package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
- package/dist/esm/hooks/__conformance__/matrix.js +137 -0
- package/dist/esm/hooks/conformance/batch.js +5 -0
- package/dist/esm/hooks/doctor.d.ts +5 -3
- package/dist/esm/hooks/doctor.js +67 -16
- package/dist/esm/hooks/permission-request/index.js +9 -3
- package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
- package/dist/esm/hooks/permission-request/policy.js +13 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
- package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/logger.js +16 -0
- package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
- package/dist/esm/hooks/pretool-guard/runner.js +68 -16
- package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +54 -0
- package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
- package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
- package/dist/esm/hooks/shared/guard-outcome.js +215 -0
- package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
- package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
- package/dist/esm/hooks/shared/types.d.ts +8 -5
- package/dist/esm/hooks/shared/types.js +50 -9
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +92 -0
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
- package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +109 -11
- package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
- package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
- package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
- package/dist/esm/mcp/blueprint/registration.js +3 -2
- package/dist/esm/mcp/server.d.ts +1 -0
- package/dist/esm/mcp/server.js +27 -0
- package/dist/esm/mcp/tools/_names.d.ts +1 -1
- package/dist/esm/mcp/tools/_names.js +5 -0
- package/dist/esm/mcp/tools/_registry.js +10 -0
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/_shared/result.js +44 -1
- package/dist/esm/mcp/tools/audit.d.ts +5 -1
- package/dist/esm/mcp/tools/audit.js +42 -0
- package/dist/esm/mcp/tools/audits.d.ts +4 -0
- package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
- package/dist/esm/mcp/tools/ci-preflight.js +183 -0
- package/dist/esm/mcp/tools/pr-upsert.js +39 -1
- package/dist/esm/mcp/tools/review-run.d.ts +28 -0
- package/dist/esm/mcp/tools/review-run.js +216 -0
- package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
- package/dist/esm/mcp/tools/run-wait.js +593 -0
- package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
- package/dist/esm/mcp/tools/session-id.d.ts +3 -0
- package/dist/esm/mcp/tools/session-id.js +45 -0
- package/dist/esm/mcp/tools/session-info.d.ts +3 -0
- package/dist/esm/mcp/tools/session-info.js +49 -0
- package/dist/esm/output-transforms/audit.d.ts +23 -0
- package/dist/esm/output-transforms/audit.js +47 -0
- package/dist/esm/output-transforms/index.js +2 -0
- package/dist/esm/package.json +2 -0
- package/dist/esm/platform/client.d.ts +7 -1
- package/dist/esm/platform/github-contract-source.d.ts +2 -1
- package/dist/esm/pr-description/contract.js +24 -4
- package/dist/esm/review/authority.js +27 -14
- package/dist/esm/review/availability.d.ts +33 -0
- package/dist/esm/review/availability.js +122 -20
- package/dist/esm/review/delivery-verifier.d.ts +27 -0
- package/dist/esm/review/delivery-verifier.js +115 -0
- package/dist/esm/review/events.d.ts +3 -2
- package/dist/esm/review/events.js +23 -3
- package/dist/esm/review/execution/adapters.d.ts +11 -1
- package/dist/esm/review/execution/adapters.js +36 -10
- package/dist/esm/review/execution/artifacts.d.ts +1 -1
- package/dist/esm/review/execution/output-policy.d.ts +2 -0
- package/dist/esm/review/execution/output-policy.js +12 -0
- package/dist/esm/review/execution/review-checkout.js +121 -21
- package/dist/esm/review/execution/supervisor.js +36 -4
- package/dist/esm/review/execution/types.d.ts +9 -0
- package/dist/esm/review/opencode-account.d.ts +24 -0
- package/dist/esm/review/opencode-account.js +68 -0
- package/dist/esm/review/opencode-auth.d.ts +33 -0
- package/dist/esm/review/opencode-auth.js +79 -0
- package/dist/esm/review/subject.d.ts +25 -0
- package/dist/esm/review/subject.js +116 -26
- package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
- package/dist/esm/runtime/opencode-account-materializer.js +33 -0
- package/dist/esm/rust/affected-manifests.d.ts +38 -0
- package/dist/esm/rust/affected-manifests.js +69 -0
- package/dist/esm/session-memory/fetch-index.d.ts +1 -1
- package/dist/esm/status/snapshot.js +32 -25
- package/dist/esm/test/duration-sequencer.d.ts +20 -0
- package/dist/esm/test/duration-sequencer.js +54 -0
- package/dist/esm/test/shard-durations.json +922 -0
- package/dist/esm/test/worker-budget.d.ts +6 -0
- package/dist/esm/test/worker-budget.js +59 -1
- package/dist/esm/test-helpers/global-setup.d.ts +1 -2
- package/dist/esm/test-helpers/global-setup.js +79 -32
- package/dist/esm/typecheck/planner.d.ts +9 -1
- package/dist/esm/typecheck/planner.js +31 -0
- package/dist/esm/ultragoal/service.d.ts +7 -0
- package/dist/esm/ultragoal/service.js +42 -2
- package/dist/esm/utils/write-json-file.js +3 -1
- package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
- package/dist/esm/worktrees/codex-project-trust.js +56 -0
- package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
- package/dist/esm/worktrees/orphan-scan.js +211 -0
- package/dist/esm/worktrees/registry.d.ts +8 -0
- package/dist/esm/worktrees/registry.js +18 -3
- package/package.json +18 -13
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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.
|
|
116
|
+
if (result.outcome === "passed")
|
|
79
117
|
return;
|
|
80
|
-
|
|
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
|
|
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.
|
|
132
|
-
details.push(`exit=${result.
|
|
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.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
+
`;
|
|
@@ -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 &&
|
|
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
|
|
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
|
-
|
|
144
|
-
|
|
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
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
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}/${
|
|
48
|
+
relativePath: `${state}/${slug}/${BLUEPRINT_OVERVIEW_FILENAME}`,
|
|
46
49
|
shape: "folder",
|
|
47
|
-
slug
|
|
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
|
|
57
|
-
return (parts.length
|
|
59
|
+
const [state] = parts;
|
|
60
|
+
return (parts.length >= 3 &&
|
|
58
61
|
isBlueprintStatus(state) &&
|
|
59
|
-
isBlueprintSlugSegment(
|
|
60
|
-
typeof
|
|
61
|
-
|
|
62
|
-
|
|
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"),
|
package/dist/esm/cli/cli.d.ts
CHANGED
|
@@ -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;
|