@opengsd/gsd-core 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -9
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +15 -55
- package/agents/gsd-planner.md +6 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +609 -134
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/quick.md +8 -4
- package/gsd-core/bin/gsd-tools.cjs +567 -51
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +336 -95
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-validator.cjs +205 -18
- package/gsd-core/bin/lib/check-command-router.cjs +145 -5
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +543 -44
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +118 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +84 -7
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/frontmatter.cjs +840 -305
- package/gsd-core/bin/lib/gap-checker.cjs +27 -3
- package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +120 -41
- package/gsd-core/bin/lib/install-engine.cjs +68 -3
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
- package/gsd-core/bin/lib/install-profiles.cjs +78 -4
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +160 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-table.cjs +52 -4
- package/gsd-core/bin/lib/milestone.cjs +90 -5
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +70 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +405 -84
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
- package/gsd-core/bin/lib/roadmap.cjs +74 -19
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
- package/gsd-core/bin/lib/smart-entry.cjs +12 -22
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +186 -0
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +517 -101
- package/gsd-core/bin/lib/state.cjs +946 -163
- package/gsd-core/bin/lib/surface.cjs +10 -2
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +1376 -125
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification.cjs +14 -1
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +95 -40
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +3 -2
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +1 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +37 -8
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +2 -2
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +3 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +4 -9
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +10 -26
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +121 -40
- package/gsd-core/workflows/complete-milestone.md +15 -10
- package/gsd-core/workflows/debug.md +5 -3
- package/gsd-core/workflows/diagnose-issues.md +12 -6
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +4 -9
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +38 -54
- package/gsd-core/workflows/execute-plan.md +17 -12
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +2 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +3 -6
- package/gsd-core/workflows/import.md +14 -11
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +44 -11
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +2 -2
- package/gsd-core/workflows/new-milestone.md +9 -21
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +12 -26
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +121 -42
- package/gsd-core/workflows/plan-review-convergence.md +46 -9
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +27 -12
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +20 -29
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +193 -15
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +2 -2
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +3 -5
- package/gsd-core/workflows/ship.md +12 -6
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +23 -1
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +5 -5
- package/gsd-core/workflows/ui-phase.md +10 -16
- package/gsd-core/workflows/ui-review.md +6 -10
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +6 -10
- package/gsd-core/workflows/validate-phase.md +2 -2
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +57 -18
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +16 -7
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +17 -8
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +215 -26
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +34 -16
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +16 -7
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +17 -8
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +215 -26
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +34 -16
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +12 -7
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +5 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +45 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +134 -1
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +21 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +122 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/prompt-injection-scan.sh +75 -14
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -11,6 +11,17 @@
|
|
|
11
11
|
* Dependencies:
|
|
12
12
|
* - ./pattern.cjs (escapeRegex — #3212 Phase 1 seam; this module is no
|
|
13
13
|
* longer the owner of pattern-escaping, only a consumer)
|
|
14
|
+
* - ./core-utils.cjs (generateSlugInternal — #3883/ADR-3473 §8.3: the
|
|
15
|
+
* canonical slug formula). core-utils.cjs also requires THIS module
|
|
16
|
+
* (comparePhaseNum, scopeToPhase), so a top-level require here would be
|
|
17
|
+
* circular and — per this codebase's compiled-.cjs convention of a
|
|
18
|
+
* single `module.exports = {...}` reassignment at the bottom of each
|
|
19
|
+
* file — a top-level circular require captures a stale, still-empty
|
|
20
|
+
* exports object forever (verified live: it throws
|
|
21
|
+
* "generateSlugInternal is not a function" when core-utils.cjs happens
|
|
22
|
+
* to load first). The require is deferred (lazy, inside each function
|
|
23
|
+
* body) instead, mirroring the same cycle-break already used by
|
|
24
|
+
* core-utils.cts's own getPhaseFileStats/plan-scan.cjs seam.
|
|
14
25
|
*/
|
|
15
26
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
16
27
|
// ─── Phase-id helpers ─────────────────────────────────────────────────────────
|
|
@@ -214,9 +225,15 @@ function getPhaseDirFromPhaseId(phaseId, phaseName, projectCode) {
|
|
|
214
225
|
const milestone = String(parseInt(m[1], 10)).padStart(2, '0');
|
|
215
226
|
const subParts = m[2].split('-').map(p => String(parseInt(p, 10)).padStart(2, '0'));
|
|
216
227
|
const sub = subParts.join('-');
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
228
|
+
// #3883 (ADR-3473 §8.3): delegate to the canonical slug formula
|
|
229
|
+
// (generateSlugInternal, core-utils.cts) rather than re-implementing it.
|
|
230
|
+
// `maxLen: null` preserves this site's pre-migration untruncated contract —
|
|
231
|
+
// the 60-char default would silently shadow one on-disk phase dir's
|
|
232
|
+
// reported phase_slug behind another distinct >60-char phase name's.
|
|
233
|
+
// Lazy require to break the core-utils.cjs <-> phase-id.cjs cycle (see the
|
|
234
|
+
// module dependency doc comment above).
|
|
235
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
236
|
+
const slug = phaseName ? (require('./core-utils.cjs').generateSlugInternal(phaseName, null) ?? '') : '';
|
|
220
237
|
const parts = [milestone, sub, slug].filter(Boolean);
|
|
221
238
|
const base = parts.join('-');
|
|
222
239
|
return projectCode ? `${projectCode}-${base}` : base;
|
|
@@ -324,7 +341,22 @@ function toDir(id, slug) {
|
|
|
324
341
|
const sub = id.subphase ? `.${id.subphase}` : '';
|
|
325
342
|
// Slug guard: the slug becomes an on-disk path segment, so collapse it to a
|
|
326
343
|
// safe lowercase token — never a path separator or `..` traversal.
|
|
327
|
-
|
|
344
|
+
// #3883 (ADR-3473 §8.3): delegate the sanitize formula itself to the
|
|
345
|
+
// canonical (generateSlugInternal, core-utils.cts) — this fixes the
|
|
346
|
+
// Cyrillic-collapses-to-empty defect (#2848-class) that toDir carried
|
|
347
|
+
// before (it never transliterated). The empty-sanitize and all-digit
|
|
348
|
+
// throw guards below stay: they are a DECLARED DIFFERENCE from every
|
|
349
|
+
// other slug call site, not a bug — a slug here becomes a real directory
|
|
350
|
+
// name, and toDir protects the parsePhaseId dir↔identity bijection
|
|
351
|
+
// (see the toDir docstring above) by refusing to emit an unusable name,
|
|
352
|
+
// where every other site silently accepts "" or a re-truncated value.
|
|
353
|
+
// `maxLen: null` preserves toDir's pre-migration untruncated contract — the
|
|
354
|
+
// 60-char default let two distinct >60-char phase names collapse onto the
|
|
355
|
+
// identical directory name, one silently shadowing the other on disk.
|
|
356
|
+
// Lazy require to break the core-utils.cjs <-> phase-id.cjs cycle (see the
|
|
357
|
+
// module dependency doc comment above).
|
|
358
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
359
|
+
const safeSlug = require('./core-utils.cjs').generateSlugInternal(slug, null) ?? '';
|
|
328
360
|
// A slug that sanitizes to nothing (e.g. '!!!') would otherwise emit a
|
|
329
361
|
// dangling trailing hyphen.
|
|
330
362
|
if (!safeSlug) {
|
|
@@ -360,6 +392,39 @@ function isSentinelPhaseId(phaseId, convention) {
|
|
|
360
392
|
return false;
|
|
361
393
|
return SENTINEL_RANGES.includes(parseInt(legacy[1], 10));
|
|
362
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Disk-side sentinel recognizer (#3639): is this on-disk PHASE DIRECTORY a
|
|
397
|
+
* sentinel (never-on-roadmap by convention)?
|
|
398
|
+
*
|
|
399
|
+
* The disk-side guards (C001 gap numbering, W007 orphan dirs) see raw
|
|
400
|
+
* directory names and do not know the repo's naming convention — and neither
|
|
401
|
+
* convention-blind route could recognize a bracket sentinel: `isSentinelPhaseId`
|
|
402
|
+
* without the convention argument reads only the legacy leading int, while
|
|
403
|
+
* `extractPhaseToken(dirName)` (convention-aware or not) strips the MILESTONE
|
|
404
|
+
* and returns the bare phase token — bracket sentinel-ness lives in the
|
|
405
|
+
* milestone portion (`GSD.999-07-icebox` is icebox because of the 999, not
|
|
406
|
+
* the 07). This helper reads the milestone directly off the dir name.
|
|
407
|
+
*
|
|
408
|
+
* The bracket branch requires the FULL bracket dir shape — code prefix, dot,
|
|
409
|
+
* milestone digits, hyphen, PHASE DIGITS — so a #1324 letter-prefixed real
|
|
410
|
+
* dir with a LETTER slug (`P0.0-foundation`) never matches it (ADR-2121
|
|
411
|
+
* indistinguishability, same gate as extractPhaseToken below). DISCLOSED
|
|
412
|
+
* RESIDUAL (#3639 review): the #1324 family also has digit continuations
|
|
413
|
+
* (`P0.0-1-foundation`, `P0.3-2` are real shapes per derivePhaseTokenSegments),
|
|
414
|
+
* and `{code}.{0|999}-{digit}...` is string-indistinguishable from a bracket
|
|
415
|
+
* sentinel dir — no convention-free discriminator exists (ADR-2121). Such a
|
|
416
|
+
* dir reads as sentinel here, which at the disk-guard call sites suppresses
|
|
417
|
+
* a warning (conservative for a linter) rather than deleting data. The
|
|
418
|
+
* digit-continuation family with NON-sentinel first decimals (`P0.3-2`)
|
|
419
|
+
* reads milestone 3 — ordinary — exactly as the convention-gated id
|
|
420
|
+
* predicate does. Everything else falls to the legacy leading-int rule.
|
|
421
|
+
*/
|
|
422
|
+
function isSentinelPhaseDir(dirName) {
|
|
423
|
+
const bracketDir = dirName.match(/^[A-Z][A-Z0-9_]*\.(\d+)-\d/); // milestone digits + hyphen + phase DIGITS
|
|
424
|
+
if (bracketDir)
|
|
425
|
+
return SENTINEL_RANGES.includes(parseInt(bracketDir[1], 10));
|
|
426
|
+
return isSentinelPhaseId(dirName);
|
|
427
|
+
}
|
|
363
428
|
/**
|
|
364
429
|
* Render a regex source fragment matching a phase number against ROADMAP/STATE
|
|
365
430
|
* prose regardless of zero-padding on either side.
|
|
@@ -1120,6 +1185,7 @@ module.exports = {
|
|
|
1120
1185
|
toDir,
|
|
1121
1186
|
SENTINEL_RANGES,
|
|
1122
1187
|
isSentinelPhaseId,
|
|
1188
|
+
isSentinelPhaseDir,
|
|
1123
1189
|
phaseMarkdownRegexSource,
|
|
1124
1190
|
phaseMarkdownRegexSourceExact,
|
|
1125
1191
|
comparePhaseNum,
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* - Issue #4 (open-gsd/gsd-core)
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.locateProgressTable = locateProgressTable;
|
|
24
25
|
exports.deriveProgressFromRoadmap = deriveProgressFromRoadmap;
|
|
25
26
|
exports.clampPercentFromFraction = clampPercentFromFraction;
|
|
26
27
|
exports.clampPercent = clampPercent;
|
|
@@ -29,8 +30,10 @@ const markdown_table_cjs_1 = require("./markdown-table.cjs");
|
|
|
29
30
|
const phaseIdMod = require("./phase-id.cjs");
|
|
30
31
|
const { isSentinelPhaseId } = phaseIdMod;
|
|
31
32
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
33
|
+
* #3227: the single owner of "where is this ROADMAP's Progress table".
|
|
34
|
+
* Lifted verbatim out of deriveProgressFromRoadmap so `state-contract.cts`
|
|
35
|
+
* enumerates phases from THE SAME table this module derives its counts from.
|
|
36
|
+
* A second copy of this locator is the DEFECT.GENERATIVE-FIX shape.
|
|
34
37
|
*
|
|
35
38
|
* ADR-2143 §3 ("addressed by NAME, never ordinal"): the Progress table is
|
|
36
39
|
* located via the markdown-table seam's `findTableWithColumns`, which is
|
|
@@ -49,6 +52,24 @@ const { isSentinelPhaseId } = phaseIdMod;
|
|
|
49
52
|
* to scanning the whole input, preserving the "Progress table not under a
|
|
50
53
|
* `## Progress` heading, or not the first table in the document, still
|
|
51
54
|
* resolves" behaviour.
|
|
55
|
+
*/
|
|
56
|
+
function locateProgressTable(roadmapContent) {
|
|
57
|
+
const progressMatch = roadmapContent.match(/^##[ \t]+Progress\b/im);
|
|
58
|
+
let scoped = roadmapContent;
|
|
59
|
+
if (progressMatch && progressMatch.index !== undefined) {
|
|
60
|
+
const afterHeading = roadmapContent.slice(progressMatch.index);
|
|
61
|
+
const nextHeading = afterHeading.search(/\n#{1,2}[ \t]/);
|
|
62
|
+
scoped = nextHeading >= 0 ? afterHeading.slice(0, nextHeading) : afterHeading;
|
|
63
|
+
}
|
|
64
|
+
return (0, markdown_table_cjs_1.findTableWithColumns)(scoped, ['Phase', 'Plans Complete', 'Status', 'Completed']);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Derive completed_phases, total_phases, and total_plans from ROADMAP content.
|
|
68
|
+
* Root cause fix for issue #4 — see gen-phase-lifecycle.mjs for full documentation.
|
|
69
|
+
*
|
|
70
|
+
* The Progress table itself is located by `locateProgressTable` (ADR-2143 §3,
|
|
71
|
+
* lifted out as #3227's single-owner extraction) — this function consumes
|
|
72
|
+
* that table.
|
|
52
73
|
*
|
|
53
74
|
* Cells are read by column NAME (`r['Status']`, `r['Plans Complete']`,
|
|
54
75
|
* `r['Phase']`), fixing #2137 (the old position-based regex assumed "Status"
|
|
@@ -67,20 +88,7 @@ function deriveProgressFromRoadmap(roadmapContent) {
|
|
|
67
88
|
// `{ ok: false, reason }`, not an exception — so the catch was masking
|
|
68
89
|
// nothing but dead code paths. Removed per ADR-2143 §5; the public
|
|
69
90
|
// `RoadmapProgress` contract (nulls = absent) is unchanged.
|
|
70
|
-
|
|
71
|
-
// ADR-2143 §3: read the Progress table by column NAME (order/injection-invariant),
|
|
72
|
-
// via the markdown-table seam. Scope to the `## Progress` section when present
|
|
73
|
-
// (#2012 decoy avoidance); a headingless milestone slice (#1445) falls back to the
|
|
74
|
-
// whole input. Requires the canonical Phase/Plans Complete/Status/Completed columns
|
|
75
|
-
// in any order (extra columns ignored) — supersedes findTableBySchema's exact-schema lookup.
|
|
76
|
-
const progressMatch = roadmapContent.match(/^##[ \t]+Progress\b/im);
|
|
77
|
-
let scoped = roadmapContent;
|
|
78
|
-
if (progressMatch && progressMatch.index !== undefined) {
|
|
79
|
-
const afterHeading = roadmapContent.slice(progressMatch.index);
|
|
80
|
-
const nextHeading = afterHeading.search(/\n#{1,2}[ \t]/);
|
|
81
|
-
scoped = nextHeading >= 0 ? afterHeading.slice(0, nextHeading) : afterHeading;
|
|
82
|
-
}
|
|
83
|
-
const table = (0, markdown_table_cjs_1.findTableWithColumns)(scoped, ['Phase', 'Plans Complete', 'Status', 'Completed']);
|
|
91
|
+
const table = locateProgressTable(roadmapContent);
|
|
84
92
|
if (table) {
|
|
85
93
|
const allRows = table.rows;
|
|
86
94
|
const completed = allRows.filter((r) => /^complete$/i.test((r['Status'] ?? '').trim())).length;
|
|
@@ -29,7 +29,7 @@ const coreUtilsModule = require("./core-utils.cjs");
|
|
|
29
29
|
const { readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath, findUnsummarizedPlans } = coreUtilsModule;
|
|
30
30
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
31
31
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
32
|
-
const { planningDir } = planningWorkspace;
|
|
32
|
+
const { planningDir, planningRoot } = planningWorkspace;
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
34
|
const frontmatterModule = require("./frontmatter.cjs");
|
|
35
35
|
const { extractFrontmatter } = frontmatterModule;
|
|
@@ -95,24 +95,56 @@ function compareArchiveVersionDesc(aName, bName) {
|
|
|
95
95
|
}
|
|
96
96
|
return 0;
|
|
97
97
|
}
|
|
98
|
-
function listArchiveVersionDirs(cwd) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
function listArchiveVersionDirs(cwd, wsOverride) {
|
|
99
|
+
// #3804: enumerate BOTH archive shapes under the CURRENT SCOPE's milestones
|
|
100
|
+
// tree (planningDir — GSD_WORKSTREAM/GSD_PROJECT-aware, exactly the
|
|
101
|
+
// #2855 scoping findPhaseInternal and getArchivedPhaseDirs rely on):
|
|
102
|
+
// flat: <scope>/milestones/vX.Y-phases/<phase-dir>/
|
|
103
|
+
// workstream archive: <scope>/milestones/ws-<slug>-<date>/phases/<phase-dir>/
|
|
104
|
+
// Pre-#3804 only the flat shape matched, so workstream-archived milestones
|
|
105
|
+
// were invisible (the reporter's repo: 20 hidden phase artifacts). The
|
|
106
|
+
// ws-* shape's phase dirs sit one level deeper (under phases/) and its dir
|
|
107
|
+
// name fails ^v[\d.]+-phases$ — both the name AND the level are modeled.
|
|
108
|
+
// Version labels: flat keeps the bare vX.Y; ws-* shapes carry the dir name
|
|
109
|
+
// (no numeric version). Flat-newest-first (compareArchiveVersionDesc), then
|
|
110
|
+
// ws dirs by name descending — deterministic. The AUDIT's cross-workstream
|
|
111
|
+
// enumeration (audit.cts listAuditPhaseTargets) calls this helper once per
|
|
112
|
+
// tree (root + each workstream) rather than widening this scope — the
|
|
113
|
+
// #2855 no-leak contract for findPhaseInternal/getArchivedPhaseDirs is
|
|
114
|
+
// preserved untouched.
|
|
115
|
+
const milestonesDir = node_path_1.default.join(planningDir(cwd, wsOverride ?? undefined), 'milestones');
|
|
116
|
+
const out = [];
|
|
117
|
+
const seen = new Set();
|
|
118
|
+
const pushVersion = (version, archivePath) => {
|
|
119
|
+
const rel = toPosixPath(node_path_1.default.relative(cwd, archivePath));
|
|
120
|
+
if (seen.has(rel))
|
|
121
|
+
return;
|
|
122
|
+
seen.add(rel);
|
|
123
|
+
out.push({ version, archivePath });
|
|
124
|
+
};
|
|
125
|
+
let entries;
|
|
102
126
|
try {
|
|
103
|
-
|
|
104
|
-
return milestoneEntries
|
|
105
|
-
.filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
|
|
106
|
-
.map(e => e.name)
|
|
107
|
-
.sort(compareArchiveVersionDesc)
|
|
108
|
-
.map(archiveName => ({
|
|
109
|
-
version: archiveName.match(/^(v[\d.]+)-phases$/)[1],
|
|
110
|
-
archivePath: node_path_1.default.join(milestonesDir, archiveName),
|
|
111
|
-
}));
|
|
127
|
+
entries = node_fs_1.default.readdirSync(milestonesDir, { withFileTypes: true });
|
|
112
128
|
}
|
|
113
129
|
catch {
|
|
114
130
|
return [];
|
|
115
131
|
}
|
|
132
|
+
const flat = entries
|
|
133
|
+
.filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
|
|
134
|
+
.map(e => e.name)
|
|
135
|
+
.sort(compareArchiveVersionDesc);
|
|
136
|
+
for (const archiveName of flat) {
|
|
137
|
+
pushVersion(archiveName.match(/^(v[\d.]+)-phases$/)[1], node_path_1.default.join(milestonesDir, archiveName));
|
|
138
|
+
}
|
|
139
|
+
const wsDirs = entries
|
|
140
|
+
.filter(e => e.isDirectory() && /^ws-/.test(e.name))
|
|
141
|
+
.map(e => e.name)
|
|
142
|
+
.sort()
|
|
143
|
+
.reverse();
|
|
144
|
+
for (const wsName of wsDirs) {
|
|
145
|
+
pushVersion(wsName, node_path_1.default.join(milestonesDir, wsName, 'phases'));
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
116
148
|
}
|
|
117
149
|
function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
118
150
|
try {
|
|
@@ -211,7 +243,12 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
211
243
|
directory: toPosixPath(node_path_1.default.join(relBase, match)),
|
|
212
244
|
phase_number: phaseNumber,
|
|
213
245
|
phase_name: phaseName,
|
|
214
|
-
|
|
246
|
+
// #3883 (ADR-3473 §8.3): delegate to the canonical slug formula
|
|
247
|
+
// (generateSlugInternal, core-utils.cts) rather than re-implementing
|
|
248
|
+
// it. `maxLen: null` preserves this site's pre-migration untruncated
|
|
249
|
+
// contract — the 60-char default would drop an on-disk phase slug's
|
|
250
|
+
// reported value out of sync with the real directory name.
|
|
251
|
+
phase_slug: phaseName ? coreUtilsModule.generateSlugInternal(phaseName, null) : null,
|
|
215
252
|
plans,
|
|
216
253
|
summaries,
|
|
217
254
|
incomplete_plans: incompletePlans,
|
|
@@ -339,13 +376,56 @@ function listMilestonePhaseDirs(phasesDir, opts = {}) {
|
|
|
339
376
|
.sort((a, b) => comparePhaseNum(a, b));
|
|
340
377
|
return { value, scope };
|
|
341
378
|
}
|
|
342
|
-
|
|
379
|
+
/**
|
|
380
|
+
* #3882 (ADR-3473 §8.2, issue #3882): the single owner of "the PHYSICAL set
|
|
381
|
+
* of phase directories on disk, entirely un-windowed" — the OTHER axis
|
|
382
|
+
* `listMilestonePhaseDirs` above deliberately does not offer. That owner
|
|
383
|
+
* refuses sentinels UNCONDITIONALLY (see its own doc comment); it has no way
|
|
384
|
+
* to say "physical set, sentinels included". That is exactly what an
|
|
385
|
+
* archival, lookup-index, or health-sweep caller needs — e.g. a heading ->
|
|
386
|
+
* directory lookup index that must resolve a directory regardless of
|
|
387
|
+
* milestone window (`cmdRoadmapAnalyze`'s `_phaseDirNames`,
|
|
388
|
+
* `cmdInitMilestoneOp`'s `diskPhaseDirs`) — which is why those callers used
|
|
389
|
+
* to hand-roll a `readdirSync` instead of calling either owner.
|
|
390
|
+
*
|
|
391
|
+
* `includeSentinels` is REQUIRED, with no default value. #3882/ADR-3473 §8.2:
|
|
392
|
+
* "a caller that wants sentinels asks for them explicitly" — obtaining
|
|
393
|
+
* sentinel-inclusion by silent omission is exactly the defect class this
|
|
394
|
+
* axis exists to close, so the call site is refused at COMPILE TIME without
|
|
395
|
+
* it, not merely documented against it here.
|
|
396
|
+
*
|
|
397
|
+
* Mirrors `listMilestonePhaseDirs`'s own absent/unreadable handling
|
|
398
|
+
* (ADR-3180 Decision 2): an ABSENT `phasesDir` is a real empty (a project
|
|
399
|
+
* with no phase directories yet), `scope: SCOPE.COMPLETE`; a `phasesDir`
|
|
400
|
+
* that EXISTS but cannot be read is a NON-answer, `scope: SCOPE.UNREADABLE`
|
|
401
|
+
* — a caller must not treat that empty list as "this project has no
|
|
402
|
+
* phases."
|
|
403
|
+
*/
|
|
404
|
+
function listAllPhaseDirs(phasesDir, opts) {
|
|
405
|
+
const { includeSentinels, phaseIdConvention = null } = opts;
|
|
406
|
+
if (!node_fs_1.default.existsSync(phasesDir))
|
|
407
|
+
return { value: [], scope: SCOPE.COMPLETE };
|
|
408
|
+
let names;
|
|
409
|
+
try {
|
|
410
|
+
names = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true })
|
|
411
|
+
.filter((e) => e.isDirectory())
|
|
412
|
+
.map((e) => e.name);
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
return { value: [], scope: SCOPE.UNREADABLE };
|
|
416
|
+
}
|
|
417
|
+
const value = names
|
|
418
|
+
.filter((name) => includeSentinels || !isSentinelPhaseId(name, phaseIdConvention ?? undefined))
|
|
419
|
+
.sort((a, b) => comparePhaseNum(a, b));
|
|
420
|
+
return { value, scope: SCOPE.COMPLETE };
|
|
421
|
+
}
|
|
422
|
+
function getArchivedPhaseDirs(cwd, wsOverride) {
|
|
343
423
|
// #2855: same workstream-scoped resolution as findPhaseInternal above, via
|
|
344
424
|
// the shared listArchiveVersionDirs helper. `phase.list --include-archived`
|
|
345
425
|
// (the primary non-init consumer) must not leak a different workstream's
|
|
346
426
|
// archive either.
|
|
347
427
|
const results = [];
|
|
348
|
-
for (const { version, archivePath } of listArchiveVersionDirs(cwd)) {
|
|
428
|
+
for (const { version, archivePath } of listArchiveVersionDirs(cwd, wsOverride)) {
|
|
349
429
|
const dirs = readSubdirectories(archivePath, true);
|
|
350
430
|
for (const dir of dirs) {
|
|
351
431
|
results.push({
|
|
@@ -358,9 +438,50 @@ function getArchivedPhaseDirs(cwd) {
|
|
|
358
438
|
}
|
|
359
439
|
return results;
|
|
360
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* #3804 — the CROSS-WORKSTREAM archive enumeration the audit surfaces need.
|
|
443
|
+
* getArchivedPhaseDirs above is deliberately #2855-SCOPED (an ambient
|
|
444
|
+
* workstream must not leak other trees' phases to findPhaseInternal), but
|
|
445
|
+
* audit-uat's charter (#2766: outstanding items do not stop mattering) is
|
|
446
|
+
* cross-workstream: enumerate the project root plus every workstream's own
|
|
447
|
+
* milestones tree, labeling workstream entries '<ws>/<version>' so
|
|
448
|
+
* acknowledge-by-label stays unambiguous. Deduped by full path (the same
|
|
449
|
+
* tree cannot be reached twice, but the guard keeps the invariant explicit).
|
|
450
|
+
*/
|
|
451
|
+
function getAllArchivedPhaseDirs(cwd) {
|
|
452
|
+
const out = [];
|
|
453
|
+
const seen = new Set();
|
|
454
|
+
const collect = (labelPrefix, wsOverride) => {
|
|
455
|
+
for (const archived of getArchivedPhaseDirs(cwd, wsOverride)) {
|
|
456
|
+
const rel = toPosixPath(node_path_1.default.relative(cwd, archived.fullPath));
|
|
457
|
+
if (seen.has(rel))
|
|
458
|
+
continue;
|
|
459
|
+
seen.add(rel);
|
|
460
|
+
out.push({ ...archived, milestone: `${labelPrefix}${archived.milestone}` });
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
collect('', null);
|
|
464
|
+
const workstreamsDir = node_path_1.default.join(planningRoot(cwd), 'workstreams');
|
|
465
|
+
try {
|
|
466
|
+
const wsEntries = node_fs_1.default.readdirSync(workstreamsDir, { withFileTypes: true })
|
|
467
|
+
.filter((e) => e.isDirectory())
|
|
468
|
+
.map((e) => e.name)
|
|
469
|
+
.sort()
|
|
470
|
+
.reverse();
|
|
471
|
+
for (const ws of wsEntries) {
|
|
472
|
+
collect(`${ws}/`, ws);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
/* no workstreams dir — the root pass above already ran */
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
361
480
|
module.exports = {
|
|
362
481
|
searchPhaseInDir,
|
|
363
482
|
findPhaseInternal,
|
|
364
483
|
getArchivedPhaseDirs,
|
|
484
|
+
getAllArchivedPhaseDirs,
|
|
365
485
|
listMilestonePhaseDirs,
|
|
486
|
+
listAllPhaseDirs,
|
|
366
487
|
};
|