@webpresso/agent-kit 3.1.28 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @webpresso/agent-kit might be problematic. Click here for more details.
- package/README.md +53 -15
- package/bin/_run.js +179 -19
- package/catalog/AGENTS.md.tpl +6 -6
- package/catalog/agent/agents/implementer.md +45 -0
- package/catalog/agent/rules/ci-cost-local-first.md +139 -0
- package/catalog/agent/rules/ci-test-perf.md +5 -1
- package/catalog/agent/rules/cmd-execution.md +9 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
- package/catalog/agent/rules/pre-implementation.md +30 -2
- package/catalog/agent/rules/supported-agent-clis.md +9 -0
- package/catalog/agent/rules/test-scan-perf.md +80 -0
- package/catalog/agent/rules/typescript-version.md +50 -0
- package/catalog/agent/rules/workflow-skills-routing.md +4 -2
- package/catalog/agent/skills/codex/SKILL.md +25 -1
- package/catalog/agent/skills/fix/SKILL.md +3 -0
- package/catalog/agent/skills/verify/SKILL.md +22 -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 +72 -7
- package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +90 -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 +11 -1
- package/dist/esm/audit/roadmap-links.js +6 -0
- package/dist/esm/audit/test-scan-perf.d.ts +18 -0
- package/dist/esm/audit/test-scan-perf.js +275 -0
- 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 +43 -1
- 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 +65 -10
- package/dist/esm/blueprint/lifecycle/canonicalize.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/canonicalize.js +135 -0
- package/dist/esm/blueprint/lifecycle/engine.d.ts +9 -0
- package/dist/esm/blueprint/lifecycle/engine.js +53 -6
- 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 +118 -12
- package/dist/esm/blueprint/markdown/helpers.js +6 -2
- 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/trust/command-runner.d.ts +26 -0
- package/dist/esm/blueprint/trust/command-runner.js +148 -0
- 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 +62 -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 +10 -0
- package/dist/esm/ci/act-replay.js +110 -7
- package/dist/esm/ci/act-runner.d.ts +4 -0
- package/dist/esm/ci/act-runner.js +85 -26
- package/dist/esm/ci/act-worktree-git.d.ts +11 -0
- package/dist/esm/ci/act-worktree-git.js +219 -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 +34 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +6 -1
- package/dist/esm/cli/commands/agent-launch.js +82 -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 +20 -5
- package/dist/esm/cli/commands/blueprint/execution.js +18 -6
- package/dist/esm/cli/commands/blueprint/mutations.js +83 -47
- 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 +40 -3
- package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
- package/dist/esm/cli/commands/ci-preflight.js +6 -3
- package/dist/esm/cli/commands/ci.d.ts +8 -1
- package/dist/esm/cli/commands/ci.js +80 -21
- 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/dash/keymap.js +1 -0
- package/dist/esm/cli/commands/dash/launch-preferences.d.ts +15 -0
- package/dist/esm/cli/commands/dash/launch-preferences.js +46 -17
- package/dist/esm/cli/commands/dash/plan-once-label.d.ts +7 -0
- package/dist/esm/cli/commands/dash/plan-once-label.js +5 -0
- package/dist/esm/cli/commands/dash/plan-once-membership.d.ts +67 -0
- package/dist/esm/cli/commands/dash/plan-once-membership.js +151 -0
- package/dist/esm/cli/commands/dash/plan-once-merge.d.ts +143 -0
- package/dist/esm/cli/commands/dash/plan-once-merge.js +238 -0
- package/dist/esm/cli/commands/dash/plan-once.d.ts +22 -2
- package/dist/esm/cli/commands/dash/plan-once.js +41 -4
- package/dist/esm/cli/commands/dash/tui/attention-hud.d.ts +9 -0
- package/dist/esm/cli/commands/dash/tui/attention-hud.js +9 -0
- package/dist/esm/cli/commands/dash/tui/chat-bridge.d.ts +41 -0
- package/dist/esm/cli/commands/dash/tui/chat-bridge.js +73 -0
- package/dist/esm/cli/commands/dash/tui/chat-mode.d.ts +47 -0
- package/dist/esm/cli/commands/dash/tui/chat-mode.js +98 -0
- package/dist/esm/cli/commands/dash/tui/frame.d.ts +3 -0
- package/dist/esm/cli/commands/dash/tui/frame.js +3 -1
- package/dist/esm/cli/commands/dash/tui/palette.d.ts +1 -1
- package/dist/esm/cli/commands/dash/tui/palette.js +14 -0
- package/dist/esm/cli/commands/dash/tui/runtime.js +239 -4
- package/dist/esm/cli/commands/dash/tui/state.d.ts +41 -0
- package/dist/esm/cli/commands/dash/tui/state.js +257 -4
- package/dist/esm/cli/commands/doctor.js +5 -0
- package/dist/esm/cli/commands/format.js +0 -2
- 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.d.ts +0 -1
- package/dist/esm/cli/commands/init/config.js +3 -6
- package/dist/esm/cli/commands/init/convergence-apply.d.ts +24 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +221 -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 +1 -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/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 +11 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +56 -1
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.d.ts +5 -6
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.js +6 -13
- 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 +51 -23
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.d.ts +0 -2
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.js +0 -2
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.d.ts +6 -3
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.js +39 -26
- 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 +297 -173
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +7 -0
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +58 -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/logs.d.ts +15 -0
- package/dist/esm/cli/commands/logs.js +192 -3
- package/dist/esm/cli/commands/opencode-rotate.d.ts +49 -0
- package/dist/esm/cli/commands/opencode-rotate.js +135 -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-log-store.d.ts +1 -0
- package/dist/esm/cli/commands/quality-log-store.js +187 -51
- package/dist/esm/cli/commands/review.d.ts +35 -3
- package/dist/esm/cli/commands/review.js +734 -217
- 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/secrets.d.ts +1 -0
- package/dist/esm/cli/commands/secrets.js +4 -1
- 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/worktree/router-dispatch.js +13 -8
- package/dist/esm/cli/direct-provider-launch.js +31 -3
- 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 +15 -0
- package/dist/esm/hooks/doctor.js +79 -8
- 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 +58 -4
- 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/mcp-redirect.d.ts +0 -1
- package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.js +8 -5
- 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.d.ts +9 -7
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +126 -14
- 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 +23 -8
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +74 -0
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +78 -4
- 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 +40 -0
- package/dist/esm/mcp/tools/audits.d.ts +4 -0
- package/dist/esm/mcp/tools/ci-act.js +9 -11
- 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-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/paths/state-root.js +3 -0
- package/dist/esm/review/authority.js +30 -7
- package/dist/esm/review/availability.d.ts +22 -0
- package/dist/esm/review/availability.js +123 -23
- 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.js +26 -9
- 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 +87 -6
- package/dist/esm/review/execution/supervisor.js +5 -0
- package/dist/esm/review/execution/types.d.ts +8 -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 +27 -0
- package/dist/esm/review/subject.js +179 -32
- package/dist/esm/runtime/executor.d.ts +1 -0
- package/dist/esm/runtime/executor.js +2 -1
- package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
- package/dist/esm/runtime/opencode-account-materializer.js +33 -0
- package/dist/esm/secret-gate/runner.d.ts +1 -0
- package/dist/esm/secret-gate/runner.js +4 -1
- package/dist/esm/status/snapshot.js +30 -19
- 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 -1
- package/dist/esm/test-helpers/global-setup.js +68 -24
- 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/current.d.ts +1 -0
- package/dist/esm/worktrees/current.js +1 -0
- package/dist/esm/worktrees/project-agent-surfaces.d.ts +1 -0
- package/dist/esm/worktrees/project-agent-surfaces.js +2 -1
- package/dist/esm/worktrees/registry.d.ts +8 -0
- package/dist/esm/worktrees/registry.js +18 -3
- package/package.json +18 -15
|
@@ -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",
|
|
@@ -229,6 +234,11 @@ export const CLI_MCP_PARITY_CLASSIFICATION = {
|
|
|
229
234
|
classification: "direct-fallback-by-design",
|
|
230
235
|
reason: "long-running local preview server remains a direct CLI workflow",
|
|
231
236
|
},
|
|
237
|
+
"public:readiness": {
|
|
238
|
+
command: "public:readiness",
|
|
239
|
+
classification: "direct-fallback-by-design",
|
|
240
|
+
reason: "public package readiness performs local package/tarball checks and is intentionally not a read-only MCP workflow",
|
|
241
|
+
},
|
|
232
242
|
qa: {
|
|
233
243
|
command: "qa",
|
|
234
244
|
classification: "existing-mcp",
|
|
@@ -31,8 +31,105 @@ export function buildReplayWorkflowSource(sourceYaml, options) {
|
|
|
31
31
|
: basename(options.workflowPath);
|
|
32
32
|
workflow.name = `replay: ${originalName}`;
|
|
33
33
|
workflow.on = normalizeWorkflowOnSection(workflow.on, options.eventName);
|
|
34
|
+
workflow.jobs = rewriteReplayJobs(workflow.jobs, options.eventName);
|
|
34
35
|
return stringify(workflow);
|
|
35
36
|
}
|
|
37
|
+
function rewriteReplayJobs(jobs, eventName) {
|
|
38
|
+
if (typeof jobs !== "object" || jobs === null || Array.isArray(jobs)) {
|
|
39
|
+
return jobs;
|
|
40
|
+
}
|
|
41
|
+
return Object.fromEntries(Object.entries(jobs).map(([jobId, job]) => [
|
|
42
|
+
jobId,
|
|
43
|
+
rewriteReplayJob(job, eventName),
|
|
44
|
+
]));
|
|
45
|
+
}
|
|
46
|
+
function rewriteReplayJob(job, eventName) {
|
|
47
|
+
if (typeof job !== "object" || job === null || Array.isArray(job)) {
|
|
48
|
+
return job;
|
|
49
|
+
}
|
|
50
|
+
const jobRecord = { ...job };
|
|
51
|
+
if (Array.isArray(jobRecord.steps)) {
|
|
52
|
+
jobRecord.steps = jobRecord.steps.map((step) => rewriteReplayStep(step, eventName));
|
|
53
|
+
}
|
|
54
|
+
return jobRecord;
|
|
55
|
+
}
|
|
56
|
+
function rewriteReplayStep(step, eventName) {
|
|
57
|
+
if (!isRecord(step) || !isSameRepoCheckoutStep(step, eventName)) {
|
|
58
|
+
return step;
|
|
59
|
+
}
|
|
60
|
+
const rewritten = {
|
|
61
|
+
name: typeof step.name === "string" ? step.name : "Use prepared local replay workspace",
|
|
62
|
+
run: [
|
|
63
|
+
'echo "wp ci act replay: using prepared local workspace instead of actions/checkout"',
|
|
64
|
+
"test -d .git",
|
|
65
|
+
"git rev-parse --is-inside-work-tree",
|
|
66
|
+
"git rev-parse --verify HEAD",
|
|
67
|
+
].join("\n"),
|
|
68
|
+
"working-directory": "${{ github.workspace }}",
|
|
69
|
+
};
|
|
70
|
+
copyOptionalStepField(step, rewritten, "id");
|
|
71
|
+
copyOptionalStepField(step, rewritten, "if");
|
|
72
|
+
copyOptionalStepField(step, rewritten, "timeout-minutes");
|
|
73
|
+
copyOptionalStepField(step, rewritten, "continue-on-error");
|
|
74
|
+
return rewritten;
|
|
75
|
+
}
|
|
76
|
+
function isSameRepoCheckoutStep(step, eventName) {
|
|
77
|
+
if (typeof step.uses !== "string" || !/^actions\/checkout(?:@|$)/u.test(step.uses)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (step.env !== undefined && step.env !== null) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const withConfig = step.with;
|
|
84
|
+
if (withConfig === undefined || withConfig === null) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
if (!isRecord(withConfig)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (hasAnyCheckoutInputExcept(withConfig, ["ref"])) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return isCurrentReplayRef(withConfig.ref, eventName);
|
|
94
|
+
}
|
|
95
|
+
function isCurrentReplayRef(value, eventName) {
|
|
96
|
+
if (value === undefined || value === null || String(value).trim().length === 0) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const normalized = normalizeGithubExpression(String(value));
|
|
100
|
+
if (normalized === null)
|
|
101
|
+
return false;
|
|
102
|
+
if (normalized === "github.sha") {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (normalized ===
|
|
106
|
+
"github.event_name=='pull_request'&&github.event.pull_request.base.sha||github.sha") {
|
|
107
|
+
return eventName !== "pull_request";
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function normalizeGithubExpression(value) {
|
|
112
|
+
const match = /^\$\{\{\s*([\s\S]*?)\s*\}\}$/u.exec(value.trim());
|
|
113
|
+
if (!match?.[1])
|
|
114
|
+
return null;
|
|
115
|
+
return match[1].replace(/"/gu, "'").replace(/\s+/gu, "");
|
|
116
|
+
}
|
|
117
|
+
function hasAnyCheckoutInputExcept(withConfig, allowedKeys) {
|
|
118
|
+
const allowed = new Set(allowedKeys);
|
|
119
|
+
return Object.entries(withConfig).some(([key, value]) => {
|
|
120
|
+
if (allowed.has(key))
|
|
121
|
+
return false;
|
|
122
|
+
return value !== undefined && value !== null && String(value).trim().length > 0;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function copyOptionalStepField(source, target, field) {
|
|
126
|
+
if (source[field] !== undefined) {
|
|
127
|
+
target[field] = source[field];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function isRecord(value) {
|
|
131
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
132
|
+
}
|
|
36
133
|
export function createReplayWorkflow(options) {
|
|
37
134
|
const sourcePath = resolve(options.cwd, options.workflowPath);
|
|
38
135
|
const sourceYaml = readFileSync(sourcePath, "utf8");
|
|
@@ -42,11 +139,17 @@ export function createReplayWorkflow(options) {
|
|
|
42
139
|
});
|
|
43
140
|
const dir = mkdtempSync(join(tmpdir(), "wp-ci-act-replay-"));
|
|
44
141
|
const workflowPath = join(dir, "workflow.yml");
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
142
|
+
try {
|
|
143
|
+
writeFileSync(workflowPath, generatedYaml, "utf8");
|
|
144
|
+
return {
|
|
145
|
+
workflowPath,
|
|
146
|
+
cleanup() {
|
|
147
|
+
rmSync(dir, { recursive: true, force: true });
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
rmSync(dir, { recursive: true, force: true });
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
52
155
|
}
|
|
@@ -6,6 +6,7 @@ export interface PublicCiActOptions {
|
|
|
6
6
|
readonly workflow?: string;
|
|
7
7
|
readonly workflowPath?: string;
|
|
8
8
|
readonly job?: string;
|
|
9
|
+
readonly matrix?: readonly string[];
|
|
9
10
|
readonly eventName?: CiActEventName;
|
|
10
11
|
readonly eventPath?: string;
|
|
11
12
|
readonly envProfile?: string;
|
|
@@ -22,6 +23,7 @@ export interface PublicCiActCommand {
|
|
|
22
23
|
readonly actArgs: readonly string[];
|
|
23
24
|
}
|
|
24
25
|
export interface PreparedPublicCiActExecution {
|
|
26
|
+
readonly cwd: string;
|
|
25
27
|
readonly command: PublicCiActCommand;
|
|
26
28
|
readonly mode: CiActExecutionMode;
|
|
27
29
|
readonly nonSecurityEquivalent: boolean;
|
|
@@ -36,5 +38,7 @@ export declare function buildPublicCiActArgs(options?: PublicCiActOptions, workf
|
|
|
36
38
|
export declare function buildPublicCiActCommand(options?: PublicCiActOptions): PublicCiActCommand;
|
|
37
39
|
export declare function preparePublicCiActExecution(options?: PublicCiActOptions): PreparedPublicCiActExecution;
|
|
38
40
|
export declare function resolveCiActSecretEnvProfile(options?: PublicCiActOptions): string | undefined;
|
|
41
|
+
export declare function shouldForceCiActSecretGate(envProfile: string | undefined): boolean;
|
|
39
42
|
export declare function sanitizePublicCiActArgv(command: PublicCiActCommand): PublicCiActCommand;
|
|
40
43
|
export declare function assertNoForbiddenCiActArgs(args: readonly string[]): void;
|
|
44
|
+
export declare function normalizeCiActMatrixSelectors(selectors: readonly string[] | undefined): readonly string[];
|
|
@@ -2,6 +2,7 @@ import { resolve } from "node:path";
|
|
|
2
2
|
import { buildSecretGateCommand } from "#secret-gate/runner.js";
|
|
3
3
|
import { resolveSecretsConfigProfileEnvironment } from "#runtime/secrets-config.js";
|
|
4
4
|
import { createReplayWorkflow, GENERATED_REPLAY_WORKFLOW_PLACEHOLDER, } from "./act-replay.js";
|
|
5
|
+
import { prepareActGitWorkspace } from "./act-worktree-git.js";
|
|
5
6
|
export { GENERATED_REPLAY_WORKFLOW_PLACEHOLDER } from "./act-replay.js";
|
|
6
7
|
const DEFAULT_WORKFLOW = "ci-e2e";
|
|
7
8
|
export const DEFAULT_PLATFORM_IMAGE = "ghcr.io/catthehacker/ubuntu:full-latest";
|
|
@@ -43,6 +44,7 @@ export function buildPublicCiActArgs(options = {}, workflowPathOverride) {
|
|
|
43
44
|
"--rm",
|
|
44
45
|
];
|
|
45
46
|
pushOption(args, "-j", options.job);
|
|
47
|
+
pushRepeatableOption(args, "--matrix", normalizeCiActMatrixSelectors(options.matrix));
|
|
46
48
|
pushOption(args, "-e", options.eventPath ? resolve(cwd, options.eventPath) : undefined);
|
|
47
49
|
pushOption(args, "--container-architecture", containerArchitecture);
|
|
48
50
|
return args;
|
|
@@ -57,7 +59,7 @@ export function buildPublicCiActCommand(options = {}) {
|
|
|
57
59
|
sink: "act",
|
|
58
60
|
profile: secretEnvProfile,
|
|
59
61
|
envProfile: options.envProfile ?? "secrets-only",
|
|
60
|
-
forceSecretGate:
|
|
62
|
+
forceSecretGate: shouldForceCiActSecretGate(options.envProfile),
|
|
61
63
|
command: "act",
|
|
62
64
|
args: actArgs,
|
|
63
65
|
});
|
|
@@ -65,32 +67,63 @@ export function buildPublicCiActCommand(options = {}) {
|
|
|
65
67
|
}
|
|
66
68
|
export function preparePublicCiActExecution(options = {}) {
|
|
67
69
|
const cwd = options.cwd ?? process.cwd();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
70
|
+
let gitWorkspace = null;
|
|
71
|
+
let replay = null;
|
|
72
|
+
try {
|
|
73
|
+
gitWorkspace = prepareActGitWorkspace(cwd);
|
|
74
|
+
const executionCwd = gitWorkspace.cwd;
|
|
75
|
+
const mode = resolveCiActExecutionMode(options);
|
|
76
|
+
const sourceWorkflowPath = resolve(cwd, resolveCiActWorkflowPath(options));
|
|
77
|
+
replay =
|
|
78
|
+
mode === "replay"
|
|
79
|
+
? createReplayWorkflow({
|
|
80
|
+
cwd,
|
|
81
|
+
workflowPath: sourceWorkflowPath,
|
|
82
|
+
eventName: options.eventName ?? "pull_request",
|
|
83
|
+
})
|
|
84
|
+
: null;
|
|
85
|
+
const executionOptions = {
|
|
86
|
+
...options,
|
|
87
|
+
cwd: executionCwd,
|
|
88
|
+
eventPath: options.eventPath ? resolve(cwd, options.eventPath) : undefined,
|
|
89
|
+
};
|
|
90
|
+
const actArgs = buildPublicCiActArgs(executionOptions, replay ? replay.workflowPath : sourceWorkflowPath);
|
|
91
|
+
if (gitWorkspace.prepared) {
|
|
92
|
+
actArgs.push("--bind");
|
|
93
|
+
}
|
|
94
|
+
const secretEnvProfile = resolveCiActSecretEnvProfile({ ...options, cwd }) ?? "preview";
|
|
95
|
+
const wrapped = buildSecretGateCommand({
|
|
96
|
+
sink: "act",
|
|
97
|
+
profile: secretEnvProfile,
|
|
98
|
+
envProfile: options.envProfile ?? "secrets-only",
|
|
99
|
+
forceSecretGate: shouldForceCiActSecretGate(options.envProfile),
|
|
100
|
+
command: "act",
|
|
101
|
+
args: actArgs,
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
cwd: executionCwd,
|
|
105
|
+
command: { command: wrapped.command, args: wrapped.args, actArgs },
|
|
106
|
+
mode,
|
|
107
|
+
nonSecurityEquivalent: mode === "replay",
|
|
108
|
+
cleanup() {
|
|
109
|
+
try {
|
|
110
|
+
replay?.cleanup();
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
gitWorkspace?.cleanup();
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
try {
|
|
91
120
|
replay?.cleanup();
|
|
92
|
-
}
|
|
93
|
-
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
gitWorkspace?.cleanup();
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
94
127
|
}
|
|
95
128
|
export function resolveCiActSecretEnvProfile(options = {}) {
|
|
96
129
|
const secretEnvProfile = options.secretEnvProfile?.trim();
|
|
@@ -103,6 +136,9 @@ export function resolveCiActSecretEnvProfile(options = {}) {
|
|
|
103
136
|
}
|
|
104
137
|
return undefined;
|
|
105
138
|
}
|
|
139
|
+
export function shouldForceCiActSecretGate(envProfile) {
|
|
140
|
+
return envProfile !== "none" && envProfile !== "public";
|
|
141
|
+
}
|
|
106
142
|
export function sanitizePublicCiActArgv(command) {
|
|
107
143
|
return {
|
|
108
144
|
command: command.command,
|
|
@@ -126,11 +162,34 @@ export function assertNoForbiddenCiActArgs(args) {
|
|
|
126
162
|
if (found)
|
|
127
163
|
throw new Error(`Unsupported unsafe ci act argument: ${found}`);
|
|
128
164
|
}
|
|
165
|
+
export function normalizeCiActMatrixSelectors(selectors) {
|
|
166
|
+
if (!selectors)
|
|
167
|
+
return [];
|
|
168
|
+
return selectors.map((selector) => normalizeCiActMatrixSelector(selector));
|
|
169
|
+
}
|
|
129
170
|
function pushOption(args, flag, value) {
|
|
130
171
|
if (value === undefined || value === "")
|
|
131
172
|
return;
|
|
132
173
|
args.push(flag, String(value));
|
|
133
174
|
}
|
|
175
|
+
function pushRepeatableOption(args, flag, values) {
|
|
176
|
+
for (const value of values) {
|
|
177
|
+
args.push(flag, value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function normalizeCiActMatrixSelector(selector) {
|
|
181
|
+
const trimmed = selector.trim();
|
|
182
|
+
const delimiter = trimmed.indexOf(":");
|
|
183
|
+
if (trimmed.length === 0 || delimiter <= 0) {
|
|
184
|
+
throw new Error(`Invalid ci act matrix selector "${selector}". Use key:value with non-empty key and value.`);
|
|
185
|
+
}
|
|
186
|
+
const key = trimmed.slice(0, delimiter).trim();
|
|
187
|
+
const value = trimmed.slice(delimiter + 1).trim();
|
|
188
|
+
if (key.length === 0 || value.length === 0) {
|
|
189
|
+
throw new Error(`Invalid ci act matrix selector "${selector}". Use key:value with non-empty key and value.`);
|
|
190
|
+
}
|
|
191
|
+
return `${key}:${value}`;
|
|
192
|
+
}
|
|
134
193
|
function sanitizeArg(value) {
|
|
135
194
|
if (/wp-ci-act-[^/\\]+[/\\]secrets\.env$/u.test(value))
|
|
136
195
|
return "[INTERNAL_SECRET_FILE]";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PreparedActGitWorkspace {
|
|
2
|
+
readonly cwd: string;
|
|
3
|
+
readonly prepared: boolean;
|
|
4
|
+
cleanup(): void;
|
|
5
|
+
}
|
|
6
|
+
interface LinkedWorktreeGitMetadata {
|
|
7
|
+
readonly gitDir: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function prepareActGitWorkspace(cwd: string): PreparedActGitWorkspace;
|
|
10
|
+
export declare function readLinkedWorktreeGitMetadata(cwd: string): LinkedWorktreeGitMetadata | null;
|
|
11
|
+
export {};
|