@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
package/scripts/base64-scan.sh
CHANGED
|
@@ -9,12 +9,29 @@
|
|
|
9
9
|
# scripts/base64-scan.sh --file path/to/file # Scan a single file
|
|
10
10
|
# scripts/base64-scan.sh --dir agents/ # Scan all files in a directory
|
|
11
11
|
#
|
|
12
|
-
# Exit codes:
|
|
13
|
-
# 0
|
|
14
|
-
# 1
|
|
15
|
-
#
|
|
12
|
+
# Exit codes (ADR-3889, #3908 — registered in gsd-core/bin/shared/exit-codes.json):
|
|
13
|
+
# 0 = clean
|
|
14
|
+
# 1 = findings detected
|
|
15
|
+
# $EXIT_USAGE (64) = usage error (bad argv, missing --file/--dir target)
|
|
16
|
+
# $EXIT_NO_INPUT (66) = ran; scope established; zero files in scope (genuinely empty)
|
|
17
|
+
# $EXIT_UNAVAILABLE (69) = could not establish scope (bad ref, not a repo, unreadable dir)
|
|
16
18
|
set -euo pipefail
|
|
17
19
|
|
|
20
|
+
# ─── Exit-code registry (ADR-3889, #3908) ────────────────────────────────────
|
|
21
|
+
# Resolved relative to THIS script's location, not the caller's cwd. Loud,
|
|
22
|
+
# non-zero failure if the fragment is missing — never fall back to a guessed
|
|
23
|
+
# literal integer, and never let a missing registry silently degrade to
|
|
24
|
+
# exit 0.
|
|
25
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
|
+
EXIT_CODES_SH="$SCRIPT_DIR/../gsd-core/bin/shared/exit-codes.sh"
|
|
27
|
+
if [[ ! -f "$EXIT_CODES_SH" ]]; then
|
|
28
|
+
echo "base64-scan: FATAL: exit-code registry not found at $EXIT_CODES_SH" >&2
|
|
29
|
+
echo " Regenerate with: node scripts/gen-exit-code-registry.cjs --write" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
# shellcheck disable=SC1090
|
|
33
|
+
. "$EXIT_CODES_SH"
|
|
34
|
+
|
|
18
35
|
# ── Locale hardening (#116) ───────────────────────────────────────────────────
|
|
19
36
|
# BSD tr (macOS) treats input bytes as multi-byte characters under any UTF-8
|
|
20
37
|
# locale. When the input to `tr -cd '[:print:]'` contains bytes that are not
|
|
@@ -180,7 +197,31 @@ collect_files() {
|
|
|
180
197
|
case "$mode" in
|
|
181
198
|
--diff)
|
|
182
199
|
local base="${1:-origin/main}"
|
|
183
|
-
git
|
|
200
|
+
# Run git separately from the filter pipe so its OWN exit status (not
|
|
201
|
+
# grep's) decides whether the diff could be established. stdout and
|
|
202
|
+
# stderr are captured SEPARATELY (never merged with `2>&1`) so that a
|
|
203
|
+
# warning git writes to stderr on an otherwise successful diff can
|
|
204
|
+
# never be mistaken for a filename in the file list. On failure the
|
|
205
|
+
# captured stderr is emitted as the diagnostic; on success it is
|
|
206
|
+
# forwarded as a warning, never folded into the file list.
|
|
207
|
+
# `|| true` on the filter below is CORRECT (not gratuitous): `grep -v`
|
|
208
|
+
# exits 1 when every line is filtered out (e.g. an all-images diff),
|
|
209
|
+
# which is a legitimate empty result, not a failure to run.
|
|
210
|
+
local raw status err_file
|
|
211
|
+
err_file=$(mktemp)
|
|
212
|
+
raw=$(git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>"$err_file")
|
|
213
|
+
status=$?
|
|
214
|
+
if (( status != 0 )); then
|
|
215
|
+
cat "$err_file" >&2
|
|
216
|
+
rm -f "$err_file"
|
|
217
|
+
exit "$EXIT_UNAVAILABLE"
|
|
218
|
+
fi
|
|
219
|
+
if [[ -s "$err_file" ]]; then
|
|
220
|
+
echo "Warning: git diff emitted stderr output:" >&2
|
|
221
|
+
cat "$err_file" >&2
|
|
222
|
+
fi
|
|
223
|
+
rm -f "$err_file"
|
|
224
|
+
printf '%s\n' "$raw" \
|
|
184
225
|
| grep -vE '\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|otf|zip|tar|gz|pdf)$' || true
|
|
185
226
|
;;
|
|
186
227
|
--file)
|
|
@@ -188,24 +229,41 @@ collect_files() {
|
|
|
188
229
|
echo "$1"
|
|
189
230
|
else
|
|
190
231
|
echo "Error: file not found: $1" >&2
|
|
191
|
-
exit
|
|
232
|
+
exit "$EXIT_USAGE"
|
|
192
233
|
fi
|
|
193
234
|
;;
|
|
194
235
|
--dir)
|
|
195
236
|
local dir="$1"
|
|
196
237
|
if [[ ! -d "$dir" ]]; then
|
|
197
238
|
echo "Error: directory not found: $dir" >&2
|
|
198
|
-
exit
|
|
239
|
+
exit "$EXIT_USAGE"
|
|
240
|
+
fi
|
|
241
|
+
# Same treatment as --diff: a `find` that fails (e.g. permission
|
|
242
|
+
# denied) must not be reported as an empty directory, and stdout/stderr
|
|
243
|
+
# are captured separately so a stderr warning never enters the file list.
|
|
244
|
+
local raw status err_file
|
|
245
|
+
err_file=$(mktemp)
|
|
246
|
+
raw=$(find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
|
|
247
|
+
! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>"$err_file")
|
|
248
|
+
status=$?
|
|
249
|
+
if (( status != 0 )); then
|
|
250
|
+
cat "$err_file" >&2
|
|
251
|
+
rm -f "$err_file"
|
|
252
|
+
exit "$EXIT_UNAVAILABLE"
|
|
253
|
+
fi
|
|
254
|
+
if [[ -s "$err_file" ]]; then
|
|
255
|
+
echo "Warning: find emitted stderr output:" >&2
|
|
256
|
+
cat "$err_file" >&2
|
|
199
257
|
fi
|
|
200
|
-
|
|
201
|
-
|
|
258
|
+
rm -f "$err_file"
|
|
259
|
+
printf '%s\n' "$raw"
|
|
202
260
|
;;
|
|
203
261
|
--stdin)
|
|
204
262
|
cat
|
|
205
263
|
;;
|
|
206
264
|
*)
|
|
207
265
|
echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
|
|
208
|
-
exit
|
|
266
|
+
exit "$EXIT_USAGE"
|
|
209
267
|
;;
|
|
210
268
|
esac
|
|
211
269
|
}
|
|
@@ -309,7 +367,7 @@ extract_and_check_blobs() {
|
|
|
309
367
|
main() {
|
|
310
368
|
if [[ $# -eq 0 ]]; then
|
|
311
369
|
echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
|
|
312
|
-
exit
|
|
370
|
+
exit "$EXIT_USAGE"
|
|
313
371
|
fi
|
|
314
372
|
|
|
315
373
|
load_ignorelist
|
|
@@ -321,8 +379,12 @@ main() {
|
|
|
321
379
|
files=$(collect_files "$mode" "$@")
|
|
322
380
|
|
|
323
381
|
if [[ -z "$files" ]]; then
|
|
382
|
+
# collect_files already exited (UNAVAILABLE/USAGE) for anything that
|
|
383
|
+
# could not establish scope. Reaching here with an empty result means
|
|
384
|
+
# scope WAS established and is genuinely empty (e.g. an all-images
|
|
385
|
+
# diff) — that is NO_INPUT, not a silent clean pass.
|
|
324
386
|
echo "base64-scan: no files to scan"
|
|
325
|
-
exit
|
|
387
|
+
exit "$EXIT_NO_INPUT"
|
|
326
388
|
fi
|
|
327
389
|
|
|
328
390
|
local total=0
|
package/scripts/build-hooks.js
CHANGED
|
@@ -61,6 +61,8 @@ const HOOKS_TO_COPY = [
|
|
|
61
61
|
'gsd-prompt-guard.js',
|
|
62
62
|
'gsd-read-guard.js',
|
|
63
63
|
'gsd-read-injection-scanner.js',
|
|
64
|
+
// Secret-file read guard (#4221) — replaces the installer's Read(.env*) deny rules
|
|
65
|
+
'gsd-secret-read-guard.js',
|
|
64
66
|
'gsd-statusline.js',
|
|
65
67
|
'gsd-update-banner.js',
|
|
66
68
|
'gsd-workflow-guard.js',
|
|
@@ -71,6 +73,16 @@ const HOOKS_TO_COPY = [
|
|
|
71
73
|
'gsd-session-state.sh',
|
|
72
74
|
'gsd-validate-commit.sh',
|
|
73
75
|
'gsd-phase-boundary.sh',
|
|
76
|
+
// Portable node resolver (#3662). Managed JS hook commands under
|
|
77
|
+
// --portable-hooks route through it (bash <resolver> <baked-node>
|
|
78
|
+
// <script>) so node resolves at hook-fire time in every environment
|
|
79
|
+
// sharing the config root. It IS registered in MANAGED_HOOKS
|
|
80
|
+
// (managed-hooks-registry.cjs) for staleness tracking like every other
|
|
81
|
+
// shipped .sh hook, and install.js stamps its {{GSD_VERSION}} header the
|
|
82
|
+
// same way (#4076 — the prior comment here claimed the opposite on both
|
|
83
|
+
// counts, which is why the header was missing and staleness detection was
|
|
84
|
+
// permanently broken for this file).
|
|
85
|
+
'gsd-node-runner.sh',
|
|
74
86
|
// Graphify auto-update hook (#3347 / PR #3557 / #3579). Opt-in via
|
|
75
87
|
// .planning/config.json graphify.auto_update; off by default.
|
|
76
88
|
'gsd-graphify-update.sh'
|
|
@@ -65,7 +65,7 @@ const TRACKED_PREFIXES = [
|
|
|
65
65
|
const TRACKED_EXACT = new Set(['bin/install.js', 'package.json']);
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Paths CONTEXT.md documents whose ABSENCE is the healthy steady state
|
|
68
|
+
* Paths CONTEXT.md documents whose ABSENCE is the healthy steady state.
|
|
69
69
|
*
|
|
70
70
|
* `TRACKED_PREFIXES` skips claims this gate *cannot* check. This is the narrower
|
|
71
71
|
* third case: a claim it must not check, because "does not exist" is the correct
|
|
@@ -78,14 +78,43 @@ const TRACKED_EXACT = new Set(['bin/install.js', 'package.json']);
|
|
|
78
78
|
* shipping an empty stub. So the file is absent on a healthy `next` and present
|
|
79
79
|
* only inside a PR that needs it, and asserting either way is wrong.
|
|
80
80
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* `
|
|
84
|
-
*
|
|
85
|
-
* by
|
|
86
|
-
*
|
|
81
|
+
* The emitted-attribution family (`tests/fixtures/golden-install-parity`,
|
|
82
|
+
* `tests/golden-install-parity.test.cjs`, `scripts/gen-golden-install-parity-zcode.cjs`,
|
|
83
|
+
* `tests/agent-size-baseline.json`, `tests/workflow-size-baseline.json`,
|
|
84
|
+
* `scripts/git-merge-regen-driver.cjs`, `scripts/update-size-baseline.cjs`) was RETIRED
|
|
85
|
+
* by the #2724 cutover — the differential attribution check replaced the committed
|
|
86
|
+
* baselines and their generator/bridge tooling. CONTEXT.md's RULESET.EMITTED_ATTRIBUTION
|
|
87
|
+
* predicate documents that retirement ("Historically …"), so the mentions are history,
|
|
88
|
+
* not live claims, and their absence is exactly the healthy state the retirement
|
|
89
|
+
* produced. The whole documented family is listed, not just the members today's tick
|
|
90
|
+
* parity happens to hide — the two tooling paths were invisible only because of where
|
|
91
|
+
* line 585's inner backticks sat, which is the #2778 luck this gate must not rely on.
|
|
92
|
+
*
|
|
93
|
+
* `scripts/eslint-rules` appears only inside a CONTRASTIVE mention ("the local
|
|
94
|
+
* plugin lives at `eslint-rules/` (repo root, NOT `scripts/eslint-rules/`)") — the
|
|
95
|
+
* predicate asserts where the directory is NOT, so non-existence is the claim
|
|
96
|
+
* being made, not drift away from one.
|
|
97
|
+
*
|
|
98
|
+
* Until #2778 this set's first entry passed only by accident: CONTEXT.md's
|
|
99
|
+
* `RULESET.` entries are themselves backtick-wrapped and contain backticks, so the
|
|
100
|
+
* sequential pairing in `extractTrackedRefs` happened to leave this token outside a
|
|
101
|
+
* code span. Any edit that shifted the parity — such as #2778's own — exposed it.
|
|
102
|
+
* A gate that passes by luck is not passing; naming the exemption makes the intent
|
|
103
|
+
* explicit and survives the next edit. #3604 removed the luck itself (pairing is
|
|
104
|
+
* per line), which is what surfaced the entries above: each names a path whose
|
|
105
|
+
* absence is deliberate, so each is exempted by name for the same reason.
|
|
87
106
|
*/
|
|
88
|
-
const INTENTIONALLY_ABSENT = new Set([
|
|
107
|
+
const INTENTIONALLY_ABSENT = new Set([
|
|
108
|
+
'tests/emitted-drift-ack.json',
|
|
109
|
+
'tests/fixtures/golden-install-parity',
|
|
110
|
+
'tests/golden-install-parity.test.cjs',
|
|
111
|
+
'scripts/gen-golden-install-parity-zcode.cjs',
|
|
112
|
+
'tests/agent-size-baseline.json',
|
|
113
|
+
'tests/workflow-size-baseline.json',
|
|
114
|
+
'scripts/git-merge-regen-driver.cjs',
|
|
115
|
+
'scripts/update-size-baseline.cjs',
|
|
116
|
+
'scripts/eslint-rules',
|
|
117
|
+
]);
|
|
89
118
|
|
|
90
119
|
/**
|
|
91
120
|
* Shape a backticked token must have to even be considered a path candidate:
|
|
@@ -119,18 +148,51 @@ function isWithinRoot(token) {
|
|
|
119
148
|
/**
|
|
120
149
|
* Every distinct, trackable file-path token referenced in `text`, with any
|
|
121
150
|
* trailing `:<line>` suffix stripped.
|
|
151
|
+
*
|
|
152
|
+
* #3604: pairing is per LINE, not over the whole text. A single whole-text pass
|
|
153
|
+
* `[^`]+` crosses newlines, so one odd-backtick line (the `RULESET.*` predicate
|
|
154
|
+
* format is backtick-wrapped and its values sometimes contain backticks) shifted
|
|
155
|
+
* the pairing of every later line — a tracked token's visibility depended on
|
|
156
|
+
* where it sat, which is how a renamed test file stayed invisible for weeks.
|
|
157
|
+
*
|
|
158
|
+
* The fact-store predicate lines (one `CLASS.subkey=value` fact per line,
|
|
159
|
+
* backtick-wrapped as a whole) carry REAL paths in their values; the span itself
|
|
160
|
+
* is not path-shaped (spaces, `=`), so a second pass harvests path-shaped tracked
|
|
161
|
+
* tokens from inside any predicate-shaped span rather than letting the outer
|
|
162
|
+
* wrapper hide them.
|
|
163
|
+
*
|
|
164
|
+
* Fragment guards: a real path in this repo never ends in `-` or `.` — those are
|
|
165
|
+
* remnants of glob/template mentions (`scripts/gen-*.cjs`, `tests/foo.*.test.cjs`)
|
|
166
|
+
* split at the `*` — and `NNNN` is the ADR filename template token
|
|
167
|
+
* (CONTRIBUTING's "Do not compute a next number locally"), never a real path.
|
|
122
168
|
*/
|
|
123
169
|
function extractTrackedRefs(text) {
|
|
124
170
|
const tokens = new Set();
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
while ((m = re.exec(text)) !== null) {
|
|
128
|
-
const raw = m[1];
|
|
129
|
-
if (!PATH_TOKEN_RE.test(raw)) continue;
|
|
171
|
+
const add = (raw) => {
|
|
172
|
+
if (!PATH_TOKEN_RE.test(raw)) return;
|
|
130
173
|
const token = raw.replace(/:\d+$/, '');
|
|
131
|
-
|
|
132
|
-
|
|
174
|
+
// Fragment guard, on the EMITTED token (after the :line strip, so
|
|
175
|
+
// `src/foo-:12` is judged on `src/foo-`): glob/template remnants end in `-`
|
|
176
|
+
// or `.`, a real path in this repo never does.
|
|
177
|
+
if (!/[A-Za-z0-9_]$/.test(token)) return;
|
|
178
|
+
if (token.includes('NNNN')) return;
|
|
179
|
+
if (!isTracked(token)) return;
|
|
180
|
+
if (!isWithinRoot(token)) return;
|
|
133
181
|
tokens.add(token);
|
|
182
|
+
};
|
|
183
|
+
const subTokenRe = /[\w.-]+(?:\/[\w.-]+)*/g;
|
|
184
|
+
for (const line of text.split(/\r?\n/)) {
|
|
185
|
+
const re = /`([^`]+)`/g;
|
|
186
|
+
let m;
|
|
187
|
+
while ((m = re.exec(line)) !== null) {
|
|
188
|
+
const span = m[1];
|
|
189
|
+
if (PATH_TOKEN_RE.test(span)) {
|
|
190
|
+
add(span);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!/^[A-Z][A-Za-z0-9_.-]*=/.test(span)) continue;
|
|
194
|
+
for (const sub of span.matchAll(subTokenRe)) add(sub[0]);
|
|
195
|
+
}
|
|
134
196
|
}
|
|
135
197
|
return tokens;
|
|
136
198
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* scripts/check-mutation-score-ratchet.cjs
|
|
6
|
+
*
|
|
7
|
+
* #3881 follow-up ("one YAML parser" mutation-matrix piece 3): makes each
|
|
8
|
+
* module's `minScore` floor (scripts/mutation-matrix.cjs COVERED) ratchet
|
|
9
|
+
* UPWARD instead of sitting wherever it was last hand-set.
|
|
10
|
+
*
|
|
11
|
+
* Two halves of "never lowerable without a reasoned marker":
|
|
12
|
+
* 1. `tests/mutation-matrix-ratchet.test.cjs`'s RATCHET_BASELINE already
|
|
13
|
+
* enforces the DOWN side: any change to a module's minScore — up or
|
|
14
|
+
* down — must land in the same diff as a matching RATCHET_BASELINE
|
|
15
|
+
* edit, making the change visible in code review (mirrors this repo's
|
|
16
|
+
* other shrink-only baselines, e.g. scripts/lint-unreachable-guard-drift.cjs's
|
|
17
|
+
* $comment convention: a number cannot move silently).
|
|
18
|
+
* 2. THIS script enforces the UP side: after a real CI Stryker run, if the
|
|
19
|
+
* module's ACHIEVED mutation score exceeds its declared floor by more
|
|
20
|
+
* than RATCHET_SLACK points, that is not "run-to-run variance" (this
|
|
21
|
+
* file's own header documents variance as 1-2 points; see
|
|
22
|
+
* scripts/mutation-matrix.cjs's "Floors are measured scores minus 1-2
|
|
23
|
+
* pts") — it is unclaimed headroom, and the floor should have been
|
|
24
|
+
* raised. Exits 1 telling the author exactly what to raise it to, so a
|
|
25
|
+
* module that improves cannot silently keep a stale, low bar forever.
|
|
26
|
+
*
|
|
27
|
+
* Consumes Stryker's `json` reporter output (mutation-testing-report-schema
|
|
28
|
+
* document; stryker.config.mjs's `reporters` includes `'json'`, default path
|
|
29
|
+
* `reports/mutation/mutation.json`) via `mutation-testing-metrics` — the same
|
|
30
|
+
* package Stryker itself uses internally to compute a report's score — rather
|
|
31
|
+
* than re-deriving killed/survived counts by hand.
|
|
32
|
+
*
|
|
33
|
+
* Wired into `.github/workflows/mutation.yml`'s `mutate` job as a step AFTER
|
|
34
|
+
* `npx stryker run`, gated on the shard having passed (a shard that failed
|
|
35
|
+
* MUTATION_BREAK is a floor problem, not a ratchet problem — Stryker's own
|
|
36
|
+
* exit code already reports that).
|
|
37
|
+
*
|
|
38
|
+
* CLI:
|
|
39
|
+
* node scripts/check-mutation-score-ratchet.cjs --module <name> [--report <path>] [--matrix <path>]
|
|
40
|
+
* `--report` defaults to reports/mutation/mutation.json (Stryker's own default).
|
|
41
|
+
* `--matrix` defaults to this repo's own scripts/mutation-matrix.cjs; it is an injectable seam
|
|
42
|
+
* so tests can point the CLI at a synthetic COVERED fixture instead of the real, live-ratcheting
|
|
43
|
+
* config (see tests/mutation-score-ratchet.test.cjs — a test asserting this script's fail/pass
|
|
44
|
+
* behaviour must never hardcode a real module's numeric floor, because that floor is exactly
|
|
45
|
+
* what this mechanism exists to move).
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
const fs = require('node:fs');
|
|
49
|
+
const path = require('node:path');
|
|
50
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
51
|
+
|
|
52
|
+
// Slack, in mutation-score points, above a module's declared minScore floor before this
|
|
53
|
+
// ratchet demands the floor be raised. Deliberately larger than the 1-2 point run-to-run
|
|
54
|
+
// TIMEOUT variance margin scripts/mutation-matrix.cjs's floors are already set with (its own
|
|
55
|
+
// header: "Floors are measured scores minus 1-2 pts for run-to-run variance") — that smaller
|
|
56
|
+
// margin is calibrated for the noise BETWEEN two runs of the SAME test list against the SAME
|
|
57
|
+
// source. RATCHET_SLACK instead has to tolerate the noise this file's own history already
|
|
58
|
+
// shows for the frontmatter module across genuine measurement events (63.35 -> 66.67 ->
|
|
59
|
+
// 60.58 -> pending, each a real CI measurement, not pure jitter, spanning >3 points on its
|
|
60
|
+
// own). 5 points sits comfortably above that observed band without being so wide that a
|
|
61
|
+
// module sitting well above TARGET_MUTATION_SCORE (80) could coast for years without its
|
|
62
|
+
// floor ever being asked to move.
|
|
63
|
+
const RATCHET_SLACK = 5;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Pure: does `achievedScore` (0-100) exceed `floor` (minScore) by more than `slack`?
|
|
67
|
+
* Returns `{ shouldRatchet, suggestedFloor }` — `suggestedFloor` follows the SAME formula
|
|
68
|
+
* scripts/mutation-matrix.cjs's own header documents for setting a floor from a measured
|
|
69
|
+
* score: `floor(measured) - 1`.
|
|
70
|
+
*/
|
|
71
|
+
function evaluateRatchet(achievedScore, floor, slack = RATCHET_SLACK) {
|
|
72
|
+
const shouldRatchet = achievedScore - floor > slack;
|
|
73
|
+
return {
|
|
74
|
+
shouldRatchet,
|
|
75
|
+
suggestedFloor: shouldRatchet ? Math.floor(achievedScore) - 1 : floor,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Extract the achieved mutation score from a Stryker `json` reporter document via
|
|
81
|
+
* `mutation-testing-metrics` (the same library Stryker itself uses to compute scores).
|
|
82
|
+
* Throws a descriptive error if the document has no scoreable mutants at all (an empty
|
|
83
|
+
* `files` map, or every file having zero mutants) — that is a wiring bug in the caller
|
|
84
|
+
* (wrong report path, or the shard mutated nothing), never a score of 0.
|
|
85
|
+
*/
|
|
86
|
+
function extractAchievedScore(report) {
|
|
87
|
+
// Lazy require: this dependency is only needed on the actual score-extraction path, not
|
|
88
|
+
// when this module is required purely for evaluateRatchet (used by unit tests without the
|
|
89
|
+
// mutation-testing-metrics package's schema-validation overhead).
|
|
90
|
+
const { calculateMutationTestMetrics } = require('mutation-testing-metrics');
|
|
91
|
+
const metrics = calculateMutationTestMetrics(report);
|
|
92
|
+
const score = metrics && metrics.systemUnderTestMetrics && metrics.systemUnderTestMetrics.metrics
|
|
93
|
+
? metrics.systemUnderTestMetrics.metrics.mutationScore
|
|
94
|
+
: undefined;
|
|
95
|
+
if (typeof score !== 'number' || Number.isNaN(score)) {
|
|
96
|
+
throw new Error('mutation report produced no scoreable mutants — check --report points at the right shard\'s reports/mutation/mutation.json');
|
|
97
|
+
}
|
|
98
|
+
return score;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseArgs(argv) {
|
|
102
|
+
const out = { module: null, report: 'reports/mutation/mutation.json', matrix: null };
|
|
103
|
+
for (let i = 0; i < argv.length; i++) {
|
|
104
|
+
const arg = argv[i];
|
|
105
|
+
if (arg === '--module') {
|
|
106
|
+
out.module = argv[++i];
|
|
107
|
+
} else if (arg === '--report') {
|
|
108
|
+
out.report = argv[++i];
|
|
109
|
+
} else if (arg === '--matrix') {
|
|
110
|
+
out.matrix = argv[++i];
|
|
111
|
+
} else {
|
|
112
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!out.module) throw new Error('--module <name> is required');
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function main() {
|
|
120
|
+
const args = parseArgs(process.argv.slice(2));
|
|
121
|
+
const matrixPath = args.matrix ? path.resolve(args.matrix) : path.join(__dirname, 'mutation-matrix.cjs');
|
|
122
|
+
const { COVERED } = require(matrixPath);
|
|
123
|
+
const entry = COVERED[args.module];
|
|
124
|
+
if (!entry) {
|
|
125
|
+
throw new ExitError(1, `check-mutation-score-ratchet: unknown module '${args.module}' — not in ${args.matrix ? matrixPath : 'scripts/mutation-matrix.cjs'} COVERED`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const reportPath = path.resolve(args.report);
|
|
129
|
+
if (!fs.existsSync(reportPath)) {
|
|
130
|
+
throw new ExitError(1, `check-mutation-score-ratchet: report not found at ${reportPath} — run \`npx stryker run\` first (with the 'json' reporter enabled)`);
|
|
131
|
+
}
|
|
132
|
+
let report;
|
|
133
|
+
try {
|
|
134
|
+
report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
135
|
+
} catch (err) {
|
|
136
|
+
throw new ExitError(1, `check-mutation-score-ratchet: ${reportPath} is not valid JSON: ${err.message}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const achieved = extractAchievedScore(report);
|
|
140
|
+
const { shouldRatchet, suggestedFloor } = evaluateRatchet(achieved, entry.minScore);
|
|
141
|
+
|
|
142
|
+
if (!shouldRatchet) {
|
|
143
|
+
console.log(`ok mutation-score-ratchet: ${args.module} achieved ${achieved.toFixed(2)}% against floor ${entry.minScore} (within ${RATCHET_SLACK}-point slack)`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.error(`mutation-score-ratchet: ${args.module} achieved ${achieved.toFixed(2)}%, more than ${RATCHET_SLACK} points above its declared floor (${entry.minScore}).`);
|
|
148
|
+
console.error(` This is unclaimed headroom, not run-to-run variance — raise the floor.`);
|
|
149
|
+
console.error(` remedy: in scripts/mutation-matrix.cjs, set COVERED['${args.module}'].minScore = ${suggestedFloor} (floor(achieved) - 1, this file's own convention),`);
|
|
150
|
+
console.error(` and update the matching entry in tests/mutation-matrix-ratchet.test.cjs's RATCHET_BASELINE in the same diff.`);
|
|
151
|
+
throw new ExitError(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { RATCHET_SLACK, evaluateRatchet, extractAchievedScore };
|
|
155
|
+
|
|
156
|
+
if (require.main === module) runMain(main);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const { computeElapsedPct, isNearCap, formatNearCapNotice } = require('./lib/ci-job-timing.cjs');
|
|
6
|
+
|
|
7
|
+
function run(env = process.env, nowMs = Date.now(), appendFileSync = fs.appendFileSync) {
|
|
8
|
+
const label = env.CI_JOB_LABEL;
|
|
9
|
+
const startEpochMs = Number(env.CI_JOB_START_EPOCH_MS);
|
|
10
|
+
const timeoutMinutes = Number(env.CI_JOB_TIMEOUT_MINUTES);
|
|
11
|
+
|
|
12
|
+
if (!label || !Number.isFinite(startEpochMs) || !Number.isFinite(timeoutMinutes) || timeoutMinutes <= 0) {
|
|
13
|
+
console.error(
|
|
14
|
+
'ci-check-job-near-cap: missing or invalid CI_JOB_LABEL / CI_JOB_START_EPOCH_MS / '
|
|
15
|
+
+ 'CI_JOB_TIMEOUT_MINUTES — skipping near-cap check (advisory only, not a failure).',
|
|
16
|
+
);
|
|
17
|
+
return { skipped: true };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { elapsedMs, capMs, pct } = computeElapsedPct({
|
|
21
|
+
startedAt: new Date(startEpochMs).toISOString(),
|
|
22
|
+
completedAt: new Date(nowMs).toISOString(),
|
|
23
|
+
timeoutMinutes,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!isNearCap(pct)) {
|
|
27
|
+
return { skipped: false, nearCap: false, pct };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { warningLine, summaryMarkdown } = formatNearCapNotice({ label, pct, elapsedMs, capMs });
|
|
31
|
+
console.log(warningLine);
|
|
32
|
+
|
|
33
|
+
if (env.GITHUB_STEP_SUMMARY) {
|
|
34
|
+
try {
|
|
35
|
+
appendFileSync(env.GITHUB_STEP_SUMMARY, `${summaryMarkdown}\n`);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error(`ci-check-job-near-cap: could not write GITHUB_STEP_SUMMARY: ${err.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { skipped: false, nearCap: true, pct };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { run };
|
|
45
|
+
|
|
46
|
+
if (require.main === module) {
|
|
47
|
+
run();
|
|
48
|
+
process.exitCode = 0;
|
|
49
|
+
}
|