@opengsd/gsd-core 1.15.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/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +7 -2
- package/agents/gsd-planner.md +3 -3
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +42 -51
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +31 -9
- package/bin/install.js +126 -196
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +5 -0
- package/commands/gsd/capture.md +8 -4
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +5 -1
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +5 -1
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +6 -1
- package/commands/gsd/quick-batch.md +20 -8
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +5 -1
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +142 -56
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +78 -44
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +183 -103
- package/gsd-core/bin/lib/capability-validator.cjs +16 -0
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +173 -170
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config.cjs +43 -12
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +413 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +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/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/init.cjs +162 -96
- package/gsd-core/bin/lib/install-engine.cjs +19 -35
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- 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 +19 -1
- package/gsd-core/bin/lib/model-resolver.cjs +18 -18
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id.cjs +1 -1
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +280 -80
- package/gsd-core/bin/lib/plan-document.cjs +93 -19
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +273 -40
- package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +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/roadmap-command-router.cjs +13 -15
- package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
- package/gsd-core/bin/lib/roadmap.cjs +169 -72
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +91 -157
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +307 -591
- package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
- 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.cjs +214 -105
- package/gsd-core/bin/lib/surface.cjs +2 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
- package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
- package/gsd-core/bin/lib/uat.cjs +424 -7
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
- package/gsd-core/bin/lib/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 +1165 -314
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +286 -567
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +10 -4
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +3 -2
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +3 -2
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +28 -18
- package/gsd-core/workflows/check-todos.md +6 -5
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +5 -4
- package/gsd-core/workflows/code-review.md +84 -90
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +12 -7
- package/gsd-core/workflows/debug.md +30 -5
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +1 -1
- package/gsd-core/workflows/docs-update.md +4 -3
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +5 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase.md +78 -170
- package/gsd-core/workflows/execute-plan.md +13 -19
- package/gsd-core/workflows/explore.md +2 -2
- package/gsd-core/workflows/extract-learnings.md +3 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +3 -2
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +5 -3
- package/gsd-core/workflows/map-codebase.md +4 -3
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +10 -10
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
- package/gsd-core/workflows/plan-phase.md +27 -18
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +42 -31
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +3 -2
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +11 -12
- package/gsd-core/workflows/settings-advanced.md +1 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +12 -12
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +1 -1
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +5 -4
- package/gsd-core/workflows/ui-review.md +4 -3
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/validate-phase.md +10 -12
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +68 -59
- package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +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 +147 -16
- package/hooks/dist/gsd-statusline.js +15 -7
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +8 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +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 +147 -16
- package/hooks/gsd-statusline.js +15 -7
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +21 -4
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +12 -8
- package/scripts/docs-guard-registry.cjs +6 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +10 -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 +20 -2
- package/scripts/lib/ndjson-reporter.cjs +28 -3
- package/scripts/lib/platform-conformance-tier.generated.cjs +34 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +29 -0
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-test-file-count.allowlist.json +11 -1
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +5 -0
- package/skills/gsd-capture/SKILL.md +8 -4
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +5 -1
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +6 -1
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +20 -8
- package/skills/gsd-review/SKILL.md +5 -1
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { isBuiltin } = require('node:module');
|
|
6
|
+
const { buildSync } = require('esbuild');
|
|
7
|
+
|
|
8
|
+
/** Rebuild locked upstream code without edits; installed trees need no node_modules. */
|
|
9
|
+
function buildVendorBundle(root, entry) {
|
|
10
|
+
const result = buildSync({
|
|
11
|
+
absWorkingDir: root,
|
|
12
|
+
entryPoints: [entry],
|
|
13
|
+
bundle: true,
|
|
14
|
+
platform: 'node',
|
|
15
|
+
format: 'cjs',
|
|
16
|
+
target: 'node24',
|
|
17
|
+
write: false,
|
|
18
|
+
metafile: true,
|
|
19
|
+
legalComments: 'inline',
|
|
20
|
+
banner: { js: '// Generated from locked npm dependencies by lint-vendored-deps.cjs --fix. Do not edit.' },
|
|
21
|
+
});
|
|
22
|
+
for (const output of Object.values(result.metafile.outputs)) {
|
|
23
|
+
for (const imported of output.imports) {
|
|
24
|
+
if (imported.external && !isBuiltin(imported.path)) {
|
|
25
|
+
throw new Error(`Vendor bundle has an external dependency: ${imported.path}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const packages = new Map();
|
|
30
|
+
for (const input of Object.keys(result.metafile.inputs)) {
|
|
31
|
+
let dir = path.dirname(path.resolve(root, input));
|
|
32
|
+
while (dir !== root && dir !== path.dirname(dir)) {
|
|
33
|
+
const manifest = path.join(dir, 'package.json');
|
|
34
|
+
if (fs.existsSync(manifest)) {
|
|
35
|
+
const pkg = JSON.parse(fs.readFileSync(manifest, 'utf8'));
|
|
36
|
+
if (pkg.name && pkg.version) {
|
|
37
|
+
const licenses = fs.readdirSync(dir).filter((name) => /^(licen[sc]e|copying|notice)(\.|$)/i.test(name)).sort();
|
|
38
|
+
// saxes 6.0.0 omits its LICENSE from npm; preserve the exact tagged
|
|
39
|
+
// upstream file (https://github.com/lddubeau/saxes/blob/v6.0.0/LICENSE).
|
|
40
|
+
const supplemental = path.join(__dirname, 'vendor-licenses', `${pkg.name}-${pkg.version}.txt`);
|
|
41
|
+
if (licenses.length === 0 && !fs.existsSync(supplemental)) throw new Error(`Missing upstream license: ${pkg.name}`);
|
|
42
|
+
packages.set(`${pkg.name}@${pkg.version}`, [
|
|
43
|
+
`${pkg.name}@${pkg.version} (package metadata: ${pkg.license})`,
|
|
44
|
+
...licenses.map((name) => `${name}:\n${fs.readFileSync(path.join(dir, name), 'utf8')}`),
|
|
45
|
+
...(licenses.length === 0 ? [fs.readFileSync(supplemental, 'utf8')] : []),
|
|
46
|
+
].join('\n\n'));
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
dir = path.dirname(dir);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
code: result.outputFiles[0].contents,
|
|
55
|
+
notices: Buffer.from([...packages.keys()].sort().map((name) => packages.get(name)).join('\n\n---\n\n') + '\n'),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { buildVendorBundle };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
The ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
15
|
+
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
|
|
17
|
+
====
|
|
18
|
+
|
|
19
|
+
The following license is the one that governed sax, from which saxes
|
|
20
|
+
was forked. Isaac Schlueter is not *directly* involved with saxes so
|
|
21
|
+
don't go bugging him for saxes issues.
|
|
22
|
+
|
|
23
|
+
The ISC License
|
|
24
|
+
|
|
25
|
+
Copyright (c) Isaac Z. Schlueter and Contributors
|
|
26
|
+
|
|
27
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
28
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
29
|
+
copyright notice and this permission notice appear in all copies.
|
|
30
|
+
|
|
31
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
32
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
33
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
34
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
35
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
36
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
37
|
+
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
38
|
+
|
|
39
|
+
====
|
|
40
|
+
|
|
41
|
+
`String.fromCodePoint` by Mathias Bynens is no longer used, but it can
|
|
42
|
+
still be found in old commits. It was once used according to terms of
|
|
43
|
+
MIT License, as follows:
|
|
44
|
+
|
|
45
|
+
Copyright Mathias Bynens <https://mathiasbynens.be/>
|
|
46
|
+
|
|
47
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
48
|
+
a copy of this software and associated documentation files (the
|
|
49
|
+
"Software"), to deal in the Software without restriction, including
|
|
50
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
51
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
52
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
53
|
+
the following conditions:
|
|
54
|
+
|
|
55
|
+
The above copyright notice and this permission notice shall be
|
|
56
|
+
included in all copies or substantial portions of the Software.
|
|
57
|
+
|
|
58
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
59
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
60
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
61
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
62
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
63
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
64
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -805,17 +805,17 @@ const OWNER_FILE = path.join('src', 'verification.cts');
|
|
|
805
805
|
// one (the owner consumes plan counts, never the reverse). Left as its
|
|
806
806
|
// own, differently-scoped answer, per design's "Rejected" list item 1.
|
|
807
807
|
//
|
|
808
|
-
// - `buildWorkstreamInventory` (src/workstream-inventory-builder.cts) —
|
|
809
|
-
// PURE, I/O-free projection
|
|
810
|
-
//
|
|
811
|
-
// `summariesMeetPlans = summaryCount >= planCount && planCount > 0`
|
|
812
|
-
// answers "are all plans summarized?" (combined with the caller-supplied
|
|
813
|
-
// verification verdict for its own `status` projection) — it cannot call
|
|
808
|
+
// - `buildWorkstreamInventory` (src/workstream-inventory-builder.cts) —
|
|
809
|
+
// formerly exempted here (a PURE, I/O-free projection that hand-rolled
|
|
810
|
+
// `summaryCount >= planCount && planCount > 0` locally, unable to call
|
|
814
811
|
// the I/O-bound `isPhaseComplete` without breaking its "No I/O. No
|
|
815
|
-
// async." contract
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
//
|
|
812
|
+
// async." contract). #5060 (Phase Status Module, epic #5056) gave this
|
|
813
|
+
// exact "pure function over pre-collected facts" shape a real owner —
|
|
814
|
+
// `phaseStatusFromFacts` (`src/phase-status.cts`) — which is itself
|
|
815
|
+
// import-free at load time and does no I/O, so this function now calls
|
|
816
|
+
// it instead of re-deriving the comparison locally. The local textual
|
|
817
|
+
// shapes this guard matches are gone; the exemption entry was removed
|
|
818
|
+
// rather than left dead.
|
|
819
819
|
//
|
|
820
820
|
// - `buildPhaseCompletionProjection` (src/init.cts) — MIGRATED onto
|
|
821
821
|
// `isPhaseComplete` for `phase_complete`/`verification_status` (shape
|
|
@@ -832,17 +832,17 @@ const OWNER_FILE = path.join('src', 'verification.cts');
|
|
|
832
832
|
//
|
|
833
833
|
// SHAPE (d) reuses this exact map (`findScanPhasePlansCompletedReadDrift`
|
|
834
834
|
// takes the same `exemptFunctions` set every other shape's finder does). The
|
|
835
|
-
//
|
|
835
|
+
// entries above were entered for shapes (a)/(b)/(c); of them, only
|
|
836
836
|
// `scanPhasePlans` (src/plan-scan.cts) also legitimately reads its OWN
|
|
837
837
|
// `completed` field for shape (d)'s purposes (building the return value it
|
|
838
838
|
// itself defines — not a call-then-read of another `scanPhasePlans(`
|
|
839
|
-
// invocation). `isPhaseComplete
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
//
|
|
845
|
-
//
|
|
839
|
+
// invocation). `isPhaseComplete` and `buildPhaseCompletionProjection` do not
|
|
840
|
+
// read `.completed` off a `scanPhasePlans(` call result at all (verified by
|
|
841
|
+
// direct inspection of each function's body as of this guard's shape-(d)
|
|
842
|
+
// addition — they consume `planCount`/`summaryCount`/`summaryFiles`, never
|
|
843
|
+
// `.completed`), so their presence in this map exempts nothing NEW for shape
|
|
844
|
+
// (d); they are listed here only because the map is shared. As of this
|
|
845
|
+
// addition, a whole-repo
|
|
846
846
|
// scan found ZERO live shape-(d) sites needing a fresh exemption entry — the
|
|
847
847
|
// one real instance this shape exists to catch (`cmdStateSync`,
|
|
848
848
|
// src/state.cts) was fixed by routing through `isPhaseComplete` rather than
|
|
@@ -852,7 +852,6 @@ const OWNER_FILE = path.join('src', 'verification.cts');
|
|
|
852
852
|
const FUNCTION_SCOPED_EXEMPTIONS = new Map([
|
|
853
853
|
[OWNER_FILE, new Set(['isPhaseComplete'])],
|
|
854
854
|
[path.join('src', 'plan-scan.cts'), new Set(['scanPhasePlans'])],
|
|
855
|
-
[path.join('src', 'workstream-inventory-builder.cts'), new Set(['buildWorkstreamInventory'])],
|
|
856
855
|
[path.join('src', 'init.cts'), new Set(['buildPhaseCompletionProjection'])],
|
|
857
856
|
]);
|
|
858
857
|
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
18
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
19
|
+
// The one frontmatter fence owner (src/frontmatter-fence.cts, built by build:lib).
|
|
20
|
+
// This dev-time script runs after the build (`npm run lint:descriptions`, the tests).
|
|
21
|
+
const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
|
|
19
22
|
|
|
20
23
|
const MAX_LENGTH = 100;
|
|
21
24
|
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
|
@@ -25,9 +28,10 @@ const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
|
|
25
28
|
* Returns null if no description is found.
|
|
26
29
|
*/
|
|
27
30
|
function parseDescription(content) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
// The block is the one the one fence owner finds (found while implementing #5105).
|
|
32
|
+
const fence = locateFrontmatterFence(content);
|
|
33
|
+
if (!fence || !fence.closed) return null;
|
|
34
|
+
const fm = content.slice(fence.openEnd, fence.bodyEnd);
|
|
31
35
|
|
|
32
36
|
const quoted = fm.match(/^description:\s+"((?:[^"\\]|\\.)*)"\s*$/m);
|
|
33
37
|
if (quoted) return quoted[1];
|
|
@@ -88,6 +88,7 @@ const DOCS_GUARD_EXEMPT_BASELINE = [
|
|
|
88
88
|
'security-prompt-injection.security.test.cjs',
|
|
89
89
|
'shipped-reference-cites.test.cjs',
|
|
90
90
|
'state.test.cjs',
|
|
91
|
+
'verification-status.test.cjs',
|
|
91
92
|
'worktree-safety.test.cjs',
|
|
92
93
|
];
|
|
93
94
|
|
|
@@ -236,6 +237,15 @@ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
|
|
|
236
237
|
// #4186: the record-session usage-contract test cites the documented
|
|
237
238
|
// signature in docs/CLI-TOOLS.md in its explanatory comment.
|
|
238
239
|
'state.test.cjs': ['docs/CLI-TOOLS.md', 'docs/CONFIGURATION.md', 'docs/reference/state-md.md'],
|
|
240
|
+
// #5095: 'docs/VERIFICATION.md' is a tmpdir fixture basename (proving the
|
|
241
|
+
// report-shaped filter matches basename, not full path); 'docs/planning'
|
|
242
|
+
// and its two nested PLAN/SUMMARY children are an in-repo `.planning ->
|
|
243
|
+
// docs/planning` symlink-alias fixture. All are WRITTEN into a throwaway
|
|
244
|
+
// mkdtemp/createTempGitProject tree; none reads real shipped docs/ content.
|
|
245
|
+
'verification-status.test.cjs': [
|
|
246
|
+
'docs/VERIFICATION.md', 'docs/planning', 'docs/planning/phases',
|
|
247
|
+
'docs/planning/phases/01-foo/01-01-PLAN.md', 'docs/planning/phases/01-foo/01-01-SUMMARY.md',
|
|
248
|
+
],
|
|
239
249
|
'worktree-safety.test.cjs': ['docs/SUMMARY.md'],
|
|
240
250
|
};
|
|
241
251
|
|
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
"path": "src/vendor/js-yaml.d.cts",
|
|
12
12
|
"reason": "Vendored third-party type declaration (ADR-3473 §8.1, #3881): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored js-yaml .cjs it describes is globally ignored as verbatim upstream output (gsd-core/bin/lib/vendor/**). Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules — same precedent as src/vendor/re2js.d.cts above."
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
"path": "src/vendor/saxes.d.cts",
|
|
16
|
+
"reason": "Upstream type forwarding for the vendored saxes bundle (#4692): declaration-only export with no executable code. scripts/lint-vendored-deps.cjs enforces the exact forwarding declaration; TypeScript checks consumers against upstream types."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "src/vendor/tap-parser.d.cts",
|
|
20
|
+
"reason": "Upstream type forwarding for the vendored tap-parser bundle (#4692): declaration-only export with no executable code. scripts/lint-vendored-deps.cjs enforces the exact forwarding declaration; TypeScript checks consumers against upstream types."
|
|
21
|
+
},
|
|
14
22
|
{
|
|
15
23
|
"path": "tests/fixtures/brand-typing/bad-calibrated-as-sample-basis.cts",
|
|
16
24
|
"reason": "Deliberate MUST-NOT-COMPILE type-error fixture (#3059): type-aware linting would fail by design; it exists to prove the compiler rejects it."
|
|
@@ -34,5 +42,17 @@
|
|
|
34
42
|
{
|
|
35
43
|
"path": "tests/fixtures/brand-typing/ok-correct-composition.cts",
|
|
36
44
|
"reason": "Positive-control counterpart to the bad-* must-not-compile fixtures (#3059); kept in the same exemption so the pair stays together."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"path": "tests/fixtures/gate-evidence-typing/bad-pass-from-unreadable.cts",
|
|
48
|
+
"reason": "Deliberate MUST-NOT-COMPILE type-error fixture (#5170): the unreadable-evidence branding makes a passing outcome from unreadable evidence a compile error, and tests/gate-evidence.test.cjs asserts tsc rejects it; type-aware linting would fail by design."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"path": "tests/fixtures/gate-evidence-typing/bad-unbranded-unreadable.cts",
|
|
52
|
+
"reason": "Deliberate MUST-NOT-COMPILE type-error fixture (#5170): the unreadable-evidence branding makes a passing outcome from unreadable evidence a compile error, and tests/gate-evidence.test.cjs asserts tsc rejects it; type-aware linting would fail by design."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"path": "tests/fixtures/gate-evidence-typing/ok-unreadable-arm.cts",
|
|
56
|
+
"reason": "Positive-control counterpart to the gate-evidence-typing bad-* must-not-compile fixtures (#5170): tsc compiles it clean; kept in the same exemption so the pair stays together."
|
|
37
57
|
}
|
|
38
58
|
]
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Anti-divergence drift guard for the FRONTMATTER FENCE seam (found while
|
|
6
|
+
* implementing #5105; ADR-5057 one-owner rule).
|
|
7
|
+
*
|
|
8
|
+
* `src/frontmatter-fence.cts`'s `locateFrontmatterFence` is the SINGLE owner of
|
|
9
|
+
* "where does a document's frontmatter block start and stop" — the BOM, the
|
|
10
|
+
* byte-0 opening fence, the whole-line closing fence, an adjacent empty block,
|
|
11
|
+
* the lenient `----` closer. Before it existed that answer was re-derived in
|
|
12
|
+
* some thirty places across `src/` (regexes like `/^---\r?\n([\s\S]*?)\r?\n---/`,
|
|
13
|
+
* `indexOf('\n---', 4)`, `lines[0].trim() === '---'`), and the copies disagreed
|
|
14
|
+
* on a BOM, CRLF, an adjacent empty block, `----`, `--- x`, a `---` inside a
|
|
15
|
+
* value and leading whitespace. This guard keeps a new copy from appearing.
|
|
16
|
+
*
|
|
17
|
+
* Mechanism: the shared `scripts/lib/drift-scan.cjs` tree walk and regex-literal
|
|
18
|
+
* tokenizer (ADR-3180 Decision 4), scanning the WHOLE `src/` and `hooks/` trees
|
|
19
|
+
* (the hooks load the built `bin/lib` behind `ensureRuntimeBuild`), the dev-time
|
|
20
|
+
* `scripts/` and `eslint-rules/` trees (every one of them that runs after
|
|
21
|
+
* `build:lib` requires the owner from `gsd-core/bin/lib/frontmatter-fence.cjs`),
|
|
22
|
+
* the two hand-written entry points that load `bin/lib` (`bin/install.js`,
|
|
23
|
+
* `gsd-core/bin/gsd-tools.cjs`), and the two native plugin adapters
|
|
24
|
+
* (`.opencode/plugins/gsd-core.js`, `.kilo/plugins/gsd-core.js`), with
|
|
25
|
+
* FUNCTION-SCOPED exemptions carrying a written reason — never a bare file
|
|
26
|
+
* allowlist — exactly as `lint-milestone-window-drift.cjs` does.
|
|
27
|
+
*
|
|
28
|
+
* KEPT COPIES (found while implementing #5105). A file that must run where the
|
|
29
|
+
* built owner may not exist keeps a self-contained copy of `locateFrontmatterFence`,
|
|
30
|
+
* and only that one function is allowlisted: `scripts/changeset/parse.cjs` (the
|
|
31
|
+
* `changeset-lint` CI job runs it with no `npm ci` and no build) and the two plugin
|
|
32
|
+
* adapters (a package/git-spec tree may carry no built `bin/lib`). Each copy is
|
|
33
|
+
* pinned to the owner by tests/frontmatter-fence.test.cjs ("kept frontmatter fence
|
|
34
|
+
* copies agree with the owner") over a fixture corpus and a property test.
|
|
35
|
+
*
|
|
36
|
+
* DETECTORS. Three files carry a regex that recognizes a fence idiom in OTHER
|
|
37
|
+
* text — this guard itself, `lint-frontmatter-scalar-broad-grep.cjs` (workflow
|
|
38
|
+
* shell snippets) and the `no-crlf-fragile-split` ESLint rule (regex literals in
|
|
39
|
+
* source). None of them locates a frontmatter block; each is exempt only for its
|
|
40
|
+
* exact fragment (`DETECTOR_EXEMPTIONS`), so any other fence code in the same
|
|
41
|
+
* file is still flagged.
|
|
42
|
+
*
|
|
43
|
+
* A line (comment text stripped) is a fence re-derivation when it carries:
|
|
44
|
+
* (a) a regex literal whose text contains `---` (`/^---/`, `/\n---/`,
|
|
45
|
+
* `/(\r?\n)---$/`), or
|
|
46
|
+
* (b) `new RegExp(` together with a string/template literal containing `---`, or
|
|
47
|
+
* (c) a fence-shaped string literal — `'---'`, `'\n---'`, `'---\n'`,
|
|
48
|
+
* `'---\r\n'`, `'\r\n---'` — that is the direct argument of
|
|
49
|
+
* `.startsWith(`/`.endsWith(`/`.indexOf(`/`.lastIndexOf(`/`.split(`, or an
|
|
50
|
+
* operand of `===`/`!==`/`==`/`!=`, or (only when it carries a line
|
|
51
|
+
* ending) the argument of `.includes(`.
|
|
52
|
+
* A template literal that BUILDS a block (`` `---\n${yaml}\n---` ``) or an array
|
|
53
|
+
* of fence lines to join is a writer emitting a fence, not a reader locating
|
|
54
|
+
* one, and is not flagged.
|
|
55
|
+
*
|
|
56
|
+
* KNOWN, ACCEPTED limits of a per-line textual scan (the sibling guards'
|
|
57
|
+
* tradeoff): a derivation split across lines, or routed through a variable
|
|
58
|
+
* holding the `---` literal, is left to code review and to the consumer-parity
|
|
59
|
+
* tests in `tests/frontmatter-fence.test.cjs`.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
const fs = require('node:fs');
|
|
63
|
+
const path = require('node:path');
|
|
64
|
+
const driftScan = require('./lib/drift-scan.cjs');
|
|
65
|
+
const { readRegexLiteralAt, MAX_REGEX_LITERAL_LEN, sanitizeForReport, scanTree } = driftScan;
|
|
66
|
+
|
|
67
|
+
// Authored TypeScript source (the generated bin/lib/*.cjs mirror it) and the runtime hooks
|
|
68
|
+
// (`hooks/dist`, their build output, is skipped by the shared walker), plus the two hand-written
|
|
69
|
+
// entry points that load bin/lib and so can — and must — read the owner too.
|
|
70
|
+
const SCAN_TREES = [
|
|
71
|
+
{ dirs: ['src'], ext: new Set(['.cts', '.ts', '.mts']) },
|
|
72
|
+
{ dirs: ['hooks'], ext: new Set(['.js', '.cjs']) },
|
|
73
|
+
{ dirs: ['scripts', 'eslint-rules'], ext: new Set(['.js', '.cjs', '.mjs']) },
|
|
74
|
+
];
|
|
75
|
+
const SCAN_FILES = [
|
|
76
|
+
path.join('bin', 'install.js'),
|
|
77
|
+
path.join('gsd-core', 'bin', 'gsd-tools.cjs'),
|
|
78
|
+
path.join('.opencode', 'plugins', 'gsd-core.js'),
|
|
79
|
+
path.join('.kilo', 'plugins', 'gsd-core.js'),
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const OWNER_FILE = path.join('src', 'frontmatter-fence.cts');
|
|
83
|
+
|
|
84
|
+
// Code outside any top-level function (a module-scope constant) is keyed as this.
|
|
85
|
+
const TOP_LEVEL = '<top-level>';
|
|
86
|
+
|
|
87
|
+
// FUNCTION-SCOPED exemptions, each with its reason:
|
|
88
|
+
// - frontmatter-fence.cts `locateFrontmatterFence`: this IS the owner. Its
|
|
89
|
+
// module-scope fence constants (`CLOSING_FENCE_LINE`, `LENIENT_CLOSING_FENCE_LINE`)
|
|
90
|
+
// sit OUTSIDE this function, at TOP_LEVEL, and are exempted by exact fragment
|
|
91
|
+
// in DETECTOR_EXEMPTIONS below instead — a blanket TOP_LEVEL pass here would
|
|
92
|
+
// let any new top-level helper in this file go unscanned (#5105 fix).
|
|
93
|
+
// - phase.cts `phaseEntryInsertOffset`: its `lastIndexOf('\n---')` finds the
|
|
94
|
+
// ROADMAP's trailing section SEPARATOR (a thematic break closing the phase
|
|
95
|
+
// list) so a new phase entry is appended before it — it never asks where a
|
|
96
|
+
// frontmatter block is.
|
|
97
|
+
// - shell-command-projection.cts `_normalizeMd`: `prevTrimmed !== '---'` is
|
|
98
|
+
// the Markdown normalizer's "no blank line after a thematic break" rule,
|
|
99
|
+
// applied line by line to BODY text; that normalizer's frontmatter skip
|
|
100
|
+
// (`leadingFrontmatterLineCount`) reads the owner.
|
|
101
|
+
// - scripts/changeset/parse.cjs `locateFrontmatterFence`: a kept copy of the
|
|
102
|
+
// owner (#5105) — the `changeset-lint` job in changeset-required.yml runs it
|
|
103
|
+
// with no `npm ci` and no `build:lib`. Pinned to the owner by the parity test
|
|
104
|
+
// tests/frontmatter-fence.test.cjs "kept frontmatter fence copies agree with the owner".
|
|
105
|
+
// - .opencode/plugins/gsd-core.js and .kilo/plugins/gsd-core.js
|
|
106
|
+
// `locateFrontmatterFence`: kept copies of the owner (#5105) — the plugin loads
|
|
107
|
+
// from a package/git-spec tree that may carry no built bin/lib. Pinned to the
|
|
108
|
+
// owner by the same parity test.
|
|
109
|
+
const FUNCTION_SCOPED_EXEMPTIONS = new Map([
|
|
110
|
+
[OWNER_FILE, new Set(['locateFrontmatterFence'])],
|
|
111
|
+
[path.join('src', 'phase.cts'), new Set(['phaseEntryInsertOffset'])],
|
|
112
|
+
[path.join('src', 'shell-command-projection.cts'), new Set(['_normalizeMd'])],
|
|
113
|
+
[path.join('scripts', 'changeset', 'parse.cjs'), new Set(['locateFrontmatterFence'])],
|
|
114
|
+
[path.join('.opencode', 'plugins', 'gsd-core.js'), new Set(['locateFrontmatterFence'])],
|
|
115
|
+
[path.join('.kilo', 'plugins', 'gsd-core.js'), new Set(['locateFrontmatterFence'])],
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
// FRAGMENT-SCOPED exemptions for DETECTORS (#5105), each pinned to the exact
|
|
119
|
+
// reported fragment (the literal's source text) — a regex that recognizes a fence
|
|
120
|
+
// idiom in other text, never one that locates a frontmatter block:
|
|
121
|
+
// - this guard's own fence-literal matcher (`FENCE_LITERAL_CONTENT_RE`);
|
|
122
|
+
// - lint-frontmatter-scalar-broad-grep.cjs `FRONTMATTER_SCOPE_RE`, which finds a
|
|
123
|
+
// `^---`-scoped extraction in a workflow's shell snippet;
|
|
124
|
+
// - the no-crlf-fragile-split ESLint rule's `^---` probe, which classifies a
|
|
125
|
+
// regex literal in linted source as frontmatter-shaped.
|
|
126
|
+
const DETECTOR_EXEMPTIONS = new Map([
|
|
127
|
+
[path.join('scripts', 'lint-frontmatter-fence-drift.cjs'), new Set(['/^(?:\\\\r)?(?:\\\\n)?---(?:\\\\r)?(?:\\\\n)?$/'])],
|
|
128
|
+
[path.join('scripts', 'lint-frontmatter-scalar-broad-grep.cjs'), new Set(['/\\^---[\\s\\S]{0,300}?---/'])],
|
|
129
|
+
[path.join('eslint-rules', 'no-crlf-fragile-split.cjs'), new Set(['/\\^---/'])],
|
|
130
|
+
// The owner's own module-scope fence-literal constants (#5105 fix): these ARE
|
|
131
|
+
// the canonical closing-fence patterns, not a re-derivation — exempted by their
|
|
132
|
+
// exact fragment rather than by a blanket TOP_LEVEL pass, so a NEW top-level
|
|
133
|
+
// helper in this file (one that merely happens to sit outside a function) is
|
|
134
|
+
// still caught.
|
|
135
|
+
[OWNER_FILE, new Set(['/^---[ \\t]*$/', '/^-{4,}[ \\t]*$/'])],
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
// Only a column-0 top-level `function` declaration updates the current-function
|
|
139
|
+
// tracker (mirrors lint-milestone-window-drift.cjs); ANY OTHER non-blank,
|
|
140
|
+
// non-`}` column-0 line — a `const`, a `module.exports =`, an `export const`
|
|
141
|
+
// arrow, anything — resets it to TOP_LEVEL. (#5105 fix: the previous version
|
|
142
|
+
// only reset on a fixed keyword list, so a top-level statement it didn't
|
|
143
|
+
// recognize — e.g. `module.exports = { ... }` — silently inherited the LAST
|
|
144
|
+
// exempted function's name and went unscanned.)
|
|
145
|
+
const TOP_LEVEL_FUNCTION_RE = /^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z0-9_$]+)\s*\(/;
|
|
146
|
+
const COLUMN_ZERO_RESET_RE = /^[^\s}]/;
|
|
147
|
+
|
|
148
|
+
const NEW_REGEXP_RE = /new\s+RegExp\s*\(/;
|
|
149
|
+
|
|
150
|
+
// A fence-shaped string literal's CONTENT (source text between the quotes):
|
|
151
|
+
// `---`, optionally preceded by `\n`/`\r\n` and optionally followed by `\n`/`\r\n`.
|
|
152
|
+
const FENCE_LITERAL_CONTENT_RE = /^(?:\\r)?(?:\\n)?---(?:\\r)?(?:\\n)?$/;
|
|
153
|
+
|
|
154
|
+
const SEARCH_CALL_BEFORE_RE = /\.(startsWith|endsWith|indexOf|lastIndexOf|split|includes)\(\s*$/;
|
|
155
|
+
const COMPARE_BEFORE_RE = /[!=]==?\s*$/;
|
|
156
|
+
const COMPARE_AFTER_RE = /^\s*[!=]==?/;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Read the quoted or backtick-delimited string literal starting at `line[start]`.
|
|
160
|
+
* Returns `{ text, end }` (delimiters included) or null. Same single-pass,
|
|
161
|
+
* escape-aware, bounded style as the shared `readRegexLiteralAt`.
|
|
162
|
+
*/
|
|
163
|
+
function readStringLiteralAt(line, start) {
|
|
164
|
+
const quote = line[start];
|
|
165
|
+
if (quote !== "'" && quote !== '"' && quote !== '`') return null;
|
|
166
|
+
const limit = Math.min(line.length, start + MAX_REGEX_LITERAL_LEN);
|
|
167
|
+
for (let i = start + 1; i < limit; i++) {
|
|
168
|
+
const ch = line[i];
|
|
169
|
+
if (ch === '\\') {
|
|
170
|
+
i++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (ch === '\r' || ch === '\n') return null;
|
|
174
|
+
if (ch === quote) return { text: line.slice(start, i + 1), end: i + 1 };
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Strip comment text from a line (whole-line `*`, `/*`, `//`, and a trailing
|
|
181
|
+
* `//`), as the sibling guards do — prose about a fence is not a fence.
|
|
182
|
+
*/
|
|
183
|
+
function stripComments(line) {
|
|
184
|
+
const trimmed = line.trim();
|
|
185
|
+
if (trimmed.startsWith('*') || trimmed.startsWith('/*') || trimmed.startsWith('//')) return '';
|
|
186
|
+
const idx = line.indexOf('//');
|
|
187
|
+
return idx === -1 ? line : line.slice(0, idx);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Every regex and string literal on `code`, in order, with its kind and span. */
|
|
191
|
+
function literalsOf(code) {
|
|
192
|
+
const out = [];
|
|
193
|
+
for (let i = 0; i < code.length; i++) {
|
|
194
|
+
const ch = code[i];
|
|
195
|
+
let literal = null;
|
|
196
|
+
let kind = null;
|
|
197
|
+
if (ch === '/' && code[i + 1] !== '/' && code[i + 1] !== '*') {
|
|
198
|
+
literal = readRegexLiteralAt(code, i);
|
|
199
|
+
kind = 'regex';
|
|
200
|
+
} else if (ch === "'" || ch === '"' || ch === '`') {
|
|
201
|
+
literal = readStringLiteralAt(code, i);
|
|
202
|
+
kind = 'string';
|
|
203
|
+
}
|
|
204
|
+
if (!literal) continue;
|
|
205
|
+
out.push({ kind, text: literal.text, start: i, end: literal.end });
|
|
206
|
+
i = literal.end - 1;
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** The fence re-derivation fragment on a comment-stripped line, or null. */
|
|
212
|
+
function fenceFragment(code) {
|
|
213
|
+
const literals = literalsOf(code);
|
|
214
|
+
const hasNewRegExp = NEW_REGEXP_RE.test(code);
|
|
215
|
+
for (const lit of literals) {
|
|
216
|
+
if (lit.kind === 'regex') {
|
|
217
|
+
if (lit.text.includes('---')) return lit.text;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const content = lit.text.slice(1, -1);
|
|
221
|
+
if (hasNewRegExp && content.includes('---')) return lit.text;
|
|
222
|
+
if (!FENCE_LITERAL_CONTENT_RE.test(content)) continue;
|
|
223
|
+
const before = code.slice(0, lit.start);
|
|
224
|
+
const after = code.slice(lit.end);
|
|
225
|
+
const call = SEARCH_CALL_BEFORE_RE.exec(before);
|
|
226
|
+
if (call && (call[1] !== 'includes' || content.includes('\\n'))) return lit.text;
|
|
227
|
+
if (COMPARE_BEFORE_RE.test(before) || COMPARE_AFTER_RE.test(after)) return lit.text;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Pure: every unsanctioned frontmatter-fence re-derivation in `text`.
|
|
234
|
+
* `relPath` is the repo-relative path, for reporting and the exemptions.
|
|
235
|
+
* Returns [{ line, fn, found }].
|
|
236
|
+
*/
|
|
237
|
+
function findFrontmatterFenceDrift(text, relPath) {
|
|
238
|
+
const out = [];
|
|
239
|
+
const lines = text.split('\n');
|
|
240
|
+
const exemptFunctions = FUNCTION_SCOPED_EXEMPTIONS.get(relPath) || null;
|
|
241
|
+
const exemptFragments = DETECTOR_EXEMPTIONS.get(relPath) || null;
|
|
242
|
+
let currentFunction = TOP_LEVEL;
|
|
243
|
+
for (let i = 0; i < lines.length; i++) {
|
|
244
|
+
const line = lines[i];
|
|
245
|
+
const fnMatch = TOP_LEVEL_FUNCTION_RE.exec(line);
|
|
246
|
+
if (fnMatch) currentFunction = fnMatch[1];
|
|
247
|
+
else if (COLUMN_ZERO_RESET_RE.test(line)) currentFunction = TOP_LEVEL;
|
|
248
|
+
|
|
249
|
+
const code = stripComments(line);
|
|
250
|
+
if (!code.includes('---')) continue;
|
|
251
|
+
const found = fenceFragment(code);
|
|
252
|
+
if (found === null) continue;
|
|
253
|
+
if (exemptFunctions && exemptFunctions.has(currentFunction)) continue;
|
|
254
|
+
if (exemptFragments && exemptFragments.has(found)) continue;
|
|
255
|
+
out.push({ line: i + 1, fn: currentFunction, found });
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Scan the authored source tree and entry points; every violation carries its repo-relative file. */
|
|
261
|
+
function scanRepo(root) {
|
|
262
|
+
const violations = [];
|
|
263
|
+
for (const { dirs, ext } of SCAN_TREES) {
|
|
264
|
+
violations.push(...scanTree({
|
|
265
|
+
root,
|
|
266
|
+
scanDirs: dirs,
|
|
267
|
+
scanExt: ext,
|
|
268
|
+
onFile(rel, text) {
|
|
269
|
+
return findFrontmatterFenceDrift(text, rel).map((d) => ({ file: rel, ...d }));
|
|
270
|
+
},
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
for (const rel of SCAN_FILES) {
|
|
274
|
+
let text;
|
|
275
|
+
try {
|
|
276
|
+
text = fs.readFileSync(path.join(root, rel), 'utf8');
|
|
277
|
+
} catch {
|
|
278
|
+
continue; // absent in a fixture tree
|
|
279
|
+
}
|
|
280
|
+
violations.push(...findFrontmatterFenceDrift(text, rel).map((d) => ({ file: rel, ...d })));
|
|
281
|
+
}
|
|
282
|
+
return violations;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function main() {
|
|
286
|
+
const root = path.join(__dirname, '..');
|
|
287
|
+
const violations = scanRepo(root);
|
|
288
|
+
if (violations.length === 0) {
|
|
289
|
+
process.stdout.write('ok frontmatter-fence-drift: no frontmatter fence re-derivations outside frontmatter-fence.cts\n');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
process.stderr.write('frontmatter-fence-drift: independent re-derivation(s) of the frontmatter fence found.\n');
|
|
293
|
+
process.stderr.write('Use `locateFrontmatterFence` (src/frontmatter-fence.cts) for offsets, or `frontmatterBlock` /\n');
|
|
294
|
+
process.stderr.write('`frontmatterRegion` / `extractFrontmatter` / `stripFrontmatter` (src/frontmatter.cts) for content:\n');
|
|
295
|
+
for (const d of violations) {
|
|
296
|
+
process.stderr.write(` ${sanitizeForReport(d.file)}:${d.line} (${sanitizeForReport(d.fn)}) ${sanitizeForReport(d.found)}\n`);
|
|
297
|
+
}
|
|
298
|
+
process.exitCode = 1;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (require.main === module) main();
|
|
302
|
+
|
|
303
|
+
module.exports = {
|
|
304
|
+
findFrontmatterFenceDrift,
|
|
305
|
+
scanRepo,
|
|
306
|
+
fenceFragment,
|
|
307
|
+
stripComments,
|
|
308
|
+
readStringLiteralAt,
|
|
309
|
+
OWNER_FILE,
|
|
310
|
+
TOP_LEVEL,
|
|
311
|
+
FUNCTION_SCOPED_EXEMPTIONS,
|
|
312
|
+
DETECTOR_EXEMPTIONS,
|
|
313
|
+
};
|