@opengsd/gsd-core 1.11.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 +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -9
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +15 -55
- package/agents/gsd-planner.md +6 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +609 -134
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/quick.md +8 -4
- package/gsd-core/bin/gsd-tools.cjs +567 -51
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +336 -95
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-validator.cjs +205 -18
- package/gsd-core/bin/lib/check-command-router.cjs +145 -5
- 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 +410 -4
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +543 -44
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +118 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +84 -7
- 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/frontmatter.cjs +840 -305
- package/gsd-core/bin/lib/gap-checker.cjs +27 -3
- package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +120 -41
- package/gsd-core/bin/lib/install-engine.cjs +68 -3
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
- package/gsd-core/bin/lib/install-profiles.cjs +78 -4
- 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 +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +160 -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-table.cjs +52 -4
- package/gsd-core/bin/lib/milestone.cjs +90 -5
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +70 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +405 -84
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
- package/gsd-core/bin/lib/roadmap.cjs +74 -19
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
- package/gsd-core/bin/lib/smart-entry.cjs +12 -22
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +186 -0
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +517 -101
- package/gsd-core/bin/lib/state.cjs +946 -163
- package/gsd-core/bin/lib/surface.cjs +10 -2
- 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/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +1376 -125
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification.cjs +14 -1
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +95 -40
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +3 -2
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +1 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- 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 +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/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-human-verify-mode.md +15 -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 +37 -8
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/runtime-aware-dispatch.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 +2 -2
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +3 -0
- package/gsd-core/templates/state.md +7 -0
- 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 +4 -9
- 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 +10 -26
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +121 -40
- package/gsd-core/workflows/complete-milestone.md +15 -10
- package/gsd-core/workflows/debug.md +5 -3
- package/gsd-core/workflows/diagnose-issues.md +12 -6
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
- 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 +2 -2
- 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 +1 -1
- package/gsd-core/workflows/eval-review.md +4 -9
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -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 +38 -54
- package/gsd-core/workflows/execute-plan.md +17 -12
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +2 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +3 -6
- package/gsd-core/workflows/import.md +14 -11
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +44 -11
- 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 +2 -2
- package/gsd-core/workflows/new-milestone.md +9 -21
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +12 -26
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -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 +121 -42
- package/gsd-core/workflows/plan-review-convergence.md +46 -9
- 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 +27 -12
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +20 -29
- 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 +193 -15
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +2 -2
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +3 -5
- package/gsd-core/workflows/ship.md +12 -6
- 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 +23 -1
- 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 +1 -1
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +5 -5
- package/gsd-core/workflows/ui-phase.md +10 -16
- package/gsd-core/workflows/ui-review.md +6 -10
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +6 -10
- package/gsd-core/workflows/validate-phase.md +2 -2
- 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 +57 -18
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- 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 +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +16 -7
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +17 -8
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +215 -26
- 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 +34 -16
- 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-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- 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 +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +16 -7
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +17 -8
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +215 -26
- 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 +34 -16
- 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-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +12 -7
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +5 -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 +45 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +134 -1
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +21 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +122 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/prompt-injection-scan.sh +75 -14
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -657,6 +657,54 @@ function escapeCell(value) {
|
|
|
657
657
|
* Raw Text Matching").
|
|
658
658
|
*/
|
|
659
659
|
exports.QUICK_TASKS_SECTION_ABSENT = 'no Quick Tasks Completed section';
|
|
660
|
+
/**
|
|
661
|
+
* #3860: heading predicate for STATE.md's Quick Tasks Completed section(s).
|
|
662
|
+
* The heading is a section LABEL, not data — milestone-scoped files
|
|
663
|
+
* legitimately carry suffixed headings (`### Quick Tasks Completed (v1.1+)`
|
|
664
|
+
* beside an archived `(v1.0)`), so the match is prefix-anchored with a word
|
|
665
|
+
* boundary, never exact: `Quick Tasks Completedness` must NOT match. Hoisted
|
|
666
|
+
* beside QUICK_TASKS_SECTION_ABSENT so `appendQuickTaskRow` and
|
|
667
|
+
* `resetQuickTaskRows` cannot drift apart again.
|
|
668
|
+
*/
|
|
669
|
+
const isQuickTasksHeading = (h) => /^quick tasks completed\b/i.test(h.text.trim());
|
|
670
|
+
/**
|
|
671
|
+
* #3860: among ALL heading-matching sections, pick the first whose body is a
|
|
672
|
+
* table with a recognized Quick Tasks schema — a legacy/unparseable table
|
|
673
|
+
* first in document order no longer shadows a usable one further down. When
|
|
674
|
+
* NO section is usable, return the FIRST match so the caller's downstream
|
|
675
|
+
* error describes the real problem (unparseable/legacy table) instead of a
|
|
676
|
+
* false QUICK_TASKS_SECTION_ABSENT.
|
|
677
|
+
*
|
|
678
|
+
* Bounding: `collectSections` ends a candidate's body only at the NEXT
|
|
679
|
+
* matching heading — far too wide for splicing (it would swallow an
|
|
680
|
+
* intervening `## Deferred Items` table into the Quick Tasks body, and
|
|
681
|
+
* `appendQuickTaskRow`'s last-table-line scan would then splice the new row
|
|
682
|
+
* into that WRONG table). Each candidate is therefore re-collected through
|
|
683
|
+
* `collectSection` with an offset-precise predicate, whose default
|
|
684
|
+
* level-bounded stop (next heading of the same or higher level) is exactly
|
|
685
|
+
* the semantics the pre-#3860 single-section lookup had.
|
|
686
|
+
*
|
|
687
|
+
* Convention: "first" is document order — the newest-on-top layout the issue
|
|
688
|
+
* itself demonstrates (`(v1.1+)` above an archived `(v1.0)`). When several
|
|
689
|
+
* suffixed sections all carry recognized schemas, this layer has no signal
|
|
690
|
+
* for which milestone is active, so document order is the pinned tie-break.
|
|
691
|
+
*/
|
|
692
|
+
function selectQuickTasksSection(stateContent) {
|
|
693
|
+
const candidates = (0, markdown_sectionizer_cjs_1.collectSections)(stateContent, isQuickTasksHeading);
|
|
694
|
+
if (candidates.length === 0)
|
|
695
|
+
return null;
|
|
696
|
+
const boundedOf = (cand) => (0, markdown_sectionizer_cjs_1.collectSection)(stateContent, (h) => h.offset === cand.heading.offset);
|
|
697
|
+
const firstBounded = boundedOf(candidates[0]);
|
|
698
|
+
for (const cand of candidates) {
|
|
699
|
+
const bounded = boundedOf(cand);
|
|
700
|
+
if (!bounded)
|
|
701
|
+
continue;
|
|
702
|
+
const parsed = parseMarkdownTable(bounded.body);
|
|
703
|
+
if (parsed.ok && matchTableSchema(parsed.value.columns)?.id === 'QuickTasks')
|
|
704
|
+
return bounded;
|
|
705
|
+
}
|
|
706
|
+
return firstBounded;
|
|
707
|
+
}
|
|
660
708
|
/**
|
|
661
709
|
* Append one row to STATE.md's "Quick Tasks Completed" table.
|
|
662
710
|
*
|
|
@@ -677,7 +725,7 @@ exports.QUICK_TASKS_SECTION_ABSENT = 'no Quick Tasks Completed section';
|
|
|
677
725
|
* rows), preserving any surrounding blank lines/trailing content in the section.
|
|
678
726
|
*/
|
|
679
727
|
function appendQuickTaskRow(stateContent, fields) {
|
|
680
|
-
const section = (
|
|
728
|
+
const section = selectQuickTasksSection(stateContent);
|
|
681
729
|
if (!section) {
|
|
682
730
|
return { ok: false, reason: exports.QUICK_TASKS_SECTION_ABSENT };
|
|
683
731
|
}
|
|
@@ -697,7 +745,7 @@ function appendQuickTaskRow(stateContent, fields) {
|
|
|
697
745
|
const rowNumber = parsed.value.rows.length + 1;
|
|
698
746
|
const cellFor = (col) => {
|
|
699
747
|
switch (col) {
|
|
700
|
-
case '#': return escapeCell(String(rowNumber));
|
|
748
|
+
case '#': return escapeCell(fields.quickId ?? String(rowNumber));
|
|
701
749
|
case 'Description': return escapeCell(fields.description);
|
|
702
750
|
case 'Date': return escapeCell(fields.date);
|
|
703
751
|
case 'Commit': return escapeCell(fields.commit);
|
|
@@ -736,7 +784,7 @@ function appendQuickTaskRow(stateContent, fields) {
|
|
|
736
784
|
* directories out from under the table (see `src/milestone.cts`'s
|
|
737
785
|
* `archiveQuickTaskDirectories` / `cmdMilestoneComplete` wiring).
|
|
738
786
|
*
|
|
739
|
-
* Mirrors `appendQuickTaskRow`'s exact contract (same `
|
|
787
|
+
* Mirrors `appendQuickTaskRow`'s exact contract (same `selectQuickTasksSection` ->
|
|
740
788
|
* `parseMarkdownTable` -> `matchTableSchema` pipeline, same fail-loud posture,
|
|
741
789
|
* same EOL-detect-before-split handling) rather than inventing a second one:
|
|
742
790
|
* - no "Quick Tasks Completed" heading -> `{ok:false, reason:
|
|
@@ -758,7 +806,7 @@ function resetQuickTaskRows(stateContent) {
|
|
|
758
806
|
if (typeof stateContent !== 'string' || stateContent.trim() === '') {
|
|
759
807
|
return { ok: false, reason: 'empty or non-string input' };
|
|
760
808
|
}
|
|
761
|
-
const section = (
|
|
809
|
+
const section = selectQuickTasksSection(stateContent);
|
|
762
810
|
if (!section) {
|
|
763
811
|
return { ok: false, reason: exports.QUICK_TASKS_SECTION_ABSENT };
|
|
764
812
|
}
|
|
@@ -31,8 +31,14 @@ const { resolveQuickTaskSummaryFile } = auditMod;
|
|
|
31
31
|
const ioMod = require("./io.cjs");
|
|
32
32
|
const { output, error } = ioMod;
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
|
+
const cliExitMod = require("./cli-exit.cjs");
|
|
35
|
+
const { ExitError } = cliExitMod;
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
const stateContract = require("./state-contract.cjs");
|
|
38
|
+
const { publishStateContract } = stateContract;
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
40
|
const phaseIdMod = require("./phase-id.cjs");
|
|
35
|
-
const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId } = phaseIdMod;
|
|
41
|
+
const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId, isSentinelPhaseDir } = phaseIdMod;
|
|
36
42
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
37
43
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
44
|
const roadmapParserMod = require("./roadmap-parser.cjs");
|
|
@@ -477,7 +483,7 @@ function applyQuickTasksReset(content) {
|
|
|
477
483
|
}
|
|
478
484
|
function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
479
485
|
if (!version) {
|
|
480
|
-
error('version required for milestone complete (e.g., v1.0)');
|
|
486
|
+
error('version required for milestone complete (e.g., v1.0) — and --confirm to mutate');
|
|
481
487
|
}
|
|
482
488
|
// #2288 security: `version` is a CLI positional that is interpolated into
|
|
483
489
|
// multiple filesystem sinks below — `path.join(archiveDir, `${version}-ROADMAP.md`)`,
|
|
@@ -488,6 +494,23 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
488
494
|
if (!ARCHIVE_VERSION_LABEL_RE.test(version)) {
|
|
489
495
|
error(`milestone complete: version "${version}" is invalid — a milestone version label may contain only letters, digits, '.', '-' and '_', and must not contain path separators or "..".`);
|
|
490
496
|
}
|
|
497
|
+
// #3726: confirmation gate — refuse before ANY read of the tree beyond the
|
|
498
|
+
// arg checks above, so an unconfirmed invocation is a guaranteed no-op on
|
|
499
|
+
// disk. The threat model is the NEVER_VALID_FLAGS one (gsd-tools.cjs): a
|
|
500
|
+
// caller supplies a token it believes is inert and a destructive operation
|
|
501
|
+
// proceeds unchecked — here the caller-side belief was that the `query`
|
|
502
|
+
// meta-prefix implies a read, and `query milestone.complete <v>` archived
|
|
503
|
+
// the milestone with no confirmation. The prefix is an intentional
|
|
504
|
+
// invocation-compatibility mechanism, not a permission boundary, so the
|
|
505
|
+
// gate lives on the destructive command itself and covers every invocation
|
|
506
|
+
// path. --dry-run needs no confirmation (it mutates nothing and is the
|
|
507
|
+
// recommended first step); --force does NOT imply it (see
|
|
508
|
+
// MilestoneCompleteOptions.confirm).
|
|
509
|
+
if (!options.dryRun && !options.confirm) {
|
|
510
|
+
error(`milestone complete is irreversible: it archives ROADMAP.md and REQUIREMENTS.md, MOVES every phase ` +
|
|
511
|
+
`directory for ${version} into .planning/milestones/, and rewrites STATE.md. ` +
|
|
512
|
+
`Nothing has been changed. Re-run with --confirm to proceed, or --dry-run to preview exactly what would move.`);
|
|
513
|
+
}
|
|
491
514
|
const roadmapPath = planningPaths(cwd).roadmap;
|
|
492
515
|
const reqPath = planningPaths(cwd).requirements;
|
|
493
516
|
const statePath = planningPaths(cwd).state;
|
|
@@ -660,6 +683,13 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
660
683
|
}
|
|
661
684
|
}
|
|
662
685
|
catch (e) {
|
|
686
|
+
// ADR-3889: error() now throws ExitError (carries no message) instead
|
|
687
|
+
// of calling process.exit() directly, so it can no longer be detected
|
|
688
|
+
// by sniffing e.message — an ExitError from our own guard above must be
|
|
689
|
+
// re-thrown UNCONDITIONALLY, before any message inspection, or the
|
|
690
|
+
// guard silently stops blocking milestone completion.
|
|
691
|
+
if (e instanceof ExitError)
|
|
692
|
+
throw e;
|
|
663
693
|
// If the error came from our guard, re-throw it; otherwise skip silently.
|
|
664
694
|
const message = e instanceof Error ? e.message : String(e);
|
|
665
695
|
if (message && message.startsWith('Cannot mark milestone complete:'))
|
|
@@ -838,6 +868,12 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
838
868
|
// Create/append MILESTONES.md entry
|
|
839
869
|
const accomplishmentsList = accomplishments.map((a) => `- ${a}`).join('\n');
|
|
840
870
|
const milestoneEntry = `## ${version} ${milestoneName} (Shipped: ${today})\n\n**Phases completed:** ${phaseCount} phases, ${totalPlans} plans, ${totalTasks} tasks\n\n**Key accomplishments:**\n${accomplishmentsList || '- (none recorded)'}\n\n---\n\n`;
|
|
871
|
+
// #3685: mirror requirementsUpdated's diff-tracking contract — the result
|
|
872
|
+
// below used to report `milestones_updated: true` hardcoded, never
|
|
873
|
+
// consulting whether the MILESTONES.md write actually changed anything.
|
|
874
|
+
// Captured before the write branches below so the after-comparison reports
|
|
875
|
+
// a real content diff instead of an assumed one.
|
|
876
|
+
const milestonesBefore = node_fs_1.default.existsSync(milestonesPath) ? node_fs_1.default.readFileSync(milestonesPath, 'utf-8') : null;
|
|
841
877
|
if (node_fs_1.default.existsSync(milestonesPath)) {
|
|
842
878
|
const existing = node_fs_1.default.readFileSync(milestonesPath, 'utf-8');
|
|
843
879
|
if (!existing.trim()) {
|
|
@@ -867,6 +903,10 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
867
903
|
else {
|
|
868
904
|
(0, shell_command_projection_cjs_1.platformWriteSync)(milestonesPath, `# Milestones\n\n${milestoneEntry}`);
|
|
869
905
|
}
|
|
906
|
+
// #3685: real content diff, not the hardcoded `true` this used to report —
|
|
907
|
+
// see the `milestonesBefore` capture above.
|
|
908
|
+
const milestonesAfter = node_fs_1.default.existsSync(milestonesPath) ? node_fs_1.default.readFileSync(milestonesPath, 'utf-8') : null;
|
|
909
|
+
const milestonesUpdated = milestonesAfter !== milestonesBefore;
|
|
870
910
|
// #2142 BLOCKER 2 (review): opt-in quick-task archival. This call MUST sit
|
|
871
911
|
// immediately adjacent to the STATE.md write block directly below it, with
|
|
872
912
|
// NO unguarded IO in between (unlike the ROADMAP/REQUIREMENTS/audit/
|
|
@@ -906,6 +946,13 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
906
946
|
// taken). `resync: true` mirrors `cmdPhaseComplete`'s posture (progress
|
|
907
947
|
// recomputed from disk; only the preserve-when-unchanged deltas apply) —
|
|
908
948
|
// milestone completion is the same kind of lifecycle transition.
|
|
949
|
+
// #3685: mirror requirementsUpdated's diff-tracking contract — this used to
|
|
950
|
+
// report `state_updated: fs.existsSync(statePath)`, true even on a no-op
|
|
951
|
+
// transaction. Declared here beside `stateUpdated`'s sibling flags and
|
|
952
|
+
// defaulted to `false` so the "STATE.md absent" case keeps today's answer
|
|
953
|
+
// (existsSync also returns false there) reached via a real content
|
|
954
|
+
// comparison instead.
|
|
955
|
+
let stateUpdated = false;
|
|
909
956
|
if (node_fs_1.default.existsSync(statePath)) {
|
|
910
957
|
withStateLock(statePath, () => {
|
|
911
958
|
const originalStateContent = (0, shell_command_projection_cjs_1.platformReadSync)(statePath) || '';
|
|
@@ -976,6 +1023,24 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
976
1023
|
divergedFields,
|
|
977
1024
|
});
|
|
978
1025
|
(0, shell_command_projection_cjs_1.platformWriteSync)(statePath, finalContent);
|
|
1026
|
+
// #3685 / #3691: compare NORMALIZED bytes, not the pre-normalize
|
|
1027
|
+
// `finalContent` string, against the pre-normalize `originalStateContent`
|
|
1028
|
+
// read above. `platformWriteSync` runs Markdown normalization (blank-line
|
|
1029
|
+
// insertion around headings/fences/lists) before persisting — the
|
|
1030
|
+
// transition core (`transitionCore`'s `## Current Position` section
|
|
1031
|
+
// reset) regenerates that section fresh on every call, including on a
|
|
1032
|
+
// genuine no-op re-run, and its raw un-normalized output differs from
|
|
1033
|
+
// the already-normalized on-disk original even though the write
|
|
1034
|
+
// converges to byte-identical content. Comparing pre-normalize strings
|
|
1035
|
+
// (mirroring cmdPhaseComplete's shape verbatim) was verified live to
|
|
1036
|
+
// report `true` on three consecutive byte-identical writes.
|
|
1037
|
+
// `contentChangedAfterNormalize` runs BOTH sides through the exact same
|
|
1038
|
+
// normalizer `platformWriteSync` used to persist (no extra disk I/O,
|
|
1039
|
+
// and immune by construction to this ordering artifact) — this used to
|
|
1040
|
+
// re-read the file to get the same answer; #3691 hoisted that seam so
|
|
1041
|
+
// this site, `updateRoadmapAfterPhaseRemoval`, and `cmdPhaseComplete`'s
|
|
1042
|
+
// roadmap/state/requirements flags all agree by construction.
|
|
1043
|
+
stateUpdated = (0, shell_command_projection_cjs_1.contentChangedAfterNormalize)(statePath, originalStateContent, finalContent);
|
|
979
1044
|
for (const field of divergedFields) {
|
|
980
1045
|
preservationWarnings.push({ field, reason: 'preserved-over-disagreeing-derived' });
|
|
981
1046
|
}
|
|
@@ -1053,11 +1118,27 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
1053
1118
|
phases_archive_skip_reason: phasesArchiveSkipReason,
|
|
1054
1119
|
quick: !!quickArchiveResult && quickArchiveResult.archived > 0,
|
|
1055
1120
|
},
|
|
1056
|
-
|
|
1057
|
-
|
|
1121
|
+
// #3685: mirror requirementsUpdated's diff-tracking contract — both flags
|
|
1122
|
+
// now report a real before/after content diff instead of the previous
|
|
1123
|
+
// hardcoded `true` (milestones_updated) / bare fs.existsSync (state_updated).
|
|
1124
|
+
milestones_updated: milestonesUpdated,
|
|
1125
|
+
state_updated: stateUpdated,
|
|
1058
1126
|
preservation_warnings: preservationWarnings,
|
|
1059
1127
|
};
|
|
1060
1128
|
output(result, raw);
|
|
1129
|
+
// #3227 (design doc §40 row 26 / "Not-corruption" rule): a refreshed
|
|
1130
|
+
// state.json `updated_at` must always mean something on disk actually
|
|
1131
|
+
// moved. This site is unconditional because every reachable path either
|
|
1132
|
+
// exits via `error()` (process.exit — refusals like a truncated milestone
|
|
1133
|
+
// window, an unstarted phase, or an invalid version never reach here) or
|
|
1134
|
+
// returns early on `--dry-run` (before any mutation, see the `dry_run:
|
|
1135
|
+
// true` branch above) — the only way execution reaches this line is after
|
|
1136
|
+
// the unconditional MILESTONES.md `platformWriteSync` a few lines above,
|
|
1137
|
+
// which always runs (new file, empty file, or append) once the run is
|
|
1138
|
+
// committed to mutating. Best-effort — cannot throw, cannot change this
|
|
1139
|
+
// command's exit code or output. publishStateContract resolves the
|
|
1140
|
+
// workstream planning root itself via planningPaths.
|
|
1141
|
+
publishStateContract(cwd);
|
|
1061
1142
|
}
|
|
1062
1143
|
function cmdPhasesClear(cwd, raw, args) {
|
|
1063
1144
|
const phasesDir = planningPaths(cwd).phases;
|
|
@@ -1100,7 +1181,11 @@ function cmdPhasesClear(cwd, raw, args) {
|
|
|
1100
1181
|
// divergence meant a `0-*` directory `roadmap analyze` preserves as a
|
|
1101
1182
|
// sentinel was DELETED here. Routed through the canonical predicate so
|
|
1102
1183
|
// every reader of "is this a sentinel phase" agrees by construction.
|
|
1103
|
-
|
|
1184
|
+
// #3639: the DIR-AWARE recognizer — the convention-less id predicate
|
|
1185
|
+
// never saw bracket sentinel dirs (GSD.999-07-icebox), so they were
|
|
1186
|
+
// counted for deletion here while the disk guards (post-#3639) preserve
|
|
1187
|
+
// them; the destructive path must not be the one blind reader left.
|
|
1188
|
+
const dirs = entries.filter((e) => e.isDirectory() && !isSentinelPhaseDir(e.name));
|
|
1104
1189
|
if (dirs.length > 0 && !confirm) {
|
|
1105
1190
|
error(`phases clear would delete ${dirs.length} phase director${dirs.length === 1 ? 'y' : 'ies'}. ` +
|
|
1106
1191
|
`Pass --confirm to proceed.`);
|
|
@@ -10,11 +10,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.RUNTIMES_WITH_FAST_MODE = exports.EFFORT_ARGV = exports.EFFORT_RENDERING = exports.CLAUDE_AGENT_ALIASES = exports.KNOWN_PROVIDERS = exports.PROVIDER_PRESETS = exports.RUNTIMES_WITH_REASONING_EFFORT = exports.KNOWN_RUNTIMES = exports.RUNTIME_PROFILE_MAP = exports.MODEL_ALIAS_MAP = exports.AGENT_DEFAULT_TIERS = exports.AGENT_TO_PHASE_TYPE = exports.MODEL_PROFILES = exports.ADAPTIVE_TIER_VALUES = exports.VALID_TIERS = exports.VALID_AGENT_TIERS = exports.VALID_PHASE_TYPES = exports.VALID_PROFILES = exports.catalog = void 0;
|
|
13
|
+
exports.RUNTIMES_WITH_FAST_MODE = exports.EFFORT_ARGV = exports.EFFORT_RENDERING = exports.CLAUDE_AGENT_ALIASES = exports.KNOWN_PROVIDERS = exports.CODEX_MODEL_EFFORT = exports.PROVIDER_PRESETS = exports.RUNTIMES_WITH_REASONING_EFFORT = exports.KNOWN_RUNTIMES = exports.RUNTIME_PROFILE_MAP = exports.MODEL_ALIAS_MAP = exports.AGENT_DEFAULT_TIERS = exports.AGENT_TO_PHASE_TYPE = exports.MODEL_PROFILES = exports.ADAPTIVE_TIER_VALUES = exports.VALID_TIERS = exports.VALID_AGENT_TIERS = exports.VALID_PHASE_TYPES = exports.VALID_PROFILES = exports.catalog = void 0;
|
|
14
14
|
exports.isAnthropicFlavoredModel = isAnthropicFlavoredModel;
|
|
15
15
|
exports.nextTier = nextTier;
|
|
16
16
|
exports.formatAgentToModelMapAsTable = formatAgentToModelMapAsTable;
|
|
17
17
|
exports.getAgentToModelMapForProfile = getAgentToModelMapForProfile;
|
|
18
|
+
exports.clampEffortForHost = clampEffortForHost;
|
|
18
19
|
exports.renderEffortArgv = renderEffortArgv;
|
|
19
20
|
exports.renderEffortForRuntime = renderEffortForRuntime;
|
|
20
21
|
exports.mergeEffortTierDefaults = mergeEffortTierDefaults;
|
|
@@ -99,6 +100,38 @@ exports.RUNTIMES_WITH_REASONING_EFFORT = new Set(Object.entries(_catalog.runtime
|
|
|
99
100
|
.filter(([, tiers]) => Object.values(tiers).some((entry) => entry && entry.reasoning_effort))
|
|
100
101
|
.map(([runtime]) => runtime));
|
|
101
102
|
exports.PROVIDER_PRESETS = _catalog.providerPresets ?? {};
|
|
103
|
+
// ─── #3007 — Codex per-model effort capability ───────────────────────────────
|
|
104
|
+
//
|
|
105
|
+
// Codex's own `models.json` publishes `supported_reasoning_levels` per model and
|
|
106
|
+
// rejects an unsupported level at request time, so GSD must be conservative
|
|
107
|
+
// about what it sends: read the ceiling as DATA from the catalog (never
|
|
108
|
+
// branch on model id in code) and fall back to the family baseline for any
|
|
109
|
+
// model the catalog doesn't know about.
|
|
110
|
+
// (b) A malformed catalog entry (e.g. a non-array value like `"gpt-x": 5`) must
|
|
111
|
+
// degrade to "ignore that entry", never throw — model-catalog.cjs is required
|
|
112
|
+
// across the whole CLI, so one bad JSON value must not kill every command.
|
|
113
|
+
// `new Set(5)` would throw at module load; filter to array values first.
|
|
114
|
+
exports.CODEX_MODEL_EFFORT = Object.fromEntries(Object.entries(_catalog.codexModelEffort ?? {})
|
|
115
|
+
.filter(([, levels]) => Array.isArray(levels))
|
|
116
|
+
.map(([model, levels]) => [model, new Set(levels)]));
|
|
117
|
+
// (a) `??` only catches null/undefined. A malformed `"_baseline": null` still
|
|
118
|
+
// produces `new Set(null)` above — an empty Set, which is truthy — so a bare
|
|
119
|
+
// `??` fallback would never fire and every level would silently lose its
|
|
120
|
+
// advertised set (every effort would render as `value: null`). Guard on
|
|
121
|
+
// `.size > 0` so an empty/missing/malformed baseline always falls back to the
|
|
122
|
+
// hardcoded floor instead of failing open.
|
|
123
|
+
const CODEX_EFFORT_BASELINE = exports.CODEX_MODEL_EFFORT['_baseline'] && exports.CODEX_MODEL_EFFORT['_baseline'].size > 0
|
|
124
|
+
? exports.CODEX_MODEL_EFFORT['_baseline']
|
|
125
|
+
: new Set(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
126
|
+
function advertisedCodexEffort(model) {
|
|
127
|
+
if (typeof model !== 'string' || model.length === 0)
|
|
128
|
+
return CODEX_EFFORT_BASELINE;
|
|
129
|
+
return Object.prototype.hasOwnProperty.call(exports.CODEX_MODEL_EFFORT, model) ? exports.CODEX_MODEL_EFFORT[model] : CODEX_EFFORT_BASELINE;
|
|
130
|
+
}
|
|
131
|
+
// The full universal effort ladder, low-to-high. Used only to find "the
|
|
132
|
+
// nearest advertised level below" when a requested level isn't supported —
|
|
133
|
+
// never to invent behaviour for a level that isn't on it at all.
|
|
134
|
+
const EFFORT_LADDER = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra'];
|
|
102
135
|
// KNOWN_PROVIDERS excludes 'generic' — it is a sentinel (all null entries) that
|
|
103
136
|
// forces users to supply model IDs via model_profile_overrides. It is not a
|
|
104
137
|
// real catalog-backed provider (#49).
|
|
@@ -124,7 +157,10 @@ exports.KNOWN_PROVIDERS = new Set(Object.entries(exports.PROVIDER_PRESETS)
|
|
|
124
157
|
// rejects all of these.
|
|
125
158
|
exports.CLAUDE_AGENT_ALIASES = new Set(['opus', 'sonnet', 'haiku', 'fable']);
|
|
126
159
|
function isAnthropicFlavoredModel(model) {
|
|
127
|
-
|
|
160
|
+
if (typeof model !== 'string')
|
|
161
|
+
return false;
|
|
162
|
+
const lower = model.toLowerCase();
|
|
163
|
+
return exports.CLAUDE_AGENT_ALIASES.has(lower) || lower.includes('claude');
|
|
128
164
|
}
|
|
129
165
|
function nextTier(currentTier) {
|
|
130
166
|
const order = ['light', 'standard', 'heavy'];
|
|
@@ -165,12 +201,21 @@ exports.EFFORT_RENDERING = {
|
|
|
165
201
|
},
|
|
166
202
|
},
|
|
167
203
|
codex: {
|
|
204
|
+
// #3007: 'max' and 'minimal' are stale here — Codex's per-model table
|
|
205
|
+
// (CODEX_MODEL_EFFORT above) is now the source of truth for what a given
|
|
206
|
+
// model actually advertises, and every model in the family baseline DOES
|
|
207
|
+
// advertise 'max' (no model advertises 'minimal'). This runtime-level
|
|
208
|
+
// spec is kept in sync with the family baseline so the two tables can
|
|
209
|
+
// never disagree; renderEffortForRuntime layers the per-model ceiling
|
|
210
|
+
// (and the 'ultra' policy rejection) on top of it.
|
|
211
|
+
// KEEP IN SYNC with EFFORT_ARGV.codex below — same family baseline, two
|
|
212
|
+
// channels (install-time vs invocation-time); they must never diverge.
|
|
168
213
|
param: 'model_reasoning_effort',
|
|
169
214
|
channel: 'api',
|
|
170
|
-
supported: new Set(['
|
|
215
|
+
supported: new Set(['low', 'medium', 'high', 'xhigh', 'max']),
|
|
171
216
|
clamp(level) {
|
|
172
|
-
if (level === '
|
|
173
|
-
return '
|
|
217
|
+
if (level === 'minimal')
|
|
218
|
+
return 'low';
|
|
174
219
|
return level;
|
|
175
220
|
},
|
|
176
221
|
},
|
|
@@ -191,12 +236,45 @@ exports.EFFORT_ARGV = {
|
|
|
191
236
|
},
|
|
192
237
|
// First-party Codex docs: `model_reasoning_effort` is a config-only key with no
|
|
193
238
|
// dedicated flag, so the generic `-c key=value` override is the only argv route.
|
|
239
|
+
// #3007: KEEP IN SYNC with EFFORT_RENDERING.codex above — this table must match
|
|
240
|
+
// the family baseline exactly (no 'minimal', 'max' passes through unclamped),
|
|
241
|
+
// otherwise the argv channel and the install-time channel disagree about the
|
|
242
|
+
// same runtime's capability.
|
|
194
243
|
codex: {
|
|
195
244
|
render: (level) => ['-c', `model_reasoning_effort=${level}`],
|
|
196
|
-
supported: new Set(['
|
|
197
|
-
clamp: (level) => (level === '
|
|
245
|
+
supported: new Set(['low', 'medium', 'high', 'xhigh', 'max']),
|
|
246
|
+
clamp: (level) => (level === 'minimal' ? 'low' : level),
|
|
198
247
|
},
|
|
199
248
|
};
|
|
249
|
+
/**
|
|
250
|
+
* Clamp a universal effort level to what a host actually accepts, or null.
|
|
251
|
+
*
|
|
252
|
+
* #3706 — extracted from `renderEffortArgv` so the two effort CHANNELS can share
|
|
253
|
+
* one capability table without one pretending to be the other. `EFFORT_ARGV[host]`
|
|
254
|
+
* describes what levels the host understands (`supported` + `clamp`); whether that
|
|
255
|
+
* reaches the host as a CLI flag or as a baked frontmatter key is the caller's
|
|
256
|
+
* business. The install-time OpenCode `variant:` writer needs the former without
|
|
257
|
+
* the latter, and hardcoding `'argv'` at that call site to borrow this logic read
|
|
258
|
+
* as if the frontmatter key were gated on the invocation-time axis. It is not —
|
|
259
|
+
* claude declares `effortSurface: "argv"` and independently bakes an `effort:` key.
|
|
260
|
+
*
|
|
261
|
+
* Returns null for a level the host does not accept, which every caller treats as
|
|
262
|
+
* "emit nothing". `inherit` lands here too: per #3533 (10d) it is not a wire level
|
|
263
|
+
* on any runtime, so it is in no `supported` set and must never be written out.
|
|
264
|
+
*/
|
|
265
|
+
function clampEffortForHost(host, universalEffort) {
|
|
266
|
+
// Own-property lookup only: a plain `EFFORT_ARGV[host]` resolves `__proto__`
|
|
267
|
+
// and friends to inherited members that carry no clamp/render.
|
|
268
|
+
if (typeof host !== 'string' || !Object.prototype.hasOwnProperty.call(exports.EFFORT_ARGV, host))
|
|
269
|
+
return null;
|
|
270
|
+
const spec = exports.EFFORT_ARGV[host];
|
|
271
|
+
if (!spec || typeof spec.clamp !== 'function')
|
|
272
|
+
return null;
|
|
273
|
+
if (typeof universalEffort !== 'string' || universalEffort.length === 0)
|
|
274
|
+
return null;
|
|
275
|
+
const clamped = spec.clamp(universalEffort);
|
|
276
|
+
return spec.supported.has(clamped) ? clamped : null;
|
|
277
|
+
}
|
|
200
278
|
/**
|
|
201
279
|
* Render the invocation-time effort argument for a host.
|
|
202
280
|
*
|
|
@@ -212,37 +290,117 @@ function renderEffortArgv(host, universalEffort, effortSurface) {
|
|
|
212
290
|
// (and `constructor`/`toString`) to inherited members, which are truthy but
|
|
213
291
|
// carry no `clamp`/`render` — a hostile host id would throw instead of
|
|
214
292
|
// degrading. The host id reaches here from a descriptor, i.e. untrusted JSON.
|
|
215
|
-
|
|
293
|
+
// #3706: the clamp/supported half lives in clampEffortForHost so the
|
|
294
|
+
// install-time channel can reuse it; this function keeps the axis gate and
|
|
295
|
+
// the argv rendering. One table, one clamp, two channels.
|
|
296
|
+
const clamped = clampEffortForHost(host, universalEffort);
|
|
297
|
+
if (clamped === null)
|
|
216
298
|
return empty;
|
|
217
299
|
const spec = exports.EFFORT_ARGV[host];
|
|
218
|
-
if (
|
|
219
|
-
return empty;
|
|
220
|
-
if (typeof universalEffort !== 'string' || universalEffort.length === 0)
|
|
221
|
-
return empty;
|
|
222
|
-
const clamped = spec.clamp(universalEffort);
|
|
223
|
-
if (!spec.supported.has(clamped))
|
|
300
|
+
if (typeof spec.render !== 'function')
|
|
224
301
|
return empty;
|
|
225
302
|
return { argv: spec.render(clamped), value: clamped, host };
|
|
226
303
|
}
|
|
227
304
|
/**
|
|
228
305
|
* Render a universal effort string for a specific runtime.
|
|
306
|
+
*
|
|
307
|
+
* `model` (#3007) is consulted ONLY for codex: Codex's per-model
|
|
308
|
+
* `supported_reasoning_levels` means the same universal level can be a clean
|
|
309
|
+
* pass-through on one model and a clamp (or, for 'ultra', an outright
|
|
310
|
+
* rejection) on another. Every other runtime ignores the third argument
|
|
311
|
+
* entirely — passing a model id to claude changes nothing.
|
|
229
312
|
*/
|
|
230
|
-
function renderEffortForRuntime(runtime, universalEffort) {
|
|
313
|
+
function renderEffortForRuntime(runtime, universalEffort, model) {
|
|
231
314
|
// #3533 (10d): 'inherit' is not a wire level on ANY runtime — it means
|
|
232
315
|
// "omit the key / pass no argument and follow the session/host default".
|
|
233
316
|
// Renderers must never emit it as a literal; null param/channel tells
|
|
234
|
-
// resolve-execution consumers there is no propagation.
|
|
317
|
+
// resolve-execution consumers there is no propagation. Never measured
|
|
318
|
+
// against any supported set.
|
|
235
319
|
if (universalEffort === 'inherit') {
|
|
236
|
-
return { value: 'inherit', param: null, channel: null };
|
|
320
|
+
return { value: 'inherit', param: null, channel: null, requested: 'inherit', clamped: false, reason: null };
|
|
237
321
|
}
|
|
238
322
|
const spec = exports.EFFORT_RENDERING[runtime];
|
|
239
323
|
if (!spec) {
|
|
240
|
-
return { value: universalEffort, param: null, channel: null };
|
|
324
|
+
return { value: universalEffort, param: null, channel: null, requested: universalEffort, clamped: false, reason: null };
|
|
325
|
+
}
|
|
326
|
+
if (runtime === 'codex') {
|
|
327
|
+
// #2167 — 'ultra' turns on Codex's automatic task delegation, which would
|
|
328
|
+
// let Codex spawn agents underneath GSD's own orchestration. GSD rejects
|
|
329
|
+
// it unconditionally as a POLICY call, never as a capability clamp — this
|
|
330
|
+
// holds even for a model (e.g. gpt-5.6-sol) that DOES advertise 'ultra',
|
|
331
|
+
// so it is never softened down to 'max'.
|
|
332
|
+
if (universalEffort === 'ultra') {
|
|
333
|
+
return {
|
|
334
|
+
value: null,
|
|
335
|
+
param: null,
|
|
336
|
+
channel: null,
|
|
337
|
+
requested: 'ultra',
|
|
338
|
+
clamped: false,
|
|
339
|
+
reason: "'ultra' turns on Codex's automatic task delegation, which would let Codex spawn agents underneath GSD's own orchestration (#2167); GSD rejects it regardless of what the model advertises.",
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
const allowed = advertisedCodexEffort(model);
|
|
343
|
+
if (allowed.has(universalEffort)) {
|
|
344
|
+
return { value: universalEffort, param: spec.param, channel: spec.channel, requested: universalEffort, clamped: false, reason: null };
|
|
345
|
+
}
|
|
346
|
+
const idx = EFFORT_LADDER.indexOf(universalEffort);
|
|
347
|
+
if (idx === -1) {
|
|
348
|
+
// Not on the ladder at all (e.g. 'MAX') — preserve prior behaviour:
|
|
349
|
+
// fall through to the runtime-level clamp rather than inventing new
|
|
350
|
+
// handling for input the ladder doesn't recognize.
|
|
351
|
+
return { value: spec.clamp(universalEffort), param: spec.param, channel: spec.channel, requested: universalEffort, clamped: false, reason: null };
|
|
352
|
+
}
|
|
353
|
+
// Every model's advertised set is a contiguous run up to 'max' (or 'ultra'
|
|
354
|
+
// for sol, already handled above), so the only unsupported level in
|
|
355
|
+
// practice is 'minimal' — below every model's floor. Walk UP the ladder
|
|
356
|
+
// to the nearest level the model actually advertises (its floor): there
|
|
357
|
+
// is nothing below 'minimal' to fall back to.
|
|
358
|
+
// Walking UP is safe today only because every advertised set floors at
|
|
359
|
+
// 'low' — a future model whose floor is, say, 'high' would silently turn
|
|
360
|
+
// a requested 'low' into 'high': MORE reasoning and MORE cost than asked
|
|
361
|
+
// for, with no error. `clamped`/`reason` below is what makes that
|
|
362
|
+
// escalation visible to a caller instead of a silent cost surprise, which
|
|
363
|
+
// is why those fields are not optional decoration.
|
|
364
|
+
for (let i = idx + 1; i < EFFORT_LADDER.length; i++) {
|
|
365
|
+
const candidate = EFFORT_LADDER[i];
|
|
366
|
+
// 'ultra' is never a valid clamp target: it would re-enter, by the back
|
|
367
|
+
// door, the delegation mode the #2167 rejection above exists to keep
|
|
368
|
+
// out. A clamp may never produce a value that a direct request for
|
|
369
|
+
// that same value would have refused.
|
|
370
|
+
if (candidate === 'ultra') {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (allowed.has(candidate)) {
|
|
374
|
+
return {
|
|
375
|
+
value: candidate,
|
|
376
|
+
param: spec.param,
|
|
377
|
+
channel: spec.channel,
|
|
378
|
+
requested: universalEffort,
|
|
379
|
+
clamped: true,
|
|
380
|
+
reason: `requested '${universalEffort}' is not in ${model ? `${model}'s` : "the codex family baseline's"} advertised reasoning levels; clamped up to its floor, '${candidate}'.`,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// No advertised level at or above the request either (shouldn't happen
|
|
385
|
+
// given today's catalog data, but never throw): reject rather than emit
|
|
386
|
+
// an unsupported level.
|
|
387
|
+
return {
|
|
388
|
+
value: null,
|
|
389
|
+
param: null,
|
|
390
|
+
channel: null,
|
|
391
|
+
requested: universalEffort,
|
|
392
|
+
clamped: false,
|
|
393
|
+
reason: `requested '${universalEffort}' is not in ${model ? `${model}'s` : "the codex family baseline's"} advertised reasoning levels, and no advertised level is available either.`,
|
|
394
|
+
};
|
|
241
395
|
}
|
|
396
|
+
const value = spec.clamp(universalEffort);
|
|
242
397
|
return {
|
|
243
|
-
value
|
|
398
|
+
value,
|
|
244
399
|
param: spec.param,
|
|
245
400
|
channel: spec.channel,
|
|
401
|
+
requested: universalEffort,
|
|
402
|
+
clamped: value !== universalEffort,
|
|
403
|
+
reason: value !== universalEffort ? `requested '${universalEffort}' clamped to '${value}' for ${runtime}.` : null,
|
|
246
404
|
};
|
|
247
405
|
}
|
|
248
406
|
/**
|
|
@@ -35,6 +35,7 @@ const model_catalog_cjs_1 = require("./model-catalog.cjs");
|
|
|
35
35
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
36
36
|
const node_path_1 = __importDefault(require("node:path"));
|
|
37
37
|
const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
|
|
38
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
38
39
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
39
40
|
const planningWorkspaceMod = require("./planning-workspace.cjs");
|
|
40
41
|
const { planningDir } = planningWorkspaceMod;
|
|
@@ -58,38 +59,19 @@ const { planningDir } = planningWorkspaceMod;
|
|
|
58
59
|
// registry-parity test guards this set so a future alias-capable runtime fails
|
|
59
60
|
// loudly here instead of silently omitting.
|
|
60
61
|
const RUNTIMES_WITH_NATIVE_ALIASES = new Set(['claude']);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const raw = node_fs_1.default.readFileSync(markerPath, 'utf8').trim();
|
|
68
|
-
_installMarkerCache = raw || null;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// No marker: dev/source tree, or an install predating #2297. Fall through to
|
|
72
|
-
// the 'claude' default (keeps tier aliases — never worse than the bug).
|
|
73
|
-
_installMarkerCache = null;
|
|
74
|
-
}
|
|
75
|
-
return _installMarkerCache;
|
|
76
|
-
}
|
|
77
|
-
// Test seams for the install-marker rung (the dev/source tree has no marker, so
|
|
78
|
-
// the file read always bottoms out at 'claude' — these let tests exercise the
|
|
79
|
-
// third precedence rung and reset the module-level cache between cases).
|
|
80
|
-
function _setInstallRuntimeMarkerForTests(value) {
|
|
81
|
-
_installMarkerCache = value;
|
|
82
|
-
}
|
|
83
|
-
function _resetInstallRuntimeMarkerCacheForTests() {
|
|
84
|
-
_installMarkerCache = undefined;
|
|
85
|
-
}
|
|
62
|
+
// #3897 rung 2: the marker reader + its cache and test seams were promoted to
|
|
63
|
+
// the canonical owner, `runtime-slash.cts` (imported above) — this module now
|
|
64
|
+
// consumes that single implementation instead of holding its own copy. N5:
|
|
65
|
+
// behaviour and the seam contract are unchanged by the move; the re-exports
|
|
66
|
+
// below (`export =` at the bottom of this file) preserve every existing
|
|
67
|
+
// caller's `require('./model-resolver.cjs')` surface byte-for-behaviour.
|
|
86
68
|
// The runtime whose install is actually resolving, canonicalized so an alias or
|
|
87
69
|
// case variant (e.g. "claude-code"/"Claude") cannot defeat the native-alias
|
|
88
70
|
// check below (#2297 review). Precedence mirrors resolveRuntime()
|
|
89
71
|
// (runtime-slash.cts): GSD_RUNTIME env → project config.runtime → per-install
|
|
90
72
|
// .gsd-runtime marker → 'claude'.
|
|
91
73
|
function resolveActiveRuntime(config) {
|
|
92
|
-
return (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME'], config['runtime'], readInstallRuntimeMarker()) || 'claude';
|
|
74
|
+
return (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME'], config['runtime'], (0, runtime_slash_cjs_1.readInstallRuntimeMarker)()) || 'claude';
|
|
93
75
|
}
|
|
94
76
|
// Did the PROJECT's own config (root `.planning/config.json` or the active
|
|
95
77
|
// workstream/project override) explicitly set resolve_model_ids to "omit"?
|
|
@@ -841,8 +823,8 @@ module.exports = {
|
|
|
841
823
|
resolveTierFromConfig,
|
|
842
824
|
_resetModelPolicyWarningCacheForTests,
|
|
843
825
|
_resetModelOverrideWarningCacheForTests,
|
|
844
|
-
_setInstallRuntimeMarkerForTests,
|
|
845
|
-
_resetInstallRuntimeMarkerCacheForTests,
|
|
826
|
+
_setInstallRuntimeMarkerForTests: runtime_slash_cjs_1._setInstallRuntimeMarkerForTests,
|
|
827
|
+
_resetInstallRuntimeMarkerCacheForTests: runtime_slash_cjs_1._resetInstallRuntimeMarkerCacheForTests,
|
|
846
828
|
VALID_GRANULARITIES,
|
|
847
829
|
resolveGranularityInternal,
|
|
848
830
|
assertValidGranularityOverride,
|
|
@@ -117,7 +117,11 @@ function listPlanningDocCandidates(cwd) {
|
|
|
117
117
|
return [...candidates].sort();
|
|
118
118
|
}
|
|
119
119
|
function listCodebaseMapFiles(cwd) {
|
|
120
|
-
|
|
120
|
+
// #3964: project-scoped, agreeing with init's map-codebase surface and
|
|
121
|
+
// verify.cts's codebase drift check — a flat-root read made
|
|
122
|
+
// has_codebase_map/needs_codebase_map answer for the wrong project under
|
|
123
|
+
// GSD_PROJECT.
|
|
124
|
+
const codebaseDir = node_path_1.default.join(planningDir(cwd), 'codebase');
|
|
121
125
|
if (!node_fs_1.default.existsSync(codebaseDir))
|
|
122
126
|
return [];
|
|
123
127
|
return REQUIRED_CODEBASE_MAP_FILES.filter((file) => node_fs_1.default.existsSync(node_path_1.default.join(codebaseDir, file)));
|
|
@@ -218,14 +218,23 @@ function applyCalibration(rawTokens, factor) {
|
|
|
218
218
|
* Extract a two-space-indented scalar block (`estimate:` / `actuals:`) out of a
|
|
219
219
|
* document's leading YAML frontmatter.
|
|
220
220
|
*
|
|
221
|
-
* Hand-rolled
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
221
|
+
* Hand-rolled for its TYPE CONTRACT, not for dependency availability — js-yaml
|
|
222
|
+
* is vendored and available at runtime as of ADR-3473 §8.1 (#3881), which
|
|
223
|
+
* migrated `src/frontmatter.cts`'s `extractFrontmatter` onto it. That parser
|
|
224
|
+
* runs under js-yaml's FAILSAFE_SCHEMA, which resolves every scalar as a
|
|
225
|
+
* string, whereas this function deliberately returns numbers for
|
|
226
|
+
* numeric-looking values (`out[m[1]] = asNumber : rawValue`) so
|
|
227
|
+
* `parseEstimate`/`parseActuals` see the types they validate. A naive swap
|
|
228
|
+
* onto `extractFrontmatter` would turn `tokens: 5000` into `"5000"` and make
|
|
229
|
+
* `isPositiveInt` reject every estimate. Scope is deliberately narrow: the
|
|
230
|
+
* leading `---` block only, so an `estimate:` line inside a fenced code
|
|
231
|
+
* block in the body cannot be mistaken for frontmatter (the
|
|
232
|
+
* DEFECT.FRONTMATTER-SCALAR-BROAD-GREP class). Migrating this function onto
|
|
233
|
+
* the shared parser (with a typed coercion layer over its string-only
|
|
234
|
+
* output) is tracked as follow-on work under ADR-3473 §8.1, not done here —
|
|
235
|
+
* this module is calibration-critical and has a history of subtle numeric
|
|
236
|
+
* defects shipping past a large green suite (#2631 factor², #2632
|
|
237
|
+
* self-defeating loop).
|
|
229
238
|
*/
|
|
230
239
|
function extractFrontmatterBlock(text, key) {
|
|
231
240
|
if (typeof text !== 'string')
|