@opengsd/gsd-core 1.17.0 → 1.19.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 +2 -1
- package/.opencode/plugins/gsd-core.js +2 -3
- package/agents/gsd-advisor-researcher.compact.md +1 -1
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-assumptions-analyzer.compact.md +1 -1
- package/agents/gsd-assumptions-analyzer.md +1 -1
- package/agents/gsd-code-fixer.compact.md +3 -3
- package/agents/gsd-code-fixer.md +5 -4
- package/agents/gsd-code-reviewer.compact.md +1 -1
- package/agents/gsd-code-reviewer.md +1 -1
- package/agents/gsd-codebase-mapper.compact.md +1 -1
- package/agents/gsd-codebase-mapper.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-verifier.compact.md +4 -4
- package/agents/gsd-doc-verifier.md +4 -3
- package/agents/gsd-doc-writer.compact.md +23 -5
- package/agents/gsd-doc-writer.md +19 -6
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +7 -3
- package/agents/gsd-integration-checker.compact.md +1 -1
- package/agents/gsd-integration-checker.md +1 -1
- package/agents/gsd-nyquist-auditor.compact.md +1 -1
- package/agents/gsd-nyquist-auditor.md +1 -1
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +2 -1
- package/agents/gsd-planner.md +5 -4
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +5 -5
- package/agents/gsd-research-synthesizer.md +5 -5
- package/agents/gsd-roadmapper.compact.md +1 -1
- package/agents/gsd-roadmapper.md +1 -1
- package/agents/gsd-security-auditor.compact.md +1 -1
- package/agents/gsd-security-auditor.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +1 -1
- package/agents/gsd-ui-auditor.md +1 -1
- package/agents/gsd-ui-checker.compact.md +1 -1
- package/agents/gsd-ui-checker.md +1 -1
- package/agents/gsd-ui-researcher.compact.md +13 -1
- package/agents/gsd-ui-researcher.md +28 -1
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +460 -105
- package/commands/gsd/code-review.md +8 -0
- package/commands/gsd/pause-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +124 -18
- package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
- package/gsd-core/bin/lib/args-terminator.cjs +114 -0
- package/gsd-core/bin/lib/broken-windows.cjs +59 -1
- package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
- package/gsd-core/bin/lib/capability-lock.cjs +41 -8
- package/gsd-core/bin/lib/capability-registry.cjs +162 -74
- package/gsd-core/bin/lib/capability-source.cjs +3 -1
- package/gsd-core/bin/lib/capability-state.cjs +20 -11
- package/gsd-core/bin/lib/capability-validator.cjs +23 -4
- package/gsd-core/bin/lib/commands.cjs +5 -1
- package/gsd-core/bin/lib/config-loader.cjs +13 -3
- package/gsd-core/bin/lib/config.cjs +256 -58
- package/gsd-core/bin/lib/core-utils.cjs +34 -0
- package/gsd-core/bin/lib/decision-coverage-support.cjs +93 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +8 -3
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +21 -1
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +19 -1
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
- package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +114 -18
- package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
- package/gsd-core/bin/lib/git-info-exclude.cjs +57 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +18 -3
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
- package/gsd-core/bin/lib/host-integration.cjs +13 -6
- package/gsd-core/bin/lib/init.cjs +76 -5
- package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
- package/gsd-core/bin/lib/install-engine.cjs +246 -32
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -7
- package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
- package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +113 -0
- package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
- package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
- package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
- package/gsd-core/bin/lib/milestone.cjs +43 -1
- package/gsd-core/bin/lib/model-catalog.cjs +8 -0
- package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
- package/gsd-core/bin/lib/phase-id.cjs +21 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +37 -4
- package/gsd-core/bin/lib/phase.cjs +235 -37
- package/gsd-core/bin/lib/planning-snapshot.cjs +3 -1
- package/gsd-core/bin/lib/planning-workspace.cjs +18 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
- package/gsd-core/bin/lib/read-only-git-env.cjs +58 -0
- package/gsd-core/bin/lib/report-parser.cjs +57 -1
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +35 -2
- package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
- package/gsd-core/bin/lib/roadmap-parser.cjs +12 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +39 -2
- package/gsd-core/bin/lib/roadmap.cjs +89 -30
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +428 -16
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
- package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
- package/gsd-core/bin/lib/shell-command-projection.cjs +123 -16
- package/gsd-core/bin/lib/smart-entry.cjs +37 -16
- package/gsd-core/bin/lib/state-contract.cjs +29 -9
- package/gsd-core/bin/lib/state-document.cjs +47 -5
- package/gsd-core/bin/lib/state-transition.cjs +150 -34
- package/gsd-core/bin/lib/state.cjs +140 -88
- package/gsd-core/bin/lib/surface.cjs +9 -0
- package/gsd-core/bin/lib/task-command-router.cjs +7 -28
- package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
- package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
- package/gsd-core/bin/lib/verification.cjs +96 -9
- package/gsd-core/bin/lib/verify.cjs +38 -1
- package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/references/agent-contracts.md +1 -1
- package/gsd-core/references/agent-skills-bootstrap.md +26 -7
- package/gsd-core/references/git-integration.md +14 -3
- package/gsd-core/references/loop-hook-dispatch.md +13 -6
- package/gsd-core/references/planner-reversibility.md +18 -4
- package/gsd-core/references/planner-verify-command-grounding.md +18 -0
- package/gsd-core/references/planning-config.md +2 -0
- package/gsd-core/references/tdd.md +5 -4
- package/gsd-core/references/verifier-phase-gates.md +8 -1
- package/gsd-core/templates/DEBUG.md +16 -0
- package/gsd-core/templates/config.json +2 -1
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/add-backlog.md +10 -2
- package/gsd-core/workflows/add-phase.md +5 -1
- package/gsd-core/workflows/add-tests.md +5 -1
- package/gsd-core/workflows/add-todo.md +5 -1
- package/gsd-core/workflows/ai-integration-phase.md +5 -1
- package/gsd-core/workflows/audit-fix.md +2 -1
- package/gsd-core/workflows/audit-milestone.md +21 -9
- package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
- package/gsd-core/workflows/autonomous.md +52 -64
- package/gsd-core/workflows/check-todos.md +5 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
- package/gsd-core/workflows/code-review-fix.md +5 -1
- package/gsd-core/workflows/code-review.md +66 -8
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
- package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
- package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
- package/gsd-core/workflows/complete-milestone.md +64 -157
- package/gsd-core/workflows/debug.md +5 -1
- package/gsd-core/workflows/diagnose-issues.md +34 -6
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +44 -5
- package/gsd-core/workflows/discuss-phase-power.md +19 -2
- package/gsd-core/workflows/discuss-phase.md +11 -11
- package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
- package/gsd-core/workflows/docs-update.md +27 -5
- package/gsd-core/workflows/edit-phase.md +5 -1
- package/gsd-core/workflows/eval-review.md +11 -3
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +19 -8
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +21 -3
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +7 -3
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
- package/gsd-core/workflows/execute-phase.md +46 -11
- package/gsd-core/workflows/execute-plan.md +8 -4
- package/gsd-core/workflows/extract-learnings.md +6 -2
- package/gsd-core/workflows/forensics.md +5 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +6 -2
- package/gsd-core/workflows/insert-phase.md +5 -1
- package/gsd-core/workflows/manager.md +10 -2
- package/gsd-core/workflows/map-codebase.md +5 -1
- package/gsd-core/workflows/milestone-summary.md +10 -2
- package/gsd-core/workflows/new-milestone.md +13 -5
- package/gsd-core/workflows/new-project.md +9 -5
- package/gsd-core/workflows/next.md +8 -4
- package/gsd-core/workflows/pause-work.md +3 -3
- package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +7 -3
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
- package/gsd-core/workflows/plan-phase.md +54 -86
- package/gsd-core/workflows/plan-review-convergence.md +17 -5
- package/gsd-core/workflows/plant-seed.md +8 -4
- package/gsd-core/workflows/progress.md +6 -2
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
- package/gsd-core/workflows/quick-batch.md +5 -1
- package/gsd-core/workflows/quick.md +23 -8
- package/gsd-core/workflows/remove-phase.md +5 -1
- package/gsd-core/workflows/resume-project.md +5 -1
- package/gsd-core/workflows/review.md +22 -15
- package/gsd-core/workflows/scan.md +5 -1
- package/gsd-core/workflows/section-manifest.json +5 -0
- package/gsd-core/workflows/secure-phase.md +6 -2
- package/gsd-core/workflows/settings.md +4 -3
- package/gsd-core/workflows/ship.md +16 -14
- package/gsd-core/workflows/spec-phase.md +3 -6
- package/gsd-core/workflows/thread.md +10 -2
- package/gsd-core/workflows/transition.md +5 -1
- package/gsd-core/workflows/ui-phase.md +5 -1
- package/gsd-core/workflows/ui-review.md +6 -2
- package/gsd-core/workflows/ultraplan-phase.md +5 -1
- package/gsd-core/workflows/update.md +5 -1
- package/gsd-core/workflows/validate-phase.md +6 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
- package/gsd-core/workflows/verify-work.md +15 -6
- package/hooks/dist/gsd-agent-isolation-guard.js +14 -2
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -3
- package/hooks/dist/gsd-path-export.js +95 -0
- package/hooks/dist/gsd-secret-read-guard.js +244 -53
- package/hooks/dist/gsd-statusline.js +35 -2
- package/hooks/dist/gsd-validate-commit.sh +27 -5
- package/hooks/dist/gsd-windsurf-pre-write.js +28 -1
- package/hooks/dist/gsd-workflow-guard.js +1 -1
- package/hooks/dist/gsd-worktree-path-guard.js +28 -1
- package/hooks/dist/lib/isolation-sentinel.js +44 -0
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +14 -2
- package/hooks/gsd-cursor-subagent-start.js +8 -3
- package/hooks/gsd-path-export.js +95 -0
- package/hooks/gsd-secret-read-guard.js +244 -53
- package/hooks/gsd-statusline.js +35 -2
- package/hooks/gsd-validate-commit.sh +27 -5
- package/hooks/gsd-windsurf-pre-write.js +28 -1
- package/hooks/gsd-workflow-guard.js +1 -1
- package/hooks/gsd-worktree-path-guard.js +28 -1
- package/hooks/hooks.json +1 -0
- package/hooks/lib/isolation-sentinel.js +44 -0
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +13 -6
- package/pi/gsd.cjs +1047 -10
- package/pi/package-agents/gsd-advisor-researcher.md +113 -0
- package/pi/package-agents/gsd-ai-researcher.md +111 -0
- package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
- package/pi/package-agents/gsd-code-fixer.md +769 -0
- package/pi/package-agents/gsd-code-reviewer.md +402 -0
- package/pi/package-agents/gsd-codebase-mapper.md +850 -0
- package/pi/package-agents/gsd-debug-session-manager.md +416 -0
- package/pi/package-agents/gsd-debugger.md +1274 -0
- package/pi/package-agents/gsd-doc-classifier.md +269 -0
- package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
- package/pi/package-agents/gsd-doc-verifier.md +212 -0
- package/pi/package-agents/gsd-doc-writer.md +626 -0
- package/pi/package-agents/gsd-dom-verifier.md +164 -0
- package/pi/package-agents/gsd-domain-researcher.md +144 -0
- package/pi/package-agents/gsd-eval-auditor.md +185 -0
- package/pi/package-agents/gsd-eval-planner.md +149 -0
- package/pi/package-agents/gsd-executor.md +894 -0
- package/pi/package-agents/gsd-framework-selector.md +159 -0
- package/pi/package-agents/gsd-integration-checker.md +476 -0
- package/pi/package-agents/gsd-intel-updater.md +338 -0
- package/pi/package-agents/gsd-mempalace-curator.md +49 -0
- package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
- package/pi/package-agents/gsd-pattern-mapper.md +340 -0
- package/pi/package-agents/gsd-phase-researcher.md +897 -0
- package/pi/package-agents/gsd-plan-checker.md +1077 -0
- package/pi/package-agents/gsd-planner.md +1003 -0
- package/pi/package-agents/gsd-project-researcher.md +611 -0
- package/pi/package-agents/gsd-research-synthesizer.md +259 -0
- package/pi/package-agents/gsd-roadmapper.md +779 -0
- package/pi/package-agents/gsd-security-auditor.md +171 -0
- package/pi/package-agents/gsd-ui-auditor.md +581 -0
- package/pi/package-agents/gsd-ui-checker.md +417 -0
- package/pi/package-agents/gsd-ui-researcher.md +469 -0
- package/pi/package-agents/gsd-user-profiler.md +174 -0
- package/pi/package-agents/gsd-verifier.md +963 -0
- package/pi/package-agents/manifest.json +39 -0
- package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
- package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
- package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
- package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
- package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
- package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
- package/pi/package-skills/gsd-capture/SKILL.md +68 -0
- package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
- package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
- package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
- package/pi/package-skills/gsd-config/SKILL.md +60 -0
- package/pi/package-skills/gsd-debug/SKILL.md +64 -0
- package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
- package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
- package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
- package/pi/package-skills/gsd-explore/SKILL.md +26 -0
- package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
- package/pi/package-skills/gsd-fast/SKILL.md +33 -0
- package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
- package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
- package/pi/package-skills/gsd-health/SKILL.md +36 -0
- package/pi/package-skills/gsd-help/SKILL.md +37 -0
- package/pi/package-skills/gsd-import/SKILL.md +46 -0
- package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
- package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
- package/pi/package-skills/gsd-manager/SKILL.md +46 -0
- package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
- package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
- package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
- package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
- package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
- package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
- package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
- package/pi/package-skills/gsd-next/SKILL.md +34 -0
- package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
- package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
- package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
- package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
- package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
- package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
- package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
- package/pi/package-skills/gsd-phase/SKILL.md +61 -0
- package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
- package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
- package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
- package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
- package/pi/package-skills/gsd-progress/SKILL.md +51 -0
- package/pi/package-skills/gsd-quick/SKILL.md +179 -0
- package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
- package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
- package/pi/package-skills/gsd-review/SKILL.md +50 -0
- package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
- package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
- package/pi/package-skills/gsd-settings/SKILL.md +30 -0
- package/pi/package-skills/gsd-ship/SKILL.md +27 -0
- package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
- package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
- package/pi/package-skills/gsd-spike/SKILL.md +53 -0
- package/pi/package-skills/gsd-stats/SKILL.md +22 -0
- package/pi/package-skills/gsd-surface/SKILL.md +177 -0
- package/pi/package-skills/gsd-thread/SKILL.md +29 -0
- package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
- package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
- package/pi/package-skills/gsd-undo/SKILL.md +38 -0
- package/pi/package-skills/gsd-update/SKILL.md +51 -0
- package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
- package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
- package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
- package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
- package/scripts/audit-workflow-script-paths.cjs +1 -1
- package/scripts/build-hooks.js +4 -0
- package/scripts/command-contract-helpers.cjs +1 -1
- package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
- package/scripts/docs-guard-registry.cjs +22 -7
- package/scripts/gen-health-docs.cjs +13 -1
- package/scripts/gen-pi-package-skills.cjs +217 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +19 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +41 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
- package/scripts/lint-phase-arg-assignment.cjs +2 -2
- package/scripts/lint-retired-runtime-name.cjs +5 -0
- package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
- package/scripts/npm-audit-baseline.cjs +41 -7
- package/scripts/release-tarball-smoke.cjs +1 -0
- package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
- package/scripts/select-docs-guards.cjs +1 -1
- package/scripts/self-test-report-policy.cjs +350 -0
- package/skills/gsd-code-review/SKILL.md +8 -0
- package/skills/gsd-pause-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
|
@@ -889,8 +889,40 @@ function compactModelName(name) {
|
|
|
889
889
|
// are a fixed array, and the workspace dir is passed via -C. Fails silently
|
|
890
890
|
// (segment absent) outside a repo, without git, or on timeout.
|
|
891
891
|
|
|
892
|
+
// #5048: bound for the `git status` spawn below. With GIT_OPTIONAL_LOCKS=0 the
|
|
893
|
+
// spawn no longer writes the refreshed index back, so what is left is reading
|
|
894
|
+
// the index and stat-ing the tracked paths. The bound keeps a hung network
|
|
895
|
+
// filesystem from stalling a render for long while leaving room for a cold stat
|
|
896
|
+
// cache on a large repo. The figure was chosen, not measured: time
|
|
897
|
+
// `git status --porcelain=v2 --branch` on a large tree with a cold cache before
|
|
898
|
+
// changing it. Exported so the test can pin the bound instead of restating the
|
|
899
|
+
// literal (which is how the constant and its assertion drifted before).
|
|
892
900
|
const GIT_STATUS_TIMEOUT_MS = 1500;
|
|
893
901
|
|
|
902
|
+
/**
|
|
903
|
+
* #5048: env for the read-only git spawn in this file.
|
|
904
|
+
*
|
|
905
|
+
* By default `git status` refreshes the index and takes an *optional*
|
|
906
|
+
* `.git/index.lock` to write the refreshed copy back. That is a write the
|
|
907
|
+
* caller never asked for, and it turns a pure read into a contender for the
|
|
908
|
+
* lock a real `git add` / `git commit` needs — a read-only statusline render
|
|
909
|
+
* can then make a concurrent commit fail with
|
|
910
|
+
* `Unable to create '.git/index.lock': File exists`. The statusline spawns
|
|
911
|
+
* git on every render, so the lock is re-created continuously.
|
|
912
|
+
*
|
|
913
|
+
* The env block itself is owned by src/read-only-git-env.cts and shared with
|
|
914
|
+
* every other read-only seam (`execGit` in src/shell-command-projection.cts,
|
|
915
|
+
* `readGitSignals` in src/smart-entry.cts, `gitExec` in
|
|
916
|
+
* src/pristine-baseline.cts, the two blocking guards). This hook reads it per
|
|
917
|
+
* call rather than snapshotting at load, so a later `process.env` mutation is
|
|
918
|
+
* visible and the seams behave identically.
|
|
919
|
+
*
|
|
920
|
+
* Deliberately NOT applied to the `git rev-list` spawn in
|
|
921
|
+
* readStateHeadCommits(): that walks the commit graph and never opens the
|
|
922
|
+
* index, so the variable would be a no-op there.
|
|
923
|
+
*/
|
|
924
|
+
const { readOnlyGitEnv } = require('../gsd-core/bin/lib/read-only-git-env.cjs');
|
|
925
|
+
|
|
894
926
|
/**
|
|
895
927
|
* Run `git status --porcelain=v2 --branch` in dir.
|
|
896
928
|
* Returns raw stdout, or null when git is missing, dir isn't a repo, or the
|
|
@@ -902,7 +934,7 @@ function readGitStatus(dir) {
|
|
|
902
934
|
// or untracked files; overflow still degrades safely to segment-absent via
|
|
903
935
|
// the catch below.
|
|
904
936
|
return childProcess.execFileSync('git', ['-C', dir, 'status', '--porcelain=v2', '--branch'],
|
|
905
|
-
{ encoding: 'utf8', timeout: GIT_STATUS_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true });
|
|
937
|
+
{ encoding: 'utf8', timeout: GIT_STATUS_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true, env: readOnlyGitEnv() });
|
|
906
938
|
} catch (e) {
|
|
907
939
|
return null;
|
|
908
940
|
}
|
|
@@ -995,7 +1027,7 @@ function isValidStateHeadStamp(raw) {
|
|
|
995
1027
|
*/
|
|
996
1028
|
function readStateHeadCommits(root, stamp) {
|
|
997
1029
|
try {
|
|
998
|
-
return childProcess.execFileSync('git',
|
|
1030
|
+
return childProcess.execFileSync('git', // allow-index-refresh: rev-list walks the commit graph and never opens the index
|
|
999
1031
|
['-C', root, 'rev-list', '--left-right', '--count', `${stamp}...HEAD`],
|
|
1000
1032
|
{ encoding: 'utf8', timeout: STATE_FRESHNESS_GIT_TIMEOUT_MS,
|
|
1001
1033
|
stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true });
|
|
@@ -1396,6 +1428,7 @@ module.exports = {
|
|
|
1396
1428
|
isAutoCompactDisabled, AUTO_COMPACT_DISABLE_ENV_KEYS, AUTO_COMPACT_ENV_TRUTHY,
|
|
1397
1429
|
resolveAutoCompactThreshold, contextMeter, SETTINGS_FILE_MAX_BYTES,
|
|
1398
1430
|
parseAutoCompactWindowEnv, canonicalModelKey, isAutoCompactWindowValue,
|
|
1431
|
+
GIT_STATUS_TIMEOUT_MS,
|
|
1399
1432
|
};
|
|
1400
1433
|
|
|
1401
1434
|
/**
|
|
@@ -39,6 +39,27 @@ cleanup_temp_files() {
|
|
|
39
39
|
}
|
|
40
40
|
trap cleanup_temp_files EXIT
|
|
41
41
|
|
|
42
|
+
# The first line of $1 (everything before the first newline), matched BYTE-wise.
|
|
43
|
+
#
|
|
44
|
+
# `${1%%$'\n'*}` is a longest-suffix glob match, and on bash 3.2 (macOS's
|
|
45
|
+
# /bin/bash) in a multibyte locale such as en_US.UTF-8 it costs ~3.5 s on a
|
|
46
|
+
# 112KB value (#5473, measured inside the macOS conformance job: the same hook
|
|
47
|
+
# on the same 114750-byte command took ~5.6 s under LC_ALL=en_US.UTF-8 and
|
|
48
|
+
# ~0.55 s under LC_ALL=C, and a trace put 3.5 s of it on this expansion). A
|
|
49
|
+
# newline is the same single byte in every locale, so matching it byte-wise
|
|
50
|
+
# changes no result.
|
|
51
|
+
#
|
|
52
|
+
# ONLY call this inside a command substitution, `SUBJECT=$(gsd_first_line "$MSG")`:
|
|
53
|
+
# the substitution's subshell is what keeps LC_ALL=C from leaking into the rest
|
|
54
|
+
# of the hook, whose regexes and length arithmetic keep the caller's locale. A
|
|
55
|
+
# substitution is a fork with no pipe between two processes, so it keeps the
|
|
56
|
+
# property the pure-expansion form was chosen for (no `head -1` SIGPIPE race
|
|
57
|
+
# under `set -euo pipefail`, #4429); printf is a builtin, so no exec either.
|
|
58
|
+
gsd_first_line() {
|
|
59
|
+
LC_ALL=C
|
|
60
|
+
printf '%s' "${1%%$'\n'*}"
|
|
61
|
+
}
|
|
62
|
+
|
|
42
63
|
# The 10 built-in Conventional Commits types — the SINGLE declaration (#3811
|
|
43
64
|
# review finding: this was previously hand-typed a second time inside the
|
|
44
65
|
# node -e script below, a generative-fix-divergence risk per CLAUDE.md's
|
|
@@ -575,18 +596,19 @@ if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
|
575
596
|
SUBJECT=$(GIT_CMD_LIB="$HOOK_DIR/lib/git-cmd.js" MSG="$MSG" node -e "
|
|
576
597
|
const {resolveCommitSubject}=require(process.env.GIT_CMD_LIB);
|
|
577
598
|
process.stdout.write(resolveCommitSubject(process.env.MSG));
|
|
578
|
-
" 2>/dev/null) || SUBJECT
|
|
599
|
+
" 2>/dev/null) || SUBJECT=$(gsd_first_line "$MSG")
|
|
579
600
|
else
|
|
580
|
-
# Pure parameter expansion, not
|
|
601
|
+
# Pure parameter expansion (in gsd_first_line), not
|
|
602
|
+
# `echo "$MSG" | head -1`: that pipeline
|
|
581
603
|
# raced a SIGPIPE under `set -euo pipefail` whenever $MSG had a body
|
|
582
604
|
# (the common case) — `head -1` can close its read end as soon as it
|
|
583
605
|
# has the first line, and if `echo`'s write lands after that close,
|
|
584
606
|
# `echo` dies with signal 13 (exit 141), which is NOT suppressed by
|
|
585
607
|
# `set -e` and aborted the whole hook intermittently (observed in
|
|
586
|
-
# tests/hooks-opt-in.test.cjs's --fixup=HEAD "round 7" case).
|
|
587
|
-
#
|
|
608
|
+
# tests/hooks-opt-in.test.cjs's --fixup=HEAD "round 7" case). No pipe
|
|
609
|
+
# means no race surface. Equivalent to
|
|
588
610
|
# `head -1` for single-line, multi-line, and trailing-newline input.
|
|
589
|
-
SUBJECT
|
|
611
|
+
SUBJECT=$(gsd_first_line "$MSG")
|
|
590
612
|
fi
|
|
591
613
|
# Single source of truth for the accepted commit-type list (#3811): the
|
|
592
614
|
# 10 built-ins plus whatever passed the safe-token filter above. Both the
|
|
@@ -49,8 +49,35 @@ const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib
|
|
|
49
49
|
// timeout does. The guard keeps its fail-open posture either way.
|
|
50
50
|
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
|
|
51
51
|
|
|
52
|
+
// #5048: GIT_OPTIONAL_LOCKS=0 on every git call from this hook. `git rev-parse`
|
|
53
|
+
// never opens the index, so this is inert for today's callers; it is set anyway
|
|
54
|
+
// so the read-only invariant has no holes if a `status` / `diff` caller is ever
|
|
55
|
+
// added here. git refreshes the index and takes an *optional*
|
|
56
|
+
// `.git/index.lock` on some reads, and this hook runs on every tool call, so a
|
|
57
|
+
// read here can otherwise fail a concurrent `git add` / `git commit` with
|
|
58
|
+
// `Unable to create '.git/index.lock': File exists`.
|
|
59
|
+
//
|
|
60
|
+
// The env block is owned by src/read-only-git-env.cts and shared with every
|
|
61
|
+
// other read-only seam (built artifact, not a sibling hooks/-tree file — same
|
|
62
|
+
// rule hooks/lib/git-cmd.js follows for token-scanner). It is read per spawn,
|
|
63
|
+
// not snapshotted at load, so the two guards behave identically.
|
|
64
|
+
// Parity: tests/git-optional-locks-parity.test.cjs.
|
|
65
|
+
// A raw plugin-marketplace or git-clone install never ran `npm run build:lib`
|
|
66
|
+
// (#3582), so the compiled owner can be absent. This guard only runs
|
|
67
|
+
// `git rev-parse`, which never opens the index, so the unmodified environment
|
|
68
|
+
// loses nothing; requiring the artifact unguarded would throw at load and the
|
|
69
|
+
// containment guard would silently stop running.
|
|
70
|
+
let readOnlyGitEnv = () => process.env;
|
|
71
|
+
try {
|
|
72
|
+
const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
73
|
+
ensureRuntimeBuild();
|
|
74
|
+
({ readOnlyGitEnv } = require('../gsd-core/bin/lib/read-only-git-env.cjs'));
|
|
75
|
+
} catch {
|
|
76
|
+
// artifact absent and not buildable: keep the inert fallback above
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
function git(args, cwd) {
|
|
53
|
-
return spawnSync('git', args, { ...SPAWNOPT, cwd });
|
|
80
|
+
return spawnSync('git', args, { ...SPAWNOPT, cwd, env: readOnlyGitEnv() });
|
|
54
81
|
}
|
|
55
82
|
|
|
56
83
|
// Walk up from `start` to find the nearest existing DIRECTORY (not merely an
|
|
@@ -81,7 +81,7 @@ function forceGitAddCwds(command, defaultCwd) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function currentBranch(cwd) {
|
|
84
|
-
const result = spawnSync('git', ['branch', '--show-current'], {
|
|
84
|
+
const result = spawnSync('git', ['branch', '--show-current'], { // allow-index-refresh: reads HEAD only, never opens the index
|
|
85
85
|
cwd,
|
|
86
86
|
encoding: 'utf8',
|
|
87
87
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
@@ -34,8 +34,35 @@ const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
|
34
34
|
// hooks/lib/git-probe.js, #5180). This guard runs at most 3 sequential probes.
|
|
35
35
|
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
|
|
36
36
|
|
|
37
|
+
// #5048: GIT_OPTIONAL_LOCKS=0 on every git call from this hook. `git rev-parse`
|
|
38
|
+
// never opens the index, so this is inert for today's callers; it is set anyway
|
|
39
|
+
// so the read-only invariant has no holes if a `status` / `diff` caller is ever
|
|
40
|
+
// added here. git refreshes the index and takes an *optional*
|
|
41
|
+
// `.git/index.lock` on some reads, and this hook runs on every tool call, so a
|
|
42
|
+
// read here can otherwise fail a concurrent `git add` / `git commit` with
|
|
43
|
+
// `Unable to create '.git/index.lock': File exists`.
|
|
44
|
+
//
|
|
45
|
+
// The env block is owned by src/read-only-git-env.cts and shared with every
|
|
46
|
+
// other read-only seam (built artifact, not a sibling hooks/-tree file — same
|
|
47
|
+
// rule hooks/lib/git-cmd.js follows for token-scanner). It is read per spawn,
|
|
48
|
+
// not snapshotted at load, so the two guards behave identically.
|
|
49
|
+
// Parity: tests/git-optional-locks-parity.test.cjs.
|
|
50
|
+
// A raw plugin-marketplace or git-clone install never ran `npm run build:lib`
|
|
51
|
+
// (#3582), so the compiled owner can be absent. This guard only runs
|
|
52
|
+
// `git rev-parse`, which never opens the index, so the unmodified environment
|
|
53
|
+
// loses nothing; requiring the artifact unguarded would throw at load and the
|
|
54
|
+
// containment guard would silently stop running.
|
|
55
|
+
let readOnlyGitEnv = () => process.env;
|
|
56
|
+
try {
|
|
57
|
+
const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
58
|
+
ensureRuntimeBuild();
|
|
59
|
+
({ readOnlyGitEnv } = require('../gsd-core/bin/lib/read-only-git-env.cjs'));
|
|
60
|
+
} catch {
|
|
61
|
+
// artifact absent and not buildable: keep the inert fallback above
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
function git(args, cwd) {
|
|
38
|
-
return spawnSync('git', args, { ...SPAWNOPT, cwd });
|
|
65
|
+
return spawnSync('git', args, { ...SPAWNOPT, cwd, env: readOnlyGitEnv() });
|
|
39
66
|
}
|
|
40
67
|
|
|
41
68
|
// Walk up from `start` to find the nearest existing directory.
|
|
@@ -91,6 +91,11 @@ const SENTINEL_RELATIVE_PATH = path.join('.gsd', 'dispatch-isolation-sentinel.js
|
|
|
91
91
|
// later, unrelated phase.
|
|
92
92
|
const SENTINEL_STALE_MS = 10 * 60 * 1000; // 10 minutes
|
|
93
93
|
|
|
94
|
+
// #5479: the longest a recorded sequential degrade keeps governing its own phase after the trust
|
|
95
|
+
// window (see agedNoneGovernsDispatch). A single executor run is minutes to a couple of hours; a
|
|
96
|
+
// day is a generous bound that still ends a stale or forged record's authority.
|
|
97
|
+
const AGED_NONE_MAX_MS = 24 * 60 * 60 * 1000;
|
|
98
|
+
|
|
94
99
|
function sentinelPath(cwd) {
|
|
95
100
|
return path.join(cwd, SENTINEL_RELATIVE_PATH);
|
|
96
101
|
}
|
|
@@ -280,6 +285,43 @@ function sentinelAppliesToDispatch(sentinel, dispatchIds) {
|
|
|
280
285
|
return true;
|
|
281
286
|
}
|
|
282
287
|
|
|
288
|
+
/**
|
|
289
|
+
* #5479: does an AGED recorded sequential degrade still govern THIS dispatch?
|
|
290
|
+
*
|
|
291
|
+
* The trust window (`SENTINEL_STALE_MS`) exists so a leftover sentinel cannot authorize an unisolated
|
|
292
|
+
* executor for a LATER, unrelated phase (#3045 F2). It also made the guard forget a legitimate
|
|
293
|
+
* degrade (the #683/#3060 base-check, the project opt-out) the moment the previous executor ran
|
|
294
|
+
* longer than the window: the next flag-less dispatch of the SAME phase was denied because the
|
|
295
|
+
* registry fallback answers "this host CAN isolate" and cannot see the shell-side degrade.
|
|
296
|
+
*
|
|
297
|
+
* True only when ALL of these hold, so the aged path can only ever loosen toward `none` for the exact
|
|
298
|
+
* phase the dispatch itself names:
|
|
299
|
+
* - the sentinel is present, well-formed, and records `isolation: 'none'` (an aged
|
|
300
|
+
* `harness-worktree`/`orchestrator-worktree` record authorizes nothing);
|
|
301
|
+
* - its `written_at` is not in the future (a forged or clock-skewed record is never trusted);
|
|
302
|
+
* - it carries a phase, the dispatch names a phase, and they are equal (a dispatch whose phase
|
|
303
|
+
* cannot be established is "cannot compare", which keeps today's fallback, never an allow).
|
|
304
|
+
* A plan-scoped record speaks only for its own plan (or a plan-less dispatch): a `none` recorded for
|
|
305
|
+
* a submodule-degraded plan A must not authorize a genuinely isolated plan B of the same phase. The
|
|
306
|
+
* phase-level degrade (the base-check, the project opt-out) is the record with no plan.
|
|
307
|
+
* - it is at most `AGED_NONE_MAX_MS` old: a static record, forged or left by a long-finished run,
|
|
308
|
+
* does not govern indefinitely. The normal flow re-records at every phase start and plan gate,
|
|
309
|
+
* so a live phase is always far younger.
|
|
310
|
+
* A fresh sentinel is handled by the existing path and never reaches here.
|
|
311
|
+
*
|
|
312
|
+
* `clock` is the injectable `{ now(): number }` seam `readSentinel` uses.
|
|
313
|
+
*/
|
|
314
|
+
function agedNoneGovernsDispatch(sentinel, dispatchIds, { clock = Date } = {}) {
|
|
315
|
+
if (!sentinel || sentinel.present !== true || sentinel.malformed) return false;
|
|
316
|
+
if (sentinel.isolation !== 'none') return false;
|
|
317
|
+
if (typeof sentinel.writtenAt !== 'number') return false;
|
|
318
|
+
const age = clock.now() - sentinel.writtenAt;
|
|
319
|
+
if (age < -5000 || age > AGED_NONE_MAX_MS) return false;
|
|
320
|
+
if (!sentinel.phase || !dispatchIds || !dispatchIds.phase) return false;
|
|
321
|
+
if (sentinel.plan && dispatchIds.plan && sentinel.plan !== dispatchIds.plan) return false;
|
|
322
|
+
return sentinel.phase === dispatchIds.phase;
|
|
323
|
+
}
|
|
324
|
+
|
|
283
325
|
/**
|
|
284
326
|
* #4594 F3: build the structured "a fresh sentinel was present but did not
|
|
285
327
|
* apply to this dispatch" descriptor, mirroring the exact comparison
|
|
@@ -307,10 +349,12 @@ module.exports = {
|
|
|
307
349
|
VALID_ISOLATION,
|
|
308
350
|
SENTINEL_RELATIVE_PATH,
|
|
309
351
|
SENTINEL_STALE_MS,
|
|
352
|
+
AGED_NONE_MAX_MS,
|
|
310
353
|
sentinelPath,
|
|
311
354
|
resolveSentinelRoot,
|
|
312
355
|
readSentinel,
|
|
313
356
|
extractDispatchIdentifiers,
|
|
314
357
|
sentinelAppliesToDispatch,
|
|
358
|
+
agedNoneGovernsDispatch,
|
|
315
359
|
buildSentinelDiscard,
|
|
316
360
|
};
|
|
@@ -29,6 +29,7 @@ const MANAGED_HOOKS = [
|
|
|
29
29
|
'gsd-cursor-subagent-stop.js',
|
|
30
30
|
'gsd-ensure-canonical-path.js',
|
|
31
31
|
'gsd-graphify-update.sh',
|
|
32
|
+
'gsd-path-export.js',
|
|
32
33
|
// #3662: portable node resolver (helper staged in hooks/; managed JS hook
|
|
33
34
|
// commands route through it under --portable-hooks).
|
|
34
35
|
'gsd-node-runner.sh',
|
|
@@ -71,7 +71,7 @@ if (require.main === module && !process.env.NODE_V8_COVERAGE) {
|
|
|
71
71
|
const fs = require('fs');
|
|
72
72
|
const path = require('path');
|
|
73
73
|
const os = require('os');
|
|
74
|
-
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch, buildSentinelDiscard } = require('./lib/isolation-sentinel.js');
|
|
74
|
+
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch, agedNoneGovernsDispatch, buildSentinelDiscard } = require('./lib/isolation-sentinel.js');
|
|
75
75
|
const { REASON_CODE, describeSentinelDiscard } = require('./lib/isolation-deny-reason.js');
|
|
76
76
|
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
77
77
|
|
|
@@ -227,7 +227,9 @@ function resolveRuntimeIdentity(cwd, configPath, resolveRuntimeNameFromCandidate
|
|
|
227
227
|
// never a resolution failure (this function only ever throws for the
|
|
228
228
|
// config.json read above, which the caller's catch already handles).
|
|
229
229
|
try {
|
|
230
|
-
|
|
230
|
+
// #5017: the store root is GSD_HOME when set and non-blank, else the home
|
|
231
|
+
// directory — the config loader's rule (src/runtime-homes.cts resolveGsdHome).
|
|
232
|
+
const defaultsPath = path.join(process.env.GSD_HOME || os.homedir(), '.gsd', 'defaults.json');
|
|
231
233
|
const defaultsRaw = fs.readFileSync(defaultsPath, 'utf-8');
|
|
232
234
|
const defaultsParsed = JSON.parse(defaultsRaw);
|
|
233
235
|
if (defaultsParsed && typeof defaultsParsed === 'object' && 'runtime' in defaultsParsed) {
|
|
@@ -463,6 +465,16 @@ function resolveIsolationState(cwd, { clock = Date, dispatchIds = null } = {}) {
|
|
|
463
465
|
};
|
|
464
466
|
}
|
|
465
467
|
|
|
468
|
+
// #5479: a recorded sequential degrade (`none`) for THIS dispatch's phase keeps governing it after the
|
|
469
|
+
// trust window. Without this, an executor that ran longer than SENTINEL_STALE_MS made the next
|
|
470
|
+
// flag-less dispatch of the same phase fall back to the registry capability ("this host CAN
|
|
471
|
+
// isolate") and be denied for a degrade the workflow legitimately recorded. The predicate only
|
|
472
|
+
// loosens toward `none`, only for the phase the dispatch itself names, never for a future-dated
|
|
473
|
+
// record (see agedNoneGovernsDispatch).
|
|
474
|
+
if (sentinel.present && sentinel.stale && agedNoneGovernsDispatch(sentinel, dispatchIds, { clock })) {
|
|
475
|
+
return { gsdProject: true, isolation: 'none', harnessFlag: null, error: null, sentinelDiscarded: null };
|
|
476
|
+
}
|
|
477
|
+
|
|
466
478
|
// Sentinel absent, stale, or does not apply to this dispatch (F2 mismatch):
|
|
467
479
|
// conservative fallback (#3045 finding — must still cover fail-closed case
|
|
468
480
|
// (a): a project that opted out of worktrees entirely via
|
|
@@ -70,7 +70,7 @@ const { allow } = require('./lib/hook-exit.js');
|
|
|
70
70
|
// hooks/lib/cursor-workspace.js. Staged next to these scripts by
|
|
71
71
|
// writeCursorHooksJson so the require always resolves post-install.
|
|
72
72
|
const { resolveStatePath } = require('./lib/cursor-workspace.js');
|
|
73
|
-
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch, buildSentinelDiscard } = require('./lib/isolation-sentinel.js');
|
|
73
|
+
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch, agedNoneGovernsDispatch, buildSentinelDiscard } = require('./lib/isolation-sentinel.js');
|
|
74
74
|
const { REASON_CODE, describeSentinelDiscard } = require('./lib/isolation-deny-reason.js');
|
|
75
75
|
// #3582: gsd-core/bin/lib/*.cjs (runtime-homes.cjs, worktree-safety.cjs,
|
|
76
76
|
// runtime-name-policy.cjs, capability-registry.cjs — required below, inside
|
|
@@ -435,7 +435,8 @@ function resolveFallbackIsolation(root, configPath) {
|
|
|
435
435
|
}
|
|
436
436
|
if (!runtimeId) {
|
|
437
437
|
try {
|
|
438
|
-
|
|
438
|
+
// #5017: GSD_HOME when set and non-blank, else the home directory — the loader's rule.
|
|
439
|
+
const defaultsPath = path.join(process.env.GSD_HOME || os.homedir(), '.gsd', 'defaults.json');
|
|
439
440
|
const defaultsParsed = JSON.parse(fs.readFileSync(defaultsPath, 'utf-8'));
|
|
440
441
|
if (defaultsParsed && typeof defaultsParsed === 'object' && 'runtime' in defaultsParsed) {
|
|
441
442
|
runtimeId = resolveRuntimeNameFromCandidates(defaultsParsed.runtime) || null;
|
|
@@ -517,9 +518,13 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
517
518
|
|
|
518
519
|
let declaredIsolation;
|
|
519
520
|
try {
|
|
521
|
+
// #5479: an aged recorded `none` for this dispatch's phase keeps governing it — the same predicate,
|
|
522
|
+
// and so the same verdicts, as the Claude guard (hooks/gsd-agent-isolation-guard.js).
|
|
520
523
|
declaredIsolation = (sentinel.present && !sentinel.stale && applies)
|
|
521
524
|
? sentinel.isolation
|
|
522
|
-
:
|
|
525
|
+
: (sentinel.present && sentinel.stale && agedNoneGovernsDispatch(sentinel, dispatchIds, { clock }))
|
|
526
|
+
? 'none'
|
|
527
|
+
: resolveFallbackIsolation(root, configPath);
|
|
523
528
|
} catch {
|
|
524
529
|
return {
|
|
525
530
|
action: 'deny',
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// gsd-hook-version: {{GSD_VERSION}}
|
|
3
|
+
// SessionStart hook: put the installed gsd-core `bin/` directory (which holds `gsd_run`) on the
|
|
4
|
+
// PATH of every later Bash tool call in this Claude Code session (#5465).
|
|
5
|
+
//
|
|
6
|
+
// Why a hook: the resolver snippet (gsd-core/workflows/_runtime-launcher.snippet.sh) persists its
|
|
7
|
+
// PATH export through $CLAUDE_ENV_FILE, but Claude Code provides that variable only to SessionStart,
|
|
8
|
+
// Setup, CwdChanged and FileChanged hooks, never to a workflow Bash block. So the write never fired
|
|
9
|
+
// and a later block that called `gsd_run` without re-including the resolver got "command not found"
|
|
10
|
+
// (hidden by `2>/dev/null || echo <fallback>`). This hook writes the same line from the one place
|
|
11
|
+
// the variable exists. The snippet's in-block write stays for users who export the variable
|
|
12
|
+
// themselves.
|
|
13
|
+
//
|
|
14
|
+
// Fail-open and side-effect-minimal by design: it only ever appends one line to the file Claude Code
|
|
15
|
+
// hands to SessionStart hooks, only for a directory that contains an installed `gsd_run`, and any
|
|
16
|
+
// problem (no variable, no gsd_run, unwritable file) exits 0 without output.
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const { allow } = require('./lib/hook-exit.js');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The `bin/` directory next to this hook's install root: hooks live in `<root>/hooks/` and the
|
|
26
|
+
* runtime in `<root>/gsd-core/` for global, project-local and plugin layouts alike. Returns the
|
|
27
|
+
* real path, or null when `gsd_run` is not there.
|
|
28
|
+
*/
|
|
29
|
+
function resolveBinDir(hookDir) {
|
|
30
|
+
try {
|
|
31
|
+
const bin = fs.realpathSync(path.join(hookDir, '..', 'gsd-core', 'bin'));
|
|
32
|
+
return fs.statSync(path.join(bin, 'gsd_run')).isFile() ? bin : null;
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The directory as the bash that sources $CLAUDE_ENV_FILE spells it. On Windows the env file is
|
|
40
|
+
* sourced by Git Bash, where `:` separates PATH entries and a drive letter is `/c/...`, which is
|
|
41
|
+
* also what the resolver snippet writes (it derives the directory from bash's `$GSD_TOOLS`). A
|
|
42
|
+
* native `C:\Users\x\...` would split at the colon and never resolve.
|
|
43
|
+
*/
|
|
44
|
+
function shellPath(dir, platform = process.platform) {
|
|
45
|
+
if (platform !== 'win32') return dir;
|
|
46
|
+
const m = /^([A-Za-z]):[\\/](.*)$/.exec(dir);
|
|
47
|
+
const rest = (m ? m[2] : dir.replace(/^[\\/]+/, '')).replace(/\\/g, '/');
|
|
48
|
+
return m ? `/${m[1].toLowerCase()}/${rest}` : dir.replace(/\\/g, '/');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The exact line the resolver snippet writes: `export PATH='<dir>':"$PATH"` with `'` escaped. */
|
|
52
|
+
function exportLine(binDir, platform = process.platform) {
|
|
53
|
+
return `export PATH='${shellPath(binDir, platform).replace(/'/g, "'\\''")}':"$PATH"`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Append the export to `envFile` unless it is already there. Returns true when it wrote.
|
|
58
|
+
* Never throws.
|
|
59
|
+
*/
|
|
60
|
+
function persistPath(envFile, binDir) {
|
|
61
|
+
try {
|
|
62
|
+
const line = exportLine(binDir);
|
|
63
|
+
let existing = '';
|
|
64
|
+
try {
|
|
65
|
+
existing = fs.readFileSync(envFile, 'utf8');
|
|
66
|
+
} catch (e) {
|
|
67
|
+
if (e && e.code !== 'ENOENT') return false;
|
|
68
|
+
}
|
|
69
|
+
if (existing.split(/\r?\n/).includes(line)) return false;
|
|
70
|
+
const prefix = existing === '' || existing.endsWith('\n') ? '' : '\n';
|
|
71
|
+
fs.appendFileSync(envFile, `${prefix}${line}\n`, 'utf8');
|
|
72
|
+
return true;
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function main() {
|
|
79
|
+
const envFile = process.env.CLAUDE_ENV_FILE;
|
|
80
|
+
if (typeof envFile !== 'string' || envFile.trim() === '') return;
|
|
81
|
+
const binDir = resolveBinDir(__dirname);
|
|
82
|
+
if (binDir === null) return;
|
|
83
|
+
persistPath(envFile, binDir);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (require.main === module) {
|
|
87
|
+
try {
|
|
88
|
+
main();
|
|
89
|
+
} catch {
|
|
90
|
+
// A PATH convenience must never fail session start.
|
|
91
|
+
}
|
|
92
|
+
allow();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = { resolveBinDir, shellPath, exportLine, persistPath };
|