@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
|
@@ -15,9 +15,9 @@ The user-flow form mirrors what a real user does: open, fill, click, see. No HTT
|
|
|
15
15
|
|
|
16
16
|
The framing fires when:
|
|
17
17
|
- The phase under verification has `**Mode:** mvp` in ROADMAP.md (parsed via `gsd_run query roadmap.get-phase --pick mode`).
|
|
18
|
-
- AND the phase has a user-story-formatted goal (set by `/gsd
|
|
18
|
+
- AND the phase has a user-story-formatted goal (set by `/gsd:mvp-phase` per Phase 2): "As a [user role], I want to [capability], so that [outcome]."
|
|
19
19
|
|
|
20
|
-
If the phase has `mode: mvp` but the goal is NOT in user-story format, the verifier surfaces this as a discrepancy and asks the user to run `/gsd
|
|
20
|
+
If the phase has `mode: mvp` but the goal is NOT in user-story format, the verifier surfaces this as a discrepancy and asks the user to run `/gsd:mvp-phase` to reformat the goal — same pattern as the planner agent under MVP_MODE (per `gsd-core/references/planner-mvp-mode.md`).
|
|
21
21
|
|
|
22
22
|
## Generated UAT script structure under MVP mode
|
|
23
23
|
|
|
@@ -77,13 +77,43 @@ and broader temp hygiene is left to OS temp cleanup or future maintenance work.
|
|
|
77
77
|
|
|
78
78
|
## Routing Propagation
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
Every workstream-scoped workflow parses the `--ws <name>` it was started with out of
|
|
81
|
+
`$ARGUMENTS` in each shell fence that needs it (fences are separate shells, so nothing is
|
|
82
|
+
carried over between them) and sets `GSD_WS`:
|
|
83
|
+
- `GSD_WS` is `--ws <name>` when the command was started with `--ws`, and empty otherwise
|
|
84
|
+
(flat mode, backward compatible). It is always re-assigned, so a stale value never leaks in.
|
|
85
|
+
- Every `gsd_run query agent-skills <agent>` and `gsd_run query init.*` call in those
|
|
86
|
+
workflows forwards it as the single token `${GSD_WS:+--ws=${GSD_WS##* }}`. The explicit
|
|
87
|
+
flag has the highest priority above, so the workstream a command was started for wins over
|
|
88
|
+
the session pointer and the shared marker. The single-token form is required because zsh
|
|
89
|
+
does not word-split an unquoted `$GSD_WS`.
|
|
90
|
+
- Workflows recognize the documented spelling `--ws <name>`; `--ws=<name>` is accepted by the
|
|
91
|
+
CLI itself but is not parsed out of `$ARGUMENTS` by workflows.
|
|
92
|
+
- Routing suggestions (`/gsd:plan-phase {X} ${GSD_WS}`) carry the same value to the next command.
|
|
93
|
+
- Project and workspace lifecycle workflows (`new-project`, `new-workspace`,
|
|
94
|
+
`list-workspaces`, `remove-workspace`, `update`) are root-scoped and do not forward it.
|
|
95
|
+
|
|
96
|
+
`tests/agent-skills.test.cjs` scans every fenced call in `gsd-core/workflows/` so a new call
|
|
97
|
+
that omits the flag fails CI.
|
|
83
98
|
|
|
84
99
|
This ensures workstream scope chains automatically through the workflow:
|
|
85
100
|
`new-milestone → discuss-phase → plan-phase → execute-phase → transition`
|
|
86
101
|
|
|
102
|
+
## Reserved names
|
|
103
|
+
|
|
104
|
+
`none` (case-insensitive) cannot name a workstream. `--ws none`, `--ws=none`, `GSD_WORKSTREAM=none`
|
|
105
|
+
and a pointer of `none` are all rejected with a "reserved" error, and `workstream create` and
|
|
106
|
+
`workstream set` refuse it unconditionally. The one exception on the read side is a
|
|
107
|
+
`.planning/workstreams/none/` directory that already exists: it stays addressable with `--ws none`
|
|
108
|
+
so projects that used the name before it was reserved keep working (the directory name must match
|
|
109
|
+
exactly; `NONE` is still rejected). Such a grandfathered directory is reachable through `--ws` and
|
|
110
|
+
`GSD_WORKSTREAM` only, because `workstream set none` refuses unconditionally. To force flat mode,
|
|
111
|
+
omit `--ws`, unset `GSD_WORKSTREAM`, or clear the pointer (`workstream set --clear`). The rejection
|
|
112
|
+
happens before any command runs, so a stale `GSD_WORKSTREAM=none` in the environment must be unset
|
|
113
|
+
first. The reserved list has a single owner
|
|
114
|
+
(`RESERVED_WORKSTREAM_NAMES` in `src/workstream-name-policy.cts`); near-misses such as `none1` or
|
|
115
|
+
`nonexistent` are ordinary names.
|
|
116
|
+
|
|
87
117
|
## Directory Structure
|
|
88
118
|
|
|
89
119
|
```
|
|
@@ -175,3 +175,324 @@ fi
|
|
|
175
175
|
**Prefer relative paths** for all Edit/Write operations. When an absolute path is
|
|
176
176
|
unavoidable, always derive it from `git rev-parse --show-toplevel` run inside the
|
|
177
177
|
worktree — never from `pwd` captured in the orchestrator context.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## `<automated>` command guard — step 0c (#4767)
|
|
182
|
+
|
|
183
|
+
The plan's `<automated>` text is where an orchestrator-cwd absolute path most often
|
|
184
|
+
arrives: the planner saw absolute paths in its own context and wrote one into the
|
|
185
|
+
command. Run as written, `cd /abs/main-checkout/… && <test>` leaves the worktree, runs
|
|
186
|
+
against the main tree, and **passes on code this worktree changed and the main tree did
|
|
187
|
+
not** — a green verify that verified nothing. Before executing any `<automated>` command,
|
|
188
|
+
scan its text for absolute paths and halt if one is outside the worktree. Fail loud; never
|
|
189
|
+
rewrite the prefix silently (#3050) — a rewritten command hides the defective plan, and the
|
|
190
|
+
next executor meets it again.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# WT_ROOT as in step 0b. MAIN_ROOT is the checkout this worktree was created from — the one an
|
|
194
|
+
# orchestrator-cwd path points at.
|
|
195
|
+
WT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
196
|
+
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." 2>/dev/null && pwd -P)
|
|
197
|
+
# Resolve a path the way the shell would land in it. A relative path is taken from the second
|
|
198
|
+
# argument — the cwd a chained `cd` has reached so far — or the worktree root. An existing directory resolves through `cd && pwd -P`; a file through its directory (a
|
|
199
|
+
# symlinked file through its link target first), so a symlink or `..` hop inside the worktree that
|
|
200
|
+
# lands in the main checkout is seen for what it is; a path that does not exist yet — a Wave-0
|
|
201
|
+
# scaffold — is normalized lexically and must still PASS when it sits under the worktree. Never
|
|
202
|
+
# `readlink -m` / `realpath -m` — GNU-only. `_norm` splits with `read -ra` (no glob expansion) and
|
|
203
|
+
# expands the array with the `${a[@]+"${a[@]}"}` idiom (bash < 4.4 errors on an empty array under -u).
|
|
204
|
+
_norm(){ local -a out=() seg; local s; IFS=/ read -ra seg <<<"$1"
|
|
205
|
+
for s in ${seg[@]+"${seg[@]}"}; do case "$s" in ''|.) ;; ..) [ ${#out[@]} -gt 0 ] && unset 'out[${#out[@]}-1]' ;; *) out+=("$s") ;; esac; done
|
|
206
|
+
printf '/%s' ${out[@]+"${out[@]}"}; [ ${#out[@]} -gt 0 ] || printf '/'; }
|
|
207
|
+
_resolve(){ local p t; case "$1" in /*) p=$1 ;; *) p="${2:-$WT_ROOT}/$1" ;; esac
|
|
208
|
+
if [ -L "$p" ] && ! [ -d "$p" ]; then t=$(readlink "$p"); case "$t" in /*) p=$t ;; *) p="$(dirname -- "$p")/$t" ;; esac; fi
|
|
209
|
+
( cd -- "$p" 2>/dev/null && pwd -P ) \
|
|
210
|
+
|| ( cd -- "$(dirname -- "$p")" 2>/dev/null && printf '%s/%s' "$(pwd -P)" "$(basename -- "$p")" ) \
|
|
211
|
+
|| _norm "$p"; }
|
|
212
|
+
_outside_wt(){ case "$1" in "$WT_ROOT"|"$WT_ROOT"/*) return 1 ;; *) return 0 ;; esac; }
|
|
213
|
+
# A shell word as the planner wrote it: runs of bare characters, "…" / '…' spans, and backslash
|
|
214
|
+
# escapes, in any mix (`"/x"/y`, `"O'Reilly"`, `path\ with\ space`, `release=main`). `_unquote`
|
|
215
|
+
# walks it with the shell's own three quoting states and returns the string the shell would pass.
|
|
216
|
+
_TOK='("[^"]*"|'"'"'[^'"'"']*'"'"'|\\.|[^[:space:]"'"'"';|&()])+'
|
|
217
|
+
_unquote(){ local s=$1 out='' q='' c i
|
|
218
|
+
for ((i=0; i<${#s}; i++)); do c=${s:i:1}
|
|
219
|
+
if [ -z "$q" ]; then case "$c" in '"'|"'") q=$c ;; '\') i=$((i+1)); out+=${s:i:1} ;; *) out+=$c ;; esac
|
|
220
|
+
elif [ "$q" = '"' ]; then case "$c" in '"') q='' ;; '\') i=$((i+1)); out+=${s:i:1} ;; *) out+=$c ;; esac
|
|
221
|
+
else case "$c" in "'") q='' ;; *) out+=$c ;; esac; fi
|
|
222
|
+
done; printf '%s' "$out"; }
|
|
223
|
+
# True when raw shell text contains an unquoted single `&` or `|`. `&&` and
|
|
224
|
+
# `||` keep the current shell's cwd; the single forms cross a process boundary.
|
|
225
|
+
_resets_cwd(){ local s=$1 q='' i c p n; for ((i=0;i<${#s};i++)); do c=${s:i:1}
|
|
226
|
+
if [ -z "$q" ]; then case "$c" in
|
|
227
|
+
"'"|'"') q=$c ;; '\') i=$((i+1)) ;;
|
|
228
|
+
'&'|'|') p=''; n=''; [ "$i" -gt 0 ] && p=${s:i-1:1}; [ "$i" -lt $((${#s}-1)) ] && n=${s:i+1:1};
|
|
229
|
+
[ "$p" = "$c" ] || [ "$n" = "$c" ] || return 0 ;;
|
|
230
|
+
esac
|
|
231
|
+
elif [ "$q" = '"' ]; then case "$c" in '"') q='' ;; '\') i=$((i+1)) ;; esac
|
|
232
|
+
else [ "$c" = "'" ] && q=''; fi
|
|
233
|
+
done; return 1; }
|
|
234
|
+
# The relocating verbs — `cd` / `pushd` (bare, `builtin`/`command`-prefixed, env-prefixed, or with
|
|
235
|
+
# `--`) and a tool's own directory flag (`npm`/`npx`/`pnpm` `--prefix`, `yarn --cwd`, `pnpm -C`,
|
|
236
|
+
# `make -C`, `git -C`; both `--flag <p>` and `--flag=<p>`) at the start of a segment, including after a
|
|
237
|
+
# `(`/`{` opener or a `&` background operator. Each match is stripped of its prefix with an ANCHORED sed,
|
|
238
|
+
# so the target is taken verbatim, unquoted, and compared LITERALLY — never interpolated into a regex.
|
|
239
|
+
_VERB='((builtin|command)[[:space:]]+)?(cd|pushd)([[:space:]]+--)?'
|
|
240
|
+
_ENV='([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+)*'
|
|
241
|
+
# `&` joins the operator set; grep is line-oriented, so a newline is already covered by `^`.
|
|
242
|
+
# CONTROL KEYWORDS (`if`, `then`, `do`, …) WERE TRIED HERE AND REVERTED, and the reason is the rule
|
|
243
|
+
# this fence is built on rather than a detail: these operators are matched against RAW TEXT, with no
|
|
244
|
+
# awareness of quoting, so a keyword that is also an ordinary English word turns prose inside a
|
|
245
|
+
# quoted argument into a command boundary — `grep -F 'if cd ../main; then' README.md` halted on a
|
|
246
|
+
# command that relocates nothing. That buys a rare false negative (`if cd ../main; then …`) at the
|
|
247
|
+
# cost of a realistic false positive, and this guard's whole posture is that the false-positive
|
|
248
|
+
# direction is the one that gets it routed around. The residual is disclosed instead.
|
|
249
|
+
# Spell the literal ERE operators as bracket expressions. `\|` / `\(` / `\{`
|
|
250
|
+
# are GNU-tolerated but undefined by POSIX and BSD sed rejects the same boundary
|
|
251
|
+
# expression with "unbalanced brackets" before the scan can run (#4767).
|
|
252
|
+
_OPEN='(^|&&|&|;|[|][|]|[|]|[(]|[{])[[:space:]]*'
|
|
253
|
+
# LAUNCHERS are a NAMED set, not `[^&;|]*`. A wrapper reached through a launcher is still a wrapper,
|
|
254
|
+
# and the set admits an absolute path (`/usr/bin/env`) and an option with a SEPARATE operand
|
|
255
|
+
# (`env -u FOO`, `timeout --signal TERM`, `stdbuf -o L`). Which options TAKE an operand is
|
|
256
|
+
# TOOL-SCOPED for the same reason `_DIR`'s flags are: `-i` takes one on `stdbuf` and NONE on `env`,
|
|
257
|
+
# so a flat set consumed the COMMAND after an operandless flag and `env -i echo bash -c '…'` —
|
|
258
|
+
# which runs `echo` — halted. `command` and `exec` are deliberately NOT in the set: `command -v
|
|
259
|
+
# bash` is a name PROBE that executes nothing, and halting it is the false-positive direction
|
|
260
|
+
# again. The cost is that a genuine `command bash -c …` is not unwrapped; that is disclosed.
|
|
261
|
+
# but a catch-all leading run also unwraps interpreter TEXT that is merely an argument — `echo bash -c
|
|
262
|
+
# 'cd ../main'` halted on a command that executes nothing. A spurious unwrap is NOT harmless: the scans
|
|
263
|
+
# halt on a path the shell would never visit. Name the launchers instead.
|
|
264
|
+
_LOPT='([[:space:]]+-[^[:space:]]+|[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*)*'
|
|
265
|
+
# THE SHAPE IS `(<generic opts> <opt> <operand>)* <generic opts>`, NOT `<generic opts> (<opt>
|
|
266
|
+
# <operand>)* <generic opts>`. The second admits only ONE contiguous run of operand-taking options,
|
|
267
|
+
# so a generic option BETWEEN two of them ends the run and hides everything after it:
|
|
268
|
+
# `env -u FOO --debug -u BAR -C ../main` relocates and passed silently. Interleaving is the normal
|
|
269
|
+
# way these tools are invoked, so the grammar has to allow it. Same shape for timeout and stdbuf.
|
|
270
|
+
# env's own separate-operand options, factored out because BOTH consumers need them: $_LAUNCH, so a
|
|
271
|
+
# payload behind `env -u FOO bash -c …` is unwrapped, and $_DIR, so `env -u FOO -C ../main` is still
|
|
272
|
+
# seen as a relocation. Generic $_LOPT cannot consume `FOO`, so without this the option HID the flag.
|
|
273
|
+
_ENVOPT='('"$_LOPT"'[[:space:]]+(-u|--unset)[[:space:]]+'"$_TOK"')*'"$_LOPT"
|
|
274
|
+
# $_LAUNCH needs a WIDER env group than $_DIR does, and the difference is deliberate. $_DIR must leave
|
|
275
|
+
# `-C`/`--chdir` UNCONSUMED — that flag is the thing it is looking for. $_LAUNCH must consume it, or
|
|
276
|
+
# `env -C scripts bash -c '…'` has its relocation seen and its PAYLOAD never unwrapped, which is the
|
|
277
|
+
# bypass class this whole guard is about. Two groups, one per consumer.
|
|
278
|
+
_ENVOPTL='('"$_LOPT"'[[:space:]]+((-u|--unset)[[:space:]]+|(-C|--chdir)([[:space:]]+|=))'"$_TOK"')*'"$_LOPT"
|
|
279
|
+
_LAUNCH='((/[^[:space:]]*/)?((env|setsid|nohup)'"$_ENVOPTL"'|timeout('"$_LOPT"'[[:space:]]+(-s|--signal|-k|--kill-after)[[:space:]]+[^[:space:]]+)*'"$_LOPT"'[[:space:]]+[0-9]+[smhd]?|(stdbuf|nice|ionice)('"$_LOPT"'[[:space:]]+(-i|-o|-e|-n|-c)[[:space:]]+[^[:space:]]+)*'"$_LOPT"')[[:space:]]+)*'
|
|
280
|
+
# The directory flags are TOOL-SCOPED, because the same spelling is not the same flag on every tool:
|
|
281
|
+
# `git archive --prefix=../main/ HEAD` names archive MEMBERS and changes no directory, so a flat
|
|
282
|
+
# six-tool `--prefix` halts a legitimate command. `--prefix`/`--cwd`/`--dir`/`-C` belong to the node
|
|
283
|
+
# package managers; `-C`/`--directory` to make and git. $_LAUNCH leads it for $_EXEC's reason — a
|
|
284
|
+
# recognised tool behind `env` is still that tool.
|
|
285
|
+
_DIR='(('"$_LAUNCH"')(npm|npx|pnpm|yarn)[[:space:]]+([^&;|]*[[:space:]]+)?(--prefix|--cwd|--dir|-C)|('"$_LAUNCH"')(make|git)[[:space:]]+([^&;|]*[[:space:]]+)?(-C|--directory)|('"$_LAUNCH"')(/[^[:space:]]*/)?env'"$_ENVOPT"'[[:space:]]+(--chdir|-C))[[:space:]=]+'
|
|
286
|
+
# Command-string interpreters. `eval "cd <main> && x"`, `sh -c "…"`, `bash -c '…'`, `node -e '…'` bury
|
|
287
|
+
# the real command inside ONE opaque quoted token, and that defeats BOTH scans below at once: the
|
|
288
|
+
# relocating verb never sits at a boundary scan 1 recognizes, and scan 2's tokenizer swallows the whole
|
|
289
|
+
# quoted span, which after unquoting does not begin with `/`. These are ordinary portability idioms, not
|
|
290
|
+
# obfuscation — a planner imitating a CI script reaches for one without any adversarial intent — so the
|
|
291
|
+
# payload is UNWRAPPED and re-scanned rather than trusted or rejected wholesale. $_LAUNCH lets the
|
|
292
|
+
# launcher forms a planner actually writes — `env bash -c`, a `timeout`-wrapped `bash -c`,
|
|
293
|
+
# `nohup sh -c` — reach the same unwrap as a bare one, and an absolute or option-bearing
|
|
294
|
+
# interpreter (`/bin/bash -c`, `bash --noprofile -c`) with it.
|
|
295
|
+
_EXEC='('"$_LAUNCH"')((/[^[:space:]]*/)?(sh|bash|zsh|dash|ksh)([[:space:]]+--[a-z-]+)*[[:space:]]+-[a-z]*c|(/[^[:space:]]*/)?(python3?|node|perl|ruby)([[:space:]]+--[a-z-]+)*[[:space:]]+-[a-z]*[ce]|eval)[[:space:]]+'
|
|
296
|
+
# `eval` runs in the current shell, unlike the other interpreters above; its payload's final cwd must
|
|
297
|
+
# therefore flow into the next event. A subprocess payload is scanned from the reached cwd and then
|
|
298
|
+
# restores its caller's cwd.
|
|
299
|
+
_EVAL='eval[[:space:]]+'
|
|
300
|
+
# ONE ordered pass per scanned string, each event tagged `C` (a cd/pushd — moves the cwd), `N` (a
|
|
301
|
+
# tool's directory flag — resolved from the cwd reached so far but does NOT move it), `E` (`eval`,
|
|
302
|
+
# whose cwd change persists), or `W` (a subprocess interpreter, whose cwd change does not). Ordering
|
|
303
|
+
# is load-bearing: scanning every outer relocation before an earlier payload lets a later `cd` supply
|
|
304
|
+
# that payload with a cwd the shell has not reached yet. `_scan` is a FUNCTION so an unwrapped payload
|
|
305
|
+
# reaches the SAME event scan as the top-level command; it halts with
|
|
306
|
+
# `exit 1`, never a return code, so a halt inside a payload is exactly as fatal as one outside; and it
|
|
307
|
+
# ends `return 0` so a trailing non-`C` target cannot make the function itself look failed under `set -e`.
|
|
308
|
+
CUR=$WT_ROOT
|
|
309
|
+
_scan(){ local CMD=$1 D=$2 RAW BODY BFR REST K Q T R P S PREFIX LDS LD _T _P
|
|
310
|
+
_T=$( printf '%s' "$CMD" | grep -oE "${_OPEN}(${_ENV}${_EXEC}|${_ENV}${_VERB}[[:space:]]+|${_DIR})${_TOK}" \
|
|
311
|
+
| sed '/^$/d' )
|
|
312
|
+
# 1. Every relocating target, relative or absolute, resolved from the cwd the command has reached
|
|
313
|
+
# (chained `cd scripts && cd ..` lands back at the root and passes; `cd scripts && cd ../..` does
|
|
314
|
+
# not): outside the worktree → halt. A target the shell would expand (`~`, `$VAR`, `$(…)`) cannot
|
|
315
|
+
# be evaluated here and passes through — the plan-checker's probe already reports those as
|
|
316
|
+
# `dynamic_path`, and `$(git rev-parse --show-toplevel)` is the form step 0b itself recommends.
|
|
317
|
+
REST=$CMD
|
|
318
|
+
while IFS= read -r RAW; do
|
|
319
|
+
[ -n "$RAW" ] || continue
|
|
320
|
+
# Preserve process boundaries even when an unrecognized command sits between
|
|
321
|
+
# one and the next recognized event (`cd x & echo y && bash -c ...`).
|
|
322
|
+
BFR=${REST%%"$RAW"*}; REST=${REST#*"$RAW"}
|
|
323
|
+
if _resets_cwd "$BFR" || _resets_cwd "$RAW"; then CUR=$WT_ROOT; fi
|
|
324
|
+
BODY=$(printf '%s' "$RAW" | sed -E "s/^(&&|&|;|[|][|]|[|]|[(]|[{])?[[:space:]]*//")
|
|
325
|
+
# Classify with the same grep implementation that produced the event and
|
|
326
|
+
# remove the matched prefix literally. Expanding these composed EREs into a
|
|
327
|
+
# multi-expression BSD sed program fails with "unbalanced brackets" on
|
|
328
|
+
# macOS even though BSD grep accepts the event expression (#4767).
|
|
329
|
+
if _P=$(printf '%s' "$BODY" | grep -oE "^${_ENV}${_EVAL}"); then K=E
|
|
330
|
+
elif _P=$(printf '%s' "$BODY" | grep -oE "^${_ENV}${_EXEC}"); then K=W
|
|
331
|
+
elif _P=$(printf '%s' "$BODY" | grep -oE "^${_DIR}"); then K=N
|
|
332
|
+
else _P=$(printf '%s' "$BODY" | grep -oE "^${_ENV}${_VERB}[[:space:]]+"); K=C
|
|
333
|
+
fi
|
|
334
|
+
Q=${BODY#"$_P"}; T=$(_unquote "$Q")
|
|
335
|
+
case "$K" in
|
|
336
|
+
E) [ "$D" -ge 4 ] || _walk "$T" "$((D+1))" ;;
|
|
337
|
+
W) if [ "$D" -lt 4 ]; then
|
|
338
|
+
S=$CUR
|
|
339
|
+
# `_EXEC` is longer than `_DIR`, so a wrapper reached through
|
|
340
|
+
# `env -C/--chdir` wins grep's leftmost-longest match. Recover that
|
|
341
|
+
# launcher's relocation here: validate it, start the child there,
|
|
342
|
+
# then restore the parent cwd when the payload scan returns.
|
|
343
|
+
PREFIX=${BODY%"$Q"}
|
|
344
|
+
LDS=$(printf '%s' "$PREFIX" | grep -oE "(^|[[:space:]])(-C|--chdir)([[:space:]]+|=)${_TOK}" \
|
|
345
|
+
| sed -E 's/^[[:space:]]*(-C|--chdir)([[:space:]]+|=)//' || true)
|
|
346
|
+
while IFS= read -r LD; do
|
|
347
|
+
[ -n "$LD" ] || continue
|
|
348
|
+
LD=$(_unquote "$LD"); R=$(_resolve "$LD" "$CUR")
|
|
349
|
+
if _outside_wt "$R"; then
|
|
350
|
+
echo "FATAL: <automated> command relocates to $LD -> $R, outside the worktree ($WT_ROOT) — it would verify the wrong checkout. Rewrite the plan's command root-relative (cwd is the checkout root); do not rewrite it in place." >&2
|
|
351
|
+
exit 1
|
|
352
|
+
fi
|
|
353
|
+
CUR=$R
|
|
354
|
+
done <<EOF_LAUNCH_DIRS
|
|
355
|
+
$LDS
|
|
356
|
+
EOF_LAUNCH_DIRS
|
|
357
|
+
_walk "$T" "$((D+1))"; CUR=$S
|
|
358
|
+
fi ;;
|
|
359
|
+
C|N)
|
|
360
|
+
R=$(_resolve "$T" "$CUR")
|
|
361
|
+
if _outside_wt "$R"; then
|
|
362
|
+
echo "FATAL: <automated> command relocates to $T -> $R, outside the worktree ($WT_ROOT) — it would verify the wrong checkout. Rewrite the plan's command root-relative (cwd is the checkout root); do not rewrite it in place." >&2
|
|
363
|
+
exit 1
|
|
364
|
+
fi
|
|
365
|
+
[ "$K" = C ] && CUR=$R ;;
|
|
366
|
+
esac
|
|
367
|
+
done <<EOF_TARGETS
|
|
368
|
+
$_T
|
|
369
|
+
EOF_TARGETS
|
|
370
|
+
# 2. Any other absolute word that resolves under the MAIN checkout — a file argument, a redirect,
|
|
371
|
+
# an include — is the same defect by a different verb; system paths such as /dev/null or /usr/bin
|
|
372
|
+
# are neither and pass.
|
|
373
|
+
while IFS= read -r P; do
|
|
374
|
+
[ -n "$P" ] || continue
|
|
375
|
+
P=$(_unquote "$P"); case "$P" in [A-Za-z_]*=*|--*=*) P=${P#*=} ;; esac # FOO=/x, --flag=/x
|
|
376
|
+
case "$P" in /*) ;; *) continue ;; esac
|
|
377
|
+
R=$(_resolve "$P")
|
|
378
|
+
_outside_wt "$R" || continue
|
|
379
|
+
case "$R" in
|
|
380
|
+
"$MAIN_ROOT"|"$MAIN_ROOT"/*)
|
|
381
|
+
echo "FATAL: <automated> command names $P (-> $R) inside the main checkout, outside the worktree ($WT_ROOT) — it would verify the wrong checkout. Rewrite the plan's command root-relative (cwd is the checkout root); do not rewrite it in place." >&2
|
|
382
|
+
exit 1 ;;
|
|
383
|
+
esac
|
|
384
|
+
done <<EOF_ABS
|
|
385
|
+
$(printf '%s' "$CMD" | grep -oE "$_TOK")
|
|
386
|
+
EOF_ABS
|
|
387
|
+
return 0; }
|
|
388
|
+
# EVERY s-COMMAND THAT INTERPOLATES ONE OF THESE REGEXES USES `#`, NOT `/`. $_EXEC, $_LAUNCH and
|
|
389
|
+
# (through $_LAUNCH) $_DIR all contain a literal `/` from their absolute-path groups, and a `/`
|
|
390
|
+
# inside the pattern closes an `s/…/…/` early: sed dies `unknown option to 's'` on STDERR inside a
|
|
391
|
+
# command substitution, the extracted list comes back EMPTY, and the scan silently stops happening
|
|
392
|
+
# while every command reads as a clean pass. It is written as a rule about ALL of them rather than
|
|
393
|
+
# about the one that had a slash first, because that is exactly how it recurred: $_EXEC was fixed,
|
|
394
|
+
# then $_LAUNCH gained `(/[^[:space:]]*/)?` and took $_DIR down with it. Both driven, not theorised.
|
|
395
|
+
# 3. The ordered event scan unwraps each interpreter payload in place, to a bounded depth — a wrapper
|
|
396
|
+
# inside a wrapper is still a wrapper, and the bound is what keeps a pathological nest finite.
|
|
397
|
+
# A payload the shell would build at run time (`bash -c $CMD`) is not a literal token here and passes
|
|
398
|
+
# through as a dynamic_path, exactly as scan 1 treats an expansion.
|
|
399
|
+
_walk(){ _scan "$1" "$2"; }
|
|
400
|
+
_walk "$AUTOMATED_CMD" 0
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
A halt here is a plan defect, not an executor deviation: report it via the checkpoint return
|
|
404
|
+
format naming the task and the offending command verbatim, and stop. The plan-checker's path
|
|
405
|
+
probe (`check verify-command-paths`) warns on the *outside-orchestrator-root* case before
|
|
406
|
+
execution; this guard is the one that sees the executor's actual root.
|
|
407
|
+
|
|
408
|
+
### What this guard does NOT see — step 0c's stated boundary
|
|
409
|
+
|
|
410
|
+
The two scans recognize **fixed sets** — relocating verbs, directory flags, interpreters, launchers,
|
|
411
|
+
command boundaries — and the domain they act on, shell a planner may write, can acquire a member without
|
|
412
|
+
this file changing. So the boundary is written out rather than left to be rediscovered, and each entry
|
|
413
|
+
says what follows from it.
|
|
414
|
+
|
|
415
|
+
**The largest one first, because it is the one a reader will otherwise assume away:**
|
|
416
|
+
|
|
417
|
+
- **A RELATIVE path argument to a command that is not a recognized relocating verb.**
|
|
418
|
+
`python3 -m pytest ../main/tests`, `node ../main/test.js`, `make -f ../main/Makefile`, a redirect from
|
|
419
|
+
`../main/file` — all pass. The catch-all scan evaluates only words that begin with `/`, so it never
|
|
420
|
+
resolves a relative one, and the verb scan does not fire because none of these relocates. This is the
|
|
421
|
+
widest gap in the guard and it is not new. Closing it means resolving every relative word in the
|
|
422
|
+
command against the reached cwd, which halts on ordinary in-worktree arguments unless it can tell a
|
|
423
|
+
path from a flag value from a bare string — a much larger change than this guard is, and one that
|
|
424
|
+
fails toward false positives, which is the direction that gets a guard deleted.
|
|
425
|
+
|
|
426
|
+
The rest, in descending order of how likely a planner is to reach them:
|
|
427
|
+
|
|
428
|
+
- **Spellings that hide the verb from a text scan.** `cd$IFS/x` and its relatives split at run time, so
|
|
429
|
+
no static tokenizer sees a `cd` at a boundary. Deliberate: the guard's domain is what a planner
|
|
430
|
+
plausibly writes, not an adversary.
|
|
431
|
+
- **Anything the shell builds at run time.** `cd $VAR`, `cd "$(…)"`, `~`, and equally `bash -c "$CMD"` —
|
|
432
|
+
the payload is not a literal token, so there is nothing to unwrap. These pass through by design;
|
|
433
|
+
`check verify-command-paths` reports them as `dynamic_path` before execution, and
|
|
434
|
+
`$(git rev-parse --show-toplevel)` is the form step 0b recommends.
|
|
435
|
+
- **A command boundary outside `$_OPEN`'s set — and the set is matched against RAW TEXT.** `&&` `&`
|
|
436
|
+
`;` `|` `(` `{` open a command. Control keywords (`if cd ../main; then …`) are deliberately NOT in
|
|
437
|
+
the set: these operators have no awareness of quoting, so a keyword that is also an ordinary English
|
|
438
|
+
word turns prose inside a quoted argument into a boundary — `grep -F 'if cd ../main; then' README.md`
|
|
439
|
+
halted on a command that relocates nothing. The same quote-blindness is why
|
|
440
|
+
`echo 'note; bash -c "cd ../main"'` halts on its `;`: an over-halt on quoted data is the standing
|
|
441
|
+
cost of scanning text, and it is paid in the loud direction.
|
|
442
|
+
- **A launcher outside `$_LAUNCH`'s set**, and therefore an interpreter or directory flag behind it.
|
|
443
|
+
`env` `timeout` `nohup` `stdbuf` `nice` `ionice` `setsid` are recognized, named by bare word or
|
|
444
|
+
absolute path, with options that take a separate operand (`env -u FOO`, `timeout --signal TERM`).
|
|
445
|
+
The set is named rather than a catch-all deliberately: a catch-all also unwrapped interpreter text
|
|
446
|
+
that was merely an *argument* (`echo bash -c '…'`), and halting on a command that executes nothing
|
|
447
|
+
is the false-positive direction. `command` and `exec` are excluded for that same reason —
|
|
448
|
+
`command -v bash` is a name probe.
|
|
449
|
+
- **An interpreter outside `$_EXEC`'s set.** The set is stated because the entries above name only the
|
|
450
|
+
handful a planner reaches for most, which reads as the whole of it — `perl -e 'chdir "/main"'` IS
|
|
451
|
+
unwrapped and rescanned. Recognized, each named by a bare word OR an absolute path: `sh` `bash`
|
|
452
|
+
`zsh` `dash` `ksh`, and `python3`/`python` `node` `perl` `ruby`. The option is a short-option
|
|
453
|
+
CLUSTER, not the literal flag — `-[a-z]*c` for the shells and `-[a-z]*[ce]` for the rest — so
|
|
454
|
+
`-c`, `-ec` and even `-abc` all match. Recognized as a BARE WORD ONLY, with no absolute-path
|
|
455
|
+
form: `eval`; `/bin/eval 'cd ../main'` is NOT seen (driven).
|
|
456
|
+
Not recognized: an interpreter with no entry (`awk`, `php`, a shell not listed), whose payload stays
|
|
457
|
+
one opaque token, so only an absolute path written as a bare word in it is caught by the catch-all
|
|
458
|
+
scan. **The two lists are delimited on purpose** — the parity test in
|
|
459
|
+
`tests/executor-mvp-tdd-section.test.cjs` reads only the span between `Recognized,` and
|
|
460
|
+
`Not recognized:`, matching BACKTICKED TOKENS rather than substrings, because a containment check over the whole section is satisfied by a name that
|
|
461
|
+
appears here saying it is UNsupported: adding `awk` to `$_EXEC` with this prose untouched passed
|
|
462
|
+
that test, driven by the pre-push review of the round that added it.
|
|
463
|
+
- **An interpreter option that takes an operand.** `bash --noprofile -c` is recognized;
|
|
464
|
+
`bash -O extglob -c` is not, because `_EXEC` admits long options without values, and widening it to
|
|
465
|
+
consume operands risks swallowing the `-c` it is looking for.
|
|
466
|
+
- **A genuine `command bash -c …` or `exec bash -c …` is not unwrapped.** Those two words were removed
|
|
467
|
+
from `$_LAUNCH` to stop `command -v bash` — a name probe that executes nothing — from halting. A
|
|
468
|
+
deliberate trade of a rare false negative for a common false positive, recorded so it reads as a
|
|
469
|
+
decision rather than an omission.
|
|
470
|
+
- **A payload behind a RELOCATING launcher is scanned from the command's cwd, not the launcher's.**
|
|
471
|
+
`env -C scripts bash -c 'cd ..'` lands back inside the worktree in reality, and halts here, because
|
|
472
|
+
the `-C` target is checked but is not threaded into the payload's starting cwd. An over-halt, in the
|
|
473
|
+
loud direction, on a shape a planner is unlikely to write.
|
|
474
|
+
- **An operand-taking option ordering outside the grammar.** Options that take a separate operand
|
|
475
|
+
are matched interleaved with ordinary ones, so `env -u FOO --debug -u BAR -C ../main` is seen; a
|
|
476
|
+
spelling outside each tool's own list still is not.
|
|
477
|
+
- **A launcher form outside the grammar**: `env -S 'cmd args'`, a launcher named through a quoted
|
|
478
|
+
string, or an option-with-operand spelling not in the tool's list. Which options take an operand is
|
|
479
|
+
TOOL-SCOPED (`-i` takes one on `stdbuf`, none on `env`); a flat list consumed the command itself
|
|
480
|
+
after an operandless flag.
|
|
481
|
+
- **`_DIR`'s tool-to-flag run is still a catch-all**, so a pass-through argument that happens to spell
|
|
482
|
+
a directory flag can over-halt: `npm run package -- --prefix=../main/` halts although the flag goes
|
|
483
|
+
to the script, not to npm. Pre-existing, and again in the loud direction.
|
|
484
|
+
- **A non-shell payload whose path is computed.** `python3 -c` / `node -e` payloads are scanned *as
|
|
485
|
+
shell text*, which catches a path written as a literal word (`os.chdir('/main')`) and not one the
|
|
486
|
+
program assembles (`os.path.join(root, '..')`).
|
|
487
|
+
- **Wrapping nested more than four deep.** `_walk` bounds its own recursion.
|
|
488
|
+
- **A directory flag on a tool outside the recognized set.** `--prefix`/`--cwd`/`--dir`/`-C` on `npm`
|
|
489
|
+
`npx` `pnpm` `yarn`, `-C`/`--directory` on `make` `git`, and `--chdir`/`-C` on `env` itself — the
|
|
490
|
+
last because `env` is not only a launcher, it relocates — including behind a launcher. The
|
|
491
|
+
scoping is per-tool because the same spelling is not the same flag everywhere: `git archive
|
|
492
|
+
--prefix=` names archive members and changes no directory. For an unrecognized tool the **absolute**
|
|
493
|
+
form of its flag is still caught by the catch-all; what is uncovered is the **relative** form.
|
|
494
|
+
|
|
495
|
+
The enumerations are the parts that rot: they mirror a toolchain this file does not own. Adding a tool
|
|
496
|
+
is one alternation in `_DIR` plus a row in each of the two tables in
|
|
497
|
+
`tests/executor-mvp-tdd-section.test.cjs`. The guard is the executor's last containment check, not its
|
|
498
|
+
only one — a halt it misses still has to get past `check verify-command-paths` at plan time.
|
|
@@ -17,7 +17,7 @@ These files live directly at `.planning/` — not inside phase subdirectories.
|
|
|
17
17
|
| `STATE.md` | `state.md` | `/gsd:new-project`, `/gsd:health --repair` | Current session state, active phase, last activity |
|
|
18
18
|
| `REQUIREMENTS.md` | `requirements.md` | `/gsd:new-milestone` | Functional requirements with traceability |
|
|
19
19
|
| `MILESTONES.md` | `milestone.md` | `/gsd:complete-milestone` | Log of completed milestones with accomplishments |
|
|
20
|
-
| `BACKLOG.md` | *(inline)* | `/gsd
|
|
20
|
+
| `BACKLOG.md` | *(inline)* | `/gsd:capture --backlog` | Pending ideas and deferred work |
|
|
21
21
|
| `LEARNINGS.md` | *(inline)* | `/gsd:extract-learnings`, `/gsd:execute-phase` (gated: `features.global_learnings`) | Phase retrospective learnings for future plans |
|
|
22
22
|
| `THREADS.md` | *(inline)* | `/gsd:thread` | Persistent discussion threads |
|
|
23
23
|
| `config.json` | `config.json` | `/gsd:new-project`, `/gsd:health --repair` | Project-specific GSD configuration |
|
|
@@ -66,7 +66,7 @@ blocked: [N]
|
|
|
66
66
|
|
|
67
67
|
<!-- YAML format for plan-phase --gaps consumption -->
|
|
68
68
|
- truth: "[expected behavior from test]"
|
|
69
|
-
status: failed
|
|
69
|
+
status: failed | resolved
|
|
70
70
|
reason: "User reported: [verbatim response]"
|
|
71
71
|
severity: blocker | major | minor | cosmetic
|
|
72
72
|
test: [N]
|
|
@@ -74,6 +74,10 @@ blocked: [N]
|
|
|
74
74
|
artifacts: [] # Filled by diagnosis
|
|
75
75
|
missing: [] # Filled by diagnosis
|
|
76
76
|
debug_session: "" # Filled by diagnosis
|
|
77
|
+
resolved_by: "" # Written by verify-work's reconcile_gaps (#1921) when
|
|
78
|
+
# an executed gap-closure plan resolves this gap —
|
|
79
|
+
# never author this directly. Plan basename.
|
|
80
|
+
resolved_at: "" # Written alongside resolved_by (#1921).
|
|
77
81
|
```
|
|
78
82
|
|
|
79
83
|
---
|
|
@@ -107,6 +111,18 @@ blocked: [N]
|
|
|
107
111
|
- APPEND only when issue found (YAML format)
|
|
108
112
|
- After diagnosis: fill `root_cause`, `artifacts`, `missing`, `debug_session`
|
|
109
113
|
- This section feeds directly into /gsd:plan-phase --gaps
|
|
114
|
+
- `status: resolved` (#1921, #4983): when `/gsd:verify-work`'s `reconcile_gaps`
|
|
115
|
+
step finds an executed gap-closure plan (a `*-PLAN.md` naming this entry's
|
|
116
|
+
`gap_id` with a matching `*-SUMMARY.md`), it updates the entry IN PLACE —
|
|
117
|
+
`status: failed` → `status: resolved` — and adds `resolved_by` (the
|
|
118
|
+
resolving plan's basename) and `resolved_at` (the date). A resolved entry
|
|
119
|
+
is no longer surfaced as an open gap, and its matching `### N.` test's
|
|
120
|
+
`result: issue` no longer blocks `phase uat-passed` — but only when
|
|
121
|
+
`resolved_by` names a plan that actually exists in the phase directory with
|
|
122
|
+
a matching SUMMARY; a hand-written `status: resolved` with no valid
|
|
123
|
+
`resolved_by` still blocks (src/uat-predicate.cts's `isTestGapResolved`).
|
|
124
|
+
A later regression against the same test gets a fresh `gap_id`, never a
|
|
125
|
+
reopened resolved one.
|
|
110
126
|
|
|
111
127
|
</section_rules>
|
|
112
128
|
|
|
@@ -41,18 +41,9 @@
|
|
|
41
41
|
"min_plans_for_parallel": 2
|
|
42
42
|
},
|
|
43
43
|
"gates": {
|
|
44
|
-
"confirm_project": true,
|
|
45
|
-
"confirm_phases": true,
|
|
46
|
-
"confirm_roadmap": true,
|
|
47
|
-
"confirm_breakdown": true,
|
|
48
|
-
"confirm_plan": true,
|
|
49
44
|
"execute_next_plan": true,
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
},
|
|
53
|
-
"safety": {
|
|
54
|
-
"always_confirm_destructive": true,
|
|
55
|
-
"always_confirm_external_services": true
|
|
45
|
+
"confirm_transition": true,
|
|
46
|
+
"confirm_milestone_scope": true
|
|
56
47
|
},
|
|
57
48
|
"hooks": {
|
|
58
49
|
"context_warnings": true
|
|
@@ -16,7 +16,7 @@ covered_files: # #4155 — see agents/gsd-verifier.md's "Create VERIFICATION.md"
|
|
|
16
16
|
- .planning/phases/XX-name/{phase_num}-{plan}-PLAN.md
|
|
17
17
|
- .planning/phases/XX-name/{phase_num}-{plan}-SUMMARY.md
|
|
18
18
|
- src/{changed-file}.cts
|
|
19
|
-
covered_digest: "
|
|
19
|
+
covered_digest: "v3:sha256:{digest from verification.fingerprint}"
|
|
20
20
|
behavior_unverified: 0 # Count of ⚠️ PRESENT_BEHAVIOR_UNVERIFIED truths (present + wired, behavior not exercised)
|
|
21
21
|
behavior_unverified_items: # Only if behavior_unverified > 0 — the truths above as structured items; emitted regardless of overall status
|
|
22
22
|
- truth: "Observable truth whose state transition or cancellation/cleanup/ordering invariant no test exercises"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; };
|
|
1
|
+
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; _gsd_id_ok() { case "$("$1" runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') return 0;; *) return 1;; esac; }; _gsd_homes() { set -- "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}" "$HOME/.gemini/antigravity-ide" "$HOME/.gemini/antigravity-cli" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}" "${CLINE_CONFIG_DIR:-$HOME/.cline}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}" "${CODEX_HOME:-$HOME/.codex}" "${COPILOT_CONFIG_DIR:-${COPILOT_HOME:-$HOME/.copilot}}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}" "${HERMES_HOME:-$HOME/.hermes}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}" "${KIMI_CONFIG_DIR:-$HOME/.config/agents}" "$HOME/.agents" "${KIMI_CODE_HOME:-$HOME/.kimi-code}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}" "${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}" "${TRAE_CONFIG_DIR:-$HOME/.trae}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}" "${ZCODE_CONFIG_DIR:-$HOME/.zcode}" "${GROK_AGENTS_HOME:-$HOME/.agents}"; for _h; do _gsd_at "$_h/gsd-core/bin/${_GSD_SHIM_NAME}" && return 0; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif _gsd_homes; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; [ -n "$_G" ] && _gsd_id_ok "$_G"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and no identity-proving gsd_run is on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; _gsd_id_ok gsd_run && GSD_IDENTITY_STATUS=ok; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
@@ -21,7 +21,7 @@ cat .planning/ROADMAP.md
|
|
|
21
21
|
## Step 2: Find next backlog number
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; };
|
|
24
|
+
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; _gsd_id_ok() { case "$("$1" runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') return 0;; *) return 1;; esac; }; _gsd_homes() { set -- "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}" "$HOME/.gemini/antigravity-ide" "$HOME/.gemini/antigravity-cli" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}" "${CLINE_CONFIG_DIR:-$HOME/.cline}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}" "${CODEX_HOME:-$HOME/.codex}" "${COPILOT_CONFIG_DIR:-${COPILOT_HOME:-$HOME/.copilot}}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}" "${HERMES_HOME:-$HOME/.hermes}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}" "${KIMI_CONFIG_DIR:-$HOME/.config/agents}" "$HOME/.agents" "${KIMI_CODE_HOME:-$HOME/.kimi-code}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}" "${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}" "${TRAE_CONFIG_DIR:-$HOME/.trae}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}" "${ZCODE_CONFIG_DIR:-$HOME/.zcode}" "${GROK_AGENTS_HOME:-$HOME/.agents}"; for _h; do _gsd_at "$_h/gsd-core/bin/${_GSD_SHIM_NAME}" && return 0; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif _gsd_homes; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; [ -n "$_G" ] && _gsd_id_ok "$_G"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and no identity-proving gsd_run is on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; _gsd_id_ok gsd_run && GSD_IDENTITY_STATUS=ok; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
25
25
|
NEXT=$(gsd_run query phase.next-decimal 999 --raw)
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -13,15 +13,15 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|
|
13
13
|
<step name="parse_arguments">
|
|
14
14
|
Parse the command arguments:
|
|
15
15
|
- All arguments become the phase description
|
|
16
|
-
- Example: `/gsd
|
|
17
|
-
- Example: `/gsd
|
|
16
|
+
- Example: `/gsd:phase "Add authentication"` → description = "Add authentication"
|
|
17
|
+
- Example: `/gsd:phase "Fix critical performance issues"` → description = "Fix critical performance issues"
|
|
18
18
|
|
|
19
19
|
If no arguments provided:
|
|
20
20
|
|
|
21
21
|
```
|
|
22
22
|
ERROR: Phase description required
|
|
23
|
-
Usage: /gsd
|
|
24
|
-
Example: /gsd
|
|
23
|
+
Usage: /gsd:phase "<description>"
|
|
24
|
+
Example: /gsd:phase "Add authentication system"
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Exit.
|
|
@@ -31,8 +31,9 @@ Exit.
|
|
|
31
31
|
Load phase operation context:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; };
|
|
35
|
-
|
|
34
|
+
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; _gsd_id_ok() { case "$("$1" runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') return 0;; *) return 1;; esac; }; _gsd_homes() { set -- "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}" "$HOME/.gemini/antigravity-ide" "$HOME/.gemini/antigravity-cli" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}" "${CLINE_CONFIG_DIR:-$HOME/.cline}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}" "${CODEX_HOME:-$HOME/.codex}" "${COPILOT_CONFIG_DIR:-${COPILOT_HOME:-$HOME/.copilot}}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}" "${HERMES_HOME:-$HOME/.hermes}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}" "${KIMI_CONFIG_DIR:-$HOME/.config/agents}" "$HOME/.agents" "${KIMI_CODE_HOME:-$HOME/.kimi-code}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}" "${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}" "${TRAE_CONFIG_DIR:-$HOME/.trae}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}" "${ZCODE_CONFIG_DIR:-$HOME/.zcode}" "${GROK_AGENTS_HOME:-$HOME/.agents}"; for _h; do _gsd_at "$_h/gsd-core/bin/${_GSD_SHIM_NAME}" && return 0; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif _gsd_homes; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; [ -n "$_G" ] && _gsd_id_ok "$_G"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and no identity-proving gsd_run is on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; _gsd_id_ok gsd_run && GSD_IDENTITY_STATUS=ok; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
35
|
+
GSD_WS=$(echo " $ARGUMENTS" | sed -nE 's/.* --ws +([A-Za-z0-9][A-Za-z0-9._-]*).*/--ws \1/p' | head -n 1)
|
|
36
|
+
INIT=$(gsd_run query init.phase-op ${GSD_WS:+--ws=${GSD_WS##* }} "0")
|
|
36
37
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
37
38
|
```
|
|
38
39
|
|
|
@@ -99,7 +100,7 @@ Roadmap updated: .planning/ROADMAP.md
|
|
|
99
100
|
---
|
|
100
101
|
|
|
101
102
|
**Also available:**
|
|
102
|
-
- `/gsd
|
|
103
|
+
- `/gsd:phase "<description>"` — add another phase
|
|
103
104
|
- Review roadmap
|
|
104
105
|
|
|
105
106
|
---
|