@opengsd/gsd-core 1.11.0 → 1.13.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 +12 -0
- 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 +78 -42
- 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 +91 -112
- package/agents/gsd-planner.md +20 -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 +82 -7
- package/agents/gsd-ui-researcher.md +70 -3
- package/agents/gsd-verifier.md +24 -2
- package/bin/install.js +847 -200
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/quick.md +8 -4
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +761 -100
- 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-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +785 -144
- package/gsd-core/bin/lib/capability-state.cjs +25 -4
- package/gsd-core/bin/lib/capability-validator.cjs +321 -18
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +229 -6
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- 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-aliases.cjs +16 -0
- 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 +877 -54
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +121 -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 +118 -14
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +23 -2
- 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/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +975 -326
- package/gsd-core/bin/lib/gap-checker.cjs +41 -8
- package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- 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 +96 -11
- package/gsd-core/bin/lib/init-command-router.cjs +132 -21
- package/gsd-core/bin/lib/init.cjs +252 -56
- package/gsd-core/bin/lib/install-engine.cjs +252 -15
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
- package/gsd-core/bin/lib/install-profiles.cjs +100 -18
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -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 +195 -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/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +175 -4
- package/gsd-core/bin/lib/milestone.cjs +112 -7
- 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-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +321 -13
- 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 +1175 -115
- package/gsd-core/bin/lib/plan-document.cjs +273 -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 +165 -34
- package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
- 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/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- 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 +62 -14
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
- package/gsd-core/bin/lib/roadmap.cjs +248 -64
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
- 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 +75 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -31
- 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 +216 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
- package/gsd-core/bin/lib/state-transition.cjs +850 -145
- package/gsd-core/bin/lib/state.cjs +1629 -287
- package/gsd-core/bin/lib/surface.cjs +33 -10
- 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/tdd-red-evidence.cjs +133 -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 +2542 -387
- 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/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- 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-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -13
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +441 -56
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- 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 +185 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -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/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +6 -5
- 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 +17 -5
- package/gsd-core/references/execute-mvp-tdd.md +18 -18
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- 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/mvp-concepts.md +2 -2
- 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/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- 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-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +76 -3
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +39 -9
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +15 -12
- 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/verifier-evidence-gate.md +160 -0
- 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 +7 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +3 -1
- package/gsd-core/workflows/add-phase.md +5 -3
- package/gsd-core/workflows/add-tests.md +4 -9
- package/gsd-core/workflows/add-todo.md +2 -2
- package/gsd-core/workflows/ai-integration-phase.md +5 -10
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +14 -3
- package/gsd-core/workflows/audit-milestone.md +11 -9
- package/gsd-core/workflows/audit-uat.md +19 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +12 -26
- package/gsd-core/workflows/check-todos.md +2 -2
- package/gsd-core/workflows/cleanup.md +3 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
- package/gsd-core/workflows/code-review-fix.md +3 -1
- package/gsd-core/workflows/code-review.md +192 -69
- package/gsd-core/workflows/complete-milestone.md +28 -14
- package/gsd-core/workflows/debug.md +6 -4
- package/gsd-core/workflows/diagnose-issues.md +17 -7
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +46 -19
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +3 -1
- package/gsd-core/workflows/eval-review.md +5 -10
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -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 +29 -5
- 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 +4 -2
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +68 -66
- package/gsd-core/workflows/execute-plan.md +25 -20
- package/gsd-core/workflows/explore.md +3 -1
- package/gsd-core/workflows/extract-learnings.md +3 -1
- package/gsd-core/workflows/fast.md +8 -2
- package/gsd-core/workflows/forensics.md +3 -1
- package/gsd-core/workflows/graduation.md +6 -6
- package/gsd-core/workflows/health.md +4 -7
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +17 -14
- package/gsd-core/workflows/inbox.md +5 -6
- package/gsd-core/workflows/ingest-docs.md +45 -12
- package/gsd-core/workflows/insert-phase.md +7 -5
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +7 -3
- package/gsd-core/workflows/list-workspaces.md +3 -1
- package/gsd-core/workflows/manager.md +15 -26
- package/gsd-core/workflows/map-codebase.md +3 -1
- package/gsd-core/workflows/milestone-summary.md +3 -1
- package/gsd-core/workflows/mvp-phase.md +3 -3
- package/gsd-core/workflows/new-milestone.md +10 -22
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +17 -29
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +4 -2
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +20 -5
- 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 +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
- package/gsd-core/workflows/plan-phase.md +251 -54
- package/gsd-core/workflows/plan-review-convergence.md +148 -19
- package/gsd-core/workflows/plant-seed.md +3 -3
- package/gsd-core/workflows/pr-branch.md +195 -51
- package/gsd-core/workflows/profile-user.md +17 -15
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +52 -15
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +33 -32
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +6 -4
- package/gsd-core/workflows/remove-workspace.md +3 -3
- package/gsd-core/workflows/resume-project.md +14 -14
- package/gsd-core/workflows/review.md +404 -21
- package/gsd-core/workflows/scan.md +3 -1
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +9 -9
- package/gsd-core/workflows/settings-integrations.md +66 -32
- package/gsd-core/workflows/settings.md +4 -6
- package/gsd-core/workflows/ship.md +22 -16
- package/gsd-core/workflows/sketch-wrap-up.md +13 -17
- package/gsd-core/workflows/sketch.md +13 -19
- package/gsd-core/workflows/smart-entry.md +4 -6
- package/gsd-core/workflows/spec-phase.md +31 -4
- package/gsd-core/workflows/spike-wrap-up.md +9 -11
- package/gsd-core/workflows/spike.md +21 -32
- package/gsd-core/workflows/stats.md +4 -2
- package/gsd-core/workflows/sync-skills.md +13 -5
- package/gsd-core/workflows/thread.md +13 -7
- package/gsd-core/workflows/transition.md +7 -5
- package/gsd-core/workflows/ui-phase.md +36 -21
- package/gsd-core/workflows/ui-review.md +7 -11
- package/gsd-core/workflows/ultraplan-phase.md +7 -13
- package/gsd-core/workflows/undo.md +9 -17
- package/gsd-core/workflows/update.md +47 -48
- package/gsd-core/workflows/validate-phase.md +3 -3
- 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 +106 -21
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +302 -22
- 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 +77 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +46 -12
- package/hooks/dist/gsd-read-guard.js +18 -7
- package/hooks/dist/gsd-read-injection-scanner.js +22 -13
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +222 -29
- package/hooks/dist/gsd-validate-commit.sh +523 -12
- 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 +36 -17
- 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-cmd.js +210 -1
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +4 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +302 -22
- 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 +77 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +46 -12
- package/hooks/gsd-read-guard.js +18 -7
- package/hooks/gsd-read-injection-scanner.js +22 -13
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +222 -29
- package/hooks/gsd-validate-commit.sh +523 -12
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +36 -17
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/hooks.json +6 -0
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +4 -0
- package/package.json +14 -9
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +12 -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 +52 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +406 -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 +189 -4
- 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/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- 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-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -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 +45 -14
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-response-language-coverage.cjs +524 -0
- 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 +124 -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/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +83 -14
- package/scripts/require-issue-link-policy.cjs +16 -1
- 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-execute-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- 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
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
const { execFileSync } = require('child_process');
|
|
33
33
|
const fs = require('fs');
|
|
34
|
+
const path = require('node:path');
|
|
34
35
|
|
|
35
36
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
36
37
|
|
|
@@ -92,28 +93,221 @@ function readStdinSync() {
|
|
|
92
93
|
// confirmed equivalent mutant is acceptable.
|
|
93
94
|
//
|
|
94
95
|
// HOW TO UPDATE:
|
|
95
|
-
// 1.
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
96
|
+
// 1. The per-module Stryker shard CANNOT be run locally: Stryker's tap
|
|
97
|
+
// runner (see stryker.config.mjs) spawns
|
|
98
|
+
// `node --test-reporter=tap -r <hook> <testFile>` once per covering test
|
|
99
|
+
// file per mutant, and .claude/hooks/block-local-node-test.sh's matcher
|
|
100
|
+
// still denies that form — its pattern `node(\s+-\S+)*\s+--test([\s=-]|$)`
|
|
101
|
+
// matches `--test-reporter` because `-` is in the trailing character
|
|
102
|
+
// class. Push the branch instead and let CI run the shard for the
|
|
103
|
+
// changed module.
|
|
104
|
+
// 2. Read the measured score from the CI shard's output.
|
|
105
|
+
// 3. Set minScore = floor(measured) - 1 (never lower than current value)
|
|
106
|
+
// and update the matching RATCHET_BASELINE entry in the same diff.
|
|
107
|
+
// 4. Open/update the PR — the CI gate will enforce the new floor on every
|
|
108
|
+
// future run.
|
|
99
109
|
|
|
100
110
|
/** Long-run target for all modules (ADR-456). */
|
|
101
111
|
const TARGET_MUTATION_SCORE = 80;
|
|
102
112
|
|
|
113
|
+
// ── Derived test-list engine (#3881 follow-up, "one YAML parser" mutation-matrix
|
|
114
|
+
// piece 2) ─────────────────────────────────────────────────────────────────
|
|
115
|
+
//
|
|
116
|
+
// PROBLEM THIS REPLACES: `tests: [...]` used to be a hand-maintained array per
|
|
117
|
+
// module, and `stryker.config.mjs`'s DEFAULT_TEST_CMD hand-duplicated the union
|
|
118
|
+
// of every such array in a second literal. The two drifted independently — PR
|
|
119
|
+
// #3888 shipped four new frontmatter test files that were never added to either
|
|
120
|
+
// list, so their mutants had zero constraining coverage and the shard's score
|
|
121
|
+
// silently fell (see the frontmatter entry's own PR #3888 note below, kept for
|
|
122
|
+
// history). A hand list can be forgotten; a derivation cannot forget a file that
|
|
123
|
+
// exists on disk.
|
|
124
|
+
//
|
|
125
|
+
// SIGNAL: a test file that directly `require()`s a covered module's built
|
|
126
|
+
// artifact (`gsd-core/bin/lib/<name>.cjs`) is declaring, by that require, that
|
|
127
|
+
// it constrains that module. That signal alone is far too broad to feed a
|
|
128
|
+
// per-mutant re-run budget: measured directly (no filter) against this tree,
|
|
129
|
+
// config-schema alone picks up 22 files most of this repo's spawn-heavy
|
|
130
|
+
// integration suites require incidentally for fixture setup — including a file
|
|
131
|
+
// literally named `graphify-auto-update.slow.test.cjs`. Stryker's command
|
|
132
|
+
// runner re-runs the WHOLE test command once per mutant, so an incidental
|
|
133
|
+
// integration require would multiply that module's shard cost by 10-20x for
|
|
134
|
+
// zero mutation-killing benefit (those suites do not assert on config-schema's
|
|
135
|
+
// internals; they merely load it as a dependency of something else under test).
|
|
136
|
+
//
|
|
137
|
+
// NARROWING RULE: a file is auto-derived into a module's shard only when BOTH
|
|
138
|
+
// hold:
|
|
139
|
+
// 1. it directly requires that module's `gsd-core/bin/lib/<name>.cjs`, and
|
|
140
|
+
// 2. its own filename starts with the module's name followed by `.` or `-`
|
|
141
|
+
// (i.e. `<name>.test.cjs`, `<name>.unit.test.cjs`, `<name>.property.test.cjs`,
|
|
142
|
+
// `<name>-anything.test.cjs`) — the file DECLARES itself, by its own name,
|
|
143
|
+
// to be that module's dedicated test surface. This is the exact naming
|
|
144
|
+
// shape every entry in this registry already used before this change
|
|
145
|
+
// (`*.property.test.cjs` / `*.unit.test.cjs`, or `<name>.test.cjs`), now
|
|
146
|
+
// made load-bearing instead of merely conventional.
|
|
147
|
+
// Measured effect of narrowing config-schema this way: 22 candidates -> 1
|
|
148
|
+
// (config-schema.property.test.cjs, the file already in the shard) — the
|
|
149
|
+
// naming filter is what keeps the derivation from silently tripling that
|
|
150
|
+
// shard's cost, per the piece-2 "watch the cost consequence" requirement.
|
|
151
|
+
//
|
|
152
|
+
// ESCAPE HATCHES (both REQUIRED to be explicit, reasoned per-module entries —
|
|
153
|
+
// never a silent list):
|
|
154
|
+
// - `extraTests`: files that constrain this module (genuinely, by writing
|
|
155
|
+
// assertions against its behaviour) but do not match the naming rule
|
|
156
|
+
// above — either because the module was extracted from another file's
|
|
157
|
+
// tests (context-composer) or because the file's own name follows a
|
|
158
|
+
// different, still-legible convention (feat-3881-yaml-parser-consequences.test.cjs).
|
|
159
|
+
// - `excludeTests`: files that DO match the naming + require signal above
|
|
160
|
+
// (so the derivation would otherwise auto-include them) but are
|
|
161
|
+
// deliberately withheld from the per-mutant shard for a measured,
|
|
162
|
+
// documented reason (almost always: they are integration-shaped and
|
|
163
|
+
// spawn a subprocess per case, so Stryker's per-mutant re-run of the
|
|
164
|
+
// whole file cannot finish inside the shard's timeout — the exact #2790
|
|
165
|
+
// planning-inspect.test.cjs precedent this file already documented before
|
|
166
|
+
// this change; the derivation engine now enforces that precedent by
|
|
167
|
+
// construction instead of leaving it to reviewer memory).
|
|
168
|
+
// `computeModuleTests` combines all three into the final `tests` array; the
|
|
169
|
+
// guard `scripts/lint-mutation-test-derivation-drift.cjs` independently
|
|
170
|
+
// verifies every SIGNAL-matching file (require + naming rule, unfiltered by
|
|
171
|
+
// this module's own excludeTests) has an explicit disposition — auto-derived,
|
|
172
|
+
// named in extraTests, or named in excludeTests — so a file that newly starts
|
|
173
|
+
// matching the naming rule (like #3888's four files would have, had they been
|
|
174
|
+
// named `frontmatter*`) cannot silently fall through the cracks again.
|
|
175
|
+
const TESTS_DIR = path.join(__dirname, '..', 'tests');
|
|
176
|
+
let _testRequireCache = null;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Scan every `tests/*.test.cjs` file once and cache, per covered module name,
|
|
180
|
+
* which files directly `require('gsd-core/bin/lib/<name>.cjs')` (or a relative
|
|
181
|
+
* equivalent — `../gsd-core/bin/lib/<name>` etc. — the require path always
|
|
182
|
+
* ends in the literal segment matched below). Pure w.r.t. process lifetime;
|
|
183
|
+
* the tests/ directory does not change while this process runs.
|
|
184
|
+
*
|
|
185
|
+
* @returns {Map<string, Set<string>>} module name -> Set of basenames (e.g. 'frontmatter.test.cjs')
|
|
186
|
+
*/
|
|
187
|
+
function scanTestRequires() {
|
|
188
|
+
if (_testRequireCache) return _testRequireCache;
|
|
189
|
+
const REQUIRE_RE = /require\(\s*['"](?:[./]*)?gsd-core\/bin\/lib\/([a-zA-Z0-9_-]+)(?:\.cjs)?['"]\s*\)/g;
|
|
190
|
+
const cache = new Map();
|
|
191
|
+
let entries;
|
|
192
|
+
try {
|
|
193
|
+
entries = fs.readdirSync(TESTS_DIR).filter((f) => f.endsWith('.test.cjs'));
|
|
194
|
+
} catch {
|
|
195
|
+
entries = [];
|
|
196
|
+
}
|
|
197
|
+
for (const file of entries) {
|
|
198
|
+
const text = fs.readFileSync(path.join(TESTS_DIR, file), 'utf8');
|
|
199
|
+
let m;
|
|
200
|
+
REQUIRE_RE.lastIndex = 0;
|
|
201
|
+
while ((m = REQUIRE_RE.exec(text))) {
|
|
202
|
+
const mod = m[1];
|
|
203
|
+
if (!cache.has(mod)) cache.set(mod, new Set());
|
|
204
|
+
cache.get(mod).add(file);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
_testRequireCache = cache;
|
|
208
|
+
return cache;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Every test file that directly requires `<moduleName>`'s built artifact —
|
|
213
|
+
* the FULL, unfiltered signal set (used by the derivation-drift guard, which
|
|
214
|
+
* must see every candidate regardless of naming, so it can demand an explicit
|
|
215
|
+
* disposition for each one).
|
|
216
|
+
*
|
|
217
|
+
* @param {string} moduleName
|
|
218
|
+
* @returns {string[]} sorted basenames
|
|
219
|
+
*/
|
|
220
|
+
function findRequiringTestFiles(moduleName) {
|
|
221
|
+
const set = scanTestRequires().get(moduleName);
|
|
222
|
+
return set ? [...set].sort() : [];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** True when `file`'s own name declares it a dedicated test surface for `moduleName`
|
|
226
|
+
* (`<moduleName>.test.cjs`, or starts with `<moduleName>.` / `<moduleName>-`). */
|
|
227
|
+
function matchesModuleNamingRule(moduleName, file) {
|
|
228
|
+
return file === `${moduleName}.test.cjs`
|
|
229
|
+
|| file.startsWith(`${moduleName}.`)
|
|
230
|
+
|| file.startsWith(`${moduleName}-`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Auto-derived candidates for `moduleName`: requires the module's artifact AND
|
|
235
|
+
* matches the naming rule. Does NOT apply that module's own `excludeTests` —
|
|
236
|
+
* callers combine that separately (`computeModuleTests` for the real shard,
|
|
237
|
+
* the guard for candidate enumeration).
|
|
238
|
+
*/
|
|
239
|
+
function deriveNamedTests(moduleName) {
|
|
240
|
+
return findRequiringTestFiles(moduleName).filter((f) => matchesModuleNamingRule(moduleName, f));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Final `tests` array for a COVERED entry: auto-derived (require + naming
|
|
245
|
+
* rule) UNION `extraTests` MINUS `excludeTests`, sorted, each prefixed
|
|
246
|
+
* `tests/`. Throws if `excludeTests` names a file that isn't actually a
|
|
247
|
+
* derived candidate (an exclusion of nothing is a stale/typo'd entry, not a
|
|
248
|
+
* real decision) or if `extraTests` names a file already auto-derived (that
|
|
249
|
+
* would silently mask which mechanism is responsible for its presence).
|
|
250
|
+
*/
|
|
251
|
+
function computeModuleTests(moduleName, entry) {
|
|
252
|
+
const derived = new Set(deriveNamedTests(moduleName));
|
|
253
|
+
const extra = entry.extraTests || [];
|
|
254
|
+
const exclude = entry.excludeTests || [];
|
|
255
|
+
for (const f of extra) {
|
|
256
|
+
if (derived.has(f)) {
|
|
257
|
+
throw new Error(`mutation-matrix: COVERED['${moduleName}'].extraTests names '${f}', which is already auto-derived — remove it from extraTests (it is redundant and hides which mechanism includes it)`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const f of exclude) {
|
|
261
|
+
if (!derived.has(f)) {
|
|
262
|
+
throw new Error(`mutation-matrix: COVERED['${moduleName}'].excludeTests names '${f}', which is not an auto-derived candidate for this module — remove the stale exclusion`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const excludeSet = new Set(exclude);
|
|
266
|
+
const final = new Set();
|
|
267
|
+
for (const f of derived) if (!excludeSet.has(f)) final.add(f);
|
|
268
|
+
for (const f of extra) final.add(f);
|
|
269
|
+
return [...final].sort().map((f) => `tests/${f}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
103
272
|
// ── Single source of truth: covered modules ───────────────────────────────────
|
|
104
|
-
// Each entry: { cjs: '<built artifact>',
|
|
273
|
+
// Each entry: { cjs: '<built artifact>', extraTests: [...], excludeTests: [...], minScore: N }
|
|
274
|
+
// `tests` is no longer hand-written — computeModuleTests() derives it below
|
|
275
|
+
// from direct `require()`s of the module's artifact (see the derivation-engine
|
|
276
|
+
// header above). extraTests/excludeTests are the two REQUIRED, reasoned escape
|
|
277
|
+
// hatches; leave both `[]` (omit the key) when a module needs neither.
|
|
105
278
|
//
|
|
106
279
|
// minScore is the CI break threshold for this module's shard.
|
|
107
280
|
// Floors are measured scores minus 1–2 pts for run-to-run variance.
|
|
108
281
|
// Measured CI scores 2026-06-14 (issue #1187, timeout-free — source of truth):
|
|
109
282
|
// context-utilization 92.31% → floor 80 (target already met)
|
|
110
283
|
// prompt-budget 68.33% → floor 66 (local was 99.6% — TIMEOUT INFLATION; CI is the truth)
|
|
111
|
-
// frontmatter 63.35% → floor 62
|
|
284
|
+
// frontmatter 63.35% → floor 62 (SUPERSEDED — see 2026-08-25 below)
|
|
112
285
|
// adr-parser 69.30% → floor 68
|
|
113
286
|
// config-schema 54.55% → floor 52 (local was 69.7% — TIMEOUT INFLATION; CI is the truth)
|
|
114
287
|
// active-workstream-store 81.91% → floor 80
|
|
115
288
|
// core-utils 77.52% → floor 75
|
|
116
289
|
//
|
|
290
|
+
// Measured CI score 2026-08-25 (#3706, PR 3867):
|
|
291
|
+
// frontmatter 66.67% → floor 65
|
|
292
|
+
// #3706 added agentScalarNeedsDoubleQuoting to frontmatter.cts and exported
|
|
293
|
+
// escapeDoubleQuoted, but the tests constraining them lived in
|
|
294
|
+
// tests/runtime-converters.test.cjs, which this lane does NOT run — the same trap the
|
|
295
|
+
// #1882 note on the frontmatter entry describes. The shard fell to 60.58 and broke the
|
|
296
|
+
// floor. Direct unit tests for both were added to tests/frontmatter.unit.test.cjs, each
|
|
297
|
+
// clause paired with a near-miss that must answer the opposite way, which took the module
|
|
298
|
+
// above its pre-change score. Floor ratcheted per the HOW TO UPDATE formula above, and
|
|
299
|
+
// RATCHET_BASELINE — which lives in tests/mutation-matrix-ratchet.test.cjs, not here — is
|
|
300
|
+
// updated in the same diff as that procedure requires.
|
|
301
|
+
//
|
|
302
|
+
// PR #3888 (#3881 follow-up): the frontmatter shard's new tests were never registered here
|
|
303
|
+
// (only the pre-existing frontmatter.property/unit + unusable-input ran), so Stryker's
|
|
304
|
+
// mutants in the new vendored-parser adapter code had nothing constraining them. Score fell
|
|
305
|
+
// to 55.8% against the 65 floor (748 killed / 593 survived / 17 timeout) and the shard also
|
|
306
|
+
// blew the 15-minute cap. Fixed by registering the branch's four new/changed frontmatter
|
|
307
|
+
// test files in the tests array above (see that entry's inline comment for which files and
|
|
308
|
+
// why) and giving the shard a measured 180-minute budget via timeoutMinutes. minScore left
|
|
309
|
+
// at 65 pending a fresh CI measurement with the corrected test list.
|
|
310
|
+
//
|
|
117
311
|
// LESSON: floors MUST be calibrated from CI mutation runs (CI runs with
|
|
118
312
|
// timeout≈0, deterministic). Local runs count timeouts as kills and
|
|
119
313
|
// inflate scores significantly (prompt-budget: 99.6% local vs 68.3% CI;
|
|
@@ -122,84 +316,311 @@ const TARGET_MUTATION_SCORE = 80;
|
|
|
122
316
|
const COVERED = {
|
|
123
317
|
'context-utilization': {
|
|
124
318
|
cjs: 'gsd-core/bin/lib/context-utilization.cjs',
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
319
|
+
// Derived: context-utilization.property.test.cjs (pre-existing) +
|
|
320
|
+
// context-utilization.test.cjs (piece-2 derivation find: it directly requires and
|
|
321
|
+
// matches the naming rule, but was never hand-added to the old literal list —
|
|
322
|
+
// exactly the #3888 drift class this derivation exists to stop. Measured cost:
|
|
323
|
+
// +50ms over the property-only baseline (58ms -> 108ms, in-process, 0 subprocess
|
|
324
|
+
// spawns) — negligible for a shard whose floor is already at TARGET.
|
|
128
325
|
// After mutation-killer assertions added in #1187: measured 92.31% (2026-06-14).
|
|
129
326
|
// 3 survivors are __esModule boilerplate (genuinely equivalent CJS interop mutants).
|
|
130
|
-
// minScore raised to TARGET (80) — module now meets ADR-456 goal.
|
|
131
|
-
|
|
327
|
+
// minScore raised to TARGET (80) — module now meets ADR-456 goal. Not yet
|
|
328
|
+
// re-measured against the wider (derived) test list; the added file only adds
|
|
329
|
+
// assertions, never removes any, so the floor cannot have fallen.
|
|
330
|
+
// CI run 33012034388 (2026-08-25, #3881 ratchet): measured 92.31% (unchanged from
|
|
331
|
+
// the #1187 measurement above — same test list, re-confirmed by the mutation
|
|
332
|
+
// ratchet's own audit). Floor = floor(92.31) - 1 = 91.
|
|
333
|
+
minScore: 91,
|
|
132
334
|
},
|
|
133
335
|
// context-composer: extracted from prompt-budget by #2929. Needs its own entry because
|
|
134
336
|
// mutation coverage does not migrate with relocated code — scoring only prompt-budget.cjs
|
|
135
|
-
// would leave the extracted ladder unmeasured.
|
|
337
|
+
// would leave the extracted ladder unmeasured. Its own filename never matches the
|
|
338
|
+
// "context-composer*" naming rule for prompt-budget-parity.test.cjs / prompt-budget.unit.test.cjs
|
|
339
|
+
// — both genuinely constrain context-composer.cjs (the ladder was relocated INTO it), so
|
|
340
|
+
// both are declared via extraTests rather than silently missing from the derivation.
|
|
136
341
|
'context-composer': {
|
|
137
342
|
cjs: 'gsd-core/bin/lib/context-composer.cjs',
|
|
138
|
-
|
|
139
|
-
'
|
|
140
|
-
'
|
|
141
|
-
'tests/context-composer.test.cjs',
|
|
142
|
-
'tests/context-composer.property.test.cjs',
|
|
343
|
+
extraTests: [
|
|
344
|
+
'prompt-budget-parity.test.cjs',
|
|
345
|
+
'prompt-budget.unit.test.cjs',
|
|
143
346
|
],
|
|
144
|
-
|
|
347
|
+
// CI run 33012034388 (2026-08-25, #3881 ratchet): measured 79.92%. Floor =
|
|
348
|
+
// floor(79.92) - 1 = 78.
|
|
349
|
+
minScore: 78,
|
|
145
350
|
},
|
|
146
351
|
'prompt-budget': {
|
|
147
352
|
cjs: 'gsd-core/bin/lib/prompt-budget.cjs',
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
353
|
+
// Derived: property + unit (pre-existing) plus two piece-2 derivation finds that
|
|
354
|
+
// directly require prompt-budget.cjs and match the naming rule but were never in the
|
|
355
|
+
// old hand list — prompt-budget-parity.test.cjs and prompt-budget.test.cjs. Measured
|
|
356
|
+
// cost: 475ms (2-file) -> 527ms (4-file), in-process, 0 subprocess spawns; +52ms is
|
|
357
|
+
// negligible next to this module's own mutant count.
|
|
152
358
|
// CI 68.33% timeout-free (164 killed / 1 timeout / 240 total) 2026-06-14;
|
|
153
|
-
// local was 99.6% — timeout inflation. Floor = 68 - 2 margin.
|
|
154
|
-
|
|
359
|
+
// local was 99.6% — timeout inflation. Floor = 68 - 2 margin. Not yet re-measured
|
|
360
|
+
// against the wider (derived) test list; both added files only add assertions, never
|
|
361
|
+
// remove any, so the floor cannot have fallen.
|
|
362
|
+
// CI run 33012034388 (2026-08-25, #3881 ratchet): re-measured against the wider
|
|
363
|
+
// (derived) test list at 88.95%. Floor = floor(88.95) - 1 = 87.
|
|
364
|
+
minScore: 87,
|
|
155
365
|
},
|
|
156
366
|
frontmatter: {
|
|
157
367
|
cjs: 'gsd-core/bin/lib/frontmatter.cjs',
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
368
|
+
// extraTests: files that genuinely constrain frontmatter.cjs but do not match the
|
|
369
|
+
// "frontmatter*" naming rule, so the derivation cannot find them on its own —
|
|
370
|
+
// each earns its slot on evidence, not blanket inclusion (verified no two duplicate
|
|
371
|
+
// the same constraining assertion):
|
|
372
|
+
// - unusable-input.test.cjs: #1882 added the unterminated-fence detection to
|
|
373
|
+
// frontmatter.cjs, and the tests that constrain it live here. Without this entry
|
|
374
|
+
// the mutants in that branch are covered by no test in the shard.
|
|
375
|
+
// - feat-3881-yaml-parser-consequences.test.cjs: consequence/boundary matrix for the
|
|
376
|
+
// #3881 parser swap (state-transition interop, unusable-input counters, and — as of
|
|
377
|
+
// the piece-1 mutation-matrix fix below — the relocated anchor-alias-bomb + B1/B2
|
|
378
|
+
// block-scalar assertions). Nothing else in the shard drives
|
|
379
|
+
// extractFrontmatter/reconstructFrontmatter through those seams.
|
|
380
|
+
extraTests: [
|
|
381
|
+
'unusable-input.test.cjs',
|
|
382
|
+
'feat-3881-yaml-parser-consequences.test.cjs',
|
|
383
|
+
],
|
|
384
|
+
// excludeTests: files the derivation WOULD auto-include (require frontmatter.cjs
|
|
385
|
+
// directly AND match the "frontmatter*" naming rule) but are deliberately withheld:
|
|
386
|
+
// - frontmatter-cli.test.cjs: 778-line CLI-integration file, 39 subprocess-spawn
|
|
387
|
+
// references (spawnSync/execFileSync/runGsdTools) — the same #2790
|
|
388
|
+
// planning-inspect.test.cjs shape (a `node --test <file>` invocation Stryker's
|
|
389
|
+
// command runner re-runs whole, once per mutant, at whatever its slowest spawn
|
|
390
|
+
// case costs). Never measured in a shard; excluded up front on the same evidence
|
|
391
|
+
// class rather than discovered by a timeout.
|
|
392
|
+
// - frontmatter.test.cjs: mutation-matrix piece 1 (#3881 follow-up). This
|
|
393
|
+
// 2932-line integration file cost 3132ms of the shard's ~4800ms per-run
|
|
394
|
+
// (measured via node:test's run() API — node --test is hard-blocked locally,
|
|
395
|
+
// this is the sanctioned substitute), which at ~1850 mutants (source grew 1.8x
|
|
396
|
+
// for #3881) projected to ~96 of the shard's 140-minute total. Its two
|
|
397
|
+
// genuinely-unique assertion classes — anchor-alias-bomb refusal (billion-laughs
|
|
398
|
+
// -style anchor/alias expansion must be rejected, not expanded) and the B1/B2
|
|
399
|
+
// block-scalar assertions (parsing commands/gsd/add-tests.md must not invent a
|
|
400
|
+
// phantom "Example" key) — were relocated verbatim into
|
|
401
|
+
// feat-3881-yaml-parser-consequences.test.cjs (already in this shard via
|
|
402
|
+
// extraTests above) rather than deleted, so the mutants they kill stay killed.
|
|
403
|
+
// frontmatter.test.cjs itself is UNCHANGED and keeps running in the normal
|
|
404
|
+
// (non-mutation) suite — only the mutation shard drops it.
|
|
405
|
+
excludeTests: [
|
|
406
|
+
'frontmatter-cli.test.cjs',
|
|
407
|
+
'frontmatter.test.cjs',
|
|
165
408
|
],
|
|
166
|
-
minScore:
|
|
409
|
+
minScore: 65,
|
|
410
|
+
// MEASUREMENT, not a projection. Under the tap runner with coverageAnalysis: 'perTest'
|
|
411
|
+
// (#3915), Stryker now re-runs only the test files that cover each mutated line instead
|
|
412
|
+
// of all six files for every one of ~1900 mutants. Measured result: the frontmatter shard
|
|
413
|
+
// completed in 713s (11m53s) — GitHub Actions run 33026833181, job wall time including
|
|
414
|
+
// checkout and `npm ci` — against 1751s (29m11s) on the command runner in run
|
|
415
|
+
// 33021042847. A 59% reduction.
|
|
416
|
+
// 20 minutes is 1.68x the measured 713s. The override is not simply deleted because the
|
|
417
|
+
// shared default is 15 minutes, which 11m53s would fit inside — but only at 79% of
|
|
418
|
+
// budget — and this module's mutant count grew 1.8x in a single change (#3881), so a
|
|
419
|
+
// shard sitting at 79% of the shared default is one growth spurt from a red lane. 20
|
|
420
|
+
// keeps a real margin while still cutting the previous 60-minute budget by 3x.
|
|
421
|
+
// Scoped to this shard only via timeoutMinutes below — every other shard keeps the
|
|
422
|
+
// 15-minute default emitted by buildResult(), well under GitHub Actions' 360-minute job
|
|
423
|
+
// ceiling.
|
|
424
|
+
timeoutMinutes: 20,
|
|
425
|
+
// isolation: no knob left to tune (#3915). Per-file process isolation is now INHERENT
|
|
426
|
+
// to @stryker-mutator/tap-runner — it drives Node's own `--test-reporter=tap` once per
|
|
427
|
+
// covering test FILE, so every file already runs in its own process by construction.
|
|
428
|
+
// The prior audit's 'none' vs 'process' comparison (recorded here before this change)
|
|
429
|
+
// is moot: there is nothing left to opt in or out of.
|
|
167
430
|
},
|
|
431
|
+
// adr-parser / config-schema / active-workstream-store / core-utils: derivation reproduces
|
|
432
|
+
// their prior hand lists exactly (every constraining file's own name already matched the
|
|
433
|
+
// "<module>*" rule) — no extraTests/excludeTests needed. Note config-schema in particular:
|
|
434
|
+
// an UNFILTERED require-scan finds 22 files that require config-schema.cjs, but only
|
|
435
|
+
// config-schema.property.test.cjs matches the naming rule — the naming filter is what
|
|
436
|
+
// keeps this shard from silently ballooning to include spawn-heavy integration suites
|
|
437
|
+
// (e.g. graphify-auto-update.slow.test.cjs) that merely load config-schema as a fixture
|
|
438
|
+
// dependency of something else under test.
|
|
168
439
|
'adr-parser': {
|
|
169
440
|
cjs: 'gsd-core/bin/lib/adr-parser.cjs',
|
|
170
|
-
tests: [
|
|
171
|
-
'tests/adr-parser.property.test.cjs',
|
|
172
|
-
'tests/adr-parser.test.cjs',
|
|
173
|
-
'tests/adr-parser.unit.test.cjs',
|
|
174
|
-
],
|
|
175
441
|
minScore: 68,
|
|
176
442
|
},
|
|
177
443
|
'config-schema': {
|
|
178
444
|
cjs: 'gsd-core/bin/lib/config-schema.cjs',
|
|
179
|
-
tests: [
|
|
180
|
-
'tests/config-schema.property.test.cjs',
|
|
181
|
-
],
|
|
182
445
|
// CI 54.55% timeout-free (18 killed / 0 timeout / 33 total) 2026-06-14;
|
|
183
446
|
// local was 69.7% — timeout inflation. Floor = 54 - 2 margin.
|
|
184
|
-
|
|
447
|
+
// CI run 33012034388 (2026-08-25, #3881 ratchet): measured 75.51%. Floor =
|
|
448
|
+
// floor(75.51) - 1 = 74.
|
|
449
|
+
minScore: 74,
|
|
185
450
|
},
|
|
186
451
|
'active-workstream-store': {
|
|
187
452
|
cjs: 'gsd-core/bin/lib/active-workstream-store.cjs',
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
],
|
|
192
|
-
minScore: 80,
|
|
453
|
+
// CI run 33012034388 (2026-08-25, #3881 ratchet): measured 87.42%. Floor =
|
|
454
|
+
// floor(87.42) - 1 = 86.
|
|
455
|
+
minScore: 86,
|
|
193
456
|
},
|
|
194
457
|
'core-utils': {
|
|
195
458
|
cjs: 'gsd-core/bin/lib/core-utils.cjs',
|
|
196
|
-
tests: [
|
|
197
|
-
'tests/core-utils.test.cjs',
|
|
198
|
-
],
|
|
199
459
|
minScore: 75, // measured 77.52% (2026-06-14, issue #1187); floor = 77 - 2
|
|
200
460
|
},
|
|
461
|
+
// planning-inspect / plan-document / planning-command-router: net-new modules
|
|
462
|
+
// added by #2790. Registered here so the Stryker gate stops SKIPPING them
|
|
463
|
+
// (previously has_work: "false" — ~1000 LOC entirely outside mutation scoring).
|
|
464
|
+
//
|
|
465
|
+
// WHY THESE SHARDS POINT AT tests/planning-inspect.unit.test.cjs, NOT
|
|
466
|
+
// tests/planning-inspect.test.cjs. CI evidence: two shards pointed at the
|
|
467
|
+
// integration file were CANCELLED at the workflow's 15-minute cap —
|
|
468
|
+
// "Mutation testing 4% (elapsed: ~3m, remaining: ~1h 19m) 27/640 tested".
|
|
469
|
+
// tests/planning-inspect.test.cjs is INTEGRATION-shaped (91 cases, most
|
|
470
|
+
// spawning a `gsd-tools` child process via `runGsdTools`); Stryker's command
|
|
471
|
+
// runner treats the whole `node --test <file>` invocation as ONE test costing
|
|
472
|
+
// whatever the slowest case costs (measured ~20s), and re-runs that entire
|
|
473
|
+
// file once per mutant — 640 mutants x 20s cannot finish in 15 minutes.
|
|
474
|
+
// tests/planning-inspect.unit.test.cjs is the dedicated, spawn-free,
|
|
475
|
+
// in-process mutation surface for exactly these three modules (measured
|
|
476
|
+
// locally: the whole file runs in well under a second) — the same shape
|
|
477
|
+
// every other entry in this registry already uses (*.property.test.cjs /
|
|
478
|
+
// *.unit.test.cjs). The integration suite is UNAFFECTED by this change: it
|
|
479
|
+
// keeps running in full in the normal (non-mutation) test job, and remains
|
|
480
|
+
// the source of truth for spawn-boundary/CLI-dispatch/read-only-proof
|
|
481
|
+
// behaviour that an in-process unit file cannot exercise.
|
|
482
|
+
//
|
|
483
|
+
// Measured CI scores (GitHub Actions run 32392791843, all three shards
|
|
484
|
+
// PASSED — not a local run; mutation shards run `node --test`, hard-blocked
|
|
485
|
+
// in this repo's local environment):
|
|
486
|
+
// planning-command-router 95.65% → floor 94 (already exceeds TARGET_MUTATION_SCORE (80))
|
|
487
|
+
// plan-document 76.58% → floor 75
|
|
488
|
+
// planning-inspect 57.03% → floor 56 (well below TARGET (80) — ratchet
|
|
489
|
+
// candidate; comfortably clears its own floor but has real room to grow.
|
|
490
|
+
// Raise as its tests improve, never lower it.)
|
|
491
|
+
//
|
|
492
|
+
// All three shards point at tests/planning-inspect.unit.test.cjs (in-process,
|
|
493
|
+
// spawn-free, ~0.3s dry run), not tests/planning-inspect.test.cjs — that is
|
|
494
|
+
// what made measurement possible at all. The integration file spawns a
|
|
495
|
+
// subprocess per case via runGsdTools; Stryker's command runner treats the
|
|
496
|
+
// whole `node --test <file>` invocation as one test costing whatever the
|
|
497
|
+
// slowest case costs (measured ~20s), and re-runs that entire file once per
|
|
498
|
+
// mutant, so 640 mutants x 20s could not finish inside the 15-minute shard
|
|
499
|
+
// cap. The integration suite is unaffected by this change: it keeps running
|
|
500
|
+
// in full in the normal (non-mutation) test job.
|
|
501
|
+
// planning-inspect's own name matches "planning-inspect.unit.test.cjs" via the naming
|
|
502
|
+
// rule, so that file is auto-derived. planning-inspect.test.cjs (the excluded integration
|
|
503
|
+
// file the comment above names) ALSO matches the naming rule and directly requires the
|
|
504
|
+
// module, so it must be an explicit excludeTests entry now — the derivation would
|
|
505
|
+
// otherwise auto-include it and reproduce the exact 15-minute-cap cancellation the
|
|
506
|
+
// comment above documents.
|
|
507
|
+
'planning-inspect': {
|
|
508
|
+
cjs: 'gsd-core/bin/lib/planning-inspect.cjs',
|
|
509
|
+
excludeTests: ['planning-inspect.test.cjs'],
|
|
510
|
+
minScore: 56,
|
|
511
|
+
},
|
|
512
|
+
// plan-document / planning-command-router: their own names never appear in any test
|
|
513
|
+
// filename (the shared dedicated unit file is named after planning-inspect, the module
|
|
514
|
+
// #2790 extracted them alongside), so the naming-rule derivation finds nothing — same
|
|
515
|
+
// cross-cutting shape as context-composer above. Declared via extraTests.
|
|
516
|
+
'plan-document': {
|
|
517
|
+
cjs: 'gsd-core/bin/lib/plan-document.cjs',
|
|
518
|
+
extraTests: ['planning-inspect.unit.test.cjs'],
|
|
519
|
+
minScore: 75,
|
|
520
|
+
},
|
|
521
|
+
'planning-command-router': {
|
|
522
|
+
cjs: 'gsd-core/bin/lib/planning-command-router.cjs',
|
|
523
|
+
extraTests: ['planning-inspect.unit.test.cjs'],
|
|
524
|
+
minScore: 94,
|
|
525
|
+
},
|
|
526
|
+
// model-catalog: net-new registration by #3007. The module was entirely
|
|
527
|
+
// outside mutation scoring (has_work: "false") before this entry, so the
|
|
528
|
+
// #3007 per-model Codex effort rewrite (renderEffortForRuntime's
|
|
529
|
+
// CODEX_MODEL_EFFORT lookup, the 'ultra' policy rejection, the ladder
|
|
530
|
+
// walk-up clamp) had zero mutation coverage.
|
|
531
|
+
//
|
|
532
|
+
// Same #2790 precedent as planning-inspect above: this shard points at a
|
|
533
|
+
// dedicated tests/model-catalog.unit.test.cjs, NOT tests/model-resolver.test.cjs
|
|
534
|
+
// — that integration file uses runGsdTools heavily and would hit the same
|
|
535
|
+
// 15-minute shard-cap cancellation #2790 documented (a `node --test <file>`
|
|
536
|
+
// invocation is ONE test costing whatever its slowest case costs, re-run
|
|
537
|
+
// per mutant). tests/model-catalog.unit.test.cjs is spawn-free, in-process,
|
|
538
|
+
// and runs in well under a second.
|
|
539
|
+
//
|
|
540
|
+
// Prior context (#3007, GitHub Actions run 32605073352, job 97108869486):
|
|
541
|
+
// model-catalog 59.62% → floor 58 (248 killed, 168 survived, 0 timeouts,
|
|
542
|
+
// 0 errors). SUPERSEDED by the 2026-08-27 measurement below.
|
|
543
|
+
//
|
|
544
|
+
// The shard completed in 57 seconds — concrete evidence the spawn-free
|
|
545
|
+
// unit-file design above worked: the #2790 precedent's 15-minute shard-cap
|
|
546
|
+
// cancellations do not apply here, and for comparison the `frontmatter`
|
|
547
|
+
// shard in the same run took 9m46s.
|
|
548
|
+
// model-catalog: derivation finds two files never in the old hand list —
|
|
549
|
+
// model-catalog-runtime-defaults.test.cjs and model-catalog-valid-tiers.test.cjs — both
|
|
550
|
+
// directly require model-catalog.cjs and match the "model-catalog*" naming rule. Measured
|
|
551
|
+
// cost: 50ms (1-file) -> 196ms (3-file), in-process, 0 subprocess spawns; still far under
|
|
552
|
+
// the 57s the shard already measured for the single-file set.
|
|
553
|
+
//
|
|
554
|
+
// CI run 33029755081 (2026-08-27, #3915): measured 75.26% (295 killed / 49 survived /
|
|
555
|
+
// 48 no-coverage / 24 runtime-error, totalValid 392). Floor = floor(75.26) - 1 = 74,
|
|
556
|
+
// following this file's documented convention.
|
|
557
|
+
//
|
|
558
|
+
// Why it moved so far: under #3915's tap-runner swap this shard first came back at
|
|
559
|
+
// 57.91% against the old floor of 58. Diagnosis from the mutation report's own JSON:
|
|
560
|
+
// all 24 of its RuntimeError mutants are in the module's load-time catalog bootstrap,
|
|
561
|
+
// so mutating them makes model-catalog.cjs throw at `require`. Under `node --test`
|
|
562
|
+
// that is a failed test file and the mutant counts as Killed; under the tap runner
|
|
563
|
+
// the process dies before emitting TAP, which Stryker classifies RuntimeError and
|
|
564
|
+
// EXCLUDES from the denominator. Adding those 24 back as killed reproduces
|
|
565
|
+
// 248/416 = 59.62 exactly — the pre-swap #3007 number — so detection never
|
|
566
|
+
// regressed, only its classification changed.
|
|
567
|
+
//
|
|
568
|
+
// The floor was NOT lowered to absorb that. 11 new behavioural tests in
|
|
569
|
+
// tests/model-catalog.unit.test.cjs killed 68 previously-surviving mutants (227 ->
|
|
570
|
+
// 295 killed), taking the module from 57.91 to 75.26 — now within striking distance
|
|
571
|
+
// of TARGET_MUTATION_SCORE (80) instead of the 59.62 it sat at before this change.
|
|
572
|
+
//
|
|
573
|
+
// The floor MUST come from a CI shard, never a local run — same rule as every other
|
|
574
|
+
// entry in this file.
|
|
575
|
+
'model-catalog': {
|
|
576
|
+
cjs: 'gsd-core/bin/lib/model-catalog.cjs',
|
|
577
|
+
minScore: 74,
|
|
578
|
+
},
|
|
579
|
+
// state-contract: net-new module from #3227. Without this entry the
|
|
580
|
+
// Stryker gate reports has_work: "false" and SKIPS it entirely — the
|
|
581
|
+
// exact gap #2790 (planning-inspect / plan-document / planning-command-router)
|
|
582
|
+
// and #3007 (model-catalog) each had to fix after the fact.
|
|
583
|
+
//
|
|
584
|
+
// Same #2790 precedent as planning-inspect / model-catalog above: this
|
|
585
|
+
// shard points at tests/state-contract.unit.test.cjs, NOT
|
|
586
|
+
// tests/state-contract.test.cjs — the latter spawns a `gsd-tools` child
|
|
587
|
+
// process per case via runGsdTools, and Stryker's command runner treats
|
|
588
|
+
// the whole `node --test <file>` invocation as ONE test costing whatever
|
|
589
|
+
// its slowest case costs, re-run once per mutant, so it cannot finish
|
|
590
|
+
// inside the 15-minute shard cap. tests/state-contract.unit.test.cjs is
|
|
591
|
+
// spawn-free and in-process.
|
|
592
|
+
//
|
|
593
|
+
// Measured CI score (GitHub Actions run 32769289750, job 97565813640,
|
|
594
|
+
// `Stryker (state-contract)`, PASSED in 2m23s):
|
|
595
|
+
// state-contract 66.25% → floor 65 (below TARGET_MUTATION_SCORE (80) —
|
|
596
|
+
// ratchet candidate like planning-inspect (56) and model-catalog (58):
|
|
597
|
+
// comfortably clears its own floor but has real room to grow. Raise as
|
|
598
|
+
// its tests improve, never lower it.)
|
|
599
|
+
// Floor follows this file's documented rule, minScore = floor(measured) - 1,
|
|
600
|
+
// matching the sibling precedent exactly (57.03 → 56, 76.58 → 75,
|
|
601
|
+
// 95.65 → 94, 59.62 → 58, 66.25 → 65).
|
|
602
|
+
//
|
|
603
|
+
// The floor MUST come from a CI shard, never a local run: local runs count
|
|
604
|
+
// timeouts as kills and inflate scores badly (this file already records
|
|
605
|
+
// prompt-budget 99.6% local vs 68.33% CI, and config-schema 69.7% local vs
|
|
606
|
+
// 54.55% CI).
|
|
607
|
+
// state-contract.test.cjs matches the naming rule and directly requires state-contract.cjs
|
|
608
|
+
// but is the same spawn-heavy integration shape as planning-inspect.test.cjs (446 lines,
|
|
609
|
+
// 16 subprocess-spawn references) — excluded explicitly rather than left to fall through.
|
|
610
|
+
'state-contract': {
|
|
611
|
+
cjs: 'gsd-core/bin/lib/state-contract.cjs',
|
|
612
|
+
excludeTests: ['state-contract.test.cjs'],
|
|
613
|
+
minScore: 65,
|
|
614
|
+
},
|
|
201
615
|
};
|
|
202
616
|
|
|
617
|
+
// Compute the final, derived `tests` array for every COVERED entry. Done once, after the
|
|
618
|
+
// full COVERED literal above is built, so every entry's extraTests/excludeTests declarations
|
|
619
|
+
// are visible to computeModuleTests regardless of source order.
|
|
620
|
+
for (const [moduleName, entry] of Object.entries(COVERED)) {
|
|
621
|
+
entry.tests = computeModuleTests(moduleName, entry);
|
|
622
|
+
}
|
|
623
|
+
|
|
203
624
|
// ── Files that, when changed, invalidate ALL modules ─────────────────────────
|
|
204
625
|
// Changes to the Stryker config, this script itself, or any covered test file
|
|
205
626
|
// affect all mutation scores and must force a full re-run.
|
|
@@ -296,6 +717,11 @@ function buildResult(moduleNames) {
|
|
|
296
717
|
mutate: COVERED[name].cjs,
|
|
297
718
|
tests: COVERED[name].tests.join(' '),
|
|
298
719
|
minScore: COVERED[name].minScore,
|
|
720
|
+
// Per-shard CI job timeout in minutes. Defaults to 15 (the shared per-shard budget);
|
|
721
|
+
// only a module that documents a measured need for more (see the frontmatter entry
|
|
722
|
+
// above) sets a higher value. Threaded through mutation.yml's job-level
|
|
723
|
+
// `timeout-minutes: ${{ matrix.timeoutMinutes }}`.
|
|
724
|
+
timeoutMinutes: COVERED[name].timeoutMinutes || 15,
|
|
299
725
|
}));
|
|
300
726
|
|
|
301
727
|
return {
|
|
@@ -315,6 +741,7 @@ function printHuman(result, changedFiles) {
|
|
|
315
741
|
console.log(` mutate: ${shard.mutate}`);
|
|
316
742
|
console.log(` tests: ${shard.tests}`);
|
|
317
743
|
console.log(` minScore: ${shard.minScore}`);
|
|
744
|
+
console.log(` timeoutMinutes:${shard.timeoutMinutes}`);
|
|
318
745
|
}
|
|
319
746
|
}
|
|
320
747
|
|
|
@@ -375,8 +802,130 @@ function resolveMutationBreak(raw) {
|
|
|
375
802
|
return n;
|
|
376
803
|
}
|
|
377
804
|
|
|
805
|
+
/**
|
|
806
|
+
* Sorted, de-duplicated union of every COVERED module's `tests` array. This
|
|
807
|
+
* is the tap-runner's local/full-run default (see resolveMutationTestFiles
|
|
808
|
+
* below) — the same union stryker.config.mjs's since-removed DEFAULT_TEST_CMD
|
|
809
|
+
* string used to build for the command runner.
|
|
810
|
+
*
|
|
811
|
+
* @returns {string[]}
|
|
812
|
+
*/
|
|
813
|
+
function allCoveredTests() {
|
|
814
|
+
return [...new Set(Object.values(COVERED).flatMap((mod) => mod.tests))].sort();
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// ── MUTATION_TEST_FILES resolver ──────────────────────────────────────────────
|
|
818
|
+
/**
|
|
819
|
+
* Resolves the per-shard tap-runner test-file list from the MUTATION_TEST_FILES
|
|
820
|
+
* env var. Fail-closed twin of resolveMutationBreak above, for #3915's swap from
|
|
821
|
+
* Stryker's `command` runner to `@stryker-mutator/tap-runner`: the tap runner
|
|
822
|
+
* takes an explicit `tap.testFiles` array rather than a shell command, so there
|
|
823
|
+
* is no single string to inject a per-shard test list into — this function is
|
|
824
|
+
* that injection point instead.
|
|
825
|
+
*
|
|
826
|
+
* Fail-closed contract:
|
|
827
|
+
* - undefined → allCoveredTests() (local run: no env set, documented backstop)
|
|
828
|
+
* - non-string → throws (the realistic caller mistake: COVERED[*].tests is an
|
|
829
|
+
* array, but the env var this reads is the SPACE-JOINED STRING form of it —
|
|
830
|
+
* passing the array itself, or any other non-string, is a wiring bug)
|
|
831
|
+
* - set but empty/whitespace-only → throws (CI shard wiring is broken:
|
|
832
|
+
* matrix.tests missing)
|
|
833
|
+
* - otherwise → trim, split on whitespace, de-duplicate, sort, and, per
|
|
834
|
+
* entry: (1) resolve it against the repo root and reject any entry whose
|
|
835
|
+
* resolved path escapes the repo root (e.g. via `../` segments); (2)
|
|
836
|
+
* reject any entry that does not exist on disk, or that exists but is
|
|
837
|
+
* not a regular file (e.g. names a directory) — each failure throws
|
|
838
|
+
* naming the offending entry(ies)
|
|
839
|
+
*
|
|
840
|
+
* This function is the single call site for reading MUTATION_TEST_FILES.
|
|
841
|
+
* stryker.config.mjs imports and calls it, so a bad value must fail
|
|
842
|
+
* immediately rather than silently degrade: the tap runner's own
|
|
843
|
+
* `findTestyLookingFiles` resolves `tap.testFiles` via `glob()`, and a
|
|
844
|
+
* non-matching glob pattern yields an EMPTY list SILENTLY — which would
|
|
845
|
+
* produce a fast, confident, meaningless mutation run (every mutant reported
|
|
846
|
+
* killed or survived against zero tests) instead of a loud error.
|
|
847
|
+
*
|
|
848
|
+
* The `undefined` branch also runs the same on-disk existence check as every
|
|
849
|
+
* other branch, so a stale `extraTests`/`excludeTests` entry in COVERED fails
|
|
850
|
+
* loudly here rather than silently producing a shard pointed at a phantom file.
|
|
851
|
+
*
|
|
852
|
+
* @param {string|undefined} raw - value of process.env.MUTATION_TEST_FILES
|
|
853
|
+
* @returns {string[]} sorted, de-duplicated, existence-checked test file paths
|
|
854
|
+
*/
|
|
855
|
+
function resolveMutationTestFiles(raw) {
|
|
856
|
+
let entries;
|
|
857
|
+
if (raw === undefined) {
|
|
858
|
+
// Local run with no MUTATION_TEST_FILES set — use the derived full-run default.
|
|
859
|
+
entries = allCoveredTests();
|
|
860
|
+
} else if (typeof raw !== 'string') {
|
|
861
|
+
throw new Error(
|
|
862
|
+
`MUTATION_TEST_FILES must be a string (space-joined test file paths), got ${typeof raw} — ` +
|
|
863
|
+
"COVERED[*].tests is an array internally, but the env var this reads is always the " +
|
|
864
|
+
'SPACE-JOINED STRING form of it; passing the array (or any other non-string) directly is a wiring bug'
|
|
865
|
+
);
|
|
866
|
+
} else if (raw.trim() === '') {
|
|
867
|
+
throw new Error(
|
|
868
|
+
'MUTATION_TEST_FILES is set but empty — CI shard wiring is broken (matrix.tests missing?)'
|
|
869
|
+
);
|
|
870
|
+
} else {
|
|
871
|
+
entries = [...new Set(raw.trim().split(/\s+/))].sort();
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const repoRoot = path.join(__dirname, '..');
|
|
875
|
+
|
|
876
|
+
const escaped = [];
|
|
877
|
+
const missing = [];
|
|
878
|
+
const notFile = [];
|
|
879
|
+
for (const entry of entries) {
|
|
880
|
+
const resolved = path.resolve(repoRoot, entry);
|
|
881
|
+
const rel = path.relative(repoRoot, resolved);
|
|
882
|
+
if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
883
|
+
escaped.push(entry);
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (!fs.existsSync(resolved)) {
|
|
887
|
+
missing.push(entry);
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (!fs.statSync(resolved).isFile()) {
|
|
891
|
+
notFile.push(entry);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (escaped.length > 0) {
|
|
896
|
+
throw new Error(
|
|
897
|
+
`MUTATION_TEST_FILES names ${escaped.length} entry(ies) that escape the repo root: ${escaped.join(', ')}`
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
if (missing.length > 0) {
|
|
901
|
+
throw new Error(
|
|
902
|
+
`MUTATION_TEST_FILES names ${missing.length} file(s) that do not exist on disk: ${missing.join(', ')}`
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
if (notFile.length > 0) {
|
|
906
|
+
throw new Error(
|
|
907
|
+
`MUTATION_TEST_FILES names ${notFile.length} entry(ies) that are not a regular file: ${notFile.join(', ')}`
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
return entries;
|
|
912
|
+
}
|
|
913
|
+
|
|
378
914
|
// Export internals for programmatic use (tests/mutation-matrix-ratchet.test.cjs).
|
|
379
915
|
// The require.main guard prevents main() from running when this file is require()d.
|
|
380
|
-
module.exports = {
|
|
916
|
+
module.exports = {
|
|
917
|
+
COVERED,
|
|
918
|
+
TARGET_MUTATION_SCORE,
|
|
919
|
+
resolveMutationBreak,
|
|
920
|
+
allCoveredTests,
|
|
921
|
+
resolveMutationTestFiles,
|
|
922
|
+
readStdinSync,
|
|
923
|
+
// Derivation-engine internals — exported for tests/mutation-test-derivation-drift.test.cjs
|
|
924
|
+
// and scripts/lint-mutation-test-derivation-drift.cjs.
|
|
925
|
+
findRequiringTestFiles,
|
|
926
|
+
matchesModuleNamingRule,
|
|
927
|
+
deriveNamedTests,
|
|
928
|
+
computeModuleTests,
|
|
929
|
+
};
|
|
381
930
|
|
|
382
931
|
if (require.main === module) runMain(main);
|