@opengsd/gsd-core 1.11.0 → 1.13.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-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 +78 -42
- 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 +91 -112
- package/agents/gsd-planner.md +20 -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 +82 -7
- package/agents/gsd-ui-researcher.md +70 -3
- package/agents/gsd-verifier.md +24 -2
- package/bin/install.js +847 -200
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/quick.md +8 -4
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +761 -100
- 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-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +785 -144
- package/gsd-core/bin/lib/capability-state.cjs +25 -4
- package/gsd-core/bin/lib/capability-validator.cjs +321 -18
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +229 -6
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- 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-aliases.cjs +16 -0
- 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 +877 -54
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +121 -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 +118 -14
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +23 -2
- 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/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +975 -326
- package/gsd-core/bin/lib/gap-checker.cjs +41 -8
- package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- 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 +60 -14
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- 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 +96 -11
- package/gsd-core/bin/lib/init-command-router.cjs +132 -21
- package/gsd-core/bin/lib/init.cjs +252 -56
- package/gsd-core/bin/lib/install-engine.cjs +252 -15
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
- package/gsd-core/bin/lib/install-profiles.cjs +100 -18
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -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 +195 -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/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +175 -4
- package/gsd-core/bin/lib/milestone.cjs +112 -7
- 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-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +321 -13
- 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 +1175 -115
- package/gsd-core/bin/lib/plan-document.cjs +273 -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 +165 -34
- package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
- 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/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/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 +62 -14
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
- package/gsd-core/bin/lib/roadmap.cjs +248 -64
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
- 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 +75 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -31
- 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 +216 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
- package/gsd-core/bin/lib/state-transition.cjs +850 -145
- package/gsd-core/bin/lib/state.cjs +1629 -287
- package/gsd-core/bin/lib/surface.cjs +33 -10
- 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/tdd-red-evidence.cjs +133 -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 +2542 -387
- 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/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- 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-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -13
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +441 -56
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- 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 +185 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -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/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +6 -5
- 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 +17 -5
- package/gsd-core/references/execute-mvp-tdd.md +18 -18
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- 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/mvp-concepts.md +2 -2
- 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/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-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- 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 +76 -3
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +39 -9
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +15 -12
- 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/verifier-evidence-gate.md +160 -0
- 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 +7 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +3 -1
- package/gsd-core/workflows/add-phase.md +5 -3
- package/gsd-core/workflows/add-tests.md +4 -9
- package/gsd-core/workflows/add-todo.md +2 -2
- package/gsd-core/workflows/ai-integration-phase.md +5 -10
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +14 -3
- package/gsd-core/workflows/audit-milestone.md +11 -9
- package/gsd-core/workflows/audit-uat.md +19 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +12 -26
- package/gsd-core/workflows/check-todos.md +2 -2
- package/gsd-core/workflows/cleanup.md +3 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
- package/gsd-core/workflows/code-review-fix.md +3 -1
- package/gsd-core/workflows/code-review.md +192 -69
- package/gsd-core/workflows/complete-milestone.md +28 -14
- package/gsd-core/workflows/debug.md +6 -4
- package/gsd-core/workflows/diagnose-issues.md +17 -7
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
- 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 +5 -7
- 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 +3 -1
- 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/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +46 -19
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +3 -1
- package/gsd-core/workflows/eval-review.md +5 -10
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -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 +29 -5
- 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 +4 -2
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +68 -66
- package/gsd-core/workflows/execute-plan.md +25 -20
- package/gsd-core/workflows/explore.md +3 -1
- package/gsd-core/workflows/extract-learnings.md +3 -1
- package/gsd-core/workflows/fast.md +8 -2
- package/gsd-core/workflows/forensics.md +3 -1
- package/gsd-core/workflows/graduation.md +6 -6
- package/gsd-core/workflows/health.md +4 -7
- 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.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +17 -14
- package/gsd-core/workflows/inbox.md +5 -6
- package/gsd-core/workflows/ingest-docs.md +45 -12
- package/gsd-core/workflows/insert-phase.md +7 -5
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +7 -3
- package/gsd-core/workflows/list-workspaces.md +3 -1
- package/gsd-core/workflows/manager.md +15 -26
- package/gsd-core/workflows/map-codebase.md +3 -1
- package/gsd-core/workflows/milestone-summary.md +3 -1
- package/gsd-core/workflows/mvp-phase.md +3 -3
- package/gsd-core/workflows/new-milestone.md +10 -22
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +17 -29
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +4 -2
- 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 +20 -5
- 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 +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
- package/gsd-core/workflows/plan-phase.md +251 -54
- package/gsd-core/workflows/plan-review-convergence.md +148 -19
- package/gsd-core/workflows/plant-seed.md +3 -3
- package/gsd-core/workflows/pr-branch.md +195 -51
- package/gsd-core/workflows/profile-user.md +17 -15
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +52 -15
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.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 +33 -32
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +6 -4
- package/gsd-core/workflows/remove-workspace.md +3 -3
- package/gsd-core/workflows/resume-project.md +14 -14
- package/gsd-core/workflows/review.md +404 -21
- package/gsd-core/workflows/scan.md +3 -1
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +9 -9
- package/gsd-core/workflows/settings-integrations.md +66 -32
- package/gsd-core/workflows/settings.md +4 -6
- package/gsd-core/workflows/ship.md +22 -16
- package/gsd-core/workflows/sketch-wrap-up.md +13 -17
- package/gsd-core/workflows/sketch.md +13 -19
- package/gsd-core/workflows/smart-entry.md +4 -6
- package/gsd-core/workflows/spec-phase.md +31 -4
- package/gsd-core/workflows/spike-wrap-up.md +9 -11
- package/gsd-core/workflows/spike.md +21 -32
- package/gsd-core/workflows/stats.md +4 -2
- package/gsd-core/workflows/sync-skills.md +13 -5
- package/gsd-core/workflows/thread.md +13 -7
- package/gsd-core/workflows/transition.md +7 -5
- package/gsd-core/workflows/ui-phase.md +36 -21
- package/gsd-core/workflows/ui-review.md +7 -11
- package/gsd-core/workflows/ultraplan-phase.md +7 -13
- package/gsd-core/workflows/undo.md +9 -17
- package/gsd-core/workflows/update.md +47 -48
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +106 -21
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +302 -22
- 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 +77 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +46 -12
- package/hooks/dist/gsd-read-guard.js +18 -7
- package/hooks/dist/gsd-read-injection-scanner.js +22 -13
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +222 -29
- package/hooks/dist/gsd-validate-commit.sh +523 -12
- 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 +36 -17
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +4 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +302 -22
- 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 +77 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +46 -12
- package/hooks/gsd-read-guard.js +18 -7
- package/hooks/gsd-read-injection-scanner.js +22 -13
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +222 -29
- package/hooks/gsd-validate-commit.sh +523 -12
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +36 -17
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/hooks.json +6 -0
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +4 -0
- package/package.json +14 -9
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +12 -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 +52 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +406 -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 +189 -4
- 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/lib/shellcheck-fetch.cjs +247 -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-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -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 +45 -14
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-response-language-coverage.cjs +524 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +124 -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/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +83 -14
- package/scripts/require-issue-link-policy.cjs +16 -1
- 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-execute-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- 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
|
@@ -23,6 +23,7 @@ exports.findTableBySchema = findTableBySchema;
|
|
|
23
23
|
exports.findTableWithColumns = findTableWithColumns;
|
|
24
24
|
exports.escapeCell = escapeCell;
|
|
25
25
|
exports.appendQuickTaskRow = appendQuickTaskRow;
|
|
26
|
+
exports.migrateQuickTasksTable = migrateQuickTasksTable;
|
|
26
27
|
exports.resetQuickTaskRows = resetQuickTaskRows;
|
|
27
28
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
28
29
|
// ─── Schema registry ──────────────────────────────────────────────────────────
|
|
@@ -657,6 +658,54 @@ function escapeCell(value) {
|
|
|
657
658
|
* Raw Text Matching").
|
|
658
659
|
*/
|
|
659
660
|
exports.QUICK_TASKS_SECTION_ABSENT = 'no Quick Tasks Completed section';
|
|
661
|
+
/**
|
|
662
|
+
* #3860: heading predicate for STATE.md's Quick Tasks Completed section(s).
|
|
663
|
+
* The heading is a section LABEL, not data — milestone-scoped files
|
|
664
|
+
* legitimately carry suffixed headings (`### Quick Tasks Completed (v1.1+)`
|
|
665
|
+
* beside an archived `(v1.0)`), so the match is prefix-anchored with a word
|
|
666
|
+
* boundary, never exact: `Quick Tasks Completedness` must NOT match. Hoisted
|
|
667
|
+
* beside QUICK_TASKS_SECTION_ABSENT so `appendQuickTaskRow` and
|
|
668
|
+
* `resetQuickTaskRows` cannot drift apart again.
|
|
669
|
+
*/
|
|
670
|
+
const isQuickTasksHeading = (h) => /^quick tasks completed\b/i.test(h.text.trim());
|
|
671
|
+
/**
|
|
672
|
+
* #3860: among ALL heading-matching sections, pick the first whose body is a
|
|
673
|
+
* table with a recognized Quick Tasks schema — a legacy/unparseable table
|
|
674
|
+
* first in document order no longer shadows a usable one further down. When
|
|
675
|
+
* NO section is usable, return the FIRST match so the caller's downstream
|
|
676
|
+
* error describes the real problem (unparseable/legacy table) instead of a
|
|
677
|
+
* false QUICK_TASKS_SECTION_ABSENT.
|
|
678
|
+
*
|
|
679
|
+
* Bounding: `collectSections` ends a candidate's body only at the NEXT
|
|
680
|
+
* matching heading — far too wide for splicing (it would swallow an
|
|
681
|
+
* intervening `## Deferred Items` table into the Quick Tasks body, and
|
|
682
|
+
* `appendQuickTaskRow`'s last-table-line scan would then splice the new row
|
|
683
|
+
* into that WRONG table). Each candidate is therefore re-collected through
|
|
684
|
+
* `collectSection` with an offset-precise predicate, whose default
|
|
685
|
+
* level-bounded stop (next heading of the same or higher level) is exactly
|
|
686
|
+
* the semantics the pre-#3860 single-section lookup had.
|
|
687
|
+
*
|
|
688
|
+
* Convention: "first" is document order — the newest-on-top layout the issue
|
|
689
|
+
* itself demonstrates (`(v1.1+)` above an archived `(v1.0)`). When several
|
|
690
|
+
* suffixed sections all carry recognized schemas, this layer has no signal
|
|
691
|
+
* for which milestone is active, so document order is the pinned tie-break.
|
|
692
|
+
*/
|
|
693
|
+
function selectQuickTasksSection(stateContent) {
|
|
694
|
+
const candidates = (0, markdown_sectionizer_cjs_1.collectSections)(stateContent, isQuickTasksHeading);
|
|
695
|
+
if (candidates.length === 0)
|
|
696
|
+
return null;
|
|
697
|
+
const boundedOf = (cand) => (0, markdown_sectionizer_cjs_1.collectSection)(stateContent, (h) => h.offset === cand.heading.offset);
|
|
698
|
+
const firstBounded = boundedOf(candidates[0]);
|
|
699
|
+
for (const cand of candidates) {
|
|
700
|
+
const bounded = boundedOf(cand);
|
|
701
|
+
if (!bounded)
|
|
702
|
+
continue;
|
|
703
|
+
const parsed = parseMarkdownTable(bounded.body);
|
|
704
|
+
if (parsed.ok && matchTableSchema(parsed.value.columns)?.id === 'QuickTasks')
|
|
705
|
+
return bounded;
|
|
706
|
+
}
|
|
707
|
+
return firstBounded;
|
|
708
|
+
}
|
|
660
709
|
/**
|
|
661
710
|
* Append one row to STATE.md's "Quick Tasks Completed" table.
|
|
662
711
|
*
|
|
@@ -677,7 +726,7 @@ exports.QUICK_TASKS_SECTION_ABSENT = 'no Quick Tasks Completed section';
|
|
|
677
726
|
* rows), preserving any surrounding blank lines/trailing content in the section.
|
|
678
727
|
*/
|
|
679
728
|
function appendQuickTaskRow(stateContent, fields) {
|
|
680
|
-
const section = (
|
|
729
|
+
const section = selectQuickTasksSection(stateContent);
|
|
681
730
|
if (!section) {
|
|
682
731
|
return { ok: false, reason: exports.QUICK_TASKS_SECTION_ABSENT };
|
|
683
732
|
}
|
|
@@ -697,7 +746,7 @@ function appendQuickTaskRow(stateContent, fields) {
|
|
|
697
746
|
const rowNumber = parsed.value.rows.length + 1;
|
|
698
747
|
const cellFor = (col) => {
|
|
699
748
|
switch (col) {
|
|
700
|
-
case '#': return escapeCell(String(rowNumber));
|
|
749
|
+
case '#': return escapeCell(fields.quickId ?? String(rowNumber));
|
|
701
750
|
case 'Description': return escapeCell(fields.description);
|
|
702
751
|
case 'Date': return escapeCell(fields.date);
|
|
703
752
|
case 'Commit': return escapeCell(fields.commit);
|
|
@@ -728,6 +777,128 @@ function appendQuickTaskRow(stateContent, fields) {
|
|
|
728
777
|
const content = (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody);
|
|
729
778
|
return { ok: true, value: { content, row, variant: match.label } };
|
|
730
779
|
}
|
|
780
|
+
// ─── migrateQuickTasksTable (#3730 option b) ───────────────────────────────
|
|
781
|
+
/**
|
|
782
|
+
* Column-name → canonical-cell mapping for migrating a legacy Quick Tasks table
|
|
783
|
+
* (#3730). Name-based, case-insensitive; every column NOT in this map lands in
|
|
784
|
+
* the Description bucket (joined with ' · ' in original column order, unknown
|
|
785
|
+
* names kept as `name: value`) so no historical datum is dropped.
|
|
786
|
+
*/
|
|
787
|
+
const QUICK_TASKS_COLUMN_ALIASES = {
|
|
788
|
+
'#': '#', id: '#',
|
|
789
|
+
date: 'Date', when: 'Date',
|
|
790
|
+
commit: 'Commit', sha: 'Commit',
|
|
791
|
+
status: 'Status',
|
|
792
|
+
directory: 'Directory', artifacts: 'Directory', path: 'Directory', dir: 'Directory',
|
|
793
|
+
description: 'Description', task: 'Description', summary: 'Description',
|
|
794
|
+
slug: 'Description', scope: 'Description', title: 'Description',
|
|
795
|
+
};
|
|
796
|
+
/**
|
|
797
|
+
* Migrate STATE.md's "Quick Tasks Completed" table onto the canonical
|
|
798
|
+
* `with-status` schema (#3730, maintainer decision 2026-09-02: option b).
|
|
799
|
+
*
|
|
800
|
+
* The pre-registry prose template licensed arbitrary column shapes GSD itself
|
|
801
|
+
* emitted, and `appendQuickTaskRow` fails loud on every one of them — leaving a
|
|
802
|
+
* project permanently unappendable. This is the supported repair path: the
|
|
803
|
+
* `gsd-tools quick-tasks-migrate` subcommand, plus an automatic check on the
|
|
804
|
+
* first `quick`/`fast` run (the workflow calls this before appending), which is
|
|
805
|
+
* a silent no-op when the section is absent, the table is already canonical, or
|
|
806
|
+
* the user never runs quick at all.
|
|
807
|
+
*
|
|
808
|
+
* Same section-selection, parse, and fail-loud posture as `appendQuickTaskRow`
|
|
809
|
+
* (ADR-2143 §7 upheld — append still rejects; this REPAIRS). Returns
|
|
810
|
+
* `{ok:true, value:{content, migrated:false}}` for the no-op cases so callers
|
|
811
|
+
* can stay silent, and `{migrated:true, from, rows}` when a rewrite happened so
|
|
812
|
+
* the CLI can report exactly what moved.
|
|
813
|
+
*/
|
|
814
|
+
function migrateQuickTasksTable(stateContent) {
|
|
815
|
+
const section = selectQuickTasksSection(stateContent);
|
|
816
|
+
if (!section) {
|
|
817
|
+
return { ok: true, value: { content: stateContent, migrated: false } };
|
|
818
|
+
}
|
|
819
|
+
const parsed = parseMarkdownTable(section.body);
|
|
820
|
+
if (!parsed.ok) {
|
|
821
|
+
return { ok: false, reason: `quick-tasks table: ${parsed.reason}` };
|
|
822
|
+
}
|
|
823
|
+
const match = matchTableSchema(parsed.value.columns);
|
|
824
|
+
if (match && match.id === 'QuickTasks') {
|
|
825
|
+
return { ok: true, value: { content: stateContent, migrated: false } };
|
|
826
|
+
}
|
|
827
|
+
const canonical = exports.TABLE_SCHEMAS.QuickTasks.find((v) => v.label === 'with-status').columns;
|
|
828
|
+
// Map each legacy column to its canonical target; unmapped names keep their
|
|
829
|
+
// identity so the Description bucket can render them losslessly.
|
|
830
|
+
const targets = parsed.value.columns.map((c) => QUICK_TASKS_COLUMN_ALIASES[c.trim().toLowerCase()] ?? null);
|
|
831
|
+
const rows = parsed.value.rows.map((row, rowIdx) => {
|
|
832
|
+
const bucket = {
|
|
833
|
+
'#': '', Description: '', Date: '', Commit: '', Status: '', Directory: '',
|
|
834
|
+
};
|
|
835
|
+
const descriptionParts = [];
|
|
836
|
+
// parseMarkdownTable hands back name-keyed records (rows[i][columnName]).
|
|
837
|
+
parsed.value.columns.forEach((col, i) => {
|
|
838
|
+
const raw = (row[col] ?? '').trim();
|
|
839
|
+
const target = targets[i];
|
|
840
|
+
if (target === 'Description') {
|
|
841
|
+
if (raw)
|
|
842
|
+
descriptionParts.push(raw);
|
|
843
|
+
}
|
|
844
|
+
else if (target) {
|
|
845
|
+
// Collision-safe (#3730 review): two legacy columns aliasing the same
|
|
846
|
+
// canonical target must not silently overwrite — the displaced value
|
|
847
|
+
// joins the Description bucket under its own name, same convention as
|
|
848
|
+
// an unknown column, so no historical datum is dropped.
|
|
849
|
+
if (raw && bucket[target]) {
|
|
850
|
+
descriptionParts.push(`${col.trim()}: ${raw}`);
|
|
851
|
+
}
|
|
852
|
+
else if (raw) {
|
|
853
|
+
bucket[target] = raw;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else if (raw) {
|
|
857
|
+
descriptionParts.push(`${col.trim()}: ${raw}`);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
bucket['#'] = bucket['#'] || String(rowIdx + 1);
|
|
861
|
+
bucket.Description = descriptionParts.join(' · ');
|
|
862
|
+
bucket.Date = bucket.Date || '—';
|
|
863
|
+
bucket.Commit = bucket.Commit || '—';
|
|
864
|
+
bucket.Status = bucket.Status || '—';
|
|
865
|
+
bucket.Directory = bucket.Directory || '—';
|
|
866
|
+
return `| ${canonical.map((c) => escapeCell(bucket[c])).join(' | ')} |`;
|
|
867
|
+
});
|
|
868
|
+
const eol = /\r\n/.test(section.body) ? '\r\n' : '\n';
|
|
869
|
+
const lines = section.body.split(/\r?\n/);
|
|
870
|
+
// CONTIGUOUS-run bound (#3730 review), mirroring resetQuickTaskRows: the
|
|
871
|
+
// section body may carry a SECOND table (a `#### Notes` subsection or a
|
|
872
|
+
// trailing table after a blank line) that parseMarkdownTable never read —
|
|
873
|
+
// scanning for the last pipe line anywhere in the body would splice the
|
|
874
|
+
// rewrite across it and silently destroy it. Only the first CONTIGUOUS run
|
|
875
|
+
// of pipe lines — the table that was parsed — is replaced.
|
|
876
|
+
const firstTableLineIdx = lines.findIndex((l) => l.trim().startsWith('|'));
|
|
877
|
+
let lastTableLineIdx = firstTableLineIdx;
|
|
878
|
+
while (lastTableLineIdx + 1 < lines.length && lines[lastTableLineIdx + 1].trim().startsWith('|')) {
|
|
879
|
+
lastTableLineIdx++;
|
|
880
|
+
}
|
|
881
|
+
const header = `| ${canonical.join(' | ')} |`;
|
|
882
|
+
// Widths match workflows/quick.md's canonical template byte-for-byte
|
|
883
|
+
// (#3730 review): its delimiter is max(3, len+2) per column.
|
|
884
|
+
const delimiter = `| ${canonical.map((c) => '-'.repeat(Math.max(3, c.length + 2))).join(' | ')} |`;
|
|
885
|
+
const newBody = [
|
|
886
|
+
...lines.slice(0, firstTableLineIdx),
|
|
887
|
+
header,
|
|
888
|
+
delimiter,
|
|
889
|
+
...rows,
|
|
890
|
+
...lines.slice(lastTableLineIdx + 1),
|
|
891
|
+
].join(eol);
|
|
892
|
+
return {
|
|
893
|
+
ok: true,
|
|
894
|
+
value: {
|
|
895
|
+
content: (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody),
|
|
896
|
+
migrated: true,
|
|
897
|
+
from: parsed.value.columns,
|
|
898
|
+
rows: rows.length,
|
|
899
|
+
},
|
|
900
|
+
};
|
|
901
|
+
}
|
|
731
902
|
// ─── resetQuickTaskRows (#2142) ────────────────────────────────────────────
|
|
732
903
|
/**
|
|
733
904
|
* Clear every DATA row from STATE.md's "Quick Tasks Completed" table, leaving
|
|
@@ -736,7 +907,7 @@ function appendQuickTaskRow(stateContent, fields) {
|
|
|
736
907
|
* directories out from under the table (see `src/milestone.cts`'s
|
|
737
908
|
* `archiveQuickTaskDirectories` / `cmdMilestoneComplete` wiring).
|
|
738
909
|
*
|
|
739
|
-
* Mirrors `appendQuickTaskRow`'s exact contract (same `
|
|
910
|
+
* Mirrors `appendQuickTaskRow`'s exact contract (same `selectQuickTasksSection` ->
|
|
740
911
|
* `parseMarkdownTable` -> `matchTableSchema` pipeline, same fail-loud posture,
|
|
741
912
|
* same EOL-detect-before-split handling) rather than inventing a second one:
|
|
742
913
|
* - no "Quick Tasks Completed" heading -> `{ok:false, reason:
|
|
@@ -758,7 +929,7 @@ function resetQuickTaskRows(stateContent) {
|
|
|
758
929
|
if (typeof stateContent !== 'string' || stateContent.trim() === '') {
|
|
759
930
|
return { ok: false, reason: 'empty or non-string input' };
|
|
760
931
|
}
|
|
761
|
-
const section = (
|
|
932
|
+
const section = selectQuickTasksSection(stateContent);
|
|
762
933
|
if (!section) {
|
|
763
934
|
return { ok: false, reason: exports.QUICK_TASKS_SECTION_ABSENT };
|
|
764
935
|
}
|
|
@@ -31,8 +31,14 @@ const { resolveQuickTaskSummaryFile } = auditMod;
|
|
|
31
31
|
const ioMod = require("./io.cjs");
|
|
32
32
|
const { output, error } = ioMod;
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
|
+
const cliExitMod = require("./cli-exit.cjs");
|
|
35
|
+
const { ExitError } = cliExitMod;
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
const stateContract = require("./state-contract.cjs");
|
|
38
|
+
const { publishStateContract } = stateContract;
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
40
|
const phaseIdMod = require("./phase-id.cjs");
|
|
35
|
-
const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId } = phaseIdMod;
|
|
41
|
+
const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId, isSentinelPhaseDir } = phaseIdMod;
|
|
36
42
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
37
43
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
44
|
const roadmapParserMod = require("./roadmap-parser.cjs");
|
|
@@ -49,7 +55,7 @@ const { scanPhasePlans } = planScanMod;
|
|
|
49
55
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
|
|
50
56
|
const phaseLocatorMod = require("./phase-locator.cjs");
|
|
51
57
|
const { listMilestonePhaseDirs } = phaseLocatorMod;
|
|
52
|
-
const { planningPaths } = planningWorkspace;
|
|
58
|
+
const { planningPaths, resolvePhaseIdConvention } = planningWorkspace;
|
|
53
59
|
const { extractFrontmatter } = frontmatterMod;
|
|
54
60
|
// ADR-3408 §8.3 / #3469: `writeStateMd` gets sync and NO preservation — the
|
|
55
61
|
// same #3374-shaped exposure the milestone-complete write used to carry (a
|
|
@@ -477,7 +483,7 @@ function applyQuickTasksReset(content) {
|
|
|
477
483
|
}
|
|
478
484
|
function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
479
485
|
if (!version) {
|
|
480
|
-
error('version required for milestone complete (e.g., v1.0)');
|
|
486
|
+
error('version required for milestone complete (e.g., v1.0) — and --confirm to mutate');
|
|
481
487
|
}
|
|
482
488
|
// #2288 security: `version` is a CLI positional that is interpolated into
|
|
483
489
|
// multiple filesystem sinks below — `path.join(archiveDir, `${version}-ROADMAP.md`)`,
|
|
@@ -488,6 +494,23 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
488
494
|
if (!ARCHIVE_VERSION_LABEL_RE.test(version)) {
|
|
489
495
|
error(`milestone complete: version "${version}" is invalid — a milestone version label may contain only letters, digits, '.', '-' and '_', and must not contain path separators or "..".`);
|
|
490
496
|
}
|
|
497
|
+
// #3726: confirmation gate — refuse before ANY read of the tree beyond the
|
|
498
|
+
// arg checks above, so an unconfirmed invocation is a guaranteed no-op on
|
|
499
|
+
// disk. The threat model is the NEVER_VALID_FLAGS one (gsd-tools.cjs): a
|
|
500
|
+
// caller supplies a token it believes is inert and a destructive operation
|
|
501
|
+
// proceeds unchecked — here the caller-side belief was that the `query`
|
|
502
|
+
// meta-prefix implies a read, and `query milestone.complete <v>` archived
|
|
503
|
+
// the milestone with no confirmation. The prefix is an intentional
|
|
504
|
+
// invocation-compatibility mechanism, not a permission boundary, so the
|
|
505
|
+
// gate lives on the destructive command itself and covers every invocation
|
|
506
|
+
// path. --dry-run needs no confirmation (it mutates nothing and is the
|
|
507
|
+
// recommended first step); --force does NOT imply it (see
|
|
508
|
+
// MilestoneCompleteOptions.confirm).
|
|
509
|
+
if (!options.dryRun && !options.confirm) {
|
|
510
|
+
error(`milestone complete is irreversible: it archives ROADMAP.md and REQUIREMENTS.md, MOVES every phase ` +
|
|
511
|
+
`directory for ${version} into .planning/milestones/, and rewrites STATE.md. ` +
|
|
512
|
+
`Nothing has been changed. Re-run with --confirm to proceed, or --dry-run to preview exactly what would move.`);
|
|
513
|
+
}
|
|
491
514
|
const roadmapPath = planningPaths(cwd).roadmap;
|
|
492
515
|
const reqPath = planningPaths(cwd).requirements;
|
|
493
516
|
const statePath = planningPaths(cwd).state;
|
|
@@ -660,6 +683,13 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
660
683
|
}
|
|
661
684
|
}
|
|
662
685
|
catch (e) {
|
|
686
|
+
// ADR-3889: error() now throws ExitError (carries no message) instead
|
|
687
|
+
// of calling process.exit() directly, so it can no longer be detected
|
|
688
|
+
// by sniffing e.message — an ExitError from our own guard above must be
|
|
689
|
+
// re-thrown UNCONDITIONALLY, before any message inspection, or the
|
|
690
|
+
// guard silently stops blocking milestone completion.
|
|
691
|
+
if (e instanceof ExitError)
|
|
692
|
+
throw e;
|
|
663
693
|
// If the error came from our guard, re-throw it; otherwise skip silently.
|
|
664
694
|
const message = e instanceof Error ? e.message : String(e);
|
|
665
695
|
if (message && message.startsWith('Cannot mark milestone complete:'))
|
|
@@ -672,6 +702,22 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
672
702
|
let totalPlans = 0;
|
|
673
703
|
let totalTasks = 0;
|
|
674
704
|
const accomplishments = [];
|
|
705
|
+
// #2761 (round-11 BLOCKER): resolved ONCE, ambiently (no explicit `ws` —
|
|
706
|
+
// this function has none of its own and already resolves everything else
|
|
707
|
+
// off `cwd` via planningPaths(cwd), matching the ambient-workstream
|
|
708
|
+
// contract `resolvePhaseIdConvention`/`planningDir` share), and threaded
|
|
709
|
+
// into the single `listMilestonePhaseDirs` call directly below. Left
|
|
710
|
+
// unthreaded, `phaseIdConvention` stays `undefined` and
|
|
711
|
+
// `getMilestonePhaseFilter` silently resolves it FROM CONFIG itself —
|
|
712
|
+
// which is exactly the behavior the changeset originally (and wrongly)
|
|
713
|
+
// claimed this PR does not reach: on a bracket-convention project that
|
|
714
|
+
// lazily-resolved convention changes `milestonePhaseDirs`/
|
|
715
|
+
// `milestonePhaseScope` below, and this function goes on to ARCHIVE
|
|
716
|
+
// (rename/move) whatever that set names. Resolving explicitly here removes
|
|
717
|
+
// the silent-inherit path without changing behavior for `null` /
|
|
718
|
+
// `milestone-prefixed` projects, whose resolved convention is the same
|
|
719
|
+
// value `undefined` would have lazily produced anyway.
|
|
720
|
+
const phaseConvention = resolvePhaseIdConvention(cwd);
|
|
675
721
|
// #3597 (ADR-3180 Decision 2): SINGLE resolution of "which phase
|
|
676
722
|
// directories belong to the current milestone" AND the SCOPE discriminator
|
|
677
723
|
// that resolution came from — shared verbatim by the read-only stats loop
|
|
@@ -685,7 +731,11 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
685
731
|
// disk, same as before #3597. Only the destructive archive pass (and its
|
|
686
732
|
// --dry-run preview) refuses to act on a non-COMPLETE (non-answer) scope;
|
|
687
733
|
// see the guard built from `milestonePhaseScope` further down.
|
|
688
|
-
const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, {
|
|
734
|
+
const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, {
|
|
735
|
+
cwd,
|
|
736
|
+
versionOverride: version,
|
|
737
|
+
phaseIdConvention: phaseConvention,
|
|
738
|
+
});
|
|
689
739
|
try {
|
|
690
740
|
for (const dir of milestonePhaseDirs) {
|
|
691
741
|
phaseCount++;
|
|
@@ -838,6 +888,12 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
838
888
|
// Create/append MILESTONES.md entry
|
|
839
889
|
const accomplishmentsList = accomplishments.map((a) => `- ${a}`).join('\n');
|
|
840
890
|
const milestoneEntry = `## ${version} ${milestoneName} (Shipped: ${today})\n\n**Phases completed:** ${phaseCount} phases, ${totalPlans} plans, ${totalTasks} tasks\n\n**Key accomplishments:**\n${accomplishmentsList || '- (none recorded)'}\n\n---\n\n`;
|
|
891
|
+
// #3685: mirror requirementsUpdated's diff-tracking contract — the result
|
|
892
|
+
// below used to report `milestones_updated: true` hardcoded, never
|
|
893
|
+
// consulting whether the MILESTONES.md write actually changed anything.
|
|
894
|
+
// Captured before the write branches below so the after-comparison reports
|
|
895
|
+
// a real content diff instead of an assumed one.
|
|
896
|
+
const milestonesBefore = node_fs_1.default.existsSync(milestonesPath) ? node_fs_1.default.readFileSync(milestonesPath, 'utf-8') : null;
|
|
841
897
|
if (node_fs_1.default.existsSync(milestonesPath)) {
|
|
842
898
|
const existing = node_fs_1.default.readFileSync(milestonesPath, 'utf-8');
|
|
843
899
|
if (!existing.trim()) {
|
|
@@ -867,6 +923,10 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
867
923
|
else {
|
|
868
924
|
(0, shell_command_projection_cjs_1.platformWriteSync)(milestonesPath, `# Milestones\n\n${milestoneEntry}`);
|
|
869
925
|
}
|
|
926
|
+
// #3685: real content diff, not the hardcoded `true` this used to report —
|
|
927
|
+
// see the `milestonesBefore` capture above.
|
|
928
|
+
const milestonesAfter = node_fs_1.default.existsSync(milestonesPath) ? node_fs_1.default.readFileSync(milestonesPath, 'utf-8') : null;
|
|
929
|
+
const milestonesUpdated = milestonesAfter !== milestonesBefore;
|
|
870
930
|
// #2142 BLOCKER 2 (review): opt-in quick-task archival. This call MUST sit
|
|
871
931
|
// immediately adjacent to the STATE.md write block directly below it, with
|
|
872
932
|
// NO unguarded IO in between (unlike the ROADMAP/REQUIREMENTS/audit/
|
|
@@ -906,6 +966,13 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
906
966
|
// taken). `resync: true` mirrors `cmdPhaseComplete`'s posture (progress
|
|
907
967
|
// recomputed from disk; only the preserve-when-unchanged deltas apply) —
|
|
908
968
|
// milestone completion is the same kind of lifecycle transition.
|
|
969
|
+
// #3685: mirror requirementsUpdated's diff-tracking contract — this used to
|
|
970
|
+
// report `state_updated: fs.existsSync(statePath)`, true even on a no-op
|
|
971
|
+
// transaction. Declared here beside `stateUpdated`'s sibling flags and
|
|
972
|
+
// defaulted to `false` so the "STATE.md absent" case keeps today's answer
|
|
973
|
+
// (existsSync also returns false there) reached via a real content
|
|
974
|
+
// comparison instead.
|
|
975
|
+
let stateUpdated = false;
|
|
909
976
|
if (node_fs_1.default.existsSync(statePath)) {
|
|
910
977
|
withStateLock(statePath, () => {
|
|
911
978
|
const originalStateContent = (0, shell_command_projection_cjs_1.platformReadSync)(statePath) || '';
|
|
@@ -976,6 +1043,24 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
976
1043
|
divergedFields,
|
|
977
1044
|
});
|
|
978
1045
|
(0, shell_command_projection_cjs_1.platformWriteSync)(statePath, finalContent);
|
|
1046
|
+
// #3685 / #3691: compare NORMALIZED bytes, not the pre-normalize
|
|
1047
|
+
// `finalContent` string, against the pre-normalize `originalStateContent`
|
|
1048
|
+
// read above. `platformWriteSync` runs Markdown normalization (blank-line
|
|
1049
|
+
// insertion around headings/fences/lists) before persisting — the
|
|
1050
|
+
// transition core (`transitionCore`'s `## Current Position` section
|
|
1051
|
+
// reset) regenerates that section fresh on every call, including on a
|
|
1052
|
+
// genuine no-op re-run, and its raw un-normalized output differs from
|
|
1053
|
+
// the already-normalized on-disk original even though the write
|
|
1054
|
+
// converges to byte-identical content. Comparing pre-normalize strings
|
|
1055
|
+
// (mirroring cmdPhaseComplete's shape verbatim) was verified live to
|
|
1056
|
+
// report `true` on three consecutive byte-identical writes.
|
|
1057
|
+
// `contentChangedAfterNormalize` runs BOTH sides through the exact same
|
|
1058
|
+
// normalizer `platformWriteSync` used to persist (no extra disk I/O,
|
|
1059
|
+
// and immune by construction to this ordering artifact) — this used to
|
|
1060
|
+
// re-read the file to get the same answer; #3691 hoisted that seam so
|
|
1061
|
+
// this site, `updateRoadmapAfterPhaseRemoval`, and `cmdPhaseComplete`'s
|
|
1062
|
+
// roadmap/state/requirements flags all agree by construction.
|
|
1063
|
+
stateUpdated = (0, shell_command_projection_cjs_1.contentChangedAfterNormalize)(statePath, originalStateContent, finalContent);
|
|
979
1064
|
for (const field of divergedFields) {
|
|
980
1065
|
preservationWarnings.push({ field, reason: 'preserved-over-disagreeing-derived' });
|
|
981
1066
|
}
|
|
@@ -1053,11 +1138,27 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
1053
1138
|
phases_archive_skip_reason: phasesArchiveSkipReason,
|
|
1054
1139
|
quick: !!quickArchiveResult && quickArchiveResult.archived > 0,
|
|
1055
1140
|
},
|
|
1056
|
-
|
|
1057
|
-
|
|
1141
|
+
// #3685: mirror requirementsUpdated's diff-tracking contract — both flags
|
|
1142
|
+
// now report a real before/after content diff instead of the previous
|
|
1143
|
+
// hardcoded `true` (milestones_updated) / bare fs.existsSync (state_updated).
|
|
1144
|
+
milestones_updated: milestonesUpdated,
|
|
1145
|
+
state_updated: stateUpdated,
|
|
1058
1146
|
preservation_warnings: preservationWarnings,
|
|
1059
1147
|
};
|
|
1060
1148
|
output(result, raw);
|
|
1149
|
+
// #3227 (design doc §40 row 26 / "Not-corruption" rule): a refreshed
|
|
1150
|
+
// state.json `updated_at` must always mean something on disk actually
|
|
1151
|
+
// moved. This site is unconditional because every reachable path either
|
|
1152
|
+
// exits via `error()` (process.exit — refusals like a truncated milestone
|
|
1153
|
+
// window, an unstarted phase, or an invalid version never reach here) or
|
|
1154
|
+
// returns early on `--dry-run` (before any mutation, see the `dry_run:
|
|
1155
|
+
// true` branch above) — the only way execution reaches this line is after
|
|
1156
|
+
// the unconditional MILESTONES.md `platformWriteSync` a few lines above,
|
|
1157
|
+
// which always runs (new file, empty file, or append) once the run is
|
|
1158
|
+
// committed to mutating. Best-effort — cannot throw, cannot change this
|
|
1159
|
+
// command's exit code or output. publishStateContract resolves the
|
|
1160
|
+
// workstream planning root itself via planningPaths.
|
|
1161
|
+
publishStateContract(cwd);
|
|
1061
1162
|
}
|
|
1062
1163
|
function cmdPhasesClear(cwd, raw, args) {
|
|
1063
1164
|
const phasesDir = planningPaths(cwd).phases;
|
|
@@ -1100,7 +1201,11 @@ function cmdPhasesClear(cwd, raw, args) {
|
|
|
1100
1201
|
// divergence meant a `0-*` directory `roadmap analyze` preserves as a
|
|
1101
1202
|
// sentinel was DELETED here. Routed through the canonical predicate so
|
|
1102
1203
|
// every reader of "is this a sentinel phase" agrees by construction.
|
|
1103
|
-
|
|
1204
|
+
// #3639: the DIR-AWARE recognizer — the convention-less id predicate
|
|
1205
|
+
// never saw bracket sentinel dirs (GSD.999-07-icebox), so they were
|
|
1206
|
+
// counted for deletion here while the disk guards (post-#3639) preserve
|
|
1207
|
+
// them; the destructive path must not be the one blind reader left.
|
|
1208
|
+
const dirs = entries.filter((e) => e.isDirectory() && !isSentinelPhaseDir(e.name));
|
|
1104
1209
|
if (dirs.length > 0 && !confirm) {
|
|
1105
1210
|
error(`phases clear would delete ${dirs.length} phase director${dirs.length === 1 ? 'y' : 'ies'}. ` +
|
|
1106
1211
|
`Pass --confirm to proceed.`);
|