@opengsd/gsd-core 1.12.0 → 1.14.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/.opencode/plugins/gsd-core.js +12 -0
- package/agents/gsd-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +458 -0
- package/agents/gsd-code-fixer.md +5 -5
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +345 -0
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +63 -35
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-plan-checker.md +76 -57
- package/agents/gsd-planner.md +14 -0
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-checker.md +19 -3
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +29 -0
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +23 -1
- package/bin/install.js +444 -134
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +1 -0
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +2 -1
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/surface.md +18 -8
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +532 -174
- package/gsd-core/bin/lib/adr-parser.cjs +1 -1
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +39 -22
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +528 -116
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-state.cjs +7 -1
- package/gsd-core/bin/lib/capability-validator.cjs +134 -5
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +198 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/commands.cjs +981 -79
- package/gsd-core/bin/lib/config-loader.cjs +4 -0
- package/gsd-core/bin/lib/config.cjs +153 -38
- package/gsd-core/bin/lib/core-utils.cjs +34 -7
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +343 -28
- package/gsd-core/bin/lib/edge-probe.cjs +14 -1
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +137 -23
- package/gsd-core/bin/lib/gap-checker.cjs +22 -13
- package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-integration.cjs +57 -5
- package/gsd-core/bin/lib/init-command-router.cjs +14 -0
- package/gsd-core/bin/lib/init.cjs +539 -60
- package/gsd-core/bin/lib/install-engine.cjs +199 -14
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
- package/gsd-core/bin/lib/install-profiles.cjs +36 -14
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
- package/gsd-core/bin/lib/io.cjs +35 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
- package/gsd-core/bin/lib/markdown-table.cjs +123 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +41 -10
- package/gsd-core/bin/lib/model-resolver.cjs +101 -10
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
- package/gsd-core/bin/lib/phase-id.cjs +412 -31
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase.cjs +941 -98
- package/gsd-core/bin/lib/plan-document.cjs +10 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
- package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
- package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
- package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
- package/gsd-core/bin/lib/roadmap.cjs +292 -69
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
- package/gsd-core/bin/lib/smart-entry.cjs +7 -9
- package/gsd-core/bin/lib/state-document.cjs +159 -32
- package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
- package/gsd-core/bin/lib/state-transition.cjs +465 -62
- package/gsd-core/bin/lib/state.cjs +906 -151
- package/gsd-core/bin/lib/surface.cjs +83 -10
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/uat.cjs +1420 -516
- package/gsd-core/bin/lib/update-context.cjs +36 -26
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +316 -23
- package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +531 -36
- package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
- package/gsd-core/references/agent-contracts.md +3 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe.md +17 -13
- package/gsd-core/references/execute-mvp-tdd.md +18 -16
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +12 -3
- package/gsd-core/references/mvp-concepts.md +2 -2
- package/gsd-core/references/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +75 -2
- package/gsd-core/references/planning-config.md +5 -1
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/tdd.md +17 -9
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/worktree-path-safety.md +112 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/phase-prompt.md +4 -0
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/add-backlog.md +2 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +2 -0
- package/gsd-core/workflows/audit-uat.md +2 -0
- package/gsd-core/workflows/autonomous.md +15 -10
- package/gsd-core/workflows/check-todos.md +5 -3
- package/gsd-core/workflows/cleanup.md +4 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
- package/gsd-core/workflows/code-review-fix.md +5 -3
- package/gsd-core/workflows/code-review.md +211 -43
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +40 -254
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +43 -13
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +15 -156
- package/gsd-core/workflows/edit-phase.md +2 -0
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +78 -159
- package/gsd-core/workflows/execute-plan.md +28 -15
- package/gsd-core/workflows/explore.md +2 -0
- package/gsd-core/workflows/extract-learnings.md +2 -0
- package/gsd-core/workflows/fast.md +6 -0
- package/gsd-core/workflows/forensics.md +2 -0
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +3 -1
- package/gsd-core/workflows/import.md +3 -3
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +2 -0
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +2 -0
- package/gsd-core/workflows/list-workspaces.md +2 -0
- package/gsd-core/workflows/manager.md +3 -3
- package/gsd-core/workflows/map-codebase.md +52 -3
- package/gsd-core/workflows/milestone-summary.md +2 -0
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +55 -13
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project.md +37 -205
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -0
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +19 -4
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
- package/gsd-core/workflows/plan-phase.md +144 -185
- package/gsd-core/workflows/plan-review-convergence.md +102 -10
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +30 -10
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +25 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
- package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +21 -4
- package/gsd-core/workflows/reapply-patches.md +79 -3
- package/gsd-core/workflows/remove-phase.md +2 -0
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +6 -2
- package/gsd-core/workflows/review.md +215 -10
- package/gsd-core/workflows/scan.md +2 -0
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings-integrations.md +9 -8
- package/gsd-core/workflows/settings.md +19 -6
- package/gsd-core/workflows/ship.md +10 -10
- package/gsd-core/workflows/sketch-wrap-up.md +2 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +24 -19
- package/gsd-core/workflows/spike-wrap-up.md +2 -0
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +2 -0
- package/gsd-core/workflows/sync-skills.md +12 -4
- package/gsd-core/workflows/thread.md +2 -0
- package/gsd-core/workflows/transition.md +2 -0
- package/gsd-core/workflows/ui-phase.md +26 -5
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +2 -0
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/update.md +48 -43
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work.md +68 -182
- package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-context-monitor.js +371 -27
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-node-runner.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +30 -5
- package/hooks/dist/gsd-read-guard.js +2 -0
- package/hooks/dist/gsd-read-injection-scanner.js +5 -5
- package/hooks/dist/gsd-secret-read-guard.js +1105 -0
- package/hooks/dist/gsd-statusline.js +18 -10
- package/hooks/dist/gsd-validate-commit.sh +474 -7
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +42 -16
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-context-monitor.js +371 -27
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-node-runner.sh +1 -0
- package/hooks/gsd-prompt-guard.js +30 -5
- package/hooks/gsd-read-guard.js +2 -0
- package/hooks/gsd-read-injection-scanner.js +5 -5
- package/hooks/gsd-secret-read-guard.js +1105 -0
- package/hooks/gsd-statusline.js +18 -10
- package/hooks/gsd-validate-commit.sh +474 -7
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/hooks.json +6 -0
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +13 -9
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +11 -4
- package/scripts/check-contract-drift.cjs +4 -1
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +133 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +17 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +67 -15
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
- package/scripts/lint-phase-enumeration-drift.cjs +24 -6
- package/scripts/lint-phase-id-drift.cjs +465 -15
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-response-language-coverage.cjs +530 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-test-file-count.allowlist.json +4 -1
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +22 -0
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +1 -0
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +2 -1
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -143,6 +143,7 @@ function parseXmlTasks(content) {
|
|
|
143
143
|
acceptanceCriteria: [],
|
|
144
144
|
done: null,
|
|
145
145
|
trackerId: null,
|
|
146
|
+
tdd: null,
|
|
146
147
|
};
|
|
147
148
|
}
|
|
148
149
|
return {
|
|
@@ -154,6 +155,7 @@ function parseXmlTasks(content) {
|
|
|
154
155
|
acceptanceCriteria: splitCriteria(elementBody(block, 'acceptance_criteria')),
|
|
155
156
|
done: collapseWhitespace(elementBody(block, 'done')),
|
|
156
157
|
trackerId: tagAttribute(openTag, 'tracker-id'),
|
|
158
|
+
tdd: tagAttribute(openTag, 'tdd'),
|
|
157
159
|
};
|
|
158
160
|
});
|
|
159
161
|
}
|
|
@@ -172,6 +174,7 @@ function parseMarkdownTasks(content) {
|
|
|
172
174
|
acceptanceCriteria: [],
|
|
173
175
|
done: null,
|
|
174
176
|
trackerId: null,
|
|
177
|
+
tdd: null,
|
|
175
178
|
}));
|
|
176
179
|
}
|
|
177
180
|
// ─── Objective ────────────────────────────────────────────────────────────────
|
|
@@ -247,8 +250,15 @@ function parsePlanDocument(content, planPath = '') {
|
|
|
247
250
|
const hintStr = String(fmAgentHint).trim();
|
|
248
251
|
agentHint = hintStr !== '' ? hintStr : null;
|
|
249
252
|
}
|
|
253
|
+
let planType = null;
|
|
254
|
+
const fmType = fm['type'];
|
|
255
|
+
if (fmType !== undefined) {
|
|
256
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- FrontmatterValue scalar-to-string
|
|
257
|
+
planType = String(fmType);
|
|
258
|
+
}
|
|
250
259
|
return {
|
|
251
260
|
objective: extractObjective(content) || fm['objective'] || null,
|
|
261
|
+
type: planType,
|
|
252
262
|
declaredWave,
|
|
253
263
|
dependsOn,
|
|
254
264
|
autonomous,
|
|
@@ -94,6 +94,9 @@ const { parseMarkdownTable, matchTableSchema } = markdownTable;
|
|
|
94
94
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
95
95
|
const coreUtilsMod = require("./core-utils.cjs");
|
|
96
96
|
const { normalizeLineEndings } = coreUtilsMod;
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
98
|
+
const securityMod = require("./security.cjs");
|
|
99
|
+
const { tryWithinRoot, PathAcceptance, isContainedIn } = securityMod;
|
|
97
100
|
/**
|
|
98
101
|
* The wire schema version. A consumer MUST reject any value other than this
|
|
99
102
|
* one rather than best-effort-parsing an unknown shape.
|
|
@@ -184,32 +187,45 @@ function toPosix(value) {
|
|
|
184
187
|
* directory like `.planning-evil/` that merely shares a string prefix. Pure
|
|
185
188
|
* string comparison, no I/O — callers own their own `fs.realpathSync` call
|
|
186
189
|
* (and its own not-found/broken-symlink handling).
|
|
190
|
+
*
|
|
191
|
+
* NOT an independent containment implementation — it is the comparison step
|
|
192
|
+
* of one, and that comparison now comes from `security.cts`'s exported
|
|
193
|
+
* `isContainedIn` rather than being redeclared here. `readDocument` below
|
|
194
|
+
* realpaths target and root itself (to keep its own exists-vs-escaped
|
|
195
|
+
* tri-state) and calls `isContainedIn` directly; `isPathContained` gets its
|
|
196
|
+
* containment DECISION from the canonical `tryWithinRoot` predicate instead
|
|
197
|
+
* (ADR-4650 decision 6) and never called this comparison directly. Every
|
|
198
|
+
* caller owns its own resolution.
|
|
187
199
|
*/
|
|
188
|
-
function isWithinRoot(resolvedTarget, resolvedRoot) {
|
|
189
|
-
return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + node_path_1.default.sep);
|
|
190
|
-
}
|
|
191
200
|
/**
|
|
192
|
-
* Containment check for a path (file OR directory)
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* `
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
+
* Containment check for a path (file OR directory), used where the caller
|
|
202
|
+
* does not need to distinguish "target vanished / broken symlink" from
|
|
203
|
+
* "target resolved but escapes root" — both degrade the same way at every
|
|
204
|
+
* call site that uses this (an escaped or unresolvable phase directory is
|
|
205
|
+
* treated identically to an unreadable one). `readDocument` below needs
|
|
206
|
+
* that distinction for its own exists/readable tri-state, so it keeps its
|
|
207
|
+
* own inline `realpathSync` calls and calls `isContainedIn` directly instead
|
|
208
|
+
* of this wrapper.
|
|
209
|
+
*
|
|
210
|
+
* The containment DECISION comes from the canonical `tryWithinRoot`
|
|
211
|
+
* predicate (ADR-4650 decision 6: a wrapper may decide HOW to degrade,
|
|
212
|
+
* never WHETHER a path is contained). Must-exist stays this module's OWN
|
|
213
|
+
* degradation condition, applied after: `tryWithinRoot` deliberately accepts
|
|
214
|
+
* a not-yet-created path under the root (ancestor-walk realpath), but every
|
|
215
|
+
* caller of `isPathContained` guards an `fs` read that is about to happen
|
|
216
|
+
* against an already-existing directory, so a vanished/unresolvable path
|
|
217
|
+
* must still degrade the same as an escaped one.
|
|
201
218
|
*/
|
|
202
219
|
function isPathContained(target, root) {
|
|
203
|
-
|
|
204
|
-
|
|
220
|
+
if (tryWithinRoot(target, root, PathAcceptance.AbsoluteInsideRoot) === null)
|
|
221
|
+
return false;
|
|
205
222
|
try {
|
|
206
|
-
|
|
207
|
-
realRoot = node_fs_1.default.realpathSync(root);
|
|
223
|
+
node_fs_1.default.realpathSync(target);
|
|
208
224
|
}
|
|
209
225
|
catch {
|
|
210
226
|
return false;
|
|
211
227
|
}
|
|
212
|
-
return
|
|
228
|
+
return true;
|
|
213
229
|
}
|
|
214
230
|
function readDocument(filePath, root) {
|
|
215
231
|
let stat;
|
|
@@ -234,7 +250,7 @@ function readDocument(filePath, root) {
|
|
|
234
250
|
// here — the same non-answer `readDocument` already gives "not exists".
|
|
235
251
|
return { text: null, exists: false, readable: false };
|
|
236
252
|
}
|
|
237
|
-
if (!
|
|
253
|
+
if (!isContainedIn(realTarget, realRoot)) {
|
|
238
254
|
return { text: null, exists: true, readable: false };
|
|
239
255
|
}
|
|
240
256
|
try {
|
|
@@ -37,7 +37,19 @@ const { isPhaseComplete } = verificationMod;
|
|
|
37
37
|
const scanPhasePlans = require("./plan-scan.cjs");
|
|
38
38
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
39
39
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
40
|
-
|
|
40
|
+
// #612: `resolvePhaseIdConvention` is the federated (workstream -> root)
|
|
41
|
+
// `phase_id_convention` reader, from the same §7 owner module `planningPaths`
|
|
42
|
+
// comes from. Resolved once in `buildPlanningSnapshot` — see the
|
|
43
|
+
// `phaseIdConvention` field's comment for why one resolution point matters.
|
|
44
|
+
// #4257: `resolveEnvWorkstream` is that same module's ONE owner of the env
|
|
45
|
+
// workstream discriminator `planningDir` applies — the name W002's scope
|
|
46
|
+
// clause prints comes from the same resolution point that scoped the reads.
|
|
47
|
+
const { planningPaths, planningRoot, resolvePhaseIdConvention, resolveEnvWorkstream } = planningWorkspace;
|
|
48
|
+
// #4257: canonical CommonMark code strippers (markdown-sectionizer is the
|
|
49
|
+
// repo's T0 structural seam, adopted per the #2365 composition order — fenced
|
|
50
|
+
// blocks first, then inline spans) so the `statePhaseTokens` harvest sees
|
|
51
|
+
// PROSE, not quoted literals.
|
|
52
|
+
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
41
53
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
42
54
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
43
55
|
const frontmatterMod = require("./frontmatter.cjs");
|
|
@@ -64,7 +76,15 @@ const configLoaderMod = require("./config-loader.cjs");
|
|
|
64
76
|
const { isGitIgnored } = configLoaderMod;
|
|
65
77
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
66
78
|
const phaseIdMod = require("./phase-id.cjs");
|
|
67
|
-
|
|
79
|
+
// #612: `phaseHeadingPrefixSrcFor`/`PHASE_HEADING_BASELINE` SELECT a heading
|
|
80
|
+
// intro by convention (a convention-less call compiles the byte-identical base
|
|
81
|
+
// source the literal it replaced spelled); `isSentinelPhaseId` gets its bracket
|
|
82
|
+
// reading only when handed the convention explicitly.
|
|
83
|
+
const { PHASE_NUMBER_TOKEN_SOURCE, OPTIONAL_PHASE_TAG_SOURCE, stripProjectCodePrefix, phaseHeadingPrefixSrcFor, PHASE_HEADING_BASELINE, isSentinelPhaseId, scopeToPhase, } = phaseIdMod;
|
|
84
|
+
// #612: `phaseTokenFromDir` is the convention-SELECTED counterpart of
|
|
85
|
+
// `PHASE_TOKEN_FROM_DIR_RE` — handed no convention it delegates to that very
|
|
86
|
+
// regex, so a legacy repo's tokenization is unchanged. `checkBracketCoherence`
|
|
87
|
+
// re-homed into `validate.cts` when #3309 deleted its `verify.cts` neighbours.
|
|
68
88
|
const validate_cjs_1 = require("./validate.cjs");
|
|
69
89
|
// ─── worstScope — the one new piece of coordination logic ───────────────────
|
|
70
90
|
/**
|
|
@@ -97,9 +117,12 @@ function worstScope(...scopes) {
|
|
|
97
117
|
* uncorrelated (isPhaseComplete's readability check never re-derives or
|
|
98
118
|
* requires scanPhasePlans, and vice versa).
|
|
99
119
|
*/
|
|
100
|
-
function buildPhaseSnapshot(phasesDir, dir) {
|
|
120
|
+
function buildPhaseSnapshot(phasesDir, dir, convention) {
|
|
101
121
|
const fullPhaseDir = node_path_1.default.join(phasesDir, dir);
|
|
102
|
-
|
|
122
|
+
// #612: the snapshot's single federated convention resolution rides into
|
|
123
|
+
// completion, so a bracket phase dir resolves and scopes its verification
|
|
124
|
+
// report exactly like its legacy twin.
|
|
125
|
+
const completionResult = isPhaseComplete(fullPhaseDir, { convention });
|
|
103
126
|
const scanResult = scanPhasePlans(fullPhaseDir);
|
|
104
127
|
return {
|
|
105
128
|
dir,
|
|
@@ -148,9 +171,11 @@ function buildPhaseSnapshot(phasesDir, dir) {
|
|
|
148
171
|
* a whole-body fallback, together.
|
|
149
172
|
* - `statePhaseTokens` scans the WHOLE document (`verify.cts`'s exact
|
|
150
173
|
* `PHASE_NUMBER_TOKEN_SOURCE` regex, relocated verbatim from
|
|
151
|
-
* `verify.cts:1731-1735
|
|
152
|
-
*
|
|
153
|
-
*
|
|
174
|
+
* `verify.cts:1731-1735`; #4257 adds the left word boundary and the
|
|
175
|
+
* fenced-block/inline-span strip — see the harvest site's comment), not
|
|
176
|
+
* just the Current Position section, so it is NOT degraded to `TRUNCATED`
|
|
177
|
+
* by a missing section header — it stays `COMPLETE` whenever the file
|
|
178
|
+
* itself was read successfully.
|
|
154
179
|
*/
|
|
155
180
|
function buildStateFields(statePath) {
|
|
156
181
|
let content;
|
|
@@ -210,7 +235,30 @@ function buildStateFields(statePath) {
|
|
|
210
235
|
scope: currentPositionScope,
|
|
211
236
|
});
|
|
212
237
|
const statePhaseTokens = {
|
|
213
|
-
|
|
238
|
+
// #4257: harvest PROSE phase references, not every literal token match.
|
|
239
|
+
// Two precisions over the pre-#4257 verbatim relocation of verify.cts's
|
|
240
|
+
// scan (which was `[Pp]hase\s+(TOKEN)`, unanchored, over the raw file):
|
|
241
|
+
//
|
|
242
|
+
// 1. Strip fenced code blocks, then inline code spans (the #2365
|
|
243
|
+
// composition order, via the canonical markdown-sectionizer seam) —
|
|
244
|
+
// a token inside backticks is a QUOTED LITERAL (a ledger row quoting
|
|
245
|
+
// `` `/gsd-execute-phase 5` `` or `` `- [ ] **Phase 40:` `` from a
|
|
246
|
+
// sibling roadmap), not a reference. Pinned tradeoff: a GENUINE
|
|
247
|
+
// reference written in backticks stops counting too — a quoted
|
|
248
|
+
// literal and a reference are indistinguishable inside a code span.
|
|
249
|
+
// 2. Left word boundary `(?<![-\w])` — the `-phase 5` tail of GSD's own
|
|
250
|
+
// command names (`/gsd-execute-phase 5`, bare or in prose) is a
|
|
251
|
+
// command mention, not a reference, and word-suffixed carriers
|
|
252
|
+
// (`myphase 5`) never were references. `Phase 5` at line start,
|
|
253
|
+
// `**Phase 5:**`, `(Phase 5)`, `[Phase 5]`, and `### Phase 5:` all
|
|
254
|
+
// still harvest — the char before `Phase` is not in `[-\w]`.
|
|
255
|
+
//
|
|
256
|
+
// Still scans the WHOLE document (frontmatter included — the `Phase: 3`
|
|
257
|
+
// field syntax never matched, `phase` is followed by a colon, not `\s`),
|
|
258
|
+
// still `COMPLETE` whenever the file itself was read successfully.
|
|
259
|
+
value: [
|
|
260
|
+
...(0, markdown_sectionizer_cjs_1.stripInlineCode)((0, markdown_sectionizer_cjs_1.stripFencedCode)(content).text).matchAll(new RegExp(`(?<![-\\w])[Pp]hase\\s+(${PHASE_NUMBER_TOKEN_SOURCE})`, 'g')),
|
|
261
|
+
].map((m) => m[1]),
|
|
214
262
|
scope: SCOPE.COMPLETE,
|
|
215
263
|
};
|
|
216
264
|
return { currentPhaseLabel, statePhaseTokens, stateStatus };
|
|
@@ -462,18 +510,31 @@ function buildProjectSectionsField(cwd) {
|
|
|
462
510
|
* only the mismatches" to "record every attribution" — this field exposes
|
|
463
511
|
* the parsed fact; the future W021/W026 rules make the mismatch judgment.
|
|
464
512
|
*/
|
|
465
|
-
function buildRoadmapDeclaredPhasesField(roadmapPath) {
|
|
513
|
+
function buildRoadmapDeclaredPhasesField(roadmapPath, convention) {
|
|
466
514
|
if (!node_fs_1.default.existsSync(roadmapPath)) {
|
|
467
|
-
return {
|
|
515
|
+
return {
|
|
516
|
+
declared: { value: [], scope: SCOPE.UNREADABLE },
|
|
517
|
+
sentinelTokens: { value: [], scope: SCOPE.UNREADABLE },
|
|
518
|
+
};
|
|
468
519
|
}
|
|
469
520
|
let content;
|
|
470
521
|
try {
|
|
471
522
|
content = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
|
|
472
523
|
}
|
|
473
524
|
catch {
|
|
474
|
-
return {
|
|
525
|
+
return {
|
|
526
|
+
declared: { value: [], scope: SCOPE.UNREADABLE },
|
|
527
|
+
sentinelTokens: { value: [], scope: SCOPE.UNREADABLE },
|
|
528
|
+
};
|
|
475
529
|
}
|
|
476
|
-
|
|
530
|
+
// #612: the declared-phase scan is SELECTED by the resolved convention — a
|
|
531
|
+
// non-bracket repo compiles the byte-identical pattern sources this call
|
|
532
|
+
// compiled before, so its declared set is unchanged. `sentinelPhases` is the
|
|
533
|
+
// same call's third output (empty off the bracket convention) and is surfaced
|
|
534
|
+
// rather than filtered in place: `roadmapPhases` feeds both a membership check
|
|
535
|
+
// (W002's valid-phase set) and a missing-directory warning (W006), and only
|
|
536
|
+
// the latter should ignore an icebox item.
|
|
537
|
+
const { roadmapPhases, sentinelPhases } = (0, validate_cjs_1.buildRoadmapPhaseVariants)(content, convention);
|
|
477
538
|
const milestoneByPhase = new Map();
|
|
478
539
|
const sectionRx = /^#{1,3}\s+(?:\[[^\]]{1,200}\]\s*)?.*v(\d+\.\d+)/gim;
|
|
479
540
|
const sections = [];
|
|
@@ -497,7 +558,10 @@ function buildRoadmapDeclaredPhasesField(roadmapPath) {
|
|
|
497
558
|
phaseId,
|
|
498
559
|
milestone: milestoneByPhase.get(phaseId) ?? null,
|
|
499
560
|
}));
|
|
500
|
-
return {
|
|
561
|
+
return {
|
|
562
|
+
declared: { value, scope: SCOPE.COMPLETE },
|
|
563
|
+
sentinelTokens: { value: [...sentinelPhases], scope: SCOPE.COMPLETE },
|
|
564
|
+
};
|
|
501
565
|
}
|
|
502
566
|
/**
|
|
503
567
|
* Resolve `roadmapPhaseCheckboxes` — parsed `[x]`/`[ ]` checkbox state per
|
|
@@ -518,7 +582,7 @@ function buildRoadmapDeclaredPhasesField(roadmapPath) {
|
|
|
518
582
|
* diagnostic (W011) whose entire purpose is flagging when the two DISAGREE —
|
|
519
583
|
* reading the data is not re-litigating who is authoritative.
|
|
520
584
|
*/
|
|
521
|
-
function buildRoadmapPhaseCheckboxesField(roadmapPath) {
|
|
585
|
+
function buildRoadmapPhaseCheckboxesField(roadmapPath, convention) {
|
|
522
586
|
if (!node_fs_1.default.existsSync(roadmapPath)) {
|
|
523
587
|
return { value: {}, scope: SCOPE.UNREADABLE };
|
|
524
588
|
}
|
|
@@ -529,7 +593,15 @@ function buildRoadmapPhaseCheckboxesField(roadmapPath) {
|
|
|
529
593
|
catch {
|
|
530
594
|
return { value: {}, scope: SCOPE.UNREADABLE };
|
|
531
595
|
}
|
|
532
|
-
|
|
596
|
+
// #612: the `Phase\s+` label intro is SELECTED, exactly as
|
|
597
|
+
// `buildNotStartedPhaseVariants` (`validate.cts`) selects it for the same
|
|
598
|
+
// ROADMAP checklist shape — this field is what W006's not-started exclusion
|
|
599
|
+
// now reads instead of that helper, so the two must recognize the same
|
|
600
|
+
// checklist lines or a bracket repo's `- [ ] **[GSD.02] 05: Name**` entries
|
|
601
|
+
// vanish from the exclusion set and every unstarted bracket phase gains a
|
|
602
|
+
// W006. NON-capturing (`capturing` defaults false), so the phase token stays
|
|
603
|
+
// group 2 and the legacy repo compiles a byte-identical source.
|
|
604
|
+
const checkboxRe = new RegExp(`-\\s*\\[([xX ])\\].*?${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention)}0*(${PHASE_NUMBER_TOKEN_SOURCE})${OPTIONAL_PHASE_TAG_SOURCE}[:\\s]`, 'gi');
|
|
533
605
|
const value = {};
|
|
534
606
|
let m;
|
|
535
607
|
while ((m = checkboxRe.exec(content)) !== null) {
|
|
@@ -537,6 +609,42 @@ function buildRoadmapPhaseCheckboxesField(roadmapPath) {
|
|
|
537
609
|
}
|
|
538
610
|
return { value, scope: SCOPE.COMPLETE };
|
|
539
611
|
}
|
|
612
|
+
/**
|
|
613
|
+
* Resolve `roadmapBracketIncoherences` — W021's bracket half (#612). Delegates
|
|
614
|
+
* wholly to `checkBracketCoherence` (`validate.cjs`), which is pure and owns
|
|
615
|
+
* both sub-checks; this builder only supplies the ROADMAP text and the
|
|
616
|
+
* convention gate.
|
|
617
|
+
*
|
|
618
|
+
* GATED, not merely filtered downstream: off the bracket convention the ROADMAP
|
|
619
|
+
* is never parsed for this at all and the field is a `COMPLETE`-scoped empty
|
|
620
|
+
* list. Inferring 'bracket' from the SHAPE of a matched heading would run a
|
|
621
|
+
* repo-failing check against a repo that never opted in — a legacy ROADMAP
|
|
622
|
+
* containing `### [RFC.2119] 5:` is legal legacy content, and that is the exact
|
|
623
|
+
* regression PR-2's round 1 killed the original ungated design over.
|
|
624
|
+
*/
|
|
625
|
+
function buildRoadmapBracketIncoherencesField(roadmapPath, convention) {
|
|
626
|
+
// File-readability is decided FIRST, so `scope` means the same thing on every
|
|
627
|
+
// repo: UNREADABLE iff ROADMAP.md could not be read, never "this convention
|
|
628
|
+
// was skipped." Ordering the convention gate first would have made an absent
|
|
629
|
+
// ROADMAP.md report COMPLETE on a legacy repo and UNREADABLE on a bracket one
|
|
630
|
+
// — the same "empty, nothing to say" state wearing two different scopes, which
|
|
631
|
+
// is precisely the non-answer/answer distinction ADR-3180 §8.1 gives `scope`
|
|
632
|
+
// to carry.
|
|
633
|
+
if (!node_fs_1.default.existsSync(roadmapPath))
|
|
634
|
+
return { value: [], scope: SCOPE.UNREADABLE };
|
|
635
|
+
// A non-bracket repo has no bracket incoherences BY DEFINITION — a real,
|
|
636
|
+
// COMPLETE answer, not a skipped read.
|
|
637
|
+
if (convention !== 'bracket')
|
|
638
|
+
return { value: [], scope: SCOPE.COMPLETE };
|
|
639
|
+
let content;
|
|
640
|
+
try {
|
|
641
|
+
content = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
return { value: [], scope: SCOPE.UNREADABLE };
|
|
645
|
+
}
|
|
646
|
+
return { value: (0, validate_cjs_1.checkBracketCoherence)(content), scope: SCOPE.COMPLETE };
|
|
647
|
+
}
|
|
540
648
|
/**
|
|
541
649
|
* Resolve `researchValidationStatus` — per phase directory, whether its
|
|
542
650
|
* `*-RESEARCH.md` contains the literal heading `## Validation Architecture`,
|
|
@@ -560,7 +668,7 @@ function buildRoadmapPhaseCheckboxesField(roadmapPath) {
|
|
|
560
668
|
* deliberate fail-open match to the pre-migration behavior, not a scope
|
|
561
669
|
* degradation, since the original never surfaced these failures either.
|
|
562
670
|
*/
|
|
563
|
-
function buildResearchValidationStatusField(phasesDir, phaseDirNames, enumerationScope) {
|
|
671
|
+
function buildResearchValidationStatusField(phasesDir, phaseDirNames, enumerationScope, convention) {
|
|
564
672
|
const value = phaseDirNames.map((dir) => {
|
|
565
673
|
const fullPhaseDir = node_path_1.default.join(phasesDir, dir);
|
|
566
674
|
let files;
|
|
@@ -574,7 +682,9 @@ function buildResearchValidationStatusField(phasesDir, phaseDirNames, enumeratio
|
|
|
574
682
|
// phase-numbered-artifact predicates, so a stray cross-phase
|
|
575
683
|
// -RESEARCH.md/-VALIDATION.md sitting in the wrong directory cannot flip
|
|
576
684
|
// this phase's flags — mirrors core-utils.cts's getPhaseFileStats.
|
|
577
|
-
|
|
685
|
+
// #612: the snapshot's federated convention threaded, so a bracket dir
|
|
686
|
+
// scopes by its real token instead of the include-everything fail-safe.
|
|
687
|
+
const scopedFiles = scopeToPhase(files, dir, convention);
|
|
578
688
|
const researchFile = scopedFiles.find((f) => f.endsWith('-RESEARCH.md'));
|
|
579
689
|
const hasValidationMd = scopedFiles.some((f) => f.endsWith('-VALIDATION.md'));
|
|
580
690
|
let hasValidationArchitecture = false;
|
|
@@ -699,7 +809,7 @@ function buildAllPhaseDirNamesField(phasesDir) {
|
|
|
699
809
|
* present-but-unreadable per-archive-dir entry is silently skipped, mirroring
|
|
700
810
|
* `forEachArchivedPhaseToken`'s own per-directory `catch { /* absent/unreadable *\/ }`.
|
|
701
811
|
*/
|
|
702
|
-
function buildArchivedPhaseTokensField(planBase) {
|
|
812
|
+
function buildArchivedPhaseTokensField(planBase, convention) {
|
|
703
813
|
const milestonesDir = node_path_1.default.join(planBase, 'milestones');
|
|
704
814
|
let archiveDirs;
|
|
705
815
|
try {
|
|
@@ -720,9 +830,19 @@ function buildArchivedPhaseTokensField(planBase) {
|
|
|
720
830
|
for (const e of entries) {
|
|
721
831
|
if (!e.isDirectory())
|
|
722
832
|
continue;
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
833
|
+
// #612: composed, not chosen. The convention-aware extractor decides
|
|
834
|
+
// WHICH directory shapes are recognized (so an archived
|
|
835
|
+
// `{CODE}.{MM}-{PP}-slug` is seen at all — `PHASE_TOKEN_FROM_DIR_RE`
|
|
836
|
+
// rejects it outright, which is why every archived bracket phase used
|
|
837
|
+
// to still draw a W006/W002); `stripProjectCodePrefix` then normalizes
|
|
838
|
+
// the token it returns. The strip is a no-op on every bracket token
|
|
839
|
+
// (`01`, `01.02` — the `{CODE}.{MM}` prefix is not part of the token)
|
|
840
|
+
// and does the #2528 work on legacy ones (`MEM-05` -> `05`), so neither
|
|
841
|
+
// side loses its case. Handed no convention, `phaseTokenFromDir`
|
|
842
|
+
// delegates to `PHASE_TOKEN_FROM_DIR_RE` itself — legacy is unchanged.
|
|
843
|
+
const token = (0, validate_cjs_1.phaseTokenFromDir)(e.name, convention);
|
|
844
|
+
if (token)
|
|
845
|
+
value.push(stripProjectCodePrefix(token));
|
|
726
846
|
}
|
|
727
847
|
}
|
|
728
848
|
catch {
|
|
@@ -740,7 +860,7 @@ function buildArchivedPhaseTokensField(planBase) {
|
|
|
740
860
|
* ROADMAP.md degrades to an empty list, mirroring every other
|
|
741
861
|
* ROADMAP-sourced field's absent-file handling.
|
|
742
862
|
*/
|
|
743
|
-
function buildCurrentMilestoneRoadmapPhaseIdsField(cwd, roadmapPath) {
|
|
863
|
+
function buildCurrentMilestoneRoadmapPhaseIdsField(cwd, roadmapPath, convention) {
|
|
744
864
|
if (!node_fs_1.default.existsSync(roadmapPath))
|
|
745
865
|
return { value: [], scope: SCOPE.UNREADABLE };
|
|
746
866
|
let content;
|
|
@@ -753,8 +873,32 @@ function buildCurrentMilestoneRoadmapPhaseIdsField(cwd, roadmapPath) {
|
|
|
753
873
|
const scoped = extractCurrentMilestone(content, cwd);
|
|
754
874
|
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal
|
|
755
875
|
// mirror of OPTIONAL_PHASE_TAG_SOURCE) — verbatim from `verify.cts:2366`.
|
|
756
|
-
|
|
757
|
-
|
|
876
|
+
//
|
|
877
|
+
// #612: this scan is convention-AGNOSTIC in POSTURE — W026 is ungated, and
|
|
878
|
+
// bug-557 pins it with an empty config so it fires on every repo — but its
|
|
879
|
+
// heading grammar is still SELECTED, never widened. Under bracket the intro
|
|
880
|
+
// CAPTURES, so the phase token moves to group 2 and `bracketGroup` carries
|
|
881
|
+
// that offset; off bracket the source is byte-identical to the literal above
|
|
882
|
+
// and `bracketGroup` is 0. Inferring the convention from a matched bracket's
|
|
883
|
+
// shape would run a repo-failing check against a repo that never opted in.
|
|
884
|
+
const bracketGroup = convention === 'bracket' ? 1 : 0;
|
|
885
|
+
const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention, Boolean(bracketGroup))}(${PHASE_NUMBER_TOKEN_SOURCE})(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`, 'gi');
|
|
886
|
+
const value = [];
|
|
887
|
+
for (const m of scoped.matchAll(phasePattern)) {
|
|
888
|
+
const bracketId = bracketGroup ? m[1] : undefined;
|
|
889
|
+
const phaseNum = m[1 + bracketGroup];
|
|
890
|
+
// A bracket sentinel is an ICEBOX item, not an unstarted phase — it
|
|
891
|
+
// legitimately has no directory, so leaving it in this list makes W026
|
|
892
|
+
// ("STATE says milestone complete but ROADMAP lists an unstarted phase")
|
|
893
|
+
// fire on every bracket repo that keeps an icebox. Filtered here rather
|
|
894
|
+
// than in `RULE_W026` because the bracket id is only visible at the match:
|
|
895
|
+
// the emitted token is `07`, and sentinel-ness lives in the `[GSD.999]`
|
|
896
|
+
// milestone this scan just discarded. This field's only consumer is W026
|
|
897
|
+
// (see its own doc comment above).
|
|
898
|
+
if (bracketId && isSentinelPhaseId(`${bracketId}-${phaseNum}`, 'bracket'))
|
|
899
|
+
continue;
|
|
900
|
+
value.push(phaseNum);
|
|
901
|
+
}
|
|
758
902
|
return { value, scope: SCOPE.COMPLETE };
|
|
759
903
|
}
|
|
760
904
|
/**
|
|
@@ -853,11 +997,39 @@ function buildPerPhasePlanScanFields(phasesDir, phaseDirNames, enumerationScope)
|
|
|
853
997
|
*/
|
|
854
998
|
function buildPlanningSnapshot(cwd) {
|
|
855
999
|
const paths = planningPaths(cwd);
|
|
1000
|
+
// #612: ONE federated (workstream -> root) resolution for the whole snapshot.
|
|
1001
|
+
// See the `phaseIdConvention` field's comment for why one resolution point is
|
|
1002
|
+
// load-bearing rather than a micro-optimisation.
|
|
1003
|
+
const phaseIdConvention = resolvePhaseIdConvention(cwd) ?? null;
|
|
1004
|
+
// #4257: the workstream `planningPaths(cwd)` just scoped every read to
|
|
1005
|
+
// (its `planningDir` call applies this exact discriminator when handed no
|
|
1006
|
+
// `ws`), resolved through the same owner so W002's scope clause names the
|
|
1007
|
+
// scope the valid set was ACTUALLY built from.
|
|
1008
|
+
const workstream = resolveEnvWorkstream();
|
|
856
1009
|
const milestone = getMilestoneInfo(cwd);
|
|
1010
|
+
// #612: deliberately LEFT to `listMilestonePhaseDirs`'s own lazy resolve —
|
|
1011
|
+
// this call is byte-identical to upstream's.
|
|
1012
|
+
//
|
|
1013
|
+
// Passing `phaseIdConvention` here would NOT be a no-op, which is exactly why
|
|
1014
|
+
// it is not passed. The lazy path resolves `resolvePhaseIdConvention(cwd, ws)`
|
|
1015
|
+
// with this call's `ws`, which defaults to `null` — the PROJECT-only reading,
|
|
1016
|
+
// with no root fallback. The field above is resolved with `ws` undefined,
|
|
1017
|
+
// i.e. the FEDERATED workstream -> root reading. On a workstream repo whose
|
|
1018
|
+
// root opts into bracket while the workstream config does not, the two answers
|
|
1019
|
+
// genuinely differ, and substituting one for the other would silently re-scope
|
|
1020
|
+
// `phaseDirs` — a change this PR does not need and no test covers. The
|
|
1021
|
+
// federation guarantee PR-2 exists to deliver is delivered where it is
|
|
1022
|
+
// observable: in the rules that read `snapshot.phaseIdConvention`. Per-phase
|
|
1023
|
+
// completion and the research/validation scoping below deliberately use the
|
|
1024
|
+
// FEDERATED `phaseIdConvention` (the same value `snapshot.phaseIdConvention`
|
|
1025
|
+
// publishes), while `phaseDirs` keeps `listMilestonePhaseDirs`'s lazy
|
|
1026
|
+
// PROJECT-only resolve — the divergence documented above is unchanged by
|
|
1027
|
+
// this thread. No behavior change.
|
|
857
1028
|
const phaseDirs = listMilestonePhaseDirs(paths.phases, { cwd });
|
|
858
|
-
const phasesValue = phaseDirs.value.map((dir) => buildPhaseSnapshot(paths.phases, dir));
|
|
1029
|
+
const phasesValue = phaseDirs.value.map((dir) => buildPhaseSnapshot(paths.phases, dir, phaseIdConvention));
|
|
859
1030
|
const stateFields = buildStateFields(paths.state);
|
|
860
1031
|
const allPhaseDirNames = buildAllPhaseDirNamesField(paths.phases);
|
|
1032
|
+
const roadmapDeclared = buildRoadmapDeclaredPhasesField(paths.roadmap, phaseIdConvention);
|
|
861
1033
|
const perPhasePlanScanFields = buildPerPhasePlanScanFields(paths.phases, allPhaseDirNames.value, allPhaseDirNames.scope);
|
|
862
1034
|
return {
|
|
863
1035
|
cwd: node_path_1.default.resolve(cwd),
|
|
@@ -875,17 +1047,21 @@ function buildPlanningSnapshot(cwd) {
|
|
|
875
1047
|
projectSections: buildProjectSectionsField(cwd),
|
|
876
1048
|
statePhaseTokens: stateFields.statePhaseTokens,
|
|
877
1049
|
stateStatus: stateFields.stateStatus,
|
|
878
|
-
roadmapDeclaredPhases:
|
|
879
|
-
roadmapPhaseCheckboxes: buildRoadmapPhaseCheckboxesField(paths.roadmap),
|
|
880
|
-
researchValidationStatus: buildResearchValidationStatusField(paths.phases, phaseDirs.value, phaseDirs.scope),
|
|
1050
|
+
roadmapDeclaredPhases: roadmapDeclared.declared,
|
|
1051
|
+
roadmapPhaseCheckboxes: buildRoadmapPhaseCheckboxesField(paths.roadmap, phaseIdConvention),
|
|
1052
|
+
researchValidationStatus: buildResearchValidationStatusField(paths.phases, phaseDirs.value, phaseDirs.scope, phaseIdConvention),
|
|
881
1053
|
milestoneArchiveStatus: buildMilestoneArchiveStatusField(cwd),
|
|
882
1054
|
planningRootFiles: buildPlanningRootFilesField(cwd),
|
|
883
1055
|
allPhaseDirNames,
|
|
884
|
-
archivedPhaseTokens: buildArchivedPhaseTokensField(paths.planning),
|
|
885
|
-
currentMilestoneRoadmapPhaseIds: buildCurrentMilestoneRoadmapPhaseIdsField(cwd, paths.roadmap),
|
|
1056
|
+
archivedPhaseTokens: buildArchivedPhaseTokensField(paths.planning, phaseIdConvention),
|
|
1057
|
+
currentMilestoneRoadmapPhaseIds: buildCurrentMilestoneRoadmapPhaseIdsField(cwd, paths.roadmap, phaseIdConvention),
|
|
886
1058
|
perPhasePlanNumbering: perPhasePlanScanFields.perPhasePlanNumbering,
|
|
887
1059
|
perPhaseOrphanSummaries: perPhasePlanScanFields.perPhaseOrphanSummaries,
|
|
888
1060
|
perPhaseWaveMissingPlans: perPhasePlanScanFields.perPhaseWaveMissingPlans,
|
|
1061
|
+
phaseIdConvention,
|
|
1062
|
+
roadmapSentinelPhaseTokens: roadmapDeclared.sentinelTokens,
|
|
1063
|
+
roadmapBracketIncoherences: buildRoadmapBracketIncoherencesField(paths.roadmap, phaseIdConvention),
|
|
1064
|
+
workstream,
|
|
889
1065
|
};
|
|
890
1066
|
}
|
|
891
1067
|
module.exports = {
|