@opengsd/gsd-core 1.14.0 → 1.16.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/.opencode/plugins/gsd-core.js +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- 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 +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- 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 +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- 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 +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- 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 +7 -2
- package/commands/gsd/capture.md +9 -5
- 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 +12 -4
- 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 +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- 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 +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- 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 +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- 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 +32 -33
- 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 +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- 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/decisions.cjs +30 -14
- 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 +426 -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 +141 -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 +80 -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 +10 -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/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- 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/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- 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/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- 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/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- 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/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- 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 +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- 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 +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- 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/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -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/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- 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 +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- 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/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- 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 +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- 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 +37 -3
- 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 +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- 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/threat-id-gate.md +28 -0
- 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/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -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 +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- 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 +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- 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 +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.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 +17 -5
- 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 +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- 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 +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- 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 +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- 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 +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- 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 +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- 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 +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- 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 +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- 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 +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -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 +38 -1
- 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-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- 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 +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- 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 +12 -4
- 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 +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- 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
|
@@ -16,6 +16,21 @@ set -euo pipefail
|
|
|
16
16
|
# Idempotent and failure-proof by construction: unset vars expand to "" (a
|
|
17
17
|
# no-op rm -f target), and `|| true` guarantees the trap itself never changes
|
|
18
18
|
# the script's exit status.
|
|
19
|
+
# Subprocess exit statuses, pre-initialised so they can never be inherited from
|
|
20
|
+
# the ambient environment. Each is captured as `... || VAR=$?`, which assigns
|
|
21
|
+
# ONLY on the failure branch; on success the variable keeps whatever it already
|
|
22
|
+
# held, and `${VAR:-0}` defaults only when unset or empty. So an EXPORTED
|
|
23
|
+
# CONFIG_STATUS / CMD_STATUS / CLASSIFY_STATUS — from a CI wrapper, a .envrc, or
|
|
24
|
+
# another hook — survived into the success path and was read as "the subprocess
|
|
25
|
+
# failed". Measured: `CLASSIFY_STATUS=3 git commit -m "nope: bad"` printed
|
|
26
|
+
# "validator disabled for this call" and exited 0, silently accepting a
|
|
27
|
+
# non-conforming commit. Same for CONFIG_STATUS and CMD_STATUS. Found by the
|
|
28
|
+
# security review of #4429; the gate is fail-open by design on a genuine
|
|
29
|
+
# subprocess failure (#3838), which is exactly what made this bypass quiet.
|
|
30
|
+
CONFIG_STATUS=0
|
|
31
|
+
CMD_STATUS=0
|
|
32
|
+
CLASSIFY_STATUS=0
|
|
33
|
+
|
|
19
34
|
ENABLED_ERR=""
|
|
20
35
|
CMD_ERR=""
|
|
21
36
|
CLASSIFY_ERR=""
|
|
@@ -61,6 +76,12 @@ if [ -f .planning/config.json ]; then
|
|
|
61
76
|
process.exit(3);
|
|
62
77
|
}
|
|
63
78
|
" 2>"$ENABLED_ERR") || CONFIG_STATUS=$?
|
|
79
|
+
# Pre-initialised, NOT left to `${...:-0}` alone: the capture below only
|
|
80
|
+
# assigns on the `||` branch, so on SUCCESS the variable keeps whatever it
|
|
81
|
+
# already held — and an EXPORTED variable of this name is inherited from the
|
|
82
|
+
# ambient environment. `${VAR:-0}` defaults only when unset/empty, so
|
|
83
|
+
# `CONFIG_STATUS=3 git commit …` made this hook print "validator disabled" and exit 0,
|
|
84
|
+
# silently accepting a non-conforming commit. Found by review of #4429.
|
|
64
85
|
CONFIG_STATUS=${CONFIG_STATUS:-0}
|
|
65
86
|
if [ "$CONFIG_STATUS" != "0" ]; then
|
|
66
87
|
# Could not determine the opt-in flag at all (node missing, JSON parse
|
|
@@ -103,6 +124,12 @@ CMD=$(echo "$INPUT" | node -e "
|
|
|
103
124
|
}
|
|
104
125
|
});
|
|
105
126
|
" 2>"$CMD_ERR") || CMD_STATUS=$?
|
|
127
|
+
# Pre-initialised, NOT left to `${...:-0}` alone: the capture below only
|
|
128
|
+
# assigns on the `||` branch, so on SUCCESS the variable keeps whatever it
|
|
129
|
+
# already held — and an EXPORTED variable of this name is inherited from the
|
|
130
|
+
# ambient environment. `${VAR:-0}` defaults only when unset/empty, so
|
|
131
|
+
# `CMD_STATUS=3 git commit …` made this hook print "validator disabled" and exit 0,
|
|
132
|
+
# silently accepting a non-conforming commit. Found by review of #4429.
|
|
106
133
|
CMD_STATUS=${CMD_STATUS:-0}
|
|
107
134
|
if [ "$CMD_STATUS" != "0" ]; then
|
|
108
135
|
# Could not extract tool_input.command at all (node missing, malformed
|
|
@@ -127,6 +154,12 @@ GIT_CMD_LIB="$HOOK_DIR/lib/git-cmd.js" node -e "
|
|
|
127
154
|
process.exit(3);
|
|
128
155
|
}
|
|
129
156
|
" "$CMD" 2>"$CLASSIFY_ERR" || CLASSIFY_STATUS=$?
|
|
157
|
+
# Pre-initialised, NOT left to `${...:-0}` alone: the capture below only
|
|
158
|
+
# assigns on the `||` branch, so on SUCCESS the variable keeps whatever it
|
|
159
|
+
# already held — and an EXPORTED variable of this name is inherited from the
|
|
160
|
+
# ambient environment. `${VAR:-0}` defaults only when unset/empty, so
|
|
161
|
+
# `CLASSIFY_STATUS=3 git commit …` made this hook print "validator disabled" and exit 0,
|
|
162
|
+
# silently accepting a non-conforming commit. Found by review of #4429.
|
|
130
163
|
CLASSIFY_STATUS=${CLASSIFY_STATUS:-0}
|
|
131
164
|
if [ "$CLASSIFY_STATUS" != "0" ] && [ "$CLASSIFY_STATUS" != "1" ]; then
|
|
132
165
|
# 0 = is a git commit (validate below); 1 = genuinely not a git commit
|
|
@@ -557,7 +590,7 @@ if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
|
557
590
|
fi
|
|
558
591
|
# Single source of truth for the accepted commit-type list (#3811): the
|
|
559
592
|
# 10 built-ins plus whatever passed the safe-token filter above. Both the
|
|
560
|
-
#
|
|
593
|
+
# membership test and the human-readable error text below are derived
|
|
561
594
|
# from this ONE array — no hand-synced second copy.
|
|
562
595
|
#
|
|
563
596
|
# The `"${EXTRA_COMMIT_TYPES[@]+"${EXTRA_COMMIT_TYPES[@]}"}"` form (not
|
|
@@ -568,7 +601,6 @@ if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
|
568
601
|
# /bin/bash 3.2.57 on macOS. The `${arr[@]+word}` form is the
|
|
569
602
|
# nounset-safe idiom for "expand if set, empty otherwise" on empty arrays.
|
|
570
603
|
COMMIT_TYPES=("${BUILTIN_COMMIT_TYPES[@]}" "${EXTRA_COMMIT_TYPES[@]+"${EXTRA_COMMIT_TYPES[@]}"}")
|
|
571
|
-
COMMIT_TYPE_ALT=$(IFS='|'; echo "${COMMIT_TYPES[*]}")
|
|
572
604
|
COMMIT_TYPE_LIST=$(printf '%s, ' "${COMMIT_TYPES[@]}")
|
|
573
605
|
COMMIT_TYPE_LIST="${COMMIT_TYPE_LIST%, }"
|
|
574
606
|
# Typed `valid_types` array (#3811 review finding): CONTRIBUTING.md bans
|
|
@@ -580,8 +612,35 @@ if [ "$CLASSIFY_STATUS" = "0" ]; then
|
|
|
580
612
|
# or `\`.
|
|
581
613
|
COMMIT_TYPES_JSON=$(printf '"%s",' "${COMMIT_TYPES[@]}")
|
|
582
614
|
COMMIT_TYPES_JSON="[${COMMIT_TYPES_JSON%,}]"
|
|
583
|
-
# Validate Conventional Commits format
|
|
584
|
-
|
|
615
|
+
# Validate Conventional Commits format.
|
|
616
|
+
#
|
|
617
|
+
# #4429: do NOT build `^(type1|type2|...)` out of COMMIT_TYPES. That
|
|
618
|
+
# alternation grows with the CONFIGURED list, and how large a pattern can be
|
|
619
|
+
# compiled is a property of the platform's regex engine. bash 3.2.57 / BSD
|
|
620
|
+
# libc (macOS, this file's stated target) caps it at 64 KiB - bisected: a
|
|
621
|
+
# 65504-byte alternation compiles, 65515 fails. bash 5.2 / glibc has no
|
|
622
|
+
# reachable cap, so this half never bit Linux. Past a cap `[[ =~ ]]`
|
|
623
|
+
# returns 2, and `if !` cannot tell a COMPILE ERROR from "the subject does
|
|
624
|
+
# not conform" - so a valid `feat(auth): ...` was blocked with
|
|
625
|
+
# CONVENTIONAL_COMMITS_VIOLATION while `feat` sat in its own valid_types.
|
|
626
|
+
#
|
|
627
|
+
# Match the SHAPE with a fixed-size pattern, then test membership against
|
|
628
|
+
# the array. The regex no longer depends on how many types are configured,
|
|
629
|
+
# and the loop adds no subprocess or pipe (the #4429 hazard this file
|
|
630
|
+
# already avoids elsewhere). The character class is exactly the safe-token
|
|
631
|
+
# filter `^[a-z][a-z0-9-]*$` applied above, so it captures every type that
|
|
632
|
+
# can legally reach COMMIT_TYPES and no token that cannot.
|
|
633
|
+
SUBJECT_TYPE=''
|
|
634
|
+
if [[ "$SUBJECT" =~ ^([a-z][a-z0-9-]*)(\(.+\))?:[[:space:]].+ ]]; then
|
|
635
|
+
SUBJECT_TYPE="${BASH_REMATCH[1]}"
|
|
636
|
+
fi
|
|
637
|
+
COMMIT_TYPE_OK=0
|
|
638
|
+
if [ -n "$SUBJECT_TYPE" ]; then
|
|
639
|
+
for _known_type in "${COMMIT_TYPES[@]}"; do
|
|
640
|
+
if [ "$_known_type" = "$SUBJECT_TYPE" ]; then COMMIT_TYPE_OK=1; break; fi
|
|
641
|
+
done
|
|
642
|
+
fi
|
|
643
|
+
if [ "$COMMIT_TYPE_OK" -ne 1 ]; then
|
|
585
644
|
# Emit typed `code` and `valid_types` fields alongside `reason` (#2974,
|
|
586
645
|
# #3811). Tests assert on the stable code string and the typed array;
|
|
587
646
|
# the reason is the human-readable copy, never grepped by tests.
|
|
@@ -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.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengsd/gsd-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
|
|
5
5
|
"main": ".opencode/plugins/gsd-core.js",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,12 @@
|
|
|
29
29
|
"!scripts/run-affected-tests.cjs",
|
|
30
30
|
"!scripts/lint-no-adhoc-regex-escape.cjs",
|
|
31
31
|
"!scripts/lint-allow-test-rule-refs.cjs",
|
|
32
|
+
"!scripts/lint-verify-lifecycle-writes.cjs",
|
|
33
|
+
"!scripts/lint-verify-lifecycle-writes.allowlist.json",
|
|
34
|
+
"!scripts/lint-evaluation-scope-drift.cjs",
|
|
35
|
+
"!scripts/lint-evaluation-scope-drift.allowlist.json",
|
|
36
|
+
"!scripts/lint-gate-evidence-drift.cjs",
|
|
37
|
+
"!scripts/lint-gate-positive-control.cjs",
|
|
32
38
|
"pi",
|
|
33
39
|
"vscode"
|
|
34
40
|
],
|
|
@@ -69,6 +75,7 @@
|
|
|
69
75
|
"@stryker-mutator/tap-runner": "^9.6.1",
|
|
70
76
|
"@types/node": "^22.19.19",
|
|
71
77
|
"c8": "^11.0.0",
|
|
78
|
+
"esbuild": "0.28.2",
|
|
72
79
|
"eslint": "^9.39.4",
|
|
73
80
|
"eslint-plugin-n": "^17.24.0",
|
|
74
81
|
"eslint-plugin-no-only-tests": "^3.4.0",
|
|
@@ -79,6 +86,8 @@
|
|
|
79
86
|
"js-yaml": "^4.3.2",
|
|
80
87
|
"mutation-testing-metrics": "^3.7.3",
|
|
81
88
|
"re2js": "^2.8.6",
|
|
89
|
+
"saxes": "6.0.0",
|
|
90
|
+
"tap-parser": "18.3.4",
|
|
82
91
|
"typescript": "^6.0.3",
|
|
83
92
|
"typescript-eslint": "^8.60.0"
|
|
84
93
|
},
|
|
@@ -121,10 +130,16 @@
|
|
|
121
130
|
"lint": "eslint . --cache --cache-location node_modules/.cache/eslint/ --max-warnings 0",
|
|
122
131
|
"lint:fix": "eslint . --fix",
|
|
123
132
|
"lint:table-schema-drift": "node scripts/lint-table-schema-drift.cjs",
|
|
133
|
+
"lint:planning-document-positive-control": "node scripts/lint-planning-document-positive-control.cjs",
|
|
124
134
|
"lint:frontmatter-scalar-broad-grep": "node scripts/lint-frontmatter-scalar-broad-grep.cjs",
|
|
125
135
|
"lint:removed-but-needed": "node scripts/lint-removed-but-needed.cjs",
|
|
136
|
+
"lint:verify-lifecycle-writes": "node scripts/lint-verify-lifecycle-writes.cjs",
|
|
137
|
+
"lint:evaluation-scope-drift": "node scripts/lint-evaluation-scope-drift.cjs",
|
|
138
|
+
"lint:gate-evidence-drift": "node scripts/lint-gate-evidence-drift.cjs",
|
|
139
|
+
"lint:gate-positive-control": "node scripts/lint-gate-positive-control.cjs",
|
|
140
|
+
"lint:frontmatter-fence-drift": "node scripts/lint-frontmatter-fence-drift.cjs",
|
|
126
141
|
"lint:response-language": "node scripts/lint-response-language-coverage.cjs",
|
|
127
|
-
"lint:ci": "npm run lint && npm run lint:skill-deps && npm run lint:generated-sync && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs && node scripts/lint-portable-timeout.cjs && node scripts/lint-portable-grep.cjs && node scripts/lint-allowed-tools-parity.cjs && node scripts/validate-registry.cjs && node scripts/lint-table-schema-drift.cjs && node scripts/lint-fix-has-regression-tests.cjs && node scripts/lint-example-parser-parity.cjs && node scripts/lint-docs-command-form.cjs && node scripts/lint-plan-count-drift.cjs && node scripts/lint-milestone-window-drift.cjs && node scripts/lint-phase-enumeration-drift.cjs && node scripts/lint-planning-prompt-drift.cjs && node scripts/lint-unreachable-guard-drift.cjs && node scripts/lint-completion-ratio-drift.cjs && node scripts/lint-slug-derivation-drift.cjs && node scripts/lint-state-field-drift.cjs && node scripts/lint-state-write-path-drift.cjs && node scripts/lint-completion-predicate-drift.cjs && node scripts/lint-planning-snapshot-bypass-drift.cjs && node scripts/lint-health-diagnostic-rule-table.cjs && node scripts/lint-planning-artifact-writer-drift.cjs && node scripts/lint-frontmatter-scalar-broad-grep.cjs && node scripts/lint-removed-but-needed.cjs && node scripts/lint-no-adhoc-regex-escape.cjs && node scripts/lint-vendored-deps.cjs && node scripts/lint-docs-guard-registration.cjs && node scripts/lint-source-test-name-collision.cjs && npm run lint:hooks-runtime-build-seam && node scripts/check-contract-drift.cjs && node scripts/lint-mutation-test-derivation-drift.cjs && node scripts/lint-seam-enforcement.cjs && node scripts/lint-workflow-shellcheck.cjs && npm run lint:response-language",
|
|
142
|
+
"lint:ci": "npm run lint && npm run lint:skill-deps && npm run lint:generated-sync && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && npm run lint:retired-runtime-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs && node scripts/lint-portable-timeout.cjs && node scripts/lint-portable-grep.cjs && node scripts/lint-allowed-tools-parity.cjs && node scripts/lint-phase-arg-assignment.cjs && node scripts/validate-registry.cjs && node scripts/lint-table-schema-drift.cjs && node scripts/lint-planning-document-positive-control.cjs && node scripts/lint-pr-branch-pattern-drift.cjs && node scripts/lint-fix-has-regression-tests.cjs && node scripts/lint-example-parser-parity.cjs && node scripts/lint-docs-command-form.cjs && node scripts/lint-plan-count-drift.cjs && node scripts/lint-milestone-window-drift.cjs && node scripts/lint-phase-enumeration-drift.cjs && node scripts/lint-planning-prompt-drift.cjs && node scripts/lint-unreachable-guard-drift.cjs && node scripts/lint-completion-ratio-drift.cjs && node scripts/lint-slug-derivation-drift.cjs && node scripts/lint-state-field-drift.cjs && node scripts/lint-state-write-path-drift.cjs && node scripts/lint-completion-predicate-drift.cjs && node scripts/lint-planning-snapshot-bypass-drift.cjs && node scripts/lint-health-diagnostic-rule-table.cjs && node scripts/lint-planning-artifact-writer-drift.cjs && node scripts/lint-frontmatter-scalar-broad-grep.cjs && node scripts/lint-removed-but-needed.cjs && node scripts/lint-no-adhoc-regex-escape.cjs && node scripts/lint-vendored-deps.cjs && node scripts/lint-docs-guard-registration.cjs && node scripts/lint-source-test-name-collision.cjs && npm run lint:hooks-runtime-build-seam && node scripts/check-contract-drift.cjs && node scripts/lint-mutation-test-derivation-drift.cjs && node scripts/lint-seam-enforcement.cjs && node scripts/lint-workflow-shellcheck.cjs && npm run lint:response-language && node scripts/lint-verify-lifecycle-writes.cjs && node scripts/lint-frontmatter-fence-drift.cjs && node scripts/lint-evaluation-scope-drift.cjs && node scripts/lint-gate-evidence-drift.cjs && node scripts/lint-gate-positive-control.cjs",
|
|
128
143
|
"lint:allow-test-rule-refs": "node scripts/lint-allow-test-rule-refs.cjs",
|
|
129
144
|
"lint:regression-names": "node scripts/lint-regression-test-names.cjs",
|
|
130
145
|
"lint:descriptions": "node scripts/lint-descriptions.cjs",
|
|
@@ -136,6 +151,7 @@
|
|
|
136
151
|
"lint:docs": "node scripts/lint-docs-required.cjs",
|
|
137
152
|
"lint:qa-smells": "node scripts/qa-smell-ratchet.cjs",
|
|
138
153
|
"lint:legacy-name": "node scripts/lint-legacy-dir-name.cjs",
|
|
154
|
+
"lint:retired-runtime-name": "node scripts/lint-retired-runtime-name.cjs",
|
|
139
155
|
"lint:seam-enforcement": "node scripts/lint-seam-enforcement.cjs",
|
|
140
156
|
"lint:docs-command-form": "node scripts/lint-docs-command-form.cjs",
|
|
141
157
|
"lint:hooks-runtime-build-seam": "node scripts/lint-hooks-runtime-build-seam.cjs",
|
|
@@ -160,9 +176,11 @@
|
|
|
160
176
|
"test:coverage:all": "npm run test:coverage",
|
|
161
177
|
"test:mutation": "stryker run",
|
|
162
178
|
"gen:features": "node scripts/gen-features.cjs",
|
|
163
|
-
"gen:state-md-docs": "node scripts/gen-state-md-docs.cjs"
|
|
179
|
+
"gen:state-md-docs": "node scripts/gen-state-md-docs.cjs",
|
|
180
|
+
"lint:vendored-deps": "node scripts/lint-vendored-deps.cjs"
|
|
164
181
|
},
|
|
165
182
|
"allowScripts": {
|
|
166
|
-
"fallow@2.70.0": true
|
|
183
|
+
"fallow@2.70.0": true,
|
|
184
|
+
"esbuild@0.28.2": true
|
|
167
185
|
}
|
|
168
186
|
}
|
package/scripts/build-hooks.js
CHANGED
|
@@ -11,16 +11,25 @@ const path = require('path');
|
|
|
11
11
|
const vm = require('vm');
|
|
12
12
|
|
|
13
13
|
const HOOKS_DIR = path.join(__dirname, '..', 'hooks');
|
|
14
|
+
const REPO_ROOT = path.join(HOOKS_DIR, '..');
|
|
14
15
|
const DIST_DIR = path.join(HOOKS_DIR, 'dist');
|
|
15
16
|
// Per-process staging directory for atomic writes. Using process.pid in the
|
|
16
17
|
// name eliminates all contention between concurrent builders: each process
|
|
17
18
|
// owns its own staging dir and never races with another builder's cleanup.
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
19
|
+
// A SIBLING of hooks/ (not inside it) — still on the same filesystem as
|
|
20
|
+
// DIST_DIR (both live under the repo checkout), so rename(2) stays atomic —
|
|
21
|
+
// but this way nothing that walks or copies hooks/ as a whole can ever
|
|
22
|
+
// observe this directory being created/populated/deleted concurrently.
|
|
23
|
+
// tests/gsd-validate-commit-sigpipe.test.cjs's fixture setup does exactly
|
|
24
|
+
// that (fs.cpSync(HOOKS_DIR, ..., {recursive: true})), and when its walk
|
|
25
|
+
// entered a staging dir that a concurrent build-hooks.js run deleted
|
|
26
|
+
// mid-iteration, Node's native recursive-copy (std::filesystem under the
|
|
27
|
+
// hood) threw an uncaught C++ exception and aborted the whole process with
|
|
28
|
+
// SIGABRT — not a catchable JS error. Moving the staging dir out of hooks/
|
|
29
|
+
// closes that race for every current and future hooks/-tree walker, not
|
|
30
|
+
// just that one call site (which also gained its own defensive filter).
|
|
31
|
+
// The parent pattern .dist-staging-*/ (repo root) is gitignored.
|
|
32
|
+
const STAGE_DIR = path.join(REPO_ROOT, `.dist-staging-${process.pid}`);
|
|
24
33
|
|
|
25
34
|
// Hooks to copy (pure Node.js, no bundling needed)
|
|
26
35
|
const HOOKS_TO_COPY = [
|
|
@@ -86,10 +86,58 @@ function extractDocsExempt(body) {
|
|
|
86
86
|
return { docsExempt: reason, body: cleaned };
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* A self-contained copy of `locateFrontmatterFence` (src/frontmatter-fence.cts, the one
|
|
91
|
+
* frontmatter fence owner). Kept here, not required from the built
|
|
92
|
+
* `gsd-core/bin/lib/frontmatter-fence.cjs`, because this module runs before any build: the
|
|
93
|
+
* `changeset-lint` job in .github/workflows/changeset-required.yml checks out and runs
|
|
94
|
+
* `node scripts/changeset/lint.cjs` with no `npm ci` and no `build:lib`. Found while
|
|
95
|
+
* implementing #5105: tests/frontmatter-fence.test.cjs ("kept frontmatter fence copies agree
|
|
96
|
+
* with the owner") pins this copy to the owner over a fixture corpus and a property test, and
|
|
97
|
+
* scripts/lint-frontmatter-fence-drift.cjs allowlists exactly this function.
|
|
98
|
+
*/
|
|
99
|
+
function locateFrontmatterFence(text) {
|
|
100
|
+
if (typeof text !== 'string') {
|
|
101
|
+
throw new TypeError(`locateFrontmatterFence: expected a string, got ${typeof text}`);
|
|
102
|
+
}
|
|
103
|
+
const closingFenceLine = /^---[ \t]*$/;
|
|
104
|
+
const lenientClosingFenceLine = /^-{4,}[ \t]*$/;
|
|
105
|
+
const bom = text.charCodeAt(0) === 0xfeff ? text.slice(0, 1) : '';
|
|
106
|
+
const start = bom.length;
|
|
107
|
+
let eol;
|
|
108
|
+
if (text.startsWith('---\r\n', start)) eol = '\r\n';
|
|
109
|
+
else if (text.startsWith('---\n', start)) eol = '\n';
|
|
110
|
+
else return null;
|
|
111
|
+
const openEnd = start + 3 + eol.length;
|
|
112
|
+
const closedAt = (lineStart, lineEnd) => {
|
|
113
|
+
let bodyEnd = openEnd;
|
|
114
|
+
if (lineStart > openEnd) {
|
|
115
|
+
bodyEnd = lineStart - 1;
|
|
116
|
+
if (bodyEnd > openEnd && text[bodyEnd - 1] === '\r') bodyEnd -= 1;
|
|
117
|
+
}
|
|
118
|
+
return { bom, eol, openEnd, closed: true, closingStart: lineStart, closingFenceEnd: lineEnd, bodyEnd };
|
|
119
|
+
};
|
|
120
|
+
let lenient = null;
|
|
121
|
+
let lineStart = openEnd;
|
|
122
|
+
while (lineStart <= text.length) {
|
|
123
|
+
const newline = text.indexOf('\n', lineStart);
|
|
124
|
+
const lineEnd = newline === -1 ? text.length : newline > lineStart && text[newline - 1] === '\r' ? newline - 1 : newline;
|
|
125
|
+
const line = text.slice(lineStart, lineEnd);
|
|
126
|
+
if (closingFenceLine.test(line)) return closedAt(lineStart, lineEnd);
|
|
127
|
+
if (lenient === null && lenientClosingFenceLine.test(line)) lenient = [lineStart, lineEnd];
|
|
128
|
+
if (newline === -1) break;
|
|
129
|
+
lineStart = newline + 1;
|
|
130
|
+
}
|
|
131
|
+
if (lenient !== null) return closedAt(lenient[0], lenient[1]);
|
|
132
|
+
return { bom, eol, openEnd, closed: false, closingStart: -1, closingFenceEnd: -1, bodyEnd: text.length };
|
|
133
|
+
}
|
|
134
|
+
|
|
89
135
|
function parseFragment(src) {
|
|
90
|
-
const
|
|
91
|
-
if (!
|
|
92
|
-
const
|
|
136
|
+
const fence = locateFrontmatterFence(src);
|
|
137
|
+
if (!fence || !fence.closed) return { ok: false, reason: FRAGMENT_ERROR.MISSING_FRONTMATTER };
|
|
138
|
+
const fmBlock = src.slice(fence.openEnd, fence.bodyEnd);
|
|
139
|
+
// Everything past the closing fence line and its line ending.
|
|
140
|
+
const body = src.slice(fence.closingFenceEnd).replace(/^\r?\n/, '');
|
|
93
141
|
|
|
94
142
|
const fields = {};
|
|
95
143
|
for (const line of fmBlock.split(/\r?\n/)) {
|
|
@@ -137,4 +185,4 @@ function parseFragment(src) {
|
|
|
137
185
|
return { ok: true, fragment: { type: fields.type, pr, body: visibleBody, docsExempt } };
|
|
138
186
|
}
|
|
139
187
|
|
|
140
|
-
module.exports = { parseFragment, extractDocsExempt, FRAGMENT_ERROR, ALLOWED_TYPES, DOCS_EXEMPT_RE };
|
|
188
|
+
module.exports = { parseFragment, extractDocsExempt, locateFrontmatterFence, FRAGMENT_ERROR, ALLOWED_TYPES, DOCS_EXEMPT_RE };
|