@opengsd/gsd-core 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check verify-schema-drift` as a gate module (#5219, epic #5056, ADR-5057 §4 closing arm C): it
|
|
4
|
+
* returns a `GateResult`; the command router formats it. Imports no io module and performs no direct
|
|
5
|
+
* console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* The `drift` capability's blocking gate at `execute:wave:post`. It reports schema-relevant files the
|
|
8
|
+
* phase's plans declare in `files_modified` for which no database push is evidenced in the phase's
|
|
9
|
+
* summaries or its own commit subjects (`checkSchemaDrift`, `schema-detect.cjs`).
|
|
10
|
+
*
|
|
11
|
+
* Dispatched gate, payload exit mode: the verdict is read from stdout (`.block`) and a non-zero exit
|
|
12
|
+
* is "the check command failed" (gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md step
|
|
13
|
+
* 1; gsd-core/references/loop-hook-dispatch.md "a gate verb never exits non-zero to say blocked";
|
|
14
|
+
* capabilities/drift declares schema-drift `blocking: true, onError: skip`). A blocking verdict is
|
|
15
|
+
* therefore exit 0 and "could not look" is exit 69 (#5170); the router declares both.
|
|
16
|
+
*
|
|
17
|
+
* Non-blocking contract: a throw anywhere yields a non-blocking `unreadable` verdict, never a crash.
|
|
18
|
+
*
|
|
19
|
+
* Argv after the verb: `<phase>`. The bypass (`GSD_SKIP_SCHEMA_CHECK=true`) arrives as `env`, read by
|
|
20
|
+
* the router from the process environment; the gate reads no ambient state for it.
|
|
21
|
+
*/
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.evaluateSchemaDriftGate = evaluateSchemaDriftGate;
|
|
27
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
28
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
29
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
30
|
+
const gate_evaluation_scope_cjs_1 = require("./gate-evaluation-scope.cjs");
|
|
31
|
+
const schema_detect_cjs_1 = require("./schema-detect.cjs");
|
|
32
|
+
const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
|
34
|
+
const planningWorkspace = require("./planning-workspace.cjs");
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-document.cjs is an export= CommonJS module
|
|
36
|
+
const planDocumentMod = require("./plan-document.cjs");
|
|
37
|
+
const { planningDir } = planningWorkspace;
|
|
38
|
+
const { parsePlanDocument } = planDocumentMod;
|
|
39
|
+
/**
|
|
40
|
+
* The schema-drift verdict for "drift was not evaluated" (#5170): non-blocking payload (the
|
|
41
|
+
* gate's non-blocking contract), outcome `unreadable` so the exit status says "could not look".
|
|
42
|
+
*/
|
|
43
|
+
function schemaDriftUnreadable(message, detail) {
|
|
44
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(false, {
|
|
45
|
+
block: false,
|
|
46
|
+
drift_detected: false,
|
|
47
|
+
blocking: false,
|
|
48
|
+
...(detail === undefined ? {} : { unreadable: true, unreadable_file: detail.file, read_error: detail.reason }),
|
|
49
|
+
message,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function runSchemaDriftGate(cwd, phaseArg, skipFlag) {
|
|
53
|
+
const pDir = planningDir(cwd);
|
|
54
|
+
const phasesDir = node_path_1.default.join(pDir, 'phases');
|
|
55
|
+
// An ABSENT phases directory is the documented "nothing to check" (`none`); one that cannot be
|
|
56
|
+
// examined (an EACCES on a parent — `fs.existsSync` said `false` for it) is `unreadable`.
|
|
57
|
+
const phasesRoot = (0, gate_evidence_cjs_1.statEvidence)(phasesDir);
|
|
58
|
+
if (phasesRoot.kind === 'unreadable') {
|
|
59
|
+
return schemaDriftUnreadable(`schema-drift could not examine ${phasesDir} (${phasesRoot.reason}); drift was not evaluated`, { file: phasesDir, reason: phasesRoot.reason });
|
|
60
|
+
}
|
|
61
|
+
if (phasesRoot.kind === 'none') {
|
|
62
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { block: false, drift_detected: false, blocking: false, message: 'No phases directory' });
|
|
63
|
+
}
|
|
64
|
+
// Resolve the phase directory with the canonical phase-directory matcher
|
|
65
|
+
// (phase-id.cjs::matchPhaseDirs), not a naive substring test. A bare
|
|
66
|
+
// `.includes(phaseArg)` lets a non-existent phase silently match a different
|
|
67
|
+
// phase whose directory name merely contains the requested token (e.g. "1"
|
|
68
|
+
// matching "11-expansion"), making the drift gate inspect the wrong phase.
|
|
69
|
+
// This shares the one selection rule with find-phase / verify
|
|
70
|
+
// phase-completeness rather than restating it. (#1571, #2528)
|
|
71
|
+
const phaseDir = (0, gate_phase_context_cjs_1.resolvePhaseDirByToken)(phasesDir, phaseArg);
|
|
72
|
+
if (!phaseDir) {
|
|
73
|
+
// An unresolvable phase is "could not look" (#5170): there is no phase directory to evaluate.
|
|
74
|
+
return schemaDriftUnreadable(`Phase directory not found: ${phaseArg}`);
|
|
75
|
+
}
|
|
76
|
+
// #3183: canonical LIVE plan/summary sets (root+nested,
|
|
77
|
+
// status: superseded EXCLUDED) from the single owner, rather than a
|
|
78
|
+
// root-only readdirSync filter — a superseded plan's claimed
|
|
79
|
+
// files_modified is no longer treated as an expected drift target, and
|
|
80
|
+
// nested (#3139 layout) plans/summaries are no longer invisible to the
|
|
81
|
+
// drift check.
|
|
82
|
+
// A scan that did not see every plan (an existing nested plans/ that could not be read) is
|
|
83
|
+
// `unreadable`, never a short plan set: the files_modified of the plans it missed are drift targets.
|
|
84
|
+
const planScan = (0, gate_evidence_cjs_1.readPlanScanEvidence)(phaseDir);
|
|
85
|
+
if (planScan.kind === 'unreadable' && !skipFlag) {
|
|
86
|
+
return schemaDriftUnreadable(`schema-drift could not scan the plans of ${phaseDir} (${planScan.reason}); drift was not evaluated`, { file: phaseDir, reason: planScan.reason });
|
|
87
|
+
}
|
|
88
|
+
const { planFiles, summaryFiles } = planScan.kind === 'found' ? planScan.value : { planFiles: [], summaryFiles: [] };
|
|
89
|
+
// #5170 (ADR-5057 §4): every read here is typed evidence. A file that cannot be read is
|
|
90
|
+
// `unreadable` and is reported as such — it is never "no files, so no drift". A file that
|
|
91
|
+
// vanished since the scan (`none`) contributes nothing.
|
|
92
|
+
let unreadableRead = null;
|
|
93
|
+
const allFiles = [];
|
|
94
|
+
for (const pf of planFiles) {
|
|
95
|
+
const read = (0, gate_evidence_cjs_1.readTextEvidence)(node_path_1.default.join(phaseDir, pf));
|
|
96
|
+
if (read.kind === 'unreadable') {
|
|
97
|
+
unreadableRead = unreadableRead ?? { file: pf, reason: read.reason };
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (read.kind === 'none')
|
|
101
|
+
continue;
|
|
102
|
+
// `files_modified` through the Frontmatter Module (the read phase-plan-index uses via
|
|
103
|
+
// RawPlan.filesModified): block sequences, inline arrays and CRLF all yield their files.
|
|
104
|
+
for (const file of parsePlanDocument(read.value).filesModified) {
|
|
105
|
+
const trimmed = file.trim();
|
|
106
|
+
if (trimmed)
|
|
107
|
+
allFiles.push(trimmed);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
let executionLog = '';
|
|
111
|
+
for (const sf of summaryFiles) {
|
|
112
|
+
const read = (0, gate_evidence_cjs_1.readTextEvidence)(node_path_1.default.join(phaseDir, sf));
|
|
113
|
+
if (read.kind === 'unreadable') {
|
|
114
|
+
unreadableRead = unreadableRead ?? { file: sf, reason: read.reason };
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (read.kind === 'found')
|
|
118
|
+
executionLog += read.value + '\n';
|
|
119
|
+
}
|
|
120
|
+
// `--skip` (GSD_SKIP_SCHEMA_CHECK) bypasses the gate, so what could not be read is moot then.
|
|
121
|
+
if (unreadableRead !== null && !skipFlag) {
|
|
122
|
+
return schemaDriftUnreadable(`schema-drift could not read ${unreadableRead.file} (${unreadableRead.reason}); drift was not evaluated`, { file: unreadableRead.file, reason: unreadableRead.reason });
|
|
123
|
+
}
|
|
124
|
+
// #5164: the phase's own commits from the evaluation-scope resolver (ADR-5057 §4) — the former
|
|
125
|
+
// `git log --all -50` let a commit on ANY branch, from ANY phase, put a schema push in the log.
|
|
126
|
+
const phaseScope = (0, gate_evaluation_scope_cjs_1.resolveEvaluationScope)(cwd, { kind: 'phase', phase: phaseArg, phaseDir }, { includeFiles: false });
|
|
127
|
+
if (phaseScope.commits.length > 0) {
|
|
128
|
+
// Subjects only, as the `git log --oneline` it replaces: a quoted push command in a commit BODY must not change the verdict.
|
|
129
|
+
executionLog += '\n' + phaseScope.commits.map((c) => `${c.sha.slice(0, 7)} ${c.subject}`).join('\n');
|
|
130
|
+
}
|
|
131
|
+
const result = (0, schema_detect_cjs_1.checkSchemaDrift)(allFiles, executionLog, { skipCheck: skipFlag });
|
|
132
|
+
const isSkipped = !!result['skipped'];
|
|
133
|
+
// Uniform gate contract: `block` = true means "this gate's bad condition is met".
|
|
134
|
+
// When skipCheck is true (GSD_SKIP_SCHEMA_CHECK=true), the gate is bypassed —
|
|
135
|
+
// block must be false regardless of whether drift was detected.
|
|
136
|
+
// drift_detected and blocking are kept for compatibility.
|
|
137
|
+
const block = isSkipped ? false : !!result['driftDetected'];
|
|
138
|
+
return (0, gate_verdict_cjs_1.gateVerdict)(isSkipped ? 'skip' : (block ? 'block' : 'pass'), block, {
|
|
139
|
+
block,
|
|
140
|
+
drift_detected: result['driftDetected'],
|
|
141
|
+
blocking: result['blocking'],
|
|
142
|
+
schema_files: result['schemaFiles'],
|
|
143
|
+
orms: result['orms'],
|
|
144
|
+
unpushed_orms: result['unpushedOrms'],
|
|
145
|
+
message: result['message'],
|
|
146
|
+
skipped: isSkipped,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function evaluateSchemaDriftGate(input) {
|
|
150
|
+
const phaseArg = input.args[0] || '';
|
|
151
|
+
if (!phaseArg) {
|
|
152
|
+
// UNKNOWN: the pre-move `error('Usage…')` named no reason, and the reason is observable (#5219).
|
|
153
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.UNKNOWN, 'Usage: verify schema-drift <phase> [--skip]');
|
|
154
|
+
}
|
|
155
|
+
const skipFlag = input.env?.['GSD_SKIP_SCHEMA_CHECK'] === 'true';
|
|
156
|
+
// Non-blocking contract: a throw anywhere yields a non-blocking payload, never a crash.
|
|
157
|
+
try {
|
|
158
|
+
return runSchemaDriftGate(input.projectDir, phaseArg, skipFlag);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
// #5170: a gate that threw did not evaluate drift. The payload stays non-blocking (the contract
|
|
162
|
+
// above), but the exit status says "could not look" (UNAVAILABLE) instead of a clean exit 0.
|
|
163
|
+
return schemaDriftUnreadable('exception: ' + (err instanceof Error ? err.message : String(err)));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check tdd-red-evidence` as a gate module (#5139, epic #5056, ADR-5057 §4 first bullet, #3770):
|
|
4
|
+
* it returns a `GateResult`; the command router formats it. Imports no io module and performs no
|
|
5
|
+
* direct console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* Validates a persisted RED-phase test-run record for a `type: tdd` plan. Only an INTENTIONAL
|
|
8
|
+
* failure of the target test (verdict RED_EVIDENCE_OK) may authorize GREEN; zero-test discovery,
|
|
9
|
+
* fixture/load crashes, nonzero exits without a failing test, unrelated failures, and unexpected
|
|
10
|
+
* greens are INVALID_RED and block GREEN. The record is the JSON the executor persists after
|
|
11
|
+
* running the RED command: `{ command, exitCode, output, targetTest, targetFile?, expected?,
|
|
12
|
+
* actual? }`. Fail-closed: a missing/unreadable/unparseable record is INVALID_RED (reason
|
|
13
|
+
* `unreadable_record`), never a pass.
|
|
14
|
+
*
|
|
15
|
+
* Argv after the verb: `<record.json>`. The record path resolves against the PROCESS cwd
|
|
16
|
+
* (`path.resolve`), as before the move, and must then stay INSIDE the project directory (realpath
|
|
17
|
+
* containment, ADR-4650) BEFORE anything is read: a record path that escapes it is the usage
|
|
18
|
+
* failure `path escapes its allowed directory: <arg>`, so the gate cannot be made to read — and
|
|
19
|
+
* echo the fields of — an arbitrary readable file (#5139 security review). The path echoed in the
|
|
20
|
+
* payload stays the resolved (not realpath'd) form, unchanged.
|
|
21
|
+
*
|
|
22
|
+
* Consequence of resolving against the process cwd: the CLI runs with cwd === projectDir in every
|
|
23
|
+
* workflow, so a project-relative path (`r.json`) works. Invoked with cwd !== projectDir, that same
|
|
24
|
+
* relative path resolves outside the project and is the usage failure above (it was an
|
|
25
|
+
* `unreadable_record` verdict before containment); an absolute path inside the project works from
|
|
26
|
+
* any cwd.
|
|
27
|
+
*/
|
|
28
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
29
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
|
+
};
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.evaluateTddRedEvidence = evaluateTddRedEvidence;
|
|
33
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
34
|
+
const security_cjs_1 = require("./security.cjs");
|
|
35
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
36
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
37
|
+
const tdd_red_evidence_cjs_1 = require("./tdd-red-evidence.cjs");
|
|
38
|
+
/** Parse the persisted record. A parse failure is typed evidence (never a swallowed `null`). */
|
|
39
|
+
function parseRecordEvidence(text, span) {
|
|
40
|
+
try {
|
|
41
|
+
return (0, gate_evidence_cjs_1.evidenceFound)((JSON.parse(text) ?? {}));
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
return (0, gate_evidence_cjs_1.evidenceFromError)(err, span);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function evaluateTddRedEvidence(input) {
|
|
48
|
+
const recordPath = typeof input.args[0] === 'string' ? input.args[0] : '';
|
|
49
|
+
if (!recordPath) {
|
|
50
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.SDK_MISSING_ARG, 'tdd-red-evidence requires a record path: check tdd-red-evidence <record.json>');
|
|
51
|
+
}
|
|
52
|
+
const resolved = node_path_1.default.resolve(recordPath);
|
|
53
|
+
// Containment BEFORE the read (the read follows symlinks, so it is decided on the resolved
|
|
54
|
+
// target). Only the verdict of the predicate is used; the echoed path stays `resolved`.
|
|
55
|
+
if ((0, security_cjs_1.tryWithinRoot)(resolved, input.projectDir, security_cjs_1.PathAcceptance.AbsoluteInsideRoot) === null) {
|
|
56
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.USAGE, `path escapes its allowed directory: ${recordPath}`);
|
|
57
|
+
}
|
|
58
|
+
const read = (0, gate_evidence_cjs_1.readTextEvidence)(resolved);
|
|
59
|
+
const text = read.kind === 'found' ? read.value : '';
|
|
60
|
+
const parsed = text ? parseRecordEvidence(text, resolved) : null;
|
|
61
|
+
// Malformed JSON is content that WAS read: it is a failing record (`block`), not a swallowed error.
|
|
62
|
+
const record = parsed !== null && parsed.kind === 'found' ? parsed.value : null;
|
|
63
|
+
if (!record) {
|
|
64
|
+
const payload = {
|
|
65
|
+
passed: false,
|
|
66
|
+
block: true,
|
|
67
|
+
verdict: 'INVALID_RED',
|
|
68
|
+
reason: 'unreadable_record',
|
|
69
|
+
record: resolved,
|
|
70
|
+
readError: text ? `record is not valid JSON: ${resolved}` : `record not found or unreadable: ${resolved}`,
|
|
71
|
+
};
|
|
72
|
+
// Fail-closed policy is unchanged (`block: true`); a record that exists but could not be read
|
|
73
|
+
// is "could not look" (#5170), so its outcome is `unreadable` and the exit status follows it.
|
|
74
|
+
return read.kind === 'unreadable' ? (0, gate_verdict_cjs_1.gateUnreadable)(true, payload) : (0, gate_verdict_cjs_1.gateVerdict)('block', true, payload);
|
|
75
|
+
}
|
|
76
|
+
const evidenceInput = {
|
|
77
|
+
command: record['command'],
|
|
78
|
+
exitCode: record['exitCode'],
|
|
79
|
+
output: record['output'],
|
|
80
|
+
targetTest: record['targetTest'],
|
|
81
|
+
targetFile: record['targetFile'],
|
|
82
|
+
expected: record['expected'],
|
|
83
|
+
actual: record['actual'],
|
|
84
|
+
};
|
|
85
|
+
const result = (0, tdd_red_evidence_cjs_1.classifyRedEvidence)(evidenceInput);
|
|
86
|
+
const built = (0, tdd_red_evidence_cjs_1.buildRedEvidenceRecord)(evidenceInput, result);
|
|
87
|
+
const ok = result.verdict === 'RED_EVIDENCE_OK';
|
|
88
|
+
// Uniform gate contract: block = !passed. INVALID_RED blocks GREEN.
|
|
89
|
+
return (0, gate_verdict_cjs_1.gateVerdict)(ok ? 'pass' : 'block', !ok, {
|
|
90
|
+
passed: ok,
|
|
91
|
+
block: !ok,
|
|
92
|
+
verdict: result.verdict,
|
|
93
|
+
reason: result.reason,
|
|
94
|
+
evidence: result.evidence,
|
|
95
|
+
record: built,
|
|
96
|
+
message: ok
|
|
97
|
+
? `RED evidence verified: target test "${result.evidence.target_test}" failed as expected (exit ${result.evidence.exit_code}). GREEN authorized.`
|
|
98
|
+
: `INVALID_RED (${result.reason}): GREEN blocked. Fix the RED phase — only an intentional failure of target test "${result.evidence.target_test}" authorizes production edits.`,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check tdd-review-checkpoint` as a gate module (#5139, epic #5056, ADR-5057 §4 first bullet): it
|
|
4
|
+
* returns a `GateResult`; the command router formats it. Imports no io module and performs no
|
|
5
|
+
* direct console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* End-of-phase advisory check: scans `type: tdd` plans for RED/GREEN/REFACTOR gate-sequence
|
|
8
|
+
* compliance (`test(<plan>):` / `feat(<plan>):` / `refactor(<plan>):` commits) and builds a review
|
|
9
|
+
* table. `passed` is always true (advisory — never truly blocks); `block` is `violations > 0` so
|
|
10
|
+
* the host loop can read one uniform field.
|
|
11
|
+
*
|
|
12
|
+
* `type: tdd` is detected by the Frontmatter Module's `frontmatterKeyHasValue` (the old
|
|
13
|
+
* `^type:\s*tdd\s*$` multiline test over the fence owner's block, key and value escaped).
|
|
14
|
+
*
|
|
15
|
+
* A plan's commits come from the evaluation-scope resolver (#5164, ADR-5057 §4): the commits
|
|
16
|
+
* reachable from HEAD whose SUBJECT is `<type>(<phase>-<plan>):`, anchored and zero-padding
|
|
17
|
+
* tolerant. A plan id that is not `<phase>-<plan>` (a plan named `.*-PLAN.md`) matches nothing,
|
|
18
|
+
* and a git failure is "no commits" exactly as before.
|
|
19
|
+
*
|
|
20
|
+
* Argv after the verb: `<phase>` (a number; an unresolvable phase reports zero plans).
|
|
21
|
+
*/
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.evaluateTddReviewCheckpoint = evaluateTddReviewCheckpoint;
|
|
27
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
28
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
29
|
+
const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
|
|
30
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
31
|
+
const gate_evaluation_scope_cjs_1 = require("./gate-evaluation-scope.cjs");
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
|
+
const frontmatterMod = require("./frontmatter.cjs");
|
|
34
|
+
const { frontmatterKeyHasValue } = frontmatterMod;
|
|
35
|
+
/**
|
|
36
|
+
* True when the plan's frontmatter declares `type: tdd` (CRLF included, #2449). The block is the
|
|
37
|
+
* one fence owner's, read as RAW text — not through the YAML parser — so a block the parser refuses
|
|
38
|
+
* (a `--- x` line before `type:`) still classifies, exactly as under the old `^type:\s*tdd\s*$`
|
|
39
|
+
* multiline regex, whose edge behaviour (a duplicate `type:` key, a value on the next line,
|
|
40
|
+
* `type : tdd` NOT matching, `type: "tdd"` / `type: tdd # note` NOT matching) is preserved.
|
|
41
|
+
*/
|
|
42
|
+
function isTddPlan(content) {
|
|
43
|
+
return frontmatterKeyHasValue(content, 'type', 'tdd');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The commit types (`test`, `feat`, `refactor`, …) among the plan's own commits — those that
|
|
47
|
+
* touched at least one path (`pathspecs: ['.']`, as the old `git log -- .` lookup did). An
|
|
48
|
+
* unresolvable scope (git unavailable, an id that is not `<phase>-<plan>`) is "no commits".
|
|
49
|
+
*/
|
|
50
|
+
function planCommitKinds(projectDir, planId) {
|
|
51
|
+
const scope = (0, gate_evaluation_scope_cjs_1.resolveEvaluationScope)(projectDir, { kind: 'plan', planId }, { pathspecs: ['.'], commitsOnly: true });
|
|
52
|
+
const kinds = new Set();
|
|
53
|
+
for (const commit of scope.commits) {
|
|
54
|
+
const kind = /^([a-z]+)\(/.exec(commit.subject)?.[1];
|
|
55
|
+
if (kind)
|
|
56
|
+
kinds.add(kind);
|
|
57
|
+
}
|
|
58
|
+
return kinds;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The verdict when the review could not read a plan or the phase directory. Advisory policy is
|
|
62
|
+
* unchanged (`block: false`); the outcome is `unreadable` (exit UNAVAILABLE) and `passed` is false
|
|
63
|
+
* because nothing was certified.
|
|
64
|
+
*/
|
|
65
|
+
function unreadableReview(phase, tddPlans, unreadable) {
|
|
66
|
+
const names = unreadable.map((u) => `${u.source} (${u.reason})`).join(', ');
|
|
67
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(false, {
|
|
68
|
+
block: false,
|
|
69
|
+
passed: false,
|
|
70
|
+
tddPlans,
|
|
71
|
+
violations: 0,
|
|
72
|
+
table: '',
|
|
73
|
+
rows: [],
|
|
74
|
+
unreadable,
|
|
75
|
+
message: `TDD review could not read: ${names}. Phase ${phase} was not reviewed.`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function evaluateTddReviewCheckpoint(input) {
|
|
79
|
+
const { projectDir } = input;
|
|
80
|
+
const phase = input.args[0] || '';
|
|
81
|
+
if (!phase) {
|
|
82
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.SDK_MISSING_ARG, 'tdd.review-checkpoint requires a phase argument: check tdd.review-checkpoint <phase>');
|
|
83
|
+
}
|
|
84
|
+
// #5170 (ADR-5057 §4): an ABSENT phase directory is `none` (no plans, the skip below); anything
|
|
85
|
+
// that exists but cannot be read is `unreadable` and the verdict says so — an unreadable plan is
|
|
86
|
+
// never "not a TDD plan".
|
|
87
|
+
const located = (0, gate_phase_context_cjs_1.resolvePhaseDir)(projectDir, phase);
|
|
88
|
+
if (located.kind === 'unreadable') {
|
|
89
|
+
return unreadableReview(phase, 0, [{ source: `phase ${phase}`, reason: located.reason }]);
|
|
90
|
+
}
|
|
91
|
+
const phaseDir = located.kind === 'found' ? located.value : '';
|
|
92
|
+
// Find all PLAN.md files with type: tdd in frontmatter
|
|
93
|
+
const tddPlanFiles = [];
|
|
94
|
+
const unreadable = [];
|
|
95
|
+
if (phaseDir) {
|
|
96
|
+
const entries = (0, gate_evidence_cjs_1.readDirEvidence)(phaseDir);
|
|
97
|
+
if (entries.kind === 'unreadable') {
|
|
98
|
+
unreadable.push({ source: phaseDir, reason: entries.reason });
|
|
99
|
+
}
|
|
100
|
+
else if (entries.kind === 'found') {
|
|
101
|
+
// #3183: canonical plan set (root+nested, superseded-excluded) from the single owner. A scan
|
|
102
|
+
// that did not see every plan (an unreadable nested plans/) is `unreadable`, never a short list.
|
|
103
|
+
const planSet = (0, gate_evidence_cjs_1.readPlanSetEvidence)(phaseDir);
|
|
104
|
+
if (planSet.kind === 'unreadable') {
|
|
105
|
+
unreadable.push({ source: planSet.span ?? phaseDir, reason: planSet.reason });
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
for (const file of planSet.value) {
|
|
109
|
+
const planPath = node_path_1.default.join(phaseDir, file);
|
|
110
|
+
const plan = (0, gate_evidence_cjs_1.readTextEvidence)(planPath);
|
|
111
|
+
if (plan.kind === 'unreadable')
|
|
112
|
+
unreadable.push({ source: planPath, reason: plan.reason });
|
|
113
|
+
else if (plan.kind === 'found' && isTddPlan(plan.value))
|
|
114
|
+
tddPlanFiles.push(planPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (unreadable.length > 0)
|
|
120
|
+
return unreadableReview(phase, tddPlanFiles.length, unreadable);
|
|
121
|
+
if (tddPlanFiles.length === 0) {
|
|
122
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, {
|
|
123
|
+
// Uniform gate contract: block = violations > 0 (advisory; never truly blocks).
|
|
124
|
+
block: false,
|
|
125
|
+
passed: true,
|
|
126
|
+
tddPlans: 0,
|
|
127
|
+
violations: 0,
|
|
128
|
+
table: '',
|
|
129
|
+
rows: [],
|
|
130
|
+
message: `No type:tdd plans found in phase ${phase}. TDD review skipped.`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// For each TDD plan, extract the plan ID (e.g. "01-02-PLAN.md" → "01-02") and check git log
|
|
134
|
+
const rows = [];
|
|
135
|
+
for (const planPath of tddPlanFiles) {
|
|
136
|
+
const planId = node_path_1.default.basename(planPath, '-PLAN.md');
|
|
137
|
+
const kinds = planCommitKinds(projectDir, planId);
|
|
138
|
+
const red = kinds.has('test');
|
|
139
|
+
const green = kinds.has('feat');
|
|
140
|
+
const refactor = kinds.has('refactor');
|
|
141
|
+
const missing = [];
|
|
142
|
+
if (!red)
|
|
143
|
+
missing.push('RED');
|
|
144
|
+
if (!green)
|
|
145
|
+
missing.push('GREEN');
|
|
146
|
+
const status = missing.length === 0 ? 'Pass' : 'FAIL';
|
|
147
|
+
rows.push({ planId, red, green, refactor, status, missing });
|
|
148
|
+
}
|
|
149
|
+
const violations = rows.filter(r => r.status === 'FAIL').length;
|
|
150
|
+
// Build review table
|
|
151
|
+
const tableHeader = '| Plan | RED | GREEN | REFACTOR | Status |';
|
|
152
|
+
const tableDivider = '|------|-----|-------|----------|--------|';
|
|
153
|
+
const tableRows = rows.map(r => `| ${r.planId.padEnd(4)} | ${r.red ? ' ✓ ' : ' ✗ '} | ${r.green ? ' ✓ ' : ' ✗ '} | ${r.refactor ? ' ✓ ' : ' — '} | ${r.status.padEnd(6)} |`);
|
|
154
|
+
let table = [
|
|
155
|
+
`### TDD REVIEW — Phase ${phase}`,
|
|
156
|
+
'',
|
|
157
|
+
`TDD Plans: ${tddPlanFiles.length} | Gate violations: ${violations}`,
|
|
158
|
+
'',
|
|
159
|
+
tableHeader,
|
|
160
|
+
tableDivider,
|
|
161
|
+
...tableRows,
|
|
162
|
+
].join('\n');
|
|
163
|
+
if (violations > 0) {
|
|
164
|
+
table += '\n\n⚠ Gate violations are advisory — review before advancing.';
|
|
165
|
+
for (const r of rows.filter(row => row.status === 'FAIL')) {
|
|
166
|
+
table += `\n Plan ${r.planId} missing: ${r.missing.join(', ')} gate commit(s).`;
|
|
167
|
+
table += `\n Expected commit pattern: test(${r.planId}): ... → feat(${r.planId}): ...`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Uniform gate contract: block = violations > 0. The gate is advisory (blocking:false in
|
|
171
|
+
// capability.json), so block:true only surfaces as a warning, never halts. The human-readable
|
|
172
|
+
// report is carried in `message` (and `table`) for the dispatch's advisory branch.
|
|
173
|
+
return (0, gate_verdict_cjs_1.gateVerdict)(violations > 0 ? 'advisory' : 'pass', violations > 0, {
|
|
174
|
+
block: violations > 0,
|
|
175
|
+
passed: true,
|
|
176
|
+
tddPlans: tddPlanFiles.length,
|
|
177
|
+
violations,
|
|
178
|
+
table,
|
|
179
|
+
rows,
|
|
180
|
+
message: table,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check ui-plan-gate` as a gate module (#5139, epic #5056, ADR-5057 §4 first bullet): it returns a
|
|
4
|
+
* `GateResult`; the command router formats it. Imports no io module and performs no direct
|
|
5
|
+
* console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* Given a phase number, checks whether the phase has frontend indicators and whether a
|
|
8
|
+
* `*-UI-SPEC.md` already exists in the phase directory. Uses `checkUiPresence` from
|
|
9
|
+
* `ui-safety-gate.cjs` (frontend detection is not reimplemented) and the shared phase-context
|
|
10
|
+
* helpers for the ROADMAP lookup and the phase directory.
|
|
11
|
+
*
|
|
12
|
+
* Argv after the verb: `<phase>`.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.computeUiPlanGate = computeUiPlanGate;
|
|
16
|
+
exports.evaluateUiPlanGate = evaluateUiPlanGate;
|
|
17
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
18
|
+
const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
|
|
19
|
+
const ui_safety_gate_cjs_1 = require("./ui-safety-gate.cjs");
|
|
20
|
+
const ui_frontend_evidence_cjs_1 = require("./ui-frontend-evidence.cjs");
|
|
21
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
22
|
+
/**
|
|
23
|
+
* Pure logic for ui-plan-gate — exposed for direct behavioral testing.
|
|
24
|
+
*
|
|
25
|
+
* Given a projectDir and phase number:
|
|
26
|
+
* (a) Reads the phase section from ROADMAP.md via the two-pass lookup `roadmap.get-phase` uses
|
|
27
|
+
* (current milestone → full roadmap). ROADMAP.md missing = project has no roadmap = cannot
|
|
28
|
+
* be frontend. Phase absent from a present ROADMAP.md sets `phaseLookupFailed` so callers
|
|
29
|
+
* can surface the miss — we do NOT silently degrade to frontend:false.
|
|
30
|
+
* (b) Runs checkUiPresence (frontend detection).
|
|
31
|
+
* (c) Resolves the phase directory; checks for *-UI-SPEC.md.
|
|
32
|
+
*
|
|
33
|
+
* `block = frontend && hasFrontendEvidence && !hasUiSpec` (#3312): `frontend` is a vocabulary
|
|
34
|
+
* signal only (a hyphen is a word boundary, so a repo named `dashboard-financeiro` matches the
|
|
35
|
+
* token `dashboard`; the boundary rule of #3718 is intentional), so the gate blocks only when the
|
|
36
|
+
* token match is corroborated by static frontend evidence in the repo tree
|
|
37
|
+
* (`hasStaticFrontendEvidence`). matchedToken/matchedLine surface what tripped the sniffer.
|
|
38
|
+
*/
|
|
39
|
+
function computeUiPlanGate(projectDir, phase) {
|
|
40
|
+
// (a) phase section text
|
|
41
|
+
const { phaseSection, phaseLookupFailed, readError: roadmapReadError } = (0, gate_phase_context_cjs_1.lookupRoadmapPhase)(projectDir, phase);
|
|
42
|
+
// (b) frontend detection — reuse the existing helper; no reimplementation
|
|
43
|
+
const presenceResult = (0, ui_safety_gate_cjs_1.checkUiPresence)(phaseSection);
|
|
44
|
+
const frontend = presenceResult.hasUI;
|
|
45
|
+
// (b') #3312 — static structural corroboration. Only probed when the sniffer matched. `unreadable`
|
|
46
|
+
// (#5170) is a manifest or tree the probe could not look at: absence of evidence is then not
|
|
47
|
+
// established, and the verdict says so (below) instead of reading it as "no frontend".
|
|
48
|
+
const frontendEvidence = frontend ? (0, ui_frontend_evidence_cjs_1.readStaticFrontendEvidence)(projectDir) : (0, gate_evidence_cjs_1.evidenceNone)();
|
|
49
|
+
const hasFrontendEvidence = frontendEvidence.kind === 'found';
|
|
50
|
+
// (c) phase directory and *-UI-SPEC.md. `none` is "no spec"; `unreadable` is "could not look"
|
|
51
|
+
// (#5170) and is carried to the verdict, never read as "no spec".
|
|
52
|
+
const uiSpec = (0, gate_phase_context_cjs_1.locateUiSpec)(projectDir, phase);
|
|
53
|
+
const uiSpecPath = uiSpec.kind === 'found' ? uiSpec.value : '';
|
|
54
|
+
const hasUiSpec = uiSpecPath !== '';
|
|
55
|
+
// Unreadable frontend evidence only matters when it could flip the verdict: with a UI-SPEC present the
|
|
56
|
+
// gate does not block whatever the tree holds.
|
|
57
|
+
const frontendReadError = frontendEvidence.kind === 'unreadable' && !hasUiSpec
|
|
58
|
+
? `static frontend evidence could not be read (${frontendEvidence.reason})`
|
|
59
|
+
: undefined;
|
|
60
|
+
const readError = roadmapReadError ?? (uiSpec.kind === 'unreadable' ? uiSpec.reason : undefined) ?? frontendReadError;
|
|
61
|
+
// block = frontend phase with structural frontend evidence and no UI-SPEC (#3312)
|
|
62
|
+
const block = frontend && hasFrontendEvidence && !hasUiSpec;
|
|
63
|
+
const result = {
|
|
64
|
+
frontend, hasFrontendEvidence, hasUiSpec, block,
|
|
65
|
+
uiSpecPath: hasUiSpec ? uiSpecPath : null,
|
|
66
|
+
matchedToken: presenceResult.matchedToken,
|
|
67
|
+
matchedLine: presenceResult.matchedLine,
|
|
68
|
+
};
|
|
69
|
+
if (phaseLookupFailed)
|
|
70
|
+
result.phaseLookupFailed = true;
|
|
71
|
+
if (readError !== undefined)
|
|
72
|
+
result.readError = readError;
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function evaluateUiPlanGate(input) {
|
|
76
|
+
const phase = input.args[0] || '';
|
|
77
|
+
if (!phase) {
|
|
78
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.SDK_MISSING_ARG, 'ui-plan-gate requires a phase argument: check ui-plan-gate <phase>');
|
|
79
|
+
}
|
|
80
|
+
const result = computeUiPlanGate(input.projectDir, phase);
|
|
81
|
+
// Evidence the gate could not read is "could not look", never a pass (ADR-5057 §4). `block` is
|
|
82
|
+
// the gate's own policy and is unchanged; the exit status follows the outcome.
|
|
83
|
+
if (result.readError !== undefined)
|
|
84
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(result.block, { ...result });
|
|
85
|
+
return (0, gate_verdict_cjs_1.gateVerdict)(result.block ? 'block' : 'pass', result.block, { ...result });
|
|
86
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check ui-safety-gate` as a gate module (#5139, epic #5056, ADR-5057 §4 first bullet): it returns
|
|
4
|
+
* a `GateResult`; the command router formats it. Imports no io module and performs no direct
|
|
5
|
+
* console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* Post-wave check that verifies UI-changed files conform to the active UI-SPEC for the phase.
|
|
8
|
+
* Uses `checkUiPresence` from `ui-safety-gate.cjs` (frontend detection is not reimplemented) and
|
|
9
|
+
* looks for frontend files in the phase's evaluation scope (#5164, ADR-5057 §4): the union of the
|
|
10
|
+
* phase's own commits' file sets from `gate-evaluation-scope`, not the last commit. A scope the
|
|
11
|
+
* resolver could not read, or had to widen, is reported (`scopeStatus` / `scopeReason`), never
|
|
12
|
+
* silently treated as "no UI files".
|
|
13
|
+
*
|
|
14
|
+
* Argv after the verb: `<phase>`.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.computeUiSafetyGate = computeUiSafetyGate;
|
|
18
|
+
exports.evaluateUiSafetyGate = evaluateUiSafetyGate;
|
|
19
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
20
|
+
const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
|
|
21
|
+
const gate_evaluation_scope_cjs_1 = require("./gate-evaluation-scope.cjs");
|
|
22
|
+
const ui_safety_gate_cjs_1 = require("./ui-safety-gate.cjs");
|
|
23
|
+
const UI_FILE_EXTENSIONS_RE = /\.(tsx|jsx|css|scss|sass|less|vue|svelte|html)$/i;
|
|
24
|
+
const UI_PATH_PATTERNS_RE = /\/(components|pages|views|screens|layouts|ui|frontend)\//i;
|
|
25
|
+
/**
|
|
26
|
+
* Pure logic for ui-safety-gate — exposed for direct behavioral testing.
|
|
27
|
+
*
|
|
28
|
+
* (a) ROADMAP phase section via the shared lookup (same as ui-plan-gate) → is this a frontend phase.
|
|
29
|
+
* (b) checkUiPresence (frontend detection).
|
|
30
|
+
* (c) UI files among the phase's evaluation scope (`resolveEvaluationScope`, phase unit; every
|
|
31
|
+
* git call is bounded by the resolver's seam; an unreadable scope is reported, not "clean").
|
|
32
|
+
* (d) Phase directory → `*-UI-SPEC.md`.
|
|
33
|
+
*
|
|
34
|
+
* `block = frontend && hasUiFiles && !hasUiSpec`.
|
|
35
|
+
*/
|
|
36
|
+
function computeUiSafetyGate(projectDir, phase) {
|
|
37
|
+
// (a) phase section text (same two-pass lookup as computeUiPlanGate)
|
|
38
|
+
const { phaseSection, phaseLookupFailed, readError: roadmapReadError } = (0, gate_phase_context_cjs_1.lookupRoadmapPhase)(projectDir, phase);
|
|
39
|
+
// (b) frontend detection — reuse the existing helper; no reimplementation
|
|
40
|
+
const presenceResult = (0, ui_safety_gate_cjs_1.checkUiPresence)(phaseSection);
|
|
41
|
+
const frontend = presenceResult.hasUI;
|
|
42
|
+
// (c) any UI files in the phase's own commits? (deleted paths count: a removed component is a UI change)
|
|
43
|
+
const scope = (0, gate_evaluation_scope_cjs_1.resolveEvaluationScope)(projectDir, { kind: 'phase', phase });
|
|
44
|
+
const hasUiFiles = scope.changedFiles.some((f) => f.trim() && (UI_FILE_EXTENSIONS_RE.test(f) || UI_PATH_PATTERNS_RE.test(f)));
|
|
45
|
+
// (d) phase directory and *-UI-SPEC.md
|
|
46
|
+
// `none` is "no spec"; `unreadable` is "could not look" (#5170) and is carried to the verdict.
|
|
47
|
+
const uiSpec = (0, gate_phase_context_cjs_1.locateUiSpec)(projectDir, phase);
|
|
48
|
+
const hasUiSpec = uiSpec.kind === 'found';
|
|
49
|
+
const readError = roadmapReadError ?? (uiSpec.kind === 'unreadable' ? uiSpec.reason : undefined);
|
|
50
|
+
// block only when: this is a frontend phase AND UI files were changed AND no UI-SPEC exists
|
|
51
|
+
const block = frontend && hasUiFiles && !hasUiSpec;
|
|
52
|
+
const result = { frontend, hasUiFiles, hasUiSpec, block };
|
|
53
|
+
if (block) {
|
|
54
|
+
result.message = `UI files changed in this wave but no UI-SPEC.md exists for Phase ${phase}. ` +
|
|
55
|
+
`Run /gsd:ui-phase ${phase} to generate the design contract before continuing.`;
|
|
56
|
+
}
|
|
57
|
+
if (phaseLookupFailed)
|
|
58
|
+
result.phaseLookupFailed = true;
|
|
59
|
+
if (scope.status !== 'resolved') {
|
|
60
|
+
result.scopeStatus = scope.status;
|
|
61
|
+
result.scopeReason = scope.reason ?? '';
|
|
62
|
+
}
|
|
63
|
+
if (readError !== undefined)
|
|
64
|
+
result.readError = readError;
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
function evaluateUiSafetyGate(input) {
|
|
68
|
+
const phase = input.args[0] || '';
|
|
69
|
+
if (!phase) {
|
|
70
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.SDK_MISSING_ARG, 'ui-safety-gate requires a phase argument: check ui-safety-gate <phase>');
|
|
71
|
+
}
|
|
72
|
+
const result = computeUiSafetyGate(input.projectDir, phase);
|
|
73
|
+
// A scope the resolver could not read, or a ROADMAP / phase directory that could not be read, is
|
|
74
|
+
// "could not look", never a pass (ADR-5057 §4): the outcome is `unreadable` and the exit status
|
|
75
|
+
// follows it. `block` is the gate's own policy and is unchanged.
|
|
76
|
+
if (result.scopeStatus === 'unresolvable' || result.readError !== undefined) {
|
|
77
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(result.block, { ...result });
|
|
78
|
+
}
|
|
79
|
+
return (0, gate_verdict_cjs_1.gateVerdict)(result.block ? 'block' : 'pass', result.block, { ...result });
|
|
80
|
+
}
|