@opengsd/gsd-core 1.11.0 → 1.13.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 +12 -0
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +78 -42
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +91 -112
- package/agents/gsd-planner.md +20 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +82 -7
- package/agents/gsd-ui-researcher.md +70 -3
- package/agents/gsd-verifier.md +24 -2
- package/bin/install.js +847 -200
- 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/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/quick.md +8 -4
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +761 -100
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +785 -144
- package/gsd-core/bin/lib/capability-state.cjs +25 -4
- package/gsd-core/bin/lib/capability-validator.cjs +321 -18
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +229 -6
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +877 -54
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +121 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +118 -14
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +23 -2
- 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/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +975 -326
- package/gsd-core/bin/lib/gap-checker.cjs +41 -8
- package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +96 -11
- package/gsd-core/bin/lib/init-command-router.cjs +132 -21
- package/gsd-core/bin/lib/init.cjs +252 -56
- package/gsd-core/bin/lib/install-engine.cjs +252 -15
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
- package/gsd-core/bin/lib/install-profiles.cjs +100 -18
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +195 -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/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +175 -4
- package/gsd-core/bin/lib/milestone.cjs +112 -7
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +321 -13
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +1175 -115
- package/gsd-core/bin/lib/plan-document.cjs +273 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
- package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
- package/gsd-core/bin/lib/roadmap.cjs +248 -64
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -31
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +216 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
- package/gsd-core/bin/lib/state-transition.cjs +850 -145
- package/gsd-core/bin/lib/state.cjs +1629 -287
- package/gsd-core/bin/lib/surface.cjs +33 -10
- 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/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +2542 -387
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -13
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +441 -56
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +6 -5
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +17 -5
- package/gsd-core/references/execute-mvp-tdd.md +18 -18
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/mvp-concepts.md +2 -2
- 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/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +76 -3
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +39 -9
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +15 -12
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +2 -2
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +7 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +3 -1
- package/gsd-core/workflows/add-phase.md +5 -3
- package/gsd-core/workflows/add-tests.md +4 -9
- package/gsd-core/workflows/add-todo.md +2 -2
- package/gsd-core/workflows/ai-integration-phase.md +5 -10
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +14 -3
- package/gsd-core/workflows/audit-milestone.md +11 -9
- package/gsd-core/workflows/audit-uat.md +19 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +12 -26
- package/gsd-core/workflows/check-todos.md +2 -2
- package/gsd-core/workflows/cleanup.md +3 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
- package/gsd-core/workflows/code-review-fix.md +3 -1
- package/gsd-core/workflows/code-review.md +192 -69
- package/gsd-core/workflows/complete-milestone.md +28 -14
- package/gsd-core/workflows/debug.md +6 -4
- package/gsd-core/workflows/diagnose-issues.md +17 -7
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +46 -19
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +3 -1
- package/gsd-core/workflows/eval-review.md +5 -10
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
- 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 +4 -2
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +68 -66
- package/gsd-core/workflows/execute-plan.md +25 -20
- package/gsd-core/workflows/explore.md +3 -1
- package/gsd-core/workflows/extract-learnings.md +3 -1
- package/gsd-core/workflows/fast.md +8 -2
- package/gsd-core/workflows/forensics.md +3 -1
- package/gsd-core/workflows/graduation.md +6 -6
- package/gsd-core/workflows/health.md +4 -7
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +17 -14
- package/gsd-core/workflows/inbox.md +5 -6
- package/gsd-core/workflows/ingest-docs.md +45 -12
- package/gsd-core/workflows/insert-phase.md +7 -5
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +7 -3
- package/gsd-core/workflows/list-workspaces.md +3 -1
- package/gsd-core/workflows/manager.md +15 -26
- package/gsd-core/workflows/map-codebase.md +3 -1
- package/gsd-core/workflows/milestone-summary.md +3 -1
- package/gsd-core/workflows/mvp-phase.md +3 -3
- package/gsd-core/workflows/new-milestone.md +10 -22
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +17 -29
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +4 -2
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +20 -5
- 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 +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
- package/gsd-core/workflows/plan-phase.md +251 -54
- package/gsd-core/workflows/plan-review-convergence.md +148 -19
- package/gsd-core/workflows/plant-seed.md +3 -3
- package/gsd-core/workflows/pr-branch.md +195 -51
- package/gsd-core/workflows/profile-user.md +17 -15
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +52 -15
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +33 -32
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +6 -4
- package/gsd-core/workflows/remove-workspace.md +3 -3
- package/gsd-core/workflows/resume-project.md +14 -14
- package/gsd-core/workflows/review.md +404 -21
- package/gsd-core/workflows/scan.md +3 -1
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +9 -9
- package/gsd-core/workflows/settings-integrations.md +66 -32
- package/gsd-core/workflows/settings.md +4 -6
- package/gsd-core/workflows/ship.md +22 -16
- package/gsd-core/workflows/sketch-wrap-up.md +13 -17
- package/gsd-core/workflows/sketch.md +13 -19
- package/gsd-core/workflows/smart-entry.md +4 -6
- package/gsd-core/workflows/spec-phase.md +31 -4
- package/gsd-core/workflows/spike-wrap-up.md +9 -11
- package/gsd-core/workflows/spike.md +21 -32
- package/gsd-core/workflows/stats.md +4 -2
- package/gsd-core/workflows/sync-skills.md +13 -5
- package/gsd-core/workflows/thread.md +13 -7
- package/gsd-core/workflows/transition.md +7 -5
- package/gsd-core/workflows/ui-phase.md +36 -21
- package/gsd-core/workflows/ui-review.md +7 -11
- package/gsd-core/workflows/ultraplan-phase.md +7 -13
- package/gsd-core/workflows/undo.md +9 -17
- package/gsd-core/workflows/update.md +47 -48
- 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 +106 -21
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +302 -22
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +77 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +46 -12
- package/hooks/dist/gsd-read-guard.js +18 -7
- package/hooks/dist/gsd-read-injection-scanner.js +22 -13
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +222 -29
- package/hooks/dist/gsd-validate-commit.sh +523 -12
- 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 +36 -17
- 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 +210 -1
- 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 +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +4 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +302 -22
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +77 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +46 -12
- package/hooks/gsd-read-guard.js +18 -7
- package/hooks/gsd-read-injection-scanner.js +22 -13
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +222 -29
- package/hooks/gsd-validate-commit.sh +523 -12
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +36 -17
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/hooks.json +6 -0
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +4 -0
- package/package.json +14 -9
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +12 -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 +52 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +406 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +189 -4
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- 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-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +45 -14
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-response-language-coverage.cjs +524 -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-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +124 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +83 -14
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -32,6 +32,9 @@ exports.analyzeCoverage = analyzeCoverage;
|
|
|
32
32
|
exports.proposeElements = proposeElements;
|
|
33
33
|
exports.autoResolve = autoResolve;
|
|
34
34
|
const probe_core_cjs_1 = require("./probe-core.cjs");
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
|
+
const cliExitModule = require("./cli-exit.cjs");
|
|
37
|
+
const { runMain } = cliExitModule;
|
|
35
38
|
/**
|
|
36
39
|
* Word-boundary cues mapping element prose -> UI element kind.
|
|
37
40
|
* Heuristic and intentionally lossy; an authored `elements` array overrides it. Every pattern is a
|
|
@@ -245,5 +248,10 @@ function autoResolve(items) {
|
|
|
245
248
|
* so it runs only when the compiled `.cjs` is executed directly.
|
|
246
249
|
*/
|
|
247
250
|
if (require.main === module) {
|
|
248
|
-
|
|
251
|
+
// runProbeCli's default `exit` now throws ExitError (src/probe-core.cts) rather
|
|
252
|
+
// than calling process.exit directly, so this entry point must run under
|
|
253
|
+
// runMain to translate that throw into process.exitCode.
|
|
254
|
+
runMain(() => {
|
|
255
|
+
(0, probe_core_cjs_1.runProbeCli)((elements, resolutions) => analyzeCoverage(elements, resolutions), { usage: 'ui-consideration-probe.cjs <elements.json> [resolutions.json]' });
|
|
256
|
+
});
|
|
249
257
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* UI Safety Gate — shell-free implementation (ADR-457 build-at-publish: the
|
|
4
|
-
* hand-written
|
|
4
|
+
* hand-written root copy of this file collapsed to a TypeScript source of
|
|
5
5
|
* truth). Behaviour is preserved byte-for-behaviour from the prior hand-written
|
|
6
6
|
* .cjs; only types are added.
|
|
7
7
|
*
|
|
@@ -22,13 +22,17 @@
|
|
|
22
22
|
*
|
|
23
23
|
* CLI usage — reads phase-section text from STDIN to avoid ARG_MAX limits:
|
|
24
24
|
* echo "$PHASE_SECTION" | node gsd-core/bin/lib/ui-safety-gate.cjs
|
|
25
|
-
* echo $? → 0 if UI tokens found, 1 if not
|
|
25
|
+
* echo $? → 0 if UI tokens found, 1 if not (real input, none found),
|
|
26
|
+
* NO_INPUT (registry, src/cli-exit.cts) if stdin was empty or
|
|
27
|
+
* whitespace-only, UNAVAILABLE (registry) if stdin read failed
|
|
28
|
+
* (ADR-3889 Phase 3, #3907)
|
|
26
29
|
*
|
|
27
|
-
* Exit codes mirror grep: 0 = match found, 1 = no match, 2 = usage error.
|
|
28
30
|
*
|
|
29
31
|
* Canonical location: gsd-core/bin/lib/ui-safety-gate.cjs (#448)
|
|
30
32
|
* This path is deployed by the GSD installer to $RUNTIME_DIR/gsd-core/bin/lib/.
|
|
31
|
-
*
|
|
33
|
+
* The former root-level copy of this file (outside gsd-core/) was removed in
|
|
34
|
+
* #3907 as dead code: no installer reference, no workflow invocation, and no
|
|
35
|
+
* fallback chain in shipped content ever pointed at it. Do not re-create it.
|
|
32
36
|
*/
|
|
33
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
38
|
exports.UI_TOKENS = void 0;
|
|
@@ -111,19 +115,45 @@ function checkUiPresence(text) {
|
|
|
111
115
|
// ── CLI entry point ─────────────────────────────────────────────────────────
|
|
112
116
|
// Reads phase-section text from STDIN (not argv) to avoid OS ARG_MAX limits.
|
|
113
117
|
// Invoked by workflow .md bash blocks as: echo "$PHASE_SECTION" | node .../ui-safety-gate.cjs
|
|
114
|
-
//
|
|
118
|
+
//
|
|
119
|
+
// Exit codes (ADR-3889 Phase 3, #3907): 0 = UI found, 1 = no UI (real input,
|
|
120
|
+
// examined, nothing found), NO_INPUT (registry — see src/cli-exit.cts) = stdin
|
|
121
|
+
// closed with zero bytes (or whitespace-only), UNAVAILABLE (registry) = stdin
|
|
122
|
+
// read failed. The prior single "2 = startup error" arm conflated "I was
|
|
123
|
+
// handed nothing" with "the phase says it has no UI" — those are different
|
|
124
|
+
// answers; only the former is new here. `hint === 'no'` on REAL input still
|
|
125
|
+
// exits 1 — that is the author's own declaration, not an empty-input
|
|
126
|
+
// artifact.
|
|
127
|
+
//
|
|
128
|
+
// Terminates via terminateNow (src/cli-exit.cts), not raw process.exit: these
|
|
129
|
+
// exits fire from inside a stdin event handler, which is exactly what
|
|
130
|
+
// terminateNow (write-then-terminate) exists for, and it is total (cannot
|
|
131
|
+
// throw or return). This module never emits --json, so every payload here is
|
|
132
|
+
// `undefined` — terminateNow's JSON.stringify(undefined) is not a valid
|
|
133
|
+
// Buffer source and is swallowed by its own emission-failure guard, so
|
|
134
|
+
// nothing is written to stdout, preserving this CLI's historical contract of
|
|
135
|
+
// silence.
|
|
115
136
|
if (require.main === module) {
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
138
|
+
const cliExit = require('./cli-exit.cjs');
|
|
116
139
|
// Collect stdin chunks asynchronously.
|
|
117
140
|
const chunks = [];
|
|
118
141
|
process.stdin.setEncoding('utf-8');
|
|
119
142
|
process.stdin.on('data', (chunk) => chunks.push(chunk));
|
|
120
143
|
process.stdin.on('end', () => {
|
|
121
144
|
const input = chunks.join('');
|
|
145
|
+
// Whitespace-only counts as empty (ADR-3889 §1's NO_INPUT: "zero units
|
|
146
|
+
// were in scope, and that emptiness is known to be genuine"). Real input
|
|
147
|
+
// — including a lone NUL byte, which .trim() does not strip — always
|
|
148
|
+
// falls through to the detector.
|
|
149
|
+
if (input.trim().length === 0) {
|
|
150
|
+
cliExit.terminateNow('NO_INPUT', undefined);
|
|
151
|
+
}
|
|
122
152
|
const result = checkUiPresence(input);
|
|
123
|
-
|
|
153
|
+
cliExit.terminateNow(result.hasUI ? 'PASS' : 'FAIL', undefined);
|
|
124
154
|
});
|
|
125
155
|
process.stdin.on('error', (err) => {
|
|
126
156
|
process.stderr.write(`ERROR: ui-safety-gate.cjs stdin read failed: ${err.message}\n`);
|
|
127
|
-
|
|
157
|
+
cliExit.terminateNow('UNAVAILABLE', undefined);
|
|
128
158
|
});
|
|
129
159
|
}
|
|
@@ -79,6 +79,18 @@ const UNUSABLE_REASON = Object.freeze({
|
|
|
79
79
|
* planning-snapshot's projectSections field)
|
|
80
80
|
*/
|
|
81
81
|
PROJECT_UNREADABLE: 'project_unreadable',
|
|
82
|
+
/**
|
|
83
|
+
* A config.json parsed cleanly, but a section that a legacy-key migration needed to write
|
|
84
|
+
* into (`git`, `planning`) holds something that is not an object — a string, number, boolean
|
|
85
|
+
* or array. Distinct from the section being absent: absence is the ordinary path and the
|
|
86
|
+
* migration simply creates the section. Only a PRESENT non-object is corruption, and it is
|
|
87
|
+
* exactly the ADR-227 "shape, not just parseability" class one level down from the top-level
|
|
88
|
+
* document check `_readConfigFile` already performs. The migration is refused rather than
|
|
89
|
+
* applied — spreading the value would enumerate a string into character keys, and a number
|
|
90
|
+
* or boolean into nothing at all — so the section, the legacy key, and the file on disk are
|
|
91
|
+
* all left exactly as the user wrote them. (#3760, tenth #1879 site)
|
|
92
|
+
*/
|
|
93
|
+
CONFIG_SECTION_NOT_OBJECT: 'config_section_not_object',
|
|
82
94
|
});
|
|
83
95
|
/** One human-readable clause per reason. Prose lives here, never in a test assertion. */
|
|
84
96
|
const REASON_PROSE = Object.freeze({
|
|
@@ -88,6 +100,7 @@ const REASON_PROSE = Object.freeze({
|
|
|
88
100
|
[UNUSABLE_REASON.STATE_UNREADABLE]: 'STATE.md exists but could not be read; the current-phase label fell back to unavailable',
|
|
89
101
|
[UNUSABLE_REASON.CONFIG_UNREADABLE]: 'config.json exists but could not be read or parsed; the config field fell back to unavailable',
|
|
90
102
|
[UNUSABLE_REASON.PROJECT_UNREADABLE]: 'PROJECT.md exists but could not be read; the projectSections field fell back to unavailable',
|
|
103
|
+
[UNUSABLE_REASON.CONFIG_SECTION_NOT_OBJECT]: 'a config section that a legacy-key migration targets holds a non-object value; the migration was SKIPPED and both the section and the legacy key were left unchanged — fix the section by hand, or the legacy key will never migrate',
|
|
91
104
|
});
|
|
92
105
|
// ─── Dedup state ──────────────────────────────────────────────────────────────
|
|
93
106
|
/**
|
|
@@ -80,6 +80,10 @@ function inferPreferredRuntime({ fs, env, preferredConfigDir }) {
|
|
|
80
80
|
return 'opencode';
|
|
81
81
|
if (fs.exists(node_path_1.default.join(preferredConfigDir, exports.CODEX_CONFIG_MARKER)))
|
|
82
82
|
return 'codex';
|
|
83
|
+
const resolved = node_path_1.default.resolve(preferredConfigDir);
|
|
84
|
+
const known = exports.RUNTIME_DIRS.find(([, reldir]) => resolved.endsWith(node_path_1.default.sep + reldir.split('/').join(node_path_1.default.sep)));
|
|
85
|
+
if (known)
|
|
86
|
+
return known[0];
|
|
83
87
|
}
|
|
84
88
|
if (env['CODEX_HOME'])
|
|
85
89
|
return 'codex';
|
|
@@ -91,7 +95,7 @@ function inferPreferredRuntime({ fs, env, preferredConfigDir }) {
|
|
|
91
95
|
return 'opencode';
|
|
92
96
|
if (env['CLAUDE_CONFIG_DIR'])
|
|
93
97
|
return 'claude';
|
|
94
|
-
return '
|
|
98
|
+
return '';
|
|
95
99
|
}
|
|
96
100
|
// Absolute env-override candidates, mirroring the bash ENV_RUNTIME_DIRS block.
|
|
97
101
|
function envRuntimeDirs({ env, home }) {
|
|
@@ -196,7 +200,7 @@ function resolveUpdateContext({ home, cwd, env = {}, fs, preferredConfigDir = ''
|
|
|
196
200
|
if (globalRuntime) {
|
|
197
201
|
return { installedVersion: '0.0.0', scope: 'GLOBAL', runtime: globalRuntime, gsdDir: globalDir };
|
|
198
202
|
}
|
|
199
|
-
return { installedVersion: '0.0.0', scope: 'UNKNOWN', runtime: '
|
|
203
|
+
return { installedVersion: '0.0.0', scope: 'UNKNOWN', runtime: '', gsdDir: '' };
|
|
200
204
|
}
|
|
201
205
|
/**
|
|
202
206
|
* CLI wiring: resolve against the real filesystem.
|
|
@@ -47,7 +47,7 @@ function routeValidateCommand({ verify, args, cwd, raw, output: outputFn, error
|
|
|
47
47
|
// context: CJS-only — complex inline logic using classifyContextUtilization
|
|
48
48
|
// with custom output formatting that has no direct SDK counterpart.
|
|
49
49
|
context: () => {
|
|
50
|
-
const opts = (0, command_arg_projection_cjs_1.
|
|
50
|
+
const opts = (0, command_arg_projection_cjs_1.parseNamedArgsOrExit)(args, { valueFlags: ['tokens-used', 'context-window'], booleanFlags: ['json'], positionals: 2 }, error);
|
|
51
51
|
if (opts['tokens-used'] === null) {
|
|
52
52
|
error('--tokens-used <integer> is required for `validate context`');
|
|
53
53
|
return;
|
|
@@ -73,7 +73,7 @@ function routeValidateCommand({ verify, args, cwd, raw, output: outputFn, error
|
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
const result = { ...classified, recommendation: RECOMMENDATIONS[classified.state] };
|
|
76
|
-
if (
|
|
76
|
+
if (opts['json'] === true) {
|
|
77
77
|
outputFn(result, raw);
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
@@ -32,11 +32,14 @@
|
|
|
32
32
|
* - PR #156 (issue #6) — validate.ts generator that #26 extends
|
|
33
33
|
*/
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.MILESTONE_ARCHIVE_DIR_RE = exports.PHASE_TOKEN_FROM_DIR_RE = exports.phaseDirNameRe = void 0;
|
|
35
|
+
exports.BRACKET_PHASE_DIR_RE = exports.MILESTONE_ARCHIVE_DIR_RE = exports.PHASE_TOKEN_FROM_DIR_RE = exports.phaseDirNameRe = void 0;
|
|
36
|
+
exports.isPhaseDirName = isPhaseDirName;
|
|
37
|
+
exports.phaseTokenFromDir = phaseTokenFromDir;
|
|
36
38
|
exports.canonicalPlanStem = canonicalPlanStem;
|
|
37
39
|
exports.phaseVariants = phaseVariants;
|
|
38
40
|
exports.buildRoadmapPhaseVariants = buildRoadmapPhaseVariants;
|
|
39
41
|
exports.buildNotStartedPhaseVariants = buildNotStartedPhaseVariants;
|
|
42
|
+
exports.checkBracketCoherence = checkBracketCoherence;
|
|
40
43
|
exports.textEncodingError = textEncodingError;
|
|
41
44
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
45
|
const phaseIdMod = require("./phase-id.cjs");
|
|
@@ -45,7 +48,17 @@ const { OPTIONAL_PROJECT_CODE_PREFIX_SOURCE,
|
|
|
45
48
|
// `replaceAll('A-Z', 'A-Za-z')` — that derivation silently no-ops (and narrows
|
|
46
49
|
// this module to uppercase-only) the day phase-id.cts renders the class any
|
|
47
50
|
// other way. See the constants' doc comment in phase-id.cts.
|
|
48
|
-
CASE_FLEXIBLE_PROJECT_CODE_PREFIX_SOURCE, CASE_FLEXIBLE_PHASE_NUMBER_TOKEN_SOURCE, PHASE_CONTINUATION_SEGMENT_SOURCE,
|
|
51
|
+
CASE_FLEXIBLE_PROJECT_CODE_PREFIX_SOURCE, CASE_FLEXIBLE_PHASE_NUMBER_TOKEN_SOURCE, PHASE_CONTINUATION_SEGMENT_SOURCE, BRACKET_DIR_PREFIX_SRC, phaseHeadingPrefixSrcFor, PHASE_HEADING_BASELINE, extractPhaseToken, isSentinelPhaseId,
|
|
52
|
+
// #612 / #3309 re-homing: `checkBracketCoherence` (below) moved into this
|
|
53
|
+
// module when #3309 migrated `cmdValidateHealth` onto the rule table and
|
|
54
|
+
// deleted every local helper it used to sit beside in `verify.cts`. These
|
|
55
|
+
// four are the grammar owners it consumes, imported unchanged — nothing about
|
|
56
|
+
// the check is widened by the relocation.
|
|
57
|
+
BRACKET_ID_SRC, PHASE_NUMBER_TOKEN_SOURCE,
|
|
58
|
+
// #2761 M3: canonical bracket milestone intro, re-homed here with
|
|
59
|
+
// checkBracketCoherence when health diagnostics moved to the rule table.
|
|
60
|
+
BRACKET_MILESTONE_INTRO_CAPTURING_SRC, foldBracketId, } = phaseIdMod;
|
|
61
|
+
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
49
62
|
// ── Issue #26: regex constants (W005, W006-archived) ────────────────────────
|
|
50
63
|
// Matches legacy numeric dirs (01-setup), milestone-prefixed dirs (02-01-setup),
|
|
51
64
|
// deep dirs (02-04-01-deep), and project-code-prefixed variants (GSD-02-01-setup).
|
|
@@ -72,6 +85,64 @@ exports.phaseDirNameRe = new RegExp(`^${OPTIONAL_PROJECT_CODE_PREFIX_SOURCE}\\d{
|
|
|
72
85
|
exports.PHASE_TOKEN_FROM_DIR_RE = new RegExp(`^(${CASE_FLEXIBLE_PROJECT_CODE_PREFIX_SOURCE}` +
|
|
73
86
|
`\\d+[A-Za-z]?(?:-${PHASE_CONTINUATION_SEGMENT_SOURCE}[A-Z]?)*(?:\\.\\d+)*)(?:-|$)`);
|
|
74
87
|
exports.MILESTONE_ARCHIVE_DIR_RE = /^v\d+.*-phases$/i;
|
|
88
|
+
// ── #612: bracket phase-directory recognition (convention-gated) ────────────
|
|
89
|
+
// `{CODE}.{MM}-{PP}[.{SS}][-slug]`, built from the one bracket identity grammar.
|
|
90
|
+
//
|
|
91
|
+
// This lives BESIDE phaseDirNameRe / PHASE_TOKEN_FROM_DIR_RE rather than being
|
|
92
|
+
// folded into them. The `{CODE}.{MM}-` prefix is string-indistinguishable from
|
|
93
|
+
// the legacy letter-prefixed-decimal family this repo documents as "ambiguous
|
|
94
|
+
// with a padded bracket dir", and folding a bracket branch in changes those
|
|
95
|
+
// constants' answers on exactly that family: `P0.34-56-name` goes null -> "56",
|
|
96
|
+
// and phaseDirNameRe goes false -> true, silencing a W005 that fires today. A
|
|
97
|
+
// RegExp constant has nowhere to attach a convention gate, so the gate goes on
|
|
98
|
+
// the functions and the constants stay byte-identical for every consumer.
|
|
99
|
+
//
|
|
100
|
+
// The numeric run mirrors the EMIT grammar rather than accepting any digit run:
|
|
101
|
+
// CANONICAL_NUMERIC_RE (what toDir enforces) is digits-only with at most one
|
|
102
|
+
// sub-phase, so `GSD.02-12A-hotfix` and `GSD.02-05.03.07-x` are not bracket
|
|
103
|
+
// directories. Admitting them would make this recognizer disagree with
|
|
104
|
+
// extractPhaseToken, which the milestone-complete check resolves through — and
|
|
105
|
+
// then W006/W007 would resolve a directory that W021 simultaneously reported
|
|
106
|
+
// unstarted, inside one `validate health` run.
|
|
107
|
+
exports.BRACKET_PHASE_DIR_RE = new RegExp(`^(?:${BRACKET_DIR_PREFIX_SRC})\\d+(?:\\.\\d+)?(?:-[\\w-]+)?$`, 'i');
|
|
108
|
+
// The constants these functions wrap are consumed as `e.name.match(RE)`, which
|
|
109
|
+
// throws on a non-string. Coercing instead would invent a phase token out of a
|
|
110
|
+
// number (`42` -> `"42"`), so the contract is preserved rather than softened.
|
|
111
|
+
function assertDirName(value, fn) {
|
|
112
|
+
if (typeof value !== 'string') {
|
|
113
|
+
throw new TypeError(`${fn}: directory name must be a string, received ${typeof value}`);
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* True when `dirName` is a recognizable phase directory under `convention`.
|
|
119
|
+
* Under 'bracket' the `{CODE}.{MM}-{PP}` form is additionally accepted, so W005
|
|
120
|
+
* stops reporting every bracket phase directory as malformed. Every other
|
|
121
|
+
* convention value delegates to the unchanged `phaseDirNameRe`.
|
|
122
|
+
*/
|
|
123
|
+
function isPhaseDirName(dirName, convention) {
|
|
124
|
+
const name = assertDirName(dirName, 'isPhaseDirName');
|
|
125
|
+
if (convention === 'bracket' && exports.BRACKET_PHASE_DIR_RE.test(name))
|
|
126
|
+
return true;
|
|
127
|
+
return exports.phaseDirNameRe.test(name);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Extract a phase token from a directory name under `convention`, or null when
|
|
131
|
+
* the name is not a phase directory — the same contract as
|
|
132
|
+
* `PHASE_TOKEN_FROM_DIR_RE.exec()[1]`.
|
|
133
|
+
*
|
|
134
|
+
* Under 'bracket' the SHAPE is recognized here and the TOKEN is delegated to the
|
|
135
|
+
* canonical owner, so this and every other bracket directory reader resolve
|
|
136
|
+
* identically by construction rather than by two regexes agreeing today.
|
|
137
|
+
*/
|
|
138
|
+
function phaseTokenFromDir(dirName, convention) {
|
|
139
|
+
const name = assertDirName(dirName, 'phaseTokenFromDir');
|
|
140
|
+
if (convention === 'bracket' && exports.BRACKET_PHASE_DIR_RE.test(name)) {
|
|
141
|
+
return extractPhaseToken(name, 'bracket');
|
|
142
|
+
}
|
|
143
|
+
const legacy = name.match(exports.PHASE_TOKEN_FROM_DIR_RE);
|
|
144
|
+
return legacy ? legacy[1] : null;
|
|
145
|
+
}
|
|
75
146
|
// ── Issue #26: I001 canonicalization ────────────────────────────────────────
|
|
76
147
|
function canonicalPlanStem(stem) {
|
|
77
148
|
// #2043: the plan component (after the phase number) must be zero-padded,
|
|
@@ -116,35 +187,72 @@ function phaseVariants(phase) {
|
|
|
116
187
|
variants.add(`${numericHead.padStart(2, '0')}${letterSuffix}${tail}`);
|
|
117
188
|
return variants;
|
|
118
189
|
}
|
|
119
|
-
function buildRoadmapPhaseVariants(roadmapContent) {
|
|
190
|
+
function buildRoadmapPhaseVariants(roadmapContent, convention) {
|
|
120
191
|
const roadmapPhases = new Set();
|
|
121
192
|
const roadmapPhaseVariants = new Set();
|
|
193
|
+
const sentinelOnly = new Set();
|
|
194
|
+
const realTokens = new Set();
|
|
122
195
|
// Matches both legacy numeric (Phase 1:), decimal (Phase 2.1:), milestone-prefixed (Phase 2-01:),
|
|
123
196
|
// and bracket-prefixed (### [GSD] Phase 2-01:) headings.
|
|
124
197
|
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
|
|
125
|
-
|
|
198
|
+
// #612: SELECTED by the resolved convention. This capture class is
|
|
199
|
+
// letter-tolerant, which makes it the site where an ungated widening does the
|
|
200
|
+
// most damage — `### [RFC.2119] 5:` enters roadmapPhases as a phantom and
|
|
201
|
+
// becomes a W007 "in ROADMAP.md but no directory on disk" on a repo that never
|
|
202
|
+
// opted in. A non-bracket repo compiles the base source unchanged.
|
|
203
|
+
const capturing = convention === 'bracket';
|
|
204
|
+
const g = capturing ? 1 : 0;
|
|
205
|
+
const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.ANY_BRACKET, convention, capturing)}([\\w][\\w.-]*)(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`, 'gi');
|
|
126
206
|
let m;
|
|
127
207
|
while ((m = phasePattern.exec(roadmapContent)) !== null) {
|
|
128
|
-
|
|
129
|
-
|
|
208
|
+
const token = m[1 + g];
|
|
209
|
+
const bracketId = g ? m[1] : undefined;
|
|
210
|
+
if (bracketId && isSentinelPhaseId(`${bracketId}-${token}`, 'bracket'))
|
|
211
|
+
sentinelOnly.add(token);
|
|
212
|
+
else
|
|
213
|
+
realTokens.add(token);
|
|
214
|
+
roadmapPhases.add(token);
|
|
215
|
+
for (const variant of phaseVariants(token))
|
|
130
216
|
roadmapPhaseVariants.add(variant);
|
|
131
217
|
}
|
|
132
218
|
// Also matches checklist-style entries (checked or unchecked):
|
|
133
219
|
// - [x] **Phase 01: name** - [X] **Phase 2-01: name** - [ ] **Phase 3: name**
|
|
134
220
|
// This is a supported ROADMAP format (parallel to buildNotStartedPhaseVariants).
|
|
135
|
-
|
|
221
|
+
// #612: CAPTURING, exactly as the sibling checklist scan in roadmap.cts does
|
|
222
|
+
// and for the same stated reason — "the bracket id rides along so the sentinel
|
|
223
|
+
// filter below is not blind to `- [ ] **[GSD.999] 01: Icebox**`". Left
|
|
224
|
+
// un-capturing here, this scan called every checklist token REAL, and the
|
|
225
|
+
// occurrence-aware un-suppression loop below then deleted the icebox token that
|
|
226
|
+
// the HEADING scan had correctly marked sentinel — so `validate consistency`
|
|
227
|
+
// warned that a bracket ICEBOX phase had no directory, in the house ROADMAP
|
|
228
|
+
// shape (bold bullet index + detail headings) where the icebox appears as both.
|
|
229
|
+
// `validate health` stayed silent on the same repo, so the two verbs disagreed
|
|
230
|
+
// — the disagreement `sentinelPhases` exists to close.
|
|
231
|
+
const checklistPattern = new RegExp(`-\\s*\\[[ xX]\\]\\s*\\*{0,2}${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention, capturing)}([\\w][\\w.-]*)\\s*:`, 'gi');
|
|
136
232
|
let cm;
|
|
137
233
|
while ((cm = checklistPattern.exec(roadmapContent)) !== null) {
|
|
138
|
-
|
|
139
|
-
|
|
234
|
+
const cBracketId = g ? cm[1] : undefined;
|
|
235
|
+
const cToken = cm[1 + g];
|
|
236
|
+
if (cBracketId && isSentinelPhaseId(`${cBracketId}-${cToken}`, 'bracket'))
|
|
237
|
+
sentinelOnly.add(cToken);
|
|
238
|
+
else
|
|
239
|
+
realTokens.add(cToken);
|
|
240
|
+
roadmapPhases.add(cToken);
|
|
241
|
+
for (const variant of phaseVariants(cToken))
|
|
140
242
|
roadmapPhaseVariants.add(variant);
|
|
141
243
|
}
|
|
142
|
-
|
|
244
|
+
// A token borne by BOTH a sentinel and a real heading is not suppressed.
|
|
245
|
+
for (const t of realTokens)
|
|
246
|
+
sentinelOnly.delete(t);
|
|
247
|
+
return { roadmapPhases, roadmapPhaseVariants, sentinelPhases: sentinelOnly };
|
|
143
248
|
}
|
|
144
|
-
function buildNotStartedPhaseVariants(roadmapContent) {
|
|
249
|
+
function buildNotStartedPhaseVariants(roadmapContent, convention) {
|
|
145
250
|
const notStartedPhases = new Set();
|
|
146
251
|
// Also matches milestone-prefixed and bracket-prefixed checklist items.
|
|
147
|
-
|
|
252
|
+
// Trailing class is `[:\s*]` — a SPACE terminates the token here, not only a
|
|
253
|
+
// colon — so this site is the loosest of the three and the one where a
|
|
254
|
+
// retro-granted bracket tolerance would suppress a live W006.
|
|
255
|
+
const uncheckedPattern = new RegExp(`-\\s*\\[\\s\\]\\s*\\*{0,2}${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention)}([\\w][\\w.-]*)[:\\s*]`, 'gi');
|
|
148
256
|
let um;
|
|
149
257
|
while ((um = uncheckedPattern.exec(roadmapContent)) !== null) {
|
|
150
258
|
for (const variant of phaseVariants(um[1]))
|
|
@@ -152,6 +260,116 @@ function buildNotStartedPhaseVariants(roadmapContent) {
|
|
|
152
260
|
}
|
|
153
261
|
return notStartedPhases;
|
|
154
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Bracket-coherence check (#612). ADVISORY, and consumed ONLY under
|
|
265
|
+
* `phase_id_convention === 'bracket'`. Two sub-checks, both surfaced as W021:
|
|
266
|
+
* (1) a phase whose in-bracket milestone differs from its enclosing section's;
|
|
267
|
+
* (2) a phase heading not in bracket form under a repo that opted into bracket.
|
|
268
|
+
* Sentinel milestones are exempt — they have no real milestone to cohere with.
|
|
269
|
+
*
|
|
270
|
+
* Anchored to tokenizeHeadings(): the tokenizer strips fenced code blocks, so a
|
|
271
|
+
* bracket heading inside a ```markdown example cannot raise a warning, and it
|
|
272
|
+
* yields the heading LEVEL so section-vs-phase is structural rather than a
|
|
273
|
+
* hash-counting regex.
|
|
274
|
+
*
|
|
275
|
+
* SCOPE RULES, all three load-bearing:
|
|
276
|
+
* - Only a genuine MILESTONE heading opens or closes a section. A `### Notes`
|
|
277
|
+
* is not a section boundary; treating any non-phase heading as one meant a
|
|
278
|
+
* single prose heading silently disabled both sub-checks for every phase
|
|
279
|
+
* after it.
|
|
280
|
+
* - A legacy `## v3.0` milestone heading CLOSES the bracket section, so a
|
|
281
|
+
* phase under it is out of scope rather than compared against — and reported
|
|
282
|
+
* against — a section it is not in.
|
|
283
|
+
* - An M-NN or letter-suffixed phase heading (`### Phase 2-01:`, `### Phase 12A:`)
|
|
284
|
+
* raises missing-bracket AND CONTINUES. Treating it as a section reset let a
|
|
285
|
+
* single M-NN heading — the exact mid-migration content this epic targets —
|
|
286
|
+
* silently disable the whole check.
|
|
287
|
+
*
|
|
288
|
+
* A bare `### 2026:` with no `Phase` label and no bracket is NOT a phase heading
|
|
289
|
+
* and raises nothing; the previous rule flagged year and version headings as
|
|
290
|
+
* phases needing migration.
|
|
291
|
+
*
|
|
292
|
+
* #3309 RE-HOMING NOTE: this function lived in `verify.cts` beside
|
|
293
|
+
* `checkMilestonePrefixMismatches`, its legacy-convention sibling, and was
|
|
294
|
+
* called inline from `cmdValidateHealth`'s W021 block. #3309 migrated
|
|
295
|
+
* `cmdValidateHealth` onto the rule table and relocated
|
|
296
|
+
* `checkMilestonePrefixMismatches`'s section walk into
|
|
297
|
+
* `planning-snapshot.cts`'s `roadmapDeclaredPhases` builder. This check follows
|
|
298
|
+
* its sibling: the body is byte-identical to the reviewed original, it moves
|
|
299
|
+
* into this pure no-I/O module (the owner of the other three ROADMAP-content
|
|
300
|
+
* scanners W006/W007 consume), `planning-snapshot.cts` calls it to produce the
|
|
301
|
+
* `roadmapBracketIncoherences` field, and `RULE_W021`
|
|
302
|
+
* (`health-diagnostic-rules/state-consistency.cts`) renders the messages. A
|
|
303
|
+
* `Rule.check(snapshot)` may not read raw `.planning/` text (ADR-3180 §8.1
|
|
304
|
+
* rule 2), so the read must happen in the snapshot layer either way.
|
|
305
|
+
*/
|
|
306
|
+
function checkBracketCoherence(roadmapContent) {
|
|
307
|
+
const incoherences = [];
|
|
308
|
+
// #3185: routed through the owner's predicate rather than reaching into its
|
|
309
|
+
// exported `SENTINEL_RANGES` array. `n` is an already-parsed, safe,
|
|
310
|
+
// non-negative milestone integer, so `String(n)` is a bare integer token and
|
|
311
|
+
// `isSentinelPhaseId`'s convention-less leading-int rule reduces to exactly
|
|
312
|
+
// `SENTINEL_RANGES.includes(n)` — same answer, one owner.
|
|
313
|
+
const isSentinel = (n) => isSentinelPhaseId(String(n));
|
|
314
|
+
const pad2 = (n) => (Number.isSafeInteger(n) ? String(n).padStart(2, '0') : 'unknown');
|
|
315
|
+
// A bracket PHASE heading: `### [CODE.MM] 05:` or `### [CODE.MM] Phase 5:`.
|
|
316
|
+
const bracketPhaseRe = new RegExp(`^\\[(${BRACKET_ID_SRC})\\][ \\t]*(?:Phase\\s+)?(${PHASE_NUMBER_TOKEN_SOURCE})\\s*:`, 'i');
|
|
317
|
+
// A NON-bracket phase heading — requires the literal `Phase` label, so a bare
|
|
318
|
+
// `2026:` year heading is not a phase. Token is the full phase-number grammar
|
|
319
|
+
// so M-NN and letter-suffixed ids are RECOGNIZED (and flagged), not skipped.
|
|
320
|
+
const legacyPhaseRe = new RegExp(`^Phase\\s+(${PHASE_NUMBER_TOKEN_SOURCE}|\\d+(?:-\\d+)+[A-Z]?(?:\\.\\d+)*)\\s*:`, 'i');
|
|
321
|
+
// A bracket MILESTONE section heading.
|
|
322
|
+
const bracketSectionRe = new RegExp(`^${BRACKET_MILESTONE_INTRO_CAPTURING_SRC}`, 'i');
|
|
323
|
+
// A legacy milestone section heading (`## v2.0 — Name`).
|
|
324
|
+
const legacyMilestoneRe = /^v\d+(?:\.\d+)*\b/i;
|
|
325
|
+
let sectionMilestone = null;
|
|
326
|
+
for (const heading of (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(roadmapContent)) {
|
|
327
|
+
const text = heading.text.trim();
|
|
328
|
+
const bracketPhase = text.match(bracketPhaseRe);
|
|
329
|
+
const legacyPhase = bracketPhase ? null : text.match(legacyPhaseRe);
|
|
330
|
+
const isPhaseHeading = Boolean(bracketPhase || legacyPhase);
|
|
331
|
+
if (!isPhaseHeading && heading.level <= 3) {
|
|
332
|
+
const bracketSection = text.match(bracketSectionRe);
|
|
333
|
+
if (bracketSection) {
|
|
334
|
+
const mm = parseInt(bracketSection[1], 10);
|
|
335
|
+
sectionMilestone = Number.isSafeInteger(mm) ? mm : null;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
// Only a MILESTONE heading closes the section. Any other heading (`### Notes`,
|
|
339
|
+
// `## Backlog`) leaves the scope exactly as it was.
|
|
340
|
+
if (legacyMilestoneRe.test(text.replace(/^\[[^\]]{0,200}\][ \t]*/, '')))
|
|
341
|
+
sectionMilestone = null;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (!isPhaseHeading)
|
|
345
|
+
continue;
|
|
346
|
+
if (sectionMilestone === null || isSentinel(sectionMilestone))
|
|
347
|
+
continue;
|
|
348
|
+
if (bracketPhase) {
|
|
349
|
+
const folded = foldBracketId(bracketPhase[1]);
|
|
350
|
+
const dot = folded.lastIndexOf('.');
|
|
351
|
+
const phaseMilestone = parseInt(folded.slice(dot + 1), 10);
|
|
352
|
+
if (!Number.isSafeInteger(phaseMilestone) || isSentinel(phaseMilestone))
|
|
353
|
+
continue;
|
|
354
|
+
if (phaseMilestone !== sectionMilestone) {
|
|
355
|
+
incoherences.push({
|
|
356
|
+
kind: 'mismatch',
|
|
357
|
+
phaseId: bracketPhase[2],
|
|
358
|
+
sectionMilestone: pad2(sectionMilestone),
|
|
359
|
+
phaseMilestone: pad2(phaseMilestone),
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
incoherences.push({
|
|
365
|
+
kind: 'missing-bracket',
|
|
366
|
+
phaseId: legacyPhase[1],
|
|
367
|
+
sectionMilestone: pad2(sectionMilestone),
|
|
368
|
+
phaseMilestone: pad2(sectionMilestone),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
return incoherences;
|
|
372
|
+
}
|
|
155
373
|
/**
|
|
156
374
|
* Detect binary corruption (embedded NUL bytes) in a text artifact's bytes.
|
|
157
375
|
*
|
|
@@ -21,17 +21,55 @@ in-tree instead of depending on it being installed as an npm package.
|
|
|
21
21
|
`gsd-core/bin/lib/pattern.cjs`) for linear-time RE2 pattern compilation.
|
|
22
22
|
- `re2js.d.cts` — verbatim copy of `node_modules/re2js/build/index.d.cts`,
|
|
23
23
|
so TypeScript resolves types for the relative import from `src/pattern.cts`.
|
|
24
|
+
- `js-yaml.cjs` — verbatim copy of `node_modules/js-yaml/dist/js-yaml.js`
|
|
25
|
+
(upstream package `js-yaml`, pinned version see `package.json`
|
|
26
|
+
`devDependencies.js-yaml`). ADR-3473 §8.1 (#3881): the single YAML parser
|
|
27
|
+
this repo standardizes on. The `dist` UMD bundle is the vendorable
|
|
28
|
+
artifact — js-yaml's `exports.require` entry (`index.js`) is *not*
|
|
29
|
+
self-contained; `dist/js-yaml.js` loads under `require()`, contains zero
|
|
30
|
+
`require()` calls of its own, and exposes `load`/`dump`/`FAILSAFE_SCHEMA`/
|
|
31
|
+
`YAMLException`.
|
|
32
|
+
|
|
33
|
+
## Two kinds of type twin
|
|
34
|
+
|
|
35
|
+
Each vendored package needs a `.d.cts` under `src/vendor/` so TypeScript can
|
|
36
|
+
resolve types for a relative `./vendor/<pkg>.cjs` import from `src/**`
|
|
37
|
+
(module resolution for a `.cts` source is relative to `src/`, not the
|
|
38
|
+
compiled output dir). There are two kinds:
|
|
39
|
+
|
|
40
|
+
- **upstream-verbatim** (`re2js.d.cts`) — a byte-for-byte copy of an
|
|
41
|
+
upstream `.d.cts`/`.d.ts` that ships with the package. Both the
|
|
42
|
+
`gsd-core/bin/lib/vendor/` copy and the `src/vendor/` copy are checked by
|
|
43
|
+
`scripts/lint-vendored-deps.cjs` against `node_modules` and against each
|
|
44
|
+
other.
|
|
45
|
+
- **hand-authored** (`js-yaml.d.cts`) — js-yaml ships **no** type
|
|
46
|
+
declarations upstream and `@types/js-yaml` is not installed, so there is
|
|
47
|
+
nothing to copy verbatim. `src/vendor/js-yaml.d.cts` is written by hand,
|
|
48
|
+
declares only the symbols actually used (`load`, `dump`,
|
|
49
|
+
`FAILSAFE_SCHEMA`, `YAMLException`), and is **excluded** from
|
|
50
|
+
`lint-vendored-deps.cjs`'s byte-compare — there is no upstream file to
|
|
51
|
+
compare it against. The narrowness is deliberate: anchors, aliases,
|
|
52
|
+
custom types and `loadAll` are unreachable from typed code, which is a
|
|
53
|
+
compile-time enforcement of ADR-3473 §8.1's refusal to expand them.
|
|
24
54
|
|
|
25
55
|
## Do not hand-edit
|
|
26
56
|
|
|
27
|
-
These files are **verbatim** copies of
|
|
28
|
-
|
|
57
|
+
These files are **verbatim** copies of upstream build output. Never edit
|
|
58
|
+
them directly — refresh them from `node_modules` instead:
|
|
29
59
|
|
|
30
60
|
```
|
|
31
61
|
cp node_modules/re2js/build/index.cjs gsd-core/bin/lib/vendor/re2js.cjs
|
|
32
62
|
cp node_modules/re2js/build/index.d.cts gsd-core/bin/lib/vendor/re2js.d.cts
|
|
63
|
+
cp node_modules/re2js/build/index.d.cts src/vendor/re2js.d.cts
|
|
64
|
+
|
|
65
|
+
cp node_modules/js-yaml/dist/js-yaml.js gsd-core/bin/lib/vendor/js-yaml.cjs
|
|
66
|
+
# js-yaml.d.cts has no upstream counterpart — update src/vendor/js-yaml.d.cts
|
|
67
|
+
# by hand if the js-yaml API surface this repo depends on changes.
|
|
33
68
|
```
|
|
34
69
|
|
|
35
|
-
`node scripts/lint-vendored-deps.cjs` fails CI if
|
|
36
|
-
byte-for-byte from `node_modules
|
|
37
|
-
pinned in
|
|
70
|
+
`node scripts/lint-vendored-deps.cjs` fails CI if any vendored copy drifts
|
|
71
|
+
byte-for-byte from its `node_modules` upstream, from its own source-side
|
|
72
|
+
type twin (upstream-verbatim twins only), or from the version pinned in
|
|
73
|
+
`package.json` `devDependencies`. It is table-driven (`VENDORED` in that
|
|
74
|
+
script) — adding a third vendored package means adding a row, not a second
|
|
75
|
+
hardcoded check block (ADR-3473 §8.3, "one implementation per rule").
|