@opengsd/gsd-core 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +12 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +22 -14
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +5 -3
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +23 -2
- package/agents/gsd-plan-checker.md +50 -53
- package/agents/gsd-planner.md +50 -50
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +13 -4
- package/bin/install.js +1448 -1103
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +9 -5
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1035 -138
- package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
- package/gsd-core/bin/lib/api-coverage.cjs +33 -14
- package/gsd-core/bin/lib/artifacts.cjs +5 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +1026 -268
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +845 -130
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +700 -40
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +216 -42
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +1172 -108
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
- package/gsd-core/bin/lib/config-loader.cjs +187 -23
- package/gsd-core/bin/lib/config.cjs +102 -3
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +208 -33
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +899 -229
- package/gsd-core/bin/lib/gap-checker.cjs +95 -10
- package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +439 -168
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +811 -259
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
- package/gsd-core/bin/lib/install-profiles.cjs +212 -61
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +170 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +183 -22
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +842 -73
- package/gsd-core/bin/lib/model-catalog.cjs +232 -16
- package/gsd-core/bin/lib/model-resolver.cjs +193 -68
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
- package/gsd-core/bin/lib/phase-id.cjs +514 -40
- package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
- package/gsd-core/bin/lib/phase-locator.cjs +262 -34
- package/gsd-core/bin/lib/phase.cjs +1038 -214
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +98 -3
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
- package/gsd-core/bin/lib/probe-core.cjs +5 -2
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
- package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +442 -96
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
- package/gsd-core/bin/lib/smart-entry.cjs +133 -23
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +52 -19
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +338 -8
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +846 -176
- package/gsd-core/bin/lib/state.cjs +2589 -369
- package/gsd-core/bin/lib/surface.cjs +33 -11
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
- package/gsd-core/bin/lib/uat.cjs +1761 -167
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
- package/gsd-core/bin/lib/unusable-input.cjs +37 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +75 -0
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +272 -9
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +453 -918
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +44 -26
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +39 -21
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +4 -6
- package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +17 -11
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +5 -4
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +61 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +44 -13
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +5 -5
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -17
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +5 -4
- package/gsd-core/templates/state.md +11 -4
- package/gsd-core/templates/verification-report.md +9 -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 +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +13 -20
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +11 -27
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +64 -5
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
- package/gsd-core/workflows/code-review-fix.md +38 -11
- package/gsd-core/workflows/code-review.md +159 -52
- package/gsd-core/workflows/complete-milestone.md +151 -23
- package/gsd-core/workflows/debug.md +12 -8
- package/gsd-core/workflows/diagnose-issues.md +47 -15
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +27 -2
- package/gsd-core/workflows/eval-review.md +7 -14
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +72 -100
- package/gsd-core/workflows/execute-plan.md +52 -15
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +76 -10
- package/gsd-core/workflows/import.md +18 -15
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +49 -16
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +8 -5
- package/gsd-core/workflows/new-milestone.md +22 -29
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +26 -40
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +14 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +162 -59
- package/gsd-core/workflows/plan-review-convergence.md +96 -11
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +61 -18
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
- package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
- package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +55 -44
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +219 -20
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +69 -7
- package/gsd-core/workflows/ship.md +116 -50
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +53 -13
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +64 -9
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +49 -14
- package/gsd-core/workflows/ui-phase.md +15 -21
- package/gsd-core/workflows/ui-review.md +8 -12
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +7 -11
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +66 -25
- package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
- package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +37 -27
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +55 -32
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +231 -24
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +162 -46
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +158 -30
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +2 -3
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +83 -3
- package/hooks/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +37 -27
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +55 -32
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +231 -24
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +162 -46
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +28 -11
- package/pi/gsd.cjs +19 -5
- package/scripts/base64-scan.sh +74 -12
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/build-hooks.js +5 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +64 -14
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +11 -21
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +138 -25
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +308 -0
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +492 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +471 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +488 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +950 -0
- package/scripts/lint-test-file-count.allowlist.json +137 -8
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +830 -0
- package/scripts/lint-vendored-deps.cjs +297 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +100 -14
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +24 -7
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +9 -5
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -107
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -7,10 +7,43 @@
|
|
|
7
7
|
#
|
|
8
8
|
# OPT-IN: This hook is a no-op unless config.json has hooks.community: true.
|
|
9
9
|
# Enable with: "hooks": { "community": true } in .planning/config.json
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
# Temp files created below for subprocess stderr capture (config read, command
|
|
13
|
+
# extraction, classifier). A single EXIT trap replaces three hand-rolled
|
|
14
|
+
# mktemp/rm-f pairs so an early or unexpected exit path can never leak one —
|
|
15
|
+
# and a future fourth check does not need its own copy (#3911 review).
|
|
16
|
+
# Idempotent and failure-proof by construction: unset vars expand to "" (a
|
|
17
|
+
# no-op rm -f target), and `|| true` guarantees the trap itself never changes
|
|
18
|
+
# the script's exit status.
|
|
19
|
+
ENABLED_ERR=""
|
|
20
|
+
CMD_ERR=""
|
|
21
|
+
CLASSIFY_ERR=""
|
|
22
|
+
cleanup_temp_files() {
|
|
23
|
+
rm -f "${ENABLED_ERR:-}" "${CMD_ERR:-}" "${CLASSIFY_ERR:-}" 2>/dev/null || true
|
|
24
|
+
}
|
|
25
|
+
trap cleanup_temp_files EXIT
|
|
10
26
|
|
|
11
27
|
# Check opt-in config — exit silently if not enabled
|
|
12
28
|
if [ -f .planning/config.json ]; then
|
|
13
|
-
|
|
29
|
+
ENABLED_ERR=$(mktemp)
|
|
30
|
+
ENABLED=$(node -e "
|
|
31
|
+
try{
|
|
32
|
+
const c=require('./.planning/config.json');
|
|
33
|
+
process.stdout.write(c.hooks?.community===true?'1':'0');
|
|
34
|
+
}catch(e){
|
|
35
|
+
process.stderr.write('CONFIG_READ_FAILED: '+(e&&e.message?e.message:String(e)));
|
|
36
|
+
process.exit(3);
|
|
37
|
+
}
|
|
38
|
+
" 2>"$ENABLED_ERR") || CONFIG_STATUS=$?
|
|
39
|
+
CONFIG_STATUS=${CONFIG_STATUS:-0}
|
|
40
|
+
if [ "$CONFIG_STATUS" != "0" ]; then
|
|
41
|
+
# Could not determine the opt-in flag at all (node missing, JSON parse
|
|
42
|
+
# error other than absence, etc.) — distinct from ".planning/config.json
|
|
43
|
+
# exists and legitimately disables the hook". Say so and pass, per #3838.
|
|
44
|
+
echo "gsd-validate-commit.sh: could not read .planning/config.json (opt-in check) — validator disabled for this call. $(cat "$ENABLED_ERR")" >&2
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
14
47
|
if [ "$ENABLED" != "1" ]; then exit 0; fi
|
|
15
48
|
else
|
|
16
49
|
exit 0
|
|
@@ -19,17 +52,58 @@ fi
|
|
|
19
52
|
INPUT=$(cat)
|
|
20
53
|
|
|
21
54
|
# Extract command from JSON using Node (handles escaping correctly, no jq needed)
|
|
22
|
-
|
|
55
|
+
CMD_ERR=$(mktemp)
|
|
56
|
+
CMD=$(echo "$INPUT" | node -e "
|
|
57
|
+
let d='';
|
|
58
|
+
process.stdin.on('data',c=>d+=c);
|
|
59
|
+
process.stdin.on('end',()=>{
|
|
60
|
+
try{
|
|
61
|
+
process.stdout.write(JSON.parse(d).tool_input?.command||'');
|
|
62
|
+
}catch(e){
|
|
63
|
+
process.stderr.write('COMMAND_EXTRACTION_FAILED: '+(e&&e.message?e.message:String(e)));
|
|
64
|
+
process.exit(3);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
" 2>"$CMD_ERR") || CMD_STATUS=$?
|
|
68
|
+
CMD_STATUS=${CMD_STATUS:-0}
|
|
69
|
+
if [ "$CMD_STATUS" != "0" ]; then
|
|
70
|
+
# Could not extract tool_input.command at all (node missing, malformed
|
|
71
|
+
# JSON, etc.) — distinct from "there is genuinely no command field". Say
|
|
72
|
+
# so and pass, per #3838.
|
|
73
|
+
echo "gsd-validate-commit.sh: could not extract tool_input.command from the hook payload — validator disabled for this call. $(cat "$CMD_ERR")" >&2
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
23
76
|
|
|
24
77
|
# Only check git commit commands.
|
|
25
78
|
# Delegates to hooks/lib/git-cmd.js isGitSubcommand() — the canonical token-walk
|
|
26
79
|
# classifier that handles env-prefix, -C path, and full-path git invocations.
|
|
27
80
|
# A naive `^git\s+commit` regex misses all three; this guard fixes that (#3129).
|
|
28
81
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
82
|
+
CLASSIFY_ERR=$(mktemp)
|
|
83
|
+
GIT_CMD_LIB="$HOOK_DIR/lib/git-cmd.js" node -e "
|
|
84
|
+
try {
|
|
85
|
+
const {isGitSubcommand}=require(process.env.GIT_CMD_LIB);
|
|
86
|
+
process.exit(isGitSubcommand(process.argv[1],'commit')?0:1);
|
|
87
|
+
} catch(e) {
|
|
88
|
+
process.stderr.write('CLASSIFIER_THREW: '+(e&&e.message?e.message:String(e)));
|
|
89
|
+
process.exit(3);
|
|
90
|
+
}
|
|
91
|
+
" "$CMD" 2>"$CLASSIFY_ERR" || CLASSIFY_STATUS=$?
|
|
92
|
+
CLASSIFY_STATUS=${CLASSIFY_STATUS:-0}
|
|
93
|
+
if [ "$CLASSIFY_STATUS" != "0" ] && [ "$CLASSIFY_STATUS" != "1" ]; then
|
|
94
|
+
# 0 = is a git commit (validate below); 1 = genuinely not a git commit
|
|
95
|
+
# (real negative, pass silently) — the ONLY intentional non-zero exit the
|
|
96
|
+
# script above ever produces on success. Any other status — 127 node
|
|
97
|
+
# missing, or 3 from the try/catch above when the git-cmd.js require chain
|
|
98
|
+
# throws (e.g. its built dependency, gsd-core/bin/lib/token-scanner.cjs, is
|
|
99
|
+
# a gitignored build artifact and absent on a fresh checkout — run
|
|
100
|
+
# `npm run build:lib`) — means the classifier could not run at all. Say so
|
|
101
|
+
# on stderr and pass (#3838): PreToolUse stderr does not disturb the JSON
|
|
102
|
+
# protocol.
|
|
103
|
+
echo "gsd-validate-commit.sh: could not classify the command via hooks/lib/git-cmd.js (exit $CLASSIFY_STATUS) — validator disabled for this call. If this persists, run \`npm run build:lib\`. $(cat "$CLASSIFY_ERR")" >&2
|
|
104
|
+
exit 0
|
|
105
|
+
fi
|
|
106
|
+
if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
33
107
|
# Extract message from -m flag
|
|
34
108
|
MSG=""
|
|
35
109
|
if [[ "$CMD" =~ -m[[:space:]]+\"([^\"]+)\" ]]; then
|
|
@@ -47,6 +47,16 @@
|
|
|
47
47
|
|
|
48
48
|
'use strict';
|
|
49
49
|
|
|
50
|
+
const { allow, deny } = require('./lib/hook-exit.js');
|
|
51
|
+
|
|
52
|
+
// #3911 (ADR-3889 Phase 7): the exit(2) call site (block(), below) is
|
|
53
|
+
// migrated to hook-exit.js's deny(undefined, reason) now that
|
|
54
|
+
// hooks/lib/cli-exit.js's terminateNow emits fd 1 and fd 2 (stderrPayload)
|
|
55
|
+
// in INDEPENDENT try/catch blocks — `payload=undefined` cleanly skips the
|
|
56
|
+
// fd 1 write (preserving "nothing written to stdout") instead of throwing
|
|
57
|
+
// into a shared catch that used to also swallow the fd 2 write, which is
|
|
58
|
+
// what silently dropped the deny reason before this fix.
|
|
59
|
+
|
|
50
60
|
// No realistic destructive command comes anywhere close to this length.
|
|
51
61
|
const MAX_COMMAND_LENGTH = 4096;
|
|
52
62
|
|
|
@@ -244,16 +254,11 @@ function destructiveReason(cmd) {
|
|
|
244
254
|
}
|
|
245
255
|
|
|
246
256
|
function block(reason) {
|
|
247
|
-
|
|
248
|
-
process.exit(2);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function allow() {
|
|
252
|
-
process.exit(0);
|
|
257
|
+
deny(undefined, `GSD windsurf pre_run_command guard: ${reason}\n`);
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
let input = '';
|
|
256
|
-
const stdinTimeout = setTimeout(() =>
|
|
261
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 10000);
|
|
257
262
|
process.stdin.setEncoding('utf8');
|
|
258
263
|
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
259
264
|
process.stdin.on('end', () => {
|
|
@@ -262,14 +267,14 @@ process.stdin.on('end', () => {
|
|
|
262
267
|
const data = JSON.parse(input || '{}');
|
|
263
268
|
const toolInfo = (data && typeof data.tool_info === 'object' && data.tool_info) || {};
|
|
264
269
|
const commandLine = typeof toolInfo.command_line === 'string' ? toolInfo.command_line : '';
|
|
265
|
-
if (!commandLine) { allow(); return; }
|
|
266
|
-
if (commandLine.length > MAX_COMMAND_LENGTH) { allow(); return; }
|
|
270
|
+
if (!commandLine) { allow(undefined); return; }
|
|
271
|
+
if (commandLine.length > MAX_COMMAND_LENGTH) { allow(undefined); return; }
|
|
267
272
|
|
|
268
273
|
const reason = destructiveReason(commandLine);
|
|
269
274
|
if (reason) { block(reason); return; }
|
|
270
|
-
allow();
|
|
275
|
+
allow(undefined);
|
|
271
276
|
} catch {
|
|
272
277
|
// Silent fail-open — never block a valid tool call due to a hook bug.
|
|
273
|
-
allow();
|
|
278
|
+
allow(undefined);
|
|
274
279
|
}
|
|
275
280
|
});
|
|
@@ -30,6 +30,13 @@
|
|
|
30
30
|
const fs = require('fs');
|
|
31
31
|
const path = require('path');
|
|
32
32
|
const { spawnSync } = require('child_process');
|
|
33
|
+
const { allow, deny } = require('./lib/hook-exit.js');
|
|
34
|
+
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
35
|
+
|
|
36
|
+
// #3911 (ADR-3889 Phase 7): the exit(2) call site (block(), below) is
|
|
37
|
+
// migrated to hook-exit.js's deny(undefined, reason) — see
|
|
38
|
+
// gsd-windsurf-pre-command.js's identical note for the fixed defect
|
|
39
|
+
// (terminateNow's fd 1/fd 2 writes now run in independent try/catch blocks).
|
|
33
40
|
|
|
34
41
|
const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000, windowsHide: true };
|
|
35
42
|
|
|
@@ -54,16 +61,11 @@ function nearestExistingDir(start) {
|
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
function block(reason) {
|
|
57
|
-
|
|
58
|
-
process.exit(2);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function allow() {
|
|
62
|
-
process.exit(0);
|
|
64
|
+
deny(undefined, `GSD windsurf pre_write_code guard: ${reason}\n`);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
let input = '';
|
|
66
|
-
const stdinTimeout = setTimeout(() =>
|
|
68
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 10000);
|
|
67
69
|
process.stdin.setEncoding('utf8');
|
|
68
70
|
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
69
71
|
process.stdin.on('end', () => {
|
|
@@ -72,14 +74,19 @@ process.stdin.on('end', () => {
|
|
|
72
74
|
const data = JSON.parse(input || '{}');
|
|
73
75
|
const toolInfo = (data && typeof data.tool_info === 'object' && data.tool_info) || {};
|
|
74
76
|
const rawFilePath = typeof toolInfo.file_path === 'string' ? toolInfo.file_path : '';
|
|
75
|
-
if (!rawFilePath) { allow(); return; }
|
|
77
|
+
if (!rawFilePath) { allow(undefined); return; }
|
|
76
78
|
|
|
77
79
|
const cwd = process.cwd();
|
|
78
80
|
|
|
79
81
|
// Determine the active project's git root. No git root at all -> nothing
|
|
80
82
|
// to enforce a boundary against -> fail open.
|
|
81
83
|
const cwdTopResult = git(['rev-parse', '--show-toplevel'], cwd);
|
|
82
|
-
|
|
84
|
+
// #3911: a timeout/spawn-failure result is indistinguishable from a clean
|
|
85
|
+
// "no git root here" answer by status/stdout alone — reportIfUndetermined
|
|
86
|
+
// is a no-op on a genuine negative and only fires when the probe itself
|
|
87
|
+
// could not run. The allow() below is UNCHANGED either way.
|
|
88
|
+
reportIfUndetermined('gsd-windsurf-pre-write', 'git rev-parse --show-toplevel (cwd)', cwdTopResult);
|
|
89
|
+
if (cwdTopResult.status !== 0 || !cwdTopResult.stdout) { allow(undefined); return; }
|
|
83
90
|
const cwdTopRaw = cwdTopResult.stdout.trim();
|
|
84
91
|
|
|
85
92
|
const filePath = path.isAbsolute(rawFilePath) ? path.resolve(rawFilePath) : path.resolve(cwd, rawFilePath);
|
|
@@ -95,14 +102,16 @@ process.stdin.on('end', () => {
|
|
|
95
102
|
}
|
|
96
103
|
})(),
|
|
97
104
|
);
|
|
98
|
-
if (!checkDir) { allow(); return; } // synthetic path with no existing ancestor — fail open
|
|
105
|
+
if (!checkDir) { allow(undefined); return; } // synthetic path with no existing ancestor — fail open
|
|
99
106
|
|
|
100
107
|
const fileTopResult = git(['rev-parse', '--show-toplevel'], checkDir);
|
|
108
|
+
reportIfUndetermined('gsd-windsurf-pre-write', 'git rev-parse --show-toplevel (file location)', fileTopResult);
|
|
101
109
|
if (fileTopResult.status !== 0 || !fileTopResult.stdout) {
|
|
102
110
|
// Not inside any git worktree. Distinguish "inside a .git/ internals
|
|
103
111
|
// directory" (dangerous — BLOCK) from "outside all git repos entirely"
|
|
104
112
|
// (not the escape vector this guard targets — fail open).
|
|
105
113
|
const insideGitDir = git(['rev-parse', '--is-inside-git-dir'], checkDir);
|
|
114
|
+
reportIfUndetermined('gsd-windsurf-pre-write', 'git rev-parse --is-inside-git-dir', insideGitDir);
|
|
106
115
|
if (insideGitDir.status === 0 && insideGitDir.stdout && insideGitDir.stdout.trim() === 'true') {
|
|
107
116
|
block(
|
|
108
117
|
`'${filePath}' is inside a git internal (.git) directory, not the active project at ` +
|
|
@@ -111,12 +120,12 @@ process.stdin.on('end', () => {
|
|
|
111
120
|
);
|
|
112
121
|
return;
|
|
113
122
|
}
|
|
114
|
-
allow();
|
|
123
|
+
allow(undefined);
|
|
115
124
|
return;
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
const fileTopRaw = fileTopResult.stdout.trim();
|
|
119
|
-
if (fileTopRaw === cwdTopRaw) { allow(); return; }
|
|
128
|
+
if (fileTopRaw === cwdTopRaw) { allow(undefined); return; }
|
|
120
129
|
|
|
121
130
|
// BLOCK: file resolves to a different git root than the active project.
|
|
122
131
|
block(
|
|
@@ -127,6 +136,6 @@ process.stdin.on('end', () => {
|
|
|
127
136
|
);
|
|
128
137
|
} catch {
|
|
129
138
|
// Silent fail-open — never block a valid tool call due to a hook bug.
|
|
130
|
-
allow();
|
|
139
|
+
allow(undefined);
|
|
131
140
|
}
|
|
132
141
|
});
|
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
// Detects when Claude attempts file edits outside a GSD workflow context
|
|
5
5
|
// (no active /gsd- skill or Task subagent) and injects an advisory warning.
|
|
6
6
|
//
|
|
7
|
-
// This is a SOFT guard — it advises, not blocks. The edit still
|
|
8
|
-
// The warning nudges Claude to use /gsd:quick or /gsd:fast instead
|
|
9
|
-
// making direct edits that bypass state tracking.
|
|
7
|
+
// This is a SOFT guard for edits — it advises, not blocks. The edit still
|
|
8
|
+
// proceeds. The warning nudges Claude to use /gsd:quick or /gsd:fast instead
|
|
9
|
+
// of making direct edits that bypass state tracking.
|
|
10
|
+
//
|
|
11
|
+
// ONE hard block lives here: `git add -f` on an agent/worktree-agent branch
|
|
12
|
+
// (WORKTREE_AGENT_FORCE_ADD_FORBIDDEN) — and that block leg fails CLOSED on
|
|
13
|
+
// internal error (#3504): when the guard is enabled and the blocking context
|
|
14
|
+
// holds, a thrown error exits 2 (block), not 0. The advisory legs keep the
|
|
15
|
+
// fail-open posture — a broken advisory must never wedge every tool call.
|
|
10
16
|
//
|
|
11
17
|
// Enable via config: hooks.workflow_guard: true (default: false)
|
|
12
18
|
// Only triggers on Write/Edit tool calls to non-.planning/ files.
|
|
@@ -14,40 +20,58 @@
|
|
|
14
20
|
const fs = require('fs');
|
|
15
21
|
const path = require('path');
|
|
16
22
|
const { spawnSync } = require('child_process');
|
|
17
|
-
const { tokenize } = require('./lib/git-cmd.js');
|
|
23
|
+
const { tokenize, skipToSubcommand } = require('./lib/git-cmd.js');
|
|
24
|
+
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
25
|
+
const { reportIfUndetermined } = require('./lib/git-probe.js');
|
|
26
|
+
|
|
27
|
+
// This guard is almost entirely advisory (fail open — a broken advisory must
|
|
28
|
+
// never wedge every tool call), with ONE hard block (#3504 force-add-on-
|
|
29
|
+
// agent-branch) that fails CLOSED on internal error instead. That split is
|
|
30
|
+
// re-derived dynamically inside the outer catch (failClosedBlockContext), not
|
|
31
|
+
// a fixed per-hook policy, so ON_CRASH names only the FINAL, unconditional
|
|
32
|
+
// fallback reached when the fail-closed context does not apply — i.e. the
|
|
33
|
+
// historical exit(0). Declared ONCE here so that fallback states its policy
|
|
34
|
+
// explicitly rather than inheriting a default (#3911).
|
|
35
|
+
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
18
36
|
|
|
19
37
|
function forceGitAddCwds(command, defaultCwd) {
|
|
20
38
|
const tokens = tokenize(command || '');
|
|
21
39
|
const separators = new Set(['&&', '||', ';', '|']);
|
|
22
40
|
const cwdList = [];
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
// #3504: per-segment walk driven by git-cmd.js's canonical skipToSubcommand.
|
|
42
|
+
// The previous inline walk knew six global flags and silently missed the
|
|
43
|
+
// rest — `git -c core.hooksPath=/tmp/x add -f x`, `git --no-optional-locks
|
|
44
|
+
// add -f x`, `--literal-pathspecs`, `--namespace=…` and friends all fell
|
|
45
|
+
// through to a silent exit 0, a no-crash bypass the fail-closed catch is
|
|
46
|
+
// structurally blind to (it only fires on throws). Sharing the classifier
|
|
47
|
+
// makes the block's flag knowledge exactly the classifier's.
|
|
48
|
+
const segments = [];
|
|
49
|
+
let start = 0;
|
|
50
|
+
for (let i = 0; i <= tokens.length; i++) {
|
|
51
|
+
if (i === tokens.length || separators.has(tokens[i])) {
|
|
52
|
+
if (i > start) segments.push(tokens.slice(start, i));
|
|
53
|
+
start = i + 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const seg of segments) {
|
|
57
|
+
const subIdx = skipToSubcommand(seg);
|
|
58
|
+
if (subIdx === -1 || subIdx >= seg.length || seg[subIdx] !== 'add') continue;
|
|
25
59
|
|
|
26
|
-
|
|
60
|
+
// Resolve a `-C <dir>` (separate-arg form) preceding the subcommand so
|
|
61
|
+
// the branch is probed at the repository the add targets.
|
|
27
62
|
let gitCwd = defaultCwd;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
gitCwd = path.resolve(gitCwd, tokens[j + 1]);
|
|
33
|
-
j += 2;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (['-C', '--git-dir', '--work-tree'].includes(flagName) && !token.includes('=')) {
|
|
37
|
-
j += 2;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (['--git-dir', '--work-tree', '--no-pager', '-p', '-P'].includes(flagName)) {
|
|
41
|
-
j++;
|
|
63
|
+
for (let k = 0; k < subIdx; ) {
|
|
64
|
+
if (seg[k] === '-C' && k + 1 < subIdx) {
|
|
65
|
+
gitCwd = path.resolve(gitCwd, seg[k + 1]);
|
|
66
|
+
k += 2;
|
|
42
67
|
continue;
|
|
43
68
|
}
|
|
44
|
-
|
|
69
|
+
k++;
|
|
45
70
|
}
|
|
46
71
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
if (tokens[k] === '--force' || tokens[k] === '-f' || /^-[A-Za-z]*f[A-Za-z]*$/.test(tokens[k])) {
|
|
72
|
+
for (let k = subIdx + 1; k < seg.length; k++) {
|
|
73
|
+
if (seg[k] === '--') break;
|
|
74
|
+
if (seg[k] === '--force' || seg[k] === '-f' || /^-[A-Za-z]*f[A-Za-z]*$/.test(seg[k])) {
|
|
51
75
|
cwdList.push(gitCwd);
|
|
52
76
|
break;
|
|
53
77
|
}
|
|
@@ -62,11 +86,93 @@ function currentBranch(cwd) {
|
|
|
62
86
|
encoding: 'utf8',
|
|
63
87
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
64
88
|
windowsHide: true,
|
|
89
|
+
// #3504: bounded — this ran unbounded before, an indefinite hang under a
|
|
90
|
+
// wedged git would hang every PreToolUse call. Host wiring allows a 5s
|
|
91
|
+
// budget for the whole hook, so the probe gets 2s of it; a timeout
|
|
92
|
+
// returns '' (branch unknown), which both the block decision and the
|
|
93
|
+
// fail-closed re-check treat as "cannot establish agent branch".
|
|
94
|
+
timeout: 2000,
|
|
95
|
+
killSignal: 'SIGTERM',
|
|
65
96
|
});
|
|
97
|
+
// #3911: a timeout/spawn-failure here previously degraded to '' exactly
|
|
98
|
+
// like a clean "not on a branch" answer — indistinguishable from the
|
|
99
|
+
// caller's point of view. reportIfUndetermined is a no-op on a genuine
|
|
100
|
+
// negative and only emits a diagnostic when the probe itself could not
|
|
101
|
+
// run; the '' fallback (and therefore this hook's exit code) is unchanged.
|
|
102
|
+
reportIfUndetermined('gsd-workflow-guard', 'git branch --show-current', result);
|
|
66
103
|
if (result.status !== 0) return '';
|
|
67
104
|
return result.stdout.trim();
|
|
68
105
|
}
|
|
69
106
|
|
|
107
|
+
// Agent-branch predicate, shared by the happy-path detector and the fail-closed
|
|
108
|
+
// re-check so the block's scope cannot drift between the two paths (#3504).
|
|
109
|
+
function isAgentBranch(branch) {
|
|
110
|
+
return /^(worktree-)?agent-/.test(branch);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Typed cwd read, shared by both paths for the same reason: a non-string cwd
|
|
114
|
+
// degrades to process.cwd() instead of throwing inside path.join().
|
|
115
|
+
function payloadCwd(data) {
|
|
116
|
+
return typeof data?.cwd === 'string' && data.cwd ? data.cwd : process.cwd();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// The force-add block payload, emitted by the happy-path detector and the
|
|
120
|
+
// fail-closed catch — one source so the two exits cannot drift. `origin`
|
|
121
|
+
// distinguishes them structurally ('force-add-detected' vs 'fail-closed') so a
|
|
122
|
+
// consumer — or the model reading Kimi's stderr feedback — is never told a
|
|
123
|
+
// force-add was detected when the call was in fact blocked unanalyzed.
|
|
124
|
+
function emitForceAddBlock(origin) {
|
|
125
|
+
const failClosed = origin === 'fail-closed';
|
|
126
|
+
const reason = failClosed
|
|
127
|
+
? 'workflow guard internal error on an agent branch - failing closed. The command was NOT analyzed and was NOT confirmed to be a force-add. Retry the call or inspect the guard.'
|
|
128
|
+
: 'agent/worktree-agent branches must not run git add -f or git add --force. Respect the SDK skipped_gitignored/skipped_commit_docs_false contract and leave gitignored files untracked.';
|
|
129
|
+
const output = {
|
|
130
|
+
decision: 'block',
|
|
131
|
+
code: 'WORKTREE_AGENT_FORCE_ADD_FORBIDDEN',
|
|
132
|
+
origin: failClosed ? 'fail-closed' : 'force-add-detected',
|
|
133
|
+
reason,
|
|
134
|
+
};
|
|
135
|
+
// Kimi CLI's exit-2 protocol feeds stderr back to the model (#2304) — deny's
|
|
136
|
+
// stderrPayload keeps fd 2 to the plain reason string, matching the
|
|
137
|
+
// pre-migration two-write byte-for-byte.
|
|
138
|
+
deny(output, output.reason);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// #3504 fail-closed context re-derivation. Runs INSIDE the outer catch, after
|
|
142
|
+
// an internal error, and answers exactly one question: does the blocking
|
|
143
|
+
// context of the force-add guard hold for this payload? Each stage is guarded —
|
|
144
|
+
// a re-derivation that itself throws must degrade to "cannot establish" (false),
|
|
145
|
+
// never take down the catch. Deliberately does NOT re-detect the force-add: the
|
|
146
|
+
// error may live in the detector itself, and on an agent branch with the guard
|
|
147
|
+
// enabled, a Bash call under an internal error is conservative-correct to block.
|
|
148
|
+
// Anything it cannot establish (unparseable payload, non-Bash tool, guard
|
|
149
|
+
// disabled, branch not determinably agent-*) fails open, preserving the
|
|
150
|
+
// advisory legs' fail-open posture.
|
|
151
|
+
function failClosedBlockContext(rawInput) {
|
|
152
|
+
let data;
|
|
153
|
+
try {
|
|
154
|
+
data = normalizeKimiPayload(JSON.parse(rawInput));
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
if (data === null || typeof data !== 'object' || data.tool_name !== 'Bash') return false;
|
|
159
|
+
const cwd = payloadCwd(data);
|
|
160
|
+
let enabled;
|
|
161
|
+
try {
|
|
162
|
+
enabled = workflowGuardEnabled(cwd);
|
|
163
|
+
} catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
if (!enabled) return false;
|
|
167
|
+
let branch;
|
|
168
|
+
try {
|
|
169
|
+
branch = currentBranch(cwd);
|
|
170
|
+
} catch {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return isAgentBranch(branch);
|
|
174
|
+
}
|
|
175
|
+
|
|
70
176
|
function workflowGuardEnabled(cwd) {
|
|
71
177
|
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
72
178
|
if (!fs.existsSync(configPath)) return false;
|
|
@@ -173,49 +279,50 @@ function normalizeKimiPayload(data) {
|
|
|
173
279
|
}
|
|
174
280
|
|
|
175
281
|
let input = '';
|
|
176
|
-
const stdinTimeout = setTimeout(() =>
|
|
282
|
+
const stdinTimeout = setTimeout(() => allow(undefined), 3000);
|
|
177
283
|
process.stdin.setEncoding('utf8');
|
|
178
284
|
process.stdin.on('data', chunk => input += chunk);
|
|
179
285
|
process.stdin.on('end', () => {
|
|
180
286
|
clearTimeout(stdinTimeout);
|
|
181
287
|
try {
|
|
182
288
|
const data = normalizeKimiPayload(JSON.parse(input));
|
|
289
|
+
// #3504 test-only fault seam: throws right after parse so the fail-closed
|
|
290
|
+
// posture of the outer catch is exercisable — no JSON-expressible input
|
|
291
|
+
// throws in this handler today (#2547/#2595 hardened every read). Gated on
|
|
292
|
+
// GSD_TEST_MODE as well so a leaked GSD_TEST_WORKFLOW_GUARD_FAULT in a real
|
|
293
|
+
// shell cannot wedge a production session. The fault's failure direction
|
|
294
|
+
// is CLOSED: the catch below may block, never bypass a block.
|
|
295
|
+
if (process.env.GSD_TEST_MODE === '1' && process.env.GSD_TEST_WORKFLOW_GUARD_FAULT === '1') {
|
|
296
|
+
throw new Error('GSD_TEST_WORKFLOW_GUARD_FAULT: injected fault');
|
|
297
|
+
}
|
|
183
298
|
const toolName = data.tool_name;
|
|
184
299
|
const cwd = data.cwd || process.cwd();
|
|
185
300
|
const isWorkflowGuardEnabled = workflowGuardEnabled(cwd);
|
|
186
301
|
|
|
187
302
|
if (toolName === 'Bash') {
|
|
188
303
|
if (!isWorkflowGuardEnabled) {
|
|
189
|
-
|
|
304
|
+
allow(undefined);
|
|
190
305
|
}
|
|
191
306
|
const command = data.tool_input?.command || '';
|
|
192
307
|
for (const gitCwd of forceGitAddCwds(command, cwd)) {
|
|
193
308
|
const branch = currentBranch(gitCwd);
|
|
194
309
|
if (/^(worktree-)?agent-/.test(branch)) {
|
|
195
|
-
|
|
196
|
-
decision: 'block',
|
|
197
|
-
code: 'WORKTREE_AGENT_FORCE_ADD_FORBIDDEN',
|
|
198
|
-
reason: 'agent/worktree-agent branches must not run git add -f or git add --force. Respect the SDK skipped_gitignored/skipped_commit_docs_false contract and leave gitignored files untracked.',
|
|
199
|
-
};
|
|
200
|
-
process.stdout.write(JSON.stringify(output));
|
|
201
|
-
// Kimi CLI's exit-2 protocol feeds stderr back to the model (#2304)
|
|
202
|
-
process.stderr.write(output.reason);
|
|
203
|
-
process.exit(2);
|
|
310
|
+
emitForceAddBlock();
|
|
204
311
|
}
|
|
205
312
|
}
|
|
206
|
-
|
|
313
|
+
allow(undefined);
|
|
207
314
|
}
|
|
208
315
|
|
|
209
316
|
// Only guard Write, Edit, and MultiEdit tool calls
|
|
210
317
|
if (!['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
211
|
-
|
|
318
|
+
allow(undefined);
|
|
212
319
|
}
|
|
213
320
|
|
|
214
321
|
// Check if we're inside a GSD workflow (Task subagent or /gsd- skill)
|
|
215
322
|
// Subagents have a session_id that differs from the parent
|
|
216
323
|
// and typically have a description field set by the orchestrator
|
|
217
324
|
if (data.tool_input?.is_subagent || data.session_type === 'task') {
|
|
218
|
-
|
|
325
|
+
allow(undefined);
|
|
219
326
|
}
|
|
220
327
|
|
|
221
328
|
// Check the file being edited
|
|
@@ -230,7 +337,7 @@ process.stdin.on('end', () => {
|
|
|
230
337
|
|
|
231
338
|
// Allow edits to .planning/ files (GSD state management)
|
|
232
339
|
if (filePath.includes('.planning/') || filePath.includes('.planning\\')) {
|
|
233
|
-
|
|
340
|
+
allow(undefined);
|
|
234
341
|
}
|
|
235
342
|
|
|
236
343
|
// Allow edits to common config/docs files that don't need GSD tracking
|
|
@@ -243,11 +350,11 @@ process.stdin.on('end', () => {
|
|
|
243
350
|
/settings\.json$/,
|
|
244
351
|
];
|
|
245
352
|
if (allowedPatterns.some(p => p.test(filePath))) {
|
|
246
|
-
|
|
353
|
+
allow(undefined);
|
|
247
354
|
}
|
|
248
355
|
|
|
249
356
|
if (!isWorkflowGuardEnabled) {
|
|
250
|
-
|
|
357
|
+
allow(undefined); // Guard disabled (default) or no GSD project
|
|
251
358
|
}
|
|
252
359
|
|
|
253
360
|
// If we get here: GSD project, guard enabled, file edit outside .planning/,
|
|
@@ -264,8 +371,17 @@ process.stdin.on('end', () => {
|
|
|
264
371
|
};
|
|
265
372
|
|
|
266
373
|
process.stdout.write(JSON.stringify(output));
|
|
267
|
-
} catch
|
|
268
|
-
//
|
|
269
|
-
|
|
374
|
+
} catch {
|
|
375
|
+
// #3504: split posture on internal error. The ONE hard block in this hook
|
|
376
|
+
// (force-add on agent branches) fails CLOSED — if the blocking context can
|
|
377
|
+
// be re-derived from the payload (Bash tool + guard enabled + determinably
|
|
378
|
+
// an agent branch), deny rather than silently allowing. Everything else
|
|
379
|
+
// keeps the historical fail-open posture: a broken advisory guard must
|
|
380
|
+
// never wedge the session's tool calls. ON_CRASH is declared ALLOW at
|
|
381
|
+
// module top for exactly that unconditional fallback (#3911).
|
|
382
|
+
if (failClosedBlockContext(input)) {
|
|
383
|
+
emitForceAddBlock('fail-closed');
|
|
384
|
+
}
|
|
385
|
+
crash(ON_CRASH, undefined);
|
|
270
386
|
}
|
|
271
387
|
});
|