@opengsd/gsd-core 1.11.0 → 1.12.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/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -9
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +15 -55
- package/agents/gsd-planner.md +6 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +609 -134
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/quick.md +8 -4
- package/gsd-core/bin/gsd-tools.cjs +567 -51
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +336 -95
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-validator.cjs +205 -18
- package/gsd-core/bin/lib/check-command-router.cjs +145 -5
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +543 -44
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +118 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +84 -7
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/frontmatter.cjs +840 -305
- package/gsd-core/bin/lib/gap-checker.cjs +27 -3
- package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +120 -41
- package/gsd-core/bin/lib/install-engine.cjs +68 -3
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
- package/gsd-core/bin/lib/install-profiles.cjs +78 -4
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +160 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-table.cjs +52 -4
- package/gsd-core/bin/lib/milestone.cjs +90 -5
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +70 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +405 -84
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
- package/gsd-core/bin/lib/roadmap.cjs +74 -19
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
- package/gsd-core/bin/lib/smart-entry.cjs +12 -22
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +186 -0
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +517 -101
- package/gsd-core/bin/lib/state.cjs +946 -163
- package/gsd-core/bin/lib/surface.cjs +10 -2
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +1376 -125
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification.cjs +14 -1
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +95 -40
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +3 -2
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +1 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +37 -8
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +2 -2
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +3 -0
- package/gsd-core/templates/state.md +7 -0
- 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 +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +4 -9
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +10 -26
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +121 -40
- package/gsd-core/workflows/complete-milestone.md +15 -10
- package/gsd-core/workflows/debug.md +5 -3
- package/gsd-core/workflows/diagnose-issues.md +12 -6
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +4 -9
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- 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 +22 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +38 -54
- package/gsd-core/workflows/execute-plan.md +17 -12
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +2 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +3 -6
- package/gsd-core/workflows/import.md +14 -11
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +44 -11
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +2 -2
- package/gsd-core/workflows/new-milestone.md +9 -21
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +12 -26
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +121 -42
- package/gsd-core/workflows/plan-review-convergence.md +46 -9
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +27 -12
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +20 -29
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +193 -15
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +2 -2
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +3 -5
- package/gsd-core/workflows/ship.md +12 -6
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +23 -1
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +5 -5
- package/gsd-core/workflows/ui-phase.md +10 -16
- package/gsd-core/workflows/ui-review.md +6 -10
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +6 -10
- package/gsd-core/workflows/validate-phase.md +2 -2
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +57 -18
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +16 -7
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +17 -8
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +215 -26
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +34 -16
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +16 -7
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +17 -8
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +215 -26
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +34 -16
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +12 -7
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +5 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +45 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +134 -1
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +21 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +122 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/prompt-injection-scan.sh +75 -14
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pinned baseline for scripts/lint-docs-guard-registration.cjs's
|
|
5
|
+
* `docs-guard-exempt` identity ratchet — mirrors
|
|
6
|
+
* scripts/lint-allow-test-rule-refs.cjs's `allow-test-rule` ratchet
|
|
7
|
+
* (scripts/lib/allowlist-ratchet.cjs, ADR-456's pattern).
|
|
8
|
+
*
|
|
9
|
+
* Every test file basename here is a KNOWN, already-reviewed
|
|
10
|
+
* `// docs-guard-exempt: <reason>` marker. Nothing caps how many files may
|
|
11
|
+
* carry the marker in the abstract, but ADDING a new one anywhere in
|
|
12
|
+
* tests/*.test.cjs fails `lint-docs-guard-registration` until this baseline
|
|
13
|
+
* is deliberately updated — so a new exemption always shows up as a
|
|
14
|
+
* reviewable diff here, instead of silently opting a file out of the
|
|
15
|
+
* docs-guard registration requirement with zero signal.
|
|
16
|
+
*
|
|
17
|
+
* A file listed here that no longer exists, or no longer carries a real
|
|
18
|
+
* `docs-guard-exempt:` marker in its first 20 lines, is reported STALE and
|
|
19
|
+
* must be pruned (ratchet-down, same as the sibling gate).
|
|
20
|
+
*
|
|
21
|
+
* Seeded 2026-08-23 (#3753 security follow-up FIX 2) from every real marker
|
|
22
|
+
* found via scripts/lint-docs-guard-registration.cjs's own `findExemption`
|
|
23
|
+
* scan of tests/*.test.cjs — derived, not retyped from memory.
|
|
24
|
+
*
|
|
25
|
+
* Extended 2026-08-23 (#3753 correctness follow-up): the reader-detection
|
|
26
|
+
* fixes (Defects A/B/C) surfaced 86 previously-undetected docs/ readers. 40
|
|
27
|
+
* were genuine docs-content guards (added to DOCS_GUARD_TESTS in
|
|
28
|
+
* scripts/docs-guard-registry.cjs); the 46 added here touch a docs/ path
|
|
29
|
+
* only incidentally (fixture data, external URL citations, comment-only
|
|
30
|
+
* mentions, metadata labels, or scan-exclusion lists) — see each file's own
|
|
31
|
+
* `docs-guard-exempt:` marker for its specific reason.
|
|
32
|
+
*/
|
|
33
|
+
const DOCS_GUARD_EXEMPT_BASELINE = [
|
|
34
|
+
'adr-parser.test.cjs',
|
|
35
|
+
'adr-parser.unit.test.cjs',
|
|
36
|
+
'agent-marker-documentation-guard.test.cjs',
|
|
37
|
+
'antigravity-upgrades.test.cjs',
|
|
38
|
+
'capability-cli.test.cjs',
|
|
39
|
+
'capability-validator-task-content-resolver.test.cjs',
|
|
40
|
+
'ci-docs-guard-registry.test.cjs',
|
|
41
|
+
'ci-test-scope.test.cjs',
|
|
42
|
+
'cline-install.test.cjs',
|
|
43
|
+
'code-review-depth.test.cjs',
|
|
44
|
+
'code-review-pipeline-regression.test.cjs',
|
|
45
|
+
'codebuddy-upgrades.test.cjs',
|
|
46
|
+
'commands.test.cjs',
|
|
47
|
+
'commit-docs-bypass.test.cjs',
|
|
48
|
+
'complexity-trigger.test.cjs',
|
|
49
|
+
'concurrency-safety.test.cjs',
|
|
50
|
+
'cursor-imperative-reference.test.cjs',
|
|
51
|
+
'declarative-reference-antigravity.test.cjs',
|
|
52
|
+
'declarative-reference-zcode.test.cjs',
|
|
53
|
+
'emitted-attribution.test.cjs',
|
|
54
|
+
'eslint-rules.test.cjs',
|
|
55
|
+
'estimate-calibrate.test.cjs',
|
|
56
|
+
'gen-context-index.test.cjs',
|
|
57
|
+
'gen-registry.test.cjs',
|
|
58
|
+
'gsd-agent-isolation-guard.test.cjs',
|
|
59
|
+
'hermes-dispatch-upgrade.test.cjs',
|
|
60
|
+
'install-minimal-hooks.test.cjs',
|
|
61
|
+
'install-runtime-artifacts.test.cjs',
|
|
62
|
+
'installer-migration-config-root-marker.test.cjs',
|
|
63
|
+
'installer-migration-pi-extension-ext.test.cjs',
|
|
64
|
+
'installer-migrations.test.cjs',
|
|
65
|
+
'kimi-upgrades.test.cjs',
|
|
66
|
+
'lint-allow-test-rule-refs.test.cjs',
|
|
67
|
+
'lint-docs-command-form.test.cjs',
|
|
68
|
+
'lint-docs-required.test.cjs',
|
|
69
|
+
'manifest-version-sync.test.cjs',
|
|
70
|
+
'milestone-archive.test.cjs',
|
|
71
|
+
'model-resolver.test.cjs',
|
|
72
|
+
'new-project-mvp-prompt.test.cjs',
|
|
73
|
+
'onboard-command.test.cjs',
|
|
74
|
+
'opencode-command-dir-plural.test.cjs',
|
|
75
|
+
'phase.test.cjs',
|
|
76
|
+
'pr-branch-planning-filter.test.cjs',
|
|
77
|
+
'precommit-alias-drift-hook.test.cjs',
|
|
78
|
+
'removed-but-needed-lint.test.cjs',
|
|
79
|
+
'repo-invariants.test.cjs',
|
|
80
|
+
'require-issue-link-policy.test.cjs',
|
|
81
|
+
'reviewer-manifest-body.test.cjs',
|
|
82
|
+
'run-tests-harness.test.cjs',
|
|
83
|
+
'runtime-name-policy.test.cjs',
|
|
84
|
+
'security-prompt-injection.security.test.cjs',
|
|
85
|
+
'shipped-reference-cites.test.cjs',
|
|
86
|
+
'state.test.cjs',
|
|
87
|
+
'worktree-safety.test.cjs',
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Security follow-up FIX 3: a per-file fingerprint of every distinct
|
|
92
|
+
* `docs/...` path TOKEN referenced by each baselined file (derived via
|
|
93
|
+
* scripts/lint-docs-guard-registration.cjs's `extractDocsPathReferences`,
|
|
94
|
+
* never retyped from memory). A baselined file whose live fingerprint
|
|
95
|
+
* DIFFERS from the recorded one here fails the lint — the exemption's
|
|
96
|
+
* premise ("this file doesn't really guard shipped docs content") may no
|
|
97
|
+
* longer hold and a human must re-confirm it before the entry is updated.
|
|
98
|
+
* Keeping this a plain, sorted, diffable path list (not a hash) is
|
|
99
|
+
* deliberate: a reviewer can see exactly WHAT changed from the PR diff
|
|
100
|
+
* alone.
|
|
101
|
+
*
|
|
102
|
+
* Seeded 2026-08-23 alongside DOCS_GUARD_EXEMPT_BASELINE above, from the
|
|
103
|
+
* exact same scan.
|
|
104
|
+
*/
|
|
105
|
+
const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
|
|
106
|
+
'adr-parser.test.cjs': ['docs/adr/0001.md', 'docs/adr/0002.md', 'docs/adr/0010.md', 'docs/adr/NNNN.md'],
|
|
107
|
+
'adr-parser.unit.test.cjs': ['docs/adr/0001.md', 'docs/adr/0099.md', 'docs/adr/NNNN.md'],
|
|
108
|
+
'agent-marker-documentation-guard.test.cjs': ['docs/reference', 'docs/reference/workflow-fragments.md'],
|
|
109
|
+
'antigravity-upgrades.test.cjs': ['docs/cli', 'docs/cli/gcli-migration', 'docs/cli/permissions'],
|
|
110
|
+
'capability-cli.test.cjs': ['docs/reference/gsd-capability-command.md'],
|
|
111
|
+
// #3970: cites docs/adr/3646-per-task-content-resolution-seam.md in an
|
|
112
|
+
// explanatory comment describing ADR-3646's Decision 3; the file never
|
|
113
|
+
// reads that (or any) docs/ file.
|
|
114
|
+
'capability-validator-task-content-resolver.test.cjs': ['docs/adr/3646-per-task-content-resolution-seam.md'],
|
|
115
|
+
'ci-docs-guard-registry.test.cjs': [
|
|
116
|
+
'docs/AGENTS.md', 'docs/COMMANDS.md', 'docs/INVENTORY.md', 'docs/a.md', 'docs/adr',
|
|
117
|
+
'docs/adr/0001-example.md', 'docs/adrenaline.md', 'docs/bar.md', 'docs/foo.md', 'docs/how-to/foo.md',
|
|
118
|
+
'docs/how-to/some-unrelated-guide.md', 'docs/how-to/x.md', 'docs/some-unrelated-file.md',
|
|
119
|
+
'docs/totally-unrelated.md',
|
|
120
|
+
],
|
|
121
|
+
'ci-test-scope.test.cjs': [
|
|
122
|
+
'docs/a.md', 'docs/adr', 'docs/adr/22-plan-drift-guard.md', 'docs/how-to/configure-model-profiles.md',
|
|
123
|
+
'docs/installer-migrations.md', 'docs/ja-JP', 'docs/ja-JP/USAGE.md', 'docs/usage.md', 'docs/x.md',
|
|
124
|
+
],
|
|
125
|
+
'cline-install.test.cjs': ['docs/guide.md'],
|
|
126
|
+
'code-review-depth.test.cjs': ['docs/src/auth/x.ts'],
|
|
127
|
+
'code-review-pipeline-regression.test.cjs': ['docs/DEVELOPMENT.md'],
|
|
128
|
+
'codebuddy-upgrades.test.cjs': ['docs/cli/sub-agents'],
|
|
129
|
+
'commands.test.cjs': ['docs/x.md'],
|
|
130
|
+
'commit-docs-bypass.test.cjs': [
|
|
131
|
+
'docs/40-design.md', 'docs/CONFIGURATION.md', 'docs/readme.md', 'docs/tracked-var-mentioning',
|
|
132
|
+
],
|
|
133
|
+
'complexity-trigger.test.cjs': ['docs/readme.md'],
|
|
134
|
+
// #3884: cites docs/CLI-TOOLS.md:736 in an explanatory comment describing
|
|
135
|
+
// the real `frontmatter get <file> [--field key]` CLI shape; the file
|
|
136
|
+
// never reads that (or any) docs/ file.
|
|
137
|
+
'concurrency-safety.test.cjs': ['docs/CLI-TOOLS.md'],
|
|
138
|
+
'cursor-imperative-reference.test.cjs': ['docs/sdk/typescript'],
|
|
139
|
+
'declarative-reference-antigravity.test.cjs': ['docs/cli/features'],
|
|
140
|
+
'declarative-reference-zcode.test.cjs': ['docs/reference/host-integration-capability-matrix.md'],
|
|
141
|
+
'emitted-attribution.test.cjs': ['docs/README.md', 'docs/tests', 'docs/tests/helpers/install-shared.cjs'],
|
|
142
|
+
// #3914: cites docs/adr/3889-process-exit-contract.md ~:350-362 in an
|
|
143
|
+
// explanatory comment describing the intentional computed-property
|
|
144
|
+
// boundary move; the file never reads that (or any) docs/ file.
|
|
145
|
+
'eslint-rules.test.cjs': ['docs/adr/3889-process-exit-contract.md', 'docs/readme.md'],
|
|
146
|
+
'estimate-calibrate.test.cjs': [
|
|
147
|
+
'docs/adr', 'docs/adr/2629-phase-effort-estimation-calibration.md', 'docs/reference',
|
|
148
|
+
'docs/reference/planning-artifacts.md',
|
|
149
|
+
],
|
|
150
|
+
'gen-context-index.test.cjs': ['docs/CONTEXT-INDEX.json', 'docs/INVENTORY-MANIFEST.json'],
|
|
151
|
+
'gen-registry.test.cjs': ['docs/registries', 'docs/registries/reviewers.json'],
|
|
152
|
+
'gsd-agent-isolation-guard.test.cjs': ['docs/adr/1239-...md', 'docs/adr/1239-gsd-embeddable-orchestration-engine.md'],
|
|
153
|
+
'hermes-dispatch-upgrade.test.cjs': ['docs/guides/delegation-patterns.md'],
|
|
154
|
+
'install-minimal-hooks.test.cjs': ['docs/en/hooks', 'docs/en/users/features/hooks'],
|
|
155
|
+
'install-runtime-artifacts.test.cjs': ['docs/CONFIGURATION.md', 'docs/adr/58-...md', 'docs/adr/58-runtime-install-policy-module.md', 'docs/cli/slash-commands'],
|
|
156
|
+
'installer-migration-config-root-marker.test.cjs': ['docs/installer-migrations.md'],
|
|
157
|
+
'installer-migration-pi-extension-ext.test.cjs': ['docs/installer-migrations.md'],
|
|
158
|
+
'installer-migrations.test.cjs': ['docs/installer-migrations.md'],
|
|
159
|
+
'kimi-upgrades.test.cjs': ['docs/reference/host-integration-capability-matrix.md'],
|
|
160
|
+
'lint-allow-test-rule-refs.test.cjs': ['docs/readme.md'],
|
|
161
|
+
'lint-docs-command-form.test.cjs': ['docs/adr', 'docs/adr/999-example.md', 'docs/how-to/example.md'],
|
|
162
|
+
'lint-docs-required.test.cjs': [
|
|
163
|
+
'docs/COMMANDS.md', 'docs/USER-GUIDE.md', 'docs/adr', 'docs/adr/0001-foo.md', 'docs/adr/0099-new.md',
|
|
164
|
+
'docs/agents', 'docs/agents/triage-labels.md',
|
|
165
|
+
],
|
|
166
|
+
'manifest-version-sync.test.cjs': [],
|
|
167
|
+
// #3884: re-confirmed — the added docs/CLI-TOOLS.md:458 reference is the
|
|
168
|
+
// same class as the existing docs/TESTING-SUITES.md one (a placement-note
|
|
169
|
+
// / explanatory comment citing documented CLI behavior for context, never
|
|
170
|
+
// a read target); the exemption's premise still holds for both.
|
|
171
|
+
'milestone-archive.test.cjs': ['docs/CLI-TOOLS.md', 'docs/TESTING-SUITES.md'],
|
|
172
|
+
'model-resolver.test.cjs': ['docs/TESTING-SUITES.md'],
|
|
173
|
+
'new-project-mvp-prompt.test.cjs': ['docs/CONFIGURATION.md'],
|
|
174
|
+
'onboard-command.test.cjs': ['docs/adr/0001-runtime.md'],
|
|
175
|
+
'opencode-command-dir-plural.test.cjs': ['docs/commands'],
|
|
176
|
+
'phase.test.cjs': ['docs/adr/3524-...md', 'docs/adr/3524-cjs-sdk-hard-seam.md'],
|
|
177
|
+
'pr-branch-planning-filter.test.cjs': ['docs/readme.md'],
|
|
178
|
+
'precommit-alias-drift-hook.test.cjs': ['docs/adr/0174-...md', 'docs/adr/0174-retire-gsd-sdk-package-boundary.md'],
|
|
179
|
+
'removed-but-needed-lint.test.cjs': [
|
|
180
|
+
'docs/README.md', 'docs/getting-started.md', 'docs/gsd-new-workspace.md', 'docs/setup.md', 'docs/some-doc.md',
|
|
181
|
+
],
|
|
182
|
+
'repo-invariants.test.cjs': ['docs/FEATURES.md', 'docs/workflows/README'],
|
|
183
|
+
'require-issue-link-policy.test.cjs': ['docs/-prefixed', 'docs/CONFIGURATION.md', 'docs/a.md', 'docs/b.md', 'docs/guide.md'],
|
|
184
|
+
'reviewer-manifest-body.test.cjs': ['docs/how-to/ship-a-reviewer-lane.md'],
|
|
185
|
+
'run-tests-harness.test.cjs': ['docs/TESTING-SUITES.md'],
|
|
186
|
+
'runtime-name-policy.test.cjs': ['docs/customize/skills'],
|
|
187
|
+
'security-prompt-injection.security.test.cjs': ['docs/notes.md'],
|
|
188
|
+
'shipped-reference-cites.test.cjs': [],
|
|
189
|
+
'state.test.cjs': ['docs/CONFIGURATION.md', 'docs/reference/state-md.md'],
|
|
190
|
+
'worktree-safety.test.cjs': ['docs/SUMMARY.md'],
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
module.exports = { DOCS_GUARD_EXEMPT_BASELINE, DOCS_GUARD_EXEMPT_DOCS_PATHS };
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
"path": "src/vendor/re2js.d.cts",
|
|
8
8
|
"reason": "Vendored third-party type declaration (#3477): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored .cjs it describes is globally ignored as verbatim upstream output. Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules."
|
|
9
9
|
},
|
|
10
|
+
{
|
|
11
|
+
"path": "src/vendor/js-yaml.d.cts",
|
|
12
|
+
"reason": "Vendored third-party type declaration (ADR-3473 §8.1, #3881): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored js-yaml .cjs it describes is globally ignored as verbatim upstream output (gsd-core/bin/lib/vendor/**). Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules — same precedent as src/vendor/re2js.d.cts above."
|
|
13
|
+
},
|
|
10
14
|
{
|
|
11
15
|
"path": "tests/fixtures/brand-typing/bad-calibrated-as-sample-basis.cts",
|
|
12
16
|
"reason": "Deliberate MUST-NOT-COMPILE type-error fixture (#3059): type-aware linting would fail by design; it exists to prove the compiler rejects it."
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* lint-fix-has-regression-
|
|
5
|
+
* lint-fix-has-regression-tests.cjs — gate: every fix(#NNNN) commit must
|
|
6
6
|
* include at least one behavioral test file (tests/*.test.cjs) that is NOT
|
|
7
7
|
* an auto-generated fixture/baseline.
|
|
8
8
|
*
|
|
9
|
+
* Renamed (was `lint-fix-has-regression-test.cjs`, singular): the old
|
|
10
|
+
* basename matched Node's `*-test.EXT` test-collection pattern, so the
|
|
11
|
+
* remote push-gate runner collected and executed this SOURCE file as a
|
|
12
|
+
* test while GitHub CI (which globs only tests/**\/*.test.cjs) never saw
|
|
13
|
+
* it — see scripts/lint-source-test-name-collision.cjs.
|
|
14
|
+
*
|
|
9
15
|
* ## Why
|
|
10
16
|
*
|
|
11
17
|
* CONTRIBUTING.md:47: "Fix it. Write a test that would have caught the bug."
|
|
@@ -82,7 +88,7 @@ function getChangedTestFiles(baseRef) {
|
|
|
82
88
|
|
|
83
89
|
function main() {
|
|
84
90
|
if (process.env.GSD_SKIP_REGRESSION_TEST_GATE === '1') {
|
|
85
|
-
console.log('lint-fix-has-regression-
|
|
91
|
+
console.log('lint-fix-has-regression-tests: SKIPPED (GSD_SKIP_REGRESSION_TEST_GATE=1)');
|
|
86
92
|
return;
|
|
87
93
|
}
|
|
88
94
|
|
|
@@ -92,12 +98,12 @@ function main() {
|
|
|
92
98
|
try {
|
|
93
99
|
fixCommits = getFixCommits(baseRef);
|
|
94
100
|
} catch {
|
|
95
|
-
console.log(`lint-fix-has-regression-
|
|
101
|
+
console.log(`lint-fix-has-regression-tests: no fix/feat commits found vs ${baseRef}, skipping`);
|
|
96
102
|
return;
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
if (fixCommits.length === 0) {
|
|
100
|
-
console.log('lint-fix-has-regression-
|
|
106
|
+
console.log('lint-fix-has-regression-tests: no fix/feat commits, passing');
|
|
101
107
|
return;
|
|
102
108
|
}
|
|
103
109
|
|
|
@@ -113,7 +119,7 @@ function main() {
|
|
|
113
119
|
.map((c) => ` ${c.sha} ${c.subject}`)
|
|
114
120
|
.join('\n');
|
|
115
121
|
throw new ExitError(1,
|
|
116
|
-
`lint-fix-has-regression-
|
|
122
|
+
`lint-fix-has-regression-tests: ${fixCommits.length} fix/feat commit(s) but ZERO behavioral test files (*.test.cjs) in the diff.\n` +
|
|
117
123
|
`Auto-generated fixtures (tests/fixtures/, *-baseline.json) do NOT count.\n\n` +
|
|
118
124
|
`Fix commits:\n${commitList}\n\n` +
|
|
119
125
|
`CONTRIBUTING.md:47: "Write a test that would have caught the bug."\n` +
|
|
@@ -123,7 +129,7 @@ function main() {
|
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
console.log(
|
|
126
|
-
`lint-fix-has-regression-
|
|
132
|
+
`lint-fix-has-regression-tests: PASS — ${fixCommits.length} fix/feat commit(s), ` +
|
|
127
133
|
`${testFiles.length} behavioral test file(s): ${testFiles.join(', ')}`
|
|
128
134
|
);
|
|
129
135
|
}
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
* real, non-mocked `buildPlanningSnapshot(tmpCwd)` call (see
|
|
59
59
|
* `tests/health-diagnostic-rules/root-existence.test.cjs`). This guard
|
|
60
60
|
* therefore verifies the fixture-proof invariant STATICALLY against the
|
|
61
|
-
* test files' own text — mirroring `scripts/lint-fix-has-regression-
|
|
61
|
+
* test files' own text — mirroring `scripts/lint-fix-has-regression-tests.cjs`'s
|
|
62
62
|
* house style — rather than dynamically re-running fixture-building code
|
|
63
63
|
* this guard does not own.
|
|
64
64
|
*/
|
|
@@ -82,14 +82,70 @@ const CONSISTENCY_TEST_FILE = path.join(REPO_ROOT, 'tests', 'health-diagnostic-r
|
|
|
82
82
|
const CONSISTENCY_CODE_PREFIX_RE = /^C\d{3}$/;
|
|
83
83
|
|
|
84
84
|
// Phase 12 (#3310, ADR-3180 §8.5 extension) — the S0NN namespace's own
|
|
85
|
-
// fixture-proof pass. These
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
85
|
+
// fixture-proof pass. These codes are NOT collected in any exported `Rule[]`
|
|
86
|
+
// array: `cmdStateValidate` (src/state.cts) builds `Diagnostic[]` directly via
|
|
87
|
+
// a local `stateDiagnostic()` helper, not via the rule-table evaluator (out of
|
|
88
|
+
// scope for the RULES/CONSISTENCY_RULES-keyed passes above).
|
|
89
|
+
//
|
|
90
|
+
// #3696: the list is DISCOVERED from the source, not hardcoded. It used to be a
|
|
91
|
+
// literal `['S001', ..., 'S007']`, which is precisely the shape ADR-3180
|
|
92
|
+
// Decision 4(a) forbids — "guards discover call sites by whole-repo scan, never
|
|
93
|
+
// by an allowlist of known files" — because such a guard can only ever be as
|
|
94
|
+
// complete as the author's recall. Adding S008/S009 to `cmdStateValidate` left
|
|
95
|
+
// this pass reporting a confident, green "7 code(s), all fixture-covered" while
|
|
96
|
+
// two new codes had no fixture requirement at all: a zero it did not earn. The
|
|
97
|
+
// scan below cannot report a code it has not read out of the source.
|
|
91
98
|
const STATE_VALIDATE_TEST_FILE = path.join(REPO_ROOT, 'tests', 'state.test.cjs');
|
|
92
|
-
const
|
|
99
|
+
const STATE_VALIDATE_SOURCE_FILE = path.join(REPO_ROOT, 'src', 'state.cts');
|
|
100
|
+
// `g` is required by String.prototype.matchAll, which (unlike .exec) does not
|
|
101
|
+
// carry lastIndex across calls — so this constant is safe to share.
|
|
102
|
+
// #3696 review: `\s*` before `(` too. Requiring no space silently dropped a
|
|
103
|
+
// `stateDiagnostic ('S010', …)` call site from the discovered set, and since the
|
|
104
|
+
// fail-closed check below only fires on a FULLY empty result, a partial miss
|
|
105
|
+
// escaped the fixture-proof check entirely — the same "only as complete as the
|
|
106
|
+
// author's recall" failure this rewrite exists to prevent.
|
|
107
|
+
const STATE_DIAGNOSTIC_CALL_RE = /\bstateDiagnostic\s*\(\s*(['"`])(S\d{3})\1/g;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Every S0NN code `cmdStateValidate` can emit, read off its
|
|
111
|
+
* `stateDiagnostic(...)` call sites in `sourceText`.
|
|
112
|
+
*
|
|
113
|
+
* Takes the source TEXT rather than reading the path itself, so the discovery
|
|
114
|
+
* rule is exercisable against controlled fixtures — including the fail-closed
|
|
115
|
+
* path below, which is the branch that matters and which a path-reading
|
|
116
|
+
* function could only be tested on by mutating the real `src/state.cts`.
|
|
117
|
+
*
|
|
118
|
+
* Fails closed. An empty result means the helper was renamed or its call shape
|
|
119
|
+
* changed, and a guard that answers "0 codes, all covered" to that is worse than
|
|
120
|
+
* no guard — so it raises instead. Line/regex-based (not full AST) per this
|
|
121
|
+
* repo's existing lint-guard house style, same as TITLED_BLOCK_RE below.
|
|
122
|
+
*/
|
|
123
|
+
function discoverStateValidateCodes(sourceText, sourceLabel = formatRepoRelative(STATE_VALIDATE_SOURCE_FILE)) {
|
|
124
|
+
const codes = [...sourceText.matchAll(STATE_DIAGNOSTIC_CALL_RE)].map((m) => m[2]);
|
|
125
|
+
const unique = [...new Set(codes)].sort();
|
|
126
|
+
if (unique.length === 0) {
|
|
127
|
+
throw new ExitError(
|
|
128
|
+
1,
|
|
129
|
+
`lint-health-diagnostic-rule-table: found no stateDiagnostic() call sites in ${sourceLabel}.\n`
|
|
130
|
+
+ ' This pass discovers the S0NN code set from those call sites (#3696), so an empty\n'
|
|
131
|
+
+ ' result means the helper was renamed or its call shape changed — not that there are\n'
|
|
132
|
+
+ ' no codes. Update STATE_DIAGNOSTIC_CALL_RE to match the new shape.\n',
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return unique;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function readStateValidateSource() {
|
|
139
|
+
if (!fs.existsSync(STATE_VALIDATE_SOURCE_FILE)) {
|
|
140
|
+
throw new ExitError(
|
|
141
|
+
1,
|
|
142
|
+
`lint-health-diagnostic-rule-table: cannot discover S0NN codes — ${formatRepoRelative(STATE_VALIDATE_SOURCE_FILE)} not found.\n`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return fs.readFileSync(STATE_VALIDATE_SOURCE_FILE, 'utf8');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const STATE_VALIDATE_CODES = discoverStateValidateCodes(readStateValidateSource());
|
|
93
149
|
|
|
94
150
|
// Matches `describe(`/`test(`/`it(` calls whose first argument is a string
|
|
95
151
|
// literal, capturing that literal as the block's title. Line/regex-based
|
|
@@ -401,4 +457,5 @@ module.exports = {
|
|
|
401
457
|
CONSISTENCY_TEST_FILE,
|
|
402
458
|
STATE_VALIDATE_TEST_FILE,
|
|
403
459
|
STATE_VALIDATE_CODES,
|
|
460
|
+
discoverStateValidateCodes,
|
|
404
461
|
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* scripts/lint-mutation-test-derivation-drift.cjs
|
|
6
|
+
*
|
|
7
|
+
* #3881 follow-up ("one YAML parser" mutation-matrix piece 2): guards the
|
|
8
|
+
* derivation engine in scripts/mutation-matrix.cjs against the exact drift
|
|
9
|
+
* class PR #3888 shipped — a test file added on a branch that directly
|
|
10
|
+
* `require()`s a covered module's built artifact AND names itself after that
|
|
11
|
+
* module (`<module>.test.cjs` / `<module>.property.test.cjs` /
|
|
12
|
+
* `<module>-anything.test.cjs`), yet is never wired into that module's
|
|
13
|
+
* mutation shard.
|
|
14
|
+
*
|
|
15
|
+
* `scripts/mutation-matrix.cjs`'s `computeModuleTests` already derives most of
|
|
16
|
+
* a module's `tests` array automatically from exactly that (require + naming)
|
|
17
|
+
* signal, so a NEWLY drifting file of that shape is normally impossible — it
|
|
18
|
+
* would be auto-included the moment it exists. What this guard catches is the
|
|
19
|
+
* one case the derivation cannot self-correct: a file matching BOTH signals
|
|
20
|
+
* that the module's own `excludeTests` withholds. `excludeTests` entries are
|
|
21
|
+
* a deliberate, reasoned decision (see mutation-matrix.cjs's per-module
|
|
22
|
+
* comments) — this guard does not second-guess that decision, but it DOES
|
|
23
|
+
* verify every such file is EITHER accounted for in `tests` (auto-derived or
|
|
24
|
+
* `extraTests`) OR named in `excludeTests`. A file satisfying both signals
|
|
25
|
+
* that is in NEITHER list is exactly the drift #3888 shipped: nobody made a
|
|
26
|
+
* decision about it at all.
|
|
27
|
+
*
|
|
28
|
+
* Pure core (`findDrift`) so tests can drive it against a synthetic COVERED +
|
|
29
|
+
* requiring-files map without touching the real tests/ tree; `main()` wires
|
|
30
|
+
* it to the real repo for `npm run lint:ci`.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const path = require('node:path');
|
|
34
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Pure: for every module in `covered`, every file in `requiringFiles[module]`
|
|
38
|
+
* that ALSO matches the module's naming rule must be present in either
|
|
39
|
+
* `covered[module].tests` (already `tests/`-prefixed, as computeModuleTests
|
|
40
|
+
* emits it) or `covered[module].excludeTests` (bare basenames). Anything
|
|
41
|
+
* matching both signals but present in neither is reported as drift.
|
|
42
|
+
*
|
|
43
|
+
* @param {object} covered - mutation-matrix.cjs's COVERED (post `computeModuleTests`)
|
|
44
|
+
* @param {(moduleName: string) => string[]} findRequiringTestFiles - basenames requiring the module's artifact
|
|
45
|
+
* @param {(moduleName: string, file: string) => boolean} matchesModuleNamingRule
|
|
46
|
+
* @returns {{module: string, file: string}[]}
|
|
47
|
+
*/
|
|
48
|
+
function findDrift(covered, findRequiringTestFiles, matchesModuleNamingRule) {
|
|
49
|
+
const drift = [];
|
|
50
|
+
for (const [moduleName, entry] of Object.entries(covered)) {
|
|
51
|
+
const testsSet = new Set((entry.tests || []).map((t) => path.basename(t)));
|
|
52
|
+
const excludeSet = new Set(entry.excludeTests || []);
|
|
53
|
+
for (const file of findRequiringTestFiles(moduleName)) {
|
|
54
|
+
if (!matchesModuleNamingRule(moduleName, file)) continue;
|
|
55
|
+
if (testsSet.has(file) || excludeSet.has(file)) continue;
|
|
56
|
+
drift.push({ module: moduleName, file });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return drift;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function main() {
|
|
63
|
+
const {
|
|
64
|
+
COVERED,
|
|
65
|
+
findRequiringTestFiles,
|
|
66
|
+
matchesModuleNamingRule,
|
|
67
|
+
} = require('./mutation-matrix.cjs');
|
|
68
|
+
|
|
69
|
+
const drift = findDrift(COVERED, findRequiringTestFiles, matchesModuleNamingRule);
|
|
70
|
+
|
|
71
|
+
if (drift.length === 0) {
|
|
72
|
+
console.log(`ok mutation-test-derivation-drift: every module-named test file requiring a covered module has an explicit disposition (${Object.keys(COVERED).length} modules checked)`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.error('mutation-test-derivation-drift: test file(s) require a covered module and match its naming rule, but have no disposition in scripts/mutation-matrix.cjs (neither auto-derived/extraTests nor excludeTests):');
|
|
77
|
+
for (const d of drift) {
|
|
78
|
+
console.error(` [${d.module}] tests/${d.file}`);
|
|
79
|
+
}
|
|
80
|
+
console.error('\n remedy: in scripts/mutation-matrix.cjs, either let it auto-derive (do nothing further if it already appears in COVERED[<module>].tests), or add it to extraTests, or add a REASONED excludeTests entry explaining why it is deliberately withheld from the mutation shard.');
|
|
81
|
+
throw new ExitError(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { findDrift };
|
|
85
|
+
|
|
86
|
+
if (require.main === module) runMain(main);
|
|
@@ -62,11 +62,22 @@
|
|
|
62
62
|
* outstanding WORK wants the live set"): a LOOKUP, DIAGNOSTIC or ARCHIVAL
|
|
63
63
|
* pass wants the physical set; only "which phases belong to this milestone"
|
|
64
64
|
* wants the scoped set.
|
|
65
|
-
* - `src/roadmap.cts` `cmdRoadmapAnalyze
|
|
66
|
-
* heading->directory LOOKUP INDEX
|
|
67
|
-
* see the PHYSICAL set so a heading already scoped by
|
|
68
|
-
* `extractCurrentMilestoneScoped` can find its directory
|
|
69
|
-
*
|
|
65
|
+
* - `src/roadmap.cts` `cmdRoadmapAnalyze` — MIGRATED (#3882, ADR-3473 §8.2):
|
|
66
|
+
* `_phaseDirNames`, a heading->directory LOOKUP INDEX (not a milestone
|
|
67
|
+
* enumeration; must see the PHYSICAL set so a heading already scoped by
|
|
68
|
+
* `extractCurrentMilestoneScoped` can find its directory), now calls
|
|
69
|
+
* `listAllPhaseDirs(phasesDir, { includeSentinels: true })` instead of a
|
|
70
|
+
* hand-rolled `readdirSync` — no longer exempted, since there is nothing
|
|
71
|
+
* left in this function for either detector to catch.
|
|
72
|
+
* - `src/init.cts` `cmdInitMilestoneOp`'s `diskPhaseDirs` — MIGRATED (#3882,
|
|
73
|
+
* ADR-3473 §8.2): the same heading->directory LOOKUP INDEX shape as
|
|
74
|
+
* `cmdRoadmapAnalyze`'s `_phaseDirNames` above, now calls
|
|
75
|
+
* `listAllPhaseDirs(phasesDir, { includeSentinels: true })`. Its sibling
|
|
76
|
+
* readdirSync (the no-ROADMAP-headings-found fallback) was already routed
|
|
77
|
+
* through `listMilestonePhaseDirs` before this phase and remains so — no
|
|
78
|
+
* longer exempted; the function's other former exemption reason
|
|
79
|
+
* (`detectHasPriorPhases`/`detectUiPhaseActive`, unrelated call sites in
|
|
80
|
+
* the same file) is unaffected.
|
|
70
81
|
* - `src/verify.cts` `cmdValidateHealth`: a project-wide HEALTH-CHECK sweep
|
|
71
82
|
* (config drift, phase-directory naming, duplicate-directory collisions,
|
|
72
83
|
* unsummarized-plan detection) — same "sweep everything, report gaps"
|
|
@@ -280,11 +291,13 @@ const OWNER_FILES = new Set([
|
|
|
280
291
|
// `lint-milestone-window-drift.cjs`'s FUNCTION_SCOPED_EXEMPTIONS mechanism.
|
|
281
292
|
// See the header comment for the full written reason behind each entry.
|
|
282
293
|
const FUNCTION_SCOPED_EXEMPTIONS = new Map([
|
|
283
|
-
[path.join('src', 'roadmap.cts'), new Set(['cmdRoadmapAnalyze'])],
|
|
284
294
|
[path.join('src', 'verify.cts'), new Set(['cmdValidateHealth', 'cmdVerifySchemaDrift'])],
|
|
285
|
-
[path.join('src', 'init.cts'), new Set(['detectHasPriorPhases', 'detectUiPhaseActive'
|
|
295
|
+
[path.join('src', 'init.cts'), new Set(['detectHasPriorPhases', 'detectUiPhaseActive'])],
|
|
286
296
|
[path.join('src', 'milestone.cts'), new Set(['archivePhaseDirectories', 'cmdMilestoneComplete', 'cmdPhasesClear'])],
|
|
287
|
-
|
|
297
|
+
// #3849: collectSiblingWorktreePhaseNums reads a SIBLING worktree's phases dir —
|
|
298
|
+
// a different checkout listMilestonePhaseDirs (cwd-scoped) cannot serve; the
|
|
299
|
+
// allocators' own on-disk scans stay unexempted.
|
|
300
|
+
[path.join('src', 'phase.cts'), new Set(['cmdPhasesList', 'cmdPhaseNextDecimal', 'cmdPhasePlanIndex', 'cmdPhaseInsert', 'renameDecimalPhases', 'renameIntegerPhases', 'collectSiblingWorktreePhaseNums'])],
|
|
288
301
|
[path.join('src', 'audit.cts'), new Set(['listAuditPhaseTargets'])],
|
|
289
302
|
[path.join('src', 'commands.cts'), new Set(['cmdHistoryDigest'])],
|
|
290
303
|
[path.join('src', 'state.cts'), new Set(['cmdStateValidate', 'cmdStateSync', 'cmdStateRebuild'])],
|
|
@@ -370,8 +370,45 @@ function writeBaseline(root, violations) {
|
|
|
370
370
|
return entries;
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
/**
|
|
374
|
+
* Resolve the scan root for a CLI run. Default: this repo — exactly what
|
|
375
|
+
* `lint:ci` invokes. `--root <dir>` overrides it (#3640) so the CLI
|
|
376
|
+
* end-to-end test can prove the guard's FAIL path against an isolated temp
|
|
377
|
+
* tree instead of writing its fixture into the shared `gsd-core/workflows/`
|
|
378
|
+
* that parallel test chunks observe mid-lifecycle (the emitted-provenance
|
|
379
|
+
* manifest build copies the transient file into all 19 runtime manifests;
|
|
380
|
+
* the #3333 TOCTOU ENOENT crash was the same writer's first symptom).
|
|
381
|
+
*
|
|
382
|
+
* Usage errors exit 2 — a code distinct from the guard's own findings exit
|
|
383
|
+
* (1), so tests and CI can tell "bad invocation" from "drift found" without
|
|
384
|
+
* parsing stderr. Fail-closed on every bad shape, because a wrong root is
|
|
385
|
+
* not harmless: `--root --update` would otherwise resolve the flag itself
|
|
386
|
+
* into a cwd-relative path and --update would happily `mkdir -p` a stray
|
|
387
|
+
* baseline tree inside the shared source directory (#3640's write class).
|
|
388
|
+
* A value that starts with `-` (another flag) or does not name an existing
|
|
389
|
+
* directory is therefore a usage error, never a scan root. With repeated
|
|
390
|
+
* `--root` flags the first wins (same first-match convention as the boolean
|
|
391
|
+
* `--update` parse).
|
|
392
|
+
*/
|
|
393
|
+
function resolveScanRoot() {
|
|
394
|
+
const rootIdx = process.argv.indexOf('--root');
|
|
395
|
+
if (rootIdx === -1) return path.join(__dirname, '..');
|
|
396
|
+
const value = process.argv[rootIdx + 1];
|
|
397
|
+
const isDir = value !== undefined
|
|
398
|
+
&& !value.startsWith('-')
|
|
399
|
+
&& fs.existsSync(value)
|
|
400
|
+
&& fs.statSync(value).isDirectory();
|
|
401
|
+
if (!isDir) {
|
|
402
|
+
process.stderr.write('planning-prompt-drift: --root requires an existing directory argument (usage: --root <dir>)\n');
|
|
403
|
+
process.exitCode = 2;
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
return path.resolve(value);
|
|
407
|
+
}
|
|
408
|
+
|
|
373
409
|
function main() {
|
|
374
|
-
const root =
|
|
410
|
+
const root = resolveScanRoot();
|
|
411
|
+
if (root === null) return;
|
|
375
412
|
const update = process.argv.includes('--update');
|
|
376
413
|
const violations = scanRepo(root);
|
|
377
414
|
|