@opengsd/gsd-core 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -9
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +15 -55
- package/agents/gsd-planner.md +6 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +609 -134
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/quick.md +8 -4
- package/gsd-core/bin/gsd-tools.cjs +567 -51
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +336 -95
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-validator.cjs +205 -18
- package/gsd-core/bin/lib/check-command-router.cjs +145 -5
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +543 -44
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +118 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +84 -7
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/frontmatter.cjs +840 -305
- package/gsd-core/bin/lib/gap-checker.cjs +27 -3
- package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +120 -41
- package/gsd-core/bin/lib/install-engine.cjs +68 -3
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
- package/gsd-core/bin/lib/install-profiles.cjs +78 -4
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +160 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-table.cjs +52 -4
- package/gsd-core/bin/lib/milestone.cjs +90 -5
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +70 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +405 -84
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
- package/gsd-core/bin/lib/roadmap.cjs +74 -19
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
- package/gsd-core/bin/lib/smart-entry.cjs +12 -22
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +186 -0
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +517 -101
- package/gsd-core/bin/lib/state.cjs +946 -163
- package/gsd-core/bin/lib/surface.cjs +10 -2
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +1376 -125
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification.cjs +14 -1
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +95 -40
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +3 -2
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +1 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +37 -8
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +2 -2
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +3 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +4 -9
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +10 -26
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +121 -40
- package/gsd-core/workflows/complete-milestone.md +15 -10
- package/gsd-core/workflows/debug.md +5 -3
- package/gsd-core/workflows/diagnose-issues.md +12 -6
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +4 -9
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +38 -54
- package/gsd-core/workflows/execute-plan.md +17 -12
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +2 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +3 -6
- package/gsd-core/workflows/import.md +14 -11
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +44 -11
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +2 -2
- package/gsd-core/workflows/new-milestone.md +9 -21
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +12 -26
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +121 -42
- package/gsd-core/workflows/plan-review-convergence.md +46 -9
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +27 -12
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +20 -29
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +193 -15
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +2 -2
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +3 -5
- package/gsd-core/workflows/ship.md +12 -6
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +23 -1
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +5 -5
- package/gsd-core/workflows/ui-phase.md +10 -16
- package/gsd-core/workflows/ui-review.md +6 -10
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +6 -10
- package/gsd-core/workflows/validate-phase.md +2 -2
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +57 -18
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +16 -7
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +17 -8
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +215 -26
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +34 -16
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +16 -7
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +17 -8
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +215 -26
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +34 -16
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +12 -7
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +5 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +45 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +134 -1
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +21 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +122 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/prompt-injection-scan.sh +75 -14
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// hooks/lib/git-probe.js — classify a bounded spawnSync(git, ...) result,
|
|
3
|
+
// distinguishing a GENUINE negative answer (git ran and said "no") from
|
|
4
|
+
// "could not determine" (timeout / spawn failure / signal kill) — #3911.
|
|
5
|
+
//
|
|
6
|
+
// Proven defect: hooks/gsd-worktree-path-guard.js, hooks/gsd-workflow-guard.js,
|
|
7
|
+
// and hooks/gsd-windsurf-pre-write.js each treat spawnSync(git, ..., {timeout})
|
|
8
|
+
// returning a non-zero/empty result as "not applicable" and allow silently.
|
|
9
|
+
// A macOS CI run showed three deny cases land at 2084ms/2112ms/2177ms — just
|
|
10
|
+
// past the 2000ms budget — each returning exit 0 with EMPTY stdout AND EMPTY
|
|
11
|
+
// stderr: a timeout is INDISTINGUISHABLE, at the call site, from git cleanly
|
|
12
|
+
// answering "not a repo" / "not this worktree" unless the spawnSync() result
|
|
13
|
+
// itself is inspected for `error`/`signal`, not just `status`/`stdout`.
|
|
14
|
+
//
|
|
15
|
+
// This module does not change any hook's decision or exit code — it only
|
|
16
|
+
// answers "was this probe's answer real?" so a caller can emit a diagnostic
|
|
17
|
+
// on the undetermined branch while keeping the existing fail-open behavior.
|
|
18
|
+
//
|
|
19
|
+
// Classification mirrors tests/helpers/process-seam.cjs's OUTCOME vocabulary
|
|
20
|
+
// (EXITED vs TIMED_OUT/SPAWN_FAILED/KILLED) — same evidence-over-classification
|
|
21
|
+
// ordering (a populated `status` outranks an attached `error`, since Node can
|
|
22
|
+
// report `error.code === 'ETIMEDOUT'` on a result that also carried a real
|
|
23
|
+
// exit that beat the timer) — so a probe here and a subprocess outcome in the
|
|
24
|
+
// test suite never disagree on the same raw spawnSync() shape.
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {{error?: (Error & {code?: string})|null, status: number|null, signal: string|null}} result
|
|
28
|
+
* the raw return value of `spawnSync('git', ...)`.
|
|
29
|
+
* @returns {{determined: true} | {determined: false, reason: string}}
|
|
30
|
+
*/
|
|
31
|
+
function classifyGitProbe(result) {
|
|
32
|
+
const error = result && result.error;
|
|
33
|
+
const status = result ? result.status : null;
|
|
34
|
+
const signal = result ? result.signal : null;
|
|
35
|
+
|
|
36
|
+
if (!error && signal === null) {
|
|
37
|
+
// A real exit — status 0 or non-zero is a genuine, trustworthy answer.
|
|
38
|
+
return { determined: true };
|
|
39
|
+
}
|
|
40
|
+
if (status !== null) {
|
|
41
|
+
// `status` is only ever populated by a real exit; it outranks an attached
|
|
42
|
+
// `error` even at the exact timeout boundary (process-seam.cjs's own
|
|
43
|
+
// "evidence over classification" rule).
|
|
44
|
+
return { determined: true };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const code = error ? (error.code ?? null) : null;
|
|
48
|
+
let reason;
|
|
49
|
+
if (code === 'ETIMEDOUT' || signal !== null) {
|
|
50
|
+
reason = signal ? `timed out (signal ${signal})` : 'timed out';
|
|
51
|
+
} else if (code) {
|
|
52
|
+
reason = `spawn failed (${code})`;
|
|
53
|
+
} else {
|
|
54
|
+
reason = 'could not run (unknown reason)';
|
|
55
|
+
}
|
|
56
|
+
return { determined: false, reason };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* If `result` could not be determined (timeout / spawn failure / signal
|
|
61
|
+
* kill), write a stderr diagnostic naming the probe and why — never changes
|
|
62
|
+
* any exit code, never throws. A no-op when the probe genuinely ran and
|
|
63
|
+
* answered (status 0 or non-zero with no error/signal).
|
|
64
|
+
*
|
|
65
|
+
* @param {string} hookName - e.g. 'gsd-worktree-path-guard'
|
|
66
|
+
* @param {string} probeLabel - e.g. "git rev-parse --show-toplevel"
|
|
67
|
+
* @param {*} result - the raw spawnSync() return value
|
|
68
|
+
*/
|
|
69
|
+
function reportIfUndetermined(hookName, probeLabel, result) {
|
|
70
|
+
const classification = classifyGitProbe(result);
|
|
71
|
+
if (classification.determined) return;
|
|
72
|
+
try {
|
|
73
|
+
process.stderr.write(
|
|
74
|
+
`${hookName}: git probe '${probeLabel}' ${classification.reason} — ` +
|
|
75
|
+
`allowing this call because the probe's answer is unknown, not because ` +
|
|
76
|
+
`git answered "no" (#3911).\n`
|
|
77
|
+
);
|
|
78
|
+
} catch {
|
|
79
|
+
// A stderr write failing (EPIPE, closed fd) must never itself change the
|
|
80
|
+
// hook's fail-open outcome.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { classifyGitProbe, reportIfUndetermined };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// hooks/lib/hook-exit.js — hand-written, NOT generated. A tiny hook-facing
|
|
3
|
+
// vocabulary layered over hooks/lib/cli-exit.js's terminateNow (ADR-3889 §3),
|
|
4
|
+
// for the 19 enforcement hooks P7/#3911 is migrating off raw process.exit().
|
|
5
|
+
//
|
|
6
|
+
// WHY onCrash is a REQUIRED argument, with no default (this is the entire
|
|
7
|
+
// point of this module): every hook audited for #3911 ended its outer
|
|
8
|
+
// try/catch in ONE of two ways — some `process.exit(0)` (fail open: a hook
|
|
9
|
+
// error must never block a legitimate tool call), others `process.exit(2)`
|
|
10
|
+
// (fail closed: a hook whose whole job is a security/safety denial must not
|
|
11
|
+
// let its own bug wave the denial through). Both are legitimate policies for
|
|
12
|
+
// DIFFERENT hooks — but neither is the "obviously correct" one, and a shared
|
|
13
|
+
// helper that silently defaulted to either would let a future hook inherit
|
|
14
|
+
// the wrong policy by omission, which is exactly the "nothing fails with
|
|
15
|
+
// success" defect ADR-3889 exists to close. Requiring the caller to name its
|
|
16
|
+
// policy at every call site — not just once at module load — makes "I forgot
|
|
17
|
+
// to decide" a load-time/call-time crash instead of a silent behavior. A
|
|
18
|
+
// caller that truly wants "allow on crash" must still write ALLOW; there is
|
|
19
|
+
// no path that reaches terminateNow without a declared choice.
|
|
20
|
+
//
|
|
21
|
+
// crash() itself is total: it does not throw on a bad `onCrash`, because
|
|
22
|
+
// throwing would unwind into the CALLER's own outer catch — the exact
|
|
23
|
+
// fail-open-by-accident hazard this module removes. Instead an unrecognized
|
|
24
|
+
// policy terminates via terminateNow('INTERNAL', ...) with a diagnostic
|
|
25
|
+
// payload naming the offending value, so a typo'd/misspelled policy is
|
|
26
|
+
// debuggable on stderr rather than silently reinterpreted as ALLOW or DENY.
|
|
27
|
+
const { terminateNow } = require('./cli-exit.js');
|
|
28
|
+
|
|
29
|
+
/** Frozen enum of the two declarable crash policies. No third option. */
|
|
30
|
+
const HOOK_ON_CRASH = Object.freeze({
|
|
31
|
+
ALLOW: 'allow',
|
|
32
|
+
DENY: 'deny',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** Terminate with the hook-protocol PASS outcome (exit 0). */
|
|
36
|
+
function allow(payload) {
|
|
37
|
+
terminateNow('PASS', payload);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Terminate with the hook-protocol deny outcome (exit 2, stdout+stderr).
|
|
42
|
+
*
|
|
43
|
+
* @param {*} payload - JSON-serializable value written to fd 1 (and, when
|
|
44
|
+
* `stderrPayload` is omitted, fd 2 too).
|
|
45
|
+
* @param {*} [stderrPayload] - optional distinct value for fd 2, forwarded
|
|
46
|
+
* verbatim to terminateNow's own `stderrPayload` param — a string is
|
|
47
|
+
* written raw, anything else is JSON-stringified.
|
|
48
|
+
*/
|
|
49
|
+
function deny(payload, stderrPayload) {
|
|
50
|
+
terminateNow('HOOK_DENY', payload, stderrPayload);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Dispatch to allow()/deny() per a DECLARED crash policy. `onCrash` is
|
|
55
|
+
* required — see the module header for why there is no default.
|
|
56
|
+
*
|
|
57
|
+
* @param {'allow'|'deny'} onCrash - one of HOOK_ON_CRASH's two values.
|
|
58
|
+
* @param {*} payload - JSON-serializable value forwarded to terminateNow.
|
|
59
|
+
*/
|
|
60
|
+
function crash(onCrash, payload) {
|
|
61
|
+
if (onCrash === HOOK_ON_CRASH.ALLOW) {
|
|
62
|
+
allow(payload);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (onCrash === HOOK_ON_CRASH.DENY) {
|
|
66
|
+
deny(payload);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Never guess a policy and never throw (a throw would unwind into the
|
|
70
|
+
// caller's own outer catch — the fail-open-by-accident defect this module
|
|
71
|
+
// exists to remove). Terminate deterministically with INTERNAL, naming the
|
|
72
|
+
// offending value so a misspelled/omitted policy is diagnosable.
|
|
73
|
+
terminateNow('INTERNAL', {
|
|
74
|
+
error: 'hook-exit: crash() called with an unrecognized onCrash policy',
|
|
75
|
+
receivedOnCrash: onCrash,
|
|
76
|
+
expectedOneOf: [HOOK_ON_CRASH.ALLOW, HOOK_ON_CRASH.DENY],
|
|
77
|
+
originalPayload: payload,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { HOOK_ON_CRASH, allow, deny, crash };
|
|
@@ -29,6 +29,9 @@ const MANAGED_HOOKS = [
|
|
|
29
29
|
'gsd-cursor-subagent-stop.js',
|
|
30
30
|
'gsd-ensure-canonical-path.js',
|
|
31
31
|
'gsd-graphify-update.sh',
|
|
32
|
+
// #3662: portable node resolver (helper staged in hooks/; managed JS hook
|
|
33
|
+
// commands route through it under --portable-hooks).
|
|
34
|
+
'gsd-node-runner.sh',
|
|
32
35
|
'gsd-phase-boundary.sh',
|
|
33
36
|
'gsd-prompt-guard.js',
|
|
34
37
|
'gsd-read-guard.js',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengsd/gsd-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
|
|
5
5
|
"main": ".opencode/plugins/gsd-core.js",
|
|
6
6
|
"bin": {
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@eslint/js": "^9.39.4",
|
|
68
68
|
"@stryker-mutator/core": "^9.6.1",
|
|
69
|
+
"@stryker-mutator/tap-runner": "^9.6.1",
|
|
69
70
|
"@types/node": "^22.19.19",
|
|
70
71
|
"c8": "^11.0.0",
|
|
71
72
|
"eslint": "^9.39.4",
|
|
@@ -75,6 +76,7 @@
|
|
|
75
76
|
"fast-check": "^4.8.0",
|
|
76
77
|
"globals": "^16.5.0",
|
|
77
78
|
"js-yaml": "^4.3.1",
|
|
79
|
+
"mutation-testing-metrics": "^3.7.3",
|
|
78
80
|
"re2js": "^2.8.6",
|
|
79
81
|
"typescript": "^6.0.3",
|
|
80
82
|
"typescript-eslint": "^8.60.0"
|
|
@@ -106,7 +108,7 @@
|
|
|
106
108
|
"gen:registry": "node scripts/gen-registry.cjs --write",
|
|
107
109
|
"gen:install-tree": "node scripts/gen-install-tree-fixtures.cjs",
|
|
108
110
|
"gen:section-manifest": "node scripts/gen-section-manifest.cjs --write",
|
|
109
|
-
"regen:derived": "npm run build && npm run gen:registry && node scripts/gen-adr-index.cjs --write && node scripts/gen-capability-matrix.cjs --write && node scripts/gen-inventory-manifest.cjs --write && node scripts/gen-context-index.cjs --write && npm run gen:section-manifest && node scripts/sync-manifest-versions.cjs && npm run gen:install-tree",
|
|
111
|
+
"regen:derived": "npm run build && npm run gen:registry && node scripts/gen-adr-index.cjs --write && node scripts/gen-features.cjs --write && node scripts/gen-capability-matrix.cjs --write && node scripts/gen-inventory-manifest.cjs --write && node scripts/gen-context-index.cjs --write && node scripts/gen-state-md-docs.cjs --write && npm run gen:section-manifest && node scripts/sync-manifest-versions.cjs && npm run gen:install-tree && node scripts/gen-scripts-cli-exit.cjs --write && node scripts/gen-hooks-cli-exit.cjs --write && node scripts/gen-exit-code-registry.cjs --write && node scripts/gen-exit-code-docs.cjs --write",
|
|
110
112
|
"validate:registry": "node scripts/validate-registry.cjs",
|
|
111
113
|
"prepack": "npm run build:lib",
|
|
112
114
|
"prepare": "npm run build:lib",
|
|
@@ -119,7 +121,7 @@
|
|
|
119
121
|
"lint:table-schema-drift": "node scripts/lint-table-schema-drift.cjs",
|
|
120
122
|
"lint:frontmatter-scalar-broad-grep": "node scripts/lint-frontmatter-scalar-broad-grep.cjs",
|
|
121
123
|
"lint:removed-but-needed": "node scripts/lint-removed-but-needed.cjs",
|
|
122
|
-
"lint:ci": "npm run lint && npm run lint:skill-deps && npm run lint:generated-sync && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs && node scripts/lint-
|
|
124
|
+
"lint:ci": "npm run lint && npm run lint:skill-deps && npm run lint:generated-sync && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs && node scripts/lint-portable-timeout.cjs && node scripts/validate-registry.cjs && node scripts/lint-table-schema-drift.cjs && node scripts/lint-fix-has-regression-tests.cjs && node scripts/lint-example-parser-parity.cjs && node scripts/lint-docs-command-form.cjs && node scripts/lint-plan-count-drift.cjs && node scripts/lint-milestone-window-drift.cjs && node scripts/lint-phase-enumeration-drift.cjs && node scripts/lint-planning-prompt-drift.cjs && node scripts/lint-unreachable-guard-drift.cjs && node scripts/lint-completion-ratio-drift.cjs && node scripts/lint-slug-derivation-drift.cjs && node scripts/lint-state-field-drift.cjs && node scripts/lint-state-write-path-drift.cjs && node scripts/lint-completion-predicate-drift.cjs && node scripts/lint-planning-snapshot-bypass-drift.cjs && node scripts/lint-health-diagnostic-rule-table.cjs && node scripts/lint-planning-artifact-writer-drift.cjs && node scripts/lint-frontmatter-scalar-broad-grep.cjs && node scripts/lint-removed-but-needed.cjs && node scripts/lint-no-adhoc-regex-escape.cjs && node scripts/lint-vendored-deps.cjs && node scripts/lint-docs-guard-registration.cjs && node scripts/lint-source-test-name-collision.cjs && npm run lint:hooks-runtime-build-seam && node scripts/check-contract-drift.cjs && node scripts/lint-mutation-test-derivation-drift.cjs && node scripts/lint-seam-enforcement.cjs",
|
|
123
125
|
"lint:allow-test-rule-refs": "node scripts/lint-allow-test-rule-refs.cjs",
|
|
124
126
|
"lint:regression-names": "node scripts/lint-regression-test-names.cjs",
|
|
125
127
|
"lint:descriptions": "node scripts/lint-descriptions.cjs",
|
|
@@ -127,10 +129,11 @@
|
|
|
127
129
|
"lint:test-file-count": "node scripts/lint-test-file-count.cjs",
|
|
128
130
|
"lint:pr-checks": "node scripts/lint-pr-check-project-dir.cjs",
|
|
129
131
|
"lint:changeset": "node scripts/changeset/lint.cjs",
|
|
130
|
-
"lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check && node scripts/gen-context-index.cjs --check && node scripts/gen-section-manifest.cjs --check && node scripts/gen-health-docs.cjs --check",
|
|
132
|
+
"lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/gen-features.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check && node scripts/gen-context-index.cjs --check && node scripts/gen-section-manifest.cjs --check && node scripts/gen-health-docs.cjs --check && node scripts/gen-state-md-docs.cjs --check && node scripts/gen-scripts-cli-exit.cjs --check && node scripts/gen-hooks-cli-exit.cjs --check && node scripts/gen-exit-code-registry.cjs --check && node scripts/gen-exit-code-docs.cjs --check",
|
|
131
133
|
"lint:docs": "node scripts/lint-docs-required.cjs",
|
|
132
134
|
"lint:qa-smells": "node scripts/qa-smell-ratchet.cjs",
|
|
133
135
|
"lint:legacy-name": "node scripts/lint-legacy-dir-name.cjs",
|
|
136
|
+
"lint:seam-enforcement": "node scripts/lint-seam-enforcement.cjs",
|
|
134
137
|
"lint:docs-command-form": "node scripts/lint-docs-command-form.cjs",
|
|
135
138
|
"lint:hooks-runtime-build-seam": "node scripts/lint-hooks-runtime-build-seam.cjs",
|
|
136
139
|
"ci:test-scope": "node scripts/ci-test-scope.cjs",
|
|
@@ -146,12 +149,14 @@
|
|
|
146
149
|
"test:affected": "node scripts/run-affected-tests.cjs",
|
|
147
150
|
"test:coverage": "c8 --check-coverage --lines 70 --branches 60 --reporter text --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs",
|
|
148
151
|
"test:coverage:scripts-floor": "c8 check-coverage --lines 55 --include 'scripts/**/*.cjs' --exclude 'tests/**' --all",
|
|
149
|
-
"test:coverage:unit": "c8 --reporter text --reporter json-summary --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs --suite unit && node scripts/check-coverage-gate.cjs",
|
|
152
|
+
"test:coverage:unit": "c8 --reporter text --reporter json-summary --merge-async --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs --suite unit && node scripts/check-coverage-gate.cjs",
|
|
150
153
|
"test:coverage:unit:raw": "c8 --reporter none node scripts/run-tests.cjs --suite unit",
|
|
151
|
-
"test:coverage:report": "c8 report --reporter text --reporter json-summary --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all && node scripts/check-coverage-gate.cjs",
|
|
154
|
+
"test:coverage:report": "c8 report --reporter text --reporter json-summary --merge-async --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all && node scripts/check-coverage-gate.cjs",
|
|
152
155
|
"test:coverage:all": "npm run test:coverage",
|
|
153
156
|
"test:mutation": "stryker run",
|
|
154
|
-
"test:mutation:since": "stryker run --incremental --since origin/next"
|
|
157
|
+
"test:mutation:since": "stryker run --incremental --since origin/next",
|
|
158
|
+
"gen:features": "node scripts/gen-features.cjs",
|
|
159
|
+
"gen:state-md-docs": "node scripts/gen-state-md-docs.cjs"
|
|
155
160
|
},
|
|
156
161
|
"allowScripts": {
|
|
157
162
|
"fallow@2.70.0": true
|
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
|
@@ -71,6 +71,11 @@ const HOOKS_TO_COPY = [
|
|
|
71
71
|
'gsd-session-state.sh',
|
|
72
72
|
'gsd-validate-commit.sh',
|
|
73
73
|
'gsd-phase-boundary.sh',
|
|
74
|
+
// Portable node resolver (#3662). Not a registered hook itself: managed JS
|
|
75
|
+
// hook commands under --portable-hooks route through it (bash <resolver>
|
|
76
|
+
// <baked-node> <script>) so node resolves at hook-fire time in every
|
|
77
|
+
// environment sharing the config root. Staged verbatim — no templating.
|
|
78
|
+
'gsd-node-runner.sh',
|
|
74
79
|
// Graphify auto-update hook (#3347 / PR #3557 / #3579). Opt-in via
|
|
75
80
|
// .planning/config.json graphify.auto_update; off by default.
|
|
76
81
|
'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
|
}
|