@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,451 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Health Diagnostic — frozen rule-table types, enums, and evaluator for
|
|
4
|
+
* `validate health` (Phase 11, #3309, ADR-3180 §8.2/§8.3/§8.5).
|
|
5
|
+
*
|
|
6
|
+
* Establishes the exact contract every extracted rule builds onto: the
|
|
7
|
+
* frozen `SEVERITY`/`REMEDY_ACTION`/`REMEDY_RISK` enums, the
|
|
8
|
+
* `Diagnostic`/`Remedy`/`Rule` shapes, the `RULES` container (the 32 rules
|
|
9
|
+
* extracted from `cmdValidateHealth`, `src/verify.cts:1616-2577`, are
|
|
10
|
+
* concatenated in from each rule-group file under
|
|
11
|
+
* `src/health-diagnostic-rules/`), the `evaluateRules` evaluator, and the
|
|
12
|
+
* `applyRepairs` `--repair`/`--backfill` dispatcher — whose per-action
|
|
13
|
+
* handlers are REAL here (ported behavior-preserving from
|
|
14
|
+
* `verify.cts:2405-2553`'s repair switch), not stubs.
|
|
15
|
+
*
|
|
16
|
+
* `applyRepairs` does not receive a `PlanningSnapshot` (its call-site
|
|
17
|
+
* signature, `(cwd, diagnostics, repair, backfill)`, is a locked contract —
|
|
18
|
+
* see `tests/health-diagnostic.test.cjs`) — so, like `cmdValidateHealth`
|
|
19
|
+
* itself before this migration, it performs its own bounded filesystem I/O
|
|
20
|
+
* to apply a repair. This is not a §8.1 rule 1 violation: that rule
|
|
21
|
+
* constrains a RULE's `check(snapshot)` signature (no ambient I/O), not the
|
|
22
|
+
* evaluator/dispatcher, which the design doc's "subject-surface gap" section
|
|
23
|
+
* already establishes performs I/O once, up front, on the rules' behalf.
|
|
24
|
+
*
|
|
25
|
+
* `PlanningSnapshot` is deliberately NOT re-exported as a type from
|
|
26
|
+
* `planning-snapshot.cts` here (see the design doc's "Known limits" and this
|
|
27
|
+
* phase's brief): `ReturnType<typeof buildPlanningSnapshot>` is used inline
|
|
28
|
+
* instead, via a type-only `import ... = require(...)` that is fully erased
|
|
29
|
+
* at compile time — zero changes to the already-shipped, already-tested
|
|
30
|
+
* `planning-snapshot.cts`.
|
|
31
|
+
*
|
|
32
|
+
* Design: .gsd/phase/refactor-3309-health-diagnostic-rule-table/40-design.md
|
|
33
|
+
* Test matrix: .gsd/phase/refactor-3309-health-diagnostic-rule-table/50-test-matrix.md
|
|
34
|
+
*
|
|
35
|
+
* ADR-457 build-at-publish: source in src/health-diagnostic.cts, compiled to
|
|
36
|
+
* gsd-core/bin/lib/health-diagnostic.cjs (gitignored).
|
|
37
|
+
*/
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
42
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
43
|
+
// Runtime values (SEVERITY/REMEDY_ACTION/REMEDY_RISK) are needed here — not
|
|
44
|
+
// just types — for `applyRepairs`'s comparisons, so this is a normal
|
|
45
|
+
// (non type-only) `import ... = require(...)`. `health-diagnostic-types.cjs`
|
|
46
|
+
// is the leaf module these enums/types were extracted to, so that this file
|
|
47
|
+
// can `require()` every rule-group file below without a circular dependency
|
|
48
|
+
// (see that module's file-level comment for the full explanation).
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
|
+
const healthDiagnosticTypesMod = require("./health-diagnostic-types.cjs");
|
|
51
|
+
const { SEVERITY, REMEDY_ACTION, REMEDY_RISK } = healthDiagnosticTypesMod;
|
|
52
|
+
// ─── Rule table ─────────────────────────────────────────────────────────────
|
|
53
|
+
// Populated by concatenating each rule group's exported `RULES` array (design
|
|
54
|
+
// doc, "Rule table organization" section) — the 32 rule functions extracted
|
|
55
|
+
// from `cmdValidateHealth`, `src/verify.cts:1616-2577`.
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
57
|
+
const rootExistenceMod = require("./health-diagnostic-rules/root-existence.cjs");
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
59
|
+
const stateConsistencyMod = require("./health-diagnostic-rules/state-consistency.cjs");
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
61
|
+
const configValidationMod = require("./health-diagnostic-rules/config-validation.cjs");
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
63
|
+
const phaseStructureMod = require("./health-diagnostic-rules/phase-structure.cjs");
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
65
|
+
const agentInstallMod = require("./health-diagnostic-rules/agent-install.cjs");
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
67
|
+
const roadmapDiskConsistencyMod = require("./health-diagnostic-rules/roadmap-disk-consistency.cjs");
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
|
+
const worktreeHealthMod = require("./health-diagnostic-rules/worktree-health.cjs");
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
71
|
+
const milestoneArchiveHygieneMod = require("./health-diagnostic-rules/milestone-archive-hygiene.cjs");
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
73
|
+
const consistencyMod = require("./health-diagnostic-rules/consistency.cjs");
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
75
|
+
const installSurfaceShadowingMod = require("./health-diagnostic-rules/install-surface-shadowing.cjs");
|
|
76
|
+
const RULES = [
|
|
77
|
+
...rootExistenceMod.RULES,
|
|
78
|
+
...stateConsistencyMod.RULES,
|
|
79
|
+
...configValidationMod.RULES,
|
|
80
|
+
...phaseStructureMod.RULES,
|
|
81
|
+
...agentInstallMod.RULES,
|
|
82
|
+
...roadmapDiskConsistencyMod.RULES,
|
|
83
|
+
...worktreeHealthMod.RULES,
|
|
84
|
+
...milestoneArchiveHygieneMod.RULES,
|
|
85
|
+
...installSurfaceShadowingMod.RULES,
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* `validate.consistency`'s own rule set (Phase 12, #3310, ADR-3180 §8.4,
|
|
89
|
+
* design doc "Which rules run where") — W006/W007 REUSED (the exact same
|
|
90
|
+
* `Rule` objects `RULES` above already carries, not new copies) plus the
|
|
91
|
+
* four new C0NN rules from `consistency.cts`. Deliberately NOT the full
|
|
92
|
+
* `RULES` table: running `validate.health`'s config/state/worktree rules
|
|
93
|
+
* under `validate.consistency` would be scope creep the issue never asked
|
|
94
|
+
* for.
|
|
95
|
+
*/
|
|
96
|
+
const CONSISTENCY_RULES = [
|
|
97
|
+
...roadmapDiskConsistencyMod.RULES.filter((r) => ['W006', 'W007'].includes(r.code)),
|
|
98
|
+
...consistencyMod.RULES,
|
|
99
|
+
];
|
|
100
|
+
// ─── Repair-handler runtime dependencies ───────────────────────────────────
|
|
101
|
+
//
|
|
102
|
+
// Same owners `cmdValidateHealth`'s pre-migration repair switch used
|
|
103
|
+
// (`verify.cts:2405-2553`) — ported verbatim, not reinvented.
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
105
|
+
const planningWorkspaceMod = require("./planning-workspace.cjs");
|
|
106
|
+
const { planningDir } = planningWorkspaceMod;
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
108
|
+
const configLoaderMod = require("./config-loader.cjs");
|
|
109
|
+
const { CONFIG_DEFAULTS } = configLoaderMod;
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
111
|
+
const roadmapParserMod = require("./roadmap-parser.cjs");
|
|
112
|
+
const { getMilestoneInfo } = roadmapParserMod;
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
114
|
+
const stateMod = require("./state.cjs");
|
|
115
|
+
const { writeStateMd } = stateMod;
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
117
|
+
const frontmatter = require("./frontmatter.cjs");
|
|
118
|
+
const { extractFrontmatter } = frontmatter;
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
120
|
+
const stateTransitionMod = require("./state-transition.cjs");
|
|
121
|
+
const { rebuildStateTransaction } = stateTransitionMod;
|
|
122
|
+
const clock_cjs_1 = require("./clock.cjs");
|
|
123
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
124
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
125
|
+
// ─── Evaluator ──────────────────────────────────────────────────────────────
|
|
126
|
+
/**
|
|
127
|
+
* Evaluate an explicit `rules` array against `snapshot`, throwing if any two
|
|
128
|
+
* entries share a `code` (defense in depth beside the static lint guard,
|
|
129
|
+
* §8.2 rule 1, `scripts/lint-health-diagnostic-rule-table.cjs`). Separated
|
|
130
|
+
* from `evaluateRules` so the duplicate-code guard is unit-testable against
|
|
131
|
+
* a small, locally-constructed fake rule array, independent of the real
|
|
132
|
+
* `RULES` table.
|
|
133
|
+
*/
|
|
134
|
+
function evaluateRuleTable(rules, snapshot) {
|
|
135
|
+
const seen = new Set();
|
|
136
|
+
for (const rule of rules) {
|
|
137
|
+
if (seen.has(rule.code)) {
|
|
138
|
+
throw new Error(`health-diagnostic: duplicate rule code "${rule.code}" in rule table`);
|
|
139
|
+
}
|
|
140
|
+
seen.add(rule.code);
|
|
141
|
+
}
|
|
142
|
+
return rules.flatMap((rule) => rule.check(snapshot));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Evaluate every rule in `RULES` against `snapshot`, flattening each rule's
|
|
146
|
+
* `Diagnostic[]` into one array.
|
|
147
|
+
*/
|
|
148
|
+
function evaluateRules(snapshot) {
|
|
149
|
+
return evaluateRuleTable(RULES, snapshot);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Evaluate `CONSISTENCY_RULES` (W006/W007 + C001-C004) against `snapshot` —
|
|
153
|
+
* `validate.consistency`'s evaluator entry point, mirroring `evaluateRules`
|
|
154
|
+
* exactly but over the smaller, command-specific rule subset.
|
|
155
|
+
*/
|
|
156
|
+
function evaluateConsistencyRules(snapshot) {
|
|
157
|
+
return evaluateRuleTable(CONSISTENCY_RULES, snapshot);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Derive every filesystem path a repair handler needs, from `cwd` alone —
|
|
161
|
+
* exactly how `cmdValidateHealth` derived them pre-migration
|
|
162
|
+
* (`verify.cts:1644-1652`/`2301-2302`). `config.json`/`MILESTONES.md`/
|
|
163
|
+
* `milestones/` are root-scoped (`planningRoot`); `STATE.md` is
|
|
164
|
+
* workstream-scoped (`planningDir`) — the same root-vs-workstream split
|
|
165
|
+
* `buildConfigField`/`buildStateFields` (`planning-snapshot.cts`) already
|
|
166
|
+
* document for the read side.
|
|
167
|
+
*/
|
|
168
|
+
function repairPaths(cwd) {
|
|
169
|
+
// #3749: the project root is PROJECT-aware but deliberately NOT workstream-
|
|
170
|
+
// scoped — under GSD_PROJECT, config.json/MILESTONES.md/milestones belong to
|
|
171
|
+
// `.planning/<project>/` (the same base init.new-project's config_path
|
|
172
|
+
// names); under GSD_WORKSTREAM they stay at the workstream PARENT, keeping
|
|
173
|
+
// the documented root-vs-workstream split. planningDir(cwd, null) is exactly
|
|
174
|
+
// that base: project env honored, ws env suppressed.
|
|
175
|
+
const rootBase = planningDir(cwd, null);
|
|
176
|
+
const wsBase = planningDir(cwd);
|
|
177
|
+
return {
|
|
178
|
+
rootBase,
|
|
179
|
+
configPath: node_path_1.default.join(rootBase, 'config.json'),
|
|
180
|
+
statePath: node_path_1.default.join(wsBase, 'STATE.md'),
|
|
181
|
+
milestonesPath: node_path_1.default.join(rootBase, 'MILESTONES.md'),
|
|
182
|
+
milestonesArchiveDir: node_path_1.default.join(rootBase, 'milestones'),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** `verify.cts:2413-2429`'s default config.json payload, ported verbatim. */
|
|
186
|
+
function defaultConfigPayload() {
|
|
187
|
+
return {
|
|
188
|
+
model_profile: CONFIG_DEFAULTS.model_profile,
|
|
189
|
+
commit_docs: CONFIG_DEFAULTS.commit_docs,
|
|
190
|
+
search_gitignored: CONFIG_DEFAULTS.search_gitignored,
|
|
191
|
+
branching_strategy: CONFIG_DEFAULTS.branching_strategy,
|
|
192
|
+
phase_branch_template: CONFIG_DEFAULTS.phase_branch_template,
|
|
193
|
+
milestone_branch_template: CONFIG_DEFAULTS.milestone_branch_template,
|
|
194
|
+
quick_branch_template: CONFIG_DEFAULTS.quick_branch_template,
|
|
195
|
+
workflow: {
|
|
196
|
+
research: CONFIG_DEFAULTS.research,
|
|
197
|
+
plan_check: CONFIG_DEFAULTS.plan_checker,
|
|
198
|
+
verifier: CONFIG_DEFAULTS.verifier,
|
|
199
|
+
nyquist_validation: CONFIG_DEFAULTS.nyquist_validation,
|
|
200
|
+
},
|
|
201
|
+
parallelization: CONFIG_DEFAULTS.parallelization,
|
|
202
|
+
brave_search: CONFIG_DEFAULTS.brave_search,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* `verify.cts:2301-2335`'s W018 archived-vs-documented-versions diff,
|
|
207
|
+
* relocated verbatim (same two regexes, same two-file read) so
|
|
208
|
+
* `backfillMilestones` can recompute exactly which versions are missing
|
|
209
|
+
* without a `PlanningSnapshot` (`applyRepairs` is not a `Rule` and is not
|
|
210
|
+
* handed one — see this file's header comment). This is the same
|
|
211
|
+
* derivation `buildMilestoneArchiveStatusField`
|
|
212
|
+
* (`src/planning-snapshot.cts`) already performs for the W018 RULE's read
|
|
213
|
+
* side; recomputed here, not re-invented, because the rule's own
|
|
214
|
+
* `Diagnostic.remedy.args` carries no version list (confirmed by direct
|
|
215
|
+
* read of `src/health-diagnostic-rules/milestone-archive-hygiene.cts`).
|
|
216
|
+
*/
|
|
217
|
+
function computeMissingMilestoneVersions(milestonesArchiveDir, milestonesPath) {
|
|
218
|
+
let archivedVersions = [];
|
|
219
|
+
try {
|
|
220
|
+
if (node_fs_1.default.existsSync(milestonesArchiveDir)) {
|
|
221
|
+
const archiveFiles = node_fs_1.default.readdirSync(milestonesArchiveDir);
|
|
222
|
+
archivedVersions = archiveFiles
|
|
223
|
+
.map((f) => f.match(/^(v\d+\.\d+(?:\.\d+)?)-ROADMAP\.md$/))
|
|
224
|
+
.filter((m) => m !== null)
|
|
225
|
+
.map((m) => m[1]);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
/* intentionally empty — mirrors the original's advisory try/catch */
|
|
230
|
+
}
|
|
231
|
+
let documentedVersions = [];
|
|
232
|
+
try {
|
|
233
|
+
if (node_fs_1.default.existsSync(milestonesPath)) {
|
|
234
|
+
const registryContent = node_fs_1.default.readFileSync(milestonesPath, 'utf-8');
|
|
235
|
+
documentedVersions = [...registryContent.matchAll(/^##\s+(v\d+\.\d+(?:\.\d+)?)/gm)].map((m) => m[1]);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
/* intentionally empty */
|
|
240
|
+
}
|
|
241
|
+
const documented = new Set(documentedVersions);
|
|
242
|
+
return archivedVersions.filter((v) => !documented.has(v));
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Execute exactly one real repair action, ported behavior-preserving from
|
|
246
|
+
* `verify.cts:2405-2553`'s `switch (repair)`. Throws are the caller's
|
|
247
|
+
* responsibility to catch (mirrors the original's per-action try/catch
|
|
248
|
+
* shape, collapsed to one seam here since every case now shares one
|
|
249
|
+
* caller).
|
|
250
|
+
*/
|
|
251
|
+
function runRepairAction(cwd, action, paths) {
|
|
252
|
+
const { rootBase, configPath, statePath, milestonesPath, milestonesArchiveDir } = paths;
|
|
253
|
+
switch (action) {
|
|
254
|
+
case REMEDY_ACTION.CREATE_CONFIG:
|
|
255
|
+
case REMEDY_ACTION.RESET_CONFIG: {
|
|
256
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(configPath, JSON.stringify(defaultConfigPayload(), null, 2));
|
|
257
|
+
return { success: true, path: 'config.json' };
|
|
258
|
+
}
|
|
259
|
+
case REMEDY_ACTION.REGENERATE_STATE: {
|
|
260
|
+
const extraDetails = [];
|
|
261
|
+
if (node_fs_1.default.existsSync(statePath)) {
|
|
262
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
263
|
+
const backupPath = `${statePath}.bak-${timestamp}`;
|
|
264
|
+
node_fs_1.default.copyFileSync(statePath, backupPath);
|
|
265
|
+
extraDetails.push({ action: 'backupState', success: true, path: backupPath });
|
|
266
|
+
}
|
|
267
|
+
const milestone = getMilestoneInfo(cwd).value;
|
|
268
|
+
const projectRef = node_path_1.default
|
|
269
|
+
.relative(cwd, node_path_1.default.join(rootBase, 'PROJECT.md'))
|
|
270
|
+
.split(node_path_1.default.sep)
|
|
271
|
+
.join('/');
|
|
272
|
+
const slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
|
|
273
|
+
const slash = (name) => (0, runtime_slash_cjs_1.formatGsdSlash)(name, slashRuntime);
|
|
274
|
+
let stateContent = `# Session State\n\n`;
|
|
275
|
+
stateContent += `## Project Reference\n\n`;
|
|
276
|
+
stateContent += `See: ${projectRef}\n\n`;
|
|
277
|
+
stateContent += `## Position\n\n`;
|
|
278
|
+
stateContent += `**Milestone:** ${milestone?.version ?? ''} ${milestone?.name ?? ''}\n`;
|
|
279
|
+
stateContent += `**Current phase:** (determining...)\n`;
|
|
280
|
+
stateContent += `**Status:** Resuming\n\n`;
|
|
281
|
+
stateContent += `## Session Log\n\n`;
|
|
282
|
+
stateContent += `- ${clock_cjs_1.realClock.localToday()}: STATE.md regenerated by ${slash('health')} --repair\n`;
|
|
283
|
+
// ADR-3473 §8.6: `/gsd-health --repair` is a factory reset, so
|
|
284
|
+
// preservation must not run; the snapshot of the file being replaced is
|
|
285
|
+
// still captured, and `{}` is the correct, legal snapshot of a
|
|
286
|
+
// STATE.md that had no parseable frontmatter — which is the usual
|
|
287
|
+
// reason this repair fires.
|
|
288
|
+
const priorState = node_fs_1.default.existsSync(statePath) ? ((0, shell_command_projection_cjs_1.platformReadSync)(statePath) ?? '') : '';
|
|
289
|
+
writeStateMd(statePath, stateContent, rebuildStateTransaction({
|
|
290
|
+
snapshot: extractFrontmatter(priorState, statePath),
|
|
291
|
+
}), cwd);
|
|
292
|
+
return { success: true, path: 'STATE.md', extraDetails };
|
|
293
|
+
}
|
|
294
|
+
case REMEDY_ACTION.ADD_NYQUIST_KEY:
|
|
295
|
+
case REMEDY_ACTION.ADD_AI_INTEGRATION_PHASE_KEY: {
|
|
296
|
+
const key = action === REMEDY_ACTION.ADD_NYQUIST_KEY ? 'nyquist_validation' : 'ai_integration_phase';
|
|
297
|
+
const configRaw = node_fs_1.default.readFileSync(configPath, 'utf-8');
|
|
298
|
+
const configParsed = JSON.parse(configRaw);
|
|
299
|
+
if (!configParsed['workflow'])
|
|
300
|
+
configParsed['workflow'] = {};
|
|
301
|
+
const wf = configParsed['workflow'];
|
|
302
|
+
if (wf[key] === undefined) {
|
|
303
|
+
wf[key] = true;
|
|
304
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(configPath, JSON.stringify(configParsed, null, 2));
|
|
305
|
+
}
|
|
306
|
+
return { success: true, path: 'config.json' };
|
|
307
|
+
}
|
|
308
|
+
case REMEDY_ACTION.BACKFILL_MILESTONES: {
|
|
309
|
+
const missing = computeMissingMilestoneVersions(milestonesArchiveDir, milestonesPath);
|
|
310
|
+
const today = clock_cjs_1.realClock.localToday();
|
|
311
|
+
const slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
|
|
312
|
+
const slash = (name) => (0, runtime_slash_cjs_1.formatGsdSlash)(name, slashRuntime);
|
|
313
|
+
let backfilled = 0;
|
|
314
|
+
for (const ver of missing) {
|
|
315
|
+
try {
|
|
316
|
+
const snapshotPath = node_path_1.default.join(milestonesArchiveDir, `${ver}-ROADMAP.md`);
|
|
317
|
+
const snapshot = (0, shell_command_projection_cjs_1.platformReadSync)(snapshotPath);
|
|
318
|
+
const titleMatch = snapshot && snapshot.match(/^#\s+(.+)$/m);
|
|
319
|
+
const milestoneName = titleMatch
|
|
320
|
+
? titleMatch[1].replace(/^Milestone\s+/i, '').replace(/^v[\d.]+\s*/, '').trim()
|
|
321
|
+
: ver;
|
|
322
|
+
const entry = `## ${ver}${milestoneName && milestoneName !== ver ? ` ${milestoneName}` : ''} (Backfilled: ${today})\n\n**Note:** Synthesized from archive snapshot by \`${slash('health')} --backfill\`. Original completion date unknown.\n\n---\n\n`;
|
|
323
|
+
const milestonesContent = node_fs_1.default.existsSync(milestonesPath)
|
|
324
|
+
? node_fs_1.default.readFileSync(milestonesPath, 'utf-8')
|
|
325
|
+
: '';
|
|
326
|
+
if (!milestonesContent.trim()) {
|
|
327
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(milestonesPath, `# Milestones\n\n${entry}`);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
const headerMatch = milestonesContent.match(/^(#{1,3}\s+[^\n]*\n\n?)/);
|
|
331
|
+
if (headerMatch) {
|
|
332
|
+
const header = headerMatch[1];
|
|
333
|
+
const rest = milestonesContent.slice(header.length);
|
|
334
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(milestonesPath, header + entry + rest);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(milestonesPath, entry + milestonesContent);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
backfilled++;
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
/* intentionally empty — partial backfill is acceptable */
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return { success: true, detail: `Backfilled ${backfilled} milestone(s) into MILESTONES.md` };
|
|
347
|
+
}
|
|
348
|
+
default:
|
|
349
|
+
return { success: false, error: `no repair handler registered for action "${action}"` };
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* `--repair`/`--backfill` dispatcher (design doc "`--repair` behavior
|
|
354
|
+
* change" section; §8.3 rule 3). For each diagnostic whose remedy is not
|
|
355
|
+
* `ADVISE`:
|
|
356
|
+
*
|
|
357
|
+
* - Not requested — `repair` is false, and for `backfillMilestones`
|
|
358
|
+
* specifically `backfill` is also false (mirrors `cmdValidateHealth`'s
|
|
359
|
+
* existing `backfillMilestones` gate, `verify.cts:2504`:
|
|
360
|
+
* `if (!options['backfill'] && !options['repair']) break;`) — skipped
|
|
361
|
+
* entirely, recorded in neither `applied` nor `refused`.
|
|
362
|
+
* - Requested and `remedy.risk === DESTRUCTIVE` — pushed onto `refused`,
|
|
363
|
+
* handler never invoked. This is the §8.3 rule 3 breaking-change
|
|
364
|
+
* enforcement point: a DESTRUCTIVE remedy is describable but is never
|
|
365
|
+
* applied by `--repair`. A `details` row is still recorded, so the
|
|
366
|
+
* refusal is VISIBLE in `cmdValidateHealth`'s `repairs_performed` output,
|
|
367
|
+
* not silently dropped.
|
|
368
|
+
* - Requested and `remedy.risk === NONE` — the real handler is invoked,
|
|
369
|
+
* pushed onto `applied`.
|
|
370
|
+
*
|
|
371
|
+
* `applied`/`refused` are unchanged in shape from the pre-existing skeleton
|
|
372
|
+
* (locked by `tests/health-diagnostic.test.cjs`, rows 11-12): arrays of
|
|
373
|
+
* diagnostic `code`s. `details` is ADDITIVE — every real action maps 1:1 to
|
|
374
|
+
* exactly one code in this rule table (confirmed: no `REMEDY_ACTION` other
|
|
375
|
+
* than `ADVISE` is used by more than one rule), so `cmdValidateHealth` can
|
|
376
|
+
* rebuild the legacy action-keyed `repairs_performed` shape directly from
|
|
377
|
+
* it.
|
|
378
|
+
*/
|
|
379
|
+
function applyRepairs(cwd, diagnostics, repair, backfill) {
|
|
380
|
+
const applied = [];
|
|
381
|
+
const refused = [];
|
|
382
|
+
const details = [];
|
|
383
|
+
const paths = repairPaths(cwd);
|
|
384
|
+
for (const diagnostic of diagnostics) {
|
|
385
|
+
const { remedy, code } = diagnostic;
|
|
386
|
+
if (remedy.action === REMEDY_ACTION.ADVISE)
|
|
387
|
+
continue;
|
|
388
|
+
const requested = remedy.action === REMEDY_ACTION.BACKFILL_MILESTONES ? repair || backfill : repair;
|
|
389
|
+
if (!requested)
|
|
390
|
+
continue;
|
|
391
|
+
if (remedy.risk === REMEDY_RISK.DESTRUCTIVE) {
|
|
392
|
+
refused.push(code);
|
|
393
|
+
details.push({
|
|
394
|
+
code,
|
|
395
|
+
action: remedy.action,
|
|
396
|
+
success: false,
|
|
397
|
+
error: `refused: '${remedy.action}' is a destructive remedy and is not auto-applied by --repair`,
|
|
398
|
+
});
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const outcome = runRepairAction(cwd, remedy.action, paths);
|
|
403
|
+
if (outcome.extraDetails) {
|
|
404
|
+
for (const extra of outcome.extraDetails) {
|
|
405
|
+
details.push({ code, action: extra.action, success: extra.success, ...(extra.path ? { path: extra.path } : {}) });
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
details.push({
|
|
409
|
+
code,
|
|
410
|
+
action: remedy.action,
|
|
411
|
+
success: outcome.success,
|
|
412
|
+
...(outcome.path ? { path: outcome.path } : {}),
|
|
413
|
+
...(outcome.detail ? { detail: outcome.detail } : {}),
|
|
414
|
+
...(outcome.error ? { error: outcome.error } : {}),
|
|
415
|
+
});
|
|
416
|
+
// `applied` means "the repair actually succeeded", not "was attempted"
|
|
417
|
+
// — a handler that returns `{success: false}` (or throws, caught
|
|
418
|
+
// below) is recorded in `details` with its failure, but must not be
|
|
419
|
+
// reported as applied. `refused` is reserved for the DESTRUCTIVE-risk
|
|
420
|
+
// gate above; a failed attempt is neither applied nor refused.
|
|
421
|
+
if (outcome.success)
|
|
422
|
+
applied.push(code);
|
|
423
|
+
}
|
|
424
|
+
catch (err) {
|
|
425
|
+
details.push({
|
|
426
|
+
code,
|
|
427
|
+
action: remedy.action,
|
|
428
|
+
success: false,
|
|
429
|
+
error: err instanceof Error ? err.message : String(err),
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return { applied, refused, details };
|
|
434
|
+
}
|
|
435
|
+
// ─── Exports ────────────────────────────────────────────────────────────────
|
|
436
|
+
const healthDiagnostic = {
|
|
437
|
+
SEVERITY,
|
|
438
|
+
REMEDY_ACTION,
|
|
439
|
+
REMEDY_RISK,
|
|
440
|
+
RULES,
|
|
441
|
+
evaluateRules,
|
|
442
|
+
// Additive beyond the phase's required-exports list — exposed so the
|
|
443
|
+
// duplicate-code guard (row 13) is directly unit-testable against a fake
|
|
444
|
+
// rule array without mutating the real, still-empty `RULES` export.
|
|
445
|
+
evaluateRuleTable,
|
|
446
|
+
applyRepairs,
|
|
447
|
+
// Phase 12 (#3310) — `validate.consistency`'s own rule subset + evaluator.
|
|
448
|
+
CONSISTENCY_RULES,
|
|
449
|
+
evaluateConsistencyRules,
|
|
450
|
+
};
|
|
451
|
+
module.exports = healthDiagnostic;
|
|
@@ -653,10 +653,25 @@ function extensionEventSurfaceFor(extensionEvents) {
|
|
|
653
653
|
* per-host branch ADR-1239 exists to remove. Omit `prompt` entirely and the
|
|
654
654
|
* resolution is byte-identical to Phase 2's (the unconsumed-resolver shape).
|
|
655
655
|
*
|
|
656
|
-
* Argv order is base args → cwd flag → prompt, so the prompt
|
|
657
|
-
* positional token for the hosts that read it that way.
|
|
656
|
+
* Argv order is base args → model flag → cwd flag → prompt, so the prompt
|
|
657
|
+
* stays the final positional token for the hosts that read it that way. The
|
|
658
|
+
* model flag is placed BEFORE the cwd flag (not after, and not appended at
|
|
659
|
+
* the very end) purely to keep it clear of that trailing positional — the
|
|
660
|
+
* model value itself is never the prompt-adjacent token a host might scan
|
|
661
|
+
* for last.
|
|
662
|
+
*
|
|
663
|
+
* `model` (Phase 4, #3714) is optional, descriptor-gated exactly like prompt:
|
|
664
|
+
* a `modelFlag` string on the descriptor names the flag that pins the
|
|
665
|
+
* spawned executor's model (codex: `--model`); `null`/absent means the host
|
|
666
|
+
* exposes no such override on this exec path, and `[modelFlag, model]` is
|
|
667
|
+
* appended ONLY when both the descriptor's `modelFlag` and the caller's
|
|
668
|
+
* `model` are non-empty strings. Omitting `model` entirely (or passing it to
|
|
669
|
+
* a host with no `modelFlag`) is byte-identical to the resolver's behavior
|
|
670
|
+
* before this parameter existed. This function decides no policy about WHICH
|
|
671
|
+
* model to pass or what 'inherit' means — that is entirely the caller's job;
|
|
672
|
+
* this seam only shapes descriptor + values into argv.
|
|
658
673
|
*/
|
|
659
|
-
function resolveOrchestratorExec(orchestratorExec, cwd, prompt) {
|
|
674
|
+
function resolveOrchestratorExec(orchestratorExec, cwd, prompt, model) {
|
|
660
675
|
if (!orchestratorExec || typeof orchestratorExec !== 'object' || Array.isArray(orchestratorExec)) {
|
|
661
676
|
return { ok: false, reason: 'missing_command' };
|
|
662
677
|
}
|
|
@@ -676,11 +691,22 @@ function resolveOrchestratorExec(orchestratorExec, cwd, prompt) {
|
|
|
676
691
|
if (oe.promptFlag !== undefined && oe.promptFlag !== null && typeof oe.promptFlag !== 'string') {
|
|
677
692
|
return { ok: false, reason: 'invalid_prompt_flag' };
|
|
678
693
|
}
|
|
694
|
+
if (oe.modelFlag !== undefined && oe.modelFlag !== null && typeof oe.modelFlag !== 'string') {
|
|
695
|
+
return { ok: false, reason: 'invalid_model_flag' };
|
|
696
|
+
}
|
|
679
697
|
// An executor spawned with no instruction is a hang, not a degraded run —
|
|
680
698
|
// fail closed rather than launching a prompt-less process.
|
|
681
699
|
if (prompt !== undefined && (typeof prompt !== 'string' || prompt.length === 0)) {
|
|
682
700
|
return { ok: false, reason: 'invalid_prompt' };
|
|
683
701
|
}
|
|
702
|
+
// Unlike `prompt` — where empty is a hang, not a degraded run, hence the
|
|
703
|
+
// fail-closed check above — an absent/null/empty model is simply "use the
|
|
704
|
+
// host default", the same benign degradation `cwdFlag: null` already
|
|
705
|
+
// expresses. Only a present-but-non-string value (number/bool/array/object)
|
|
706
|
+
// is a caller error; null/undefined/'' fall through to "omit the flag".
|
|
707
|
+
if (model !== undefined && model !== null && typeof model !== 'string') {
|
|
708
|
+
return { ok: false, reason: 'invalid_model' };
|
|
709
|
+
}
|
|
684
710
|
// Leading-dash guard, mirroring worktree-safety.cts's `unsafe_leading_dash`
|
|
685
711
|
// check on git arguments. A positional prompt (or a cwd) beginning with '-'
|
|
686
712
|
// is parsed by the spawned CLI as a FLAG, not a value — the same failure the
|
|
@@ -695,10 +721,17 @@ function resolveOrchestratorExec(orchestratorExec, cwd, prompt) {
|
|
|
695
721
|
if (cwd.startsWith('-')) {
|
|
696
722
|
return { ok: false, reason: 'unsafe_leading_dash_cwd' };
|
|
697
723
|
}
|
|
724
|
+
if (typeof model === 'string' && model.startsWith('-')) {
|
|
725
|
+
return { ok: false, reason: 'unsafe_leading_dash_model' };
|
|
726
|
+
}
|
|
698
727
|
const baseArgs = Array.isArray(oe.args) ? [...oe.args] : [];
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
728
|
+
let args = baseArgs;
|
|
729
|
+
if (typeof oe.modelFlag === 'string' && oe.modelFlag.length > 0 && typeof model === 'string' && model.length > 0) {
|
|
730
|
+
args = [...args, oe.modelFlag, model];
|
|
731
|
+
}
|
|
732
|
+
args = typeof oe.cwdFlag === 'string' && oe.cwdFlag.length > 0
|
|
733
|
+
? [...args, oe.cwdFlag, cwd]
|
|
734
|
+
: args;
|
|
702
735
|
if (typeof prompt === 'string') {
|
|
703
736
|
if (typeof oe.promptFlag === 'string' && oe.promptFlag.length > 0) {
|
|
704
737
|
args.push(oe.promptFlag, prompt);
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Host runtime detection — ADR-2313 Phase 5 (#3245, folded from #2320).
|
|
4
|
+
*
|
|
5
|
+
* `init`'s reported `agent_runtime` was hardcoding `claude` even when run
|
|
6
|
+
* inside a Codex session, because resolveRuntime's ladder only checks the
|
|
7
|
+
* explicit `GSD_RUNTIME` env var and `.planning/config.json`'s `runtime`
|
|
8
|
+
* field — there was no fallback that looked at the actual host process.
|
|
9
|
+
*
|
|
10
|
+
* This module adds a host-detection rung strictly BELOW those two explicit
|
|
11
|
+
* sources: it only runs when neither `GSD_RUNTIME` nor config `runtime` is
|
|
12
|
+
* set. It never writes anything (#2297 — no shared-defaults poisoning: this
|
|
13
|
+
* module never touches .planning/config.json or any other file). It never
|
|
14
|
+
* shells out, so there is no subprocess to time-bound — detection is pure
|
|
15
|
+
* env-var and existence-check inspection.
|
|
16
|
+
*/
|
|
17
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.CODEX_CONFIG_HOME_ENV = exports.CODEX_SESSION_ENV_SIGNALS = exports.CODEX_CONFIG_MARKER = void 0;
|
|
22
|
+
exports.detectHostRuntime = detectHostRuntime;
|
|
23
|
+
exports.resolveReportedRuntime = resolveReportedRuntime;
|
|
24
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
25
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
26
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
27
|
+
const update_context_cjs_1 = require("./update-context.cjs");
|
|
28
|
+
Object.defineProperty(exports, "CODEX_CONFIG_MARKER", { enumerable: true, get: function () { return update_context_cjs_1.CODEX_CONFIG_MARKER; } });
|
|
29
|
+
// Codex sandbox env vars set by the shell tool / Seatbelt child-process spawn.
|
|
30
|
+
// Evidence: openai/codex AGENTS.md — "The sandbox environment automatically
|
|
31
|
+
// sets CODEX_SANDBOX_NETWORK_DISABLED=1 when using the shell tool, and
|
|
32
|
+
// CODEX_SANDBOX=seatbelt for child processes spawned via Seatbelt." (injected
|
|
33
|
+
// by spawn_child_async in codex-rs/core/src/spawn.rs). These are absent under
|
|
34
|
+
// sandbox_mode = "danger-full-access", so this signal is best-effort and
|
|
35
|
+
// degrades to the default when unset.
|
|
36
|
+
//
|
|
37
|
+
// Note: CODEX_THREAD_ID (which appears in src/active-workstream-store.cts's
|
|
38
|
+
// WORKSTREAM_SESSION_ENV_KEYS) is deliberately NOT used here — it is
|
|
39
|
+
// undocumented in Codex's published env-var reference and source, so it
|
|
40
|
+
// fails this repo's citation bar.
|
|
41
|
+
exports.CODEX_SESSION_ENV_SIGNALS = Object.freeze([
|
|
42
|
+
'CODEX_SANDBOX',
|
|
43
|
+
'CODEX_SANDBOX_NETWORK_DISABLED',
|
|
44
|
+
]);
|
|
45
|
+
// Evidence: learn.chatgpt.com/docs/config-file/environment-variables —
|
|
46
|
+
// "Sets the root for Codex state, including config…". The marker FILENAME
|
|
47
|
+
// (`config.toml`) is single-sourced from `update-context.cts`'s
|
|
48
|
+
// `inferPreferredRuntime` (imported above and re-exported for existing
|
|
49
|
+
// importers) — that is the only thing shared between the two functions. The
|
|
50
|
+
// TRUTHINESS RULE deliberately differs: `inferPreferredRuntime` treats a
|
|
51
|
+
// bare, unchecked `CODEX_HOME` as sufficient to resolve an update context,
|
|
52
|
+
// while THIS module additionally requires the marker file to exist, because
|
|
53
|
+
// it is asserting session identity rather than resolving an update context
|
|
54
|
+
// and needs the stronger signal. That difference is intentional and pinned
|
|
55
|
+
// by a test in `tests/host-runtime-detection.test.cjs` rather than left
|
|
56
|
+
// implicit.
|
|
57
|
+
//
|
|
58
|
+
// The DEFAULT `~/.codex/config.toml` is deliberately NEVER probed here:
|
|
59
|
+
// every machine that has ever run Codex has that file, so probing it
|
|
60
|
+
// unconditionally would misreport Claude Code sessions (or any other
|
|
61
|
+
// runtime) as codex just because Codex was installed at some point. An
|
|
62
|
+
// explicitly-exported CODEX_HOME is the user designating a Codex root for
|
|
63
|
+
// the CURRENT session, which is a much stronger signal.
|
|
64
|
+
exports.CODEX_CONFIG_HOME_ENV = 'CODEX_HOME';
|
|
65
|
+
// The degraded no-detection result. Also the fallback returned when ANY step
|
|
66
|
+
// of detection throws (see the module's stated no-throw premise below).
|
|
67
|
+
const NO_DETECTION = { runtime: null, source: 'none', signal: null };
|
|
68
|
+
/**
|
|
69
|
+
* Detect the host runtime from process environment signals, without ever
|
|
70
|
+
* consulting the explicit GSD_RUNTIME/config.json sources (those are a
|
|
71
|
+
* higher-priority rung handled by resolveExplicitRuntime).
|
|
72
|
+
*
|
|
73
|
+
* Never throws: the whole body — including the raw `env[key]` reads, which a
|
|
74
|
+
* caller could supply as a throwing Proxy — is wrapped in a single guarded
|
|
75
|
+
* region that degrades to `NO_DETECTION` on any unexpected error, rather than
|
|
76
|
+
* only guarding the `fileExists` probe.
|
|
77
|
+
*/
|
|
78
|
+
function detectHostRuntime(deps) {
|
|
79
|
+
try {
|
|
80
|
+
const env = deps?.env ?? process.env;
|
|
81
|
+
const fileExists = deps?.fileExists ?? ((p) => node_fs_1.default.existsSync(p));
|
|
82
|
+
for (const key of exports.CODEX_SESSION_ENV_SIGNALS) {
|
|
83
|
+
const value = env[key];
|
|
84
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
85
|
+
return { runtime: 'codex', source: 'session-env', signal: key };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const codexHome = env[exports.CODEX_CONFIG_HOME_ENV];
|
|
89
|
+
if (typeof codexHome === 'string' && codexHome.trim() !== '') {
|
|
90
|
+
try {
|
|
91
|
+
if (fileExists(node_path_1.default.join(codexHome, update_context_cjs_1.CODEX_CONFIG_MARKER))) {
|
|
92
|
+
return { runtime: 'codex', source: 'config-home', signal: exports.CODEX_CONFIG_HOME_ENV };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Swallow probe failures (EACCES etc.) and fall through to no-detection.
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return NO_DETECTION;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// A malformed `deps.env` (e.g. a throwing Proxy) must degrade like any
|
|
103
|
+
// other unreadable signal, not propagate — this function's contract is
|
|
104
|
+
// that it never throws.
|
|
105
|
+
return NO_DETECTION;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Resolve the runtime to report from init: explicit sources first, then the
|
|
110
|
+
* host-detection rung, then the 'claude' default. This is intentionally
|
|
111
|
+
* separate from resolveRuntime — only init's agent_runtime reporting call
|
|
112
|
+
* site uses this ladder; every other resolveRuntime caller is unaffected.
|
|
113
|
+
*
|
|
114
|
+
* Never throws: degrades to the 'claude' default on any unexpected error
|
|
115
|
+
* (e.g. a throwing `deps.env`), matching detectHostRuntime's no-throw
|
|
116
|
+
* contract.
|
|
117
|
+
*/
|
|
118
|
+
function resolveReportedRuntime(projectDir, deps) {
|
|
119
|
+
try {
|
|
120
|
+
return resolveReportedRuntimeUnsafe(projectDir, deps);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return 'claude';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function resolveReportedRuntimeUnsafe(projectDir, deps) {
|
|
127
|
+
const explicit = (0, runtime_slash_cjs_1.resolveExplicitRuntime)(projectDir, deps?.env ?? process.env);
|
|
128
|
+
if (explicit)
|
|
129
|
+
return explicit;
|
|
130
|
+
const detected = detectHostRuntime(deps);
|
|
131
|
+
if (detected.runtime)
|
|
132
|
+
return detected.runtime;
|
|
133
|
+
return 'claude';
|
|
134
|
+
}
|