@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
|
@@ -23,7 +23,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
23
23
|
const node_path_1 = __importDefault(require("node:path"));
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
25
|
const phaseIdModule = require("./phase-id.cjs");
|
|
26
|
-
const { comparePhaseNum } = phaseIdModule;
|
|
26
|
+
const { comparePhaseNum, scopeToPhase } = phaseIdModule;
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
28
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
29
29
|
const { findContextMdIn } = planningWorkspace;
|
|
@@ -72,17 +72,30 @@ function extractOneLinerFromBody(content) {
|
|
|
72
72
|
if (!content)
|
|
73
73
|
return null;
|
|
74
74
|
const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
75
|
-
const body = normalized.replace(/^---\n[\s\S]*?\n---\n*/, '');
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
const body = normalized.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, '');
|
|
76
|
+
// #3170: anchor to a summary-shaped heading (Summary / Overview /
|
|
77
|
+
// Accomplishments) so an incidental first heading (a rule list, task
|
|
78
|
+
// breakdown, deviation note) does not contribute its first bold run as the
|
|
79
|
+
// deliverable one-liner. Iterate headings in document order and extract from
|
|
80
|
+
// the first summary-shaped one that has a bold run; fall back to null (not the
|
|
81
|
+
// wrong text) when no such heading exists.
|
|
82
|
+
const headingRe = /^#+\s*([^\n]*)\n+\*\*([^*\n]+)\*\*([^\n]*)/gm;
|
|
83
|
+
let match;
|
|
84
|
+
while ((match = headingRe.exec(body)) !== null) {
|
|
85
|
+
if (!/summary|overview|accomplish/i.test(match[1]))
|
|
86
|
+
continue;
|
|
87
|
+
const boldInner = match[2].trim();
|
|
88
|
+
const afterBold = match[3];
|
|
89
|
+
if (/:\s*$/.test(boldInner)) {
|
|
90
|
+
const prose = afterBold.trim();
|
|
91
|
+
if (prose.length > 0)
|
|
92
|
+
return prose;
|
|
93
|
+
}
|
|
94
|
+
else if (boldInner.length > 0) {
|
|
95
|
+
return boldInner;
|
|
96
|
+
}
|
|
84
97
|
}
|
|
85
|
-
return
|
|
98
|
+
return null;
|
|
86
99
|
}
|
|
87
100
|
// ─── Misc utilities ───────────────────────────────────────────────────────────
|
|
88
101
|
function pathExistsInternal(cwd, targetPath) {
|
|
@@ -98,29 +111,119 @@ function pathExistsInternal(cwd, targetPath) {
|
|
|
98
111
|
function generateSlugInternal(text) {
|
|
99
112
|
if (!text)
|
|
100
113
|
return null;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return
|
|
114
|
+
// #2849: strip leading/trailing hyphens AFTER truncation, not only before.
|
|
115
|
+
// .substring(0, 60) can land on a separator, re-introducing a trailing hyphen
|
|
116
|
+
// the strip step exists to prevent. Truncation cannot add a leading hyphen, so
|
|
117
|
+
// running the full ^-+|-+$ pass last is equivalent for leading hyphens and
|
|
118
|
+
// fixes the trailing-hyphen-after-truncation case.
|
|
119
|
+
return transliterateForSlug(text).replace(/[^a-z0-9]+/g, '-').substring(0, 60).replace(/^-+|-+$/g, '');
|
|
107
120
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
// ─── Transliteration (#2848) ─────────────────────────────────────────────────
|
|
122
|
+
//
|
|
123
|
+
// Non-Latin titles used to reduce to an empty slug: the `[^a-z0-9]+` strip
|
|
124
|
+
// removed every character of an all-Cyrillic title and the hyphen cleanup left
|
|
125
|
+
// "". Callers then created unnamed phase directories (`01-`) and empty
|
|
126
|
+
// `milestone_slug` init JSON. The fix transliterates Cyrillic to ASCII BEFORE
|
|
127
|
+
// the existing ASCII filter, so a non-Latin title yields a usable ASCII slug
|
|
128
|
+
// while Latin-script text (which hits zero map entries) is byte-for-byte
|
|
129
|
+
// unchanged — the negative control is satisfied by construction.
|
|
130
|
+
//
|
|
131
|
+
// Multi-letter mappings (ж→zh, ч→ch, ш→sh, щ→sch, ю→yu, я→ya) are applied as a
|
|
132
|
+
// single pass; soft/hard signs (ъ, ь) drop to nothing rather than a hyphen.
|
|
133
|
+
// Scope is Cyrillic (Russian + the reported Ukrainian/Belarusian extras
|
|
134
|
+
// і ї є ґ ў) per the issue's confirmed-working patch. CJK and other
|
|
135
|
+
// non-transliterated scripts keep the existing strip-to-ASCII behavior.
|
|
136
|
+
const CYRILLIC_TRANSLITERATION = {
|
|
137
|
+
// multi-letter first (longest-match-safe within a single pass via ordered keys)
|
|
138
|
+
а: 'a', б: 'b', в: 'v', г: 'g', д: 'd', е: 'e', ё: 'e', ж: 'zh',
|
|
139
|
+
з: 'z', и: 'i', й: 'y', к: 'k', л: 'l', м: 'm', н: 'n', о: 'o',
|
|
140
|
+
п: 'p', р: 'r', с: 's', т: 't', у: 'u', ф: 'f', х: 'h', ц: 'ts',
|
|
141
|
+
ч: 'ch', ш: 'sh', щ: 'sch', ъ: '', ы: 'y', ь: '', э: 'e', ю: 'yu',
|
|
142
|
+
я: 'ya',
|
|
143
|
+
// Ukrainian / Belarusian extras reported in #2848
|
|
144
|
+
є: 'ye', і: 'i', ї: 'yi', ґ: 'g', ў: 'u',
|
|
145
|
+
};
|
|
146
|
+
const CYRILLIC_TRANSLITERATION_KEYS = Object.keys(CYRILLIC_TRANSLITERATION);
|
|
147
|
+
/**
|
|
148
|
+
* Lowercase + transliterate Cyrillic characters to ASCII. The output still
|
|
149
|
+
* contains non-ASCII for scripts outside the map (CJK, etc.) — the caller's
|
|
150
|
+
* existing `[^a-z0-9]+` filter handles those. Latin-script input is returned
|
|
151
|
+
* lowercased with no other change.
|
|
152
|
+
*
|
|
153
|
+
* Shared by `generateSlugInternal` (core-utils) and `slugify` (gsd2-import) so
|
|
154
|
+
* the transliteration step is not duplicated across the two slug helpers (#2848
|
|
155
|
+
* explicitly requires both be fixed).
|
|
156
|
+
*/
|
|
157
|
+
function transliterateForSlug(text) {
|
|
158
|
+
const lowered = text.toLowerCase();
|
|
159
|
+
let out = '';
|
|
160
|
+
for (const ch of lowered) {
|
|
161
|
+
out += CYRILLIC_TRANSLITERATION_KEYS.includes(ch)
|
|
162
|
+
? CYRILLIC_TRANSLITERATION[ch]
|
|
163
|
+
: ch;
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
111
166
|
}
|
|
112
167
|
/**
|
|
113
168
|
* Read a phase directory and return counts/flags for common file types.
|
|
169
|
+
*
|
|
170
|
+
* #3183 (ADR-3180 Decision 2): `plans`/`summaries` are derived from the
|
|
171
|
+
* canonical `scanPhasePlans` rather than a local re-derivation, so this
|
|
172
|
+
* primitive can no longer diverge from the single owner of live-plan
|
|
173
|
+
* counting. `scanPhasePlans`
|
|
174
|
+
* lives in plan-scan.cjs, which itself imports `countMatchedSummaries` from
|
|
175
|
+
* THIS module — a top-level import here would be circular, so the require
|
|
176
|
+
* is deferred (lazy, inside the function body) to break the cycle at load
|
|
177
|
+
* time. This mirrors the lazy-require seam already used elsewhere in this
|
|
178
|
+
* repo (see src/audit-command-router.cts) for the same "module A needs
|
|
179
|
+
* module B which needs module A" shape.
|
|
180
|
+
*
|
|
181
|
+
* `hasResearch`/`hasContext`/`hasVerification`/`hasReviews` stay on the raw
|
|
182
|
+
* `readdirSync` listing — they are not plan-scan concerns.
|
|
183
|
+
*
|
|
184
|
+
* #3511 BLOCKER-2: the raw listing is scoped through `scopeToPhase` (keyed on
|
|
185
|
+
* `path.basename(phaseDir)`) before any of the four artifact predicates run,
|
|
186
|
+
* so a stray cross-phase file (e.g. `04-VERIFICATION.md` sitting inside phase
|
|
187
|
+
* 03's directory) cannot flip `hasResearch`/`hasContext`/`hasVerification`/
|
|
188
|
+
* `hasReviews` true for a phase it does not belong to — the same membership
|
|
189
|
+
* rule every other aggregate phase-directory scan (`uat.cts`, `audit.cts`,
|
|
190
|
+
* `phase.cts`, `state.cts`) already routes through. `hasContext` is scoped by
|
|
191
|
+
* passing the already-scoped array into `findContextMdIn` at this call site
|
|
192
|
+
* only — `findContextMdIn` itself is unchanged and its other 4 call sites
|
|
193
|
+
* (roadmap.cts, gap-checker.cts, init.cts) are unaffected.
|
|
194
|
+
*
|
|
195
|
+
* Degrades on an unreadable directory instead of throwing: empty arrays,
|
|
196
|
+
* every flag false, scope UNREADABLE (mirroring scanPhasePlans's own
|
|
197
|
+
* degrade path).
|
|
114
198
|
*/
|
|
115
199
|
function getPhaseFileStats(phaseDir) {
|
|
116
|
-
|
|
200
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
201
|
+
const scanPhasePlans = require('./plan-scan.cjs');
|
|
202
|
+
const scan = scanPhasePlans(phaseDir);
|
|
203
|
+
let files;
|
|
204
|
+
try {
|
|
205
|
+
files = node_fs_1.default.readdirSync(phaseDir);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return {
|
|
209
|
+
plans: scan.planFiles,
|
|
210
|
+
summaries: scan.summaryFiles,
|
|
211
|
+
hasResearch: false,
|
|
212
|
+
hasContext: false,
|
|
213
|
+
hasVerification: false,
|
|
214
|
+
hasReviews: false,
|
|
215
|
+
scope: scan.scope,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const scopedFiles = scopeToPhase(files, node_path_1.default.basename(phaseDir));
|
|
117
219
|
return {
|
|
118
|
-
plans:
|
|
119
|
-
summaries:
|
|
120
|
-
hasResearch:
|
|
121
|
-
hasContext: findContextMdIn(
|
|
122
|
-
hasVerification:
|
|
123
|
-
hasReviews:
|
|
220
|
+
plans: scan.planFiles,
|
|
221
|
+
summaries: scan.summaryFiles,
|
|
222
|
+
hasResearch: scopedFiles.some(f => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md'),
|
|
223
|
+
hasContext: findContextMdIn(scopedFiles) !== null,
|
|
224
|
+
hasVerification: scopedFiles.some(f => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md'),
|
|
225
|
+
hasReviews: scopedFiles.some(f => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md'),
|
|
226
|
+
scope: scan.scope,
|
|
124
227
|
};
|
|
125
228
|
}
|
|
126
229
|
/**
|
|
@@ -222,32 +325,106 @@ function countMatchedSummaries(planFiles, summaryFiles) {
|
|
|
222
325
|
const summarySet = new Set(summaryFiles);
|
|
223
326
|
let matched = 0;
|
|
224
327
|
for (const plan of planFiles) {
|
|
225
|
-
|
|
226
|
-
const dir = slashIdx >= 0 ? plan.slice(0, slashIdx + 1) : '';
|
|
227
|
-
const base = (dir ? plan.slice(dir.length) : plan).replace(/\.md$/i, '');
|
|
228
|
-
const candidates = [
|
|
229
|
-
dir + base.replace(/PLAN/i, 'SUMMARY') + '.md',
|
|
230
|
-
dir + base + '-SUMMARY.md',
|
|
231
|
-
];
|
|
232
|
-
const extended = base.match(/^(\d+)-PLAN-(\d+)/i);
|
|
233
|
-
if (extended)
|
|
234
|
-
candidates.push(dir + extended[1] + '-' + extended[2] + '-SUMMARY.md');
|
|
235
|
-
if (candidates.some((c) => summarySet.has(c)))
|
|
328
|
+
if (summaryCandidates(plan).some((c) => summarySet.has(c)))
|
|
236
329
|
matched++;
|
|
237
330
|
}
|
|
238
331
|
return matched;
|
|
239
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* The candidate `*-SUMMARY.md` filenames a single plan's completion record
|
|
335
|
+
* could take, per the three naming conventions documented above
|
|
336
|
+
* `countMatchedSummaries`. Extracted so `findUnsummarizedPlans` can reuse the
|
|
337
|
+
* exact same matching rule without duplicating it (a divergence between the
|
|
338
|
+
* count and the list would let a plan be counted as matched while still
|
|
339
|
+
* appearing in the unsummarized set, or vice versa).
|
|
340
|
+
*/
|
|
341
|
+
function summaryCandidates(plan) {
|
|
342
|
+
const slashIdx = plan.lastIndexOf('/');
|
|
343
|
+
const dir = slashIdx >= 0 ? plan.slice(0, slashIdx + 1) : '';
|
|
344
|
+
const base = (dir ? plan.slice(dir.length) : plan).replace(/\.md$/i, '');
|
|
345
|
+
const candidates = [
|
|
346
|
+
dir + base.replace(/PLAN/i, 'SUMMARY') + '.md',
|
|
347
|
+
dir + base + '-SUMMARY.md',
|
|
348
|
+
];
|
|
349
|
+
const extended = base.match(/^(\d+)-PLAN-(\d+)/i);
|
|
350
|
+
if (extended)
|
|
351
|
+
candidates.push(dir + extended[1] + '-' + extended[2] + '-SUMMARY.md');
|
|
352
|
+
// #3183: canonical-id form. Restores the coverage of the pre-migration
|
|
353
|
+
// bespoke I001 rule (verify.cts, pre-#3183, via validate.cjs's now-unused
|
|
354
|
+
// `canonicalPlanStem` — behaviourally identical to `extractCanonicalPlanId`,
|
|
355
|
+
// confirmed empirically), which matched a plan carrying a descriptive slug
|
|
356
|
+
// after its <phase>-<plan> id — e.g. `68-01-scaffolding-PLAN.md` — against
|
|
357
|
+
// a summary named only by the bare id — `68-01-SUMMARY.md`. None of the
|
|
358
|
+
// three candidates above produce that filename.
|
|
359
|
+
//
|
|
360
|
+
// Narrowed to the case `extractCanonicalPlanId` actually extracted an
|
|
361
|
+
// <id>-<id> pair (its result differs from the plan's own PLAN-stripped
|
|
362
|
+
// base). When no pair is found it falls back to returning that same base
|
|
363
|
+
// unchanged, which would otherwise push a redundant candidate identical to
|
|
364
|
+
// the `<stem>-SUMMARY.md` form above (e.g. `setup-PLAN.md` -> canonical
|
|
365
|
+
// 'setup' -> 'setup-SUMMARY.md', already candidate #2) rather than the
|
|
366
|
+
// original rule's actual behavior of matching only real id pairs.
|
|
367
|
+
//
|
|
368
|
+
// Collision, matching the original rule byte-for-behaviour: two plans that
|
|
369
|
+
// share the same <phase>-<plan> id but differ only in their descriptive
|
|
370
|
+
// slug (`68-01-alpha-PLAN.md` + `68-01-beta-PLAN.md`) both generate the
|
|
371
|
+
// SAME candidate `68-01-SUMMARY.md` and therefore BOTH read as summarized
|
|
372
|
+
// off one shared summary file. This is not a new regression: the
|
|
373
|
+
// pre-migration bespoke rule collapsed the same way (it populated one
|
|
374
|
+
// `summaryBases` Set keyed by canonical stem, so any plan whose canonical
|
|
375
|
+
// stem hit the set counted as matched, with no cardinality check against
|
|
376
|
+
// how many plans shared that stem).
|
|
377
|
+
const planStem = base.replace(/-PLAN$/i, '');
|
|
378
|
+
const canonicalId = extractCanonicalPlanId(base + '.md');
|
|
379
|
+
if (canonicalId !== planStem)
|
|
380
|
+
candidates.push(dir + canonicalId + '-SUMMARY.md');
|
|
381
|
+
return candidates;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* #2648: the plan files in `planFiles` that have NO matching completion record
|
|
385
|
+
* in `summaryFiles`, using the identical matching rule as `countMatchedSummaries`
|
|
386
|
+
* (so the count and the named list can never disagree). Callers that must NAME
|
|
387
|
+
* the missing plans — e.g. phase.complete's fail-closed coverage gate, which
|
|
388
|
+
* refuses completion when any non-retired plan lacks a SUMMARY — need the list,
|
|
389
|
+
* not just the count. `planFiles` is expected to be already superseded-filtered
|
|
390
|
+
* (the caller passes `scanPhasePlans(...).planFiles`, which drops
|
|
391
|
+
* `status: superseded` plans), so a deliberately-retired plan never appears
|
|
392
|
+
* here and never blocks completion.
|
|
393
|
+
*/
|
|
394
|
+
function findUnsummarizedPlans(planFiles, summaryFiles) {
|
|
395
|
+
const summarySet = new Set(summaryFiles);
|
|
396
|
+
return planFiles.filter((plan) => !summaryCandidates(plan).some((c) => summarySet.has(c)));
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* #3183: the mirror image of `findUnsummarizedPlans` — the summary files in
|
|
400
|
+
* `summaryFiles` that do NOT pair with ANY plan in `planFiles`, using the
|
|
401
|
+
* identical `summaryCandidates` matching rule as `countMatchedSummaries` /
|
|
402
|
+
* `findUnsummarizedPlans`. Callers that must name orphaned summaries (a
|
|
403
|
+
* stray non-plan summary, or a summary whose plan was renamed/removed) need
|
|
404
|
+
* this instead of a bespoke exact-suffix Set-diff, which cannot recognize
|
|
405
|
+
* the nested or extended naming forms `summaryCandidates` already handles —
|
|
406
|
+
* a divergence that produced false "orphan summary" warnings.
|
|
407
|
+
*/
|
|
408
|
+
function findOrphanSummaries(planFiles, summaryFiles) {
|
|
409
|
+
const claimed = new Set();
|
|
410
|
+
for (const plan of planFiles) {
|
|
411
|
+
for (const candidate of summaryCandidates(plan))
|
|
412
|
+
claimed.add(candidate);
|
|
413
|
+
}
|
|
414
|
+
return summaryFiles.filter((s) => !claimed.has(s));
|
|
415
|
+
}
|
|
240
416
|
module.exports = {
|
|
241
417
|
toPosixPath,
|
|
242
418
|
detectSubRepos,
|
|
243
419
|
extractOneLinerFromBody,
|
|
244
420
|
pathExistsInternal,
|
|
245
421
|
generateSlugInternal,
|
|
246
|
-
|
|
247
|
-
filterSummaryFiles,
|
|
422
|
+
transliterateForSlug,
|
|
248
423
|
getPhaseFileStats,
|
|
249
424
|
readSubdirectories,
|
|
250
425
|
timeAgo,
|
|
251
426
|
extractCanonicalPlanId,
|
|
252
427
|
countMatchedSummaries,
|
|
428
|
+
findUnsummarizedPlans,
|
|
429
|
+
findOrphanSummaries,
|
|
253
430
|
};
|
|
@@ -21,6 +21,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
exports.extractDecisions = extractDecisions;
|
|
22
22
|
exports.parseDecisions = parseDecisions;
|
|
23
23
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
24
|
+
const token_scanner_cjs_1 = require("./token-scanner.cjs");
|
|
24
25
|
const DISCRETION_HEADINGS = new Set([
|
|
25
26
|
"claude's discretion",
|
|
26
27
|
'claudes discretion',
|
|
@@ -89,12 +90,14 @@ function parseDecisionLines(block) {
|
|
|
89
90
|
let category = '';
|
|
90
91
|
let inDiscretion = false;
|
|
91
92
|
let current = null;
|
|
93
|
+
let openIndent = null;
|
|
92
94
|
let parseMisses = 0;
|
|
93
95
|
const flush = () => {
|
|
94
96
|
if (current) {
|
|
95
97
|
current.text = current.text.trim();
|
|
96
98
|
out.push(current);
|
|
97
99
|
current = null;
|
|
100
|
+
openIndent = null;
|
|
98
101
|
}
|
|
99
102
|
};
|
|
100
103
|
for (const line of lines) {
|
|
@@ -115,6 +118,23 @@ function parseDecisionLines(block) {
|
|
|
115
118
|
inDiscretion = DISCRETION_HEADINGS.has(normalized);
|
|
116
119
|
continue;
|
|
117
120
|
}
|
|
121
|
+
// Nested bullet under an open decision (#3212 Phase 3, #3169): a bullet
|
|
122
|
+
// indented deeper than the currently-open decision's own bullet is that
|
|
123
|
+
// decision's elaboration (e.g. a cross-reference to a sibling decision),
|
|
124
|
+
// not a fresh declaration attempt — fold it into current.text exactly
|
|
125
|
+
// like a continuation line, before it ever reaches the declaration/
|
|
126
|
+
// parse-miss regexes below. A bullet at the SAME or a SHALLOWER indent
|
|
127
|
+
// is unaffected — tested exactly as before this fix. See design doc
|
|
128
|
+
// .gsd/phase/chore-3414-tokenizer-first-seam/40-design.md §1.3 for why
|
|
129
|
+
// nesting depth, not bullet content, is the signal that distinguishes
|
|
130
|
+
// this from a genuinely malformed top-level declaration.
|
|
131
|
+
if (current &&
|
|
132
|
+
openIndent !== null &&
|
|
133
|
+
trimmed.startsWith('-') &&
|
|
134
|
+
(0, token_scanner_cjs_1.indentWidth)(line) > openIndent) {
|
|
135
|
+
current.text += ' ' + trimmed;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
118
138
|
// Colon form: `- **D-NN[ [tags]]:** text`
|
|
119
139
|
const colonMatch = line.match(bulletColonRe);
|
|
120
140
|
if (colonMatch) {
|
|
@@ -125,6 +145,7 @@ function parseDecisionLines(block) {
|
|
|
125
145
|
: [];
|
|
126
146
|
const trackable = !inDiscretion && !tags.some((t) => NON_TRACKABLE_TAGS.has(t));
|
|
127
147
|
current = { id, text: colonMatch[3], category, tags, trackable };
|
|
148
|
+
openIndent = (0, token_scanner_cjs_1.indentWidth)(line);
|
|
128
149
|
continue;
|
|
129
150
|
}
|
|
130
151
|
// Em-dash form: `- **D-NN[ [tags]] — title** body`
|
|
@@ -140,6 +161,7 @@ function parseDecisionLines(block) {
|
|
|
140
161
|
// title itself is embedded in the bold run but we report the body as text
|
|
141
162
|
// (consistent with how the gate cares only about coverage, not title/body split).
|
|
142
163
|
current = { id, text: emDashMatch[3] || '', category, tags, trackable };
|
|
164
|
+
openIndent = (0, token_scanner_cjs_1.indentWidth)(line);
|
|
143
165
|
continue;
|
|
144
166
|
}
|
|
145
167
|
// Titled-colon form: `- **D-NN[ [tags]]: Title.** body` (#1639). Checked LAST — it is
|
|
@@ -155,6 +177,7 @@ function parseDecisionLines(block) {
|
|
|
155
177
|
: [];
|
|
156
178
|
const trackable = !inDiscretion && !tags.some((t) => NON_TRACKABLE_TAGS.has(t));
|
|
157
179
|
current = { id, text: titledColonMatch[3] || '', category, tags, trackable };
|
|
180
|
+
openIndent = (0, token_scanner_cjs_1.indentWidth)(line);
|
|
158
181
|
continue;
|
|
159
182
|
}
|
|
160
183
|
// Parse-miss guard (FIX B + #1343): a line that looks like a `D-NN` decision
|
|
@@ -30,6 +30,7 @@ const { pathExistsInternal, toPosixPath } = coreUtils;
|
|
|
30
30
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
31
31
|
const agentInstallCheck = require("./agent-install-check.cjs");
|
|
32
32
|
const { checkAgentsInstalled } = agentInstallCheck;
|
|
33
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
33
34
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
34
35
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
35
36
|
const GSD_MARKER = '<!-- generated-by: gsd-doc-writer -->';
|
|
@@ -256,7 +257,7 @@ function cmdDocsInit(cwd, raw) {
|
|
|
256
257
|
};
|
|
257
258
|
// Inject project_root and agent installation status (mirrors withProjectRoot in init.cjs)
|
|
258
259
|
result['project_root'] = cwd;
|
|
259
|
-
const agentStatus = checkAgentsInstalled();
|
|
260
|
+
const agentStatus = checkAgentsInstalled((0, runtime_slash_cjs_1.resolveRuntime)(cwd), cwd);
|
|
260
261
|
result['agents_installed'] = agentStatus.agents_installed;
|
|
261
262
|
result['missing_agents'] = agentStatus.missing_agents;
|
|
262
263
|
// #2402: withProjectRoot injects response_language when set; cmdDocsInit predates
|
|
@@ -267,4 +268,4 @@ function cmdDocsInit(cwd, raw) {
|
|
|
267
268
|
}
|
|
268
269
|
output(result, raw, undefined);
|
|
269
270
|
}
|
|
270
|
-
module.exports = { cmdDocsInit };
|
|
271
|
+
module.exports = { cmdDocsInit, detectMonorepoWorkspaces };
|
|
@@ -219,6 +219,11 @@ function _isNonTerminal(status) {
|
|
|
219
219
|
* - Same `job_id` for the same `plan_id` -> allowed (status progression).
|
|
220
220
|
* - A prior job for the same `plan_id` that is already terminal -> allowed
|
|
221
221
|
* (the duplicate guard only protects against re-dispatching live work).
|
|
222
|
+
* - If a SIBLING manifest (not the target) cannot be read or parsed, the
|
|
223
|
+
* duplicate scan cannot be completed and may be hiding a live duplicate
|
|
224
|
+
* -> refuse (`scan_incomplete`), naming the offending file's path so an
|
|
225
|
+
* operator can quarantine or repair it. Never silently skip a sibling the
|
|
226
|
+
* scan could not inspect.
|
|
222
227
|
*/
|
|
223
228
|
function writeManifest(manifest, planningDir, opts = {}) {
|
|
224
229
|
const fs = opts.fs ?? node_fs_1.default;
|
|
@@ -240,18 +245,28 @@ function writeManifest(manifest, planningDir, opts = {}) {
|
|
|
240
245
|
try {
|
|
241
246
|
raw = String(fs.readFileSync(p));
|
|
242
247
|
}
|
|
243
|
-
catch {
|
|
244
|
-
|
|
248
|
+
catch (e) {
|
|
249
|
+
return {
|
|
250
|
+
ok: false,
|
|
251
|
+
kind: 'scan_incomplete',
|
|
252
|
+
message: `duplicate scan incomplete: failed to READ sibling manifest ${p} (${e.message}); quarantine or repair this file before retrying`,
|
|
253
|
+
offendingPath: p,
|
|
254
|
+
};
|
|
245
255
|
}
|
|
246
256
|
let existing;
|
|
247
257
|
try {
|
|
248
258
|
existing = JSON.parse(raw);
|
|
249
259
|
}
|
|
250
|
-
catch {
|
|
260
|
+
catch (e) {
|
|
251
261
|
if (p === target) {
|
|
252
262
|
return { ok: false, kind: 'malformed_existing', message: `target manifest ${p} is not valid JSON` };
|
|
253
263
|
}
|
|
254
|
-
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
kind: 'scan_incomplete',
|
|
267
|
+
message: `duplicate scan incomplete: failed to PARSE sibling manifest ${p} (${e.message}); quarantine or repair this file before retrying`,
|
|
268
|
+
offendingPath: p,
|
|
269
|
+
};
|
|
255
270
|
}
|
|
256
271
|
const samePlan = existing.plan_id === manifest.plan_id;
|
|
257
272
|
const sameJob = existing.job_id === manifest.job_id;
|
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Parses the real fallow `audit --format json` schema (schema_version 3
|
|
10
10
|
* envelope, nested dead_code/duplication sections). See fallow 2.70.0+.
|
|
11
|
+
*
|
|
12
|
+
* #3411 Phase 2 (#3618): binary resolution no longer lives here — it delegates
|
|
13
|
+
* to the platform seam's `resolveExecutableBinary` (shell-command-projection.cts).
|
|
14
|
+
* This file's prior private resolver deliberately included an extensionless
|
|
15
|
+
* `fallow` as a win32 candidate; the seam does not, and that is a fix, not a
|
|
16
|
+
* regression — an extensionless file sitting beside `fallow.cmd` is npm's POSIX
|
|
17
|
+
* `sh` shim, which `CreateProcess` cannot run (#3275).
|
|
11
18
|
*/
|
|
12
19
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
20
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -19,51 +26,20 @@ exports.normalizeFallowReport = normalizeFallowReport;
|
|
|
19
26
|
exports.normalizeFallowReportFile = normalizeFallowReportFile;
|
|
20
27
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
21
28
|
const node_path_1 = __importDefault(require("node:path"));
|
|
22
|
-
|
|
23
|
-
return process.platform === 'win32'
|
|
24
|
-
? ['fallow.exe', 'fallow.cmd', 'fallow.bat', 'fallow']
|
|
25
|
-
: ['fallow'];
|
|
26
|
-
}
|
|
27
|
-
function isExecutableFile(filePath) {
|
|
28
|
-
try {
|
|
29
|
-
const stat = node_fs_1.default.statSync(filePath);
|
|
30
|
-
if (!stat.isFile())
|
|
31
|
-
return false;
|
|
32
|
-
if (process.platform === 'win32')
|
|
33
|
-
return true;
|
|
34
|
-
node_fs_1.default.accessSync(filePath, node_fs_1.default.constants.X_OK);
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function findInPath(envPath) {
|
|
42
|
-
if (!envPath)
|
|
43
|
-
return null;
|
|
44
|
-
const names = candidateNames();
|
|
45
|
-
const segments = envPath.split(node_path_1.default.delimiter).filter(Boolean);
|
|
46
|
-
for (const segment of segments) {
|
|
47
|
-
for (const name of names) {
|
|
48
|
-
const candidate = node_path_1.default.join(segment, name);
|
|
49
|
-
if (isExecutableFile(candidate))
|
|
50
|
-
return candidate;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
function findInNodeModules(cwd) {
|
|
56
|
-
const names = candidateNames();
|
|
57
|
-
const binDir = node_path_1.default.join(cwd, 'node_modules', '.bin');
|
|
58
|
-
for (const name of names) {
|
|
59
|
-
const candidate = node_path_1.default.join(binDir, name);
|
|
60
|
-
if (isExecutableFile(candidate))
|
|
61
|
-
return candidate;
|
|
62
|
-
}
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
29
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
65
30
|
function resolveFallowBinary({ cwd, envPath = process.env['PATH'] ?? '' }) {
|
|
66
|
-
return
|
|
31
|
+
return (0, shell_command_projection_cjs_1.resolveExecutableBinary)('fallow', {
|
|
32
|
+
prependPaths: [node_path_1.default.join(cwd, 'node_modules', '.bin')],
|
|
33
|
+
// #3619 (epic #3411 Phase 3): pathOverride carries envPath as the search
|
|
34
|
+
// path while leaving `env` unset, so the seam falls back to its default
|
|
35
|
+
// `env` (process.env) for everything else — PATHEXT included. Ambient
|
|
36
|
+
// PATHEXT therefore still governs win32 resolution exactly as before, but
|
|
37
|
+
// this file never reads it itself: that's what keeps this module clean
|
|
38
|
+
// under local/no-private-binary-resolution, which forbids a PATHEXT read
|
|
39
|
+
// outside the seam.
|
|
40
|
+
pathOverride: envPath,
|
|
41
|
+
requireExecutable: true,
|
|
42
|
+
});
|
|
67
43
|
}
|
|
68
44
|
function requireFallowBinary({ cwd, envPath = process.env['PATH'] ?? '' }) {
|
|
69
45
|
const binary = resolveFallowBinary({ cwd, envPath });
|