@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
|
@@ -37,7 +37,7 @@ No Pass/Fail buttons. No severity questions. Just: "Here's what should happen. D
|
|
|
37
37
|
If $ARGUMENTS contains a phase number, load context:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$
|
|
40
|
+
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif _gsd_at "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd_run is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; case "$(gsd_run runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') GSD_IDENTITY_STATUS=ok;; esac; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
41
41
|
GSD_WS=""
|
|
42
42
|
echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[A-Za-z0-9._-]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[A-Za-z0-9._-]+')
|
|
43
43
|
PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[A-Za-z0-9._-]+//g' | xargs)
|
|
@@ -61,15 +61,26 @@ MVP_MODE=$(gsd_run query phase.mvp-mode "${phase_number}" ${GSD_WS} --pick activ
|
|
|
61
61
|
</step>
|
|
62
62
|
|
|
63
63
|
<step name="verify_pre_hooks">
|
|
64
|
-
**Verify:pre
|
|
65
|
-
|
|
66
|
-
data-driven — resolved from the capability
|
|
64
|
+
**Verify:pre capability dispatch.** Before verification begins, dispatch every
|
|
65
|
+
active hook registered at the `verify:pre` loop extension point — of **every**
|
|
66
|
+
kind, not gates alone. Each hook is data-driven — resolved from the capability
|
|
67
|
+
registry, not hardcoded here.
|
|
67
68
|
|
|
68
69
|
```bash
|
|
69
70
|
VERIFY_PRE_HOOKS_JSON=$(gsd_run loop render-hooks verify:pre --raw)
|
|
70
71
|
PHASE_DIR=$(printf '%s' "$INIT" | jq -r '.phase_dir // empty')
|
|
71
72
|
```
|
|
72
73
|
|
|
74
|
+
Read the `activeHooks` array from `VERIFY_PRE_HOOKS_JSON` in-context (do NOT pipe through a shell parser).
|
|
75
|
+
|
|
76
|
+
**If `activeHooks` is empty or absent:** skip silently to `check_active_session`.
|
|
77
|
+
|
|
78
|
+
**Contribution dispatch:** inject every `kind == "contribution"` fragment per @gsd-core/references/loop-hook-dispatch.md (skip when none), before the steps and gates below.
|
|
79
|
+
|
|
80
|
+
**Step dispatch:** dispatch every `kind == "step"` hook per @gsd-core/references/loop-hook-dispatch.md (skip when none) — not one shape of one. A step here is advisory: it never blocks the start of UAT, and a step that errors is routed by its own `onError` without failing verification. ⚠ **Validate `ref.command` in-context before any shell use** (third-party manifest input) — loop-hook-dispatch.md § `step`.
|
|
81
|
+
|
|
82
|
+
Record the union of `produces` artefact names declared by the active step entries as `VERIFY_PRE_PRODUCED` — `extract_tests` consumes it below. An entry declaring `produces: []` contributes nothing, which is the normal case.
|
|
83
|
+
|
|
73
84
|
⚠ **Validate `check` before shell use** (third-party manifest input) — `loop-hook-dispatch.md` § `gate`.
|
|
74
85
|
|
|
75
86
|
Resolve active gate hooks from `VERIFY_PRE_HOOKS_JSON` where `kind == "gate"`.
|
|
@@ -187,6 +198,35 @@ Read the JSON result (`mode`, `total`, `all_auto_covered`, `auto_passed[]`, `pre
|
|
|
187
198
|
|
|
188
199
|
The cold-start smoke test injection below still applies in `coverage` mode.
|
|
189
200
|
|
|
201
|
+
**Verify:pre produced-artefact seam (#3866).** If `VERIFY_PRE_PRODUCED` (recorded in
|
|
202
|
+
`verify_pre_hooks`) is empty or absent, skip this paragraph entirely — derivation is unchanged.
|
|
203
|
+
Otherwise, for each artefact name in it, locate the artefact the producing step wrote under
|
|
204
|
+
`$PHASE_DIR` and merge its checkpoints into the test list **additively**.
|
|
205
|
+
|
|
206
|
+
**The artefact contract.** A consumable artefact is a Markdown file holding a list of checkpoint
|
|
207
|
+
entries in the **same shape `extract_tests` already emits and `create_uat_file` already consumes** —
|
|
208
|
+
each entry a `name` (brief test name) and an `expected` (specific, user-observable outcome).
|
|
209
|
+
Nothing else is read: extra fields are ignored, not an error. There is no new schema and no new
|
|
210
|
+
parser — a producing step writes what a checkpoint already looks like. An artefact that yields zero
|
|
211
|
+
parseable entries is treated exactly like an absent one (see below).
|
|
212
|
+
|
|
213
|
+
Merge rules:
|
|
214
|
+
|
|
215
|
+
- ⚠ **Validate the artefact name in-context before resolving it** (third-party manifest input).
|
|
216
|
+
An artefact name is a registry-declared name, **not** a path: check the value you read from
|
|
217
|
+
`produces` against `^[A-Za-z0-9][A-Za-z0-9._-]*$` yourself — **never** by pasting it into a
|
|
218
|
+
shell command to be tested there. A name carrying `/`, `..`, a leading `-`, a leading path
|
|
219
|
+
separator, or any shell metacharacter is a malformed manifest: record a warning, skip that
|
|
220
|
+
name, continue. Only a validated name is resolved, and only against what the step wrote inside
|
|
221
|
+
`$PHASE_DIR` — never above it, and never through a symlink that leaves it.
|
|
222
|
+
- A name with no artefact on disk means that step was inactive, skipped, or failed. Note it to the
|
|
223
|
+
user and derive normally — **never drop a deliverable and never block UAT over it.**
|
|
224
|
+
- Merged entries are added to, never subtracted from, what `coverage:` classification and the
|
|
225
|
+
prose fallback produce. An `auto_passed[]` entry stays un-presented; a `present[]` entry stays a
|
|
226
|
+
human checkpoint. This seam can deepen UAT, not suppress it.
|
|
227
|
+
- Deduplicate against already-derived checkpoints by `name`, keeping the earlier entry's
|
|
228
|
+
`expected` text so a produced artefact cannot silently rewrite a criterion.
|
|
229
|
+
|
|
190
230
|
**Extract testable deliverables from SUMMARY.md (legacy fallback — used when `mode: legacy`):**
|
|
191
231
|
|
|
192
232
|
Parse for:
|
|
@@ -534,6 +574,8 @@ VERIFY_POST_HOOKS_JSON=$(gsd_run loop render-hooks verify:post --raw)
|
|
|
534
574
|
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
|
|
535
575
|
```
|
|
536
576
|
|
|
577
|
+
**Generic step dispatch:** dispatch every `kind == "step"` hook from `VERIFY_POST_HOOKS_JSON` per @gsd-core/references/loop-hook-dispatch.md (skip silently when none). Each step is advisory and best-effort — honor `onError` and continue. The secure-phase handling below is an additional specialization of one such hook, not a replacement for the generic dispatch.
|
|
578
|
+
|
|
537
579
|
Resolve active step hooks from `VERIFY_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "secure-phase"`.
|
|
538
580
|
|
|
539
581
|
If an active secure-phase step hook exists AND `SECURITY_FILE` is empty, dispatch the registry-provided skill stem:
|
|
@@ -633,7 +675,7 @@ All tests passed. Phase {phase} marked complete.
|
|
|
633
675
|
<step name="scan_phase_artifacts">
|
|
634
676
|
Run phase artifact scan to surface any open items before marking phase verified:
|
|
635
677
|
|
|
636
|
-
`audit-open` is CJS-only until registered on `
|
|
678
|
+
`audit-open` is CJS-only until registered on `gsd_run query`:
|
|
637
679
|
|
|
638
680
|
```bash
|
|
639
681
|
gsd_run query audit-open --json
|
|
@@ -647,9 +689,12 @@ Parse the JSON output. For the CURRENT PHASE ONLY, surface:
|
|
|
647
689
|
If any are found, display:
|
|
648
690
|
```
|
|
649
691
|
Phase {N} Artifact Check
|
|
650
|
-
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
651
695
|
{list each item with status and file path}
|
|
652
|
-
|
|
696
|
+
|
|
697
|
+
---
|
|
653
698
|
These items are open. Proceed anyway? [Y/n]
|
|
654
699
|
```
|
|
655
700
|
|
|
@@ -685,9 +730,7 @@ Diagnosis runs automatically - no user prompt. Parallel agents investigate simul
|
|
|
685
730
|
|
|
686
731
|
Display:
|
|
687
732
|
```
|
|
688
|
-
|
|
689
|
-
GSD ► PLANNING FIXES
|
|
690
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
733
|
+
### GSD ► PLANNING FIXES
|
|
691
734
|
|
|
692
735
|
◆ Spawning planner for gap closure... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
693
736
|
```
|
|
@@ -752,9 +795,7 @@ On return:
|
|
|
752
795
|
|
|
753
796
|
Display:
|
|
754
797
|
```
|
|
755
|
-
|
|
756
|
-
GSD ► VERIFYING FIX PLANS
|
|
757
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
798
|
+
### GSD ► VERIFYING FIX PLANS
|
|
758
799
|
|
|
759
800
|
◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
760
801
|
```
|
|
@@ -858,9 +899,7 @@ Wait for user response.
|
|
|
858
899
|
**Present completion and next steps:**
|
|
859
900
|
|
|
860
901
|
```
|
|
861
|
-
|
|
862
|
-
GSD ► FIXES READY ✓
|
|
863
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
902
|
+
### GSD ► FIXES READY ✓
|
|
864
903
|
|
|
865
904
|
**Phase {X}: {Name}** — {N} gap(s) diagnosed, {M} fix plan(s) created
|
|
866
905
|
|
|
@@ -871,7 +910,7 @@ Wait for user response.
|
|
|
871
910
|
|
|
872
911
|
Plans verified and ready for execution.
|
|
873
912
|
|
|
874
|
-
|
|
913
|
+
---
|
|
875
914
|
|
|
876
915
|
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
|
877
916
|
|
|
@@ -879,7 +918,7 @@ Plans verified and ready for execution.
|
|
|
879
918
|
|
|
880
919
|
`/clear` then `/gsd:execute-phase {phase} --gaps-only`
|
|
881
920
|
|
|
882
|
-
|
|
921
|
+
---
|
|
883
922
|
```
|
|
884
923
|
</step>
|
|
885
924
|
|
|
@@ -65,6 +65,23 @@ const path = require('path');
|
|
|
65
65
|
const os = require('os');
|
|
66
66
|
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch } = require('./lib/isolation-sentinel.js');
|
|
67
67
|
const { REASON_CODE } = require('./lib/isolation-deny-reason.js');
|
|
68
|
+
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
69
|
+
|
|
70
|
+
// Required at module top, alongside the other ./lib requires — NOT behind
|
|
71
|
+
// ensureRuntimeBuild() below. Terminating on a parse/timeout failure must
|
|
72
|
+
// never depend on the gitignored build artifacts this hook self-heals for
|
|
73
|
+
// its own registry lookups (#3911).
|
|
74
|
+
//
|
|
75
|
+
// This guard's outer catch (main(), below) has always exited 0 (fail open):
|
|
76
|
+
// that outer catch only covers payload PARSING failing before applicability
|
|
77
|
+
// could even be determined (malformed stdin JSON, etc.) — the guard's real
|
|
78
|
+
// fail-closed logic (a GSD project whose dispatch-isolation configuration
|
|
79
|
+
// cannot be verified) is handled separately, inside evaluateDispatch/
|
|
80
|
+
// resolveIsolationState, and already returns a 'block' decision through the
|
|
81
|
+
// normal exit-2 path rather than through this catch. So an unparseable
|
|
82
|
+
// payload has nothing to enforce; allowing it preserves today's behavior
|
|
83
|
+
// exactly.
|
|
84
|
+
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
68
85
|
// #3582: gsd-core/bin/lib/*.cjs (runtime-name-policy.cjs, capability-registry.cjs
|
|
69
86
|
// below) are tsc build artifacts (ADR-457), gitignored and absent on a raw
|
|
70
87
|
// plugin-marketplace / git-clone install that never ran `npm run build:lib`.
|
|
@@ -95,40 +112,40 @@ function parseHarnessFlag(flag) {
|
|
|
95
112
|
return { param: m[1], value: m[2] };
|
|
96
113
|
}
|
|
97
114
|
|
|
98
|
-
// ─── #
|
|
115
|
+
// ─── #3897 rung 2: per-install runtime marker, single canonical owner ────────
|
|
99
116
|
// bin/install.js writes `<install>/gsd-core/.gsd-runtime` for EVERY runtime
|
|
100
117
|
// install (#2297), co-located with VERSION. Unlike `~/.gsd/defaults.json` —
|
|
101
118
|
// which is host-wide and names whichever runtime's install ran LAST, the exact
|
|
102
119
|
// leakage #2840's config.cjs change exists to prevent — the marker describes
|
|
103
120
|
// THIS install, which is the property runtime identity needs on a machine
|
|
104
|
-
// with 2+ runtimes.
|
|
105
|
-
// (
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
// consolidating every marker reader into one shared seam.
|
|
110
|
-
let _installMarkerCache; // undefined = unread; null = known absent; string = value
|
|
111
|
-
|
|
121
|
+
// with 2+ runtimes. Previously this hook held its own private reader/cache
|
|
122
|
+
// (one of four #3897 found); it now delegates to the single canonical owner,
|
|
123
|
+
// `src/runtime-slash.cts` (compiled to gsd-core/bin/lib/runtime-slash.cjs),
|
|
124
|
+
// reached through `ensureRuntimeBuild()` like every other compiled-lib require
|
|
125
|
+
// in this file (`scripts/lint-hooks-runtime-build-seam.cjs`).
|
|
112
126
|
function readInstallRuntimeMarker() {
|
|
113
|
-
if (_installMarkerCache !== undefined) return _installMarkerCache;
|
|
114
127
|
try {
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
128
|
+
ensureRuntimeBuild();
|
|
129
|
+
const runtimeSlash = require('../gsd-core/bin/lib/runtime-slash.cjs');
|
|
130
|
+
return runtimeSlash.readInstallRuntimeMarker();
|
|
118
131
|
} catch {
|
|
119
|
-
//
|
|
120
|
-
// from this rung", never a resolution failure.
|
|
121
|
-
|
|
122
|
-
_installMarkerCache = null;
|
|
132
|
+
// Unbuilt runtime library, or any other failure reaching the canonical
|
|
133
|
+
// owner — "no signal from this rung" (N4), never a resolution failure.
|
|
134
|
+
return null;
|
|
123
135
|
}
|
|
124
|
-
return _installMarkerCache;
|
|
125
136
|
}
|
|
126
137
|
|
|
127
|
-
// Test seam for the marker rung
|
|
128
|
-
//
|
|
129
|
-
// model-resolver.cts's _setInstallRuntimeMarkerForTests, #2297).
|
|
138
|
+
// Test seam for the marker rung — forwards to the canonical owner's seam so
|
|
139
|
+
// this hook and runtime-slash.cjs always share one cache (#3897 rung 2).
|
|
130
140
|
function _setInstallRuntimeMarkerForTests(value) {
|
|
131
|
-
|
|
141
|
+
try {
|
|
142
|
+
ensureRuntimeBuild();
|
|
143
|
+
const runtimeSlash = require('../gsd-core/bin/lib/runtime-slash.cjs');
|
|
144
|
+
runtimeSlash._setInstallRuntimeMarkerForTests(value);
|
|
145
|
+
} catch {
|
|
146
|
+
// Test-only seam; an unbuilt library here means the test itself will fail
|
|
147
|
+
// downstream, which is a louder and more actionable signal than throwing here.
|
|
148
|
+
}
|
|
132
149
|
}
|
|
133
150
|
|
|
134
151
|
/**
|
|
@@ -282,18 +299,40 @@ function resolveRegistryIsolation(cwd, configPath) {
|
|
|
282
299
|
|
|
283
300
|
if (isolation === 'harness-worktree') {
|
|
284
301
|
let useWorktrees = true;
|
|
302
|
+
// #3972: the opt-out read shares the ONE owner every other
|
|
303
|
+
// isolation-deciding surface uses — planning-workspace's
|
|
304
|
+
// worktreesOptedOut ladder (scoped own-key wins; root inherited under
|
|
305
|
+
// the GSD_WORKSTREAM gate; strict === false). A flat single-file read
|
|
306
|
+
// here made a workstream-LOCAL opt-out invisible, so the sentinel-absent
|
|
307
|
+
// fallback denied a sequential dispatch the config explicitly allowed.
|
|
308
|
+
// Reached through ensureRuntimeBuild() like every other compiled-lib
|
|
309
|
+
// require in this file (scripts/lint-hooks-runtime-build-seam.cjs); if
|
|
310
|
+
// the library is unreachable the flat-root read below remains as the
|
|
311
|
+
// degraded fallback — today's behavior, never worse.
|
|
312
|
+
let ladderAnswered = false;
|
|
285
313
|
try {
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
useWorktrees = false;
|
|
291
|
-
}
|
|
314
|
+
ensureRuntimeBuild();
|
|
315
|
+
const { worktreesOptedOut } = require('../gsd-core/bin/lib/planning-workspace.cjs');
|
|
316
|
+
useWorktrees = !worktreesOptedOut(cwd);
|
|
317
|
+
ladderAnswered = true;
|
|
292
318
|
} catch {
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
319
|
+
// Unbuilt runtime library or a ladder failure — fall through to the
|
|
320
|
+
// legacy flat-root read (conservative: keep enforcing).
|
|
321
|
+
}
|
|
322
|
+
if (!ladderAnswered) {
|
|
323
|
+
try {
|
|
324
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
325
|
+
const parsedCfg = JSON.parse(raw);
|
|
326
|
+
if (parsedCfg && typeof parsedCfg === 'object' && parsedCfg.workflow &&
|
|
327
|
+
typeof parsedCfg.workflow === 'object' && parsedCfg.workflow.use_worktrees === false) {
|
|
328
|
+
useWorktrees = false;
|
|
329
|
+
}
|
|
330
|
+
} catch {
|
|
331
|
+
// Unreadable config already propagated to the outer caller's catch
|
|
332
|
+
// before this point in practice (resolveRuntimeIdentity reads it
|
|
333
|
+
// first); tolerate defensively and keep the conservative (enforce)
|
|
334
|
+
// default rather than silently disabling the guard.
|
|
335
|
+
}
|
|
297
336
|
}
|
|
298
337
|
if (!useWorktrees) isolation = 'none';
|
|
299
338
|
}
|
|
@@ -476,7 +515,7 @@ function evaluateDispatch(data, { clock = Date } = {}) {
|
|
|
476
515
|
/* istanbul ignore next -- stdin adapter, exercised via spawnSync in tests */
|
|
477
516
|
function main() {
|
|
478
517
|
let input = '';
|
|
479
|
-
const stdinTimeout = setTimeout(() =>
|
|
518
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 3000);
|
|
480
519
|
process.stdin.setEncoding('utf8');
|
|
481
520
|
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
482
521
|
process.stdin.on('end', () => {
|
|
@@ -486,18 +525,18 @@ function main() {
|
|
|
486
525
|
const decision = evaluateDispatch(data);
|
|
487
526
|
if (decision.action === 'block') {
|
|
488
527
|
const out = { decision: 'block', reason: decision.reason, reason_code: decision.reasonCode };
|
|
489
|
-
process.stdout.write(JSON.stringify(out));
|
|
490
528
|
// Kimi feeds stderr (not stdout) back to the model on exit 2.
|
|
491
|
-
|
|
492
|
-
process.exit(2);
|
|
529
|
+
deny(out, decision.reason);
|
|
493
530
|
}
|
|
494
|
-
|
|
531
|
+
allow(undefined);
|
|
495
532
|
} catch {
|
|
496
533
|
// Silent fail — never block valid tool calls due to hook errors
|
|
497
534
|
// (malformed payload, etc.). This is distinct from resolveIsolationState's
|
|
498
535
|
// internal error handling, which DOES deny — this outer catch only
|
|
499
536
|
// covers payload parsing before applicability could even be determined.
|
|
500
|
-
|
|
537
|
+
// ON_CRASH is declared ALLOW at module top: this preserves today's
|
|
538
|
+
// exit(0) fail-open behavior exactly (#3911).
|
|
539
|
+
crash(ON_CRASH, undefined);
|
|
501
540
|
}
|
|
502
541
|
});
|
|
503
542
|
}
|
|
@@ -20,11 +20,17 @@
|
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
22
|
const path = require('path');
|
|
23
|
+
const { HOOK_ON_CRASH, allow, crash } = require('./lib/hook-exit.js');
|
|
24
|
+
|
|
25
|
+
// This hook only injects an advisory config-reload summary; a crash mid-parse
|
|
26
|
+
// must not block the session or the FileChanged event that triggered it — the
|
|
27
|
+
// agent simply keeps using the config context it already had (#3911).
|
|
28
|
+
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
23
29
|
|
|
24
30
|
let input = '';
|
|
25
31
|
// Timeout guard: if stdin does not close within 8s exit silently rather than
|
|
26
32
|
// hanging until Claude Code kills the process and reports "hook error".
|
|
27
|
-
const stdinTimeout = setTimeout(() =>
|
|
33
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 8000);
|
|
28
34
|
process.stdin.setEncoding('utf8');
|
|
29
35
|
process.stdin.on('data', chunk => (input += chunk));
|
|
30
36
|
process.stdin.on('end', () => {
|
|
@@ -42,24 +48,23 @@ process.stdin.on('end', () => {
|
|
|
42
48
|
// inject spurious additionalContext.
|
|
43
49
|
const basename = path.basename(filePath);
|
|
44
50
|
if (basename !== 'config.json') {
|
|
45
|
-
|
|
51
|
+
allow(undefined);
|
|
46
52
|
}
|
|
47
53
|
const expectedPath = path.resolve(cwd, '.planning', 'config.json');
|
|
48
54
|
if (path.resolve(filePath) !== expectedPath) {
|
|
49
|
-
|
|
55
|
+
allow(undefined);
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
// On unlink (deletion) emit a brief notice and exit
|
|
53
59
|
if (event === 'unlink') {
|
|
54
|
-
|
|
60
|
+
allow({
|
|
55
61
|
hookSpecificOutput: {
|
|
56
62
|
hookEventName: 'FileChanged',
|
|
57
63
|
additionalContext:
|
|
58
64
|
'GSD config (.planning/config.json) was deleted. ' +
|
|
59
65
|
'Falling back to built-in defaults for this session.',
|
|
60
66
|
},
|
|
61
|
-
})
|
|
62
|
-
process.exit(0);
|
|
67
|
+
});
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
// Read the updated config file
|
|
@@ -68,17 +73,16 @@ process.stdin.on('end', () => {
|
|
|
68
73
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
69
74
|
config = JSON.parse(raw);
|
|
70
75
|
} catch (e) {
|
|
71
|
-
if (e && e.code === 'ENOENT')
|
|
76
|
+
if (e && e.code === 'ENOENT') allow(undefined);
|
|
72
77
|
// Malformed JSON — inform the agent without crashing
|
|
73
|
-
|
|
78
|
+
allow({
|
|
74
79
|
hookSpecificOutput: {
|
|
75
80
|
hookEventName: 'FileChanged',
|
|
76
81
|
additionalContext:
|
|
77
82
|
'GSD config (.planning/config.json) was modified but could not be parsed. ' +
|
|
78
83
|
'Check the file for JSON syntax errors.',
|
|
79
84
|
},
|
|
80
|
-
})
|
|
81
|
-
process.exit(0);
|
|
85
|
+
});
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
// Build a concise summary of key config fields the agent cares about
|
|
@@ -127,7 +131,9 @@ process.stdin.on('end', () => {
|
|
|
127
131
|
},
|
|
128
132
|
}));
|
|
129
133
|
} catch (e) {
|
|
130
|
-
// Silent fail — never block the session on a config reload error
|
|
131
|
-
|
|
134
|
+
// Silent fail — never block the session on a config reload error.
|
|
135
|
+
// ON_CRASH is declared ALLOW at module top: this preserves today's
|
|
136
|
+
// exit(0) fail-open behavior exactly (#3911).
|
|
137
|
+
crash(ON_CRASH, undefined);
|
|
132
138
|
}
|
|
133
139
|
});
|
|
@@ -22,6 +22,13 @@ const fs = require('fs');
|
|
|
22
22
|
const os = require('os');
|
|
23
23
|
const path = require('path');
|
|
24
24
|
const { spawn } = require('child_process');
|
|
25
|
+
const { HOOK_ON_CRASH, allow, crash } = require('./lib/hook-exit.js');
|
|
26
|
+
|
|
27
|
+
// This hook only injects an advisory context-usage warning; it never blocks
|
|
28
|
+
// the tool call it rides in on. A crash here (e.g. a malformed bridge file)
|
|
29
|
+
// must not turn a PostToolUse advisory into a blocked tool call — losing a
|
|
30
|
+
// context warning is far cheaper than stalling the agent's work (#3911).
|
|
31
|
+
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
25
32
|
|
|
26
33
|
const WARNING_THRESHOLD = 35; // remaining_percentage <= 35%
|
|
27
34
|
const CRITICAL_THRESHOLD = 25; // remaining_percentage <= 25%
|
|
@@ -33,7 +40,7 @@ let input = '';
|
|
|
33
40
|
// Windows/Git Bash, or slow Claude Code piping during large outputs),
|
|
34
41
|
// exit silently instead of hanging until Claude Code kills the process
|
|
35
42
|
// and reports "hook error". See #775, #1162.
|
|
36
|
-
const stdinTimeout = setTimeout(() =>
|
|
43
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 10000);
|
|
37
44
|
process.stdin.setEncoding('utf8');
|
|
38
45
|
process.stdin.on('data', chunk => input += chunk);
|
|
39
46
|
process.stdin.on('end', () => {
|
|
@@ -43,14 +50,14 @@ process.stdin.on('end', () => {
|
|
|
43
50
|
const sessionId = data.session_id;
|
|
44
51
|
|
|
45
52
|
if (!sessionId) {
|
|
46
|
-
|
|
53
|
+
allow(undefined);
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
// Reject session IDs that contain path traversal sequences or path separators.
|
|
50
57
|
// session_id is used to construct file paths in /tmp — an unsanitized value
|
|
51
58
|
// could escape the temp directory and read or write arbitrary files.
|
|
52
59
|
if (/[/\\]|\.\./.test(sessionId)) {
|
|
53
|
-
|
|
60
|
+
allow(undefined);
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
// Check if context warnings are disabled via config.
|
|
@@ -61,7 +68,7 @@ process.stdin.on('end', () => {
|
|
|
61
68
|
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
62
69
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
63
70
|
if (config.hooks?.context_warnings === false) {
|
|
64
|
-
|
|
71
|
+
allow(undefined);
|
|
65
72
|
}
|
|
66
73
|
} catch (e) {
|
|
67
74
|
// Missing or unparseable config → proceed with defaults (context warnings enabled)
|
|
@@ -77,7 +84,7 @@ process.stdin.on('end', () => {
|
|
|
77
84
|
try {
|
|
78
85
|
metricsRaw = fs.readFileSync(metricsPath, 'utf8');
|
|
79
86
|
} catch (e) {
|
|
80
|
-
if (e && e.code === 'ENOENT')
|
|
87
|
+
if (e && e.code === 'ENOENT') allow(undefined);
|
|
81
88
|
throw e;
|
|
82
89
|
}
|
|
83
90
|
const metrics = JSON.parse(metricsRaw);
|
|
@@ -85,7 +92,7 @@ process.stdin.on('end', () => {
|
|
|
85
92
|
|
|
86
93
|
// Ignore stale metrics
|
|
87
94
|
if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
|
|
88
|
-
|
|
95
|
+
allow(undefined);
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
const remaining = metrics.remaining_percentage;
|
|
@@ -93,7 +100,7 @@ process.stdin.on('end', () => {
|
|
|
93
100
|
|
|
94
101
|
// No warning needed
|
|
95
102
|
if (remaining > WARNING_THRESHOLD) {
|
|
96
|
-
|
|
103
|
+
allow(undefined);
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
// Debounce: check if we warned recently
|
|
@@ -121,7 +128,7 @@ process.stdin.on('end', () => {
|
|
|
121
128
|
if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
|
|
122
129
|
// Update counter and exit without warning
|
|
123
130
|
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
124
|
-
|
|
131
|
+
allow(undefined);
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
// Reset debounce counter
|
|
@@ -208,7 +215,9 @@ process.stdin.on('end', () => {
|
|
|
208
215
|
process.stdout.write(JSON.stringify(output));
|
|
209
216
|
}
|
|
210
217
|
} catch (e) {
|
|
211
|
-
// Silent fail -- never block tool execution
|
|
212
|
-
|
|
218
|
+
// Silent fail -- never block tool execution.
|
|
219
|
+
// ON_CRASH is declared ALLOW at module top: this preserves today's
|
|
220
|
+
// exit(0) fail-open behavior exactly (#3911).
|
|
221
|
+
crash(ON_CRASH, undefined);
|
|
213
222
|
}
|
|
214
223
|
});
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
|
+
const { allow } = require('./lib/hook-exit.js');
|
|
26
|
+
|
|
25
27
|
const WRITE_TOOL_RE = /write|edit|replace|create|delete|remove|append|apply|patch|insert|mkdir/i;
|
|
26
28
|
const PATH_KEY_RE = /^(path|file|file_?path|filepath|target_?path|target|dir|directory|uri|filename)$/i;
|
|
27
29
|
const PLANNING_PATH_RE = /(^|[\\/])\.planning([\\/]|$)/;
|
|
@@ -29,7 +31,7 @@ const PLANNING_PATH_RE = /(^|[\\/])\.planning([\\/]|$)/;
|
|
|
29
31
|
let raw = '';
|
|
30
32
|
const stdinTimeout = setTimeout(() => {
|
|
31
33
|
// Timeout guard: exit silently rather than hanging.
|
|
32
|
-
|
|
34
|
+
allow(undefined);
|
|
33
35
|
}, 10000);
|
|
34
36
|
|
|
35
37
|
process.stdin.setEncoding('utf8');
|
|
@@ -22,13 +22,15 @@
|
|
|
22
22
|
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
|
+
const { allow } = require('./lib/hook-exit.js');
|
|
26
|
+
|
|
25
27
|
const WRITE_TOOL_RE = /write|edit|replace|create|delete|remove|append|apply|patch|insert|mkdir/i;
|
|
26
28
|
const PATH_KEY_RE = /^(path|file|file_?path|filepath|target_?path|target|dir|directory|uri|filename)$/i;
|
|
27
29
|
const PLANNING_PATH_RE = /(^|[\\/])\.planning([\\/]|$)/;
|
|
28
30
|
|
|
29
31
|
let raw = '';
|
|
30
32
|
const stdinTimeout = setTimeout(() => {
|
|
31
|
-
|
|
33
|
+
allow(undefined);
|
|
32
34
|
}, 10000);
|
|
33
35
|
|
|
34
36
|
process.stdin.setEncoding('utf8');
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
25
|
const fs = require('fs');
|
|
26
|
+
const { allow } = require('./lib/hook-exit.js');
|
|
26
27
|
|
|
27
28
|
const MSG_PRESENT =
|
|
28
29
|
'GSD: .planning/STATE.md is present — review the current phase and any blockers before acting.';
|
|
@@ -37,7 +38,7 @@ const { resolveStatePath } = require('./lib/cursor-workspace.js');
|
|
|
37
38
|
let raw = '';
|
|
38
39
|
const stdinTimeout = setTimeout(() => {
|
|
39
40
|
// Timeout guard: exit silently rather than hanging.
|
|
40
|
-
|
|
41
|
+
allow(undefined);
|
|
41
42
|
}, 10000);
|
|
42
43
|
|
|
43
44
|
process.stdin.setEncoding('utf8');
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
'use strict';
|
|
22
22
|
|
|
23
23
|
const fs = require('fs');
|
|
24
|
+
const { allow } = require('./lib/hook-exit.js');
|
|
24
25
|
|
|
25
26
|
// Workspace resolution is shared across the Cursor hooks (#2587) — see
|
|
26
27
|
// hooks/lib/cursor-workspace.js. Staged next to these scripts by
|
|
@@ -29,7 +30,7 @@ const { resolveStatePath } = require('./lib/cursor-workspace.js');
|
|
|
29
30
|
|
|
30
31
|
let raw = '';
|
|
31
32
|
const stdinTimeout = setTimeout(() => {
|
|
32
|
-
|
|
33
|
+
allow(undefined);
|
|
33
34
|
}, 10000);
|
|
34
35
|
|
|
35
36
|
process.stdin.setEncoding('utf8');
|