@opengsd/gsd-core 1.9.1 → 1.11.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 +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +27 -3
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +12 -10
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +46 -0
- package/agents/gsd-planner.md +49 -54
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +26 -73
- package/bin/install.js +1272 -1238
- package/bin/lib/ui-safety-gate.cjs +2 -0
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +2 -2
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +2 -2
- package/commands/gsd/plan-phase.md +1 -1
- package/commands/gsd/quick.md +1 -1
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1009 -115
- package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
- package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
- package/gsd-core/bin/lib/api-coverage.cjs +123 -5
- package/gsd-core/bin/lib/artifacts.cjs +3 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +926 -202
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- 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-registry.cjs +608 -148
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +507 -24
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +114 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
- package/gsd-core/bin/lib/command-aliases.cjs +94 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +665 -99
- package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +76 -0
- package/gsd-core/bin/lib/config.cjs +22 -2
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +217 -40
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +239 -32
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
- 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 +145 -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 +265 -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 +173 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
- package/gsd-core/bin/lib/host-integration.cjs +13 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1325 -169
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +805 -264
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
- package/gsd-core/bin/lib/install-profiles.cjs +160 -57
- 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-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
- package/gsd-core/bin/lib/io.cjs +38 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +821 -109
- package/gsd-core/bin/lib/model-catalog.cjs +59 -1
- package/gsd-core/bin/lib/model-resolver.cjs +183 -40
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
- package/gsd-core/bin/lib/phase-id.cjs +507 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +258 -58
- package/gsd-core/bin/lib/phase.cjs +891 -156
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +86 -2
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
- package/gsd-core/bin/lib/probe-core.cjs +1 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +405 -84
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
- package/gsd-core/bin/lib/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
- package/gsd-core/bin/lib/smart-entry.cjs +154 -22
- package/gsd-core/bin/lib/state-command-router.cjs +5 -1
- package/gsd-core/bin/lib/state-document.cjs +152 -8
- package/gsd-core/bin/lib/state-transition.cjs +424 -105
- package/gsd-core/bin/lib/state.cjs +1927 -401
- package/gsd-core/bin/lib/surface.cjs +35 -10
- 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 +20 -4
- package/gsd-core/bin/lib/uat.cjs +706 -64
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
- package/gsd-core/bin/lib/unusable-input.cjs +33 -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.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +37 -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 +287 -20
- package/gsd-core/bin/lib/verify.cjs +368 -880
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
- package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
- package/gsd-core/bin/lib/workstream.cjs +8 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/debugger-techniques.md +255 -0
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/execute-mvp-tdd.md +3 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
- 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 +6 -2
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-planning-commit.md +2 -1
- package/gsd-core/references/loop-hook-dispatch.md +39 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planning-config.md +8 -6
- package/gsd-core/references/research-documentation-lookup.md +5 -3
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +8 -7
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- package/gsd-core/references/worktree-branch-check.md +2 -2
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +2 -4
- package/gsd-core/templates/state.md +4 -4
- package/gsd-core/templates/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +33 -70
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +74 -166
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +160 -95
- package/gsd-core/workflows/debug.md +16 -17
- package/gsd-core/workflows/diagnose-issues.md +56 -8
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- package/gsd-core/workflows/edit-phase.md +26 -1
- package/gsd-core/workflows/eval-review.md +3 -5
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- 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 +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +103 -187
- package/gsd-core/workflows/execute-plan.md +36 -4
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/health.md +73 -4
- package/gsd-core/workflows/help/modes/full.md +6 -1
- package/gsd-core/workflows/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +7 -6
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +35 -47
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +27 -240
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +89 -209
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +45 -159
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +55 -405
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +41 -13
- package/gsd-core/workflows/section-manifest.json +219 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings.md +66 -2
- package/gsd-core/workflows/ship.md +104 -44
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +41 -20
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +106 -13
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +53 -31
- package/gsd-core/workflows/ui-phase.md +13 -12
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +19 -8
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +17 -65
- package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
- package/hooks/dist/gsd-check-update-worker.js +64 -12
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
- package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/dist/gsd-prompt-guard.js +21 -20
- package/hooks/dist/gsd-read-injection-scanner.js +45 -24
- package/hooks/dist/gsd-statusline.js +90 -6
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- 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 +277 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +517 -0
- package/hooks/gsd-check-update-worker.js +64 -12
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +607 -26
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +45 -24
- package/hooks/gsd-statusline.js +90 -6
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +277 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +31 -10
- package/pi/gsd.cjs +71 -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 +9 -0
- package/scripts/changeset/lint.cjs +68 -6
- package/scripts/changeset/serialize.cjs +5 -1
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/ci-test-scope.cjs +19 -2
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-matrix.cjs +1 -1
- package/scripts/gen-capability-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +439 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +150 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/drift-scan.cjs +278 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
- 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-compiled-artifact-sync.cjs +6 -1
- 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-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/lint-example-parser-parity.cjs +395 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-phase-enumeration-drift.cjs +479 -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 +434 -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 +320 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +1045 -0
- package/scripts/lint-test-file-count.allowlist.json +40 -3
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +52 -6
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/state-write-path-drift-baseline.json +19 -0
- package/scripts/sync-runtime-launcher.cjs +2 -4
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +2 -2
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +2 -2
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +1 -2
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +1 -1
- 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/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 -577
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/gen-emitted-baseline.cjs +0 -145
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1050
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates the `<error_codes>` and `<repair_actions>` tables in
|
|
6
|
+
* `gsd-core/workflows/health.md` from `src/health-diagnostic.cts`'s `RULES`
|
|
7
|
+
* table (Phase 11 follow-up, #3309 "Proposed behavior": "health.md's tables
|
|
8
|
+
* are generated rather than hand-maintained, closing the 16-vs-30+
|
|
9
|
+
* documentation gap structurally").
|
|
10
|
+
*
|
|
11
|
+
* Sources:
|
|
12
|
+
* - The 31 real rules in the compiled `RULES` array
|
|
13
|
+
* (`gsd-core/bin/lib/health-diagnostic.cjs`, built from
|
|
14
|
+
* `src/health-diagnostic.cts` + `src/health-diagnostic-rules/*.cts`),
|
|
15
|
+
* each carrying a static `description`/`repairable` (see
|
|
16
|
+
* `src/health-diagnostic-types.cts`'s `Rule` interface).
|
|
17
|
+
* - `PRECHECK_CODES` below — E001, E010, I010 — the three diagnostics
|
|
18
|
+
* `cmdValidateHealth` (`src/verify.cts`) emits as pre-checks OUTSIDE the
|
|
19
|
+
* rule table entirely (ADR-3180 §8.2 rule 4, "no precedence system" —
|
|
20
|
+
* see `.gsd/phase/refactor-3309-health-diagnostic-rule-table/40-design.md`,
|
|
21
|
+
* "Two guards that stay OUTSIDE the rule table entirely"). These will
|
|
22
|
+
* never appear in `RULES`, so they are a small, static, clearly-labeled
|
|
23
|
+
* list merged in here instead.
|
|
24
|
+
* - `REMEDY_ACTION_METADATA` below — the Effect/Risk prose for each of the
|
|
25
|
+
* 6 real repair actions (`REMEDY_ACTION`, excluding `ADVISE`, which never
|
|
26
|
+
* acts). Static because the compiled module carries no Effect/Risk text
|
|
27
|
+
* of its own — only the action identifier.
|
|
28
|
+
*
|
|
29
|
+
* Deliberately EXCLUDED from the generated `<error_codes>` table: `W025`
|
|
30
|
+
* (the `workflow.use_worktrees`/`dispatch.isolation` check, #2486). It is a
|
|
31
|
+
* workflow-layer diagnostic emitted directly by this same file's own
|
|
32
|
+
* `run_health_check` step (a bash block in `health.md` itself), never by
|
|
33
|
+
* `cmdValidateHealth`/`RULES` — it has no `Rule` entry and is not one of the
|
|
34
|
+
* three pre-checks above. It stays fully documented in prose at its own step
|
|
35
|
+
* (`<step name="run_health_check">`), which is the authoritative, more
|
|
36
|
+
* detailed source `<error_codes>` used to merely summarize; dropping the
|
|
37
|
+
* redundant table row is not a loss of information, and folding it back in
|
|
38
|
+
* here would require this generator to parse bash, which it does not do.
|
|
39
|
+
* Same reasoning for `I002` (stale Windows task-directory cleanup,
|
|
40
|
+
* `<stale_task_cleanup>` step) — it was never part of the `<error_codes>`
|
|
41
|
+
* tagged region even before this generator existed.
|
|
42
|
+
*
|
|
43
|
+
* Usage:
|
|
44
|
+
* node scripts/gen-health-docs.cjs # print both tables to stdout
|
|
45
|
+
* node scripts/gen-health-docs.cjs --write # rewrite the tagged regions in health.md
|
|
46
|
+
* node scripts/gen-health-docs.cjs --check # exit 1 if either region is stale
|
|
47
|
+
* node scripts/gen-health-docs.cjs --write --target <path> # test-only: target a fixture file
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
const fs = require('node:fs');
|
|
51
|
+
const path = require('node:path');
|
|
52
|
+
|
|
53
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
54
|
+
|
|
55
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
56
|
+
const HEALTH_MD_REL = 'gsd-core/workflows/health.md';
|
|
57
|
+
const HEALTH_MD_PATH = path.join(ROOT, HEALTH_MD_REL);
|
|
58
|
+
const COMPILED_MODULE_REL = 'gsd-core/bin/lib/health-diagnostic.cjs';
|
|
59
|
+
const COMPILED_MODULE_PATH = path.join(ROOT, COMPILED_MODULE_REL);
|
|
60
|
+
|
|
61
|
+
const ERROR_CODES_START = '<error_codes>';
|
|
62
|
+
const ERROR_CODES_END = '</error_codes>';
|
|
63
|
+
const REPAIR_ACTIONS_START = '<repair_actions>';
|
|
64
|
+
const REPAIR_ACTIONS_END = '</repair_actions>';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The 3 pre-check diagnostics `cmdValidateHealth` emits OUTSIDE the rule
|
|
68
|
+
* table (see module header). All three are non-repairable safety rails, not
|
|
69
|
+
* `.planning/` findings a remedy could act on.
|
|
70
|
+
*/
|
|
71
|
+
const PRECHECK_CODES = [
|
|
72
|
+
{
|
|
73
|
+
code: 'E001',
|
|
74
|
+
severity: 'error',
|
|
75
|
+
description: '.planning/ directory not found',
|
|
76
|
+
repairable: false,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
code: 'E010',
|
|
80
|
+
severity: 'error',
|
|
81
|
+
description: "CWD resolves to the user's home directory — health check would target the wrong .planning/",
|
|
82
|
+
repairable: false,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
code: 'I010',
|
|
86
|
+
severity: 'info',
|
|
87
|
+
description: 'Resolved CWD reported alongside the E010 home-directory guard',
|
|
88
|
+
repairable: false,
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Effect/Risk prose per real `REMEDY_ACTION` (everything except `ADVISE`,
|
|
94
|
+
* which never acts and has no row in `<repair_actions>`). Text for the 5
|
|
95
|
+
* actions the hand-written table already documented is reused VERBATIM;
|
|
96
|
+
* `addAiIntegrationPhaseKey` is new — #3309 itself notes it was "live in
|
|
97
|
+
* code, missing from docs" (mirrors `addNyquistKey`, its structural sibling:
|
|
98
|
+
* same shape, one config key each).
|
|
99
|
+
*/
|
|
100
|
+
const REMEDY_ACTION_METADATA = new Map([
|
|
101
|
+
['createConfig', { effect: 'Create config.json with defaults', risk: 'None' }],
|
|
102
|
+
['resetConfig', { effect: 'Delete + recreate config.json', risk: 'Loses custom settings' }],
|
|
103
|
+
[
|
|
104
|
+
'regenerateState',
|
|
105
|
+
{
|
|
106
|
+
effect: 'Create STATE.md from ROADMAP structure when it is missing',
|
|
107
|
+
risk: 'Loses session history',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
[
|
|
111
|
+
'addNyquistKey',
|
|
112
|
+
{ effect: 'Add workflow.nyquist_validation: true to config.json', risk: 'None — matches existing default' },
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
'addAiIntegrationPhaseKey',
|
|
116
|
+
{ effect: 'Add workflow.ai_integration_phase: true to config.json', risk: 'None — matches existing default' },
|
|
117
|
+
],
|
|
118
|
+
[
|
|
119
|
+
'backfillMilestones',
|
|
120
|
+
{
|
|
121
|
+
effect: 'Synthesize missing MILESTONES.md entries from `.planning/milestones/vX.Y-ROADMAP.md` snapshots',
|
|
122
|
+
risk: 'None — additive only; triggered by `--backfill` flag',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
/** Order the Effect/Risk table renders in — matches `REMEDY_ACTION`'s own declaration order. */
|
|
128
|
+
const REMEDY_ACTION_ORDER = [
|
|
129
|
+
'createConfig',
|
|
130
|
+
'resetConfig',
|
|
131
|
+
'regenerateState',
|
|
132
|
+
'addNyquistKey',
|
|
133
|
+
'addAiIntegrationPhaseKey',
|
|
134
|
+
'backfillMilestones',
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Per-code override for the "Repairable" cell's display text, for codes
|
|
139
|
+
* whose remedy is conditional on a flag the plain `Yes`/`No` can't express
|
|
140
|
+
* (mirrors the hand-written table's pre-existing `W018` row: `Yes (--backfill)`).
|
|
141
|
+
*/
|
|
142
|
+
const REPAIRABLE_DISPLAY_OVERRIDE = new Map([['W018', 'Yes (`--backfill`)']]);
|
|
143
|
+
|
|
144
|
+
const STATIC_NOT_REPAIRABLE_BULLETS = [
|
|
145
|
+
'PROJECT.md, ROADMAP.md content',
|
|
146
|
+
'Phase directory renaming',
|
|
147
|
+
'Orphaned plan cleanup',
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
const FOOTNOTE_PARAGRAPH =
|
|
151
|
+
'Note: this table is **generated** — do not hand-edit it. It is produced by ' +
|
|
152
|
+
'`node scripts/gen-health-docs.cjs --write` from `src/health-diagnostic.cts`\'s `RULES` table ' +
|
|
153
|
+
'(31 rules as of #3309, each carrying a static `description`/`repairable` on its `Rule` entry — ' +
|
|
154
|
+
'see `src/health-diagnostic-types.cts`) plus the 3 pre-checks that stay outside the rule table by ' +
|
|
155
|
+
'design (`E001`, `E010`, `I010` — safety rails in `cmdValidateHealth`, `src/verify.cts`, never ' +
|
|
156
|
+
'`.planning/` findings). `scripts/lint-health-diagnostic-rule-table.cjs` already enforces the 1:1 ' +
|
|
157
|
+
'code invariant this table depends on (no duplicate codes; severity is always a `Rule` property, ' +
|
|
158
|
+
'never set per emit call) — before assigning a new code, add a `Rule` entry under ' +
|
|
159
|
+
'`src/health-diagnostic-rules/` (its `code` is simply the next free number the lint guard has not ' +
|
|
160
|
+
'yet seen) and run `node scripts/gen-health-docs.cjs --write` to regenerate this table; ' +
|
|
161
|
+
'`npm run lint:generated-sync` fails if it drifts. `W025` (the `workflow.use_worktrees`/' +
|
|
162
|
+
'`dispatch.isolation` check, #2486) is a workflow-layer diagnostic emitted directly by this file\'s ' +
|
|
163
|
+
'own `run_health_check` step, not by `cmdValidateHealth` — it stays documented in that step, not in ' +
|
|
164
|
+
'this generated table.';
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Load the compiled health-diagnostic module. Throws a clear ExitError (not
|
|
168
|
+
* a raw MODULE_NOT_FOUND) if `npm run build:lib` has not run — mirrors
|
|
169
|
+
* `scripts/lint-health-diagnostic-rule-table.cjs`'s `loadCompiledModule`.
|
|
170
|
+
*/
|
|
171
|
+
function loadCompiledModule(compiledPath = COMPILED_MODULE_PATH) {
|
|
172
|
+
if (!fs.existsSync(compiledPath)) {
|
|
173
|
+
throw new ExitError(
|
|
174
|
+
2,
|
|
175
|
+
`gen-health-docs: compiled artifact not found at ${COMPILED_MODULE_REL}.\n` +
|
|
176
|
+
'Run `npm run build:lib` first.',
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return require(compiledPath);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Sort order for the `<error_codes>` table: E-codes, then W-codes
|
|
184
|
+
* numerically, then I-codes — matching the hand-written table's pre-existing
|
|
185
|
+
* order. NOT insertion order from `RULES` (which is grouped by
|
|
186
|
+
* subject-area file, not sorted by code).
|
|
187
|
+
*/
|
|
188
|
+
const PREFIX_RANK = { E: 0, W: 1, I: 2 };
|
|
189
|
+
|
|
190
|
+
function parseCode(code) {
|
|
191
|
+
const m = code.match(/^([A-Z]+)(\d+)$/);
|
|
192
|
+
if (!m) throw new Error(`gen-health-docs: unparseable diagnostic code "${code}"`);
|
|
193
|
+
return { prefix: m[1], number: Number(m[2]) };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function compareCodes(a, b) {
|
|
197
|
+
const pa = parseCode(a.code);
|
|
198
|
+
const pb = parseCode(b.code);
|
|
199
|
+
const rankA = PREFIX_RANK[pa.prefix] ?? 99;
|
|
200
|
+
const rankB = PREFIX_RANK[pb.prefix] ?? 99;
|
|
201
|
+
if (rankA !== rankB) return rankA - rankB;
|
|
202
|
+
return pa.number - pb.number;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Combine the 31 real rules + the 3 static pre-checks into one sorted row
|
|
207
|
+
* list for the `<error_codes>` table.
|
|
208
|
+
*
|
|
209
|
+
* @param {Array<{code: string, severity: string, description: string, repairable: boolean}>} rules
|
|
210
|
+
*/
|
|
211
|
+
function buildErrorCodeRows(rules) {
|
|
212
|
+
const seen = new Set();
|
|
213
|
+
const rows = [];
|
|
214
|
+
for (const entry of [...rules, ...PRECHECK_CODES]) {
|
|
215
|
+
if (seen.has(entry.code)) {
|
|
216
|
+
throw new Error(`gen-health-docs: duplicate diagnostic code "${entry.code}" across RULES + PRECHECK_CODES`);
|
|
217
|
+
}
|
|
218
|
+
seen.add(entry.code);
|
|
219
|
+
rows.push(entry);
|
|
220
|
+
}
|
|
221
|
+
rows.sort(compareCodes);
|
|
222
|
+
return rows;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Escape a cell's markdown-table-hostile characters (mirrors gen-adr-index.cjs's `cellText`). */
|
|
226
|
+
function cellText(text) {
|
|
227
|
+
return String(text)
|
|
228
|
+
.replace(/\\/g, '\\\\')
|
|
229
|
+
.replace(/\|/g, '\\|')
|
|
230
|
+
.replace(/</g, '<')
|
|
231
|
+
.replace(/>/g, '>')
|
|
232
|
+
.replace(/\r?\n/g, ' ')
|
|
233
|
+
.trim();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function repairableCell(row) {
|
|
237
|
+
if (REPAIRABLE_DISPLAY_OVERRIDE.has(row.code)) return REPAIRABLE_DISPLAY_OVERRIDE.get(row.code);
|
|
238
|
+
return row.repairable ? 'Yes' : 'No';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function renderErrorCodesRegion(rules) {
|
|
242
|
+
const rows = buildErrorCodeRows(rules);
|
|
243
|
+
const lines = ['', '| Code | Severity | Description | Repairable |', '|------|----------|-------------|------------|'];
|
|
244
|
+
for (const row of rows) {
|
|
245
|
+
lines.push(`| ${row.code} | ${row.severity} | ${cellText(row.description)} | ${repairableCell(row)} |`);
|
|
246
|
+
}
|
|
247
|
+
lines.push('', FOOTNOTE_PARAGRAPH, '');
|
|
248
|
+
return lines.join('\n');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function renderRepairActionsRegion() {
|
|
252
|
+
const lines = ['', '| Action | Effect | Risk |', '|--------|--------|------|'];
|
|
253
|
+
for (const action of REMEDY_ACTION_ORDER) {
|
|
254
|
+
const meta = REMEDY_ACTION_METADATA.get(action);
|
|
255
|
+
if (!meta) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`gen-health-docs: no Effect/Risk metadata registered for repair action "${action}" — add an entry to REMEDY_ACTION_METADATA.`,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
lines.push(`| ${action} | ${meta.effect} | ${meta.risk} |`);
|
|
261
|
+
}
|
|
262
|
+
lines.push('', '**Not repairable (too risky):**');
|
|
263
|
+
for (const bullet of STATIC_NOT_REPAIRABLE_BULLETS) lines.push(`- ${bullet}`);
|
|
264
|
+
lines.push('');
|
|
265
|
+
return lines.join('\n');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Splice `newInner` between `${startTag}`/`${endTag}` inside `text`. Throws
|
|
270
|
+
* if either tag is missing, or if the tags appear more than once (this
|
|
271
|
+
* generator only ever targets the FIRST occurrence pair, and a duplicate
|
|
272
|
+
* tag anywhere in the file would silently corrupt the splice).
|
|
273
|
+
*/
|
|
274
|
+
function spliceRegion(text, startTag, endTag, newInner) {
|
|
275
|
+
const startIdx = text.indexOf(startTag);
|
|
276
|
+
const endIdx = text.indexOf(endTag);
|
|
277
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
278
|
+
throw new ExitError(
|
|
279
|
+
1,
|
|
280
|
+
`gen-health-docs: ${HEALTH_MD_REL} is missing the ${startTag}/${endTag} tags.`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (text.indexOf(startTag, startIdx + 1) !== -1 || text.indexOf(endTag, endIdx + 1) !== -1) {
|
|
284
|
+
throw new ExitError(1, `gen-health-docs: ${HEALTH_MD_REL} has more than one ${startTag}/${endTag} pair.`);
|
|
285
|
+
}
|
|
286
|
+
const before = text.slice(0, startIdx + startTag.length);
|
|
287
|
+
const after = text.slice(endIdx);
|
|
288
|
+
return `${before}${newInner}\n${after}`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Regenerate `health.md`'s full text from `rules` (the compiled `RULES`
|
|
293
|
+
* array) and the current on-disk `health.md` content.
|
|
294
|
+
*/
|
|
295
|
+
function regenerateHealthMd(rules, currentText) {
|
|
296
|
+
let out = spliceRegion(currentText, ERROR_CODES_START, ERROR_CODES_END, renderErrorCodesRegion(rules));
|
|
297
|
+
out = spliceRegion(out, REPAIR_ACTIONS_START, REPAIR_ACTIONS_END, renderRepairActionsRegion());
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @param {string[]} argv
|
|
303
|
+
* @returns {{write: boolean, check: boolean, targetPath: string|null}}
|
|
304
|
+
*/
|
|
305
|
+
function parseArgs(argv) {
|
|
306
|
+
const opts = { write: false, check: false, targetPath: null };
|
|
307
|
+
for (let i = 0; i < argv.length; i++) {
|
|
308
|
+
const arg = argv[i];
|
|
309
|
+
if (arg === '--write') opts.write = true;
|
|
310
|
+
else if (arg === '--check') opts.check = true;
|
|
311
|
+
else if (arg === '--target') {
|
|
312
|
+
const value = argv[i + 1];
|
|
313
|
+
if (value === undefined) throw new ExitError(1, '--target requires a path argument.');
|
|
314
|
+
opts.targetPath = value;
|
|
315
|
+
i++;
|
|
316
|
+
} else {
|
|
317
|
+
throw new ExitError(1, `unknown flag: ${arg}\nRecognized flags: --write, --check, --target <path>.`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return opts;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function main() {
|
|
324
|
+
const { write, check, targetPath } = parseArgs(process.argv.slice(2));
|
|
325
|
+
const { RULES } = loadCompiledModule();
|
|
326
|
+
|
|
327
|
+
// `--target` overrides the real committed health.md path, exclusively for
|
|
328
|
+
// test isolation (mirrors gen-section-manifest.cjs's `--manifest-path`
|
|
329
|
+
// override) — no production caller ever passes it.
|
|
330
|
+
const resolvedPath = targetPath ? path.resolve(targetPath) : HEALTH_MD_PATH;
|
|
331
|
+
const displayPath = targetPath ? targetPath : HEALTH_MD_REL;
|
|
332
|
+
|
|
333
|
+
const currentText = fs.existsSync(resolvedPath) ? fs.readFileSync(resolvedPath, 'utf8') : null;
|
|
334
|
+
if (currentText === null) {
|
|
335
|
+
throw new ExitError(1, `gen-health-docs: ${displayPath} not found.`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const expected = regenerateHealthMd(RULES, currentText);
|
|
339
|
+
|
|
340
|
+
if (write) {
|
|
341
|
+
fs.writeFileSync(resolvedPath, expected, 'utf8');
|
|
342
|
+
process.stdout.write(
|
|
343
|
+
`Wrote ${displayPath} — ${RULES.length + PRECHECK_CODES.length} error/warning/info code(s), ` +
|
|
344
|
+
`${REMEDY_ACTION_ORDER.length} repair action(s).\n`,
|
|
345
|
+
);
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (check) {
|
|
350
|
+
if (expected !== currentText) {
|
|
351
|
+
process.stderr.write(
|
|
352
|
+
`${displayPath} is stale — its <error_codes>/<repair_actions> tables do not match ` +
|
|
353
|
+
"src/health-diagnostic.cts's RULES table.\nRun:\n node scripts/gen-health-docs.cjs --write\n\n",
|
|
354
|
+
);
|
|
355
|
+
throw new ExitError(1);
|
|
356
|
+
}
|
|
357
|
+
process.stdout.write(
|
|
358
|
+
`${displayPath} is up to date (${RULES.length + PRECHECK_CODES.length} codes, ${REMEDY_ACTION_ORDER.length} repair actions).\n`,
|
|
359
|
+
);
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
process.stdout.write(renderErrorCodesRegion(RULES) + '\n\n' + renderRepairActionsRegion() + '\n');
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Guarded: requiring this module (the test suite imports the pure render
|
|
368
|
+
// functions directly) must not also run the generator as a side effect.
|
|
369
|
+
if (require.main === module) runMain(main);
|
|
370
|
+
|
|
371
|
+
module.exports = {
|
|
372
|
+
loadCompiledModule,
|
|
373
|
+
buildErrorCodeRows,
|
|
374
|
+
renderErrorCodesRegion,
|
|
375
|
+
renderRepairActionsRegion,
|
|
376
|
+
regenerateHealthMd,
|
|
377
|
+
spliceRegion,
|
|
378
|
+
compareCodes,
|
|
379
|
+
parseCode,
|
|
380
|
+
PRECHECK_CODES,
|
|
381
|
+
REMEDY_ACTION_METADATA,
|
|
382
|
+
REMEDY_ACTION_ORDER,
|
|
383
|
+
REPAIRABLE_DISPLAY_OVERRIDE,
|
|
384
|
+
HEALTH_MD_PATH,
|
|
385
|
+
COMPILED_MODULE_PATH,
|
|
386
|
+
ERROR_CODES_START,
|
|
387
|
+
ERROR_CODES_END,
|
|
388
|
+
REPAIR_ACTIONS_START,
|
|
389
|
+
REPAIR_ACTIONS_END,
|
|
390
|
+
};
|
|
@@ -60,14 +60,150 @@ const FAMILIES = [
|
|
|
60
60
|
},
|
|
61
61
|
];
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* One-level-nested families (#2996, epic #1671 Phase 6.5).
|
|
65
|
+
*
|
|
66
|
+
* `buildManifest`'s flat `readdirSync` + `isFile()` walk cannot see a workflow's
|
|
67
|
+
* own sub-files, so `gsd-core/workflows/<wf>/steps/*.md` (the fragment tree
|
|
68
|
+
* extracted by Phases 6.1-6.3) and `gsd-core/workflows/<wf>/modes/*.md` (the
|
|
69
|
+
* #717 progressive-disclosure pattern) shipped invisible to both the manifest
|
|
70
|
+
* and `docs/INVENTORY.md` — exactly the `DEFECT.INVENTORY-DRIFT` class.
|
|
71
|
+
*
|
|
72
|
+
* Keyed by `<parent>/<subdir>/<file>` rather than a bare basename ON PURPOSE:
|
|
73
|
+
* two workflows may each own a `regression-gate.md`, and a step file may share a
|
|
74
|
+
* name with a top-level workflow. A basename key would let one silently
|
|
75
|
+
* overwrite the other, and because the manifest is compared by JSON equality a
|
|
76
|
+
* collision would read as "up to date".
|
|
77
|
+
*
|
|
78
|
+
* Recursion is bounded at exactly one level, by named subdirectory. It is not a
|
|
79
|
+
* general recursive walk.
|
|
80
|
+
*/
|
|
81
|
+
const NESTED_FAMILIES = [
|
|
82
|
+
{
|
|
83
|
+
name: 'workflow_modes',
|
|
84
|
+
root: path.join(ROOT, 'gsd-core', 'workflows'),
|
|
85
|
+
subdir: 'modes',
|
|
86
|
+
filter: (f) => f.endsWith('.md'),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'workflow_steps',
|
|
90
|
+
root: path.join(ROOT, 'gsd-core', 'workflows'),
|
|
91
|
+
subdir: 'steps',
|
|
92
|
+
filter: (f) => f.endsWith('.md'),
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Collect `<root>/<parent>/<subdir>/<file>` entries as POSIX-relative keys.
|
|
98
|
+
*
|
|
99
|
+
* A parent that has no such subdirectory contributes nothing, and an EMPTY
|
|
100
|
+
* subdirectory contributes nothing — never an empty-array key, which would be a
|
|
101
|
+
* committed diff that signals nothing. `statSync().isDirectory()` is checked
|
|
102
|
+
* before every `readdirSync` so a plain FILE named `steps` cannot throw.
|
|
103
|
+
*/
|
|
104
|
+
/**
|
|
105
|
+
* `fs.statSync` throws on a dangling symlink and on an EACCES-denied path. An
|
|
106
|
+
* entry we cannot stat is, for inventory purposes, not a countable file — the
|
|
107
|
+
* same disposition as "not a directory" below. Swallowing the throw here keeps
|
|
108
|
+
* one unreadable entry from taking down `--check` for the entire repo, which is
|
|
109
|
+
* a manifest generator's worst failure mode: it turns a local filesystem oddity
|
|
110
|
+
* into a red gate on every PR.
|
|
111
|
+
*/
|
|
112
|
+
function statOrNull(p) {
|
|
113
|
+
try {
|
|
114
|
+
return fs.statSync(p);
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Collect `<dir>/<subdir>/<file>` entries as `<subdir>/<file>` keys — ONE level of
|
|
122
|
+
* subdirectory beneath `dir` itself, where the subdirectory's NAME is the thing being
|
|
123
|
+
* collected (unlike `collectNested`, there is no fixed subdir name to look for; every
|
|
124
|
+
* child directory of `dir` is scanned). This is what makes `gsd-core/bin/lib/<subdir>/*.cjs`
|
|
125
|
+
* (e.g. `health-diagnostic-rules/`, `installer-migrations/`, `host-integration-adapters/`,
|
|
126
|
+
* `observability/`) visible to the `cli_modules` family, mirroring the shape
|
|
127
|
+
* `docs/INVENTORY.md`'s CLI Modules table already uses for these files.
|
|
128
|
+
*
|
|
129
|
+
* Same defensive `statOrNull`-based style as `collectNested`: a stat/readdir failure on
|
|
130
|
+
* one entry is swallowed rather than thrown, so one unreadable subdirectory cannot take
|
|
131
|
+
* down `--check` for the whole repo.
|
|
132
|
+
*/
|
|
133
|
+
function collectOneLevelSubdirs({ dir, filter }) {
|
|
134
|
+
if (!fs.existsSync(dir)) return [];
|
|
135
|
+
const out = [];
|
|
136
|
+
let children;
|
|
137
|
+
try {
|
|
138
|
+
children = fs.readdirSync(dir);
|
|
139
|
+
} catch {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
for (const child of children) {
|
|
143
|
+
const childStat = statOrNull(path.join(dir, child));
|
|
144
|
+
if (!childStat || !childStat.isDirectory()) continue;
|
|
145
|
+
const subdirPath = path.join(dir, child);
|
|
146
|
+
let files;
|
|
147
|
+
try {
|
|
148
|
+
files = fs.readdirSync(subdirPath);
|
|
149
|
+
} catch {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
for (const file of files) {
|
|
153
|
+
const fileStat = statOrNull(path.join(subdirPath, file));
|
|
154
|
+
if (!fileStat || !fileStat.isFile() || !filter(file)) continue;
|
|
155
|
+
out.push([child, file].join('/'));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return out.sort();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function collectNested({ root, subdir, filter }) {
|
|
162
|
+
if (!fs.existsSync(root)) return [];
|
|
163
|
+
const out = [];
|
|
164
|
+
let parents;
|
|
165
|
+
try {
|
|
166
|
+
parents = fs.readdirSync(root);
|
|
167
|
+
} catch {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
for (const parent of parents) {
|
|
171
|
+
const parentStat = statOrNull(path.join(root, parent));
|
|
172
|
+
if (!parentStat || !parentStat.isDirectory()) continue;
|
|
173
|
+
const nestedDir = path.join(root, parent, subdir);
|
|
174
|
+
const nestedStat = statOrNull(nestedDir);
|
|
175
|
+
if (!nestedStat || !nestedStat.isDirectory()) continue;
|
|
176
|
+
let files;
|
|
177
|
+
try {
|
|
178
|
+
files = fs.readdirSync(nestedDir);
|
|
179
|
+
} catch {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
for (const file of files) {
|
|
183
|
+
const fileStat = statOrNull(path.join(nestedDir, file));
|
|
184
|
+
if (!fileStat || !fileStat.isFile() || !filter(file)) continue;
|
|
185
|
+
out.push([parent, subdir, file].join('/'));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return out.sort();
|
|
189
|
+
}
|
|
190
|
+
|
|
63
191
|
function buildManifest() {
|
|
64
192
|
const manifest = { families: {} };
|
|
65
193
|
for (const { name, dir, filter, toName } of FAMILIES) {
|
|
66
|
-
|
|
194
|
+
const flat = fs
|
|
67
195
|
.readdirSync(dir)
|
|
68
196
|
.filter((f) => fs.statSync(path.join(dir, f)).isFile() && filter(f))
|
|
69
|
-
.map(toName)
|
|
70
|
-
|
|
197
|
+
.map(toName);
|
|
198
|
+
// `cli_modules` also ships subdirectory modules (`health-diagnostic-rules/`,
|
|
199
|
+
// `installer-migrations/`, `host-integration-adapters/`, `observability/`) invisible to
|
|
200
|
+
// the flat readdirSync above; merge them into the SAME sorted array, matching the single
|
|
201
|
+
// "CLI Modules" table shape docs/INVENTORY.md already uses (#3309).
|
|
202
|
+
const nested = name === 'cli_modules' ? collectOneLevelSubdirs({ dir, filter }) : [];
|
|
203
|
+
manifest.families[name] = [...flat, ...nested].sort();
|
|
204
|
+
}
|
|
205
|
+
for (const family of NESTED_FAMILIES) {
|
|
206
|
+
manifest.families[family.name] = collectNested(family);
|
|
71
207
|
}
|
|
72
208
|
return manifest;
|
|
73
209
|
}
|
|
@@ -109,4 +245,14 @@ function main() {
|
|
|
109
245
|
}
|
|
110
246
|
}
|
|
111
247
|
|
|
112
|
-
|
|
248
|
+
/* c8 ignore next 3 -- CLI entry guard; this repo measures coverage with c8, which does not honor istanbul pragmas */
|
|
249
|
+
if (require.main === module) {
|
|
250
|
+
runMain(main);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Single source of truth for the family tables (#2996). `tests/inventory-manifest-sync.test.cjs`
|
|
254
|
+
// previously carried its own duplicate copy of FAMILIES, which is the
|
|
255
|
+
// `DEFECT.GENERATIVE-FIX` divergence class: adding a family here while the test kept
|
|
256
|
+
// its own list meant the test silently verified fewer families than shipped, and still
|
|
257
|
+
// passed. The test now imports these, so the two surfaces cannot drift.
|
|
258
|
+
module.exports = { FAMILIES, NESTED_FAMILIES, collectNested, collectOneLevelSubdirs, buildManifest };
|
|
@@ -20,6 +20,8 @@ const fs = require('node:fs');
|
|
|
20
20
|
const path = require('node:path');
|
|
21
21
|
|
|
22
22
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
23
|
+
const { escapeRegex: escapeRegExp } = require('../gsd-core/bin/lib/pattern.cjs');
|
|
24
|
+
const { normalizeEol } = require('../gsd-core/bin/lib/text-lines.cjs');
|
|
23
25
|
|
|
24
26
|
const ROOT = path.resolve(__dirname, '..');
|
|
25
27
|
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
|
|
@@ -191,13 +193,6 @@ function parseLoopHostBlock(content, fileName) {
|
|
|
191
193
|
* @param {string} fileName For error messages
|
|
192
194
|
* @returns {string[]} Array of error strings; empty = OK
|
|
193
195
|
*/
|
|
194
|
-
/**
|
|
195
|
-
* Escape a string for literal use in a RegExp.
|
|
196
|
-
*/
|
|
197
|
-
function escapeRegExp(s) {
|
|
198
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
196
|
function crossCheckRoles(content, agentRoles, fileName) {
|
|
202
197
|
const errors = [];
|
|
203
198
|
for (const role of agentRoles) {
|
|
@@ -375,21 +370,6 @@ function serializeContract(contract) {
|
|
|
375
370
|
return lines.join('\n');
|
|
376
371
|
}
|
|
377
372
|
|
|
378
|
-
// ─── --check diff helper ──────────────────────────────────────────────────────
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Normalize line endings to LF for CRLF-agnostic comparison.
|
|
382
|
-
* FIX 4: The serializer has no nondeterministic content (no timestamp), so
|
|
383
|
-
* the generated-by-line stripping that was here has been removed — full content
|
|
384
|
-
* comparison is now used so header drift is caught by --check.
|
|
385
|
-
*
|
|
386
|
-
* @param {string} content
|
|
387
|
-
* @returns {string}
|
|
388
|
-
*/
|
|
389
|
-
function normalizeLineEndings(content) {
|
|
390
|
-
return content.replace(/\r/g, '');
|
|
391
|
-
}
|
|
392
|
-
|
|
393
373
|
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
394
374
|
|
|
395
375
|
function main() {
|
|
@@ -415,7 +395,7 @@ function main() {
|
|
|
415
395
|
|
|
416
396
|
const committed = fs.readFileSync(CONTRACT_PATH, 'utf8');
|
|
417
397
|
// FIX 4: Compare full content (no generated-by stripping) so header drift is caught.
|
|
418
|
-
if (
|
|
398
|
+
if (normalizeEol(committed) !== normalizeEol(live)) {
|
|
419
399
|
process.stderr.write(
|
|
420
400
|
'gsd-core/bin/lib/loop-host-contract.cjs is stale. Run:\n' +
|
|
421
401
|
' node scripts/gen-loop-host-contract.cjs --write\n',
|
|
@@ -509,7 +489,7 @@ module.exports = {
|
|
|
509
489
|
assertPointsCoverage,
|
|
510
490
|
buildContract,
|
|
511
491
|
serializeContract,
|
|
512
|
-
normalizeLineEndings,
|
|
492
|
+
normalizeLineEndings: normalizeEol,
|
|
513
493
|
STEP_WORKFLOWS,
|
|
514
494
|
HOST_LOOP_FILES,
|
|
515
495
|
CANONICAL_POINTS,
|