@opengsd/gsd-core 1.13.0 → 1.15.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/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-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +459 -0
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +360 -0
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +13 -8
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +8 -7
- package/agents/gsd-planner.md +12 -8
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-auditor.md +155 -17
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +10 -9
- package/bin/install.js +848 -163
- package/commands/gsd/autonomous.md +2 -2
- package/commands/gsd/capture.md +1 -1
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +8 -3
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +1 -0
- package/commands/gsd/plan-review-convergence.md +6 -6
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/progress.md +1 -1
- package/commands/gsd/quick-batch.md +1 -1
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/review.md +2 -3
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +672 -146
- package/gsd-core/bin/lib/adr-parser.cjs +4 -2
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +119 -34
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +96 -189
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-validator.cjs +14 -2
- package/gsd-core/bin/lib/check-command-router.cjs +213 -49
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
- package/gsd-core/bin/lib/commands.cjs +823 -112
- package/gsd-core/bin/lib/config-loader.cjs +66 -4
- package/gsd-core/bin/lib/config.cjs +186 -45
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +164 -45
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/frontmatter.cjs +13 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +614 -86
- package/gsd-core/bin/lib/install-engine.cjs +29 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
- package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +37 -13
- package/gsd-core/bin/lib/model-resolver.cjs +253 -53
- package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
- 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 +268 -27
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase.cjs +393 -88
- package/gsd-core/bin/lib/plan-document.cjs +49 -1
- package/gsd-core/bin/lib/planning-document.cjs +459 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
- package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
- package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch.cjs +1 -1
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
- package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
- package/gsd-core/bin/lib/roadmap.cjs +356 -42
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
- package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
- package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
- package/gsd-core/bin/lib/state-document.cjs +130 -28
- package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
- package/gsd-core/bin/lib/state-transition.cjs +181 -30
- package/gsd-core/bin/lib/state.cjs +265 -27
- package/gsd-core/bin/lib/surface.cjs +77 -3
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
- package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
- package/gsd-core/bin/lib/uat.cjs +9 -1
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/update-context.cjs +30 -24
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification.cjs +315 -30
- package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
- package/gsd-core/bin/lib/verify.cjs +320 -48
- package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
- package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- 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-phase-between-wave-reset.md +7 -6
- 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 +18 -0
- package/gsd-core/references/model-profiles.md +13 -4
- 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 +14 -2
- package/gsd-core/references/tdd.md +30 -4
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/worktree-path-safety.md +433 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +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 +1 -1
- package/gsd-core/workflows/add-tests.md +2 -2
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +11 -3
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- 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 +29 -16
- package/gsd-core/workflows/check-todos.md +6 -4
- package/gsd-core/workflows/cleanup.md +5 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
- package/gsd-core/workflows/code-review-fix.md +108 -22
- package/gsd-core/workflows/code-review.md +216 -73
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +41 -264
- package/gsd-core/workflows/debug.md +3 -3
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +17 -158
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +10 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +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/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -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/wave-post-gate-hooks.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +83 -172
- package/gsd-core/workflows/execute-plan.md +24 -10
- package/gsd-core/workflows/explore.md +3 -3
- package/gsd-core/workflows/extract-learnings.md +2 -1
- 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 +2 -2
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/help.md +1 -1
- package/gsd-core/workflows/import.md +2 -2
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +2 -2
- package/gsd-core/workflows/map-codebase.md +52 -5
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +56 -14
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- 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 +39 -209
- 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 +1 -1
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- 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 +45 -187
- 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 +132 -20
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +1 -1
- package/gsd-core/workflows/quick.md +29 -10
- package/gsd-core/workflows/reapply-patches.md +86 -6
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +31 -16
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -2
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +22 -9
- package/gsd-core/workflows/ship.md +3 -2
- 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 +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +44 -8
- package/gsd-core/workflows/ui-review.md +18 -4
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +14 -12
- package/gsd-core/workflows/validate-phase.md +3 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +101 -196
- package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
- package/hooks/dist/gsd-context-monitor.js +88 -15
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-secret-read-guard.js +71 -19
- package/hooks/dist/gsd-statusline.js +81 -20
- package/hooks/dist/gsd-validate-commit.sh +97 -8
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/gsd-agent-isolation-guard.js +66 -16
- package/hooks/gsd-context-monitor.js +88 -15
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-secret-read-guard.js +71 -19
- package/hooks/gsd-statusline.js +81 -20
- package/hooks/gsd-validate-commit.sh +97 -8
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/package.json +11 -6
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +15 -6
- package/scripts/check-contract-drift.cjs +131 -12
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +126 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/command-contract-helpers.cjs +3 -0
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +35 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +69 -0
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
- package/scripts/lib/ndjson-reporter.cjs +3 -2
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-id-drift.cjs +623 -13
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +9 -3
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +29 -9
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
- package/scripts/prompt-injection-scan.sh +18 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-autonomous/SKILL.md +2 -2
- package/skills/gsd-capture/SKILL.md +1 -1
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +8 -3
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -0
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-progress/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +1 -1
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review/SKILL.md +2 -3
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -1,7 +1,117 @@
|
|
|
1
1
|
# Worktree Path Safety
|
|
2
2
|
|
|
3
|
-
Guards for executor agents running inside Claude Code worktrees.
|
|
4
|
-
|
|
3
|
+
Guards for executor agents running inside Claude Code worktrees. The
|
|
4
|
+
supplied-root pin (step 0p) runs in EVERY mode; the remaining checks run before
|
|
5
|
+
any staging, Edit, or Write operation in worktree mode.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Supplied-root pin — step 0p (#4254, EVERY mode)
|
|
10
|
+
|
|
11
|
+
Sequential-mode dispatch (no `isolation="worktree"`) gives the executor no
|
|
12
|
+
spawn-time cwd guarantee, and the worktree-only guards below do not apply — so
|
|
13
|
+
a sequential executor whose process cwd resolved to a different checkout of
|
|
14
|
+
the same repo would self-derive that checkout as its root and commit there,
|
|
15
|
+
silently. Step 0p closes that hole by comparing the executor's actual root
|
|
16
|
+
against a root the ORCHESTRATOR already validated — never against anything the
|
|
17
|
+
executor derives itself.
|
|
18
|
+
|
|
19
|
+
**Runtime contract (executor):** if your prompt contains a `<project_root_pin>`
|
|
20
|
+
block, run its guard script verbatim before your first Edit/Write and again
|
|
21
|
+
before every commit, in the same cwd as that write or commit. On FATAL, halt
|
|
22
|
+
and report — recovery (moving commits between checkouts) is an
|
|
23
|
+
orchestrator/human decision, never agent self-repair. If your prompt contains
|
|
24
|
+
NO `<project_root_pin>` block (worktree/isolated dispatch, or a legacy
|
|
25
|
+
orchestrator), emit one warning line and continue with steps 0a/0b below — do
|
|
26
|
+
not fail closed on dispatches that never carried a pin. **Never bind
|
|
27
|
+
`{PINNED_ROOT}` yourself**: if this template reaches you unbound it is
|
|
28
|
+
reference prose, not your pin — only the orchestrator's build-time
|
|
29
|
+
substitution produces a valid guard.
|
|
30
|
+
|
|
31
|
+
**Composition contract (orchestrator — build time, NOT a sub-agent runtime
|
|
32
|
+
step):** copy the guard below into the dispatched prompt inside a
|
|
33
|
+
`<project_root_pin>` block, substituting `{PINNED_ROOT}` with the literal value
|
|
34
|
+
of `$ORCHESTRATOR_WT` captured at execute_waves entry, shell-single-quoted:
|
|
35
|
+
wrap the path in `'…'` and escape any embedded `'` as `'\''`. A path that
|
|
36
|
+
cannot be quoted this way must halt the phase (surface a blocker) rather than
|
|
37
|
+
ship a pin that could mis-parse. The comparison is git-vs-git on BOTH sides —
|
|
38
|
+
`git -C` resolves the pinned path to its repo's canonical toplevel in git's
|
|
39
|
+
own path representation, so symlink aliases, trailing slashes, `/var` vs
|
|
40
|
+
`/private/var` spellings, and Windows drive-letter forms — forward- or
|
|
41
|
+
backslash-separated, `RUNNER~1`-style short names included — compare equal by
|
|
42
|
+
construction (shell `pwd -P` normalization does NOT match git's emission on
|
|
43
|
+
Windows — do not re-introduce it).
|
|
44
|
+
|
|
45
|
+
Two portability rules baked into the guard below, learned from the #4254 CI
|
|
46
|
+
Windows legs: (1) a backslash comparator must be GENERATED at runtime
|
|
47
|
+
(`printf '\134'`), because a backslash written twice in the script text does
|
|
48
|
+
not survive the Windows command-line round-trip into bash — the doubled form
|
|
49
|
+
arrives halved, which silently rewrites any escape pattern that relies on it;
|
|
50
|
+
(2) every FATAL names its `Guard stage` and, where a git capture failed,
|
|
51
|
+
git's own stderr in a `Diagnostic` line, so a platform failure self-describes
|
|
52
|
+
instead of surfacing as a bare `Actual root: <none>`.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# gsd:guard=supplied-root-pin (#4254) — run before the first Edit/Write and before every commit.
|
|
56
|
+
PINNED_ROOT='{PINNED_ROOT}' # orchestrator build-time substitution — the only valid source of this value
|
|
57
|
+
PIN_STAGE=''
|
|
58
|
+
PIN_DIAG=''
|
|
59
|
+
gsd_pin_fail() {
|
|
60
|
+
echo "FATAL: executor root does not match the orchestrator-supplied PROJECT_ROOT pin (#4254)." >&2
|
|
61
|
+
echo " Pinned root: ${PINNED_ROOT:-<empty or unexpanded>}" >&2
|
|
62
|
+
echo " Actual root: ${ACTUAL_ROOT:-<none>}" >&2
|
|
63
|
+
echo " Guard stage: ${PIN_STAGE:-<unset>}" >&2
|
|
64
|
+
if [ -n "$PIN_DIAG" ]; then echo " Diagnostic: $PIN_DIAG" >&2; fi
|
|
65
|
+
echo " No writes or commits are permitted from this checkout. HALT and report; recovery is an" >&2
|
|
66
|
+
echo " orchestrator/human decision. Only the IMMEDIATE submodule of the pinned checkout is a" >&2
|
|
67
|
+
echo " legitimate other cwd — nested submodules must surface as a blocker, not self-route." >&2
|
|
68
|
+
exit 1
|
|
69
|
+
}
|
|
70
|
+
# Backslash comparator, generated at runtime: a backslash written twice in this
|
|
71
|
+
# script does not survive the Windows spawn path into bash (the command-line
|
|
72
|
+
# round-trip halves the doubled form), which rejected every C:\ pin at the form
|
|
73
|
+
# gate on the #4254 CI Windows legs. printf's octal escape is a lone backslash,
|
|
74
|
+
# which does survive; the quoted expansion below is literal in a case pattern.
|
|
75
|
+
BS=$(printf '\134')
|
|
76
|
+
# Fail closed if the comparator could not be generated: an empty BS would widen
|
|
77
|
+
# the drive-form arm below to drive-RELATIVE pins (C:foo) — the one fail-open
|
|
78
|
+
# seam in this construction, closed loudly rather than trusted to the shell.
|
|
79
|
+
if [ -z "$BS" ]; then
|
|
80
|
+
PIN_STAGE=form-gate
|
|
81
|
+
PIN_DIAG='backslash comparator generation failed (printf octal escape returned empty)'
|
|
82
|
+
gsd_pin_fail
|
|
83
|
+
fi
|
|
84
|
+
case "$PINNED_ROOT" in
|
|
85
|
+
''|'{PINNED_ROOT}') PIN_STAGE=pin-unbound; gsd_pin_fail ;; # empty or unexpanded pin — fail closed, never warn-and-proceed
|
|
86
|
+
/*) ;; # absolute POSIX form
|
|
87
|
+
[A-Za-z]:/*|[A-Za-z]:"$BS"*) ;; # Windows drive form, forward- or backslash-separated
|
|
88
|
+
*) PIN_STAGE=form-gate; gsd_pin_fail ;; # relative pin — never trustworthy across cwds
|
|
89
|
+
esac
|
|
90
|
+
ACTUAL_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
91
|
+
if [ -z "$ACTUAL_ROOT" ]; then
|
|
92
|
+
PIN_STAGE=actual-capture
|
|
93
|
+
PIN_DIAG="git rev-parse --show-toplevel from the cwd failed: $(git rev-parse --show-toplevel 2>&1 1>/dev/null)"
|
|
94
|
+
gsd_pin_fail
|
|
95
|
+
fi
|
|
96
|
+
PINNED_TL=$(git -C "$PINNED_ROOT" rev-parse --show-toplevel 2>/dev/null)
|
|
97
|
+
if [ -z "$PINNED_TL" ]; then
|
|
98
|
+
PIN_STAGE=pinned-capture
|
|
99
|
+
PIN_DIAG="git -C <pinned root> rev-parse --show-toplevel failed: $(git -C "$PINNED_ROOT" rev-parse --show-toplevel 2>&1 1>/dev/null)"
|
|
100
|
+
gsd_pin_fail
|
|
101
|
+
fi
|
|
102
|
+
if [ "$ACTUAL_ROOT" != "$PINNED_TL" ]; then
|
|
103
|
+
# Registered-submodule allowance: sub_repos plans legitimately commit inside an
|
|
104
|
+
# immediate submodule of the pinned checkout. The superproject working tree is
|
|
105
|
+
# git-emitted in the same representation as PINNED_TL, so the equality is
|
|
106
|
+
# representation-safe on every platform.
|
|
107
|
+
SUPER_TL=$(git rev-parse --show-superproject-working-tree 2>/dev/null)
|
|
108
|
+
if [ "$SUPER_TL" != "$PINNED_TL" ]; then
|
|
109
|
+
PIN_STAGE=root-mismatch
|
|
110
|
+
PIN_DIAG="actual=${ACTUAL_ROOT} pinned=${PINNED_TL} superproject=${SUPER_TL:-<none>}"
|
|
111
|
+
gsd_pin_fail
|
|
112
|
+
fi
|
|
113
|
+
fi
|
|
114
|
+
```
|
|
5
115
|
|
|
6
116
|
---
|
|
7
117
|
|
|
@@ -65,3 +175,324 @@ fi
|
|
|
65
175
|
**Prefer relative paths** for all Edit/Write operations. When an absolute path is
|
|
66
176
|
unavoidable, always derive it from `git rev-parse --show-toplevel` run inside the
|
|
67
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.
|
|
@@ -21,8 +21,14 @@ These files live directly at `.planning/` — not inside phase subdirectories.
|
|
|
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 |
|
|
24
|
-
| `CLAUDE.md` |
|
|
24
|
+
| `CLAUDE.md` | *(inline)* | `/gsd-profile` | Auto-assembled Claude Code context file |
|
|
25
25
|
| `RETROSPECTIVE.md` | *(inline)* | `/gsd:complete-milestone` | Living milestone retrospective updated at each milestone close |
|
|
26
|
+
| `WINDOWS.md` | *(none)* | broken-windows ledger (`src/broken-windows.cts`) | Tracked known-broken items pending resolution (#3224) |
|
|
27
|
+
| `STATE-ARCHIVE.md` | *(none)* | `state.cts`'s `cmdStatePrune` | Pruned historical STATE.md entries |
|
|
28
|
+
| `milestone.lock` | *(none)* | `src/milestone-lock.cts` | Persistent milestone (phase + session) claim, unlike the transient `STATE.md.lock`/`WAITING.json` (#3311) |
|
|
29
|
+
| `state.json` | *(none)* | `src/state-contract.cts` | Machine-readable state contract published at step boundaries (#3227) |
|
|
30
|
+
| `skill-manifest.json` | *(none)* | `init.cts`'s `cmdSkillManifest --write` | Project-scoped skill manifest (#3964) |
|
|
31
|
+
| `PATTERNS.md` | *(inline)* | `/gsd:extract-learnings` (graduation, `workflows/graduation.md`, `patterns` target) | Graduated cross-phase patterns -- distinct from the per-phase `NN-PATTERNS.md` below (#4282) |
|
|
26
32
|
|
|
27
33
|
### Version-stamped artifacts (pattern: `vX.Y-*.md`)
|
|
28
34
|
|
|
@@ -172,9 +172,12 @@ Updated after each plan completion.
|
|
|
172
172
|
**Decisions:** Reference to PROJECT.md Key Decisions table, plus recent decisions summary for quick access. Full decision log lives in PROJECT.md.
|
|
173
173
|
|
|
174
174
|
**Pending Todos:** Ideas captured via /gsd-add-todo
|
|
175
|
-
-
|
|
176
|
-
|
|
177
|
-
-
|
|
175
|
+
- One bullet per pending todo, rendered by `init.todos`'s `pending_todos_markdown`
|
|
176
|
+
(each bullet capped at 240 characters: `- [date] [area] title — [todo file](path) — Needs ...`;
|
|
177
|
+
the todo-file link is repo-relative, so the cap does not depend on checkout path length)
|
|
178
|
+
- `None yet.` when there are no pending todos
|
|
179
|
+
- No collapse-by-count fallback — every pending todo gets its own line, always
|
|
180
|
+
(see #2618 design doc for why a "count if many" fallback was rejected)
|
|
178
181
|
|
|
179
182
|
**Blockers/Concerns:** From "Next Phase Readiness" sections
|
|
180
183
|
- Issues that affect future work
|