@opengsd/gsd-core 1.15.0 → 1.17.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 -2
- package/.opencode/plugins/gsd-core.js +336 -6
- package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
- package/GEMINI.md +1 -1
- package/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +7 -1
- package/agents/gsd-debug-session-manager.md +7 -1
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +7 -2
- package/agents/gsd-planner.md +3 -3
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +42 -51
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +31 -9
- package/bin/gsd-mcp-server.js +3 -1
- package/bin/install.js +487 -290
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +5 -0
- package/commands/gsd/capture.md +8 -4
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +5 -1
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +5 -1
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +6 -1
- package/commands/gsd/quick-batch.md +20 -8
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +5 -1
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +159 -58
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +37 -5
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +78 -44
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +249 -117
- package/gsd-core/bin/lib/capability-validator.cjs +18 -1
- package/gsd-core/bin/lib/capability-writer.cjs +2 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +201 -178
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config.cjs +43 -12
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +413 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +142 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +81 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +3 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/init.cjs +194 -108
- package/gsd-core/bin/lib/install-engine.cjs +30 -35
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
- package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
- package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/mcp-server.cjs +5 -1
- package/gsd-core/bin/lib/milestone.cjs +19 -1
- package/gsd-core/bin/lib/model-resolver.cjs +18 -18
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
- package/gsd-core/bin/lib/opencode-version.cjs +486 -0
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id.cjs +1 -1
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +301 -80
- package/gsd-core/bin/lib/plan-document.cjs +93 -19
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +273 -40
- package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +13 -9
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
- package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
- package/gsd-core/bin/lib/roadmap.cjs +169 -72
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +186 -161
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +365 -594
- package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
- package/gsd-core/bin/lib/runtime-slash.cjs +60 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
- package/gsd-core/bin/lib/smart-entry.cjs +111 -56
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +67 -9
- package/gsd-core/bin/lib/state.cjs +668 -171
- package/gsd-core/bin/lib/surface.cjs +2 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
- package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
- package/gsd-core/bin/lib/uat.cjs +424 -7
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
- package/gsd-core/bin/lib/update-context.cjs +18 -1
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1265 -324
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +286 -567
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +10 -4
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- 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 +8 -7
- package/gsd-core/workflows/add-tests.md +3 -2
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +3 -2
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +28 -18
- package/gsd-core/workflows/check-todos.md +6 -5
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +5 -4
- package/gsd-core/workflows/code-review.md +100 -91
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +12 -7
- package/gsd-core/workflows/debug.md +30 -5
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +1 -1
- package/gsd-core/workflows/docs-update.md +4 -3
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +5 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- 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 +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase.md +81 -171
- package/gsd-core/workflows/execute-plan.md +13 -19
- package/gsd-core/workflows/explore.md +2 -2
- package/gsd-core/workflows/extract-learnings.md +3 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +3 -2
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +5 -3
- package/gsd-core/workflows/map-codebase.md +4 -3
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +10 -10
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
- package/gsd-core/workflows/plan-phase.md +27 -18
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +42 -31
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +17 -12
- package/gsd-core/workflows/review.md +3 -2
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +11 -12
- package/gsd-core/workflows/settings-advanced.md +1 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +12 -12
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +1 -1
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +5 -4
- package/gsd-core/workflows/ui-review.md +4 -3
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/validate-phase.md +10 -12
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +68 -59
- package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +34 -20
- package/hooks/dist/gsd-context-monitor.js +45 -13
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +263 -16
- package/hooks/dist/gsd-statusline.js +336 -29
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +8 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +34 -20
- package/hooks/gsd-context-monitor.js +45 -13
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +263 -16
- package/hooks/gsd-statusline.js +336 -29
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +21 -4
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +12 -8
- package/scripts/docs-guard-registry.cjs +6 -0
- package/scripts/gen-capability-registry.cjs +7 -1
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +14 -2
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +24 -2
- package/scripts/lib/ndjson-reporter.cjs +28 -3
- package/scripts/lib/platform-conformance-tier.generated.cjs +44 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +29 -0
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-test-file-count.allowlist.json +11 -1
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
- package/scripts/mutation-matrix.cjs +53 -6
- package/scripts/release-tarball-smoke.cjs +14 -7
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +5 -0
- package/skills/gsd-capture/SKILL.md +8 -4
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +5 -1
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +6 -1
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +20 -8
- package/skills/gsd-review/SKILL.md +5 -1
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
package/hooks/gsd-statusline.js
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
// Claude Code Statusline - GSD Edition
|
|
4
4
|
// Shows: model | current task (or GSD state) | directory | context usage
|
|
5
5
|
|
|
6
|
+
// #5183: Node's on-disk compile cache, so later runs reuse V8's compiled code
|
|
7
|
+
// for unchanged modules. Only as the entry (a test require() is unaffected),
|
|
8
|
+
// skipped under V8 coverage, optional (Bun stubs it), and never fatal.
|
|
9
|
+
// NODE_DISABLE_COMPILE_CACHE (any value) turns it off.
|
|
10
|
+
if (require.main === module && !process.env.NODE_V8_COVERAGE) {
|
|
11
|
+
try { require('node:module').enableCompileCache?.(); } catch { /* optimization only */ }
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
const fs = require('fs');
|
|
7
15
|
const path = require('path');
|
|
8
16
|
const os = require('os');
|
|
@@ -42,6 +50,7 @@ if (require.main === module) {
|
|
|
42
50
|
const { isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs');
|
|
43
51
|
const { PACKAGE_NAME, updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs');
|
|
44
52
|
const { normalizeStateStatus } = require('../gsd-core/bin/lib/state-document.cjs');
|
|
53
|
+
const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
|
|
45
54
|
const {
|
|
46
55
|
renderBracketPhaseDisplay,
|
|
47
56
|
renderBracketMilestoneDisplay,
|
|
@@ -161,6 +170,303 @@ function readStateFileOrNull(statePath) {
|
|
|
161
170
|
}
|
|
162
171
|
}
|
|
163
172
|
|
|
173
|
+
/**
|
|
174
|
+
* The env vars Claude Code itself consults to switch auto-compaction off, and
|
|
175
|
+
* the values it accepts as true. Both are taken from the shipped binary's own
|
|
176
|
+
* resolution — `Boolean(truthy(DISABLE_COMPACT) || DISABLE_AUTO_COMPACT)`, and
|
|
177
|
+
* only when that is false does `autoCompactEnabled` decide — rather than
|
|
178
|
+
* guessed: an invented name reads as "not disabled" forever, which is exactly
|
|
179
|
+
* the defect this gate exists to close.
|
|
180
|
+
*
|
|
181
|
+
* The truthy set applies to both keys: in the binary (claude 2.1.292) the
|
|
182
|
+
* second operand is the env registry's `D.bool()` field, which runs the same
|
|
183
|
+
* `["1","true","yes","on"]` helper as `DISABLE_COMPACT`.
|
|
184
|
+
*/
|
|
185
|
+
const AUTO_COMPACT_DISABLE_ENV_KEYS = ['DISABLE_AUTO_COMPACT', 'DISABLE_COMPACT'];
|
|
186
|
+
const AUTO_COMPACT_ENV_TRUTHY = ['1', 'true', 'yes', 'on'];
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Is Claude Code's auto-compact switched off for this session? When it is,
|
|
190
|
+
* the auto-compact buffer is ordinary usable context, so the meter's 100% is
|
|
191
|
+
* the model window — otherwise the bar pins at 100% early and the last of
|
|
192
|
+
* the window is invisible.
|
|
193
|
+
*
|
|
194
|
+
* The sources and their precedence mirror Claude Code's own resolution —
|
|
195
|
+
* either env var wins over the setting, and only then does the setting decide:
|
|
196
|
+
* 1. env: DISABLE_AUTO_COMPACT, or DISABLE_COMPACT (which switches off
|
|
197
|
+
* manual `/compact` as well). Truthy is `1` / `true` / `yes` / `on`,
|
|
198
|
+
* trimmed and case-insensitive — Claude Code's own accepted set.
|
|
199
|
+
* 2. <dir>/.claude/settings.local.json, <dir>/.claude/settings.json
|
|
200
|
+
* 3. (CLAUDE_CONFIG_DIR || ~/.claude)/settings.local.json, settings.json
|
|
201
|
+
* reading the boolean `autoCompactEnabled`. Fail-soft: anything unreadable
|
|
202
|
+
* or absent means "not disabled" (current behaviour).
|
|
203
|
+
*/
|
|
204
|
+
function isAutoCompactDisabled(dir, env = process.env) {
|
|
205
|
+
for (const key of AUTO_COMPACT_DISABLE_ENV_KEYS) {
|
|
206
|
+
const v = env[key];
|
|
207
|
+
if (typeof v === 'string' && AUTO_COMPACT_ENV_TRUTHY.includes(v.trim().toLowerCase())) return true;
|
|
208
|
+
}
|
|
209
|
+
return readClaudeSetting(dir, env, 'autoCompactEnabled', v => typeof v === 'boolean') === false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The first value of `key` that `accept` takes, reading the Claude Code
|
|
214
|
+
* settings files highest precedence first:
|
|
215
|
+
* <dir>/.claude/settings.local.json, <dir>/.claude/settings.json,
|
|
216
|
+
* (CLAUDE_CONFIG_DIR || ~/.claude)/settings.local.json, settings.json.
|
|
217
|
+
* Fail-soft: an absent or unparseable file is skipped; nothing found is
|
|
218
|
+
* `undefined`.
|
|
219
|
+
*/
|
|
220
|
+
function readClaudeSetting(dir, env, key, accept) {
|
|
221
|
+
// Lazy: stop at the first file that answers, so a lower file is never read.
|
|
222
|
+
for (const file of claudeSettingsPaths(dir, env)) {
|
|
223
|
+
const settings = readSettingsFile(file);
|
|
224
|
+
if (settings && accept(settings[key])) return settings[key];
|
|
225
|
+
}
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The parsed Claude Code settings files, highest precedence first. An absent,
|
|
231
|
+
* unparseable or non-object file is left out.
|
|
232
|
+
*/
|
|
233
|
+
function readClaudeSettingsFiles(dir, env) {
|
|
234
|
+
return claudeSettingsPaths(dir, env).map(readSettingsFile).filter(Boolean);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* A settings file larger than this is treated as absent. Claude Code's own
|
|
239
|
+
* settings files are a few kilobytes; the cap keeps a hostile project's
|
|
240
|
+
* `.claude/settings.json` from bloating a read that runs on every render.
|
|
241
|
+
*/
|
|
242
|
+
const SETTINGS_FILE_MAX_BYTES = 1024 * 1024;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The parsed settings object in `file`, or null. Only a regular file of at
|
|
246
|
+
* most SETTINGS_FILE_MAX_BYTES is read: the project's `.claude/` is
|
|
247
|
+
* repository content, and a FIFO there would block the statusline forever.
|
|
248
|
+
* The file is opened non-blocking and checked through its own descriptor,
|
|
249
|
+
* so the check and the read see the same file, and at most the checked size
|
|
250
|
+
* is read: a file that grew or shrank after the check is absent. A symlink
|
|
251
|
+
* is followed — a settings file linked in from a dotfiles repo is ordinary.
|
|
252
|
+
*/
|
|
253
|
+
function readSettingsFile(file) {
|
|
254
|
+
let fd;
|
|
255
|
+
try {
|
|
256
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NONBLOCK || 0));
|
|
257
|
+
const st = fs.fstatSync(fd);
|
|
258
|
+
if (!st.isFile() || st.size > SETTINGS_FILE_MAX_BYTES) return null;
|
|
259
|
+
// One byte past the checked size, so growth after the check shows as a longer read.
|
|
260
|
+
const buf = Buffer.alloc(st.size + 1);
|
|
261
|
+
let n = 0;
|
|
262
|
+
for (let r; n < buf.length && (r = fs.readSync(fd, buf, n, buf.length - n, n)) > 0;) n += r;
|
|
263
|
+
if (n !== st.size) return null;
|
|
264
|
+
const settings = JSON.parse(buf.toString('utf8', 0, n));
|
|
265
|
+
return settings && typeof settings === 'object' && !Array.isArray(settings) ? settings : null;
|
|
266
|
+
} catch (e) {
|
|
267
|
+
return null; // absent, unreadable or unparseable — keep looking
|
|
268
|
+
} finally {
|
|
269
|
+
if (fd !== undefined) {
|
|
270
|
+
try { fs.closeSync(fd); } catch (e) { /* already closed */ }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The Claude Code settings file paths, highest precedence first:
|
|
277
|
+
* <dir>/.claude/settings.local.json, <dir>/.claude/settings.json,
|
|
278
|
+
* (CLAUDE_CONFIG_DIR || ~/.claude)/settings.local.json, settings.json.
|
|
279
|
+
*/
|
|
280
|
+
function claudeSettingsPaths(dir, env) {
|
|
281
|
+
const candidates = [];
|
|
282
|
+
if (dir) {
|
|
283
|
+
candidates.push(path.join(dir, '.claude', 'settings.local.json'));
|
|
284
|
+
candidates.push(path.join(dir, '.claude', 'settings.json'));
|
|
285
|
+
}
|
|
286
|
+
let claudeDir;
|
|
287
|
+
try {
|
|
288
|
+
claudeDir = env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
289
|
+
} catch (e) {
|
|
290
|
+
claudeDir = null;
|
|
291
|
+
}
|
|
292
|
+
if (claudeDir) {
|
|
293
|
+
candidates.push(path.join(claudeDir, 'settings.local.json'));
|
|
294
|
+
candidates.push(path.join(claudeDir, 'settings.json'));
|
|
295
|
+
}
|
|
296
|
+
return candidates;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Claude Code's settings key for a model: its canonical name, which also
|
|
301
|
+
* matches the dated, `[1m]`, Bedrock and Vertex spellings of it
|
|
302
|
+
* (`claude-opus-5-5[1m]`, `us.anthropic.claude-opus-5-5-v1:0` and
|
|
303
|
+
* `claude-opus-5-5@20260101` all key as `claude-opus-5-5`). A Bedrock
|
|
304
|
+
* revision suffix is stripped only after a version or date segment, so a
|
|
305
|
+
* model whose own name ends in `-v<n>` (`anthropic.claude-v2`) keeps it.
|
|
306
|
+
* The rules repeat until nothing changes, so a key is its own key: one pass
|
|
307
|
+
* left `x[1m][1m]` as `x[1m]`. Lowercasing runs once, before the loop (it
|
|
308
|
+
* can lengthen a string: `İ` becomes two characters); inside the loop every
|
|
309
|
+
* rule only removes characters, so it ends. A key longer than
|
|
310
|
+
* MODEL_KEY_MAX_LENGTH is no model's — '' — which also bounds the loop's
|
|
311
|
+
* work on a hostile settings file. '' for a non-string.
|
|
312
|
+
*/
|
|
313
|
+
const MODEL_KEY_MAX_LENGTH = 256;
|
|
314
|
+
|
|
315
|
+
function canonicalModelKey(id) {
|
|
316
|
+
if (typeof id !== 'string') return '';
|
|
317
|
+
let key = id.toLowerCase();
|
|
318
|
+
if (key.length > MODEL_KEY_MAX_LENGTH) return '';
|
|
319
|
+
for (let prev; prev !== key;) {
|
|
320
|
+
prev = key;
|
|
321
|
+
key = key.trim()
|
|
322
|
+
.replace(/(?:\[1m\])+$/, '')
|
|
323
|
+
.replace(/^(?:[a-z]+\.)?anthropic\./, '')
|
|
324
|
+
.replace(/(\d)-v\d+(?::\d+)?$/, '$1')
|
|
325
|
+
.replace(/@\d{8}$/, '')
|
|
326
|
+
.replace(/-\d{8}$/, '');
|
|
327
|
+
}
|
|
328
|
+
return key;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isAutoCompactWindowValue(v) {
|
|
332
|
+
return v === 'auto' ||
|
|
333
|
+
(Number.isInteger(v) && v >= AUTO_COMPACT_WINDOW_MIN && v <= AUTO_COMPACT_WINDOW_MAX);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* The `autoCompactWindow` setting for this model, merged across the settings
|
|
338
|
+
* files the way Claude Code merges it (claude 2.1.292): lowest precedence
|
|
339
|
+
* first, each file's `modelSettings.<model>.autoCompactWindow` entries
|
|
340
|
+
* layer over the lower files', and a file that sets a top-level
|
|
341
|
+
* `autoCompactWindow` replaces the default and drops the lower files'
|
|
342
|
+
* per-model entries. The model's own entry wins over the default.
|
|
343
|
+
* `/autocompact` saves the per-model entry; older versions saved the
|
|
344
|
+
* top-level one. `"auto"` (Claude Code's tuned window) and an absent setting
|
|
345
|
+
* are null, so the caller falls back to the model window. An invalid value
|
|
346
|
+
* is treated as absent.
|
|
347
|
+
*/
|
|
348
|
+
function readAutoCompactWindowSetting(dir, env, modelId) {
|
|
349
|
+
let fallback;
|
|
350
|
+
let byModel = {};
|
|
351
|
+
for (const settings of readClaudeSettingsFiles(dir, env).reverse()) {
|
|
352
|
+
const own = {};
|
|
353
|
+
const perModel = settings.modelSettings;
|
|
354
|
+
if (perModel && typeof perModel === 'object' && !Array.isArray(perModel)) {
|
|
355
|
+
for (const [name, entry] of Object.entries(perModel)) {
|
|
356
|
+
const v = entry && typeof entry === 'object' ? entry.autoCompactWindow : undefined;
|
|
357
|
+
if (!isAutoCompactWindowValue(v)) continue;
|
|
358
|
+
const key = canonicalModelKey(name);
|
|
359
|
+
// A canonically spelled key wins over another spelling of it.
|
|
360
|
+
if (name === key || !Object.hasOwn(own, key)) own[key] = v;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (isAutoCompactWindowValue(settings.autoCompactWindow)) {
|
|
364
|
+
fallback = settings.autoCompactWindow;
|
|
365
|
+
byModel = own;
|
|
366
|
+
} else {
|
|
367
|
+
byModel = { ...byModel, ...own };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const key = canonicalModelKey(modelId);
|
|
371
|
+
const value = key && Object.hasOwn(byModel, key) ? byModel[key] : fallback;
|
|
372
|
+
return Number.isInteger(value) ? value : null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Where Claude Code auto-compacts (#4985). The meter's 100% is this token
|
|
377
|
+
* count, so it has to be resolved the way Claude Code resolves it:
|
|
378
|
+
*
|
|
379
|
+
* window CLAUDE_CODE_AUTO_COMPACT_WINDOW (an unparseable or non-positive
|
|
380
|
+
* value is ignored; capped at 1M, raised to 100k), else the
|
|
381
|
+
* `autoCompactWindow` setting `/autocompact` saves, per model or
|
|
382
|
+
* top-level (an integer in 100k–1M; see
|
|
383
|
+
* readAutoCompactWindowSetting), else the model window. Always
|
|
384
|
+
* capped at the model window.
|
|
385
|
+
* threshold window − min(model max output, 20,000) − 13,000, lowered to
|
|
386
|
+
* floor((window − 20,000) × pct / 100) when
|
|
387
|
+
* CLAUDE_AUTOCOMPACT_PCT_OVERRIDE (0 < pct ≤ 100) is lower.
|
|
388
|
+
*
|
|
389
|
+
* `/context` shows the 33,000 as "Autocompact buffer". The hardcoded 16.5%
|
|
390
|
+
* this replaces is 33,000 / 200,000, so it was right only on a 200K window
|
|
391
|
+
* with nothing configured. Every current model's max output is at least
|
|
392
|
+
* 20,000, so the reserve is taken as 20,000.
|
|
393
|
+
*
|
|
394
|
+
* Not visible to a statusline, so not modelled: the `--autocompact` launch
|
|
395
|
+
* flag, managed-policy settings, and Claude Code's server-side and per-model
|
|
396
|
+
* window defaults (what `"auto"` resolves to). Those sessions fall back to
|
|
397
|
+
* the model window.
|
|
398
|
+
*/
|
|
399
|
+
const AUTO_COMPACT_WINDOW_MIN = 100_000;
|
|
400
|
+
const AUTO_COMPACT_WINDOW_MAX = 1_000_000;
|
|
401
|
+
const AUTO_COMPACT_OUTPUT_RESERVE = 20_000;
|
|
402
|
+
const AUTO_COMPACT_SUMMARY_BUFFER = 13_000;
|
|
403
|
+
|
|
404
|
+
function parseAutoCompactWindowEnv(raw) {
|
|
405
|
+
if (typeof raw !== 'string' || raw.trim() === '') return null;
|
|
406
|
+
const s = raw.trim();
|
|
407
|
+
const n = /^\d[\d_,]*$/.test(s) ? parseInt(s.replace(/[_,]/g, ''), 10) : parseInt(s, 10);
|
|
408
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
409
|
+
return Math.max(AUTO_COMPACT_WINDOW_MIN, Math.min(AUTO_COMPACT_WINDOW_MAX, n));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* The token count at which Claude Code auto-compacts this session, or null
|
|
414
|
+
* when auto-compaction is off (see isAutoCompactDisabled).
|
|
415
|
+
*/
|
|
416
|
+
function resolveAutoCompactThreshold(modelWindow, dir, env = process.env, modelId) {
|
|
417
|
+
if (!(modelWindow > 0) || isAutoCompactDisabled(dir, env)) return null;
|
|
418
|
+
let window = parseAutoCompactWindowEnv(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW);
|
|
419
|
+
if (window === null) {
|
|
420
|
+
window = readAutoCompactWindowSetting(dir, env, modelId) ?? modelWindow;
|
|
421
|
+
}
|
|
422
|
+
const effective = Math.min(window, modelWindow) - AUTO_COMPACT_OUTPUT_RESERVE;
|
|
423
|
+
let threshold = effective - AUTO_COMPACT_SUMMARY_BUFFER;
|
|
424
|
+
const pct = parseFloat(env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE);
|
|
425
|
+
if (Number.isFinite(pct) && pct > 0 && pct <= 100) {
|
|
426
|
+
threshold = Math.min(threshold, Math.floor(effective * (pct / 100)));
|
|
427
|
+
}
|
|
428
|
+
return threshold > 0 ? threshold : null;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* The context meter, in one scale: `used` + `remaining` = 100, where 100% is
|
|
433
|
+
* the auto-compact threshold, or the model window when compaction is off.
|
|
434
|
+
* `usedTokens` is Claude Code's own count (input + cache creation + cache
|
|
435
|
+
* read of the last turn, the sum /context reports) and `limitTokens` the
|
|
436
|
+
* token count 100% stands for.
|
|
437
|
+
*
|
|
438
|
+
* Taken from token counts, not by re-scaling `remaining_percentage`: that
|
|
439
|
+
* value is already rounded against the model window, which loses up to 5,000
|
|
440
|
+
* tokens on a 1M model. Without `current_usage` the tokens are recovered
|
|
441
|
+
* from the percentage; without `context_window_size` the window is unknown,
|
|
442
|
+
* so the meter shows Claude Code's own percentage unscaled. Returns null
|
|
443
|
+
* when there is no usage yet.
|
|
444
|
+
*/
|
|
445
|
+
function contextMeter(contextWindow, dir, env = process.env, modelId) {
|
|
446
|
+
if (!contextWindow || typeof contextWindow !== 'object') return null;
|
|
447
|
+
const size = Number(contextWindow.context_window_size);
|
|
448
|
+
const usage = contextWindow.current_usage;
|
|
449
|
+
const remainingPct = contextWindow.remaining_percentage;
|
|
450
|
+
if (size > 0) {
|
|
451
|
+
let usedTokens = null;
|
|
452
|
+
if (usage && typeof usage === 'object') {
|
|
453
|
+
usedTokens = (Number(usage.input_tokens) || 0) +
|
|
454
|
+
(Number(usage.cache_creation_input_tokens) || 0) +
|
|
455
|
+
(Number(usage.cache_read_input_tokens) || 0);
|
|
456
|
+
} else if (remainingPct != null) {
|
|
457
|
+
usedTokens = Math.round(((100 - remainingPct) * size) / 100);
|
|
458
|
+
}
|
|
459
|
+
if (usedTokens !== null) {
|
|
460
|
+
const limitTokens = resolveAutoCompactThreshold(size, dir, env, modelId) ?? size;
|
|
461
|
+
const used = Math.max(0, Math.min(100, Math.round((usedTokens / limitTokens) * 100)));
|
|
462
|
+
return { used, remaining: 100 - used, usedTokens, limitTokens };
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (remainingPct == null) return null;
|
|
466
|
+
const used = Math.max(0, Math.min(100, Math.round(100 - remainingPct)));
|
|
467
|
+
return { used, remaining: 100 - used, usedTokens: null, limitTokens: null };
|
|
468
|
+
}
|
|
469
|
+
|
|
164
470
|
/**
|
|
165
471
|
* Walk up from dir looking for .planning/STATE.md (flat mode). If an ancestor
|
|
166
472
|
* has no flat STATE.md but IS in workstream mode (.planning/workstreams/
|
|
@@ -250,13 +556,12 @@ function readGsdState(dir, opts = {}) {
|
|
|
250
556
|
function parseStateMd(content) {
|
|
251
557
|
const state = {};
|
|
252
558
|
|
|
253
|
-
// YAML frontmatter
|
|
254
|
-
// #2754
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const fm = fmMatch[1];
|
|
559
|
+
// YAML frontmatter: the block the one fence owner finds (`locateFrontmatterFence`), so a
|
|
560
|
+
// CRLF STATE.md (#2754), a BOM, and a `---` inside a value read exactly as every other
|
|
561
|
+
// STATE.md reader reads them (found while implementing #5105).
|
|
562
|
+
const fence = locateFrontmatterFence(content);
|
|
563
|
+
if (fence && fence.closed) {
|
|
564
|
+
const fm = content.slice(fence.openEnd, fence.bodyEnd);
|
|
260
565
|
// Top-level scalar key: value
|
|
261
566
|
for (const line of fm.split(/\r?\n/)) {
|
|
262
567
|
const m = line.match(/^(\w+):\s*(.+)/);
|
|
@@ -822,28 +1127,22 @@ function runStatusline() {
|
|
|
822
1127
|
const model = compactModelName(data.model?.display_name || 'Claude');
|
|
823
1128
|
const dir = data.workspace?.current_dir || process.cwd();
|
|
824
1129
|
const session = data.session_id || '';
|
|
825
|
-
const remaining = data.context_window?.remaining_percentage;
|
|
826
1130
|
|
|
827
1131
|
// Read .planning config once — used by the context meter (token suffix)
|
|
828
1132
|
// and the last-command/position block below. Fail-soft to {}.
|
|
829
1133
|
let cfg = {};
|
|
830
1134
|
try { cfg = readGsdConfig(dir); } catch (e) {}
|
|
831
1135
|
|
|
832
|
-
// Context window display
|
|
833
|
-
//
|
|
834
|
-
//
|
|
835
|
-
// (
|
|
836
|
-
// the
|
|
837
|
-
|
|
838
|
-
const
|
|
839
|
-
const AUTO_COMPACT_BUFFER_PCT = acw > 0
|
|
840
|
-
? Math.min(100, Math.max(0, (1 - acw / totalCtx) * 100))
|
|
841
|
-
: 16.5;
|
|
1136
|
+
// Context window display: USED percentage of the auto-compact threshold,
|
|
1137
|
+
// so 100% is where Claude Code compacts — resolved from
|
|
1138
|
+
// CLAUDE_CODE_AUTO_COMPACT_WINDOW, the `autoCompactWindow` setting, or the
|
|
1139
|
+
// model window (#2219, #4985). With auto-compact disabled (#4844) 100% is
|
|
1140
|
+
// the model window. Project settings are read from workspace.project_dir,
|
|
1141
|
+
// Claude Code's launch directory, not the current directory.
|
|
1142
|
+
const meter = contextMeter(data.context_window, data.workspace?.project_dir || dir, process.env, data.model?.id);
|
|
842
1143
|
let ctx = '';
|
|
843
|
-
if (
|
|
844
|
-
|
|
845
|
-
const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
|
|
846
|
-
const used = Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
|
|
1144
|
+
if (meter) {
|
|
1145
|
+
const { used } = meter;
|
|
847
1146
|
|
|
848
1147
|
// Write context metrics to bridge file for the context-monitor PostToolUse hook.
|
|
849
1148
|
// The monitor reads this file to inject agent-facing warnings when context is low.
|
|
@@ -853,15 +1152,20 @@ function runStatusline() {
|
|
|
853
1152
|
if (sessionSafe) {
|
|
854
1153
|
try {
|
|
855
1154
|
const bridgePath = path.join(os.tmpdir(), `claude-ctx-${session}.json`);
|
|
856
|
-
//
|
|
857
|
-
//
|
|
858
|
-
//
|
|
859
|
-
//
|
|
860
|
-
|
|
1155
|
+
// The bridge is on the bar's scale: remaining_percentage counts down
|
|
1156
|
+
// to the auto-compact threshold, so the monitor's WARNING/CRITICAL
|
|
1157
|
+
// fire before compaction rather than after it (#4985). The token
|
|
1158
|
+
// counts let its message match /context, which is what #2451 kept
|
|
1159
|
+
// the bridge raw for; they are absent when the payload has no
|
|
1160
|
+
// context_window_size.
|
|
861
1161
|
const bridgeData = JSON.stringify({
|
|
862
1162
|
session_id: session,
|
|
863
|
-
remaining_percentage: remaining,
|
|
864
|
-
used_pct:
|
|
1163
|
+
remaining_percentage: meter.remaining,
|
|
1164
|
+
used_pct: used,
|
|
1165
|
+
...(meter.usedTokens !== null && {
|
|
1166
|
+
used_tokens: meter.usedTokens,
|
|
1167
|
+
threshold_tokens: meter.limitTokens,
|
|
1168
|
+
}),
|
|
865
1169
|
timestamp: Math.floor(Date.now() / 1000)
|
|
866
1170
|
});
|
|
867
1171
|
fs.writeFileSync(bridgePath, bridgeData);
|
|
@@ -1089,6 +1393,9 @@ module.exports = {
|
|
|
1089
1393
|
readStateHeadCommits, parseRevListCounts, deriveStateFreshness,
|
|
1090
1394
|
formatStateFreshness, resolveStatuslineOptions,
|
|
1091
1395
|
renderBracketPhaseDisplay, renderBracketMilestoneDisplay,
|
|
1396
|
+
isAutoCompactDisabled, AUTO_COMPACT_DISABLE_ENV_KEYS, AUTO_COMPACT_ENV_TRUTHY,
|
|
1397
|
+
resolveAutoCompactThreshold, contextMeter, SETTINGS_FILE_MAX_BYTES,
|
|
1398
|
+
parseAutoCompactWindowEnv, canonicalModelKey, isAutoCompactWindowValue,
|
|
1092
1399
|
};
|
|
1093
1400
|
|
|
1094
1401
|
/**
|
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
|
+
// #5183: Node's on-disk compile cache, so later runs reuse V8's compiled code
|
|
16
|
+
// for unchanged modules. Only as the entry (a test require() is unaffected),
|
|
17
|
+
// skipped under V8 coverage, optional (Bun stubs it), and never fatal.
|
|
18
|
+
// NODE_DISABLE_COMPILE_CACHE (any value) turns it off.
|
|
19
|
+
if (require.main === module && !process.env.NODE_V8_COVERAGE) {
|
|
20
|
+
try { require('node:module').enableCompileCache?.(); } catch { /* optimization only */ }
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
const fs = require('fs');
|
|
16
24
|
const path = require('path');
|
|
17
25
|
const os = require('os');
|
|
@@ -31,14 +31,23 @@ const fs = require('fs');
|
|
|
31
31
|
const path = require('path');
|
|
32
32
|
const { spawnSync } = require('child_process');
|
|
33
33
|
const { allow, deny } = require('./lib/hook-exit.js');
|
|
34
|
-
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
34
|
+
const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib/git-probe.js');
|
|
35
35
|
|
|
36
36
|
// #3911 (ADR-3889 Phase 7): the exit(2) call site (block(), below) is
|
|
37
37
|
// migrated to hook-exit.js's deny(undefined, reason) — see
|
|
38
38
|
// gsd-windsurf-pre-command.js's identical note for the fixed defect
|
|
39
39
|
// (terminateNow's fd 1/fd 2 writes now run in independent try/catch blocks).
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
// Shared blocking-guard probe budget (hooks/lib/git-probe.js, #5180): a timed-out
|
|
42
|
+
// probe fails open, so it is sized for a starved host. Windsurf documents NO
|
|
43
|
+
// hook timeout (docs.devin.ai/desktop/cascade/hooks lists only an aspirational
|
|
44
|
+
// "sub-100ms" target) and this surface's registration entry
|
|
45
|
+
// (buildWindsurfHookEntry in src/runtime-hooks-surface.cts) carries none, so the
|
|
46
|
+
// host budget is UNKNOWN. This guard runs at most 3 sequential probes (15 s
|
|
47
|
+
// worst case); that worst case is NOT claimed to fit any host budget — if
|
|
48
|
+
// Windsurf kills the hook sooner, the host kill fails open exactly as a probe
|
|
49
|
+
// timeout does. The guard keeps its fail-open posture either way.
|
|
50
|
+
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
|
|
42
51
|
|
|
43
52
|
function git(args, cwd) {
|
|
44
53
|
return spawnSync('git', args, { ...SPAWNOPT, cwd });
|
|
@@ -22,7 +22,7 @@ const path = require('path');
|
|
|
22
22
|
const { spawnSync } = require('child_process');
|
|
23
23
|
const { tokenize, skipToSubcommand } = require('./lib/git-cmd.js');
|
|
24
24
|
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
25
|
-
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
25
|
+
const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib/git-probe.js');
|
|
26
26
|
|
|
27
27
|
// This guard is almost entirely advisory (fail open — a broken advisory must
|
|
28
28
|
// never wedge every tool call), with ONE hard block (#3504 force-add-on-
|
|
@@ -87,11 +87,12 @@ function currentBranch(cwd) {
|
|
|
87
87
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
88
88
|
windowsHide: true,
|
|
89
89
|
// #3504: bounded — this ran unbounded before, an indefinite hang under a
|
|
90
|
-
// wedged git would hang every PreToolUse call.
|
|
91
|
-
// budget
|
|
90
|
+
// wedged git would hang every PreToolUse call. The probe gets the shared
|
|
91
|
+
// blocking-guard budget (hooks/lib/git-probe.js, #5180), well inside the
|
|
92
|
+
// 120s host budget this guard is registered with (#3981); a timeout
|
|
92
93
|
// returns '' (branch unknown), which both the block decision and the
|
|
93
94
|
// fail-closed re-check treat as "cannot establish agent branch".
|
|
94
|
-
timeout:
|
|
95
|
+
timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS,
|
|
95
96
|
killSignal: 'SIGTERM',
|
|
96
97
|
});
|
|
97
98
|
// #3911: a timeout/spawn-failure here previously degraded to '' exactly
|
|
@@ -18,7 +18,7 @@ const fs = require('fs');
|
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const { spawnSync } = require('child_process');
|
|
20
20
|
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
21
|
-
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
21
|
+
const { reportIfUndetermined, BLOCKING_GUARD_PROBE_TIMEOUT_MS } = require('./lib/git-probe.js');
|
|
22
22
|
|
|
23
23
|
// This guard's outer catch has always exited 0 (fail open): a path guard that
|
|
24
24
|
// cannot resolve the worktree must not block the user's edit — its whole job
|
|
@@ -28,7 +28,11 @@ const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
|
28
28
|
// rather than inheriting a default (#3911).
|
|
29
29
|
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
// A timed-out git probe fails OPEN (see ON_CRASH above), so the per-probe
|
|
32
|
+
// budget is the shared blocking-guard value, sized for a starved host rather
|
|
33
|
+
// than git's ~0.1s typical run (rationale and arithmetic in
|
|
34
|
+
// hooks/lib/git-probe.js, #5180). This guard runs at most 3 sequential probes.
|
|
35
|
+
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: BLOCKING_GUARD_PROBE_TIMEOUT_MS, windowsHide: true };
|
|
32
36
|
|
|
33
37
|
function git(args, cwd) {
|
|
34
38
|
return spawnSync('git', args, { ...SPAWNOPT, cwd });
|
package/hooks/hooks.json
CHANGED
|
@@ -12,32 +12,32 @@
|
|
|
12
12
|
{
|
|
13
13
|
"matcher": "Write|Edit",
|
|
14
14
|
"hooks": [
|
|
15
|
-
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-prompt-guard.js\"", "timeout":
|
|
15
|
+
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-prompt-guard.js\"", "timeout": 120 },
|
|
16
16
|
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-read-guard.js\"", "timeout": 5 }
|
|
17
17
|
]
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"matcher": "Write|Edit|MultiEdit",
|
|
21
21
|
"hooks": [
|
|
22
|
-
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-worktree-path-guard.js\"", "timeout":
|
|
22
|
+
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-worktree-path-guard.js\"", "timeout": 120 }
|
|
23
23
|
]
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"matcher": "Write",
|
|
27
27
|
"hooks": [
|
|
28
|
-
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-write-guard.js\"", "timeout":
|
|
28
|
+
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-write-guard.js\"", "timeout": 120 }
|
|
29
29
|
]
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"matcher": "Read|Grep|Bash",
|
|
33
33
|
"hooks": [
|
|
34
|
-
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-secret-read-guard.js\"", "timeout":
|
|
34
|
+
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-secret-read-guard.js\"", "timeout": 120 }
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
"matcher": "Agent|Task",
|
|
39
39
|
"hooks": [
|
|
40
|
-
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-agent-isolation-guard.js\"", "timeout":
|
|
40
|
+
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-agent-isolation-guard.js\"", "timeout": 120 }
|
|
41
41
|
]
|
|
42
42
|
}
|
|
43
43
|
],
|
package/hooks/lib/cli-exit.js
CHANGED
|
@@ -185,6 +185,17 @@ function setPendingOutcome(v) {
|
|
|
185
185
|
function getPendingOutcome() {
|
|
186
186
|
return globalThis[PENDING_OUTCOME_KEY];
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Declare the outcome a void-returning `main()` ends with (#5170, ADR-5057 §4): writes the
|
|
190
|
+
* pending-outcome cell that `runMain` projects. Validates `name` is an outcome
|
|
191
|
+
* `projectOutcome` accepts (PASS, FAIL or a registered name) and THROWS otherwise, so a typo'd
|
|
192
|
+
* name fails at the declaration rather than at process exit. Call it AFTER `output()`, which
|
|
193
|
+
* rewrites the cell on every call. `output()` and this are the only writers of the cell.
|
|
194
|
+
*/
|
|
195
|
+
function declareOutcome(name) {
|
|
196
|
+
projectOutcome(name, 'v1');
|
|
197
|
+
setPendingOutcome(name);
|
|
198
|
+
}
|
|
188
199
|
const EXIT_CONTRACT_FLAG_PREFIX = '--exit-contract=';
|
|
189
200
|
/** Scan argv for the FIRST `--exit-contract=<value>` token; undefined if absent. */
|
|
190
201
|
function findExitContractFlag(argv) {
|
|
@@ -557,4 +568,5 @@ module.exports = {
|
|
|
557
568
|
terminateNow,
|
|
558
569
|
setPendingOutcome,
|
|
559
570
|
getPendingOutcome,
|
|
571
|
+
declareOutcome,
|
|
560
572
|
};
|
package/hooks/lib/git-probe.js
CHANGED
|
@@ -81,4 +81,20 @@ function reportIfUndetermined(hookName, probeLabel, result) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
// Per-probe budget for the git probes of BLOCKING guards. A probe that times
|
|
85
|
+
// out fails OPEN (see reportIfUndetermined above), which is the same outcome
|
|
86
|
+
// as the host killing the hook — so a budget sized to git's ~0.1s typical run
|
|
87
|
+
// silently disables the gate on a starved host (a 2000ms budget was exceeded
|
|
88
|
+
// on a loaded Windows runner, #5180). Worst case for a guard that runs up to
|
|
89
|
+
// BLOCKING_GUARD_MAX_SEQUENTIAL_PROBES sequential probes:
|
|
90
|
+
// 3 x 5000ms + ~500ms node start/kill/reap = ~15.5s,
|
|
91
|
+
// inside the 120s host budget blocking guards are registered with (#3981).
|
|
92
|
+
const BLOCKING_GUARD_PROBE_TIMEOUT_MS = 5000;
|
|
93
|
+
const BLOCKING_GUARD_MAX_SEQUENTIAL_PROBES = 3;
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
classifyGitProbe,
|
|
97
|
+
reportIfUndetermined,
|
|
98
|
+
BLOCKING_GUARD_PROBE_TIMEOUT_MS,
|
|
99
|
+
BLOCKING_GUARD_MAX_SEQUENTIAL_PROBES,
|
|
100
|
+
};
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
// explicit fallback for the per-plan submodule-degrade override and shares
|
|
24
24
|
// the exact same atomic-write implementation.
|
|
25
25
|
//
|
|
26
|
-
// Sentinel path: `<cwd>/.gsd/dispatch-isolation-sentinel.json`.
|
|
27
|
-
//
|
|
26
|
+
// Sentinel path: `<cwd>/.gsd/dispatch-isolation-sentinel.json`. The writer
|
|
27
|
+
// seeds `.gsd/.gitignore` so this stays ignored in user projects too,
|
|
28
28
|
// and lives inside the checkout that ran the workflow — concurrent worktrees
|
|
29
29
|
// of the same repo are separate directories on disk, so each gets its own
|
|
30
30
|
// sentinel with no cross-worktree collision, no lock file needed.
|