@xulthekl/team-flow 0.22.4
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/.agents/plugins/marketplace.json +20 -0
- package/.agents/skills +1 -0
- package/.claude/always/phase-guard.md +3 -0
- package/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +31 -0
- package/.codex-plugin/plugin.json +45 -0
- package/.codexignore +6 -0
- package/.cursor-plugin/marketplace.json +17 -0
- package/.cursor-plugin/plugin.json +31 -0
- package/.github/copilot-instructions.md +15 -0
- package/.github/dependabot.yml +10 -0
- package/.github/plugin/marketplace.json +42 -0
- package/.github/workflows/ci.yml +98 -0
- package/.github/workflows/hol-plugin-scanner.yml +17 -0
- package/.opencode/INSTALL.md +61 -0
- package/.opencode/plugins/team-flow.js +70 -0
- package/AGENTS.md +280 -0
- package/CHANGELOG.md +635 -0
- package/CONTRIBUTING.md +115 -0
- package/GEMINI.md +14 -0
- package/HANDOFF.md +219 -0
- package/INSTALL.md +810 -0
- package/LICENSE +21 -0
- package/README.md +185 -0
- package/SECURITY.md +37 -0
- package/agents/bug-investigator.md +180 -0
- package/agents/change-split-auditor.md +257 -0
- package/agents/code-reviewer.md +171 -0
- package/agents/cross-change-consistency-checker.md +215 -0
- package/agents/prd-completeness-reviewer.md +197 -0
- package/agents/prototype-builder.md +230 -0
- package/agents/prototype-env-scout.md +156 -0
- package/agents/prototype-reviewer.md +226 -0
- package/assets/icon.svg +6 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +5 -0
- package/dist/parsing/change-parser.d.ts +12 -0
- package/dist/parsing/change-parser.js +54 -0
- package/dist/parsing/requirement-blocks.d.ts +31 -0
- package/dist/parsing/requirement-blocks.js +192 -0
- package/dist/schema/base.d.ts +7 -0
- package/dist/schema/base.js +1 -0
- package/dist/schema/change.d.ts +25 -0
- package/dist/schema/change.js +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/spec.d.ts +11 -0
- package/dist/schema/spec.js +1 -0
- package/dist/validation/constants.d.ts +42 -0
- package/dist/validation/constants.js +42 -0
- package/dist/validation/tokenizer.d.ts +17 -0
- package/dist/validation/tokenizer.js +173 -0
- package/dist/validation/types.d.ts +40 -0
- package/dist/validation/types.js +1 -0
- package/dist/validation/validator.d.ts +21 -0
- package/dist/validation/validator.js +489 -0
- package/docs/README_en.md +358 -0
- package/docs/artifact-contract.md +96 -0
- package/docs/decision-points.md +88 -0
- package/docs/e2e-integration-design.md +207 -0
- package/docs/examples/README.md +25 -0
- package/docs/examples/add-dark-mode/README.md +13 -0
- package/docs/examples/add-dark-mode/design.md +49 -0
- package/docs/examples/add-dark-mode/execution-contract.md +56 -0
- package/docs/examples/add-dark-mode/proposal.md +46 -0
- package/docs/examples/add-dark-mode/specs/ui-theme/spec.md +35 -0
- package/docs/examples/add-dark-mode/tasks.md +26 -0
- package/docs/examples/refactor-auth-boundary/README.md +15 -0
- package/docs/examples/refactor-auth-boundary/design.md +41 -0
- package/docs/examples/refactor-auth-boundary/execution-contract.md +61 -0
- package/docs/examples/refactor-auth-boundary/proposal.md +47 -0
- package/docs/examples/refactor-auth-boundary/specs/auth-boundary/spec.md +45 -0
- package/docs/examples/refactor-auth-boundary/tasks.md +25 -0
- package/docs/plans/2026-07-07-fix-batch-design.md +68 -0
- package/docs/plans/2026-07-20-001-refactor-skill-collaboration-protocol-plan.md +128 -0
- package/docs/platform-matrix.md +45 -0
- package/docs/prototype-design-research.md +206 -0
- package/docs/release-checklist.md +96 -0
- package/docs/showcase.html +699 -0
- package/docs/skill-rename-v0.8.0.md +49 -0
- package/docs/state-machine.md +162 -0
- package/gemini-extension.json +6 -0
- package/hooks/hooks-cursor.json +8 -0
- package/hooks/hooks.json +26 -0
- package/hooks/pre-tool-use-guard +116 -0
- package/hooks/session-start +14 -0
- package/llms.txt +51 -0
- package/package.json +48 -0
- package/plugin.json +27 -0
- package/prd/v1/plan.md +443 -0
- package/prd/v1/prd.md +580 -0
- package/scripts/check-update.mjs +91 -0
- package/scripts/check-version-consistency.mjs +345 -0
- package/scripts/ensure-branch.mjs +71 -0
- package/scripts/get-config +31 -0
- package/scripts/guard/checks/artifacts-exist.mjs +36 -0
- package/scripts/guard/checks/contract-current.mjs +33 -0
- package/scripts/guard/checks/contract-fresh.mjs +17 -0
- package/scripts/guard/checks/dp-gate-passed.mjs +35 -0
- package/scripts/guard/checks/dp3-approved.mjs +22 -0
- package/scripts/guard/checks/execution-plan-ready.mjs +40 -0
- package/scripts/guard/checks/execution-reviews-passed.mjs +36 -0
- package/scripts/guard/checks/schema-valid.mjs +63 -0
- package/scripts/guard/checks/specs-merged.mjs +51 -0
- package/scripts/guard/checks/tasks-complete.mjs +31 -0
- package/scripts/guard/checks/tests-passing.mjs +31 -0
- package/scripts/guard/design-token-guard.mjs +237 -0
- package/scripts/guard/guard.mjs +205 -0
- package/scripts/infer-workflow.mjs +136 -0
- package/scripts/install-amazon-q.mjs +9 -0
- package/scripts/install-cline.mjs +9 -0
- package/scripts/install-continue.mjs +9 -0
- package/scripts/install-cursor.mjs +265 -0
- package/scripts/install-git-hooks.mjs +82 -0
- package/scripts/install-kiro.mjs +9 -0
- package/scripts/install-pi.mjs +9 -0
- package/scripts/install-qoder.mjs +9 -0
- package/scripts/install-qwen.mjs +9 -0
- package/scripts/install-roocode.mjs +9 -0
- package/scripts/install-windsurf.mjs +9 -0
- package/scripts/install-zcode.mjs +265 -0
- package/scripts/lib/cmd-audit.mjs +127 -0
- package/scripts/lib/cmd-checkpoint.mjs +118 -0
- package/scripts/lib/cmd-config.mjs +88 -0
- package/scripts/lib/cmd-doctor.mjs +267 -0
- package/scripts/lib/cmd-execution.mjs +206 -0
- package/scripts/lib/cmd-handoff.mjs +94 -0
- package/scripts/lib/cmd-inject.mjs +310 -0
- package/scripts/lib/cmd-install-amazon-q.mjs +11 -0
- package/scripts/lib/cmd-install-cline.mjs +11 -0
- package/scripts/lib/cmd-install-continue.mjs +11 -0
- package/scripts/lib/cmd-install-cursor.mjs +14 -0
- package/scripts/lib/cmd-install-kiro.mjs +11 -0
- package/scripts/lib/cmd-install-pi.mjs +11 -0
- package/scripts/lib/cmd-install-qoder.mjs +11 -0
- package/scripts/lib/cmd-install-qwen.mjs +11 -0
- package/scripts/lib/cmd-install-roocode.mjs +11 -0
- package/scripts/lib/cmd-install-windsurf.mjs +11 -0
- package/scripts/lib/cmd-install-workbuddy.mjs +359 -0
- package/scripts/lib/cmd-install-zcode.mjs +14 -0
- package/scripts/lib/cmd-isolate.mjs +37 -0
- package/scripts/lib/cmd-list.mjs +71 -0
- package/scripts/lib/cmd-runtime.mjs +85 -0
- package/scripts/lib/cmd-solutions.mjs +71 -0
- package/scripts/lib/cmd-state.mjs +231 -0
- package/scripts/lib/cmd-sync.mjs +102 -0
- package/scripts/lib/cmd-validate.mjs +105 -0
- package/scripts/lib/cmd-version.mjs +148 -0
- package/scripts/lib/config-loader.mjs +107 -0
- package/scripts/lib/execution-plan.mjs +492 -0
- package/scripts/lib/execution-recommendation.mjs +221 -0
- package/scripts/lib/hash.mjs +74 -0
- package/scripts/lib/install.mjs +267 -0
- package/scripts/lib/platform-runtime-inventory.mjs +27 -0
- package/scripts/lib/platforms.mjs +165 -0
- package/scripts/lib/prototype-sync.mjs +192 -0
- package/scripts/lib/sdd-overlay.mjs +262 -0
- package/scripts/lib/shell-quote.mjs +4 -0
- package/scripts/lib/solutions-capture.mjs +102 -0
- package/scripts/lib/solutions-index-gen.mjs +112 -0
- package/scripts/lib/solutions-inject.mjs +77 -0
- package/scripts/lib/solutions-promote.mjs +145 -0
- package/scripts/lib/spec-paths.mjs +85 -0
- package/scripts/lib/state-loader.mjs +154 -0
- package/scripts/lint/lint-skills.mjs +149 -0
- package/scripts/lint/rules/behavior-consistency.mjs +56 -0
- package/scripts/lint/rules/dp-trigger-points.mjs +60 -0
- package/scripts/lint/rules/exception-handling.mjs +59 -0
- package/scripts/lint/rules/no-contradictory-instructions.mjs +67 -0
- package/scripts/lint/rules/no-redundant-checks.mjs +50 -0
- package/scripts/lint/rules/token-rules.mjs +122 -0
- package/scripts/review-package +44 -0
- package/scripts/task-brief +40 -0
- package/scripts/team-flow.mjs +164 -0
- package/scripts/token-baseline.mjs +158 -0
- package/scripts/validate-artifacts +96 -0
- package/scripts/verify-marketplace-release.mjs +59 -0
- package/skills/architecture-design/SKILL.md +140 -0
- package/skills/architecture-design/chapters/ch01-4a-domains.md +27 -0
- package/skills/architecture-design/chapters/ch02-change-cascade.md +27 -0
- package/skills/architecture-design/chapters/ch03-architecture-outputs.md +25 -0
- package/skills/architecture-design/chapters/ch04-entity-to-aggregate.md +27 -0
- package/skills/architecture-design/chapters/ch05-cqrs.md +32 -0
- package/skills/architecture-design/chapters/ch06-integration.md +54 -0
- package/skills/architecture-design/cheatsheet.md +51 -0
- package/skills/architecture-design/glossary.md +23 -0
- package/skills/architecture-design/patterns.md +32 -0
- package/skills/bug-investigator/SKILL.md +77 -0
- package/skills/build-executor/SKILL.md +163 -0
- package/skills/build-executor/implementer-prompt.md +162 -0
- package/skills/build-executor/references/execution-modes.md +86 -0
- package/skills/build-executor/task-reviewer-prompt.md +195 -0
- package/skills/ce-brainstorm/SKILL.md +415 -0
- package/skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
- package/skills/ce-brainstorm/references/blindspot-pass.md +70 -0
- package/skills/ce-brainstorm/references/brainstorm-sections.md +285 -0
- package/skills/ce-brainstorm/references/handoff.md +168 -0
- package/skills/ce-brainstorm/references/html-rendering.md +634 -0
- package/skills/ce-brainstorm/references/markdown-rendering.md +241 -0
- package/skills/ce-brainstorm/references/model-tiers.md +9 -0
- package/skills/ce-brainstorm/references/prd-mapping.md +71 -0
- package/skills/ce-brainstorm/references/product-pressure-test.md +42 -0
- package/skills/ce-brainstorm/references/reasoning-elevation.md +63 -0
- package/skills/ce-brainstorm/references/settled-decisions.md +45 -0
- package/skills/ce-brainstorm/references/synthesis-summary.md +282 -0
- package/skills/ce-brainstorm/references/universal-brainstorming.md +73 -0
- package/skills/ce-brainstorm/references/verdict-routing.md +26 -0
- package/skills/ce-brainstorm/references/visual-probes.md +158 -0
- package/skills/ce-brainstorm/scripts/visual-probe-server.js +418 -0
- package/skills/ce-compound/SKILL.md +114 -0
- package/skills/ce-compound/assets/resolution-template.md +94 -0
- package/skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
- package/skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
- package/skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-compound/references/agents/performance-oracle.md +108 -0
- package/skills/ce-compound/references/agents/security-sentinel.md +91 -0
- package/skills/ce-compound/references/agents/session-historian.md +83 -0
- package/skills/ce-compound/references/concepts-vocabulary.md +78 -0
- package/skills/ce-compound/references/full-mode-workflow.md +413 -0
- package/skills/ce-compound/references/grounding-validation.md +84 -0
- package/skills/ce-compound/references/lightweight-mode.md +53 -0
- package/skills/ce-compound/references/output-standards.md +182 -0
- package/skills/ce-compound/references/promotion-rules.md +38 -0
- package/skills/ce-compound/references/schema.yaml +231 -0
- package/skills/ce-compound/references/three-tier-index.md +55 -0
- package/skills/ce-compound/references/v0.5-upgrade.md +32 -0
- package/skills/ce-compound/references/write-flow.md +36 -0
- package/skills/ce-compound/references/yaml-schema.md +118 -0
- package/skills/ce-compound/scripts/session-history/discover-sessions.sh +130 -0
- package/skills/ce-compound/scripts/session-history/extract-errors.py +254 -0
- package/skills/ce-compound/scripts/session-history/extract-metadata.py +456 -0
- package/skills/ce-compound/scripts/session-history/extract-skeleton.py +575 -0
- package/skills/ce-compound/scripts/validate-doc-claims.py +346 -0
- package/skills/ce-compound/scripts/validate-frontmatter.py +137 -0
- package/skills/ce-ideate/SKILL.md +402 -0
- package/skills/ce-ideate/references/agents/issue-intelligence-analyst.md +200 -0
- package/skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-ideate/references/agents/slack-researcher.md +127 -0
- package/skills/ce-ideate/references/agents/web-researcher.md +121 -0
- package/skills/ce-ideate/references/divergent-ideation.md +89 -0
- package/skills/ce-ideate/references/html-rendering.md +634 -0
- package/skills/ce-ideate/references/ideation-sections.md +191 -0
- package/skills/ce-ideate/references/markdown-rendering.md +238 -0
- package/skills/ce-ideate/references/post-ideation-workflow.md +167 -0
- package/skills/ce-ideate/references/universal-ideation.md +107 -0
- package/skills/ce-ideate/references/web-research-cache.md +56 -0
- package/skills/ce-plan/SKILL.md +137 -0
- package/skills/ce-plan/references/agents/agent-native-planning-strategist.md +62 -0
- package/skills/ce-plan/references/agents/architecture-strategist.md +46 -0
- package/skills/ce-plan/references/agents/best-practices-researcher.md +114 -0
- package/skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-plan/references/agents/data-migration-reviewer.md +103 -0
- package/skills/ce-plan/references/agents/deployment-verification-agent.md +157 -0
- package/skills/ce-plan/references/agents/framework-docs-researcher.md +93 -0
- package/skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
- package/skills/ce-plan/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-plan/references/agents/performance-oracle.md +108 -0
- package/skills/ce-plan/references/agents/repo-research-analyst.md +258 -0
- package/skills/ce-plan/references/agents/security-sentinel.md +91 -0
- package/skills/ce-plan/references/agents/slack-researcher.md +127 -0
- package/skills/ce-plan/references/agents/spec-flow-analyzer.md +80 -0
- package/skills/ce-plan/references/agents/web-researcher.md +121 -0
- package/skills/ce-plan/references/approach-altitude.md +55 -0
- package/skills/ce-plan/references/change-splitting.md +65 -0
- package/skills/ce-plan/references/completion-contract.md +33 -0
- package/skills/ce-plan/references/core-principles.md +40 -0
- package/skills/ce-plan/references/deepening-workflow.md +263 -0
- package/skills/ce-plan/references/dependency-graph.md +36 -0
- package/skills/ce-plan/references/html-rendering.md +634 -0
- package/skills/ce-plan/references/intake-routing.md +189 -0
- package/skills/ce-plan/references/markdown-rendering.md +238 -0
- package/skills/ce-plan/references/plan-handoff.md +150 -0
- package/skills/ce-plan/references/plan-sections.md +427 -0
- package/skills/ce-plan/references/plan-structure.md +120 -0
- package/skills/ce-plan/references/planning-modes.md +69 -0
- package/skills/ce-plan/references/reasoning-elevation.md +63 -0
- package/skills/ce-plan/references/research-workflow.md +165 -0
- package/skills/ce-plan/references/review-and-write.md +127 -0
- package/skills/ce-plan/references/settled-decisions.md +45 -0
- package/skills/ce-plan/references/synthesis-summary.md +418 -0
- package/skills/ce-plan/references/universal-planning.md +168 -0
- package/skills/ce-proof/SKILL.md +346 -0
- package/skills/ce-strategy/SKILL.md +97 -0
- package/skills/ce-strategy/references/interview.md +143 -0
- package/skills/ce-strategy/references/strategy-template.md +89 -0
- package/skills/code-reviewer/SKILL.md +84 -0
- package/skills/code-reviewer/code-reviewer-prompt.md +195 -0
- package/skills/contract-builder/SKILL.md +72 -0
- package/skills/design-system/SKILL.md +75 -0
- package/skills/design-system/references/agents/design-system-architect.md +138 -0
- package/skills/design-system/references/creation-flow.md +170 -0
- package/skills/design-system/references/preview-template.html +319 -0
- package/skills/design-system/references/token-derivation.md +115 -0
- package/skills/design-system/references/variant-schema.md +60 -0
- package/skills/e2e/SKILL.md +56 -0
- package/skills/e2e/references/test-verifier.md +40 -0
- package/skills/need-explorer/SKILL.md +79 -0
- package/skills/prototype/SKILL.md +98 -0
- package/skills/prototype/references/agents/design-system-architect.md +122 -0
- package/skills/prototype/references/checklist.md +82 -0
- package/skills/prototype/references/craft/accessibility-baseline.md +71 -0
- package/skills/prototype/references/craft/anti-ai-slop.md +87 -0
- package/skills/prototype/references/craft/laws-of-ux.md +60 -0
- package/skills/prototype/references/craft/state-coverage.md +81 -0
- package/skills/prototype/references/craft/typography-hierarchy.md +50 -0
- package/skills/prototype/references/layouts.md +223 -0
- package/skills/prototype/references/orchestration-flow.md +101 -0
- package/skills/prototype/references/prototype-scaffold/assets/design-tokens.css +19 -0
- package/skills/prototype/references/prototype-scaffold/components/button.html +3 -0
- package/skills/prototype/references/prototype-scaffold/design-system.md +7 -0
- package/skills/prototype/references/prototype-scaffold/flow.md +11 -0
- package/skills/prototype/references/prototype-scaffold/index.html +20 -0
- package/skills/prototype/references/prototype-scaffold/pages/index.html +11 -0
- package/skills/prototype/references/template.html +396 -0
- package/skills/release-archivist/SKILL.md +166 -0
- package/skills/release-archivist/references/closing-procedures.md +81 -0
- package/skills/session-handoff/SKILL.md +110 -0
- package/skills/session-handoff/references/context-extraction.md +70 -0
- package/skills/session-handoff/references/handoff-template.md +90 -0
- package/skills/session-handoff/references/skill-recommendation.md +43 -0
- package/skills/spec-merger/SKILL.md +66 -0
- package/skills/spec-writer/SKILL.md +149 -0
- package/skills/workflow-bootstrap/SKILL.md +148 -0
- package/skills/workflow-bootstrap/references/agents/codebase-recon-analyst.md +121 -0
- package/skills/workflow-bootstrap/references/b1-reconnaissance.md +80 -0
- package/skills/workflow-bootstrap/scripts/recon-probe.sh +328 -0
- package/skills/workflow-feedback/SKILL.md +120 -0
- package/skills/workflow-feedback/references/category-guide.md +121 -0
- package/skills/workflow-feedback/references/feedback-template.md +73 -0
- package/skills/workflow-feedback/references/review-mode.md +92 -0
- package/skills/workflow-orchestrator/SKILL.md +94 -0
- package/skills/workflow-orchestrator/references/feedback-loops.md +56 -0
- package/skills/workflow-orchestrator/references/s1-path-router.md +81 -0
- package/skills/workflow-orchestrator/references/s2-prd-prototype-loop.md +96 -0
- package/skills/workflow-orchestrator/references/s3-plan-pipeline.md +50 -0
- package/skills/workflow-orchestrator/references/s4-split-validate.md +108 -0
- package/skills/workflow-orchestrator/references/s5-monitoring.md +61 -0
- package/skills/workflow-orchestrator/references/state-model.md +185 -0
- package/skills/workflow-start/SKILL.md +147 -0
- package/skills/workflow-start/references/routing-rules.md +155 -0
- package/src/index.ts +16 -0
- package/src/parsing/change-parser.ts +79 -0
- package/src/parsing/requirement-blocks.ts +244 -0
- package/src/schema/base.ts +8 -0
- package/src/schema/change.ts +29 -0
- package/src/schema/index.ts +3 -0
- package/src/schema/spec.ts +12 -0
- package/src/validation/constants.ts +53 -0
- package/src/validation/tokenizer.ts +180 -0
- package/src/validation/types.ts +47 -0
- package/src/validation/validator.ts +552 -0
- package/templates/abandonment-summary.md +29 -0
- package/templates/design.md +37 -0
- package/templates/execution-contract.md +99 -0
- package/templates/plan.md +86 -0
- package/templates/prd.md +468 -0
- package/templates/proposal.md +37 -0
- package/templates/spec.md +31 -0
- package/templates/tasks.md +47 -0
- package/tests/e2e.test.mjs +266 -0
- package/tests/lib/cmd-audit.test.mjs +202 -0
- package/tests/lib/cmd-checkpoint.test.mjs +98 -0
- package/tests/lib/cmd-config.test.mjs +101 -0
- package/tests/lib/cmd-doctor.test.mjs +450 -0
- package/tests/lib/cmd-execution.test.mjs +647 -0
- package/tests/lib/cmd-handoff.test.mjs +137 -0
- package/tests/lib/cmd-inject.test.mjs +218 -0
- package/tests/lib/cmd-install-workbuddy.test.mjs +113 -0
- package/tests/lib/cmd-install-zcode.test.mjs +42 -0
- package/tests/lib/cmd-isolate.test.mjs +68 -0
- package/tests/lib/cmd-list.test.mjs +127 -0
- package/tests/lib/cmd-runtime.test.mjs +83 -0
- package/tests/lib/cmd-state-missing.test.mjs +65 -0
- package/tests/lib/cmd-state.test.mjs +365 -0
- package/tests/lib/cmd-sync-paths.test.mjs +84 -0
- package/tests/lib/cmd-validate-paths.test.mjs +90 -0
- package/tests/lib/config-loader.test.mjs +175 -0
- package/tests/lib/ensure-branch.test.mjs +59 -0
- package/tests/lib/execution-control-plane.test.mjs +168 -0
- package/tests/lib/execution-plan.test.mjs +474 -0
- package/tests/lib/guard-specs-merged.test.mjs +108 -0
- package/tests/lib/guard-tests-passing.test.mjs +112 -0
- package/tests/lib/guard-transitions.test.mjs +262 -0
- package/tests/lib/guard.test.mjs +611 -0
- package/tests/lib/hash.test.mjs +138 -0
- package/tests/lib/infer-workflow.test.mjs +161 -0
- package/tests/lib/install-git-hooks.test.mjs +20 -0
- package/tests/lib/marketplace-release-docs.test.mjs +31 -0
- package/tests/lib/minimality-discipline.test.mjs +37 -0
- package/tests/lib/model-profiles-docs.test.mjs +33 -0
- package/tests/lib/node20-compatibility.test.mjs +73 -0
- package/tests/lib/node20-test-entry.test.mjs +17 -0
- package/tests/lib/platform-runtime-distribution.test.mjs +150 -0
- package/tests/lib/raw-mode-smoke.test.mjs +42 -0
- package/tests/lib/sdd-overlay.test.mjs +118 -0
- package/tests/lib/spec-paths.test.mjs +80 -0
- package/tests/lib/state-loader.test.mjs +251 -0
- package/tests/lib/token-baseline.test.mjs +72 -0
- package/tests/lib/token-rules.test.mjs +94 -0
- package/tests/lib/verify-marketplace-release.test.mjs +128 -0
- package/tests/tsconfig.json +12 -0
- package/token-baseline.json +95 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// scripts/guard/checks/schema-valid.mjs — validate artifacts using the existing Validator engine
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { validateSpecPathLayout, relativeSpecPath } from '../../lib/spec-paths.mjs';
|
|
5
|
+
|
|
6
|
+
// Cached Validator instance, lazily loaded from dist/
|
|
7
|
+
let _Validator = null;
|
|
8
|
+
|
|
9
|
+
async function getValidator() {
|
|
10
|
+
if (_Validator) return _Validator;
|
|
11
|
+
try {
|
|
12
|
+
const distPath = new URL('../../../dist/index.js', import.meta.url).pathname;
|
|
13
|
+
const mod = await import(distPath);
|
|
14
|
+
_Validator = mod.Validator;
|
|
15
|
+
return _Validator;
|
|
16
|
+
} catch (err) {
|
|
17
|
+
throw new Error(`Failed to load Validator engine. Run 'npm run build' first. Original error: ${err.message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validate all artifacts in a change directory using the Validator engine.
|
|
23
|
+
* Returns { pass, failures[] } — pass is true only if all artifacts are valid.
|
|
24
|
+
*/
|
|
25
|
+
export async function checkSchemaValid(changeDir) {
|
|
26
|
+
const failures = [];
|
|
27
|
+
const warnings = [];
|
|
28
|
+
const Validator = await getValidator();
|
|
29
|
+
const validator = new Validator();
|
|
30
|
+
|
|
31
|
+
// Validate proposal.md
|
|
32
|
+
const proposalPath = path.join(changeDir, 'proposal.md');
|
|
33
|
+
if (fs.existsSync(proposalPath)) {
|
|
34
|
+
const content = fs.readFileSync(proposalPath, 'utf-8');
|
|
35
|
+
const changeName = path.basename(changeDir);
|
|
36
|
+
const report = validator.validateChangeContent(changeName, content);
|
|
37
|
+
for (const issue of report.issues) {
|
|
38
|
+
if (issue.level === 'ERROR') {
|
|
39
|
+
failures.push(`proposal.md: ${issue.message}`);
|
|
40
|
+
} else if (issue.level === 'WARNING') {
|
|
41
|
+
warnings.push(`proposal.md: ${issue.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const specLayout = validateSpecPathLayout(changeDir, { requireSpecs: true });
|
|
47
|
+
failures.push(...specLayout.failures);
|
|
48
|
+
|
|
49
|
+
for (const specFile of specLayout.specFiles) {
|
|
50
|
+
const content = fs.readFileSync(specFile, 'utf-8');
|
|
51
|
+
const report = validator.validateDeltaSpec(content);
|
|
52
|
+
const rel = relativeSpecPath(changeDir, specFile);
|
|
53
|
+
for (const issue of report.issues) {
|
|
54
|
+
if (issue.level === 'ERROR') {
|
|
55
|
+
failures.push(`${rel}: ${issue.message}`);
|
|
56
|
+
} else if (issue.level === 'WARNING') {
|
|
57
|
+
warnings.push(`${rel}: ${issue.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { pass: failures.length === 0, failures, warnings };
|
|
63
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// scripts/guard/checks/specs-merged.mjs — block closing while delta specs are unmerged
|
|
2
|
+
import { readState } from '../../lib/state-loader.mjs';
|
|
3
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
// A delta spec block is introduced by any of these requirement headers.
|
|
7
|
+
const DELTA_RE = /^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements/m;
|
|
8
|
+
|
|
9
|
+
function collectSpecFiles(specsDir) {
|
|
10
|
+
const out = [];
|
|
11
|
+
const entries = readdirSync(specsDir, { recursive: true, withFileTypes: true });
|
|
12
|
+
for (const e of entries) {
|
|
13
|
+
if (e.isFile() && /\.(md|markdown)$/i.test(e.name)) {
|
|
14
|
+
// With recursive readdir, e.parentPath gives the directory.
|
|
15
|
+
const dir = typeof e.parentPath === 'string' ? e.parentPath : specsDir;
|
|
16
|
+
out.push(join(dir, e.name));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function hasDeltaSpecs(changeDir) {
|
|
23
|
+
const specsDir = join(changeDir, 'specs');
|
|
24
|
+
if (!existsSync(specsDir)) return false;
|
|
25
|
+
for (const file of collectSpecFiles(specsDir)) {
|
|
26
|
+
const content = readFileSync(file, 'utf-8');
|
|
27
|
+
if (DELTA_RE.test(content)) return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns { pass, failures[] }.
|
|
34
|
+
* Passes when either spec_merged is recorded, or there are no delta specs to merge.
|
|
35
|
+
* Blocks `executing → closing` when delta specs exist but spec-merger hasn't run.
|
|
36
|
+
*/
|
|
37
|
+
export function checkSpecsMerged(changeDir) {
|
|
38
|
+
const state = readState(changeDir);
|
|
39
|
+
if (state.spec_merged === true || state.spec_merged === 'true') {
|
|
40
|
+
return { pass: true, failures: [] };
|
|
41
|
+
}
|
|
42
|
+
if (!hasDeltaSpecs(changeDir)) {
|
|
43
|
+
return { pass: true, failures: [] };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
pass: false,
|
|
47
|
+
failures: [
|
|
48
|
+
'Delta specs exist in specs/ but spec-merger has not run (spec_merged not recorded). Run spec-merger to merge ADDED/MODIFIED/REMOVED/RENAMED requirements before closing.',
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// scripts/guard/checks/tasks-complete.mjs — verify all tasks in tasks.md are checked off
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check that tasks.md has no unchecked items and at least one completed item.
|
|
7
|
+
* Returns { pass, failures[] }.
|
|
8
|
+
*/
|
|
9
|
+
export function checkTasksComplete(changeDir) {
|
|
10
|
+
const tasksPath = path.join(changeDir, 'tasks.md');
|
|
11
|
+
if (!fs.existsSync(tasksPath)) {
|
|
12
|
+
return { pass: false, failures: ['tasks.md: missing'] };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const content = fs.readFileSync(tasksPath, 'utf-8');
|
|
16
|
+
const unchecked = content.match(/^[ \t]*- \[ \]/gm);
|
|
17
|
+
|
|
18
|
+
if (unchecked && unchecked.length > 0) {
|
|
19
|
+
return {
|
|
20
|
+
pass: false,
|
|
21
|
+
failures: [`tasks.md: ${unchecked.length} unchecked task(s) remaining`],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const hasAny = content.match(/^[ \t]*- \[[xX]\]/gm);
|
|
26
|
+
if (!hasAny) {
|
|
27
|
+
return { pass: false, failures: ['tasks.md: no completed tasks found'] };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { pass: true, failures: [] };
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// scripts/guard/checks/tests-passing.mjs — verify verification outcome is recorded as pass
|
|
2
|
+
import { readState } from '../../lib/state-loader.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check that .team-flow.yaml records a passing verification outcome.
|
|
6
|
+
*
|
|
7
|
+
* Historically only `test_result: pass` was accepted, but release-archivist
|
|
8
|
+
* records the verification result in `dp_6_result` (e.g. "pass: ..."). The
|
|
9
|
+
* closing transition was unreachable because nothing ever wrote `test_result`,
|
|
10
|
+
* so we accept either signal. See BUG-A (issue #28 root cause).
|
|
11
|
+
*
|
|
12
|
+
* Returns { pass, failures[] }.
|
|
13
|
+
*/
|
|
14
|
+
export function checkTestsPassing(changeDir) {
|
|
15
|
+
const state = readState(changeDir);
|
|
16
|
+
const markers = [state.test_result, state.dp_6_result];
|
|
17
|
+
const passed = markers.some((v) => {
|
|
18
|
+
if (typeof v !== 'string') return false;
|
|
19
|
+
const normalized = v.trim().replace(/^["']|["']$/g, '');
|
|
20
|
+
return normalized.toLowerCase().startsWith('pass');
|
|
21
|
+
});
|
|
22
|
+
if (passed) {
|
|
23
|
+
return { pass: true, failures: [] };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
pass: false,
|
|
27
|
+
failures: [
|
|
28
|
+
`verification outcome missing: test_result is '${state.test_result || 'null'}' and dp_6_result is '${state.dp_6_result || 'null'}' — expected a result starting with 'pass'. Run release-archivist verification (DP-6) first.`,
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// design-token-guard.mjs — 设计系统完整性检查(v0.18.0)
|
|
3
|
+
//
|
|
4
|
+
// Validates that a design-system.md is structurally complete and that a
|
|
5
|
+
// design-tokens.css provides the token coverage the prototype skill relies on
|
|
6
|
+
// (A1 identity tokens, A2 derived state tokens, B-slot alias tokens).
|
|
7
|
+
//
|
|
8
|
+
// Usage: node scripts/guard/design-token-guard.mjs <design-system.md path> [design-tokens.css path]
|
|
9
|
+
// Exit 0: PASS — all required checks satisfied
|
|
10
|
+
// Exit 1: FAIL — one or more issues found (listed in the report)
|
|
11
|
+
|
|
12
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
13
|
+
|
|
14
|
+
// ── Check definitions ──
|
|
15
|
+
|
|
16
|
+
// The 9 required ## sections in design-system.md.
|
|
17
|
+
const REQUIRED_SECTIONS = [
|
|
18
|
+
'color', 'typography', 'spacing', 'layout', 'components',
|
|
19
|
+
'motion', 'voice', 'brand', 'anti-patterns',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// Optional ## sections — reported for visibility, never counted as failures.
|
|
23
|
+
const OPTIONAL_SECTIONS = ['palette', 'aliases', 'extensions'];
|
|
24
|
+
|
|
25
|
+
// The 5 palette directions checked when a palette section exists.
|
|
26
|
+
const PALETTE_DIRECTIONS = ['neutral', 'primary', 'success', 'warning', 'danger'];
|
|
27
|
+
|
|
28
|
+
// A1 identity tokens — 8 required in design-tokens.css.
|
|
29
|
+
const A1_IDENTITY_TOKENS = [
|
|
30
|
+
'--bg', '--surface', '--fg', '--muted', '--border',
|
|
31
|
+
'--accent', '--font-display', '--font-body',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// A2 derived state tokens — must be defined via a color-mix() formula.
|
|
35
|
+
const A2_DERIVED_TOKENS = ['--accent-hover', '--accent-active', '--focus-ring', '--elev-raised'];
|
|
36
|
+
|
|
37
|
+
// B-slot alias tokens — each should resolve (via var()) to its base token.
|
|
38
|
+
const B_SLOT_ALIASES = [
|
|
39
|
+
{ token: '--fg-2', base: '--fg' },
|
|
40
|
+
{ token: '--meta', base: '--muted' },
|
|
41
|
+
{ token: '--border-soft', base: '--border' },
|
|
42
|
+
{ token: '--surface-warm', base: '--surface' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// ── Helpers ──
|
|
46
|
+
|
|
47
|
+
// Read a file as UTF-8, or return null when it does not exist.
|
|
48
|
+
function readFileOrNull(filePath) {
|
|
49
|
+
if (!filePath || !existsSync(filePath)) return null;
|
|
50
|
+
return readFileSync(filePath, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Collect the lower-cased text of every level-2 ("## ") markdown header.
|
|
54
|
+
function extractH2Headers(markdown) {
|
|
55
|
+
return markdown
|
|
56
|
+
.split('\n')
|
|
57
|
+
.filter(line => /^##\s+/.test(line) && !/^###/.test(line))
|
|
58
|
+
.map(line => line.replace(/^##\s+/, '').trim().toLowerCase());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// True when any ## header mentions the given section name.
|
|
62
|
+
function hasSection(headers, section) {
|
|
63
|
+
return headers.some(header => header.includes(section));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Return the body of a section (text between its ## header and the next ##
|
|
67
|
+
// header or EOF), lower-cased; null when the section is absent.
|
|
68
|
+
function sectionBody(markdown, section) {
|
|
69
|
+
const lines = markdown.split('\n');
|
|
70
|
+
const startIdx = lines.findIndex(
|
|
71
|
+
line => /^##\s+/.test(line) && !/^###/.test(line) &&
|
|
72
|
+
line.replace(/^##\s+/, '').trim().toLowerCase().includes(section),
|
|
73
|
+
);
|
|
74
|
+
if (startIdx === -1) return null;
|
|
75
|
+
const body = [];
|
|
76
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
77
|
+
if (/^##\s+/.test(lines[i]) && !/^###/.test(lines[i])) break;
|
|
78
|
+
body.push(lines[i]);
|
|
79
|
+
}
|
|
80
|
+
return body.join('\n').toLowerCase();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Escape a token name for safe use inside a RegExp (names contain "--").
|
|
84
|
+
function escapeRegExp(value) {
|
|
85
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Return the declared value of a CSS custom property, or null when absent.
|
|
89
|
+
function getTokenValue(css, name) {
|
|
90
|
+
const match = css.match(new RegExp(`${escapeRegExp(name)}\\s*:\\s*([^;]+);`));
|
|
91
|
+
return match ? match[1].trim() : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Extract the var(--x) reference from a token value, if present.
|
|
95
|
+
function extractVarTarget(value) {
|
|
96
|
+
const match = value.match(/var\(\s*(--[a-z0-9-]+)/i);
|
|
97
|
+
return match ? match[1] : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Report accumulation ──
|
|
101
|
+
|
|
102
|
+
const issues = [];
|
|
103
|
+
function line(text = '') { console.log(text); }
|
|
104
|
+
function pass(text) { line(` ✅ ${text}`); }
|
|
105
|
+
function fail(text) { issues.push(text); line(` ❌ ${text}`); }
|
|
106
|
+
|
|
107
|
+
// ── Argument parsing ──
|
|
108
|
+
|
|
109
|
+
const mdPath = process.argv[2];
|
|
110
|
+
const cssPath = process.argv[3];
|
|
111
|
+
|
|
112
|
+
if (!mdPath) {
|
|
113
|
+
console.error('Usage: node scripts/guard/design-token-guard.mjs <design-system.md path> [design-tokens.css path]');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const markdown = readFileOrNull(mdPath);
|
|
118
|
+
const css = cssPath ? readFileOrNull(cssPath) : null;
|
|
119
|
+
|
|
120
|
+
line('Design Token Guard v0.18.0');
|
|
121
|
+
line('==========================');
|
|
122
|
+
line(`design-system.md: ${mdPath}`);
|
|
123
|
+
line(`design-tokens.css: ${cssPath || 'not provided'}`);
|
|
124
|
+
line();
|
|
125
|
+
|
|
126
|
+
// ── File availability ──
|
|
127
|
+
|
|
128
|
+
if (markdown === null) {
|
|
129
|
+
fail(`design-system.md not found: ${mdPath}`);
|
|
130
|
+
}
|
|
131
|
+
if (cssPath && css === null) {
|
|
132
|
+
fail(`design-tokens.css not found: ${cssPath}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── 9-Section Check ──
|
|
136
|
+
|
|
137
|
+
line('## 9-Section Check');
|
|
138
|
+
if (markdown === null) {
|
|
139
|
+
for (const section of REQUIRED_SECTIONS) fail(`${section} — MISSING (file not found)`);
|
|
140
|
+
} else {
|
|
141
|
+
const headers = extractH2Headers(markdown);
|
|
142
|
+
for (const section of REQUIRED_SECTIONS) {
|
|
143
|
+
if (hasSection(headers, section)) pass(section);
|
|
144
|
+
else fail(`${section} — MISSING`);
|
|
145
|
+
}
|
|
146
|
+
// Optional sections: report presence only, never fail.
|
|
147
|
+
for (const section of OPTIONAL_SECTIONS) {
|
|
148
|
+
if (hasSection(headers, section)) line(` ℹ️ ${section} (optional, present)`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
line();
|
|
152
|
+
|
|
153
|
+
// ── Palette Check (5 directions) ──
|
|
154
|
+
|
|
155
|
+
line('## Palette Check (5 directions)');
|
|
156
|
+
if (markdown === null) {
|
|
157
|
+
line(' ⏭️ skipped — design-system.md not found');
|
|
158
|
+
} else {
|
|
159
|
+
const palette = sectionBody(markdown, 'palette');
|
|
160
|
+
if (palette === null) {
|
|
161
|
+
line(' ⏭️ skipped — no palette section (optional)');
|
|
162
|
+
} else {
|
|
163
|
+
for (const direction of PALETTE_DIRECTIONS) {
|
|
164
|
+
if (palette.includes(direction)) pass(direction);
|
|
165
|
+
else fail(`${direction} — MISSING`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
line();
|
|
170
|
+
|
|
171
|
+
// ── A1 Identity Tokens ──
|
|
172
|
+
|
|
173
|
+
line('## A1 Identity Tokens (8 required)');
|
|
174
|
+
if (css === null) {
|
|
175
|
+
line(` ⏭️ skipped — design-tokens.css ${cssPath ? 'not found' : 'not provided'}`);
|
|
176
|
+
} else {
|
|
177
|
+
for (const token of A1_IDENTITY_TOKENS) {
|
|
178
|
+
if (getTokenValue(css, token) !== null) pass(token);
|
|
179
|
+
else fail(`${token} — MISSING`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
line();
|
|
183
|
+
|
|
184
|
+
// ── A2 Derived Tokens ──
|
|
185
|
+
|
|
186
|
+
line('## A2 Derived Tokens');
|
|
187
|
+
if (css === null) {
|
|
188
|
+
line(` ⏭️ skipped — design-tokens.css ${cssPath ? 'not found' : 'not provided'}`);
|
|
189
|
+
} else {
|
|
190
|
+
for (const token of A2_DERIVED_TOKENS) {
|
|
191
|
+
const value = getTokenValue(css, token);
|
|
192
|
+
if (value === null) {
|
|
193
|
+
fail(`${token} — MISSING`);
|
|
194
|
+
} else if (/color-mix\(/i.test(value)) {
|
|
195
|
+
pass(`${token} (color-mix found)`);
|
|
196
|
+
} else {
|
|
197
|
+
fail(`${token} — no color-mix formula`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
line();
|
|
202
|
+
|
|
203
|
+
// ── B-Slot Aliases ──
|
|
204
|
+
|
|
205
|
+
line('## B-Slot Aliases');
|
|
206
|
+
if (css === null) {
|
|
207
|
+
line(` ⏭️ skipped — design-tokens.css ${cssPath ? 'not found' : 'not provided'}`);
|
|
208
|
+
} else {
|
|
209
|
+
for (const { token, base } of B_SLOT_ALIASES) {
|
|
210
|
+
const value = getTokenValue(css, token);
|
|
211
|
+
if (value === null) {
|
|
212
|
+
fail(`${token} — MISSING`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const target = extractVarTarget(value);
|
|
216
|
+
if (target === base) {
|
|
217
|
+
pass(`${token} → var(${target})`);
|
|
218
|
+
} else if (target) {
|
|
219
|
+
// Alias exists but points somewhere other than the expected base token.
|
|
220
|
+
fail(`${token} → var(${target}) (expected var(${base}))`);
|
|
221
|
+
} else {
|
|
222
|
+
// Defined as a literal value rather than an alias to the base token.
|
|
223
|
+
pass(`${token} = ${value}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
line();
|
|
228
|
+
|
|
229
|
+
// ── Verdict ──
|
|
230
|
+
|
|
231
|
+
if (issues.length === 0) {
|
|
232
|
+
line('Verdict: PASS');
|
|
233
|
+
process.exit(0);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
line(`Verdict: FAIL (${issues.length} issue${issues.length === 1 ? '' : 's'})`);
|
|
237
|
+
process.exit(1);
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/guard/guard.mjs — dimension-based phase transition guard
|
|
3
|
+
// Usage: node guard.mjs check <change-dir> <from-state> <to-state> [--json]
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { checkArtifactsExist } from './checks/artifacts-exist.mjs';
|
|
6
|
+
import { checkTasksComplete } from './checks/tasks-complete.mjs';
|
|
7
|
+
import { checkTestsPassing } from './checks/tests-passing.mjs';
|
|
8
|
+
import { checkContractFresh } from './checks/contract-fresh.mjs';
|
|
9
|
+
import { check as checkDpGate } from './checks/dp-gate-passed.mjs';
|
|
10
|
+
import { checkSpecsMerged } from './checks/specs-merged.mjs';
|
|
11
|
+
import { checkContractCurrent } from './checks/contract-current.mjs';
|
|
12
|
+
import { checkDp3Approved } from './checks/dp3-approved.mjs';
|
|
13
|
+
import { checkExecutionPlanReady } from './checks/execution-plan-ready.mjs';
|
|
14
|
+
import { checkExecutionReviewsPassed } from './checks/execution-reviews-passed.mjs';
|
|
15
|
+
|
|
16
|
+
// Transition matrix: <from>:<to> → required check dimensions
|
|
17
|
+
const TRANSITION_CHECKS = {
|
|
18
|
+
// Forward transitions
|
|
19
|
+
'exploring:specifying': ['artifacts-exist'],
|
|
20
|
+
'specifying:bridging': ['artifacts-exist', 'schema-valid'],
|
|
21
|
+
'bridging:approved-for-build': ['artifacts-exist', 'schema-valid', 'contract-fresh', 'dp-gate-passed'],
|
|
22
|
+
'approved-for-build:executing': ['artifacts-exist', 'contract-fresh', 'dp-gate-passed', 'execution-plan-ready'],
|
|
23
|
+
'executing:closing': ['tasks-complete', 'tests-passing', 'specs-merged', 'execution-plan-ready', 'execution-reviews-passed'],
|
|
24
|
+
|
|
25
|
+
// Debugging side-path
|
|
26
|
+
'executing:debugging': [],
|
|
27
|
+
'debugging:executing': ['contract-fresh', 'execution-plan-ready'],
|
|
28
|
+
|
|
29
|
+
// Fast-path transitions are workflow-gated; full workflow must reject them explicitly.
|
|
30
|
+
'exploring:bridging': [],
|
|
31
|
+
'exploring:approved-for-build': [],
|
|
32
|
+
|
|
33
|
+
// Rewind transitions (scope change, contract drift, verification failure)
|
|
34
|
+
'specifying:exploring': [],
|
|
35
|
+
'bridging:specifying': [],
|
|
36
|
+
'approved-for-build:specifying': [],
|
|
37
|
+
'approved-for-build:bridging': [],
|
|
38
|
+
'executing:specifying': [],
|
|
39
|
+
'executing:bridging': [],
|
|
40
|
+
'closing:specifying': [],
|
|
41
|
+
|
|
42
|
+
// Abandon transitions (terminal)
|
|
43
|
+
'exploring:abandoned': [],
|
|
44
|
+
'specifying:abandoned': [],
|
|
45
|
+
'bridging:abandoned': [],
|
|
46
|
+
'approved-for-build:abandoned': [],
|
|
47
|
+
'executing:abandoned': [],
|
|
48
|
+
'debugging:abandoned': [],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const WORKFLOW_TRANSITION_CHECKS = {
|
|
52
|
+
hotfix: {
|
|
53
|
+
'exploring:bridging': [],
|
|
54
|
+
'bridging:approved-for-build': ['contract-current', 'dp3-approved'],
|
|
55
|
+
'approved-for-build:executing': ['contract-current', 'dp3-approved', 'execution-plan-ready'],
|
|
56
|
+
},
|
|
57
|
+
tweak: {
|
|
58
|
+
'exploring:approved-for-build': [],
|
|
59
|
+
// Tweak remains a low-risk fast path: it is intentionally exempt from
|
|
60
|
+
// execution-plan and per-wave review receipt requirements.
|
|
61
|
+
'approved-for-build:executing': ['artifacts-exist', 'contract-fresh', 'dp-gate-passed'],
|
|
62
|
+
'executing:closing': ['tasks-complete', 'tests-passing', 'specs-merged'],
|
|
63
|
+
'debugging:executing': ['contract-fresh'],
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const TRANSITION_WORKFLOW_REQUIREMENTS = {
|
|
68
|
+
'exploring:bridging': ['hotfix'],
|
|
69
|
+
'exploring:approved-for-build': ['tweak'],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function checkWorkflowAllowed(key, workflow) {
|
|
73
|
+
const allowed = TRANSITION_WORKFLOW_REQUIREMENTS[key];
|
|
74
|
+
if (!allowed || allowed.includes(workflow)) return { pass: true, checks: [] };
|
|
75
|
+
return {
|
|
76
|
+
pass: false,
|
|
77
|
+
checks: [{
|
|
78
|
+
dimension: 'workflow-mode',
|
|
79
|
+
pass: false,
|
|
80
|
+
failures: [`${key.replace(':', ' -> ')} is a fast-path transition allowed only for workflow ${allowed.join(' or ')}; current workflow is ${workflow}`],
|
|
81
|
+
}],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveDimensions(key, workflow) {
|
|
86
|
+
return WORKFLOW_TRANSITION_CHECKS[workflow]?.[key] ?? TRANSITION_CHECKS[key];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function main() {
|
|
90
|
+
const { positionals, values } = parseArgs({
|
|
91
|
+
options: {
|
|
92
|
+
json: { type: 'boolean', default: false },
|
|
93
|
+
workflow: { type: 'string', default: 'full' },
|
|
94
|
+
},
|
|
95
|
+
allowPositionals: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const subcommand = positionals[0];
|
|
99
|
+
if (subcommand !== 'check') {
|
|
100
|
+
console.error('Usage: guard.mjs check <change-dir> <from-state> <to-state> [--json] [--workflow <mode>]');
|
|
101
|
+
process.exit(2);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const changeDir = positionals[1];
|
|
105
|
+
const fromState = positionals[2];
|
|
106
|
+
const toState = positionals[3];
|
|
107
|
+
const useJson = values.json;
|
|
108
|
+
const workflow = values.workflow;
|
|
109
|
+
|
|
110
|
+
const VALID_WORKFLOWS = ['full', 'hotfix', 'tweak'];
|
|
111
|
+
if (!VALID_WORKFLOWS.includes(workflow)) {
|
|
112
|
+
console.error(`Invalid workflow: ${workflow}. Must be one of: ${VALID_WORKFLOWS.join(', ')}`);
|
|
113
|
+
process.exit(2);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!changeDir || !fromState || !toState) {
|
|
117
|
+
console.error('Usage: guard.mjs check <change-dir> <from-state> <to-state> [--json]');
|
|
118
|
+
process.exit(2);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const key = `${fromState}:${toState}`;
|
|
122
|
+
const dimensions = resolveDimensions(key, workflow);
|
|
123
|
+
|
|
124
|
+
if (!dimensions) {
|
|
125
|
+
const valid = Object.keys(TRANSITION_CHECKS).join(', ');
|
|
126
|
+
const msg = `Unknown transition: ${fromState} -> ${toState}. Valid transitions: ${valid}`;
|
|
127
|
+
if (useJson) console.log(JSON.stringify({ pass: false, checks: [], error: msg }));
|
|
128
|
+
else console.error(msg);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const workflowCheck = checkWorkflowAllowed(key, workflow);
|
|
133
|
+
if (!workflowCheck.pass) {
|
|
134
|
+
if (useJson) {
|
|
135
|
+
console.log(JSON.stringify({ pass: false, checks: workflowCheck.checks }, null, 2));
|
|
136
|
+
} else {
|
|
137
|
+
console.error('Guard checks failed:');
|
|
138
|
+
for (const c of workflowCheck.checks) {
|
|
139
|
+
for (const f of c.failures) {
|
|
140
|
+
console.error(` [FAIL] ${c.dimension}: ${f}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (dimensions.length === 0) {
|
|
148
|
+
const result = { pass: true, checks: [] };
|
|
149
|
+
if (useJson) console.log(JSON.stringify(result));
|
|
150
|
+
else console.log('All checks passed (no checks required for this transition).');
|
|
151
|
+
process.exit(0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const CHECK_RUNNERS = {
|
|
155
|
+
'artifacts-exist': (dir) => checkArtifactsExist(dir),
|
|
156
|
+
'schema-valid': async (dir) => (await import('./checks/schema-valid.mjs')).checkSchemaValid(dir),
|
|
157
|
+
'contract-fresh': (dir) => checkContractFresh(dir),
|
|
158
|
+
'contract-current': (dir) => checkContractCurrent(dir),
|
|
159
|
+
'tasks-complete': (dir) => checkTasksComplete(dir),
|
|
160
|
+
'tests-passing': (dir) => checkTestsPassing(dir),
|
|
161
|
+
'specs-merged': (dir) => checkSpecsMerged(dir),
|
|
162
|
+
'dp-gate-passed': (dir) => checkDpGate(dir, fromState, toState),
|
|
163
|
+
'dp3-approved': (dir) => checkDp3Approved(dir),
|
|
164
|
+
'execution-plan-ready': (dir) => checkExecutionPlanReady(dir),
|
|
165
|
+
'execution-reviews-passed': (dir) => checkExecutionReviewsPassed(dir),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const checks = [];
|
|
169
|
+
let pass = true;
|
|
170
|
+
|
|
171
|
+
for (const dim of dimensions) {
|
|
172
|
+
const runner = CHECK_RUNNERS[dim];
|
|
173
|
+
const result = runner
|
|
174
|
+
? await runner(changeDir)
|
|
175
|
+
: { pass: false, failures: [`Unknown dimension: ${dim}`] };
|
|
176
|
+
checks.push({ dimension: dim, pass: result.pass, failures: result.failures || [] });
|
|
177
|
+
if (!result.pass) pass = false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
pass = checks.every(c => c.pass);
|
|
181
|
+
|
|
182
|
+
if (useJson) {
|
|
183
|
+
console.log(JSON.stringify({ pass, checks }, null, 2));
|
|
184
|
+
} else {
|
|
185
|
+
if (pass) {
|
|
186
|
+
console.log('All checks passed.');
|
|
187
|
+
} else {
|
|
188
|
+
console.error('Guard checks failed:');
|
|
189
|
+
for (const c of checks) {
|
|
190
|
+
if (!c.pass) {
|
|
191
|
+
for (const f of c.failures) {
|
|
192
|
+
console.error(` [FAIL] ${c.dimension}: ${f}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
process.exit(pass ? 0 : 1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
main().catch(err => {
|
|
203
|
+
console.error('Guard error:', err.message);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
});
|