@opengsd/gsd-core 1.5.0 → 1.6.0-rc.2
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/plugin.json +1 -1
- package/agents/gsd-plan-checker.md +34 -0
- package/agents/gsd-planner.md +2 -0
- package/agents/gsd-roadmapper.md +6 -0
- package/bin/install.js +199 -365
- package/commands/gsd/capture.md +5 -1
- package/gemini-extension.json +1 -1
- package/gsd-core/bin/gsd-tools.cjs +695 -5
- package/gsd-core/bin/lib/adr-parser.cjs +45 -23
- package/gsd-core/bin/lib/audit.cjs +2 -2
- package/gsd-core/bin/lib/capability-consent.cjs +763 -0
- package/gsd-core/bin/lib/capability-ledger.cjs +831 -0
- package/gsd-core/bin/lib/capability-lifecycle.cjs +1551 -0
- package/gsd-core/bin/lib/capability-loader.cjs +764 -0
- package/gsd-core/bin/lib/capability-lock.cjs +553 -0
- package/gsd-core/bin/lib/capability-registry.cjs +198 -4
- package/gsd-core/bin/lib/capability-source.cjs +1242 -0
- package/gsd-core/bin/lib/capability-state.cjs +9 -6
- package/gsd-core/bin/lib/capability-trust.cjs +550 -0
- package/gsd-core/bin/lib/capability-validator.cjs +2066 -0
- package/gsd-core/bin/lib/capability-writer.cjs +14 -5
- package/gsd-core/bin/lib/check-command-router.cjs +69 -18
- package/gsd-core/bin/lib/command-aliases.cjs +8 -0
- package/gsd-core/bin/lib/commands.cjs +247 -0
- package/gsd-core/bin/lib/config-loader.cjs +98 -84
- package/gsd-core/bin/lib/config-schema.cjs +26 -7
- package/gsd-core/bin/lib/config.cjs +7 -1
- package/gsd-core/bin/lib/decisions.cjs +149 -60
- package/gsd-core/bin/lib/frontmatter.cjs +7 -3
- package/gsd-core/bin/lib/gap-checker.cjs +126 -11
- package/gsd-core/bin/lib/init.cjs +91 -22
- package/gsd-core/bin/lib/legacy-cleanup.cjs +96 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +26 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +471 -0
- package/gsd-core/bin/lib/milestone.cjs +41 -2
- package/gsd-core/bin/lib/phase-command-router.cjs +5 -0
- package/gsd-core/bin/lib/phase-id.cjs +25 -11
- package/gsd-core/bin/lib/phase-lifecycle.cjs +14 -5
- package/gsd-core/bin/lib/phase.cjs +33 -4
- package/gsd-core/bin/lib/probe-core.cjs +7 -0
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +59 -26
- package/gsd-core/bin/lib/project-root.cjs +89 -2
- package/gsd-core/bin/lib/resolution.cjs +26 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +16 -3
- package/gsd-core/bin/lib/roadmap-parser.cjs +73 -106
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +47 -17
- package/gsd-core/bin/lib/roadmap.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +423 -3
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +77 -0
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +1 -28
- package/gsd-core/bin/lib/runtime-homes.cjs +53 -1
- package/gsd-core/bin/lib/runtime-name-policy.cjs +44 -0
- package/gsd-core/bin/lib/semver-compare.cjs +127 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +55 -1
- package/gsd-core/bin/lib/state-document.cjs +4 -2
- package/gsd-core/bin/lib/state.cjs +317 -161
- package/gsd-core/bin/lib/surface.cjs +12 -19
- package/gsd-core/bin/lib/uat-predicate.cjs +7 -47
- package/gsd-core/bin/lib/uat.cjs +39 -26
- package/gsd-core/bin/lib/validate.cjs +5 -2
- package/gsd-core/bin/lib/verify.cjs +40 -15
- package/gsd-core/bin/lib/worktree-safety.cjs +202 -0
- package/gsd-core/bin/shared/config-defaults.manifest.json +6 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -1
- package/gsd-core/references/context-budget.md +8 -8
- package/gsd-core/references/execute-phase-between-wave-reset.md +43 -0
- package/gsd-core/references/execute-phase-context-guard.md +16 -0
- package/gsd-core/references/execute-phase-wave-guard.md +33 -0
- package/gsd-core/references/planner-antipatterns.md +48 -0
- package/gsd-core/references/planning-config.md +4 -0
- package/gsd-core/references/prohibition-probe.md +15 -9
- package/gsd-core/references/scout-codebase.md +2 -2
- package/gsd-core/workflows/autonomous.md +33 -33
- package/gsd-core/workflows/diagnose-issues.md +6 -1
- package/gsd-core/workflows/discuss-phase/templates/context.md +1 -1
- package/gsd-core/workflows/discuss-phase.md +1 -2
- package/gsd-core/workflows/execute-phase.md +12 -12
- package/gsd-core/workflows/help/modes/full.md +10 -0
- package/gsd-core/workflows/list-seeds.md +63 -0
- package/gsd-core/workflows/manager.md +37 -37
- package/gsd-core/workflows/pr-branch.md +156 -0
- package/gsd-core/workflows/quick.md +6 -1
- package/gsd-core/workflows/review.md +10 -2
- package/gsd-core/workflows/spec-phase.md +8 -3
- package/gsd-core/workflows/verify-phase.md +2 -2
- package/package.json +6 -3
- package/scripts/gen-capability-matrix.cjs +284 -0
- package/scripts/gen-capability-registry.cjs +96 -1853
- package/scripts/lint-regression-test-names.allowlist.json +1 -0
- package/scripts/lint-resolution-provenance.allowlist.json +1 -0
- package/scripts/lint-resolution-provenance.cjs +192 -0
- package/scripts/lint-test-file-count.allowlist.json +9 -0
- package/scripts/prompt-injection-scan.sh +1 -0
- package/scripts/run-tests.cjs +14 -0
- package/scripts/sync-manifest-versions.cjs +77 -5
|
@@ -29,14 +29,14 @@ Every workflow that spawns agents or reads significant content must follow these
|
|
|
29
29
|
|
|
30
30
|
## Context Degradation Tiers
|
|
31
31
|
|
|
32
|
-
Monitor context usage and adjust behavior accordingly:
|
|
33
|
-
|
|
34
|
-
| Tier | Usage | Behavior |
|
|
35
|
-
|
|
36
|
-
| PEAK | 0-30% | Full operations. Read bodies, spawn multiple agents, inline results. |
|
|
37
|
-
| GOOD | 30-50% | Normal operations. Prefer frontmatter reads, delegate aggressively. |
|
|
38
|
-
| DEGRADING | 50-70% | Economize. Frontmatter-only reads, minimal inlining, warn user about budget. |
|
|
39
|
-
| POOR | 70%+ | Emergency mode. Checkpoint progress immediately. No new reads unless critical. |
|
|
32
|
+
Monitor context usage and adjust behavior accordingly. The `workflow.context_guard_mode` config key (values: `auto`, `warn`, `off`; default `warn`) controls how `execute-phase.md` responds when the guard fires at a wave boundary.
|
|
33
|
+
|
|
34
|
+
| Tier | Usage | Behavior | Trigger Action (execute-phase) |
|
|
35
|
+
|------|-------|----------|-------------------------------|
|
|
36
|
+
| PEAK | 0-30% | Full operations. Read bodies, spawn multiple agents, inline results. | None |
|
|
37
|
+
| GOOD | 30-50% | Normal operations. Prefer frontmatter reads, delegate aggressively. | None |
|
|
38
|
+
| DEGRADING | 50-70% | Economize. Frontmatter-only reads, minimal inlining, warn user about budget. | Emit warning, continue |
|
|
39
|
+
| POOR | 70%+ | Emergency mode. Checkpoint progress immediately. No new reads unless critical. | `warn`: emit warning + recommend `/gsd:pause-work`. `auto`: invoke pause-work before next wave. `off`: proceed anyway. |
|
|
40
40
|
|
|
41
41
|
## Context Degradation Warning Signs
|
|
42
42
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
7b. **Pre-wave dependency check (waves 2+ only):**
|
|
2
|
+
Before wave N+1, run `gsd-tools.cjs query verify.key-links {phase_dir}/{plan}-PLAN.md` for each upcoming plan.
|
|
3
|
+
If any PRIOR-wave artifact link fails, present:
|
|
4
|
+
- `## Cross-Plan Wiring Gap` with plan/link/from/pattern rows
|
|
5
|
+
- Options: investigate+fix before continue, or continue with cascade risk
|
|
6
|
+
Skip key-links that reference files in the CURRENT (upcoming) wave.
|
|
7
|
+
|
|
8
|
+
7c. **Between-wave manifest reset and worktree base refresh (waves 2+ only — #1369):**
|
|
9
|
+
|
|
10
|
+
**REQUIRED before each wave transition when `USE_WORKTREES != "false"` and `RUNTIME = "claude"`.**
|
|
11
|
+
|
|
12
|
+
Wave N's `WAVE_WORKTREE_MANIFEST` was consumed by `worktree.cleanup-wave` in step 5.5. It must be
|
|
13
|
+
unset so wave N+1's step 3 creates a fresh manifest for the new wave's worktrees. Without this,
|
|
14
|
+
the wave N+1 manifest guard (step 5.5, #3384) blocks on the stale/empty consumed file.
|
|
15
|
+
|
|
16
|
+
After wave N merges and tracking commits, the orchestrator HEAD has advanced past the commit the
|
|
17
|
+
Claude Code harness may have cached as the worktree fork base at session start. New worktrees
|
|
18
|
+
spawned for wave N+1 could fork from the stale pre-wave-N HEAD, causing every executor to trip the
|
|
19
|
+
`worktree_branch_check` FATAL guard immediately (symptom: `HEAD is <old-sha>, expected <new-sha>`).
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Unset per-wave manifest so wave N+1 creates a fresh one (#3384, #1369).
|
|
23
|
+
unset WAVE_WORKTREE_MANIFEST
|
|
24
|
+
|
|
25
|
+
# Between-wave base refresh (#1369): after wave N merges and tracking commits, HEAD has
|
|
26
|
+
# advanced. Re-assert worktree.baseRef:"head" (idempotent — no-op if already set) so the
|
|
27
|
+
# Claude Code harness re-reads the live HEAD on the next Agent(isolation="worktree") call
|
|
28
|
+
# rather than using a cached session-start commit as the fork base.
|
|
29
|
+
if [ "$RUNTIME" = "claude" ] && [ "$USE_WORKTREES" != "false" ]; then
|
|
30
|
+
gsd_run query worktree.set-baseref 2>/dev/null || true
|
|
31
|
+
|
|
32
|
+
# Safety re-check: evaluate degradation AFTER the wave N commits. If HEAD has diverged
|
|
33
|
+
# from origin/HEAD and baseRef is NOT "head", degrade remaining waves to sequential to
|
|
34
|
+
# avoid the base-mismatch FATAL in executor agents.
|
|
35
|
+
_BETWEEN_DEGRADE=$(gsd_run query worktree.base-check --pick shouldDegrade 2>/dev/null || echo "false")
|
|
36
|
+
if [ "$_BETWEEN_DEGRADE" = "true" ]; then
|
|
37
|
+
_DEGRADE_MSG=$(gsd_run query worktree.base-check --pick message 2>/dev/null || true)
|
|
38
|
+
[ -n "$_DEGRADE_MSG" ] && printf '%s\n' "$_DEGRADE_MSG" >&2
|
|
39
|
+
printf 'Degrading to sequential mode for remaining waves: HEAD advanced past worktree fork base after wave %s merge (#1369).\n' "${N}" >&2
|
|
40
|
+
USE_WORKTREES=false
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
0. **Context exhaustion guard — `context_guard` (BEFORE spawning, #1452):**
|
|
2
|
+
|
|
3
|
+
Before spawning any agents for this wave, self-assess context pressure using the
|
|
4
|
+
degradation signals in `references/context-budget.md`. Signs of POOR tier (70%+):
|
|
5
|
+
increasing vagueness, skipped steps, silent partial completion.
|
|
6
|
+
|
|
7
|
+
Read `workflow.context_guard_mode` from `.planning/config.json` (default `warn`).
|
|
8
|
+
|
|
9
|
+
| Tier | `warn` (default) | `auto` | `off` |
|
|
10
|
+
|------|-----------------|--------|-------|
|
|
11
|
+
| PEAK / GOOD | No output | No output | No output |
|
|
12
|
+
| DEGRADING (50-70%) | Emit: "⚠ Context pressure DEGRADING — switching to frontmatter-only reads for remaining waves." Continue. | Same as warn | Skip |
|
|
13
|
+
| POOR (70%+) | Emit: "🛑 Context pressure POOR — risk of context exhaustion. Run `/gsd:pause-work` to checkpoint before this wave, then resume in a fresh session." Continue (user decides). | Invoke `/gsd:pause-work` immediately and halt. Do NOT spawn wave agents. | Skip |
|
|
14
|
+
|
|
15
|
+
The guard is heuristic — no programmatic context-percentage API exists. Use your
|
|
16
|
+
assessment of degradation signals, not a fixed token count.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
0.5. **Inter-wave worktree base re-check (wave N+1 guard — #1369):**
|
|
2
|
+
|
|
3
|
+
After Wave N merges and tracking commits advance orchestrator HEAD, Claude Code's
|
|
4
|
+
`isolation="worktree"` still forks new worktrees from `origin/HEAD` (the "fresh" base),
|
|
5
|
+
not the live HEAD. This means Wave N+1 worktrees would be created from the stale
|
|
6
|
+
pre-Wave-N base, causing the `worktree_branch_check` guard inside each executor to halt
|
|
7
|
+
immediately with a base-mismatch fatal.
|
|
8
|
+
|
|
9
|
+
**Run this check at the start of every wave when `USE_WORKTREES != "false"` and
|
|
10
|
+
`RUNTIME = "claude"`**, including Wave 1 (where it mirrors the initialize-step check):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
if [ "$RUNTIME" = "claude" ] && [ "${USE_WORKTREES:-true}" != "false" ]; then
|
|
14
|
+
_WAVE_DEGRADE=$(gsd_run query worktree.base-check --pick shouldDegrade 2>/dev/null || true)
|
|
15
|
+
if [ "$_WAVE_DEGRADE" = "true" ]; then
|
|
16
|
+
_WAVE_DEGRADE_MSG=$(gsd_run query worktree.base-check --pick message 2>/dev/null || true)
|
|
17
|
+
[ -n "$_WAVE_DEGRADE_MSG" ] && printf '%s\n' "$_WAVE_DEGRADE_MSG" >&2
|
|
18
|
+
echo "⚠ [#1369] Worktree fork base diverged from orchestrator HEAD (wave merges advanced HEAD past origin/HEAD). Auto-degrading to sequential mode for this wave to avoid base-mismatch halts." >&2
|
|
19
|
+
USE_WORKTREES=false
|
|
20
|
+
fi
|
|
21
|
+
fi
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If `shouldDegrade` is `true`, override `USE_WORKTREES=false` for **this wave only** —
|
|
25
|
+
all plans in this wave execute sequentially on the main working tree. Later waves re-run
|
|
26
|
+
this check and may re-enable worktree isolation if `origin/HEAD` is updated (e.g. via
|
|
27
|
+
`git fetch` or `worktree.baseRef:"head"` config).
|
|
28
|
+
|
|
29
|
+
**To avoid this degrade across all waves:** set `worktree.baseRef:"head"` in
|
|
30
|
+
`.claude/settings.local.json` (or run `gsd-tools worktree set-baseref`). This tells
|
|
31
|
+
Claude Code to fork from the live HEAD instead of `origin/HEAD`, so each wave's new
|
|
32
|
+
worktrees always start from the correct post-merge base. See #683 for the base-ref
|
|
33
|
+
configuration detail.
|
|
@@ -174,3 +174,51 @@ If region-scoping is genuinely impractical and the file split is intentional, su
|
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
One marker per pattern. The marker exempts only the exact pattern it names. Prefer region-scoping over suppression.
|
|
177
|
+
|
|
178
|
+
## CLI Output Format Anchor Mismatch (#1478)
|
|
179
|
+
|
|
180
|
+
`pnpm ls vite | grep -E '^vite@7\.'` looks correct but silently fails. `pnpm ls` uses tree characters as line prefixes:
|
|
181
|
+
```
|
|
182
|
+
my-project@1.0.0
|
|
183
|
+
└── vite@7.3.5
|
|
184
|
+
```
|
|
185
|
+
Lines begin with `└──`, not `vite`. The `^` anchor matches line start, which is a tree character — the grep finds nothing.
|
|
186
|
+
|
|
187
|
+
**Bad:** `pnpm ls vite | grep -E '^vite@7\.'`
|
|
188
|
+
**Good:** `pnpm ls vite | grep -E 'vite@7\.'`
|
|
189
|
+
**Good (strict):** `pnpm ls vite | grep -E '(└|├)── vite@7\.'`
|
|
190
|
+
|
|
191
|
+
Same trap: `npm ls`, `yarn list`, `docker ps` column output, `kubectl get` table output.
|
|
192
|
+
|
|
193
|
+
## Fabricated Numeric Baselines (#1478)
|
|
194
|
+
|
|
195
|
+
Never emit `grep '714 tests'` or `grep '52 test files'` unless you ran the count command in this session. Model-recalled counts are stale from training.
|
|
196
|
+
|
|
197
|
+
**Bad:** `npm test 2>&1 | grep '714 passed'`
|
|
198
|
+
**Good:** `npm test 2>&1 | grep -E '[0-9]+ passed'` or just `npm test`
|
|
199
|
+
|
|
200
|
+
## Error-Suppressing Fallbacks in Verify Gates (#1479)
|
|
201
|
+
|
|
202
|
+
`2>/dev/null || echo "0"` in an assignment that feeds a comparison converts any failure into a passing gate that measures nothing.
|
|
203
|
+
|
|
204
|
+
**Bad — both sides default to "0" when files are missing:**
|
|
205
|
+
```bash
|
|
206
|
+
EN_KEYS=$(jq 'keys | length' i18n/en.json 2>/dev/null || echo "0")
|
|
207
|
+
DE_KEYS=$(jq 'keys | length' i18n/de.json 2>/dev/null || echo "0")
|
|
208
|
+
[ "$EN_KEYS" = "$DE_KEYS" ] && echo "ok"
|
|
209
|
+
```
|
|
210
|
+
If files don't exist (wrong path, etc.), both sides become `"0"`. Comparison passes. Gate certifies parity while measuring nothing.
|
|
211
|
+
|
|
212
|
+
**Good — let failure propagate:**
|
|
213
|
+
```bash
|
|
214
|
+
EN_KEYS=$(jq 'keys | length' src/i18n/en.json)
|
|
215
|
+
DE_KEYS=$(jq 'keys | length' src/i18n/de.json)
|
|
216
|
+
[ "$EN_KEYS" = "$DE_KEYS" ] && echo "ok"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Good — explicit guard:**
|
|
220
|
+
```bash
|
|
221
|
+
test -f src/i18n/en.json && test -f src/i18n/de.json || { echo "missing input files"; exit 1; }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**When `|| echo "default"` is acceptable:** only when absence is semantically the default AND the result is NOT used in a comparison that should detect absence.
|
|
@@ -266,6 +266,10 @@ Set via `workflow.*` namespace in config.json (e.g., `"workflow": { "research":
|
|
|
266
266
|
| `workflow.subagent_timeout` | number | `300000` | Any positive integer (ms) | Timeout for parallel subagent tasks (default: 5 minutes) |
|
|
267
267
|
| `workflow.test_command` | string\|null | `null` | Any shell command | Regression/test gate command run by verify-phase, execute-phase, audit-fix, and post-merge-gate. Unset → GSD auto-detects (Makefile / package.json / Cargo.toml / go.mod / pyproject.toml). |
|
|
268
268
|
| `workflow.build_command` | string\|null | `null` | Any shell command | Build gate command run by the post-merge gate. Unset → build step auto-detected/skipped. |
|
|
269
|
+
| `workflow.mvp_mode` | boolean | `false` | `true`, `false` | Persist the MVP-mode flag in config so every phase defaults to MVP framing without requiring `--mvp` on the CLI. Resolved via the chain: `--mvp` CLI flag → ROADMAP.md `**Mode:** mvp` field → this config value → `false`. When `true`, the planner, executor, verifier, and discovery surfaces (progress, stats, graphify) all treat the phase as an MVP vertical slice (UI → API → DB) of one user-visible capability. |
|
|
270
|
+
| `workflow.context_guard_mode` | string | `"warn"` | `"auto"`, `"warn"`, `"off"` | Context exhaustion guard mode for `execute-phase`. Before each wave, the orchestrator self-assesses context pressure using degradation signals from `context-budget.md`. `"warn"` (default): emit a warning and recommend `/gsd:pause-work` when POOR tier is detected. `"auto"`: automatically invoke `/gsd:pause-work` before the next wave when POOR tier is detected. `"off"`: disable the guard. The guard is heuristic — no programmatic context-% API exists. |
|
|
271
|
+
| `workflow.plan_chunked` | boolean | `false` | `true`, `false` | Enable chunked planning mode. When `true`, the plan-phase orchestrator splits the single long-lived planner Task into a short outline Task followed by N short per-plan Tasks (~3–5 min each). Each plan is committed individually for crash resilience. Particularly useful on Windows where long-lived Tasks may hang on stdio. Also activated by the `--chunked` flag. |
|
|
272
|
+
| `workflow.code_review_command` | string\|null | `null` | Any shell command | External code-review command integrated into `/gsd:ship`. The diff is piped to the command via stdin; the command must output JSON with a `verdict` field (`"APPROVED"` or `"REVISE"`). Non-zero exit or `"REVISE"` verdict blocks the ship workflow. When unset, the built-in review flow runs. Example: `my-review-tool --review`. |
|
|
269
273
|
| `workflow.inline_plan_threshold` | number | `2` | `0`–`10` | Plans with ≤N tasks execute inline instead of spawning a subagent |
|
|
270
274
|
| `workflow.code_review` | boolean | `true` | `true`, `false` | Enable built-in code review step in the ship workflow |
|
|
271
275
|
| `workflow.code_review_depth` | string | `"standard"` | `"light"`, `"standard"`, `"deep"` | Depth level for code review analysis in the ship workflow |
|
|
@@ -157,7 +157,7 @@ A `resolved`/`test`-tier prohibition MAY carry an **optional `check` descriptor*
|
|
|
157
157
|
the wired mechanical check, so verify-phase locates it deterministically instead of inventing
|
|
158
158
|
`{kind, target, rule}` each run. The descriptor is captured at spec-phase (soft / optional —
|
|
159
159
|
the author wires it when the negative test or lint rule already exists) and is represented as
|
|
160
|
-
**
|
|
160
|
+
**five flat scalar keys** on the `must_haves.prohibitions` item — never a nested `check: {}`
|
|
161
161
|
object:
|
|
162
162
|
|
|
163
163
|
- `check_kind` — `node-test` | `lint-rule` (which producer mechanism runs the check).
|
|
@@ -165,16 +165,19 @@ object:
|
|
|
165
165
|
- `check_rule` — the `ruleId` to filter on, **lint-rule only** (absent for `node-test`).
|
|
166
166
|
- `check_violation_fixture` — path to a KNOWN-BAD subject the #1279 prover runs the check against to
|
|
167
167
|
machine-prove fail-first (rides BOTH kinds; for `node-test` it is injected via `GSD_PROHIB_SUBJECT`).
|
|
168
|
+
- `check_clean_fixture` — **optional** path to a KNOWN-CLEAN control subject (#1346). When present the
|
|
169
|
+
node-test prover also runs the check against it and requires GREEN, proving the violation's RED is
|
|
170
|
+
caused by the subject's *content* (not merely by `GSD_PROHIB_SUBJECT` being set). Absent → no control.
|
|
168
171
|
|
|
169
172
|
The flat-scalar shape is load-bearing: the shared `parseMustHavesBlock` is a flat parser and a
|
|
170
173
|
nested object would flatten/mangle the round-trip (ADR-550 2026-06-15 addendum; #644 "no parser
|
|
171
174
|
rewrite" precedent). `projectProhibitions` emits these keys **only for a well-formed descriptor**
|
|
172
175
|
(valid `check_kind` + non-empty `check_target`; `check_rule` only on the lint-rule path;
|
|
173
|
-
`check_violation_fixture` only when non-empty), and verify-phase reads them
|
|
174
|
-
`descriptorFromProjection` into the `CheckDescriptor` handed to `check prohibition-enforcement`.
|
|
175
|
-
closes
|
|
176
|
-
descriptor authoring**: a prohibition authored with
|
|
177
|
-
projection alone.
|
|
176
|
+
`check_violation_fixture` and `check_clean_fixture` only when non-empty), and verify-phase reads them
|
|
177
|
+
back via `descriptorFromProjection` into the `CheckDescriptor` handed to `check prohibition-enforcement`.
|
|
178
|
+
This closes the locate (#1278), the machine-proof-fixture (#1279), and the causation-control (#1346)
|
|
179
|
+
halves with **zero manual descriptor authoring**: a prohibition authored with the scalars greens
|
|
180
|
+
end-to-end through the projection alone.
|
|
178
181
|
|
|
179
182
|
**Fail-closed + backward-compat.** A partial descriptor (`lint-rule` missing `check_rule`), an
|
|
180
183
|
unknown `check_kind`, an **absent** descriptor, OR a descriptor with **no `check_violation_fixture`**
|
|
@@ -182,8 +185,11 @@ falls through to the producer's fail-closed paths (`located: false`, or located-
|
|
|
182
185
|
never a silent green. A prohibition with no descriptor parses and disposes byte-identically to today.
|
|
183
186
|
`failFirst` is **not** sourced from the descriptor and is **demoted** (machine-proven fail-first
|
|
184
187
|
DELIVERED in #1279 — no path greens on attestation alone, FF-08); the `dispositionForProhibition`
|
|
185
|
-
policy is unchanged.
|
|
186
|
-
|
|
188
|
+
policy is unchanged. Causation (**#1346**): the node-test proof confirms the fixture exists and the
|
|
189
|
+
check goes RED; supplying `check_clean_fixture` adds an opt-in control that *also* requires GREEN on a
|
|
190
|
+
known-clean subject, proving the red is content-caused. With no clean fixture the control cannot run,
|
|
191
|
+
so that one residual case (a deceptive test reding merely because the env var is set) stays a
|
|
192
|
+
documented constraint — an author opts into the stronger proof by wiring a clean control subject.
|
|
187
193
|
|
|
188
194
|
## Output schema
|
|
189
195
|
|
|
@@ -191,7 +197,7 @@ The probe emits, per kept prohibition, an item of the form:
|
|
|
191
197
|
|
|
192
198
|
```
|
|
193
199
|
{ requirement_id, category, status, verification, resolution, reason, statement,
|
|
194
|
-
check_kind?, check_target?, check_rule? }
|
|
200
|
+
check_kind?, check_target?, check_rule?, check_violation_fixture?, check_clean_fixture? }
|
|
195
201
|
```
|
|
196
202
|
|
|
197
203
|
where `statement` is the must-NOT sentence and `category` is the values/safety/ethics class
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Codebase scout — map selection table
|
|
2
2
|
|
|
3
3
|
> Lazy-loaded reference for the `scout_codebase` step in
|
|
4
|
-
> `workflows/discuss-phase.md` (extracted via
|
|
5
|
-
>
|
|
4
|
+
> `workflows/discuss-phase.md` (extracted via the discuss-phase/modes progressive-disclosure split, #717).
|
|
5
|
+
> Read this only when prior `.planning/codebase/*.md` maps exist
|
|
6
6
|
> and the workflow needs to pick which 2–3 to load.
|
|
7
7
|
|
|
8
8
|
## Phase-type → recommended maps
|
|
@@ -61,7 +61,7 @@ fi
|
|
|
61
61
|
|
|
62
62
|
When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
|
|
63
63
|
|
|
64
|
-
When `--interactive` is set, discuss runs inline with questions (not auto-answered). On
|
|
64
|
+
When `--interactive` is set, discuss runs inline with questions (not auto-answered). On Codex, where a backgrounded agent can still spawn subagents, plan and execute are dispatched as background agents — keeping the main context lean (only discuss conversations accumulate) and enabling overlap. On every other runtime (Claude Code and all other non-Codex runtimes), backgrounded agents cannot reliably nest subagents, so plan and execute run inline to preserve worktree isolation and independent verification, and phases run sequentially with their work accumulating in the main context. Either way, user input is preserved on all design decisions.
|
|
65
65
|
|
|
66
66
|
When `PLAN_STRATEGY=converge`, the planning step MUST invoke the plan-review convergence workflow instead of `gsd-plan-phase`. `--cross-ai` is an alias for `--converge`. Forward `CONVERGENCE_ARGS` exactly as parsed so reviewer flags and `--max-cycles N` retain the same meaning as they have on `/gsd:plan-review-convergence`.
|
|
67
67
|
|
|
@@ -111,7 +111,7 @@ Display startup banner:
|
|
|
111
111
|
If `ONLY_PHASE` is set, display: `Single phase mode: Phase ${ONLY_PHASE}`
|
|
112
112
|
Else if `FROM_PHASE` is set, display: `Starting from phase ${FROM_PHASE}`
|
|
113
113
|
If `TO_PHASE` is set, display: `Stopping after phase ${TO_PHASE}`
|
|
114
|
-
If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execute
|
|
114
|
+
If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execute inline — background on Codex only)`
|
|
115
115
|
If `PLAN_STRATEGY` is `converge`, display: `Planning: Plan-review convergence enabled`
|
|
116
116
|
|
|
117
117
|
</step>
|
|
@@ -357,27 +357,13 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
|
357
357
|
|
|
358
358
|
**3b. Plan**
|
|
359
359
|
|
|
360
|
-
**If `INTERACTIVE` is set:** Background dispatch is only safe where a backgrounded agent can still
|
|
360
|
+
**If `INTERACTIVE` is set:** Background dispatch is only safe on a runtime where a backgrounded agent can still nest the pipeline's subagents (plan-checker / worktree executors / verifier). Among supported runtimes only **Codex** (`spawn_agent`) can do this; Claude Code's backgrounded agents have no `Agent`/`Task` tool, and every other runtime either prohibits nested subagents or disables them by default. So run **inline** everywhere except Codex, which is dispatched in the background. Resolve the runtime first:
|
|
361
361
|
|
|
362
362
|
```bash
|
|
363
|
-
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
|
|
363
|
+
RUNTIME=$(gsd_run query config-get runtime --default claude --raw 2>/dev/null || echo "claude")
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
-
- **
|
|
367
|
-
|
|
368
|
-
- If `PLAN_STRATEGY=converge`:
|
|
369
|
-
|
|
370
|
-
```
|
|
371
|
-
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
- Otherwise (local planning):
|
|
375
|
-
|
|
376
|
-
```
|
|
377
|
-
Skill(skill="gsd-plan-phase", args="${PHASE_NUM}")
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
- **On other runtimes:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
|
366
|
+
- **If `RUNTIME` is `codex`:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
|
381
367
|
|
|
382
368
|
- If `PLAN_STRATEGY=converge`, print: `◆ Spawning background plan-convergence loop for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
|
383
369
|
|
|
@@ -401,6 +387,20 @@ RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo
|
|
|
401
387
|
|
|
402
388
|
Store the agent task_id. After discuss for the next phase completes (or if no next phase), wait for the plan agent to finish before proceeding to execute.
|
|
403
389
|
|
|
390
|
+
- **Otherwise (Claude Code or any other non-Codex runtime):** Run plan **inline** (do NOT background) so the plan-checker runs. The next phase's discuss does not overlap planning here — correctness over overlap.
|
|
391
|
+
|
|
392
|
+
- If `PLAN_STRATEGY=converge`:
|
|
393
|
+
|
|
394
|
+
```
|
|
395
|
+
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
- Otherwise (local planning):
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
Skill(skill="gsd-plan-phase", args="${PHASE_NUM}")
|
|
402
|
+
```
|
|
403
|
+
|
|
404
404
|
**If `INTERACTIVE` is NOT set (default):** Run plan inline.
|
|
405
405
|
|
|
406
406
|
If `PLAN_STRATEGY=converge`, run the convergence loop:
|
|
@@ -419,19 +419,13 @@ Verify plan produced output — re-run `init phase-op` and check `has_plans`. If
|
|
|
419
419
|
|
|
420
420
|
**3c. Execute**
|
|
421
421
|
|
|
422
|
-
**If `INTERACTIVE` is set:** Wait for the plan agent to complete (if not already) and verify plans exist. Background dispatch is only safe where a backgrounded agent can still
|
|
422
|
+
**If `INTERACTIVE` is set:** Wait for the plan agent to complete (if not already) and verify plans exist. Background dispatch is only safe on a runtime where a backgrounded agent can still nest the pipeline's subagents (plan-checker / worktree executors / verifier). Among supported runtimes only **Codex** (`spawn_agent`) can do this; Claude Code's backgrounded agents have no `Agent`/`Task` tool, and every other runtime either prohibits nested subagents or disables them by default. So run **inline** everywhere except Codex, which is dispatched in the background. Resolve the runtime first:
|
|
423
423
|
|
|
424
424
|
```bash
|
|
425
|
-
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
- **On Claude Code (`RUNTIME` is `claude`):** Run execute **inline** (do NOT background) so worktree isolation and verification run:
|
|
429
|
-
|
|
430
|
-
```
|
|
431
|
-
Skill(skill="gsd-execute-phase", args="${PHASE_NUM} --no-transition")
|
|
425
|
+
RUNTIME=$(gsd_run query config-get runtime --default claude --raw 2>/dev/null || echo "claude")
|
|
432
426
|
```
|
|
433
427
|
|
|
434
|
-
- **
|
|
428
|
+
- **If `RUNTIME` is `codex`:** Dispatch execute as a background agent:
|
|
435
429
|
|
|
436
430
|
```
|
|
437
431
|
Agent(
|
|
@@ -443,6 +437,12 @@ Agent(
|
|
|
443
437
|
|
|
444
438
|
Store the agent task_id. The workflow can now start discussing the next phase while this phase executes in the background. Before starting post-execution routing for this phase, wait for the execute agent to complete.
|
|
445
439
|
|
|
440
|
+
- **Otherwise (Claude Code or any other non-Codex runtime):** Run execute **inline** (do NOT background) so worktree isolation and verification run:
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
Skill(skill="gsd-execute-phase", args="${PHASE_NUM} --no-transition")
|
|
444
|
+
```
|
|
445
|
+
|
|
446
446
|
**If `INTERACTIVE` is NOT set (default):** Run execute inline as before.
|
|
447
447
|
|
|
448
448
|
```
|
|
@@ -656,12 +656,12 @@ Check for blockers in the Blockers/Concerns section. If blockers are found, go t
|
|
|
656
656
|
|
|
657
657
|
If incomplete phases remain: proceed to next phase, loop back to execute_phase.
|
|
658
658
|
|
|
659
|
-
**Interactive mode overlap:** When `INTERACTIVE` is set, the iterate step enables pipeline parallelism **on
|
|
659
|
+
**Interactive mode overlap:** When `INTERACTIVE` is set, the iterate step enables pipeline parallelism **on Codex** (on every other runtime, plan/execute run inline — see 3b/3c — so there is no overlap and phases run sequentially):
|
|
660
660
|
1. After discuss completes for Phase N, dispatch plan+execute as background agents
|
|
661
661
|
2. Immediately start discuss for Phase N+1 (the next incomplete phase) while Phase N builds
|
|
662
662
|
3. Before starting plan for Phase N+1, wait for Phase N's execute agent to complete and handle its post-execution routing (verification, gap closure, etc.)
|
|
663
663
|
|
|
664
|
-
This means the user is always answering discuss questions (lightweight, interactive) while the heavy work (planning, code generation) runs in the background. The main context only accumulates discuss conversations — plan and execute contexts are isolated in their agents. (On Claude Code, plan and execute run inline, so they run sequentially and their work accumulates in the main context.)
|
|
664
|
+
This means the user is always answering discuss questions (lightweight, interactive) while the heavy work (planning, code generation) runs in the background. The main context only accumulates discuss conversations — plan and execute contexts are isolated in their agents. (On Claude Code and all other non-Codex runtimes, plan and execute run inline, so they run sequentially and their work accumulates in the main context.)
|
|
665
665
|
|
|
666
666
|
If all phases complete, proceed to lifecycle step.
|
|
667
667
|
|
|
@@ -873,9 +873,9 @@ When any phase operation fails or a blocker is detected, present 3 options via A
|
|
|
873
873
|
- [ ] `--to N` handle_blocker resume message preserves --to flag
|
|
874
874
|
- [ ] `--to N` skips lifecycle when not all milestone phases complete
|
|
875
875
|
- [ ] `--interactive` runs discuss inline via gsd-discuss-phase (asks questions, waits for user)
|
|
876
|
-
- [ ] `--interactive` dispatches plan and execute as background agents on
|
|
877
|
-
- [ ] `--interactive` enables pipeline parallelism (discuss Phase N+1 while Phase N builds) on
|
|
878
|
-
- [ ] `--interactive` main context only accumulates discuss conversations on
|
|
876
|
+
- [ ] `--interactive` dispatches plan and execute as background agents on Codex (the only runtime where a backgrounded agent can nest subagents); runs them inline on all other runtimes
|
|
877
|
+
- [ ] `--interactive` enables pipeline parallelism (discuss Phase N+1 while Phase N builds) on Codex; phases run sequentially on all other runtimes
|
|
878
|
+
- [ ] `--interactive` main context only accumulates discuss conversations on Codex (on all other runtimes, inline plan/execute also accumulate)
|
|
879
879
|
- [ ] `--interactive` waits for background agents before post-execution routing
|
|
880
880
|
- [ ] `--interactive` compatible with `--only`, `--from`, and `--to` flags
|
|
881
881
|
- [ ] `--converge` routes planning through `gsd-plan-review-convergence`
|
|
@@ -59,7 +59,12 @@ gaps = [
|
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
_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}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; 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
|
|
62
|
-
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees 2>/dev/null || echo "true")
|
|
62
|
+
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees --raw 2>/dev/null || echo "true")
|
|
63
|
+
RUNTIME=$(gsd_run query config-get runtime --default claude --raw 2>/dev/null || echo "claude")
|
|
64
|
+
if [ "$RUNTIME" != "claude" ] && [ "$USE_WORKTREES" != "false" ]; then
|
|
65
|
+
echo "FATAL: git worktree isolation (isolation=\"worktree\") is unsupported on runtime '$RUNTIME' — it would run executor agents unisolated against the main checkout. Set workflow.use_worktrees=false." >&2
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
63
68
|
```
|
|
64
69
|
|
|
65
70
|
**Report diagnosis plan to user:**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
> `workflows/discuss-phase.md`, immediately before writing
|
|
5
5
|
> `${phase_dir}/${padded_phase}-CONTEXT.md`. Do not put a reference to this
|
|
6
6
|
> file in `<required_reading>` — that defeats the progressive-disclosure
|
|
7
|
-
> savings
|
|
7
|
+
> savings from the discuss-phase/modes split (#717).
|
|
8
8
|
|
|
9
9
|
## Variable substitutions
|
|
10
10
|
|
|
@@ -19,8 +19,7 @@ You are a thinking partner, not an interviewer. The user is the visionary — yo
|
|
|
19
19
|
|
|
20
20
|
<progressive_disclosure>
|
|
21
21
|
**Per-mode bodies, templates, and the advisor flow are lazy-loaded** to keep
|
|
22
|
-
this file under the
|
|
23
|
-
budget). Read only the files needed for the current invocation:
|
|
22
|
+
this file under the discuss-phase byte budget (32000 bytes, #717; mirrors the agent size-budget convention). Read only the files needed for the current invocation:
|
|
24
23
|
|
|
25
24
|
| When | Read |
|
|
26
25
|
|---|---|
|
|
@@ -91,13 +91,13 @@ Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelizat
|
|
|
91
91
|
Read runtime/worktree config and fail closed before any executor dispatch:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
|
|
95
|
-
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees 2>/dev/null || echo "true")
|
|
94
|
+
RUNTIME=$(gsd_run query config-get runtime --default claude --raw 2>/dev/null || echo "claude")
|
|
95
|
+
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees --raw 2>/dev/null || echo "true")
|
|
96
96
|
EXECUTOR_STALL_INTERVAL_MINUTES=$(gsd_run query config-get executor.stall_detect_interval_minutes 2>/dev/null || echo "5")
|
|
97
97
|
EXECUTOR_STALL_THRESHOLD_MINUTES=$(gsd_run query config-get executor.stall_threshold_minutes 2>/dev/null || echo "10")
|
|
98
98
|
|
|
99
|
-
if [ "$RUNTIME"
|
|
100
|
-
echo "FATAL:
|
|
99
|
+
if [ "$RUNTIME" != "claude" ] && [ "$USE_WORKTREES" != "false" ]; then
|
|
100
|
+
echo "FATAL: git worktree isolation (isolation=\"worktree\") is unsupported on runtime '$RUNTIME' — it would run executor agents unisolated against the main checkout. Set workflow.use_worktrees=false." >&2
|
|
101
101
|
exit 1
|
|
102
102
|
fi
|
|
103
103
|
# Sweep orphaned locked worktrees from prior crashed sessions before spawning executors (#3707).
|
|
@@ -113,7 +113,7 @@ if [ "$RUNTIME" = "claude" ] && [ "$USE_WORKTREES" != "false" ]; then
|
|
|
113
113
|
fi
|
|
114
114
|
fi
|
|
115
115
|
```
|
|
116
|
-
Codex maps subagents to `spawn_agent`,
|
|
116
|
+
`isolation="worktree"` is a Claude-Code-specific agent primitive; no other runtime can honor it (Codex maps subagents to `spawn_agent`, others prohibit or omit worktree binding). Failing closed prevents main-checkout edits while the workflow believes agents are isolated.
|
|
117
117
|
|
|
118
118
|
If the project uses git submodules, worktree isolation is unsafe **only when a plan touches a submodule path** — the executor commit protocol cannot correctly handle submodule commits inside isolated worktrees. The previous behavior unconditionally disabled worktree isolation whenever `.gitmodules` existed, which penalised every plan in a submodule project even when the plan was nowhere near a submodule. Compute submodule paths once and intersect them per-plan with the plan's declared `files_modified` frontmatter.
|
|
119
119
|
|
|
@@ -491,6 +491,10 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
|
|
491
491
|
|
|
492
492
|
**For each wave:**
|
|
493
493
|
|
|
494
|
+
@~/.claude/gsd-core/references/execute-phase-wave-guard.md
|
|
495
|
+
|
|
496
|
+
@~/.claude/gsd-core/references/execute-phase-context-guard.md
|
|
497
|
+
|
|
494
498
|
1. **Intra-wave files_modified overlap check (BEFORE spawning):**
|
|
495
499
|
|
|
496
500
|
Before spawning any agents for this wave, inspect the `files_modified` list of all plans
|
|
@@ -683,7 +687,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
|
|
683
687
|
)
|
|
684
688
|
```
|
|
685
689
|
|
|
686
|
-
After each `Agent()` returns, parse executor-returned worktree metadata (`<worktree_metadata>`) before harness metadata, then
|
|
690
|
+
After each `Agent()` returns, parse executor-returned worktree metadata (`<worktree_metadata>`) before harness metadata, then record the `{agent_id, worktree_path, branch, expected_base}` entry with `gsd_run query worktree.record-agent --manifest "$WAVE_WORKTREE_MANIFEST" --agent-id … --path … --branch … --base …`. The verb validates every field at write time using the same rules the `cleanup-wave` reader enforces (write-strict `--agent-id`), failing loudly with a non-zero exit and recovery hint rather than appending an under-populated entry the reader would later drop silently. On a non-zero exit or any missing field: stop and ask for recovery instead of scanning worktrees.
|
|
687
691
|
|
|
688
692
|
> **Worktree recovery policy (#48 + #1292):** See `execute-phase/steps/worktree-recovery-policy.md` — FAIL-CLOSED rule for base/HEAD-namespace mismatches AND isolated-run fail-safe recovery.
|
|
689
693
|
|
|
@@ -1038,12 +1042,8 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
|
|
1038
1042
|
**Step 7.3 — `class == "unknown-failure"`:**
|
|
1039
1043
|
Report failed plan and ask Continue/Stop; continuing may cascade into dependent plan failures.
|
|
1040
1044
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
If any PRIOR-wave artifact link fails, present:
|
|
1044
|
-
- `## Cross-Plan Wiring Gap` with plan/link/from/pattern rows
|
|
1045
|
-
- Options: investigate+fix before continue, or continue with cascade risk
|
|
1046
|
-
Skip key-links that reference files in the CURRENT (upcoming) wave.
|
|
1045
|
+
@~/.claude/gsd-core/references/execute-phase-between-wave-reset.md
|
|
1046
|
+
|
|
1047
1047
|
8. **Execute checkpoint plans between waves** — see `<checkpoint_handling>`.
|
|
1048
1048
|
9. **Proceed to next wave.**
|
|
1049
1049
|
</step>
|
|
@@ -394,6 +394,16 @@ List pending todos and select one to work on.
|
|
|
394
394
|
Usage: `/gsd:capture --list`
|
|
395
395
|
Usage: `/gsd:capture --list api`
|
|
396
396
|
|
|
397
|
+
**`/gsd:capture --list-seeds [status]`**
|
|
398
|
+
List and audit captured seeds (read-only).
|
|
399
|
+
|
|
400
|
+
- Lists all seeds with ID, status, scope, trigger, and title
|
|
401
|
+
- Optional status filter (e.g., `/gsd:capture --list-seeds dormant`)
|
|
402
|
+
- Does not modify any seed — enrich with `/gsd:capture --seed --enrich SEED-NNN`
|
|
403
|
+
|
|
404
|
+
Usage: `/gsd:capture --list-seeds`
|
|
405
|
+
Usage: `/gsd:capture --list-seeds dormant`
|
|
406
|
+
|
|
397
407
|
### User Acceptance Testing
|
|
398
408
|
|
|
399
409
|
**`/gsd:verify-work [phase]`**
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<purpose>
|
|
2
|
+
List captured seeds for browsing and audit, with an optional status filter. Read-only — never mutates seeds.
|
|
3
|
+
</purpose>
|
|
4
|
+
|
|
5
|
+
<required_reading>
|
|
6
|
+
Read all files referenced by the invoking prompt's execution_context before starting.
|
|
7
|
+
</required_reading>
|
|
8
|
+
|
|
9
|
+
<process>
|
|
10
|
+
|
|
11
|
+
<step name="load_seeds">
|
|
12
|
+
Load seed context. An optional status filter (e.g. `dormant`, `active`, `triggered`) may follow `--list-seeds`.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
_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}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; 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
|
|
16
|
+
SEEDS=$(gsd_run list-seeds "$STATUS_FILTER")
|
|
17
|
+
if [[ "$SEEDS" == @file:* ]]; then SEEDS=$(cat "${SEEDS#@file:}"); fi
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Replace `$STATUS_FILTER` with the filter token from `$ARGUMENTS` if one was given, otherwise omit it.
|
|
21
|
+
|
|
22
|
+
Extract from the JSON: `count`, `seeds[]` (each has `seed_id`, `status`, `scope`, `trigger_when`, `planted`, `title`), and `summary` (a `{ status: count }` map).
|
|
23
|
+
</step>
|
|
24
|
+
|
|
25
|
+
<step name="empty_case">
|
|
26
|
+
If `count` is 0:
|
|
27
|
+
```
|
|
28
|
+
No seeds found.
|
|
29
|
+
|
|
30
|
+
Plant one with /gsd:capture --seed "<forward-looking idea>".
|
|
31
|
+
```
|
|
32
|
+
(If a status filter was given and nothing matched, say so: `No seeds with status "<filter>".`) Exit.
|
|
33
|
+
</step>
|
|
34
|
+
|
|
35
|
+
<step name="render_table">
|
|
36
|
+
Render the seeds as a table, sorted by `seed_id` (already sorted by the tool). Truncate `trigger_when` and `title` to keep the table readable.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Seeds
|
|
40
|
+
─────────────────────────────────────────────────────────────────────
|
|
41
|
+
ID Status Scope Trigger Title
|
|
42
|
+
SEED-001 dormant large when websockets land Real-time collaboration
|
|
43
|
+
SEED-006 triggered medium MILE-04 planning Remove legacy auth crates
|
|
44
|
+
─────────────────────────────────────────────────────────────────────
|
|
45
|
+
<count> seeds (<summary rendered as "N status" pairs, e.g. "1 dormant, 1 triggered">)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Then offer next actions as plain text (no mutation here):
|
|
49
|
+
```
|
|
50
|
+
- /gsd:capture --seed --enrich <ID> enrich a seed with trigger, why, and scope
|
|
51
|
+
- /gsd:capture --list-seeds <status> filter by status
|
|
52
|
+
```
|
|
53
|
+
</step>
|
|
54
|
+
|
|
55
|
+
</process>
|
|
56
|
+
|
|
57
|
+
<success_criteria>
|
|
58
|
+
- [ ] Seeds listed with ID, status, scope, trigger, and title
|
|
59
|
+
- [ ] Status filter applied when provided
|
|
60
|
+
- [ ] Empty / no-match case handled with guidance
|
|
61
|
+
- [ ] Summary line shows total and per-status counts
|
|
62
|
+
- [ ] No seed files were modified (read-only)
|
|
63
|
+
</success_criteria>
|