@opengsd/gsd-core 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +12 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +22 -14
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +5 -3
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +23 -2
- package/agents/gsd-plan-checker.md +50 -53
- package/agents/gsd-planner.md +50 -50
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +13 -4
- package/bin/install.js +1448 -1103
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +9 -5
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1035 -138
- package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
- package/gsd-core/bin/lib/api-coverage.cjs +33 -14
- package/gsd-core/bin/lib/artifacts.cjs +5 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +1026 -268
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +845 -130
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +700 -40
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +216 -42
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +1172 -108
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
- package/gsd-core/bin/lib/config-loader.cjs +187 -23
- package/gsd-core/bin/lib/config.cjs +102 -3
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +208 -33
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +899 -229
- package/gsd-core/bin/lib/gap-checker.cjs +95 -10
- package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +439 -168
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +811 -259
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
- package/gsd-core/bin/lib/install-profiles.cjs +212 -61
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +170 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +183 -22
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +842 -73
- package/gsd-core/bin/lib/model-catalog.cjs +232 -16
- package/gsd-core/bin/lib/model-resolver.cjs +193 -68
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
- package/gsd-core/bin/lib/phase-id.cjs +514 -40
- package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
- package/gsd-core/bin/lib/phase-locator.cjs +262 -34
- package/gsd-core/bin/lib/phase.cjs +1038 -214
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +98 -3
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
- package/gsd-core/bin/lib/probe-core.cjs +5 -2
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
- package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +442 -96
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
- package/gsd-core/bin/lib/smart-entry.cjs +133 -23
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +52 -19
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +338 -8
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +846 -176
- package/gsd-core/bin/lib/state.cjs +2589 -369
- package/gsd-core/bin/lib/surface.cjs +33 -11
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
- package/gsd-core/bin/lib/uat.cjs +1761 -167
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
- package/gsd-core/bin/lib/unusable-input.cjs +37 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +75 -0
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +272 -9
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +453 -918
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +44 -26
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +39 -21
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +4 -6
- package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +17 -11
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +5 -4
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +61 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +44 -13
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +5 -5
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -17
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +5 -4
- package/gsd-core/templates/state.md +11 -4
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +13 -20
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +11 -27
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +64 -5
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
- package/gsd-core/workflows/code-review-fix.md +38 -11
- package/gsd-core/workflows/code-review.md +159 -52
- package/gsd-core/workflows/complete-milestone.md +151 -23
- package/gsd-core/workflows/debug.md +12 -8
- package/gsd-core/workflows/diagnose-issues.md +47 -15
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +27 -2
- package/gsd-core/workflows/eval-review.md +7 -14
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +72 -100
- package/gsd-core/workflows/execute-plan.md +52 -15
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +76 -10
- package/gsd-core/workflows/import.md +18 -15
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +49 -16
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +8 -5
- package/gsd-core/workflows/new-milestone.md +22 -29
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +26 -40
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +14 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +162 -59
- package/gsd-core/workflows/plan-review-convergence.md +96 -11
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +61 -18
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
- package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
- package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +55 -44
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +219 -20
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +69 -7
- package/gsd-core/workflows/ship.md +116 -50
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +53 -13
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +64 -9
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +49 -14
- package/gsd-core/workflows/ui-phase.md +15 -21
- package/gsd-core/workflows/ui-review.md +8 -12
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +7 -11
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +66 -25
- package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
- package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +37 -27
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +55 -32
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +231 -24
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +162 -46
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +158 -30
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +2 -3
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +83 -3
- package/hooks/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +37 -27
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +55 -32
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +231 -24
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +162 -46
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +28 -11
- package/pi/gsd.cjs +19 -5
- package/scripts/base64-scan.sh +74 -12
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/build-hooks.js +5 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +64 -14
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +11 -21
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +138 -25
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +308 -0
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +492 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +471 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +488 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +950 -0
- package/scripts/lint-test-file-count.allowlist.json +137 -8
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +830 -0
- package/scripts/lint-vendored-deps.cjs +297 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +100 -14
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +24 -7
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +9 -5
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -107
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Anti-divergence drift guard for the completion-RATIO seam
|
|
6
|
+
* (epic #3180, ADR-3180 "Planning Semantic Model Single Owner").
|
|
7
|
+
*
|
|
8
|
+
* `src/phase-lifecycle.cts`'s `clampPercent(completed, total)` /
|
|
9
|
+
* `clampPercentFromFraction(fraction)` are the SINGLE canonical owner of
|
|
10
|
+
* "turn a completed/total pair into an integer completion percentage,
|
|
11
|
+
* clamped to 100". Until just before this guard was added, the identical
|
|
12
|
+
* expression `total > 0 ? Math.min(100, Math.round((completed / total) * 100)) : 0`
|
|
13
|
+
* was hand-inlined at six call sites across five modules while the owner sat
|
|
14
|
+
* exported and unused by them — the exact ADR-3180 divergence class, in a
|
|
15
|
+
* derivation the epic had not previously named. Those six sites have been
|
|
16
|
+
* migrated onto the owner; this guard is what stops a seventh copy.
|
|
17
|
+
*
|
|
18
|
+
* Per ADR-3180 Decision 4(a) this guard discovers call sites by SCANNING THE
|
|
19
|
+
* WHOLE `src/` TREE, not by consulting an allowlist of known files — an
|
|
20
|
+
* allowlist only measures re-derivations in files someone remembered to
|
|
21
|
+
* list, and a new call site added anywhere else would sail through silently.
|
|
22
|
+
*
|
|
23
|
+
* DETECTION. A line is a re-derivation when ALL THREE hold, on that ONE
|
|
24
|
+
* source line:
|
|
25
|
+
* (a) it calls one of the `Math.round(`/`Math.floor(`/`Math.trunc(`/
|
|
26
|
+
* `Math.ceil(` rounding family — MATH_ROUND_FAMILY_RE;
|
|
27
|
+
* (b) it SCALES by 100 — a `*` followed by optional whitespace then `100`
|
|
28
|
+
* at a word boundary — SCALE_100_RE;
|
|
29
|
+
* (c) it contains a DIVISION — an identifier/closing-bracket, optional
|
|
30
|
+
* whitespace, `/`, optional whitespace, an identifier/opening-paren —
|
|
31
|
+
* DIVISION_RE — AND that division's index in the line is EARLIER than
|
|
32
|
+
* the index of the `* 100` scale from (b).
|
|
33
|
+
*
|
|
34
|
+
* Clause (c)'s ORDERING requirement is the whole precision of this guard.
|
|
35
|
+
* `(a / b) * 100` — divide FIRST, scale SECOND — is a percentage: the
|
|
36
|
+
* completed/total-derived shape this guard exists to catch. `Math.round(n *
|
|
37
|
+
* 100) / 100` — scale FIRST, divide SECOND — is a completely unrelated
|
|
38
|
+
* idiom (2-decimal-place rounding of an already-fractional value) that
|
|
39
|
+
* happens to share both a rounding call and a `* 100` token; it appears in
|
|
40
|
+
* this repo at `src/eval.cts` and `src/commands.cts` and MUST stay
|
|
41
|
+
* unflagged. Comparing leftmost-match indices (rather than merely testing
|
|
42
|
+
* "does a division exist anywhere on the line") is what tells the two
|
|
43
|
+
* idioms apart: this guard finds the EARLIEST division and the EARLIEST
|
|
44
|
+
* `* 100` scale on the line and requires divIdx < scaleIdx, so a line with a
|
|
45
|
+
* scale-then-divide shape (divIdx > scaleIdx, or no division at all) never
|
|
46
|
+
* matches, regardless of what else is on the line.
|
|
47
|
+
*
|
|
48
|
+
* `Math.floor(Math.random() * 100)` carries (a) and (b) but no division
|
|
49
|
+
* anywhere on the line (DIVISION_RE finds nothing, divIdx === -1) and is
|
|
50
|
+
* correctly excluded by clause (c) alone.
|
|
51
|
+
*
|
|
52
|
+
* `src/context-utilization.cts`'s `Math.min(Math.round(ratio * 100), 100)`
|
|
53
|
+
* is OUT OF SCOPE BY DOMAIN, not by exemption: it scales an
|
|
54
|
+
* ALREADY-COMPUTED fraction (`ratio`, a context-window utilization figure —
|
|
55
|
+
* unrelated to `.planning/` phase/plan completion) and there is no division
|
|
56
|
+
* anywhere on that line either, so clause (c) excludes it the same way as
|
|
57
|
+
* the `Math.random()` case above; it needs no FUNCTION_SCOPED_EXEMPTIONS
|
|
58
|
+
* entry because it was never going to match.
|
|
59
|
+
*
|
|
60
|
+
* Every regex below is small, bounded, and has no nested/overlapping
|
|
61
|
+
* quantifiers — each character class is followed by a fixed literal or a
|
|
62
|
+
* single `\s*` run bounded by the next required literal, so there is
|
|
63
|
+
* nothing for a backtracking engine to explore more than linearly.
|
|
64
|
+
* `npm run lint:ci` runs CodeQL js/redos over this repo; mirrors the
|
|
65
|
+
* ReDoS discipline of `lint-plan-count-drift.cjs` / `lint-milestone-window-drift.cjs`.
|
|
66
|
+
*
|
|
67
|
+
* The tree-walk / root-confinement / sanitizer machinery is SHARED with the
|
|
68
|
+
* sibling drift guards via `scripts/lib/drift-scan.cjs` (ADR-3180 Decision 4)
|
|
69
|
+
* — see that module for the `isInsideRoot` case-sensitivity note and the
|
|
70
|
+
* `walk` symlink-confinement rationale. This guard's detection shape needs
|
|
71
|
+
* no regex-LITERAL extraction (unlike the milestone-window guard), so it
|
|
72
|
+
* does not use `readRegexLiteralAt`; the reported fragment is simply the
|
|
73
|
+
* trimmed source line, bounded to MAX_REGEX_LITERAL_LEN characters.
|
|
74
|
+
*
|
|
75
|
+
* KNOWN, ACCEPTED limits of a per-line textual scan (same tradeoff the
|
|
76
|
+
* sibling drift guards document): a re-derivation whose division and
|
|
77
|
+
* `Math.round`/scale are split across two DIFFERENT lines with no single
|
|
78
|
+
* line carrying all three tokens is not caught by this narrow shape, nor is
|
|
79
|
+
* one routed through a helper that itself performs the division one call
|
|
80
|
+
* away from the rounding. That is left to code review, not this regex.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
const path = require('node:path');
|
|
84
|
+
const driftScan = require('./lib/drift-scan.cjs');
|
|
85
|
+
const { MAX_REGEX_LITERAL_LEN, sanitizeForReport, scanTree } = driftScan;
|
|
86
|
+
|
|
87
|
+
// (a) The `Math.round`/`Math.floor`/`Math.trunc`/`Math.ceil` rounding family,
|
|
88
|
+
// called with an open paren. `\b` before `Math` keeps this from matching
|
|
89
|
+
// inside a longer identifier (e.g. `fooMath.round(` never occurs in this
|
|
90
|
+
// codebase, but the boundary costs nothing and documents intent).
|
|
91
|
+
const MATH_ROUND_FAMILY_RE = /\bMath\.(?:round|floor|trunc|ceil)\(/;
|
|
92
|
+
|
|
93
|
+
// (b) A `* 100` scale — a `*` operator, optional whitespace, then the
|
|
94
|
+
// literal digits `100` at a word boundary (so `*1000` or `*100.5` do not
|
|
95
|
+
// match a bare `100` inside a longer number).
|
|
96
|
+
const SCALE_100_RE = /\*\s*100\b/;
|
|
97
|
+
|
|
98
|
+
// (c) A division: an identifier character/closing-bracket (the end of the
|
|
99
|
+
// numerator expression), optional whitespace, `/`, optional whitespace, an
|
|
100
|
+
// identifier character/opening-paren (the start of the denominator
|
|
101
|
+
// expression). Deliberately does not try to distinguish this from a regex
|
|
102
|
+
// literal or a `//` comment — the detection window is a Math.round-family
|
|
103
|
+
// call on the same line, which neither idiom co-occurs with in practice, and
|
|
104
|
+
// keeping the class small is what keeps the regex non-backtracking.
|
|
105
|
+
const DIVISION_RE = /[A-Za-z0-9_$)\]]\s*\/\s*[A-Za-z0-9_$(]/;
|
|
106
|
+
|
|
107
|
+
// Authored TypeScript source only (the generated bin/lib/*.cjs mirror it).
|
|
108
|
+
const SCAN_DIRS = ['src'];
|
|
109
|
+
const SCAN_EXT = new Set(['.cts', '.ts', '.mts']);
|
|
110
|
+
|
|
111
|
+
// The canonical owner defines the ratio-to-percent grammar. It is NOT
|
|
112
|
+
// exempt as a whole file (ADR-3180 Decision 4(a) forbids bare file
|
|
113
|
+
// allowlists) — it is scanned like every other file in SCAN_DIRS, and only
|
|
114
|
+
// the two named functions below are exempt, each for a documented reason.
|
|
115
|
+
// An unrelated re-derivation added elsewhere in this same file (including a
|
|
116
|
+
// future one) is still caught.
|
|
117
|
+
const OWNER_FILE = path.join('src', 'phase-lifecycle.cts');
|
|
118
|
+
|
|
119
|
+
// Per ADR-3180 Decision 4(a): function-scoped, not a bare file allowlist.
|
|
120
|
+
// - clampPercentFromFraction: `Math.min(100, Math.round(fraction * 100))`
|
|
121
|
+
// IS the canonical fraction-to-percent kernel this guard exists to
|
|
122
|
+
// protect, not a copy of it — every other caller in the tree is
|
|
123
|
+
// expected to CALL this function rather than re-express its body.
|
|
124
|
+
// - clampPercent: the canonical count-shaped entry point; it delegates to
|
|
125
|
+
// `clampPercentFromFraction(completed / total)` rather than computing
|
|
126
|
+
// `Math.round(...)` itself, so it is exempted for the same reason even
|
|
127
|
+
// though its own line does not currently carry a Math.round-family call.
|
|
128
|
+
const FUNCTION_SCOPED_EXEMPTIONS = new Map([[OWNER_FILE, new Set(['clampPercent', 'clampPercentFromFraction'])]]);
|
|
129
|
+
|
|
130
|
+
// Optional `export ` modifier, matching the sibling guards' convention —
|
|
131
|
+
// only a column-0 top-level `function` declaration updates the
|
|
132
|
+
// current-function tracker.
|
|
133
|
+
const TOP_LEVEL_FUNCTION_RE = /^(?:export\s+)?function\s+([A-Za-z0-9_]+)\s*\(/;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Pure: find every unsanctioned completion-ratio re-derivation in `text`.
|
|
137
|
+
* `relPath` is the repo-relative path, used both to report file:line and to
|
|
138
|
+
* apply the narrow, function-scoped owner exemptions above.
|
|
139
|
+
* Returns [{ line, found }].
|
|
140
|
+
*/
|
|
141
|
+
function findCompletionRatioDrift(text, relPath) {
|
|
142
|
+
const out = [];
|
|
143
|
+
const lines = text.split('\n');
|
|
144
|
+
const exemptFunctions = FUNCTION_SCOPED_EXEMPTIONS.get(relPath) || null;
|
|
145
|
+
let currentFunction = null;
|
|
146
|
+
for (let i = 0; i < lines.length; i++) {
|
|
147
|
+
const line = lines[i];
|
|
148
|
+
const fnMatch = TOP_LEVEL_FUNCTION_RE.exec(line);
|
|
149
|
+
if (fnMatch) currentFunction = fnMatch[1];
|
|
150
|
+
|
|
151
|
+
if (!MATH_ROUND_FAMILY_RE.test(line)) continue;
|
|
152
|
+
const scaleIdx = line.search(SCALE_100_RE);
|
|
153
|
+
if (scaleIdx === -1) continue;
|
|
154
|
+
const divIdx = line.search(DIVISION_RE);
|
|
155
|
+
if (divIdx === -1 || divIdx >= scaleIdx) continue;
|
|
156
|
+
|
|
157
|
+
if (exemptFunctions && exemptFunctions.has(currentFunction)) continue;
|
|
158
|
+
|
|
159
|
+
out.push({ line: i + 1, found: line.trim().slice(0, MAX_REGEX_LITERAL_LEN) });
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Scan the authored source tree and return every unsanctioned re-derivation,
|
|
166
|
+
* each annotated with the repo-relative file path.
|
|
167
|
+
*/
|
|
168
|
+
function scanRepo(root) {
|
|
169
|
+
return scanTree({
|
|
170
|
+
root,
|
|
171
|
+
scanDirs: SCAN_DIRS,
|
|
172
|
+
scanExt: SCAN_EXT,
|
|
173
|
+
onFile(rel, text) {
|
|
174
|
+
// `rel` is already the REAL (canonical) path (scanTree resolves
|
|
175
|
+
// symlinks before calling onFile), so this — and
|
|
176
|
+
// FUNCTION_SCOPED_EXEMPTIONS above, also keyed on `rel` — match
|
|
177
|
+
// consistently regardless of which symlink reached the file.
|
|
178
|
+
return findCompletionRatioDrift(text, rel).map((d) => ({ file: rel, ...d }));
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function main() {
|
|
184
|
+
const root = path.join(__dirname, '..');
|
|
185
|
+
const violations = scanRepo(root);
|
|
186
|
+
if (violations.length === 0) {
|
|
187
|
+
process.stdout.write('ok completion-ratio-drift: no unsanctioned completed/total percent re-derivations outside phase-lifecycle.cts\n');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
process.stderr.write('completion-ratio-drift: independent re-derivation(s) of completed/total percent found.\n');
|
|
191
|
+
process.stderr.write('Use src/phase-lifecycle.cjs `clampPercent(completed, total)` (or `clampPercentFromFraction(fraction)`\n');
|
|
192
|
+
process.stderr.write('when you already hold a fraction) instead of re-deriving Math.round((completed / total) * 100):\n');
|
|
193
|
+
for (const d of violations) {
|
|
194
|
+
// `d.file` is exactly as attacker-controlled as `d.found`: a repo can
|
|
195
|
+
// legally track a filename containing control bytes / bidi overrides,
|
|
196
|
+
// and it is a fork-PR-authored value reaching a CI log the same way the
|
|
197
|
+
// matched line text does — sanitize it at the same reporting boundary.
|
|
198
|
+
process.stderr.write(` ${sanitizeForReport(d.file)}:${d.line} ${sanitizeForReport(d.found)}\n`);
|
|
199
|
+
}
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (require.main === module) main();
|
|
204
|
+
|
|
205
|
+
module.exports = {
|
|
206
|
+
findCompletionRatioDrift,
|
|
207
|
+
scanRepo,
|
|
208
|
+
MATH_ROUND_FAMILY_RE,
|
|
209
|
+
SCALE_100_RE,
|
|
210
|
+
DIVISION_RE,
|
|
211
|
+
OWNER_FILE,
|
|
212
|
+
FUNCTION_SCOPED_EXEMPTIONS,
|
|
213
|
+
MAX_REGEX_LITERAL_LEN,
|
|
214
|
+
};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* lint-default-flip-documentation.cjs — DEFECT.DEFAULT-FLIP-DOCUMENTATION
|
|
6
|
+
* (CONTEXT.md).
|
|
7
|
+
*
|
|
8
|
+
* ## Why
|
|
9
|
+
*
|
|
10
|
+
* A PR flips a config default but doesn't call out the migration semantics
|
|
11
|
+
* (when the new default takes effect; existing configs vs new configs; what
|
|
12
|
+
* the opt-back-in looks like — #3309, the v2 default flip from mid-flight to
|
|
13
|
+
* end-of-phase).
|
|
14
|
+
*
|
|
15
|
+
* ## Scope (deliberately narrower than the full DEFECT.detect clause)
|
|
16
|
+
*
|
|
17
|
+
* The DEFECT text names two surfaces: `CONFIG_DEFAULTS` and
|
|
18
|
+
* `buildNewProjectConfig`. This check covers ONLY the single-source-of-truth
|
|
19
|
+
* defaults manifest, `gsd-core/bin/shared/config-defaults.manifest.json`
|
|
20
|
+
* (what `CONFIG_DEFAULTS` in `src/configuration.cts` / `src/config.cts`
|
|
21
|
+
* actually loads at runtime) — because it is pure JSON, a resolved
|
|
22
|
+
* key→value-map diff between base and head is trivially reliable: no line
|
|
23
|
+
* movement, reordering, or refactor can ever produce a false "value changed"
|
|
24
|
+
* verdict, only an actual value change can.
|
|
25
|
+
*
|
|
26
|
+
* `buildNewProjectConfig`'s `hardcoded` object literal in `src/config.cts`
|
|
27
|
+
* is DELIBERATELY OUT OF SCOPE here. It mixes literal values with
|
|
28
|
+
* environment-derived branches (`hasBraveSearch`, etc.) and spreads of
|
|
29
|
+
* `CONFIG_DEFAULTS.*` — there is no reliable way to compute its *resolved*
|
|
30
|
+
* value map from source text alone without executing the compiled module at
|
|
31
|
+
* both refs, and a line/AST-level diff of that literal would inherit exactly
|
|
32
|
+
* the false-positive risk (a harmless refactor that moves or restructures
|
|
33
|
+
* the literal reads as a "flip") this check exists to avoid. Per the audit's
|
|
34
|
+
* own risk callout, a noisy check here is worse than no check — the
|
|
35
|
+
* `buildNewProjectConfig` half of the DEFECT stays prose-only.
|
|
36
|
+
*
|
|
37
|
+
* ## What this checks
|
|
38
|
+
*
|
|
39
|
+
* If any *value* differs between the base and head resolved manifest
|
|
40
|
+
* key→value maps (additions/removals alone don't count as a "flip" — the
|
|
41
|
+
* symptom is specifically about an EXISTING default changing), fail unless
|
|
42
|
+
* the PR body contains a `## Breaking Changes` (or `# Breaking Changes`)
|
|
43
|
+
* heading.
|
|
44
|
+
*
|
|
45
|
+
* Needs a PR event payload (`GITHUB_EVENT_PATH`) to read the PR body — this
|
|
46
|
+
* is a dedicated-workflow check (like `lint-canary-version-leak.cjs`), not a
|
|
47
|
+
* `lint:ci` member, since a local/push run has no PR body to check against.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
const fs = require('node:fs');
|
|
51
|
+
const path = require('node:path');
|
|
52
|
+
const cp = require('node:child_process');
|
|
53
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
54
|
+
|
|
55
|
+
const ROOT = path.join(__dirname, '..');
|
|
56
|
+
const MANIFEST_PATH = path.join('gsd-core', 'bin', 'shared', 'config-defaults.manifest.json');
|
|
57
|
+
const BREAKING_CHANGES_RE = /^#{1,6}\s*Breaking Changes\b/im;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Pure: flatten a nested plain-object JSON value into dot-path
|
|
61
|
+
* `{ "a.b.c": value }` leaves. Arrays and primitives are leaves (compared by
|
|
62
|
+
* JSON.stringify equality, never recursed into) so array reordering reads as
|
|
63
|
+
* one value change, not N.
|
|
64
|
+
* @param {unknown} value
|
|
65
|
+
* @param {string} prefix
|
|
66
|
+
* @param {Record<string, unknown>} out
|
|
67
|
+
* @returns {Record<string, unknown>}
|
|
68
|
+
*/
|
|
69
|
+
function flatten(value, prefix = '', out = {}) {
|
|
70
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
71
|
+
for (const [key, v] of Object.entries(value)) {
|
|
72
|
+
flatten(v, prefix ? `${prefix}.${key}` : key, out);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
out[prefix] = value;
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Pure: given two resolved (already-flattened) key→value maps, return the
|
|
82
|
+
* keys present in BOTH whose value differs. Additions/removals are NOT
|
|
83
|
+
* "flips" — a brand-new default has no prior behavior to contradict.
|
|
84
|
+
* @param {Record<string, unknown>} baseMap
|
|
85
|
+
* @param {Record<string, unknown>} headMap
|
|
86
|
+
* @returns {{ key: string, from: unknown, to: unknown }[]}
|
|
87
|
+
*/
|
|
88
|
+
function findDefaultValueChanges(baseMap, headMap) {
|
|
89
|
+
const changes = [];
|
|
90
|
+
for (const key of Object.keys(baseMap)) {
|
|
91
|
+
if (!Object.prototype.hasOwnProperty.call(headMap, key)) continue;
|
|
92
|
+
if (JSON.stringify(baseMap[key]) !== JSON.stringify(headMap[key])) {
|
|
93
|
+
changes.push({ key, from: baseMap[key], to: headMap[key] });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return changes;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Pure verdict: given the detected default-value changes and the PR body,
|
|
101
|
+
* decide pass/fail.
|
|
102
|
+
* @param {{ key: string, from: unknown, to: unknown }[]} changes
|
|
103
|
+
* @param {string} prBody
|
|
104
|
+
* @returns {{ ok: boolean, changes: object[] }}
|
|
105
|
+
*/
|
|
106
|
+
function evaluateDefaultFlipDoc(changes, prBody) {
|
|
107
|
+
if (changes.length === 0) return { ok: true, changes: [] };
|
|
108
|
+
if (BREAKING_CHANGES_RE.test(prBody || '')) return { ok: true, changes };
|
|
109
|
+
return { ok: false, changes };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Read and JSON.parse the manifest at a given git ref. Returns `{}` when the
|
|
114
|
+
* file doesn't exist at that ref (new file, or ref predates it) — that is
|
|
115
|
+
* not a "flip", it's an addition, and is silently excluded by
|
|
116
|
+
* findDefaultValueChanges's both-sides-present requirement anyway.
|
|
117
|
+
* @param {string} root
|
|
118
|
+
* @param {string} ref
|
|
119
|
+
* @returns {Record<string, unknown>}
|
|
120
|
+
*/
|
|
121
|
+
function readManifestAtRef(root, ref) {
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
raw = cp.execFileSync('git', ['show', `${ref}:${MANIFEST_PATH.split(path.sep).join('/')}`], {
|
|
125
|
+
cwd: root,
|
|
126
|
+
encoding: 'utf8',
|
|
127
|
+
timeout: 15000,
|
|
128
|
+
});
|
|
129
|
+
} catch {
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(raw);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
throw new ExitError(2, `lint-default-flip-documentation: ${ref}:${MANIFEST_PATH} is not valid JSON: ${e.message}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function readPrBody() {
|
|
140
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
141
|
+
if (!eventPath || !fs.existsSync(eventPath)) return null;
|
|
142
|
+
try {
|
|
143
|
+
const event = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
|
|
144
|
+
return typeof event.pull_request?.body === 'string' ? event.pull_request.body : '';
|
|
145
|
+
} catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function main() {
|
|
151
|
+
const prBody = readPrBody();
|
|
152
|
+
if (prBody === null) {
|
|
153
|
+
console.log('lint-default-flip-documentation: no PR event payload (not a pull_request run), skipping');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const baseRef = `origin/${process.env.GITHUB_BASE_REF || 'next'}`; // #2988
|
|
158
|
+
const baseMap = flatten(readManifestAtRef(ROOT, baseRef));
|
|
159
|
+
const headMap = flatten(readManifestAtRef(ROOT, 'HEAD'));
|
|
160
|
+
const changes = findDefaultValueChanges(baseMap, headMap);
|
|
161
|
+
const verdict = evaluateDefaultFlipDoc(changes, prBody);
|
|
162
|
+
|
|
163
|
+
if (!verdict.ok) {
|
|
164
|
+
const detail = verdict.changes
|
|
165
|
+
.map((c) => ` ${c.key}: ${JSON.stringify(c.from)} → ${JSON.stringify(c.to)}`)
|
|
166
|
+
.join('\n');
|
|
167
|
+
throw new ExitError(
|
|
168
|
+
1,
|
|
169
|
+
'lint-default-flip-documentation: this PR changes an existing default value in\n'
|
|
170
|
+
+ 'config-defaults.manifest.json (DEFECT.DEFAULT-FLIP-DOCUMENTATION) but the PR body has no\n'
|
|
171
|
+
+ '`## Breaking Changes` section. Add one covering: (a) when the new default takes effect\n'
|
|
172
|
+
+ '(config-set, fresh project, regenerated config), (b) the opt-back-in command\n'
|
|
173
|
+
+ '(`gsd config-set <key> <old-value>`), (c) effect on in-flight artifacts. Changed default(s):\n'
|
|
174
|
+
+ detail,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
console.log(
|
|
178
|
+
changes.length === 0
|
|
179
|
+
? 'ok lint-default-flip-documentation: no default value changed'
|
|
180
|
+
: `ok lint-default-flip-documentation: ${changes.length} default value change(s), PR body documents Breaking Changes`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
flatten,
|
|
186
|
+
findDefaultValueChanges,
|
|
187
|
+
evaluateDefaultFlipDoc,
|
|
188
|
+
readManifestAtRef,
|
|
189
|
+
MANIFEST_PATH,
|
|
190
|
+
BREAKING_CHANGES_RE,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
if (require.main === module) runMain(main);
|