@opengsd/gsd-core 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lint-retired-runtime-name.cjs
|
|
4
|
+
*
|
|
5
|
+
* Prevents a RETIRED GSD runtime name from being presented as if it were a
|
|
6
|
+
* live runtime (#1928 retired the "Gemini CLI" runtime lane in favor of
|
|
7
|
+
* Antigravity). Mirrors scripts/lint-legacy-dir-name.cjs's structure and
|
|
8
|
+
* conventions (same problem shape: forbid a retired token, allowlist
|
|
9
|
+
* frozen/legitimate content, self-exempt, inline marker, `GSD_LINT_*_REPO_ROOT`
|
|
10
|
+
* test seam, ./lib/cli-exit.cjs, binary-file skip).
|
|
11
|
+
*
|
|
12
|
+
* THE PREDICATE (load-bearing — do not "simplify"):
|
|
13
|
+
* The forbidden token is matched CASE-SENSITIVELY, standalone (word
|
|
14
|
+
* boundaries), capitalised: /\bGemini\b/ — NO `i` flag.
|
|
15
|
+
*
|
|
16
|
+
* Case sensitivity IS the mechanism. Every LEGITIMATE reference to the
|
|
17
|
+
* "gemini" string in this repo is spelled differently and therefore cannot
|
|
18
|
+
* match a case-sensitive `Gemini`:
|
|
19
|
+
* - Antigravity's config homes are lowercase with a slash:
|
|
20
|
+
* ~/.gemini/antigravity, ~/.gemini/antigravity-ide, ~/.gemini/config
|
|
21
|
+
* - Google's model ids are lowercase and hyphenated:
|
|
22
|
+
* gemini-3.1-pro-preview, gemini-2.5-flash-lite
|
|
23
|
+
* - env vars are uppercase: GEMINI_CONFIG_DIR, GEMINI_SESSION_ID, GEMINI_API_KEY
|
|
24
|
+
* - the instruction file is GEMINI.md
|
|
25
|
+
* A bare capitalised `Gemini` therefore means the retired RUNTIME (or its
|
|
26
|
+
* retired reviewer lane) is being named as if live — which is the defect
|
|
27
|
+
* this guard exists to catch.
|
|
28
|
+
*
|
|
29
|
+
* RETIRED_RUNTIMES is a table, not a hardcoded single name, so the guard
|
|
30
|
+
* generalises to the next retired runtime for free. The name is built via
|
|
31
|
+
* split-string concatenation (same trick the legacy-dir-name precedent
|
|
32
|
+
* uses) so this guard script cannot flag itself.
|
|
33
|
+
*
|
|
34
|
+
* THREE TIERS OF EXEMPTION, deliberately ordered narrowest-first. The
|
|
35
|
+
* allowlist is the entire risk surface of this guard, so each tier states
|
|
36
|
+
* what it can and cannot see:
|
|
37
|
+
*
|
|
38
|
+
* 1. GENERAL RULES (apply anywhere, no per-file registration) — the two
|
|
39
|
+
* spellings that are legitimate wherever they appear:
|
|
40
|
+
* (a) the hook DIALECT Antigravity genuinely inherits: `Gemini-style`,
|
|
41
|
+
* `Gemini-compatible`, `Gemini スタイル`;
|
|
42
|
+
* (b) a model-display version on the provider/model axis
|
|
43
|
+
* (`Gemini 2.5 Pro`), REFUSED when a runtime word also appears on
|
|
44
|
+
* the line so a version can never launder a runtime claim.
|
|
45
|
+
* 2. PINNED OCCURRENCES (`ALLOWLIST_OCCURRENCES`) — per-file, per-line
|
|
46
|
+
* approved snippets. This is what makes a NEW occurrence in an
|
|
47
|
+
* otherwise-legitimate file FAIL: the file is not blanket-trusted, only
|
|
48
|
+
* the enumerated lines are. A pin that stops matching is reported as a
|
|
49
|
+
* stale allowlist entry, so the allowlist cannot silently rot.
|
|
50
|
+
* 3. FILE / DIR ALLOWLIST — blanket trust, and therefore reserved for
|
|
51
|
+
* content that is append-only by policy (ADRs, dated research) or
|
|
52
|
+
* generated-and-locked (CHANGELOG.md). A new occurrence inside these
|
|
53
|
+
* IS invisible to this guard; that is the accepted cost of not
|
|
54
|
+
* re-litigating frozen history on every run, and it is why the tier is
|
|
55
|
+
* kept as small as it is.
|
|
56
|
+
*
|
|
57
|
+
* Plus an anti-vacuity floor on files actually READ (not merely listed), so
|
|
58
|
+
* an empty or failed walk can never report a false clean.
|
|
59
|
+
*
|
|
60
|
+
* Exit 0 if no violations; exit 1 if any are found (with stderr diagnostics).
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
'use strict';
|
|
64
|
+
|
|
65
|
+
const { execFileSync } = require('child_process');
|
|
66
|
+
const fs = require('fs');
|
|
67
|
+
const path = require('path');
|
|
68
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
69
|
+
const { escapeRegex } = require('../gsd-core/bin/lib/pattern.cjs');
|
|
70
|
+
|
|
71
|
+
// Table of retired runtimes. Each name is constructed via split-string
|
|
72
|
+
// concatenation so this guard script cannot flag itself when scanned.
|
|
73
|
+
const RETIRED_RUNTIMES = [
|
|
74
|
+
{
|
|
75
|
+
name: 'Gem' + 'ini',
|
|
76
|
+
retiredBy: '#1928',
|
|
77
|
+
sunset: '2026-06-18',
|
|
78
|
+
successor: 'Antigravity',
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const RETIRED_RUNTIME_MATCHERS = RETIRED_RUNTIMES.map((r) => ({
|
|
83
|
+
runtime: r,
|
|
84
|
+
// Case-sensitive, no 'i' flag — see module header for why.
|
|
85
|
+
re: new RegExp('\\b' + r.name + '\\b', 'g'),
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
// Minimum plausible number of files actually READ. An empty or failed walk
|
|
89
|
+
// must never silently report "clean" (anti-vacuity requirement).
|
|
90
|
+
const MIN_EXPECTED_FILES = 150;
|
|
91
|
+
|
|
92
|
+
const ALLOW_MARKER = 'gsd-allow-retired-runtime-name';
|
|
93
|
+
// The escape hatch must carry a justification. Bare-marker abuse was raised in
|
|
94
|
+
// review: the marker is checked first, excuses the whole line, and the failure
|
|
95
|
+
// message advertises it, so an unexplained one is indistinguishable from a
|
|
96
|
+
// defect somebody silenced. Require `gsd-allow-retired-runtime-name: <reason>`.
|
|
97
|
+
const ALLOW_MARKER_RE = new RegExp(escapeRegex(ALLOW_MARKER) + ':\\s*\\S{3,}');
|
|
98
|
+
const SELF_PATH = path.resolve(__filename);
|
|
99
|
+
// GSD_LINT_RETIRED_RUNTIME_REPO_ROOT is used by tests to redirect the guard
|
|
100
|
+
// to a temporary fixture git repo without touching the real working tree.
|
|
101
|
+
const REPO_ROOT = process.env.GSD_LINT_RETIRED_RUNTIME_REPO_ROOT
|
|
102
|
+
? path.resolve(process.env.GSD_LINT_RETIRED_RUNTIME_REPO_ROOT)
|
|
103
|
+
: path.resolve(__dirname, '..');
|
|
104
|
+
|
|
105
|
+
// Every tracked *.md file is in scope. An earlier prefix list
|
|
106
|
+
// (docs/ gsd-core/ commands/ agents/ skills/ + root) left `.changeset/`,
|
|
107
|
+
// `.github/`, `capabilities/`, `playbooks/` and `references/` invisible —
|
|
108
|
+
// and `.changeset/*.md` RENDERS INTO CHANGELOG.md, which is blanket-trusted,
|
|
109
|
+
// so a live claim introduced there was invisible at both ends.
|
|
110
|
+
|
|
111
|
+
// TIER 3 — blanket trust. Append-only by policy, or generated-and-locked.
|
|
112
|
+
// A new occurrence inside these is invisible to this guard; see the module
|
|
113
|
+
// header for why that cost is accepted here and nowhere else.
|
|
114
|
+
const ALLOWLIST_FILES = new Set([
|
|
115
|
+
// Locked by CLAUDE.md ("CHANGELOG.md is Locked") and generated from
|
|
116
|
+
// changeset fragments: frozen release history, one entry per shipped
|
|
117
|
+
// release, including the releases that removed the runtime.
|
|
118
|
+
'CHANGELOG.md',
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
const ALLOWLIST_DIR_PREFIXES = [
|
|
122
|
+
// `.changeset/` is CHANGELOG.md before rendering — the same surface, one
|
|
123
|
+
// step upstream — so it gets the same treatment. Measured: all 21 hits are
|
|
124
|
+
// fragments DESCRIBING the retirement or a fix to it (e.g. "Retired the
|
|
125
|
+
// Gemini CLI reviewer lane", "Gemini install output is valid on Windows
|
|
126
|
+
// PowerShell"), which is what a release note is for. 464 of the 479 live
|
|
127
|
+
// under `archived/`. Pinning them would add friction with no signal: a
|
|
128
|
+
// fragment can only describe what already shipped, and it is deleted at
|
|
129
|
+
// release. The cost is stated rather than hidden — a fragment introducing a
|
|
130
|
+
// live claim is invisible here, as it is in CHANGELOG.md itself.
|
|
131
|
+
'.changeset/',
|
|
132
|
+
// Architecture decision records are append-only history.
|
|
133
|
+
'docs/adr/',
|
|
134
|
+
// Frozen historical release notes.
|
|
135
|
+
'docs/RELEASE-NOTES-LEGACY.md',
|
|
136
|
+
// Dated research records, incl. the gemini-to-antigravity migration note.
|
|
137
|
+
'docs/research/',
|
|
138
|
+
// Dated records.
|
|
139
|
+
'docs/issueevidence/',
|
|
140
|
+
'docs/discussions/',
|
|
141
|
+
'docs/superpowers/',
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
// TIER 2 — pinned occurrences. relPath -> [{ snippet, reason }]. A match is
|
|
145
|
+
// excused only when its LINE contains one of that file's snippets, so a new,
|
|
146
|
+
// unrelated occurrence in the same file still fails. Every snippet is a
|
|
147
|
+
// verbatim fragment of the line it approves.
|
|
148
|
+
const ALLOWLIST_OCCURRENCES = new Map([
|
|
149
|
+
['CONTEXT.md', [
|
|
150
|
+
// The name precedes the annotation on this line, so the snippet must span
|
|
151
|
+
// both for span containment to cover the match.
|
|
152
|
+
{ snippet: 'Gemini [runtime removed #1928]', reason: 'session log annotates the removal inline' },
|
|
153
|
+
]],
|
|
154
|
+
['GEMINI.md', [
|
|
155
|
+
{ snippet: 'Gemini CLI was sunset by Google on 2026-06-18', reason: 'states the sunset' },
|
|
156
|
+
{ snippet: 'shared Gemini 3 backend', reason: 'names the shared model backend Antigravity uses' },
|
|
157
|
+
]],
|
|
158
|
+
['docs/whats-new-1.7.0.md', [
|
|
159
|
+
// Two occurrences on one line, so two pins: span containment deliberately
|
|
160
|
+
// refuses to let the first excuse the second.
|
|
161
|
+
{ snippet: 'Gemini CLI removed** (#1928)', reason: 'release note announcing the removal' },
|
|
162
|
+
{ snippet: 'Google discontinued Gemini CLI on 2026-06-18', reason: 'states the sunset date' },
|
|
163
|
+
]],
|
|
164
|
+
['docs/explanation/embeddable-orchestration-system.md', [
|
|
165
|
+
{ snippet: 'Gemini CLI was retired in', reason: 'records the retirement' },
|
|
166
|
+
]],
|
|
167
|
+
// The fragment is the SOURCE; docs/FEATURES.md is generated from it by
|
|
168
|
+
// scripts/gen-features.cjs, so both carry the same approved line and the
|
|
169
|
+
// same pin covers each. A marker written into FEATURES.md by hand would be
|
|
170
|
+
// dropped on the next regeneration.
|
|
171
|
+
['docs/features/embeddable-orchestration-system-host-integration-interface.md', [
|
|
172
|
+
{ snippet: 'retired Gemini CLI now redirects to Antigravity CLI', reason: 'records the redirect' },
|
|
173
|
+
]],
|
|
174
|
+
['docs/FEATURES.md', [
|
|
175
|
+
{ snippet: 'retired Gemini CLI now redirects to Antigravity CLI', reason: 'generated from the fragment above' },
|
|
176
|
+
]],
|
|
177
|
+
['docs/reference/host-integration-capability-matrix.md', [
|
|
178
|
+
{ snippet: '(Gemini CLI\'s `thinkingConfig`) was removed as a sunset runtime', reason: 'explains why no config-file member exists' },
|
|
179
|
+
{ snippet: 'successor to the sunset Gemini CLI per #1928', reason: 'records the sunset and the inherited contract' },
|
|
180
|
+
]],
|
|
181
|
+
// --- The inherited hook dialect, where the line ALSO names a runtime -----
|
|
182
|
+
// Tier 1(a) requires no runtime word on the line, because the tight
|
|
183
|
+
// "immediately after the name" veto let real laundering through. These ten
|
|
184
|
+
// lines legitimately pair a dialect compound with a runtime word (usually
|
|
185
|
+
// `~/.gemini/antigravity-cli` in a table cell, or "runtime files" in the
|
|
186
|
+
// same sentence), so each is pinned explicitly instead of loosening the
|
|
187
|
+
// veto for everyone.
|
|
188
|
+
['docs/ARCHITECTURE.md', [
|
|
189
|
+
{ snippet: 'Gemini-style `settings.json` hook entries', reason: 'hook dialect Antigravity inherits' },
|
|
190
|
+
]],
|
|
191
|
+
['docs/ja-JP/ARCHITECTURE.md', [
|
|
192
|
+
{ snippet: 'Gemini スタイル `settings.json` フックエントリ', reason: 'hook dialect Antigravity inherits' },
|
|
193
|
+
]],
|
|
194
|
+
['docs/ko-KR/ARCHITECTURE.md', [
|
|
195
|
+
{ snippet: 'Gemini 스타일 `settings.json` 훅 항목', reason: 'hook dialect Antigravity inherits' },
|
|
196
|
+
]],
|
|
197
|
+
['docs/pt-BR/ARCHITECTURE.md', [
|
|
198
|
+
{ snippet: 'no estilo Gemini quando instalado', reason: 'hook dialect Antigravity inherits' },
|
|
199
|
+
]],
|
|
200
|
+
['docs/zh-CN/ARCHITECTURE.md', [
|
|
201
|
+
{ snippet: 'Gemini 风格 `settings.json` hook 条目', reason: 'hook dialect Antigravity inherits' },
|
|
202
|
+
]],
|
|
203
|
+
['docs/how-to/install-on-your-runtime.md', [
|
|
204
|
+
{ snippet: 'Uses Gemini-compatible settings policy', reason: 'settings dialect Antigravity inherits' },
|
|
205
|
+
]],
|
|
206
|
+
['docs/ja-JP/how-to/install-on-your-runtime.md', [
|
|
207
|
+
{ snippet: 'Gemini 互換', reason: 'settings dialect Antigravity inherits' },
|
|
208
|
+
]],
|
|
209
|
+
['docs/ko-KR/how-to/install-on-your-runtime.md', [
|
|
210
|
+
{ snippet: 'Gemini 호환 설정 정책을 사용합니다', reason: 'settings dialect Antigravity inherits' },
|
|
211
|
+
]],
|
|
212
|
+
['docs/pt-BR/how-to/install-on-your-runtime.md', [
|
|
213
|
+
{ snippet: 'compatível com Gemini', reason: 'settings dialect Antigravity inherits' },
|
|
214
|
+
]],
|
|
215
|
+
['docs/zh-CN/how-to/install-on-your-runtime.md', [
|
|
216
|
+
{ snippet: '与 Gemini 兼容的设置策略', reason: 'settings dialect Antigravity inherits' },
|
|
217
|
+
]],
|
|
218
|
+
['gsd-core/workflows/reapply-patches.md', [
|
|
219
|
+
{ snippet: 'pre-#1928 Gemini CLI install', reason: 'legacy-install patch location, explicitly pre-retirement' },
|
|
220
|
+
]],
|
|
221
|
+
['gsd-core/workflows/settings-advanced.md', [
|
|
222
|
+
{ snippet: '(Claude / OpenAI / Gemini / Qwen)', reason: 'model-provider menu, not a GSD runtime' },
|
|
223
|
+
]],
|
|
224
|
+
['gsd-core/references/ai-frameworks.md', [
|
|
225
|
+
{ snippet: 'Google Cloud / Gemini-committed teams', reason: 'provider axis (Google ADK)' },
|
|
226
|
+
{ snippet: 'Optimized for Gemini; supports other models', reason: 'model axis' },
|
|
227
|
+
{ snippet: 'teams already committed to Gemini', reason: 'model axis' },
|
|
228
|
+
{ snippet: 'Model flexibility is required beyond Gemini', reason: 'model axis' },
|
|
229
|
+
{ snippet: 'Gemini vendor lock-in in practice', reason: 'model axis' },
|
|
230
|
+
{ snippet: 'Google/Gemini-committed', reason: 'provider axis (Google ADK)' },
|
|
231
|
+
]],
|
|
232
|
+
['agents/gsd-framework-selector.md', [
|
|
233
|
+
// Two occurrences on one line: the menu label and its description.
|
|
234
|
+
{ snippet: 'Google (Gemini)', reason: 'model-provider choice, not a GSD runtime' },
|
|
235
|
+
{ snippet: 'Committed to Gemini / Google Cloud / Vertex AI', reason: 'the same menu entry\'s description' },
|
|
236
|
+
]],
|
|
237
|
+
['agents/gsd-framework-selector.compact.md', [
|
|
238
|
+
{ snippet: 'Google (Gemini)', reason: 'model-provider choice, not a GSD runtime' },
|
|
239
|
+
]],
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
// Every pin must contain a retired runtime name. Span containment means a
|
|
243
|
+
// snippet that does not contain the name can never cover a match, so such a
|
|
244
|
+
// pin is dead config that would also be reported stale forever — fail loud at
|
|
245
|
+
// load rather than mislead later.
|
|
246
|
+
for (const [relPath, pins] of ALLOWLIST_OCCURRENCES) {
|
|
247
|
+
for (const pin of pins) {
|
|
248
|
+
const names = RETIRED_RUNTIMES.filter((r) => pin.snippet.includes(r.name));
|
|
249
|
+
if (names.length === 0) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
'lint-retired-runtime-name: pin for ' + relPath + ' does not contain a retired '
|
|
252
|
+
+ 'runtime name and can never match: ' + JSON.stringify(pin.snippet),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// docs/RELEASE-NOTES-LEGACY.md is a file, not a directory, but the brief
|
|
259
|
+
// lists it alongside the dir prefixes — treat both files and dirs the same
|
|
260
|
+
// way via a "startsWith" prefix check.
|
|
261
|
+
function isAllowlisted(relPath) {
|
|
262
|
+
if (ALLOWLIST_FILES.has(relPath)) return true;
|
|
263
|
+
for (const prefix of ALLOWLIST_DIR_PREFIXES) {
|
|
264
|
+
if (relPath === prefix || relPath.startsWith(prefix)) return true;
|
|
265
|
+
}
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function isInScanSet(relPath) {
|
|
270
|
+
return relPath.endsWith('.md');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function isBinary(fullPath) {
|
|
274
|
+
// Read a small chunk and check for NUL bytes.
|
|
275
|
+
let fd;
|
|
276
|
+
try {
|
|
277
|
+
fd = fs.openSync(fullPath, 'r');
|
|
278
|
+
const buf = Buffer.allocUnsafe(512);
|
|
279
|
+
const bytesRead = fs.readSync(fd, buf, 0, 512, 0);
|
|
280
|
+
return buf.subarray(0, bytesRead).includes(0);
|
|
281
|
+
} catch {
|
|
282
|
+
return false;
|
|
283
|
+
} finally {
|
|
284
|
+
if (fd != null) {
|
|
285
|
+
try { fs.closeSync(fd); } catch { /* best-effort */ }
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// --- TIER 1(a): the hook dialect Antigravity genuinely inherits -------------
|
|
291
|
+
// Antigravity reads Gemini-shaped `settings.json` hook entries, and every
|
|
292
|
+
// locale names that dialect. Marker POSITION is language-dependent and was
|
|
293
|
+
// measured: en `Gemini-style` / `Gemini-compatible`, ja `Gemini スタイル`,
|
|
294
|
+
// ko `Gemini 스타일` / `호환`, zh `Gemini 风格` / `与 Gemini 兼容的`, and pt
|
|
295
|
+
// `no estilo Gemini` / `compatível com Gemini`, where it PRECEDES the name.
|
|
296
|
+
//
|
|
297
|
+
// The marker must form an ADJACENT COMPOUND with the name. An earlier version
|
|
298
|
+
// searched a ±24-character window, which let one legitimate dialect reference
|
|
299
|
+
// launder a second, unrelated live claim on the same line — a reviewer
|
|
300
|
+
// demonstrated `| Antigravity | Gemini-style hooks | Gemini support is live |`
|
|
301
|
+
// exiting 0, because the second occurrence sat 21 characters from `style`.
|
|
302
|
+
// Adjacency is the property that actually distinguishes a dialect NAME from a
|
|
303
|
+
// runtime CLAIM, so it is what is tested.
|
|
304
|
+
const DIALECT_MARKERS = [
|
|
305
|
+
'style', 'compatible', // en
|
|
306
|
+
'スタイル', '互換', // ja
|
|
307
|
+
'스타일', '호환', // ko
|
|
308
|
+
'风格', '兼容', // zh
|
|
309
|
+
'estilo', 'compatível', 'compatibilidade', // pt
|
|
310
|
+
];
|
|
311
|
+
const DIALECT_ALT = DIALECT_MARKERS.map(escapeRegex).join('|');
|
|
312
|
+
// Marker immediately follows: "Gemini-style", "Gemini スタイル", "Gemini 兼容".
|
|
313
|
+
const DIALECT_AFTER_RE = new RegExp('^[-\\s]?(?:' + DIALECT_ALT + ')', 'i');
|
|
314
|
+
// Marker immediately precedes, optionally through ONE short connective:
|
|
315
|
+
// "no estilo Gemini", "compatível com Gemini".
|
|
316
|
+
const DIALECT_BEFORE_RE = new RegExp(
|
|
317
|
+
'(?:' + DIALECT_ALT + ')\\s*(?:com|with|de|da|do|and)?\\s*$',
|
|
318
|
+
'i',
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
function isDialectReference(line, matchIndex, afterMatch) {
|
|
322
|
+
const adjacent = DIALECT_AFTER_RE.test(afterMatch)
|
|
323
|
+
|| DIALECT_BEFORE_RE.test(line.slice(0, matchIndex));
|
|
324
|
+
if (!adjacent) return false;
|
|
325
|
+
// A dialect marker is NOT a licence when the line also asserts a runtime.
|
|
326
|
+
// This veto is line-global on purpose: the tight "immediately after the
|
|
327
|
+
// name" form let `Suportamos Gemini, no estilo padrao, como runtime de
|
|
328
|
+
// instalacao.` and `Gemini 兼容,并且是受支持的运行时之一。` both exit 0 while
|
|
329
|
+
// literally containing `runtime` / `运行时`. Real lines that legitimately
|
|
330
|
+
// pair a dialect compound with a runtime word are handled by an explicit
|
|
331
|
+
// Tier-2 pin, not by loosening this.
|
|
332
|
+
return !RUNTIME_WORD_RE.test(line);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// --- TIER 1(b): the provider/model axis ------------------------------------
|
|
336
|
+
// Matches the display-name spelling of a model version: a version number
|
|
337
|
+
// optionally followed by a model qualifier, or one that ends the list item.
|
|
338
|
+
// Deliberately NOT "space then a digit" — that blanket rule also matched
|
|
339
|
+
// "Gemini 2.5 CLI as a supported runtime.", laundering a genuine stale-runtime
|
|
340
|
+
// claim through an attached version number. Full-width digits and CJK
|
|
341
|
+
// punctuation are included because the translated docs use them.
|
|
342
|
+
const DIGIT = '0-9\\uFF10-\\uFF19';
|
|
343
|
+
const SP = ' \\u3000'; // ASCII space + ideographic space
|
|
344
|
+
const QUALIFIER = 'Pro|Flash|Ultra|Nano|Lite|Preview|Exp|Thinking';
|
|
345
|
+
const PUNCT = ',;.:)\\]\\u3001\\u3002\\uFF0C\\uFF09\\uFF3D\\uFF1A';
|
|
346
|
+
const MODEL_DISPLAY_AFTER = new RegExp(
|
|
347
|
+
'^[' + SP + ']'
|
|
348
|
+
+ '[' + DIGIT + ']+(?:[.\\uFF0E][' + DIGIT + ']+)*'
|
|
349
|
+
+ '(?:'
|
|
350
|
+
+ '[' + SP + ']+(?:' + QUALIFIER + ')\\b[\\w-]*'
|
|
351
|
+
+ '|[' + SP + ']*[' + PUNCT + ']'
|
|
352
|
+
+ '|[' + SP + ']*$'
|
|
353
|
+
+ ')',
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
// Words that indicate a RUNTIME (not a model) is being named on the line, in
|
|
357
|
+
// every language this repo ships translated docs for. If any appears
|
|
358
|
+
// alongside a model-display version, the version does NOT get to launder the
|
|
359
|
+
// runtime claim — the line is still a violation.
|
|
360
|
+
//
|
|
361
|
+
// Latin terms are anchored with \b so "CLI" cannot match inside "client" and
|
|
362
|
+
// "host" cannot match inside "ghost"; CJK terms are matched raw because \b is
|
|
363
|
+
// ASCII-word-based and would never fire next to a non-word ideograph.
|
|
364
|
+
//
|
|
365
|
+
// Deliberately EXCLUDED: "agent" and "target". Both occur throughout ordinary
|
|
366
|
+
// prose ("AI coding agents (Claude Code, Codex, Gemini 2.5 Pro)"), so vetoing
|
|
367
|
+
// on them would red legitimate model lists rather than catch runtime claims.
|
|
368
|
+
const RUNTIME_WORDS = [
|
|
369
|
+
'CLI',
|
|
370
|
+
'runtime',
|
|
371
|
+
'runtimes',
|
|
372
|
+
'IDE',
|
|
373
|
+
'extension',
|
|
374
|
+
'host',
|
|
375
|
+
'ランタイム', // ja
|
|
376
|
+
'実行環境', // ja
|
|
377
|
+
'런타임', // ko
|
|
378
|
+
'运行时', // zh
|
|
379
|
+
'运行环境', // zh
|
|
380
|
+
'tempo de execução', // pt
|
|
381
|
+
];
|
|
382
|
+
const RUNTIME_WORD_RE = new RegExp(
|
|
383
|
+
RUNTIME_WORDS.map((w) => {
|
|
384
|
+
const esc = escapeRegex(w);
|
|
385
|
+
// Anchor only when the term begins/ends with an ASCII word character;
|
|
386
|
+
// \b is meaningless beside an ideograph.
|
|
387
|
+
return /^[A-Za-z0-9]/.test(w) ? '\\b' + esc + '\\b' : esc;
|
|
388
|
+
}).join('|'),
|
|
389
|
+
'i',
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
// Positive evidence that the line is on the MODEL axis. Required, not merely
|
|
393
|
+
// the absence of a runtime word: a reviewer demonstrated that "absence of a
|
|
394
|
+
// veto word" is not evidence, with `The installer now offers Gemini 3.`,
|
|
395
|
+
// `GSD installs cleanly on Gemini 3, Kimi, and Codex.` and
|
|
396
|
+
// `Supported agents include Gemini 3, Kimi, and Cursor.` all exiting 0 — the
|
|
397
|
+
// exact laundering class this guard exists to catch. Every real model-axis
|
|
398
|
+
// line in this repo names a model explicitly, so requiring it costs nothing
|
|
399
|
+
// and inverts the failure direction from "silently allow" to "flag".
|
|
400
|
+
const MODEL_WORDS = [
|
|
401
|
+
'model', 'models', // en
|
|
402
|
+
'モデル', // ja
|
|
403
|
+
'모델', // ko
|
|
404
|
+
'模型', // zh
|
|
405
|
+
'modelo', 'modelos', // pt
|
|
406
|
+
];
|
|
407
|
+
const MODEL_WORD_RE = new RegExp(
|
|
408
|
+
MODEL_WORDS.map((w) => {
|
|
409
|
+
const esc = escapeRegex(w);
|
|
410
|
+
return /^[A-Za-z0-9]/.test(w) ? '\\b' + esc + '\\b' : esc;
|
|
411
|
+
}).join('|'),
|
|
412
|
+
'i',
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Is `pin` satisfied AT this match? The match must fall INSIDE an occurrence
|
|
417
|
+
* of the pin's snippet, not merely share a line with it.
|
|
418
|
+
*
|
|
419
|
+
* Line-level containment was too weak: a reviewer showed
|
|
420
|
+
* `Known provider menu update: Gemini CLI is once again a selectable GSD
|
|
421
|
+
* runtime.` and `Install target: Google (Gemini) — choose Gemini CLI as your
|
|
422
|
+
* GSD runtime.` both exiting 0, because a short snippet elsewhere on the line
|
|
423
|
+
* pre-approved a brand-new claim. Span containment means a pin can only ever
|
|
424
|
+
* excuse the occurrence its own text covers.
|
|
425
|
+
*/
|
|
426
|
+
function pinCoversMatch(line, matchIndex, matchedText, pin) {
|
|
427
|
+
const end = matchIndex + matchedText.length;
|
|
428
|
+
let from = 0;
|
|
429
|
+
for (;;) {
|
|
430
|
+
const at = line.indexOf(pin.snippet, from);
|
|
431
|
+
if (at === -1) return false;
|
|
432
|
+
if (matchIndex >= at && end <= at + pin.snippet.length) return true;
|
|
433
|
+
from = at + 1;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Decide whether one MATCH (not the whole line) is legitimate.
|
|
439
|
+
*
|
|
440
|
+
* Returns the matched pin, `true` for a general-rule exemption, or `false`
|
|
441
|
+
* for a violation.
|
|
442
|
+
*/
|
|
443
|
+
function classifyMatch(line, matchIndex, matchedText, pins) {
|
|
444
|
+
// The explicit escape hatch, which must carry a reason.
|
|
445
|
+
if (ALLOW_MARKER_RE.test(line)) return true;
|
|
446
|
+
|
|
447
|
+
const afterMatch = line.slice(matchIndex + matchedText.length);
|
|
448
|
+
|
|
449
|
+
// TIER 1(a) — inherited hook dialect, as an adjacent compound.
|
|
450
|
+
if (isDialectReference(line, matchIndex, afterMatch)) return true;
|
|
451
|
+
|
|
452
|
+
// TIER 1(b) — provider/model axis. Needs positive model evidence AND no
|
|
453
|
+
// runtime word; a version number alone never launders a claim.
|
|
454
|
+
if (MODEL_DISPLAY_AFTER.test(afterMatch)
|
|
455
|
+
&& MODEL_WORD_RE.test(line)
|
|
456
|
+
&& !RUNTIME_WORD_RE.test(line)) {
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// TIER 2 — a pinned occurrence for this specific file, span-scoped.
|
|
461
|
+
for (const pin of pins) {
|
|
462
|
+
if (pinCoversMatch(line, matchIndex, matchedText, pin)) return pin;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function main() {
|
|
469
|
+
// Enumerate tracked files via git ls-files so only committed/staged source is checked.
|
|
470
|
+
let trackedFiles;
|
|
471
|
+
try {
|
|
472
|
+
trackedFiles = execFileSync('git', ['ls-files'], { cwd: REPO_ROOT, encoding: 'utf8' })
|
|
473
|
+
.split('\n')
|
|
474
|
+
.map((f) => f.trim())
|
|
475
|
+
.filter(Boolean);
|
|
476
|
+
} catch (err) {
|
|
477
|
+
throw new ExitError(1, 'ERROR lint-retired-runtime-name: git ls-files failed: ' + err.message);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const scanFiles = trackedFiles.filter(isInScanSet);
|
|
481
|
+
|
|
482
|
+
const violations = [];
|
|
483
|
+
const usedPins = new Set();
|
|
484
|
+
// Files present in the scan set, so a stale-pin check can distinguish
|
|
485
|
+
// "this pin's file is gone/not in this tree" from "this pin matched nothing".
|
|
486
|
+
const scannedRelPaths = new Set();
|
|
487
|
+
// Anti-vacuity counts files actually READ, not merely listed: if every read
|
|
488
|
+
// failed, a candidate count would still look healthy.
|
|
489
|
+
let filesRead = 0;
|
|
490
|
+
|
|
491
|
+
for (const relPath of scanFiles) {
|
|
492
|
+
if (isAllowlisted(relPath)) continue;
|
|
493
|
+
|
|
494
|
+
const fullPath = path.join(REPO_ROOT, relPath);
|
|
495
|
+
|
|
496
|
+
// Skip this guard script itself (path-based, though it is not a .md file
|
|
497
|
+
// and would never be in-scan-set anyway — defense in depth).
|
|
498
|
+
if (path.resolve(fullPath) === SELF_PATH) continue;
|
|
499
|
+
|
|
500
|
+
if (isBinary(fullPath)) continue;
|
|
501
|
+
|
|
502
|
+
let content;
|
|
503
|
+
try {
|
|
504
|
+
content = fs.readFileSync(fullPath, 'utf8');
|
|
505
|
+
} catch {
|
|
506
|
+
// Unreadable files (permissions, etc.) — skip silently.
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
filesRead += 1;
|
|
510
|
+
scannedRelPaths.add(relPath);
|
|
511
|
+
|
|
512
|
+
const pins = ALLOWLIST_OCCURRENCES.get(relPath) || [];
|
|
513
|
+
|
|
514
|
+
const lines = content.split('\n');
|
|
515
|
+
for (let i = 0; i < lines.length; i++) {
|
|
516
|
+
const line = lines[i];
|
|
517
|
+
|
|
518
|
+
// Mark pins used by PRESENCE on the line, independent of which tier
|
|
519
|
+
// ultimately excused the match. Previously this was recorded only on
|
|
520
|
+
// the Tier-2 branch, so a pinned line that a general rule also matched
|
|
521
|
+
// never marked its pin used and the pin was reported stale — a reviewer
|
|
522
|
+
// demonstrated a provably false "no line matches pinned snippet" whose
|
|
523
|
+
// printed remedy told the maintainer to delete a still-needed pin.
|
|
524
|
+
for (const pin of pins) {
|
|
525
|
+
if (line.includes(pin.snippet)) usedPins.add(relPath + ' ' + pin.snippet);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
for (const { runtime, re } of RETIRED_RUNTIME_MATCHERS) {
|
|
529
|
+
re.lastIndex = 0;
|
|
530
|
+
let match;
|
|
531
|
+
while ((match = re.exec(line)) !== null) {
|
|
532
|
+
const verdict = classifyMatch(line, match.index, match[0], pins);
|
|
533
|
+
if (verdict) continue;
|
|
534
|
+
violations.push({
|
|
535
|
+
file: relPath,
|
|
536
|
+
line: i + 1,
|
|
537
|
+
col: match.index + 1,
|
|
538
|
+
text: line.trim(),
|
|
539
|
+
runtime,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Anti-vacuity: the scan must have READ a plausible number of files.
|
|
547
|
+
if (filesRead < MIN_EXPECTED_FILES) {
|
|
548
|
+
throw new ExitError(
|
|
549
|
+
1,
|
|
550
|
+
'ERROR lint-retired-runtime-name: only ' + filesRead + ' file(s) read '
|
|
551
|
+
+ '(expected at least ' + MIN_EXPECTED_FILES + '); the walk is empty or broken — refusing '
|
|
552
|
+
+ 'to report a false clean.',
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// A pin that no longer matches anything is a stale allowlist entry: the
|
|
557
|
+
// content it approved was edited or removed, and leaving it in place would
|
|
558
|
+
// pre-approve a future occurrence nobody reviewed. Only checked for files
|
|
559
|
+
// actually present in this tree, so fixture roots are unaffected.
|
|
560
|
+
const stalePins = [];
|
|
561
|
+
for (const [relPath, pins] of ALLOWLIST_OCCURRENCES) {
|
|
562
|
+
if (!scannedRelPaths.has(relPath)) continue;
|
|
563
|
+
for (const pin of pins) {
|
|
564
|
+
if (!usedPins.has(relPath + ' ' + pin.snippet)) {
|
|
565
|
+
stalePins.push({ file: relPath, snippet: pin.snippet });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (violations.length === 0 && stalePins.length === 0) {
|
|
571
|
+
process.stdout.write(
|
|
572
|
+
'ok lint-retired-runtime-name: ' + filesRead + ' files scanned, 0 violations\n',
|
|
573
|
+
);
|
|
574
|
+
return 0;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (violations.length > 0) {
|
|
578
|
+
process.stderr.write('\nERROR lint-retired-runtime-name: ' + violations.length + ' violation(s) found\n\n');
|
|
579
|
+
for (const v of violations) {
|
|
580
|
+
process.stderr.write(' ' + v.file + ':' + v.line + ': ' + v.text + '\n');
|
|
581
|
+
}
|
|
582
|
+
process.stderr.write('\n');
|
|
583
|
+
for (const runtime of RETIRED_RUNTIMES) {
|
|
584
|
+
process.stderr.write(
|
|
585
|
+
'Fix: `' + runtime.name + '` was retired by ' + runtime.retiredBy + ' (sunset ' + runtime.sunset + ') '
|
|
586
|
+
+ 'in favor of `' + runtime.successor + '`. Either correct the reference to name the live runtime, '
|
|
587
|
+
+ 'or, if the use is a legitimate historical/provider reference, add the `' + ALLOW_MARKER + '` '
|
|
588
|
+
+ 'marker on the line.\n',
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
process.stderr.write('\n');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (stalePins.length > 0) {
|
|
595
|
+
process.stderr.write(
|
|
596
|
+
'ERROR lint-retired-runtime-name: ' + stalePins.length + ' stale allowlist entr'
|
|
597
|
+
+ (stalePins.length === 1 ? 'y' : 'ies') + ' in ALLOWLIST_OCCURRENCES\n\n',
|
|
598
|
+
);
|
|
599
|
+
for (const s of stalePins) {
|
|
600
|
+
process.stderr.write(' ' + s.file + ': no line matches pinned snippet ' + JSON.stringify(s.snippet) + '\n');
|
|
601
|
+
}
|
|
602
|
+
process.stderr.write(
|
|
603
|
+
'\nFix: the approved line was edited or removed. Delete the pin, or update its '
|
|
604
|
+
+ 'snippet to the new wording — an unmatched pin pre-approves a future occurrence '
|
|
605
|
+
+ 'nobody reviewed.\n\n',
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return 1;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Only run the lint when invoked directly. `require()`ing this module (the
|
|
613
|
+
// parity test in tests/runtime-name-policy.test.cjs does) must not
|
|
614
|
+
// execute a full repository walk as a side effect.
|
|
615
|
+
if (require.main === module) {
|
|
616
|
+
runMain(main);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
module.exports = { RETIRED_RUNTIMES };
|
|
@@ -29,6 +29,8 @@ const { runMain } = require('./lib/cli-exit.cjs');
|
|
|
29
29
|
|
|
30
30
|
const PROFILES_MODULE = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'install-profiles.cjs');
|
|
31
31
|
const { PROFILES, loadSkillsManifest, resolveProfile } = require(PROFILES_MODULE);
|
|
32
|
+
// The one frontmatter fence owner, from the same build:lib output as install-profiles.
|
|
33
|
+
const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
|
|
32
34
|
|
|
33
35
|
// ---------------------------------------------------------------------------
|
|
34
36
|
// Argument parsing
|
|
@@ -58,9 +60,11 @@ function extractBodyReferences(body) {
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
function extractBody(content) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
// The block is the one the one fence owner finds (found while implementing #5105);
|
|
64
|
+
// the body starts past the closing fence line and its line ending.
|
|
65
|
+
const fence = locateFrontmatterFence(content);
|
|
66
|
+
if (!fence || !fence.closed) return content;
|
|
67
|
+
return content.slice(fence.closingFenceEnd).replace(/^\r?\n/, '');
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
// ---------------------------------------------------------------------------
|