@opengsd/gsd-core 1.14.0 → 1.16.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 +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- 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 +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- 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-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -35,9 +35,19 @@ const commandRoutingHub = require("./command-routing-hub.cjs");
|
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
36
|
const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
|
|
37
37
|
const security_cjs_1 = require("./security.cjs");
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
39
|
+
const nodeFs = require("node:fs");
|
|
40
|
+
// #5118: the writer set is owned by src/verification.cts.
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
|
+
const verificationMod = require("./verification.cjs");
|
|
38
43
|
const { output, ERROR_REASON } = io;
|
|
44
|
+
const { VERIFIER_STATUSES } = verificationMod;
|
|
39
45
|
const { makeInvalidArgs } = commandRoutingHub;
|
|
40
46
|
const { routeHubCommandFamily } = cjsCommandRouterAdapter;
|
|
47
|
+
/** Default stdin reader for `parse-args --stdin` (fd 0, UTF-8, whole stream). */
|
|
48
|
+
function readProcessStdin() {
|
|
49
|
+
return nodeFs.readFileSync(0, 'utf8');
|
|
50
|
+
}
|
|
41
51
|
// ─── Small arg-parsing helpers (local — no new shared convention needed) ────
|
|
42
52
|
/** `--flag value` lookup; undefined when the flag is absent. */
|
|
43
53
|
function argValue(args, flag) {
|
|
@@ -57,7 +67,7 @@ function parseJsonArg(raw, label) {
|
|
|
57
67
|
return { ok: true, value: parsed.value };
|
|
58
68
|
}
|
|
59
69
|
// ─── Implementation ───────────────────────────────────────────────────────────
|
|
60
|
-
function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatchDispatch }) {
|
|
70
|
+
function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatchDispatch, _readStdin }) {
|
|
61
71
|
const qb = _quickBatch ?? quickBatch;
|
|
62
72
|
const dispatch = _quickBatchDispatch ?? quickBatchDispatch;
|
|
63
73
|
/** Forward a `Result<T>` from either module straight to output()/error(). */
|
|
@@ -211,7 +221,7 @@ function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatc
|
|
|
211
221
|
// `quick-batch verification-routing --status <passed|gaps_found|human_needed>`
|
|
212
222
|
'verification-routing': () => {
|
|
213
223
|
const status = argValue(args, '--status');
|
|
214
|
-
if (
|
|
224
|
+
if (typeof status !== 'string' || !VERIFIER_STATUSES.has(status)) {
|
|
215
225
|
return makeInvalidArgs('--status', 'Usage: gsd-tools quick-batch verification-routing --status <passed|gaps_found|human_needed>', ERROR_REASON.USAGE);
|
|
216
226
|
}
|
|
217
227
|
output(dispatch.routeVerificationOutcome(status), raw);
|
|
@@ -252,17 +262,33 @@ function routeQuickBatchCommand({ args, cwd, raw, error, _quickBatch, _quickBatc
|
|
|
252
262
|
planContent,
|
|
253
263
|
}), raw);
|
|
254
264
|
},
|
|
255
|
-
// `quick-batch parse-args --
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
// the
|
|
265
|
+
// `quick-batch parse-args --stdin` (preferred, #4780 — the caller feeds
|
|
266
|
+
// the typed text through a QUOTED heredoc, so the shell never parses it:
|
|
267
|
+
// quotes, `$(...)`, backticks and newlines in attacker-influenced task
|
|
268
|
+
// text cannot break out of any shell position, because there is no
|
|
269
|
+
// shell position for the text to occupy).
|
|
270
|
+
// `quick-batch parse-args --text "<raw string>"` (still supported for a
|
|
271
|
+
// caller that already holds the text as ONE real argv element with no
|
|
272
|
+
// shell quoting involved, e.g. a test harness; it is NOT safe to build
|
|
273
|
+
// that argument by splicing user text into a shell command line).
|
|
274
|
+
// In both forms this handler does the whitespace split itself, in Node,
|
|
275
|
+
// so shell pathname expansion (globbing) never happens.
|
|
262
276
|
// `quick-batch parse-args -- <already-tokenized args>` (legacy/direct
|
|
263
277
|
// form — still supported for a caller that already has a real argv
|
|
264
278
|
// array with no shell splitting involved, e.g. a test harness).
|
|
265
279
|
'parse-args': () => {
|
|
280
|
+
if (args.includes('--stdin')) {
|
|
281
|
+
let stdinText;
|
|
282
|
+
try {
|
|
283
|
+
stdinText = (_readStdin ?? readProcessStdin)();
|
|
284
|
+
}
|
|
285
|
+
catch (readError) {
|
|
286
|
+
return makeInvalidArgs('--stdin', `could not read standard input: ${readError instanceof Error ? readError.message : String(readError)}`, ERROR_REASON.USAGE);
|
|
287
|
+
}
|
|
288
|
+
const stdinTokens = stdinText.trim().length === 0 ? [] : stdinText.trim().split(/\s+/);
|
|
289
|
+
emit(dispatch.parseQuickBatchArgs(stdinTokens));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
266
292
|
const textArg = argValue(args, '--text');
|
|
267
293
|
if (textArg !== undefined) {
|
|
268
294
|
const rawArgs = textArg.trim().length === 0 ? [] : textArg.trim().split(/\s+/);
|
|
@@ -140,6 +140,16 @@ function computeSpawnPlan(input) {
|
|
|
140
140
|
}
|
|
141
141
|
return { spawn, pending };
|
|
142
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* #5118: one routing entry per writer-set status, keyed by the owner's type —
|
|
145
|
+
* no status literal is compared (local/no-verification-status-literal's
|
|
146
|
+
* type-aware arm), and `Record<VerifierStatus, …>` makes the table total.
|
|
147
|
+
*/
|
|
148
|
+
const VERIFICATION_OUTCOME_ROUTES = Object.freeze({
|
|
149
|
+
passed: { action: 'complete' },
|
|
150
|
+
human_needed: { action: 'human_needed' },
|
|
151
|
+
gaps_found: { action: 'fail', failureReason: 'verification reported gaps_found (no automatic gap-fix retry in v1)' },
|
|
152
|
+
});
|
|
143
153
|
/**
|
|
144
154
|
* Route a verifier's status to an item action. `human_needed` is terminal
|
|
145
155
|
* for the item — the caller must NOT call `completeQuickItem` (row 30, no
|
|
@@ -147,14 +157,7 @@ function computeSpawnPlan(input) {
|
|
|
147
157
|
* WITHOUT an automatic gap-fix retry (row 31,34). `passed` completes it.
|
|
148
158
|
*/
|
|
149
159
|
function routeVerificationOutcome(status) {
|
|
150
|
-
|
|
151
|
-
case 'passed':
|
|
152
|
-
return { action: 'complete' };
|
|
153
|
-
case 'human_needed':
|
|
154
|
-
return { action: 'human_needed' };
|
|
155
|
-
case 'gaps_found':
|
|
156
|
-
return { action: 'fail', failureReason: 'verification reported gaps_found (no automatic gap-fix retry in v1)' };
|
|
157
|
-
}
|
|
160
|
+
return VERIFICATION_OUTCOME_ROUTES[status];
|
|
158
161
|
}
|
|
159
162
|
/**
|
|
160
163
|
* Route a merge attempt's outcome to an item action. Both `merge_failed`
|
|
@@ -105,6 +105,14 @@ const SANDBOX_MARKER = 'GSD_TEST_HOME_SANDBOX';
|
|
|
105
105
|
* `statSync` follows symlinks and reports the inode itself, so a case-variant
|
|
106
106
|
* HOME, a symlinked HOME, and a bind-mounted HOME all compare equal.
|
|
107
107
|
*
|
|
108
|
+
* `identify` calls `fs.statSync(p, { bigint: true })`, not the plain numeric
|
|
109
|
+
* form. Node documents that the default numeric `dev`/`ino` lose precision past
|
|
110
|
+
* `Number.MAX_SAFE_INTEGER` (2^53), and NTFS/ReFS file IDs routinely exceed
|
|
111
|
+
* that — so on Windows two DISTINCT directories can round to the same numeric
|
|
112
|
+
* `ino` and compare equal here, which is a fail-OPEN in a module whose whole
|
|
113
|
+
* job is refusing writes into the real home. `{ bigint: true }` reports the
|
|
114
|
+
* true 64-bit values with no precision loss.
|
|
115
|
+
*
|
|
108
116
|
* Fails CLOSED, and "closed" here means returning FALSE. The sole caller is the
|
|
109
117
|
* passwd-less marker branch, which READS a true as permission to proceed:
|
|
110
118
|
*
|
|
@@ -130,7 +138,7 @@ function sameDirectory(a, b) {
|
|
|
130
138
|
}
|
|
131
139
|
function identify(p) {
|
|
132
140
|
try {
|
|
133
|
-
const st = node_fs_1.default.statSync(p);
|
|
141
|
+
const st = node_fs_1.default.statSync(p, { bigint: true });
|
|
134
142
|
return { kind: 'ok', dev: st.dev, ino: st.ino };
|
|
135
143
|
}
|
|
136
144
|
catch (err) {
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TestStatus = exports.ReportFormat = void 0;
|
|
4
|
+
exports.parseTestReport = parseTestReport;
|
|
5
|
+
/** Format adapters for RED evidence. Parsing never depends on the runner command. */
|
|
6
|
+
const tap_parser_cjs_1 = require("./vendor/tap-parser.cjs");
|
|
7
|
+
const saxes_cjs_1 = require("./vendor/saxes.cjs");
|
|
8
|
+
exports.ReportFormat = {
|
|
9
|
+
Tap: 'tap', Junit: 'junit', SwiftTesting: 'swift-testing', Unittest: 'unittest', Unknown: 'unknown',
|
|
10
|
+
};
|
|
11
|
+
exports.TestStatus = { Passed: 'passed', Failed: 'failed', Skipped: 'skipped', Todo: 'todo' };
|
|
12
|
+
function parseTap(output) {
|
|
13
|
+
const report = { format: exports.ReportFormat.Tap, valid: true, tests: [], issues: [] };
|
|
14
|
+
const parser = new tap_parser_cjs_1.Parser({ strict: true });
|
|
15
|
+
const assertions = [];
|
|
16
|
+
const openBuffered = new Map();
|
|
17
|
+
let completed = false;
|
|
18
|
+
const watch = (p) => {
|
|
19
|
+
p.on('child', (child) => {
|
|
20
|
+
if (child.buffered)
|
|
21
|
+
openBuffered.set(p, (openBuffered.get(p) ?? 0) + 1);
|
|
22
|
+
watch(child);
|
|
23
|
+
});
|
|
24
|
+
// tap-parser finalizes buffered children at EOF even without their closing
|
|
25
|
+
// brace. Track its child/line events so that truncation cannot prove RED.
|
|
26
|
+
p.on('line', (line) => {
|
|
27
|
+
if (line === '}\n')
|
|
28
|
+
openBuffered.set(p, (openBuffered.get(p) ?? 0) - 1);
|
|
29
|
+
});
|
|
30
|
+
p.on('assert', (result) => {
|
|
31
|
+
if (!result.closingTestPoint)
|
|
32
|
+
assertions.push({ result, owner: p });
|
|
33
|
+
if (result.buffered && !result.closingTestPoint)
|
|
34
|
+
report.issues.push('Missing buffered TAP subtest');
|
|
35
|
+
const diag = result.diag;
|
|
36
|
+
if (diag && ['cancelledByParent', 'hookFailed', 'testTimeoutFailure'].includes(String(diag['failureType']))) {
|
|
37
|
+
report.issues.push('Test run interrupted by cancellation, hook failure, or timeout');
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
p.on('extra', (extra) => { if (extra.trim())
|
|
41
|
+
report.issues.push('Non-TAP data in report'); });
|
|
42
|
+
p.on('complete', (summary) => {
|
|
43
|
+
if (p === parser)
|
|
44
|
+
completed = true;
|
|
45
|
+
const { start, end } = summary.plan;
|
|
46
|
+
if (start !== 1 || end === null || summary.count !== end)
|
|
47
|
+
report.issues.push('Missing or incomplete TAP plan');
|
|
48
|
+
if (summary.bailout)
|
|
49
|
+
report.issues.push('TAP bailout');
|
|
50
|
+
if (summary.failures.some((failure) => failure.tapError))
|
|
51
|
+
report.issues.push('Malformed TAP');
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
watch(parser);
|
|
55
|
+
parser.end(output);
|
|
56
|
+
if ([...openBuffered.values()].some((count) => count !== 0))
|
|
57
|
+
report.issues.push('Unclosed buffered TAP subtest');
|
|
58
|
+
for (const { result, owner } of assertions) {
|
|
59
|
+
let skipped = Boolean(result.skip);
|
|
60
|
+
let todo = Boolean(result.todo);
|
|
61
|
+
for (let parent = owner; parent; parent = parent.parent) {
|
|
62
|
+
skipped ||= Boolean(parent.closingTestPoint?.skip);
|
|
63
|
+
todo ||= Boolean(parent.closingTestPoint?.todo);
|
|
64
|
+
}
|
|
65
|
+
report.tests.push({
|
|
66
|
+
name: result.name,
|
|
67
|
+
identities: [...new Set([result.name, result.fullname])],
|
|
68
|
+
group: null,
|
|
69
|
+
groupIdentities: [],
|
|
70
|
+
status: skipped ? exports.TestStatus.Skipped : todo ? exports.TestStatus.Todo : result.ok ? exports.TestStatus.Passed : exports.TestStatus.Failed,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (!completed)
|
|
74
|
+
report.issues.push('Incomplete TAP stream');
|
|
75
|
+
report.valid = report.issues.length === 0;
|
|
76
|
+
return report;
|
|
77
|
+
}
|
|
78
|
+
/** Surefire and Failsafe share the JUnit XML report shape. */
|
|
79
|
+
function parseJunit(output) {
|
|
80
|
+
const report = { format: exports.ReportFormat.Junit, valid: true, tests: [], issues: [] };
|
|
81
|
+
const parser = new saxes_cjs_1.SaxesParser({ xmlns: false });
|
|
82
|
+
const elements = [];
|
|
83
|
+
const suites = [];
|
|
84
|
+
let current = null;
|
|
85
|
+
parser.on('error', () => { report.issues.push('Malformed XML'); });
|
|
86
|
+
parser.on('doctype', () => { report.issues.push('JUnit reports must not declare a DTD'); });
|
|
87
|
+
parser.on('opentag', (tag) => {
|
|
88
|
+
const parent = elements.at(-1);
|
|
89
|
+
if (!parent && tag.name !== 'testsuite' && tag.name !== 'testsuites')
|
|
90
|
+
report.issues.push('Not a JUnit report');
|
|
91
|
+
if (tag.name === 'testsuite' || tag.name === 'testsuites') {
|
|
92
|
+
if (parent && parent !== 'testsuite' && parent !== 'testsuites')
|
|
93
|
+
report.issues.push('Misplaced test suite');
|
|
94
|
+
const count = tag.attributes['tests'];
|
|
95
|
+
const planned = count === undefined ? null : Number(count);
|
|
96
|
+
if (count !== undefined && (!/^\d+$/.test(count) || !Number.isSafeInteger(planned)))
|
|
97
|
+
report.issues.push('Invalid JUnit test count');
|
|
98
|
+
suites.push({ firstTest: report.tests.length, planned });
|
|
99
|
+
}
|
|
100
|
+
else if (tag.name === 'testcase') {
|
|
101
|
+
if (parent !== 'testsuite' || current)
|
|
102
|
+
report.issues.push('Misplaced test case');
|
|
103
|
+
const name = tag.attributes['name'] ?? '';
|
|
104
|
+
const className = tag.attributes['classname'] ?? '';
|
|
105
|
+
const qualified = className ? `${className}#${name}` : name;
|
|
106
|
+
if (!name)
|
|
107
|
+
report.issues.push('Unnamed test case');
|
|
108
|
+
current = {
|
|
109
|
+
name: qualified,
|
|
110
|
+
identities: [...new Set([name, qualified])],
|
|
111
|
+
group: className || null,
|
|
112
|
+
groupIdentities: className ? [className, className.split('.').at(-1)] : [],
|
|
113
|
+
status: exports.TestStatus.Passed,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
else if (current && parent === 'testcase') {
|
|
117
|
+
if (tag.name === 'failure' || tag.name === 'error') {
|
|
118
|
+
if (current.status === exports.TestStatus.Skipped)
|
|
119
|
+
report.issues.push('Contradictory JUnit status');
|
|
120
|
+
current.status = exports.TestStatus.Failed;
|
|
121
|
+
}
|
|
122
|
+
if (tag.name === 'skipped') {
|
|
123
|
+
if (current.status === exports.TestStatus.Failed)
|
|
124
|
+
report.issues.push('Contradictory JUnit status');
|
|
125
|
+
current.status = exports.TestStatus.Skipped;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
elements.push(tag.name);
|
|
129
|
+
});
|
|
130
|
+
parser.on('closetag', (tag) => {
|
|
131
|
+
if (tag.name === 'testcase' && current) {
|
|
132
|
+
report.tests.push(current);
|
|
133
|
+
current = null;
|
|
134
|
+
}
|
|
135
|
+
else if (tag.name === 'testsuite' || tag.name === 'testsuites') {
|
|
136
|
+
const suite = suites.pop();
|
|
137
|
+
if (!suite || (suite.planned !== null && suite.planned !== report.tests.length - suite.firstTest)) {
|
|
138
|
+
report.issues.push('Incomplete JUnit test suite');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
elements.pop();
|
|
142
|
+
});
|
|
143
|
+
parser.write(output).close();
|
|
144
|
+
report.valid = report.issues.length === 0;
|
|
145
|
+
return report;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* swift-testing console output (#4957). Only the anchored aggregate line marks
|
|
149
|
+
* the format; every declared test must have its own result line. The aggregate
|
|
150
|
+
* counts skipped tests too, and a parameterized test names its case count.
|
|
151
|
+
* A test is named by its quoted display name (with `(aka 'function()')` under
|
|
152
|
+
* --verbose) or, without one, by its bare function name. Windows consoles
|
|
153
|
+
* print √ × - in place of ✔ ✘ ━. swift-testing 6.1 omits the suite count from
|
|
154
|
+
* the aggregate and marks skipped tests with ✘ (later sources add the count
|
|
155
|
+
* and use ➜).
|
|
156
|
+
*/
|
|
157
|
+
const SWIFT_AGGREGATE = /^[ \t]*[✘✔━×√-][ \t]*Test run with (\d+) tests?(?: in \d+ suites?)? (passed|failed)\b/gm;
|
|
158
|
+
const SWIFT_RESULT = /^[ \t]*[✘✔━➜×√-][ \t]*Test (?:"([^"]+)"(?: \(aka '([^']+)'\))?|([^\s"]\S*\)))(?: with \d+ test cases?)? (?:(failed|passed|was cancelled) after [\d.]+ seconds?(?: with \d+ [^\n]*?)?(?:\.|: "[^\n]*")|(skipped)(?:\.|: "[^\n]*"))$/gm;
|
|
159
|
+
function parseSwiftTesting(output) {
|
|
160
|
+
const report = { format: exports.ReportFormat.SwiftTesting, valid: true, tests: [], issues: [] };
|
|
161
|
+
let declared = 0;
|
|
162
|
+
let runPassed = true;
|
|
163
|
+
for (const match of output.matchAll(SWIFT_AGGREGATE)) {
|
|
164
|
+
declared += Number(match[1]);
|
|
165
|
+
runPassed &&= match[2] === 'passed';
|
|
166
|
+
}
|
|
167
|
+
for (const match of output.matchAll(SWIFT_RESULT)) {
|
|
168
|
+
const fn = match[2] ?? match[3];
|
|
169
|
+
report.tests.push({
|
|
170
|
+
name: match[1] ?? match[3],
|
|
171
|
+
identities: [...new Set([match[1], fn, fn?.replace(/\(.*\)$/, '')].filter((id) => Boolean(id)))],
|
|
172
|
+
group: null,
|
|
173
|
+
groupIdentities: [],
|
|
174
|
+
status: match[4] === 'failed' ? exports.TestStatus.Failed : match[4] === 'passed' ? exports.TestStatus.Passed : exports.TestStatus.Skipped,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (report.tests.length !== declared)
|
|
178
|
+
report.issues.push('Incomplete swift-testing report');
|
|
179
|
+
if (runPassed && report.tests.some((test) => test.status === exports.TestStatus.Failed)) {
|
|
180
|
+
report.issues.push('Contradictory swift-testing status');
|
|
181
|
+
}
|
|
182
|
+
report.valid = report.issues.length === 0;
|
|
183
|
+
return report;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Python stdlib unittest text output (#4970). Passing tests are unnamed unless
|
|
187
|
+
* verbose, so they are counted from the summary; failures are named by their
|
|
188
|
+
* FAIL:/ERROR: headers, which must account for every counted failure. Each
|
|
189
|
+
* failing subTest repeats its method's header and counts as a failure, while
|
|
190
|
+
* Ran counts methods, so identical headers collapse into one failed test. An
|
|
191
|
+
* unexpected success fails the run but is a test that passed.
|
|
192
|
+
*/
|
|
193
|
+
const UNITTEST_RAN = /^Ran (\d+) tests? in [\d.]+s$/gm;
|
|
194
|
+
const UNITTEST_HEADER = /^(?:FAIL|ERROR): (\S+)(?: \(([^)]*)\))?/gm;
|
|
195
|
+
function parseUnittest(output) {
|
|
196
|
+
const report = { format: exports.ReportFormat.Unittest, valid: true, tests: [], issues: [] };
|
|
197
|
+
const runs = [...output.matchAll(UNITTEST_RAN)];
|
|
198
|
+
if (runs.length !== 1) {
|
|
199
|
+
report.issues.push('Expected exactly one unittest summary');
|
|
200
|
+
report.valid = false;
|
|
201
|
+
return report;
|
|
202
|
+
}
|
|
203
|
+
const ran = Number(runs[0][1]);
|
|
204
|
+
const outcome = /^(OK|FAILED)(?: \(([^)]*)\))?$/m.exec(output.slice(runs[0].index + runs[0][0].length));
|
|
205
|
+
const counts = {};
|
|
206
|
+
for (const part of outcome?.[2]?.split(', ') ?? []) {
|
|
207
|
+
const [key, value] = part.split('=');
|
|
208
|
+
counts[key] = Number(value);
|
|
209
|
+
}
|
|
210
|
+
if (!outcome || Object.keys(counts).some((key) => !['failures', 'errors', 'skipped', 'expected failures', 'unexpected successes'].includes(key))) {
|
|
211
|
+
report.issues.push('Unsupported unittest outcome');
|
|
212
|
+
}
|
|
213
|
+
let headers = 0;
|
|
214
|
+
const failedIds = new Set();
|
|
215
|
+
for (const match of output.matchAll(UNITTEST_HEADER)) {
|
|
216
|
+
headers++;
|
|
217
|
+
const id = match[2] ?? '';
|
|
218
|
+
const key = `${match[1]} ${id}`;
|
|
219
|
+
if (failedIds.has(key))
|
|
220
|
+
continue;
|
|
221
|
+
failedIds.add(key);
|
|
222
|
+
// unittest synthesizes _FailedTest for an import/collection crash; its
|
|
223
|
+
// method name can equal the target, so it is a load failure, never RED.
|
|
224
|
+
if (id.includes('_FailedTest')) {
|
|
225
|
+
report.issues.push('unittest module failed to load');
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
report.tests.push({
|
|
229
|
+
name: match[1],
|
|
230
|
+
identities: [...new Set([match[1], id].filter(Boolean))],
|
|
231
|
+
group: null,
|
|
232
|
+
groupIdentities: [],
|
|
233
|
+
status: exports.TestStatus.Failed,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const failed = (counts['failures'] ?? 0) + (counts['errors'] ?? 0);
|
|
237
|
+
const skipped = counts['skipped'] ?? 0;
|
|
238
|
+
const runFailed = failed > 0 || (counts['unexpected successes'] ?? 0) > 0;
|
|
239
|
+
if (headers !== failed || (outcome?.[1] === 'FAILED') !== runFailed || failedIds.size + skipped > ran) {
|
|
240
|
+
report.issues.push('Incomplete unittest report');
|
|
241
|
+
}
|
|
242
|
+
const unnamed = (status) => ({ name: '', identities: [], group: null, groupIdentities: [], status });
|
|
243
|
+
for (let i = 0; i < skipped; i++)
|
|
244
|
+
report.tests.push(unnamed(exports.TestStatus.Skipped));
|
|
245
|
+
for (let i = Math.max(ran - failedIds.size - skipped, 0); i > 0; i--)
|
|
246
|
+
report.tests.push(unnamed(exports.TestStatus.Passed));
|
|
247
|
+
report.valid = report.issues.length === 0;
|
|
248
|
+
return report;
|
|
249
|
+
}
|
|
250
|
+
const adapters = [
|
|
251
|
+
{ matches: (output) => /^(?:TAP version \d+|(?:not )?ok\b|1\.\.\d+|#)/.test(output.trimStart()), parse: parseTap },
|
|
252
|
+
{ matches: (output) => output.trimStart().startsWith('<'), parse: parseJunit },
|
|
253
|
+
{ matches: (output) => new RegExp(SWIFT_AGGREGATE.source, 'm').test(output), parse: parseSwiftTesting },
|
|
254
|
+
{ matches: (output) => new RegExp(UNITTEST_RAN.source, 'm').test(output), parse: parseUnittest },
|
|
255
|
+
];
|
|
256
|
+
/** Parse supported reports into one result contract; unknown/malformed input fails closed. */
|
|
257
|
+
function parseTestReport(output) {
|
|
258
|
+
const adapter = adapters.find((candidate) => candidate.matches(output));
|
|
259
|
+
if (!adapter)
|
|
260
|
+
return { format: exports.ReportFormat.Unknown, valid: false, tests: [], issues: ['Unsupported report format'] };
|
|
261
|
+
try {
|
|
262
|
+
const report = adapter.parse(output);
|
|
263
|
+
// One entry per problem: a long run of bad lines must not amplify the payload.
|
|
264
|
+
return { ...report, issues: [...new Set(report.issues)] };
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return { format: exports.ReportFormat.Unknown, valid: false, tests: [], issues: ['Malformed report'] };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -64,7 +64,7 @@ exports.checkReviewerDocsParity = checkReviewerDocsParity;
|
|
|
64
64
|
* `args` is an argv TEMPLATE, not a prefix. The injected pieces — model, effort, output file,
|
|
65
65
|
* argv-borne prompt — do not all go in the same place, and no positional rule expresses that:
|
|
66
66
|
* `codex` injects the model in the MIDDLE (after the `exec --ephemeral` subcommand) and the output
|
|
67
|
-
* file later still, while `
|
|
67
|
+
* file later still, while `kimi-code` injects the model first and five lanes end with a bare `-` that
|
|
68
68
|
* must stay last. Splicing by position silently produced
|
|
69
69
|
* `codex --model M -o F exec --ephemeral …`, which is not a valid codex invocation.
|
|
70
70
|
*
|
|
@@ -94,36 +94,16 @@ const SPAWN_STDIN_STDOUT = {
|
|
|
94
94
|
outputChannel: 'stdout',
|
|
95
95
|
};
|
|
96
96
|
/**
|
|
97
|
-
* The
|
|
97
|
+
* The eleven declared lanes, in `write_reviews` order.
|
|
98
|
+
*
|
|
99
|
+
* The `gemini` lane was retired by #4709: Google sunset Gemini CLI on 2026-06-18 (the same
|
|
100
|
+
* sunset that removed the gemini RUNTIME in #1928/1.8.0), so the lane spawned a binary that no
|
|
101
|
+
* longer serves the free/Pro/Ultra tiers that are GSD's audience.
|
|
98
102
|
*
|
|
99
103
|
* `kimi-code` joined in Phase 5b (#2799, closes #2718) — ADR-2782's phase table lands it here
|
|
100
104
|
* rather than in 5a precisely so it arrives together with the iteration that can invoke it.
|
|
101
105
|
*/
|
|
102
106
|
exports.REVIEWER_LANES = Object.freeze([
|
|
103
|
-
{
|
|
104
|
-
slug: 'gemini',
|
|
105
|
-
flags: ['--gemini'],
|
|
106
|
-
transport: 'spawn',
|
|
107
|
-
probe: { kind: 'command-exists', binary: 'gemini' },
|
|
108
|
-
invoke: {
|
|
109
|
-
binary: 'gemini',
|
|
110
|
-
args: ['{{model}}', '-p', '-'],
|
|
111
|
-
...SPAWN_STDIN_STDOUT,
|
|
112
|
-
modelArg: '-m',
|
|
113
|
-
effortChannel: 'none',
|
|
114
|
-
},
|
|
115
|
-
timeoutFloorMs: 900_000,
|
|
116
|
-
timeoutConfigKey: 'review.timeouts.gemini',
|
|
117
|
-
emptyOutput: 'stub-with-stderr',
|
|
118
|
-
reviewsSection: 'Gemini',
|
|
119
|
-
evidenceClass: 'source-grounded',
|
|
120
|
-
requiresBinaries: [],
|
|
121
|
-
promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.gemini',
|
|
122
|
-
modelConfigKey: 'review.models.gemini',
|
|
123
|
-
effortConfigKey: null,
|
|
124
|
-
defaultEffort: null,
|
|
125
|
-
handler: null,
|
|
126
|
-
},
|
|
127
107
|
{
|
|
128
108
|
// 1_200_000 rather than the 900_000 floor: headless Claude measured ~525 s
|
|
129
109
|
// on a large plan set (review.md:304).
|
|
@@ -139,8 +119,8 @@ exports.REVIEWER_LANES = Object.freeze([
|
|
|
139
119
|
effortChannel: 'argv',
|
|
140
120
|
// #2483: without these the claude leg is the only reviewer that additionally inherits the
|
|
141
121
|
// invoking user's global CLAUDE.md, the project CLAUDE.md, and Claude Code auto-memory —
|
|
142
|
-
// a context asymmetry against the independent-review premise (
|
|
143
|
-
// assembled prompt
|
|
122
|
+
// a context asymmetry against the independent-review premise (codex sees only the
|
|
123
|
+
// assembled prompt and runs --ephemeral). Both flags, not just the first: CLAUDE.md
|
|
144
124
|
// loading and auto-memory are independently-toggled mechanisms, and an environment
|
|
145
125
|
// exporting CLAUDE_CODE_DISABLE_AUTO_MEMORY=0 forces auto-memory back ON — the explicit
|
|
146
126
|
// pair is robust against that. Applies when /gsd:review runs from a non-Claude-Code host;
|
|
@@ -796,7 +776,7 @@ const NON_LANE_SIGNATURE_FLAGS = new Set(['--all']);
|
|
|
796
776
|
/**
|
|
797
777
|
* Is `flag` documented in `text` under one of the two structural shapes docs actually use?
|
|
798
778
|
*
|
|
799
|
-
* Backticked is the `COMMANDS.md` table-cell shape; bracketed (`[--
|
|
779
|
+
* Backticked is the `COMMANDS.md` table-cell shape; bracketed (`[--codex]`) is the
|
|
800
780
|
* `FEATURES.md` signature shape. Requiring one of those two delimiters — rather than a bare
|
|
801
781
|
* substring — is what keeps prose and fenced examples from satisfying the gate, and it bounds the
|
|
802
782
|
* token for free: a backticked `--claude` demands its closing backtick, so a backticked
|
|
@@ -889,7 +869,7 @@ const BRACKETED_FLAG_RE = /\[(--[a-z0-9][a-z0-9-]*)\]/g;
|
|
|
889
869
|
* cell 3. A file-scoped check is therefore satisfied by the forwarding row alone, so deleting a
|
|
890
870
|
* lane's actual table row — the exact #2781 regression — passes. Keying on cell 1 separates the
|
|
891
871
|
* two: `| `--agy` / `--antigravity` | … |` is one lane row declaring two flags, while
|
|
892
|
-
* `| Reviewer flags | No | … `--
|
|
872
|
+
* `| Reviewer flags | No | … `--codex`, `--claude` … |` is not a lane row at all.
|
|
893
873
|
*/
|
|
894
874
|
function flagsInFirstTableCell(lines, declared) {
|
|
895
875
|
const found = new Set();
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `reviewer` bodies in the capability registry, not a hand-maintained tail.
|
|
21
21
|
* A capability of EITHER `role: "runtime"` (the six dual-purpose hosts —
|
|
22
22
|
* antigravity/claude/codex/cursor/opencode/qwen) or the lane-only
|
|
23
|
-
* `role: "reviewer"` (
|
|
23
|
+
* `role: "reviewer"` (coderabbit/ollama/lm_studio/llama_cpp) may carry
|
|
24
24
|
* a `reviewer` body, and it is the body's `reviewer.slug` — NOT the capability
|
|
25
25
|
* id — that becomes the roster entry: the two differ for `lm-studio` (id) /
|
|
26
26
|
* `lm_studio` (slug) and `llama-cpp` (id) / `llama_cpp` (slug), ADR-2782's
|
|
@@ -229,7 +229,7 @@ function resolveReviewerSelection(input) {
|
|
|
229
229
|
// ADR-2782 D4: absent-safe governs DISCOVERY, never explicit selection.
|
|
230
230
|
// Not finding a lane nobody asked for is normal; failing to run a lane
|
|
231
231
|
// somebody asked for is an error. Every miss used to be an `info`, so a
|
|
232
|
-
// PARTIAL miss (`--
|
|
232
|
+
// PARTIAL miss (`--codex --qwen` with qwen absent) ran the review with a
|
|
233
233
|
// thinner reviewer set while present_results reported success — "a cross-AI
|
|
234
234
|
// review that silently drops a lane is blind in one eye" (review.md).
|
|
235
235
|
// A total miss already errored, but only as a side effect of the selected
|
|
@@ -13,12 +13,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
15
|
const command_aliases_cjs_1 = require("./command-aliases.cjs");
|
|
16
|
+
const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
16
17
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
17
18
|
const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
|
|
18
19
|
const { routeCjsCommandFamily } = cjsCommandRouterAdapter;
|
|
19
20
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
20
21
|
const roadmapUpgrade = require("./roadmap-upgrade.cjs");
|
|
21
22
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
23
|
+
const phaseIdCardMod = require("./phase-id-card.cjs");
|
|
24
|
+
const { phaseIdCard } = phaseIdCardMod;
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
22
26
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
23
27
|
const { planningDir } = planningWorkspace;
|
|
24
28
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
@@ -142,14 +146,13 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
|
|
|
142
146
|
if (roadmapContent.trim() === '') {
|
|
143
147
|
warnings.push({ code: 'V002', message: 'ROADMAP.md is empty' });
|
|
144
148
|
}
|
|
145
|
-
// Malformed frontmatter — a `---` opener with no matching closer
|
|
146
|
-
//
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
149
|
+
// Malformed frontmatter — a `---` opener with no matching closer, as the one fence
|
|
150
|
+
// owner reads it (a leading BOM, #3057, is tolerated there).
|
|
151
|
+
const roadmapFence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(roadmapContent);
|
|
152
|
+
// The milestone-window read below sees the document without its leading BOM (#3057).
|
|
153
|
+
const contentAfterBom = roadmapContent.charCodeAt(0) === 0xFEFF ? roadmapContent.slice(1) : roadmapContent;
|
|
154
|
+
if (roadmapFence && !roadmapFence.closed) {
|
|
155
|
+
warnings.push({ code: 'V003', message: 'ROADMAP.md frontmatter is malformed (unterminated --- fence)' });
|
|
153
156
|
}
|
|
154
157
|
// #3641: resolve phase_id_convention ONCE, ahead of every consumer in
|
|
155
158
|
// this validate pass — V004's entry check and V005's scope classifier
|
|
@@ -166,13 +169,11 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
|
|
|
166
169
|
convention = undefined;
|
|
167
170
|
}
|
|
168
171
|
if (convention === undefined || convention === null) {
|
|
169
|
-
// Fallback: read from ROADMAP.md frontmatter
|
|
170
|
-
// cmdRoadmapMilestoneScope
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (fmMatch) {
|
|
175
|
-
const kvMatch = fmMatch[1].match(/^phase_id_convention:\s*(.*)$/m);
|
|
172
|
+
// Fallback: read from ROADMAP.md frontmatter — the block the one fence owner finds,
|
|
173
|
+
// exactly as cmdRoadmapMilestoneScope reads it (#3641 review NEW-1: two copies of
|
|
174
|
+
// this read disagreed on long frontmatter, diverging validate from the probe).
|
|
175
|
+
if (roadmapFence?.closed) {
|
|
176
|
+
const kvMatch = roadmapContent.slice(roadmapFence.openEnd, roadmapFence.bodyEnd).match(/^phase_id_convention:\s*(.*)$/m);
|
|
176
177
|
if (kvMatch) {
|
|
177
178
|
const val = kvMatch[1].trim();
|
|
178
179
|
if (val !== 'null' && val !== '') {
|
|
@@ -234,7 +235,7 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
|
|
|
234
235
|
'upgrade': () => {
|
|
235
236
|
const dryRun = !args.includes('--apply');
|
|
236
237
|
// Parse `--convention <value>` and `--convention=<value>`. When the flag is
|
|
237
|
-
// absent entirely, default to the
|
|
238
|
+
// absent entirely, default to the legacy supported convention; when present
|
|
238
239
|
// with a missing/unsupported value, fall through to the rejection below
|
|
239
240
|
// (fail-closed — never silently run a migration the user did not request).
|
|
240
241
|
let convention = 'milestone-prefixed';
|
|
@@ -245,13 +246,18 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
|
|
|
245
246
|
? token.slice(token.indexOf('=') + 1)
|
|
246
247
|
: (args[conventionFlagIdx + 1] ?? '');
|
|
247
248
|
}
|
|
248
|
-
if (convention !== 'milestone-prefixed') {
|
|
249
|
+
if (convention !== 'milestone-prefixed' && convention !== 'bracket') {
|
|
249
250
|
// No-throw hub contract (ADR-0012): a hub-dispatched handler must not call
|
|
250
251
|
// process.exit. Throw instead — the hub converts this to HandlerFailure and
|
|
251
252
|
// the adapter routes it through the injected error() boundary.
|
|
252
|
-
throw new Error('Only --convention milestone-prefixed is supported');
|
|
253
|
+
throw new Error('Only --convention milestone-prefixed or bracket is supported');
|
|
254
|
+
}
|
|
255
|
+
if (convention === 'bracket') {
|
|
256
|
+
// Keep stdout machine-readable for the dry-run JSON plan. The card is
|
|
257
|
+
// human guidance emitted at command start for both dry-run and apply.
|
|
258
|
+
process.stderr.write(`${phaseIdCard({ title: 'Bracket phase-ID convention' })}\n`);
|
|
253
259
|
}
|
|
254
|
-
const plan = roadmapUpgrade.computeMigrationPlan(cwd);
|
|
260
|
+
const plan = roadmapUpgrade.computeMigrationPlan(cwd, { convention });
|
|
255
261
|
roadmapUpgrade.applyMigration(cwd, plan, { dryRun });
|
|
256
262
|
},
|
|
257
263
|
},
|