From e03921c7d8f3a5853b0a099aea95ceb23ee54844 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Tue, 8 Sep 2026 00:17:22 -0400 Subject: [PATCH] enhance(#4405): split the rest of the eager-window workflows worth splitting (#4536) --- .changeset/jolly-badgers-wake.md | 5 + docs/CONFIGURATION.md | 2 +- docs/INVENTORY-MANIFEST.json | 7 +- docs/INVENTORY.md | 2 +- docs/PARTITION-RULES.md | 26 ++ gsd-core/references/planning-config.md | 2 +- gsd-core/workflows/complete-milestone.md | 266 +---------------- .../complete-milestone/detail/elaboration.md | 274 ++++++++++++++++++ gsd-core/workflows/docs-update.md | 167 +---------- .../docs-update/detail/elaboration.md | 179 ++++++++++++ gsd-core/workflows/execute-phase.md | 112 +------ .../execute-phase/detail/elaboration.md | 124 ++++++++ gsd-core/workflows/new-project.md | 208 +------------ .../new-project/detail/elaboration.md | 216 ++++++++++++++ gsd-core/workflows/verify-work.md | 200 ++----------- .../verify-work/detail/elaboration.md | 230 +++++++++++++++ .../lint-workflow-shellcheck-baseline.json | 35 +++ .../compact-content-benchmark-baseline.json | 31 +- tests/fixtures/install-tree/antigravity.json | 5 + tests/fixtures/install-tree/augment.json | 5 + tests/fixtures/install-tree/claude-local.json | 5 + tests/fixtures/install-tree/claude.json | 5 + tests/fixtures/install-tree/cline.json | 5 + tests/fixtures/install-tree/codebuddy.json | 5 + tests/fixtures/install-tree/codex.json | 5 + tests/fixtures/install-tree/copilot.json | 5 + tests/fixtures/install-tree/cursor.json | 5 + tests/fixtures/install-tree/hermes.json | 5 + tests/fixtures/install-tree/kilo.json | 5 + tests/fixtures/install-tree/kimi-code.json | 5 + tests/fixtures/install-tree/kimi.json | 5 + tests/fixtures/install-tree/opencode.json | 5 + tests/fixtures/install-tree/pi.json | 5 + tests/fixtures/install-tree/qwen.json | 5 + tests/fixtures/install-tree/trae.json | 5 + tests/fixtures/install-tree/windsurf.json | 5 + tests/fixtures/install-tree/zcode.json | 5 + tests/helpers/compact-content-split.cjs | 54 +++- 38 files changed, 1328 insertions(+), 907 deletions(-) create mode 100644 .changeset/jolly-badgers-wake.md create mode 100644 gsd-core/workflows/complete-milestone/detail/elaboration.md create mode 100644 gsd-core/workflows/docs-update/detail/elaboration.md create mode 100644 gsd-core/workflows/execute-phase/detail/elaboration.md create mode 100644 gsd-core/workflows/new-project/detail/elaboration.md create mode 100644 gsd-core/workflows/verify-work/detail/elaboration.md diff --git a/.changeset/jolly-badgers-wake.md b/.changeset/jolly-badgers-wake.md new file mode 100644 index 000000000..ac1f1cd12 --- /dev/null +++ b/.changeset/jolly-badgers-wake.md @@ -0,0 +1,5 @@ +--- +type: Added +pr: 4536 +--- +**Five more workflow spines split into a terser form under `workflow.compact_content`** — `execute-phase`, `docs-update`, `new-project`, `verify-work`, and `complete-milestone` join `plan-phase` (#4402), bringing the total to six, each moving genuinely optional or rare content (interactive-mode flows, off-by-default features, gap-closure loops, cross-AI delegation, branch-merge mechanics) into a deferred `/detail/*.md` elaboration read only when the key is off; several pre-existing structural drift guards pin exact wording in specific spine steps (crash-resume detection, checkpoint auto-approval, learnings extraction, revision-conflict handling), so those sections keep their full text in the spine rather than deferring it. The refreshed benchmark reports a 15.66% aggregate token reduction across the six splits. The remaining eagerly-included workflows were reviewed and recorded as not worth splitting, with reasons, in `docs/PARTITION-RULES.md`. (#4405) diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index ea5fc7dfa..90ae0cdcc 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -517,7 +517,7 @@ All workflow toggles follow the **absent = enabled** pattern. If a key is missin | `workflow.text_mode` | boolean | `false` | Replaces AskUserQuestion TUI menus with plain-text numbered lists. Required for Claude Code remote sessions (`/rc` mode) where TUI menus don't render. Can also be set per-session with `--text` flag on discuss-phase. Added in v1.28 | | `workflow.use_worktrees` | boolean | `true` | When `false`, disables git worktree isolation for parallel execution. Users who prefer sequential execution or whose environment does not support worktrees can disable this. Added in v1.31. **Branch-divergence note:** when your branch has diverged from `origin/HEAD`, GSD auto-degrades to sequential and prints a warning. See [`worktree.baseRef`](#worktree-settings) to restore parallel execution on a diverged branch. **Per-runtime note:** whether this key can be honored depends on the runtime's declared `dispatch.isolation` capability, not on its name (#2584). Runtimes whose own harness isolates each executor (**Claude Code**, **Cursor**) run parallel worktrees natively; runtimes exposing a headless exec with an explicit working directory (**Codex**, **OpenCode**, **Kimi**, **Kimi Code**) get worktrees GSD itself creates and merges — where a dispatch site can only drive the harness model, those hosts degrade to sequential with a warning rather than aborting. Every other runtime declares no isolation primitive, and forcing `use_worktrees: true` there still fails closed before any executor dispatch. `/gsd-health` reports such a value as warning `W025` (#2486). **Default on a non-Claude install:** if a worktree-capable non-Claude host is not isolating as described above, check whether the install stamped this key's default to `false` and set an explicit `use_worktrees: true`. See [Executor isolation per runtime](#executor-isolation-per-runtime). | | `workflow.agent_hint_routing` | boolean | `true` | Per-plan specialist executor routing (#1689). When `true`, a plan whose `agent_hint:` frontmatter names a subagent that resolves on the active runtime is dispatched to that specialist instead of `gsd-executor`. Default `true` — a no-op for plans without `agent_hint:`, so existing dispatch is unchanged. Set `false` to disable. See [PLAN.md `agent_hint`](reference/plan-md.md#per-plan-executor-routing). | -| `workflow.compact_content` | boolean | `false` | Compact content mode (#4139, [ADR-4139](adr/4139-compact-content-seam.md)). Per-project boolean selecting the terser form of GSD's own shipped prompt content (workflows, templates, agent-skill payloads). `/gsd-plan-phase` is the pilot workflow that actually branches on it today (#4402) — with the key off, its spine reads a deferred elaboration file back in before continuing (byte-identical instruction set to before); with it on, that read is skipped. The rest of the corpus does not branch on it yet — full coverage is later sub-issues (#4405+). The eager-window token reduction each split actually achieves is measured, not asserted: `npm run benchmark:compact-content` reports per-split and aggregate on/off token counts (a proxy-tokenizer delta — Anthropic publishes no tokenizer for Claude 3+, so the comparison is exact under a pinned tokenizer even though the absolute counts are not Claude's real ones) against a committed baseline (`tests/fixtures/compact-content-benchmark-baseline.json`, #4404). Reporting-only — it never fails CI. | +| `workflow.compact_content` | boolean | `false` | Compact content mode (#4139, [ADR-4139](adr/4139-compact-content-seam.md)). Per-project boolean selecting the terser form of GSD's own shipped prompt content (workflows, templates, agent-skill payloads). Six workflows branch on it today — `plan-phase` (#4402, the pilot), `execute-phase`, `docs-update`, `new-project`, `verify-work`, and `complete-milestone` (#4405) — each split into a spine plus a deferred `/detail/*.md` elaboration: with the key off, the spine reads its own elaboration back in before continuing (byte-identical instruction set to before); with it on, that read is skipped. The remaining eagerly-`@`-included workflows were reviewed and recorded as not worth splitting (see `docs/PARTITION-RULES.md` § "Deciding whether a file is worth splitting") — either their size comes from safety-critical orchestration logic rather than deferrable narrative (`review.md`), or they're small enough that a split's fixed structural overhead would exceed the savings. The eager-window token reduction each split actually achieves is measured, not asserted: `npm run benchmark:compact-content` reports per-split and aggregate on/off token counts (a proxy-tokenizer delta — Anthropic publishes no tokenizer for Claude 3+, so the comparison is exact under a pinned tokenizer even though the absolute counts are not Claude's real ones) against a committed baseline (`tests/fixtures/compact-content-benchmark-baseline.json`, #4404). Reporting-only — it never fails CI. | | `workflow.worktree_skip_hooks` | boolean | `false` | When `true`, executor agents in worktree mode pass `--no-verify` (skipping pre-commit hooks) and post-wave hook validation runs against the merged result instead. Opt-in escape hatch for projects whose hooks cannot run in agent worktrees. Default `false` runs hooks on every commit (#2924). | | `workflow.code_review` | boolean | `true` | Enable `/gsd-code-review` and `/gsd-code-review --fix` commands. When `false`, the commands exit with a configuration gate message. Added in v1.34 | | `workflow.code_review_point` | string | `execute:post` | Loop point at which the code-review capability's step registers: `execute:post` reviews once, after every wave in a phase has landed (default — unchanged behavior); `execute:wave:post` reviews once per completed wave instead, scoped to what changed since the phase's prior review (the whole phase's diff on the first wave, each subsequent wave's own diff thereafter). Manual `/gsd-code-review ` invocation is unaffected by this key — it is gated by `workflow.code_review` alone and runs regardless of which point is configured. `/gsd-autonomous` and `/gsd-quick` have no wave granularity of their own, so setting this to `execute:wave:post` means code review does not run automatically inside those two flows (consistent with how every other `execute:wave:post`-only capability already behaves for them). Added in #3661 | diff --git a/docs/INVENTORY-MANIFEST.json b/docs/INVENTORY-MANIFEST.json index b5285f12a..33cdc9651 100644 --- a/docs/INVENTORY-MANIFEST.json +++ b/docs/INVENTORY-MANIFEST.json @@ -672,7 +672,12 @@ "verify-work/steps/mvp-uat-framing.md" ], "workflow_detail": [ - "plan-phase/detail/elaboration.md" + "complete-milestone/detail/elaboration.md", + "docs-update/detail/elaboration.md", + "execute-phase/detail/elaboration.md", + "new-project/detail/elaboration.md", + "plan-phase/detail/elaboration.md", + "verify-work/detail/elaboration.md" ], "workflow_templates": [ "discuss-phase/templates/context.md", diff --git a/docs/INVENTORY.md b/docs/INVENTORY.md index 30abf0411..12420ec0f 100644 --- a/docs/INVENTORY.md +++ b/docs/INVENTORY.md @@ -280,7 +280,7 @@ none is separately invocable — the parent workflow reaches them. |---|---|---|---| | `/steps/*.md` | Gated section bodies extracted by the fragment model (ADR-1671, epic #1671 Phases 6.1–6.3). The parent carries a `section_manifest`-gated stub; `gsd-core/workflows/section-manifest.json` names which step a given invocation reads. | `workflow_steps` | See `docs/INVENTORY-MANIFEST.json` for the authoritative per-file list | | `/modes/*.md` | Progressive-disclosure mode files (#717). The parent dispatches to exactly one; `discuss-phase/modes/` is the canonical example. | `workflow_modes` | `discuss-phase`, `help` | -| `/detail/*.md` | Elaboration content deferred from a workflow spine, read at runtime only when `workflow.compact_content` is `false` (ADR-4139; epic #4139 Phase 2 #4402 established the first example, Phase 3 #4403 added the CI guard). | `workflow_detail` | `plan-phase` | +| `/detail/*.md` | Elaboration content deferred from a workflow spine, read at runtime only when `workflow.compact_content` is `false` (ADR-4139; epic #4139 Phase 2 #4402 established the first example, Phase 3 #4403 added the CI guard, Phase 5 #4405 split the rest of the eager-window corpus worth splitting). | `workflow_detail` | `plan-phase`, `execute-phase`, `docs-update`, `new-project`, `verify-work`, `complete-milestone` | | `/templates/*.md` | Fill-in template bodies the parent workflow renders at runtime; also referenced as a `FRAGMENT_DIRS` entry in `scripts/lint-response-language-coverage.cjs`. | `workflow_templates` | `discuss-phase` | All four families are keyed by `//.md` rather than a bare filename, because diff --git a/docs/PARTITION-RULES.md b/docs/PARTITION-RULES.md index 7e84820dc..a4f48a6c0 100644 --- a/docs/PARTITION-RULES.md +++ b/docs/PARTITION-RULES.md @@ -104,3 +104,29 @@ Checks 1 and 4–5 read `merge-base..HEAD`, never `base..HEAD` (two-dot) — the ADR-3942 made for its own trailer range, for the same reason: a two-dot range would let the set of commits being checked and the set of files being diffed disagree about what "this PR" means. + +## Deciding whether a file is worth splitting + +Not every eagerly-`@`-included workflow benefits from a spine/detail partition. Epic #4139 +Phase 5 ([#4405](https://github.com/open-gsd/gsd-core/issues/4405)) established the working +criteria, applied to every file in the eager-window corpus: + +- **Split it** when the file has one or more clearly-delineated steps or sub-sections that are + genuinely optional or rare in normal execution — gated by an explicit flag, an off-by-default + config key, an uncommon runtime condition, or a fallback path most runs never take. The five + files split in Phase 5 (`execute-phase.md`, `docs-update.md`, `new-project.md`, + `verify-work.md`, `complete-milestone.md`) all had this shape. +- **Record it as not worth splitting** when either: (a) the file's size comes predominantly + from safety-critical, always-relevant orchestration logic and documented bug-history + comments rather than deferrable narrative elaboration — extracting from it would butcher + core happy-path logic or bury a regression-preventing "why" comment (`review.md`'s + disposition in Phase 5, despite being named among the epic's heaviest files); or (b) the + file is small enough that the fixed structural cost of a split — a new `detail/` directory, + the five checks' ongoing enforcement surface, `docs/INVENTORY.md`/manifest bookkeeping, and + reviewer attention — is not justified by the achievable savings. The smallest file split in + Phase 5 (`complete-milestone.md`, 41,278 bytes) still only yielded roughly 10.4 KB of actual + reduction; a file well under that size buys proportionally less for the same fixed cost. + +A "not worth splitting" disposition is not permanent — re-evaluate a file if it grows +substantially, or a later change gives it a genuinely optional or rare execution branch it +didn't have before. diff --git a/gsd-core/references/planning-config.md b/gsd-core/references/planning-config.md index fd8c694cc..48b40eabe 100644 --- a/gsd-core/references/planning-config.md +++ b/gsd-core/references/planning-config.md @@ -289,7 +289,7 @@ Set via `workflow.*` namespace in config.json (e.g., `"workflow": { "research": | `workflow.ui_phase` | boolean | `true` | `true`, `false` | Generate UI-SPEC.md for frontend phases | | `workflow.ui_safety_gate` | boolean | `true` | `true`, `false` | Require safety gate approval for UI changes | | `workflow.text_mode` | boolean | `false` | `true`, `false` | Use plain-text numbered lists instead of AskUserQuestion menus | -| `workflow.compact_content` | boolean | `false` | `true`, `false` | Compact content mode (#4139, ADR-4139) — per-project boolean selecting terser payloads. `/gsd:plan-phase` branches on it today as the pilot (#4402); the rest of the corpus does not yet | +| `workflow.compact_content` | boolean | `false` | `true`, `false` | Compact content mode (#4139, ADR-4139) — per-project boolean selecting terser payloads. Six workflows branch on it: `plan-phase` (#4402, pilot), `execute-phase`, `docs-update`, `new-project`, `verify-work`, `complete-milestone` (#4405). The rest of the eager-window corpus was reviewed and recorded as not worth splitting (`docs/PARTITION-RULES.md`) | | `workflow.research_before_questions` | boolean | `false` | `true`, `false` | Run research before interactive questions in discuss phase (also honored on the `/gsd:quick` path, #3894). _Alias:_ `research_before_questions` is the flat-key form used in `CONFIG_DEFAULTS`; `workflow.research_before_questions` is the canonical namespaced form. | | `workflow.discuss_mode` | string | `"discuss"` | `"discuss"`, `"assumptions"` | Default mode for discuss-phase: `"discuss"` runs interactive questioning; `"assumptions"` analyzes codebase and surfaces assumptions instead | | `workflow.skip_discuss` | boolean | `false` | `true`, `false` | Skip discuss phase entirely | diff --git a/gsd-core/workflows/complete-milestone.md b/gsd-core/workflows/complete-milestone.md index 4d4d064d7..daf9735e3 100644 --- a/gsd-core/workflows/complete-milestone.md +++ b/gsd-core/workflows/complete-milestone.md @@ -37,6 +37,8 @@ When a milestone completes: +**Compact Content Gate.** Read and follow `gsd-core/references/compact-content-gate.md` now — it states the `workflow.compact_content` check and the resolution rule this spine defers to. When it directs a Read, read `gsd-core/workflows/complete-milestone/detail/elaboration.md` in full before continuing past this point; its content elaborates on the audit-acknowledge branch and the handle_branches step below. + Before proceeding with milestone close, run the comprehensive open artifact audit. @@ -60,136 +62,14 @@ These items are open. Choose an action: [C] Cancel — exit without closing ``` -If user chooses [A] (Acknowledge): -1. Re-run `gsd_run query audit-open --json` to get structured data. -2. Acknowledge every open item through the `audit-open acknowledge` CLI writer — this is what actually suppresses each item starting at the NEXT `audit-open` scan; the STATE.md table in step 3 is a disclosure record only, it is no longer the suppression mechanism. Every acknowledge call's exit status is accumulated (`ACK_FAILURES`); the step HALTS before closing if any failed — a refusal (`unsupported_heading_shape`, `ambiguous`, `not_found`, missing file, etc.) must never be silently discarded and let the close proceed as if everything were suppressed. `AUDIT_JSON` uses the same `@file:` large-payload sentinel handling `INIT_MANAGER` uses in `verify_readiness` below — `io.output` swaps any JSON payload over 50000 chars for a `@file:` marker, and feeding that literal string to `jq` would silently make every loop body below iterate zero times: - ```bash - AUDIT_JSON=$(gsd_run query audit-open --json) - if [[ "$AUDIT_JSON" == @file:* ]]; then AUDIT_JSON=$(cat "${AUDIT_JSON#@file:}"); fi - MILESTONE_VERSION="v[X.Y]" # already known from ROADMAP.md's active milestone header — the same identifier `milestone.complete` uses in the archive_milestone step +**If user chooses [A] (Acknowledge):** re-fetch `audit-open --json`, then acknowledge EVERY open item (across all categories — debug_sessions, threads, seeds, todos, quick_tasks, uat_gaps, verification_gaps, context_questions, deferred_items) through the `audit-open acknowledge` CLI writer, which is what actually suppresses each item starting at the next scan (the STATE.md `## Deferred Items` table is a disclosure record only). Any failed acknowledge call HALTS the close before proceeding — a refusal must never be silently discarded. After a clean pass, append one row per acknowledged item to STATE.md's `## Deferred Items` table (sanitized via `sanitizeForDisplay()`, never raw content), set `closeout_type=override_closeout`, and record a `Known verification overrides: {N} newly acknowledged, {M} carried forward` line in MILESTONES.md. Acknowledging is verdict-preserving and self-invalidating — it never rewrites the artifact's own status (except `deferred_items`), and the suppression lapses automatically the moment the artifact's state changes again (a reopened session, an edited gap, a re-triggered seed), resurfacing at the next audit. - ACK_FAILURES=0 - ACK_FAILURE_LOG="" - record_ack_failure() { - ACK_FAILURES=$((ACK_FAILURES + 1)) - ACK_FAILURE_LOG="${ACK_FAILURE_LOG} - - $1" - } - - # debug_sessions / threads (--slug) - # NOTE: `< <(...)` process substitution, not `... | while`, so the loop - # runs in THIS shell — a `| while` pipeline puts the loop in a subshell - # and any ACK_FAILURES/ACK_FAILURE_LOG update inside it is lost the - # moment the pipeline exits. - for cat in debug_sessions threads; do - while IFS= read -r slug; do - [ -z "$slug" ] && continue - if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --slug "$slug"; then - record_ack_failure "$cat slug=$slug" - fi - done < <(printf '%s' "$AUDIT_JSON" | jq -r --arg cat "$cat" '.items[$cat][] | select(.scan_error | not) | .slug') - done - - # seeds (--seed-id) - while IFS= read -r seed_id; do - [ -z "$seed_id" ] && continue - if ! gsd_run query audit-open acknowledge --category seeds --milestone "$MILESTONE_VERSION" --seed-id "$seed_id"; then - record_ack_failure "seeds seed_id=$seed_id" - fi - done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.seeds[] | select(.scan_error | not) | .seed_id') - - # todos (--filename) — the scanner caps its list to 5 entries per scan - # (remainder items carry `_remainder_count`, no `filename`, and are skipped) - while IFS= read -r filename; do - [ -z "$filename" ] && continue - if ! gsd_run query audit-open acknowledge --category todos --milestone "$MILESTONE_VERSION" --filename "$filename"; then - record_ack_failure "todos filename=$filename" - fi - done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.todos[] | select((.scan_error or ._remainder_count) | not) | .filename') - - # quick_tasks (--dir) — the scanner's `slug` strips a leading - # YYYYMMDD-/YYYY-MM-DD- date prefix for display; `--dir` needs the - # ORIGINAL .planning/quick// name, so reconstruct it from `date`+`slug`. - while IFS= read -r dir; do - [ -z "$dir" ] && continue - if ! gsd_run query audit-open acknowledge --category quick_tasks --milestone "$MILESTONE_VERSION" --dir "$dir"; then - record_ack_failure "quick_tasks dir=$dir" - fi - done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.quick_tasks[] | select(.scan_error | not) | if .date != "" then "\(.date)-\(.slug)" else .slug end') - - # uat_gaps / verification_gaps / context_questions — phase-scoped - # (--phase --file [--archived-milestone] when the item was found in an archived phase) - for cat in uat_gaps verification_gaps context_questions; do - while IFS= read -r item; do - [ -z "$item" ] && continue - phase=$(printf '%s' "$item" | jq -r '.phase') - file=$(printf '%s' "$item" | jq -r '.file') - archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty') - if [ -n "$archived" ]; then - if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --archived-milestone "$archived"; then - record_ack_failure "$cat phase=$phase file=$file archived-milestone=$archived" - fi - else - if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file"; then - record_ack_failure "$cat phase=$phase file=$file" - fi - fi - done < <(printf '%s' "$AUDIT_JSON" | jq -c --arg cat "$cat" '.items[$cat][] | select(.scan_error | not)') - done - - # deferred_items — same phase-scoped identification, plus --text (the - # exact bullet the audit read, which uniquely identifies the entry) - while IFS= read -r item; do - [ -z "$item" ] && continue - phase=$(printf '%s' "$item" | jq -r '.phase') - file=$(printf '%s' "$item" | jq -r '.file') - text=$(printf '%s' "$item" | jq -r '.text') - archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty') - if [ -n "$archived" ]; then - if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text" --archived-milestone "$archived"; then - record_ack_failure "deferred_items phase=$phase file=$file archived-milestone=$archived" - fi - else - if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text"; then - record_ack_failure "deferred_items phase=$phase file=$file" - fi - fi - done < <(printf '%s' "$AUDIT_JSON" | jq -c '.items.deferred_items[] | select(.scan_error | not)') - - if [ "$ACK_FAILURES" -gt 0 ]; then - echo "ERROR: $ACK_FAILURES acknowledge call(s) failed — HALTING before milestone close. Resolve each listed item manually (e.g. edit the file directly for unsupported_heading_shape/ambiguous, or re-run the audit if a --text/--file target has since changed) and re-run /gsd:complete-milestone:" >&2 - printf '%s\n' "$ACK_FAILURE_LOG" >&2 - exit 1 - fi - ``` - `todos` is the only category the scanner caps (5 entries per scan, with a remainder count for the rest). Re-run `gsd_run query audit-open --json` (through the same `@file:` handling above) and repeat the `todos` block until it reports no `todos` items — every other category always returns its full open set in one pass. -3. Re-run `gsd_run query audit-open --json` once more and write the items just acknowledged as new rows to STATE.md under `## Deferred Items` — append to the existing table (creating the section if absent) rather than overwriting it, preserving rows recorded at earlier milestone closes: - ```markdown - ## Deferred Items - - Items acknowledged and deferred at milestone close, most recent first: - - | Category | Item | Status | Deferred At | Milestone | - |----------|------|--------|-------------|-----------| - | debug_sessions | {slug} | {status} | {date} | {milestone} | - | quick_tasks | {slug} | {status} | {date} | {milestone} | - | threads | {slug} | {status} | {date} | {milestone} | - | seeds | {seed_id} | {status} | {date} | {milestone} | - | todos | {filename} | (presence-only) | {date} | {milestone} | - | uat_gaps | {phase}/{file} | {status} | {date} | {milestone} | - | verification_gaps | {phase}/{file} | {status} | {date} | {milestone} | - | context_questions | {phase}/{file} | {question_count} questions | {date} | {milestone} | - | deferred_items | {phase}/{file}: {text} | acknowledged | {date} | {milestone} | - ``` - One row per item actually acknowledged in step 2 (omit categories with nothing to disclose this close). `{date}` is today's date; `{milestone}` is `MILESTONE_VERSION`. Sanitize all slug/status/text values via `sanitizeForDisplay()` before writing. Never inject raw file content into STATE.md. -4. Set `closeout_type=override_closeout` and record in the MILESTONES.md entry: `Known verification overrides: {N} newly acknowledged, {M} carried forward from a prior close (see STATE.md Deferred Items)` — `{N}` is the count of items acknowledged in step 2 (the pre-acknowledgment audit JSON's `counts.total`) and `{M}` is that same audit JSON's `acknowledged.total` (items a PRIOR close already suppressed and still are). -5. Proceed with milestone close. - -Acknowledging is verdict-preserving and self-invalidating: it never rewrites the artifact's own `status:` field (except `deferred_items`, whose entry has no other meaning for that field), and the suppression it grants lapses automatically the moment the artifact's observed state changes again — a reopened debug session, an edited UAT gap, a re-triggered seed, etc. resurfaces on its own at the next audit and must be acknowledged again. - -If output shows all clear (no open items): set `closeout_type=verified_closeout`. If the audit JSON's `acknowledged.total` is `0`, print `All artifact types clear.` and proceed. Otherwise the close is clean only because `{acknowledged.total}` item(s) acknowledged at an earlier milestone close are still being suppressed, not because everything was fixed this time — print `All artifact types clear ({acknowledged.total} previously acknowledged item(s) still suppressed — see STATE.md Deferred Items).` and record `Known verification overrides: 0 newly acknowledged, {acknowledged.total} carried forward from a prior close (see STATE.md Deferred Items)` in the MILESTONES.md entry before proceeding. +If output shows all clear (no open items): set `closeout_type=verified_closeout` — but if any items are `acknowledged.total` from a PRIOR close, note that carried-forward suppression explicitly rather than implying everything was fixed this time. + SECURITY: Audit JSON output is structured data from the `audit-open` query handler (same JSON contract as legacy `gsd_run audit-open`) — validated and sanitized at source. The `audit-open acknowledge` writer is the only path that sets the `audit_acknowledged` suppression marker — it snapshots each artifact's current state itself from the identifiers passed on the command line, so this workflow never hand-authors the marker. When writing the STATE.md disclosure table, item identifiers, statuses, and deferred-item text are sanitized via `sanitizeForDisplay()` before inclusion. Never inject raw user-supplied content into STATE.md without sanitization. + +Exact per-category bash (including the `@file:` large-payload handling, the `todos` 5-per-scan cap, and the phase-scoped `--archived-milestone` handling) and the exact STATE.md table shape: `gsd-core/workflows/complete-milestone/detail/elaboration.md` § 1. @@ -716,140 +596,14 @@ See: .planning/PROJECT.md (updated [today]) -Check branching strategy and offer merge options. - -Use `init milestone-op` for context, or load config directly: - -```bash -INIT=$(gsd_run query init.execute-phase "1") -if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi -INIT_CM=$(gsd_run query init.complete-milestone) -if [[ "$INIT_CM" == @file:* ]]; then INIT_CM=$(cat "${INIT_CM#@file:}"); fi -``` - -Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_template`, and `commit_docs` from init JSON. Extract `git_create_tag` and `section_manifest` from `INIT_CM` (used by the `git_tag` step below). - -Detect base branch: +Check the project's `branching_strategy` (from `init.execute-phase`/`init.complete-milestone`). `"none"` skips straight to `git_tag`. For `"phase"` or `"milestone"`, list the matching branches (by the configured prefix template); no branches found also skips to `git_tag`. Resolve the base branch through the single shared resolver, never a bare `main`/`master` fallback (Issue #1146): ```bash BASE_BRANCH=$(gsd_run query git.base-branch) ``` -**If "none":** Skip to git_tag. +If branches exist, present them and ask (AskUserQuestion): **Squash merge** (recommended) / **Merge with history** / **Delete without merging** / **Keep branches**. All three merge/delete options iterate every matching branch (phase strategy) or the one milestone branch, checking out `BASE_BRANCH` first and returning to the original branch after; both merge options strip `.planning/` from staging first when `commit_docs` is false. "Keep branches" just reports them as preserved for manual handling. -**For "phase" strategy:** - -```bash -BRANCH_PREFIX=$(echo "$PHASE_BRANCH_TEMPLATE" | sed 's/{.*//') -PHASE_BRANCHES=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ') -``` - -**For "milestone" strategy:** - -```bash -BRANCH_PREFIX=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed 's/{.*//') -MILESTONE_BRANCH=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ' | head -1) -``` - -**If no branches found:** Skip to git_tag. - -**If branches exist:** - -``` -## Git Branches Detected - -Branching strategy: {phase/milestone} -Branches: {list} - -Options: -1. **Merge to main** — Merge branch(es) to main -2. **Delete without merging** — Already merged or not needed -3. **Keep branches** — Leave for manual handling -``` - -AskUserQuestion with options: Squash merge (Recommended), Merge with history, Delete without merging, Keep branches. - -**Squash merge:** - -```bash -CURRENT_BRANCH=$(git branch --show-current) -git checkout ${BASE_BRANCH} - -if [ "$BRANCHING_STRATEGY" = "phase" ]; then - # Rewrapped through unquoted command substitution (gsd-core#4109): a bare - # `$VAR` word-splits under bash but not zsh, collapsing every element onto - # one iteration there. - for branch in $(printf '%s' "$PHASE_BRANCHES"); do - git merge --squash "$branch" - # Strip .planning/ from staging if commit_docs is false - if [ "$COMMIT_DOCS" = "false" ]; then - git reset HEAD .planning/ 2>/dev/null || true - fi - git commit -m "feat: $branch for v[X.Y]" - done -fi - -if [ "$BRANCHING_STRATEGY" = "milestone" ]; then - git merge --squash "$MILESTONE_BRANCH" - # Strip .planning/ from staging if commit_docs is false - if [ "$COMMIT_DOCS" = "false" ]; then - git reset HEAD .planning/ 2>/dev/null || true - fi - git commit -m "feat: $MILESTONE_BRANCH for v[X.Y]" -fi - -git checkout "$CURRENT_BRANCH" -``` - -**Merge with history:** - -```bash -CURRENT_BRANCH=$(git branch --show-current) -git checkout ${BASE_BRANCH} - -if [ "$BRANCHING_STRATEGY" = "phase" ]; then - # Rewrapped through unquoted command substitution (gsd-core#4109): a bare - # `$VAR` word-splits under bash but not zsh, collapsing every element onto - # one iteration there. - for branch in $(printf '%s' "$PHASE_BRANCHES"); do - git merge --no-ff --no-commit "$branch" - # Strip .planning/ from staging if commit_docs is false - if [ "$COMMIT_DOCS" = "false" ]; then - git reset HEAD .planning/ 2>/dev/null || true - fi - git commit -m "Merge branch '$branch' for v[X.Y]" - done -fi - -if [ "$BRANCHING_STRATEGY" = "milestone" ]; then - git merge --no-ff --no-commit "$MILESTONE_BRANCH" - # Strip .planning/ from staging if commit_docs is false - if [ "$COMMIT_DOCS" = "false" ]; then - git reset HEAD .planning/ 2>/dev/null || true - fi - git commit -m "Merge branch '$MILESTONE_BRANCH' for v[X.Y]" -fi - -git checkout "$CURRENT_BRANCH" -``` - -**Delete without merging:** - -```bash -if [ "$BRANCHING_STRATEGY" = "phase" ]; then - # Rewrapped through unquoted command substitution (gsd-core#4109): a bare - # `$VAR` word-splits under bash but not zsh, collapsing every element onto - # one iteration there. - for branch in $(printf '%s' "$PHASE_BRANCHES"); do - git branch -d "$branch" 2>/dev/null || git branch -D "$branch" - done -fi - -if [ "$BRANCHING_STRATEGY" = "milestone" ]; then - git branch -d "$MILESTONE_BRANCH" 2>/dev/null || git branch -D "$MILESTONE_BRANCH" -fi -``` - -**Keep branches:** Report "Branches preserved for manual handling" +Exact bash for each of the four options (squash, history-preserving merge, delete, keep): `gsd-core/workflows/complete-milestone/detail/elaboration.md` § 2. diff --git a/gsd-core/workflows/complete-milestone/detail/elaboration.md b/gsd-core/workflows/complete-milestone/detail/elaboration.md new file mode 100644 index 000000000..d77ff7806 --- /dev/null +++ b/gsd-core/workflows/complete-milestone/detail/elaboration.md @@ -0,0 +1,274 @@ +# complete-milestone.md — deferred elaboration + +Read in full when `workflow.compact_content` is `false` (the default) — see +`gsd-core/references/compact-content-gate.md` for the check and resolution rule this +spine defers to. Each `§` below is the full text the spine condenses at the point it +names. + +## § 1 — pre_close_artifact_audit: the [A] Acknowledge branch + +If user chooses [A] (Acknowledge): +1. Re-run `gsd_run query audit-open --json` to get structured data. +2. Acknowledge every open item through the `audit-open acknowledge` CLI writer — this is what actually suppresses each item starting at the NEXT `audit-open` scan; the STATE.md table in step 3 is a disclosure record only, it is no longer the suppression mechanism. Every acknowledge call's exit status is accumulated (`ACK_FAILURES`); the step HALTS before closing if any failed — a refusal (`unsupported_heading_shape`, `ambiguous`, `not_found`, missing file, etc.) must never be silently discarded and let the close proceed as if everything were suppressed. `AUDIT_JSON` uses the same `@file:` large-payload sentinel handling `INIT_MANAGER` uses in `verify_readiness` below — `io.output` swaps any JSON payload over 50000 chars for a `@file:` marker, and feeding that literal string to `jq` would silently make every loop body below iterate zero times: + ```bash +_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif _gsd_at "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd_run is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; case "$(gsd_run runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') GSD_IDENTITY_STATUS=ok;; esac; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi + AUDIT_JSON=$(gsd_run query audit-open --json) + if [[ "$AUDIT_JSON" == @file:* ]]; then AUDIT_JSON=$(cat "${AUDIT_JSON#@file:}"); fi + MILESTONE_VERSION="v[X.Y]" # already known from ROADMAP.md's active milestone header — the same identifier `milestone.complete` uses in the archive_milestone step + + ACK_FAILURES=0 + ACK_FAILURE_LOG="" + record_ack_failure() { + ACK_FAILURES=$((ACK_FAILURES + 1)) + ACK_FAILURE_LOG="${ACK_FAILURE_LOG} + - $1" + } + + # debug_sessions / threads (--slug) + # NOTE: `< <(...)` process substitution, not `... | while`, so the loop + # runs in THIS shell — a `| while` pipeline puts the loop in a subshell + # and any ACK_FAILURES/ACK_FAILURE_LOG update inside it is lost the + # moment the pipeline exits. + for cat in debug_sessions threads; do + while IFS= read -r slug; do + [ -z "$slug" ] && continue + if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --slug "$slug"; then + record_ack_failure "$cat slug=$slug" + fi + done < <(printf '%s' "$AUDIT_JSON" | jq -r --arg cat "$cat" '.items[$cat][] | select(.scan_error | not) | .slug') + done + + # seeds (--seed-id) + while IFS= read -r seed_id; do + [ -z "$seed_id" ] && continue + if ! gsd_run query audit-open acknowledge --category seeds --milestone "$MILESTONE_VERSION" --seed-id "$seed_id"; then + record_ack_failure "seeds seed_id=$seed_id" + fi + done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.seeds[] | select(.scan_error | not) | .seed_id') + + # todos (--filename) — the scanner caps its list to 5 entries per scan + # (remainder items carry `_remainder_count`, no `filename`, and are skipped) + while IFS= read -r filename; do + [ -z "$filename" ] && continue + if ! gsd_run query audit-open acknowledge --category todos --milestone "$MILESTONE_VERSION" --filename "$filename"; then + record_ack_failure "todos filename=$filename" + fi + done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.todos[] | select((.scan_error or ._remainder_count) | not) | .filename') + + # quick_tasks (--dir) — the scanner's `slug` strips a leading + # YYYYMMDD-/YYYY-MM-DD- date prefix for display; `--dir` needs the + # ORIGINAL .planning/quick// name, so reconstruct it from `date`+`slug`. + while IFS= read -r dir; do + [ -z "$dir" ] && continue + if ! gsd_run query audit-open acknowledge --category quick_tasks --milestone "$MILESTONE_VERSION" --dir "$dir"; then + record_ack_failure "quick_tasks dir=$dir" + fi + done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.quick_tasks[] | select(.scan_error | not) | if .date != "" then "\(.date)-\(.slug)" else .slug end') + + # uat_gaps / verification_gaps / context_questions — phase-scoped + # (--phase --file [--archived-milestone] when the item was found in an archived phase) + for cat in uat_gaps verification_gaps context_questions; do + while IFS= read -r item; do + [ -z "$item" ] && continue + phase=$(printf '%s' "$item" | jq -r '.phase') + file=$(printf '%s' "$item" | jq -r '.file') + archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty') + if [ -n "$archived" ]; then + if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --archived-milestone "$archived"; then + record_ack_failure "$cat phase=$phase file=$file archived-milestone=$archived" + fi + else + if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file"; then + record_ack_failure "$cat phase=$phase file=$file" + fi + fi + done < <(printf '%s' "$AUDIT_JSON" | jq -c --arg cat "$cat" '.items[$cat][] | select(.scan_error | not)') + done + + # deferred_items — same phase-scoped identification, plus --text (the + # exact bullet the audit read, which uniquely identifies the entry) + while IFS= read -r item; do + [ -z "$item" ] && continue + phase=$(printf '%s' "$item" | jq -r '.phase') + file=$(printf '%s' "$item" | jq -r '.file') + text=$(printf '%s' "$item" | jq -r '.text') + archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty') + if [ -n "$archived" ]; then + if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text" --archived-milestone "$archived"; then + record_ack_failure "deferred_items phase=$phase file=$file archived-milestone=$archived" + fi + else + if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text"; then + record_ack_failure "deferred_items phase=$phase file=$file" + fi + fi + done < <(printf '%s' "$AUDIT_JSON" | jq -c '.items.deferred_items[] | select(.scan_error | not)') + + if [ "$ACK_FAILURES" -gt 0 ]; then + echo "ERROR: $ACK_FAILURES acknowledge call(s) failed — HALTING before milestone close. Resolve each listed item manually (e.g. edit the file directly for unsupported_heading_shape/ambiguous, or re-run the audit if a --text/--file target has since changed) and re-run /gsd:complete-milestone:" >&2 + printf '%s\n' "$ACK_FAILURE_LOG" >&2 + exit 1 + fi + ``` + `todos` is the only category the scanner caps (5 entries per scan, with a remainder count for the rest). Re-run `gsd_run query audit-open --json` (through the same `@file:` handling above) and repeat the `todos` block until it reports no `todos` items — every other category always returns its full open set in one pass. +3. Re-run `gsd_run query audit-open --json` once more and write the items just acknowledged as new rows to STATE.md under `## Deferred Items` — append to the existing table (creating the section if absent) rather than overwriting it, preserving rows recorded at earlier milestone closes: + ```markdown + ## Deferred Items + + Items acknowledged and deferred at milestone close, most recent first: + + | Category | Item | Status | Deferred At | Milestone | + |----------|------|--------|-------------|-----------| + | debug_sessions | {slug} | {status} | {date} | {milestone} | + | quick_tasks | {slug} | {status} | {date} | {milestone} | + | threads | {slug} | {status} | {date} | {milestone} | + | seeds | {seed_id} | {status} | {date} | {milestone} | + | todos | {filename} | (presence-only) | {date} | {milestone} | + | uat_gaps | {phase}/{file} | {status} | {date} | {milestone} | + | verification_gaps | {phase}/{file} | {status} | {date} | {milestone} | + | context_questions | {phase}/{file} | {question_count} questions | {date} | {milestone} | + | deferred_items | {phase}/{file}: {text} | acknowledged | {date} | {milestone} | + ``` + One row per item actually acknowledged in step 2 (omit categories with nothing to disclose this close). `{date}` is today's date; `{milestone}` is `MILESTONE_VERSION`. Sanitize all slug/status/text values via `sanitizeForDisplay()` before writing. Never inject raw file content into STATE.md. +4. Set `closeout_type=override_closeout` and record in the MILESTONES.md entry: `Known verification overrides: {N} newly acknowledged, {M} carried forward from a prior close (see STATE.md Deferred Items)` — `{N}` is the count of items acknowledged in step 2 (the pre-acknowledgment audit JSON's `counts.total`) and `{M}` is that same audit JSON's `acknowledged.total` (items a PRIOR close already suppressed and still are). +5. Proceed with milestone close. + +Acknowledging is verdict-preserving and self-invalidating: it never rewrites the artifact's own `status:` field (except `deferred_items`, whose entry has no other meaning for that field), and the suppression it grants lapses automatically the moment the artifact's observed state changes again — a reopened debug session, an edited UAT gap, a re-triggered seed, etc. resurfaces on its own at the next audit and must be acknowledged again. + +If output shows all clear (no open items): set `closeout_type=verified_closeout`. If the audit JSON's `acknowledged.total` is `0`, print `All artifact types clear.` and proceed. Otherwise the close is clean only because `{acknowledged.total}` item(s) acknowledged at an earlier milestone close are still being suppressed, not because everything was fixed this time — print `All artifact types clear ({acknowledged.total} previously acknowledged item(s) still suppressed — see STATE.md Deferred Items).` and record `Known verification overrides: 0 newly acknowledged, {acknowledged.total} carried forward from a prior close (see STATE.md Deferred Items)` in the MILESTONES.md entry before proceeding. + +(The SECURITY note on audit JSON provenance and STATE.md sanitization is stated in the spine, not repeated here.) + +## § 2 — handle_branches + +Check branching strategy and offer merge options. + +Use `init milestone-op` for context, or load config directly: + +```bash +INIT=$(gsd_run query init.execute-phase "1") +if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi +INIT_CM=$(gsd_run query init.complete-milestone) +if [[ "$INIT_CM" == @file:* ]]; then INIT_CM=$(cat "${INIT_CM#@file:}"); fi +``` + +Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_template`, and `commit_docs` from init JSON. Extract `git_create_tag` and `section_manifest` from `INIT_CM` (used by the `git_tag` step below). + +`BASE_BRANCH` is already resolved by the spine at this point (via the shared `git.base-branch` resolver) — the branch options below use it as-is. + +**If "none":** Skip to git_tag. + +**For "phase" strategy:** + +```bash +BRANCH_PREFIX=$(echo "$PHASE_BRANCH_TEMPLATE" | sed 's/{.*//') +PHASE_BRANCHES=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ') +``` + +**For "milestone" strategy:** + +```bash +BRANCH_PREFIX=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed 's/{.*//') +MILESTONE_BRANCH=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ' | head -1) +``` + +**If no branches found:** Skip to git_tag. + +**If branches exist:** + +``` +## Git Branches Detected + +Branching strategy: {phase/milestone} +Branches: {list} + +Options: +1. **Merge to main** — Merge branch(es) to main +2. **Delete without merging** — Already merged or not needed +3. **Keep branches** — Leave for manual handling +``` + +AskUserQuestion with options: Squash merge (Recommended), Merge with history, Delete without merging, Keep branches. + +**Squash merge:** + +```bash +CURRENT_BRANCH=$(git branch --show-current) +git checkout ${BASE_BRANCH} + +if [ "$BRANCHING_STRATEGY" = "phase" ]; then + # Rewrapped through unquoted command substitution (gsd-core#4109): a bare + # `$VAR` word-splits under bash but not zsh, collapsing every element onto + # one iteration there. + for branch in $(printf '%s' "$PHASE_BRANCHES"); do + git merge --squash "$branch" + # Strip .planning/ from staging if commit_docs is false + if [ "$COMMIT_DOCS" = "false" ]; then + git reset HEAD .planning/ 2>/dev/null || true + fi + git commit -m "feat: $branch for v[X.Y]" + done +fi + +if [ "$BRANCHING_STRATEGY" = "milestone" ]; then + git merge --squash "$MILESTONE_BRANCH" + # Strip .planning/ from staging if commit_docs is false + if [ "$COMMIT_DOCS" = "false" ]; then + git reset HEAD .planning/ 2>/dev/null || true + fi + git commit -m "feat: $MILESTONE_BRANCH for v[X.Y]" +fi + +git checkout "$CURRENT_BRANCH" +``` + +**Merge with history:** + +```bash +CURRENT_BRANCH=$(git branch --show-current) +git checkout ${BASE_BRANCH} + +if [ "$BRANCHING_STRATEGY" = "phase" ]; then + # Rewrapped through unquoted command substitution (gsd-core#4109): a bare + # `$VAR` word-splits under bash but not zsh, collapsing every element onto + # one iteration there. + for branch in $(printf '%s' "$PHASE_BRANCHES"); do + git merge --no-ff --no-commit "$branch" + # Strip .planning/ from staging if commit_docs is false + if [ "$COMMIT_DOCS" = "false" ]; then + git reset HEAD .planning/ 2>/dev/null || true + fi + git commit -m "Merge branch '$branch' for v[X.Y]" + done +fi + +if [ "$BRANCHING_STRATEGY" = "milestone" ]; then + git merge --no-ff --no-commit "$MILESTONE_BRANCH" + # Strip .planning/ from staging if commit_docs is false + if [ "$COMMIT_DOCS" = "false" ]; then + git reset HEAD .planning/ 2>/dev/null || true + fi + git commit -m "Merge branch '$MILESTONE_BRANCH' for v[X.Y]" +fi + +git checkout "$CURRENT_BRANCH" +``` + +**Delete without merging:** + +```bash +if [ "$BRANCHING_STRATEGY" = "phase" ]; then + # Rewrapped through unquoted command substitution (gsd-core#4109): a bare + # `$VAR` word-splits under bash but not zsh, collapsing every element onto + # one iteration there. + for branch in $(printf '%s' "$PHASE_BRANCHES"); do + git branch -d "$branch" 2>/dev/null || git branch -D "$branch" + done +fi + +if [ "$BRANCHING_STRATEGY" = "milestone" ]; then + git branch -d "$MILESTONE_BRANCH" 2>/dev/null || git branch -D "$MILESTONE_BRANCH" +fi +``` + +**Keep branches:** Report "Branches preserved for manual handling" diff --git a/gsd-core/workflows/docs-update.md b/gsd-core/workflows/docs-update.md index cff09c920..5b16b2b0b 100644 --- a/gsd-core/workflows/docs-update.md +++ b/gsd-core/workflows/docs-update.md @@ -10,6 +10,8 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo +**Compact Content Gate.** Read and follow `gsd-core/references/compact-content-gate.md` now — it states the `workflow.compact_content` check and the resolution rule this spine defers to. When it directs a Read, read `gsd-core/workflows/docs-update/detail/elaboration.md` in full before continuing past this point; its content elaborates on three steps below (sequential_generation, fix_loop, verify_only_report). + Load docs-update context: @@ -721,56 +723,9 @@ If `section_manifest` (from `INIT_DOCS_UPDATE`) is `null` or `"dispatch-monorepo -**Read the work manifest first:** `Read .planning/tmp/docs-work-manifest.json` — use `canonical_queue` items for generation order. Update `status` after each doc is generated. Write the updated manifest back to disk after all docs are complete. +When the `Task` tool is unavailable, generate all queued docs sequentially in the current context instead of spawning subagents — this step replaces dispatch_wave_1, collect_wave_1, dispatch_wave_2, and collect_wave_2. Read `agents/gsd-doc-writer.md` once, then for each queued doc (Wave 1: README/ARCHITECTURE/CONFIGURATION, complete before Wave 2; Wave 2: GETTING-STARTED/DEVELOPMENT/TESTING plus any queued conditional docs, referencing Wave 1 outputs) construct the same doc_assignment fields the parallel path uses and write the file inline, using only file system tools (never browser-based tools). If `monorepo_workspaces` is non-empty, generate per-package READMEs sequentially afterward. Continue to verify_docs. -When the `Task` tool is unavailable, generate docs sequentially in the current context. This step replaces dispatch_wave_1, collect_wave_1, dispatch_wave_2, and collect_wave_2. - -**IMPORTANT:** Do NOT use `browser_subagent`, `Explore`, or any browser-based tool. Use only file system tools (Read, Bash, Write, Grep, Glob, or equivalent tools available in your runtime). - -Read `agents/gsd-doc-writer.md` instructions once before beginning. Follow the create_mode or update_mode instructions from that agent for each doc, using the same doc_assignment fields as the parallel path. - -**Wave 1 (sequential — complete all three before starting Wave 2):** - -For each Wave 1 doc, construct the equivalent doc_assignment block and generate the file inline: - -1. **README** — mode from resolve_modes; for update/supplement mode, include existing_content - - Construct doc_assignment: `type: readme`, `mode: {create|update|supplement}`, `preservation_mode: {value|null}`, `project_context: {INIT JSON}`, `existing_content:` (if update/supplement) - - Explore the codebase (Read, Grep, Glob, Bash) following gsd-doc-writer create_mode / update_mode instructions - - Write the file to the resolved path (README.md) - -2. **ARCHITECTURE** — mode from resolve_modes; for update/supplement mode, include existing_content - - Construct doc_assignment: `type: architecture`, `mode: {create|update|supplement}`, `preservation_mode: {value|null}`, `project_context: {INIT JSON}`, `existing_content:` (if update/supplement) - - Explore the codebase following gsd-doc-writer instructions - - Write the file to the resolved path (docs/ARCHITECTURE.md, or ARCHITECTURE.md if found at root as fallback) - -3. **CONFIGURATION** — mode from resolve_modes; for update/supplement mode, include existing_content - - Construct doc_assignment: `type: configuration`, `mode: {create|update|supplement}`, `preservation_mode: {value|null}`, `project_context: {INIT JSON}`, `existing_content:` (if update/supplement) - - Apply VERIFY markers to any infrastructure claim not discoverable from the repository - - Explore the codebase following gsd-doc-writer instructions - - Write the file to the resolved path (docs/CONFIGURATION.md, or CONFIGURATION.md if found at root as fallback) - -**Wave 2 (sequential — begin only after all Wave 1 docs are written):** - -Wave 2 docs can reference Wave 1 outputs since they are already written. Include `wave_1_outputs` in each doc_assignment. - -4. **GETTING-STARTED** — mode from resolve_modes; include wave_1_outputs: [README.md, docs/ARCHITECTURE.md, docs/CONFIGURATION.md] -5. **DEVELOPMENT** — mode from resolve_modes; include wave_1_outputs -6. **TESTING** — mode from resolve_modes; include wave_1_outputs -7. **API** (only if queued) — mode from resolve_modes; include wave_1_outputs -8. **DEPLOYMENT** (only if queued) — Apply VERIFY markers to any infrastructure claim not discoverable from the repository; include wave_1_outputs -9. **CONTRIBUTING** (only if queued) — mode from resolve_modes; include wave_1_outputs - -**Monorepo per-package READMEs (only if `monorepo_workspaces` is non-empty):** - -After all 9 root-level docs are written, generate per-package READMEs sequentially: - -For each resolved package directory (from workspace glob expansion) that contains a `package.json`: -- Determine mode: if `{package_dir}/README.md` exists, mode = `update`; else mode = `create` -- Construct doc_assignment: `type: readme`, `mode: {create|update}`, `scope: per_package`, `package_dir: {absolute path}`, `project_context: {INIT JSON with project_root set to package directory}`, `existing_content:` (if update) -- Follow gsd-doc-writer instructions for per_package scope -- Write the file to `{package_dir}/README.md` - -Continue to verify_docs. +Exact per-doc construction and the monorepo per-package loop: `gsd-core/workflows/docs-update/detail/elaboration.md` § 1. @@ -847,39 +802,16 @@ If any doc (canonical OR non-canonical) has `claims_failed > 0`: continue to fix -**Read the work manifest first:** `Read .planning/tmp/docs-work-manifest.json` — identify ALL docs (canonical AND non-canonical) with `claims_failed > 0` from the verification results in `.planning/tmp/verify-*.json`. Both queues are eligible for fixes. +**Skip condition:** if every doc passed verification (no `claims_failed > 0`), skip this step entirely. -Correct flagged inaccuracies by re-sending failing docs to the doc-writer in fix mode. Per D-06, max 2 iterations. Per D-05, halt immediately on regression. +Otherwise, correct flagged inaccuracies by re-sending failing docs to `gsd-doc-writer` in `fix` mode (one spawn per doc, never batched), for at most 2 iterations (D-06). Each spawn carries a `` block: `type` (the doc's original type), `mode: fix`, `doc_path`, `project_context`, `existing_content` (current file content), and `failures:` — a structured array of `{line, claim, expected, actual}` objects, one per failed claim. -**Skip condition:** If all docs passed verification (no failures), skip this step. - -**Iteration tracking:** -- `MAX_FIX_ITERATIONS = 2` -- `iteration = 0` -- `previous_passed_docs` = set of doc_paths where claims_failed === 0 after initial verification - -**For each iteration (while iteration < MAX_FIX_ITERATIONS and there are docs with failures):** - -1. For each doc with `claims_failed > 0` in the latest verification_results: +For each doc with a failure, per iteration: a. Read the current file content from disk. Record the pre-fix line count: ```bash PRE_FIX_LINES=$(wc -l < "{doc_path}" 2>/dev/null || echo 0) ``` - b. Spawn `gsd-doc-writer` agent (or invoke sequentially) with a fix assignment: - ```xml - - type: {original doc type from the queue, e.g. readme} - mode: fix - doc_path: {relative path} - project_context: {INIT JSON} - existing_content: {current file content read from disk} - failures: - - line: {line} - claim: "{claim}" - expected: "{expected}" - actual: "{actual}" - - ``` + b. Spawn `gsd-doc-writer` with the `` block above. c. One agent spawn per doc with failures. Do not batch multiple docs into one spawn. d. **Post-fix truncation guard:** After the fix agent completes, check for file corruption: ```bash @@ -891,90 +823,17 @@ Correct flagged inaccuracies by re-sending failing docs to the doc-writer in fix - Mark this doc as `"fix-corrupted"` in the manifest; it will appear in remaining failures at the end - Do NOT attempt to fix this doc again this iteration. It is still included in the step 2 re-verification (so its failures are counted) but no further fix agent will be dispatched for it in this iteration. -2. After all fix agents complete, re-verify ALL docs (not just the ones that were fixed): - - Re-run the same verification process as verify_docs step. - - Read updated result JSONs from `.planning/tmp/verify-{doc_filename}.json`. +After each iteration's fix agents complete, re-verify ALL docs and check for regression (D-05): any doc that previously passed and now fails HALTS the loop immediately — remaining failures require manual review, no further fixes attempted. After 2 iterations with failures remaining, report them and continue. -3. **Regression detection (D-05):** - For each doc in the new verification_results: - - If this doc was in `previous_passed_docs` (passed in the prior round) AND now has `claims_failed > 0`, this is a REGRESSION. - - If regression detected: HALT the loop immediately. Present: - ``` - REGRESSION DETECTED -- halting fix loop. +Continue to scan_for_secrets either way. - {doc_path} previously passed verification but now has {claims_failed} failures after fix iteration {iteration + 1}. - - This means the fix introduced new errors. Remaining failures require manual review. - ``` - Continue to scan_for_secrets (do not attempt further fixes). - -4. Update `previous_passed_docs` with docs that now pass. -5. Increment `iteration`. - -**After loop exhaustion (iteration === MAX_FIX_ITERATIONS and failures remain):** - -Present remaining failures: -``` -Fix loop completed ({MAX_FIX_ITERATIONS} iterations). Remaining failures: - -| Doc | Failed Claims | -|-------------------|---------------| -| {doc_path} | {count} | - -These failures require manual correction. Review the verification output in .planning/tmp/verify-*.json for details. -``` - -Continue to scan_for_secrets. +Exact iteration bookkeeping and the regression-halt report wording: `gsd-core/workflows/docs-update/detail/elaboration.md` § 2. -**Reached when `--verify-only` is present in `$ARGUMENTS`.** This is an early-exit step — do not proceed to dispatch, generation, commit, or report steps after this step. +**Reached when `--verify-only` is present in `$ARGUMENTS`** — an early-exit reporting mode: do not proceed to dispatch, generation, commit, or report steps after this step. Spawn `gsd-doc-verifier` (read-only) for every file in `existing_docs`, count ` **Carve-out — overrides all branches above.** If the returned `Gate:` is `blocking-human` (precondition-unmet, #3210), or its `` mentions `Package verification required before install` or `Package install failed — human verification required`, never auto-approve or auto-select. Present to user (standard flow). Log `⛔ blocking-human gate — auto-mode suspended`. **Standard flow (not auto-mode, human-action, or blocking-human):** diff --git a/gsd-core/workflows/execute-phase/detail/elaboration.md b/gsd-core/workflows/execute-phase/detail/elaboration.md new file mode 100644 index 000000000..af8ba0e01 --- /dev/null +++ b/gsd-core/workflows/execute-phase/detail/elaboration.md @@ -0,0 +1,124 @@ +# execute-phase.md — deferred elaboration + +Read in full when `workflow.compact_content` is `false` (the default) — see +`gsd-core/references/compact-content-gate.md` for the check and resolution rule this +spine defers to. Each `§` below is the full text the spine condenses at the point it +names. + +(safe_resume_gate, checkpoint_handling, and auto_copy_learnings are stated verbatim in the +spine itself — pre-existing structural drift guards in this repo's test suite pin their exact +wording and bash there, so nothing about them is deferred to this file.) + +## § 1 — check_interactive_mode + +**Parse `--interactive` flag from $ARGUMENTS.** + +**If `--interactive` flag present:** Switch to interactive execution mode. + +Interactive mode executes plans sequentially **inline** (no subagent spawning) with user +checkpoints between tasks. The user can review, modify, or redirect work at any point. + +**Interactive execution flow:** + +1. Load plan inventory as normal (discover_and_group_plans) +2. For each plan (sequentially, ignoring wave grouping): + + a. **Present the plan to the user:** + ``` + ## Plan {plan_id}: {plan_name} + + Objective: {from plan file} + Tasks: {task_count} + + Options: + - Execute (proceed with all tasks) + - Review first (show task breakdown before starting) + - Skip (move to next plan) + - Stop (end execution, save progress) + ``` + + b. **If "Review first":** Read and display the full plan file. Ask again: Execute, Modify, Skip. + + c. **If "Execute":** Read and follow `~/.claude/gsd-core/workflows/execute-plan.md` **inline** + (do NOT spawn a subagent). Execute tasks one at a time. + + d. **After each task:** Pause briefly. If the user intervenes (types anything), stop and address + their feedback before continuing. Otherwise proceed to next task. + + e. **After plan complete:** Show results, commit, create SUMMARY.md, then present next plan. + +3. After all plans: proceed to verification (same as normal mode). + +(The spine's own condensed text already states the handle_branching hand-off; not repeated here.) + +## § 2 — cross_ai_delegation + +**Optional step 2.5 — Delegate plans to an external AI runtime.** + +This step runs after plan discovery and before normal wave execution. It identifies plans +that should be delegated to an external AI command and executes them via stdin-based prompt +delivery. Plans handled here are removed from the execute_waves plan list so the normal +executor skips them. + +**Activation logic:** + +1. If `CROSS_AI_DISABLED` is true (`--no-cross-ai` flag): skip this step entirely. +2. If `CROSS_AI_FORCE` is true (`--cross-ai` flag): mark ALL incomplete plans for cross-AI execution. +3. Otherwise: check each plan's frontmatter for `cross_ai: true` AND verify config + `workflow.cross_ai_execution` is `true`. Plans matching both conditions are marked for cross-AI. + +```bash +_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif _gsd_at "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd_run is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; case "$(gsd_run runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') GSD_IDENTITY_STATUS=ok;; esac; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi +CROSS_AI_ENABLED=$(gsd_run query config-get workflow.cross_ai_execution --raw 2>/dev/null || echo "false") +CROSS_AI_CMD=$(gsd_run query config-get workflow.cross_ai_command --raw 2>/dev/null || echo "") +CROSS_AI_TIMEOUT=$(gsd_run query config-get workflow.cross_ai_timeout --raw 2>/dev/null || echo "300") +``` + +**If no plans are marked for cross-AI:** Skip to execute_waves. + +**If plans are marked but `cross_ai_command` is empty:** Error — tell user to set +`workflow.cross_ai_command` via `gsd_run query config-set workflow.cross_ai_command ""`. + +**For each cross-AI plan (sequentially):** + +1. **Construct the task prompt** from the plan file: + - Extract `` and `` sections from the PLAN.md + - Append PROJECT.md context (project name, description, tech stack) + - Format as a self-contained execution prompt + +2. **Check for dirty working tree before execution:** + ```bash + if ! git diff --quiet HEAD 2>/dev/null; then + echo "WARNING: dirty working tree detected — the external AI command may produce uncommitted changes that conflict with existing modifications" + fi + ``` + +3. **Run the external command** from the project root, writing the prompt to stdin. + Never shell-interpolate the prompt — always pipe via stdin to prevent injection: + ```bash + echo "$TASK_PROMPT" | gsd_run run-with-timeout "${CROSS_AI_TIMEOUT}" -- ${CROSS_AI_CMD} > "$CANDIDATE_SUMMARY" 2>"$ERROR_LOG" + EXIT_CODE=$? + ``` + +4. **Evaluate the result:** + + **Success (exit 0 + valid summary):** + - Read `$CANDIDATE_SUMMARY` and validate it contains meaningful content + (not empty, has at least a heading and description — a valid SUMMARY.md structure) + - Write it as the plan's SUMMARY.md file + - Update STATE.md plan status to complete + - Update ROADMAP.md progress + - Mark plan as handled — skip it in execute_waves + + **Failure (non-zero exit or invalid summary):** + - Display the error output and exit code + - Warn: "The external command may have left uncommitted changes or partial edits + in the working tree. Review `git status` and `git diff` before proceeding." + - Offer three choices: + - **retry** — run the same plan through cross-AI again + - **skip** — fall back to normal executor for this plan (re-add to execute_waves list) + - **abort** — stop execution entirely, preserve state for resume + +5. **After all cross-AI plans processed:** Remove successfully handled plans from the + incomplete plan list so execute_waves skips them. Any skipped-to-fallback plans remain + in the list for normal executor processing. diff --git a/gsd-core/workflows/new-project.md b/gsd-core/workflows/new-project.md index 0fb56e70b..3b03cefbc 100644 --- a/gsd-core/workflows/new-project.md +++ b/gsd-core/workflows/new-project.md @@ -23,6 +23,8 @@ If `section_manifest` is `null` or `"auto-mode-detection"` is in its `included` +**Compact Content Gate.** Read and follow `gsd-core/references/compact-content-gate.md` now — it states the `workflow.compact_content` check and the resolution rule this spine defers to. When it directs a Read, read `gsd-core/workflows/new-project/detail/elaboration.md` in full before continuing past this point; its content elaborates on two sections below (Step 2b's prior spike/sketch detection, and Step 6's researcher/synthesizer prompts). + ## 1. Setup **MANDATORY FIRST STEP — Execute these checks before ANY user interaction:** @@ -118,33 +120,9 @@ If `section_manifest` is `null` or `"auto-mode-config"` is in its `included` lis ## 2b. Prior Spike/Sketch Detection -Check for existing spike and sketch work that should inform project setup: +Check for a spike/sketch findings skill or raw `.planning/{spikes,sketches}/MANIFEST.md` files. If any exist, surface them before questioning (which findings-skill, if any, and any raw un-wrapped spikes/sketches worth `/gsd:spike --wrap-up` / `/gsd:sketch --wrap-up`), and if a findings skill exists, read its SKILL.md to inform the questioning phase — it carries validated patterns, constraints, and design decisions that should shape the project definition. -```bash -# Check for spike findings skill (project-local) -SPIKE_SKILL=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1 || true) - -# Check for sketch findings skill (project-local) -SKETCH_SKILL=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true) - -# Check for raw spikes/sketches in .planning/ -HAS_SPIKES=$(ls .planning/spikes/MANIFEST.md 2>/dev/null) -HAS_SKETCHES=$(ls .planning/sketches/MANIFEST.md 2>/dev/null) -``` - -If any of these exist, surface them before questioning: - -``` -⚡ Prior exploration detected: -{if SPIKE_SKILL} ✓ Spike findings skill: {path} — validated patterns from experiments -{if SKETCH_SKILL} ✓ Sketch findings skill: {path} — validated design decisions -{if HAS_SPIKES && !SPIKE_SKILL} ◆ Raw spikes in .planning/spikes/ — consider `/gsd:spike --wrap-up` to package findings -{if HAS_SKETCHES && !SKETCH_SKILL} ◆ Raw sketches in .planning/sketches/ — consider `/gsd:sketch --wrap-up` to package findings - -These findings will be incorporated into project context and available to planning agents. -``` - -If spike/sketch findings skills exist, read their SKILL.md files to inform the questioning phase — they contain validated patterns, constraints, and design decisions that should shape the project definition. +Exact detection commands and the surfaced-findings banner: `gsd-core/workflows/new-project/detail/elaboration.md` § 1. ## 3. Deep Questioning @@ -728,180 +706,13 @@ Display spawning indicator: → Pitfalls research ``` -Spawn 4 parallel gsd-project-researcher agents with path references: - - - -> **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`researcher_model`, `synthesizer_model`, `roadmapper_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md. - -```text -Agent(prompt=" -Project Research — Stack dimension for [domain]. - - - -[greenfield OR subsequent] - -Greenfield: Research the standard stack for building [domain] from scratch. -Subsequent: Research what's needed to add [target features] to an existing [domain] app. Don't re-research the existing system. - - - -What's the standard 2025 stack for [domain]? - - - -- {project_path} (Project context and goals) - - -${AGENT_SKILLS_RESEARCHER} - - -Your STACK.md feeds into roadmap creation. Be prescriptive: -- Specific libraries with versions -- Clear rationale for each choice -- What NOT to use and why - - - -- [ ] Versions are current (verify with Context7/official docs, not training data) -- [ ] Rationale explains WHY, not just WHAT -- [ ] Confidence levels assigned to each recommendation - - - - -> **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md. - - -Write to: {research_dir}/STACK.md -Use template: ~/.claude/gsd-core/templates/research-project/STACK.md - -", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Stack research") - -Agent(prompt=" -Project Research — Features dimension for [domain]. - - - -[greenfield OR subsequent] - -Greenfield: What features do [domain] products have? What's table stakes vs differentiating? -Subsequent: How do [target features] typically work? What's expected behavior? - - - -What features do [domain] products have? What's table stakes vs differentiating? - - - -- {project_path} (Project context) - - -${AGENT_SKILLS_RESEARCHER} - - -Your FEATURES.md feeds into requirements definition. Categorize clearly: -- Table stakes (must have or users leave) -- Differentiators (competitive advantage) -- Anti-features (things to deliberately NOT build) - - - -- [ ] Categories are clear (table stakes vs differentiators vs anti-features) -- [ ] Complexity noted for each feature -- [ ] Dependencies between features identified - - - -Write to: {research_dir}/FEATURES.md -Use template: ~/.claude/gsd-core/templates/research-project/FEATURES.md - -", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Features research") - -Agent(prompt=" -Project Research — Architecture dimension for [domain]. - - - -[greenfield OR subsequent] - -Greenfield: How are [domain] systems typically structured? What are major components? -Subsequent: How do [target features] integrate with existing [domain] architecture? - - - -How are [domain] systems typically structured? What are major components? - - - -- {project_path} (Project context) - - -${AGENT_SKILLS_RESEARCHER} - - -Your ARCHITECTURE.md informs phase structure in roadmap. Include: -- Component boundaries (what talks to what) -- Data flow (how information moves) -- Suggested build order (dependencies between components) - - - -- [ ] Components clearly defined with boundaries -- [ ] Data flow direction explicit -- [ ] Build order implications noted - - - -Write to: {research_dir}/ARCHITECTURE.md -Use template: ~/.claude/gsd-core/templates/research-project/ARCHITECTURE.md - -", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Architecture research") - -Agent(prompt=" -Project Research — Pitfalls dimension for [domain]. - - - -[greenfield OR subsequent] - -Greenfield: What do [domain] projects commonly get wrong? Critical mistakes? -Subsequent: What are common mistakes when adding [target features] to [domain]? - - - -What do [domain] projects commonly get wrong? Critical mistakes? - - - -- {project_path} (Project context) - - -${AGENT_SKILLS_RESEARCHER} - - -Your PITFALLS.md prevents mistakes in roadmap/planning. For each pitfall: -- Warning signs (how to detect early) -- Prevention strategy (how to avoid) -- Which phase should address it - - - -- [ ] Pitfalls are specific to this domain (not generic advice) -- [ ] Prevention strategies are actionable -- [ ] Phase mapping included where relevant - - - -Write to: {research_dir}/PITFALLS.md -Use template: ~/.claude/gsd-core/templates/research-project/PITFALLS.md - -", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Pitfalls research") -``` +Spawn 4 parallel gsd-project-researcher agents — one per dimension (Stack, Features, Architecture, Pitfalls) — each given the domain and greenfield/subsequent milestone context, a dimension-specific question, a downstream-consumer note (what the next stage needs from this file), and a quality gate; each writes its own file (STACK.md / FEATURES.md / ARCHITECTURE.md / PITFALLS.md) under `{research_dir}/` from its template. + > **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling all 4 researcher Agent() calls above, do NOT read research files or synthesize content independently while the subagents are active. Wait for all 4 researchers to complete before spawning the synthesizer. This prevents duplicate work and wasted context. +**Model omission (#2517) applies to every one of these 5 spawns** (4 researchers + synthesizer): omit the `model=` parameter entirely when the value it would carry (`researcher_model`, `synthesizer_model`) is `"inherit"` or empty — passing it literally 404s on runtimes without native tier aliases (the default on non-Claude runtimes). Omitting `model=` inherits the orchestrator's model. + After all 4 agents complete, spawn synthesizer to create SUMMARY.md: ```text @@ -927,6 +738,7 @@ Commit after writing. ", subagent_type="gsd-research-synthesizer", model="{synthesizer_model}", description="Synthesize research") ``` + > **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available. **Synthesizer output self-heal (#222) — verify SUMMARY.md materialized:** The synthesizer's canonical output is `.planning/research/SUMMARY.md` on disk; its brief structured return (`## SYNTHESIS COMPLETE` plus a few `###` confirmation lines) is NOT the file content. A known LLM false-refusal (issue #222) sometimes makes the agent return the full SUMMARY.md document inline — fabricating a write restriction (e.g. "the runtime is blocking file writes") — instead of writing the file. Prompt hardening alone does not fully eliminate it, so the orchestrator MUST absorb the failure deterministically before spawning `gsd-roadmapper`: @@ -937,6 +749,8 @@ Commit after writing. This guarantees `gsd-roadmapper` (which lists SUMMARY.md as required reading) never runs against a missing or truncated SUMMARY.md. +Exact agent prompts (all four researcher dimensions): `gsd-core/workflows/new-project/detail/elaboration.md` § 2. + Display research complete banner and key findings: ``` diff --git a/gsd-core/workflows/new-project/detail/elaboration.md b/gsd-core/workflows/new-project/detail/elaboration.md new file mode 100644 index 000000000..7c68a9d61 --- /dev/null +++ b/gsd-core/workflows/new-project/detail/elaboration.md @@ -0,0 +1,216 @@ +# new-project.md — deferred elaboration + +Read in full when `workflow.compact_content` is `false` (the default) — see +`gsd-core/references/compact-content-gate.md` for the check and resolution rule this +spine defers to. Each `§` below is the full text the spine condenses at the point it +names. + +## § 1 — Prior Spike/Sketch Detection (Step 2b) + +Check for existing spike and sketch work that should inform project setup: + +```bash +# Check for spike findings skill (project-local) +SPIKE_SKILL=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1 || true) + +# Check for sketch findings skill (project-local) +SKETCH_SKILL=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true) + +# Check for raw spikes/sketches in .planning/ +HAS_SPIKES=$(ls .planning/spikes/MANIFEST.md 2>/dev/null) +HAS_SKETCHES=$(ls .planning/sketches/MANIFEST.md 2>/dev/null) +``` + +If any of these exist, surface them before questioning: + +``` +⚡ Prior exploration detected: +{if SPIKE_SKILL} ✓ Spike findings skill: {path} — validated patterns from experiments +{if SKETCH_SKILL} ✓ Sketch findings skill: {path} — validated design decisions +{if HAS_SPIKES && !SPIKE_SKILL} ◆ Raw spikes in .planning/spikes/ — consider `/gsd:spike --wrap-up` to package findings +{if HAS_SKETCHES && !SKETCH_SKILL} ◆ Raw sketches in .planning/sketches/ — consider `/gsd:sketch --wrap-up` to package findings + +These findings will be incorporated into project context and available to planning agents. +``` + +If spike/sketch findings skills exist, read their SKILL.md files to inform the questioning phase — they contain validated patterns, constraints, and design decisions that should shape the project definition. + +## § 2 — Research Decision: the four researcher prompts, synthesizer prompt, and self-heal steps + +Spawn 4 parallel gsd-project-researcher agents with path references: + + + +> **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`researcher_model`, `synthesizer_model`, `roadmapper_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md. + +```text +Agent(prompt=" +Project Research — Stack dimension for [domain]. + + + +[greenfield OR subsequent] + +Greenfield: Research the standard stack for building [domain] from scratch. +Subsequent: Research what's needed to add [target features] to an existing [domain] app. Don't re-research the existing system. + + + +What's the standard 2025 stack for [domain]? + + + +- {project_path} (Project context and goals) + + +${AGENT_SKILLS_RESEARCHER} + + +Your STACK.md feeds into roadmap creation. Be prescriptive: +- Specific libraries with versions +- Clear rationale for each choice +- What NOT to use and why + + + +- [ ] Versions are current (verify with Context7/official docs, not training data) +- [ ] Rationale explains WHY, not just WHAT +- [ ] Confidence levels assigned to each recommendation + + + + +> **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md. + + +Write to: {research_dir}/STACK.md +Use template: ~/.claude/gsd-core/templates/research-project/STACK.md + +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Stack research") + +Agent(prompt=" +Project Research — Features dimension for [domain]. + + + +[greenfield OR subsequent] + +Greenfield: What features do [domain] products have? What's table stakes vs differentiating? +Subsequent: How do [target features] typically work? What's expected behavior? + + + +What features do [domain] products have? What's table stakes vs differentiating? + + + +- {project_path} (Project context, for the Features researcher) + + +${AGENT_SKILLS_RESEARCHER} + + +Your FEATURES.md feeds into requirements definition. Categorize clearly: +- Table stakes (must have or users leave) +- Differentiators (competitive advantage) +- Anti-features (things to deliberately NOT build) + + + +- [ ] Categories are clear (table stakes vs differentiators vs anti-features) +- [ ] Complexity noted for each feature +- [ ] Dependencies between features identified + + + +Write to: {research_dir}/FEATURES.md +Use template: ~/.claude/gsd-core/templates/research-project/FEATURES.md + +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Features research") + +Agent(prompt=" +Project Research — Architecture dimension for [domain]. + + + +[greenfield OR subsequent] + +Greenfield: How are [domain] systems typically structured? What are major components? +Subsequent: How do [target features] integrate with existing [domain] architecture? + + + +How are [domain] systems typically structured? What are major components? + + + +- {project_path} (Project context, for the Architecture researcher) + + +${AGENT_SKILLS_RESEARCHER} + + +Your ARCHITECTURE.md informs phase structure in roadmap. Include: +- Component boundaries (what talks to what) +- Data flow (how information moves) +- Suggested build order (dependencies between components) + + + +- [ ] Components clearly defined with boundaries +- [ ] Data flow direction explicit +- [ ] Build order implications noted + + + +Write to: {research_dir}/ARCHITECTURE.md +Use template: ~/.claude/gsd-core/templates/research-project/ARCHITECTURE.md + +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Architecture research") + +Agent(prompt=" +Project Research — Pitfalls dimension for [domain]. + + + +[greenfield OR subsequent] + +Greenfield: What do [domain] projects commonly get wrong? Critical mistakes? +Subsequent: What are common mistakes when adding [target features] to [domain]? + + + +What do [domain] projects commonly get wrong? Critical mistakes? + + + +- {project_path} (Project context, for the Pitfalls researcher) + + +${AGENT_SKILLS_RESEARCHER} + + +Your PITFALLS.md prevents mistakes in roadmap/planning. For each pitfall: +- Warning signs (how to detect early) +- Prevention strategy (how to avoid) +- Which phase should address it + + + +- [ ] Pitfalls are specific to this domain (not generic advice) +- [ ] Prevention strategies are actionable +- [ ] Phase mapping included where relevant + + + +Write to: {research_dir}/PITFALLS.md +Use template: ~/.claude/gsd-core/templates/research-project/PITFALLS.md + +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Pitfalls research") +``` + +(The "wait for all 4 researchers before spawning the synthesizer" orchestrator rule, the full +synthesizer `Agent()` call, and the entire #222 self-heal recovery sequence — including its log +message and closing guarantee — are all stated verbatim in the spine. A pre-existing structural +drift guard (`tests/research-agent-profiles.test.cjs`) pins them there, so nothing about this +particular sub-flow is deferred to this file.) diff --git a/gsd-core/workflows/verify-work.md b/gsd-core/workflows/verify-work.md index a32a39dc7..b9acff80c 100644 --- a/gsd-core/workflows/verify-work.md +++ b/gsd-core/workflows/verify-work.md @@ -33,6 +33,8 @@ No Pass/Fail buttons. No severity questions. Just: "Here's what should happen. D +**Compact Content Gate.** Read and follow `gsd-core/references/compact-content-gate.md` now — it states the `workflow.compact_content` check and the resolution rule this spine defers to. When it directs a Read, read `gsd-core/workflows/verify-work/detail/elaboration.md` in full before continuing past this point; its content elaborates on the resume/reconcile steps and the full gap-closure sub-flow below. + If $ARGUMENTS contains a phase number, load context: @@ -489,50 +491,20 @@ If no more tests → Go to `complete_session` -**Reconcile diagnosed gaps against completed gap-closure plans (#1921):** +**Reconcile diagnosed gaps against completed gap-closure plans (#1921):** when verify-work resumes after `/gsd:execute-phase --gaps-only`, UAT `## Gaps` entries still read `status: failed` even though their fix plans already executed — without reconciliation they'd be re-diagnosed as fresh blockers. For each `status: failed` gap with a `*-PLAN.md` whose `gap_ids` names it AND a matching `*-SUMMARY.md`, mark it `resolved` (with `resolved_by`/`resolved_at`) in place; otherwise leave it `failed`. Resolved gaps are never re-diagnosed or re-planned; a later regression gets a fresh `gap_id`, not a reopened old one. -When verify-work resumes after `/gsd:execute-phase --gaps-only`, the UAT `## Gaps` entries still read `status: failed` even though their fix plans have executed. Without reconciliation verify-work re-diagnoses them as fresh blockers and spawns new gap plans — losing the verification state. This step closes the loop. - -Read the UAT `## Gaps` section and the phase dir `*-PLAN.md` frontmatter. For each gap with `status: failed`: -1. Find a `*-PLAN.md` whose frontmatter `gap_ids` includes the gap's `gap_id` (`G-{phase}-{N}`). -2. If such a plan exists AND has a matching `*-SUMMARY.md` in the phase dir (the plan was executed by `--gaps-only`), the gap is **resolved** — update its YAML in place: - ```yaml - - gap_id: G-{phase}-{N} - status: resolved # was: failed - resolved_by: {plan basename} - resolved_at: {today} - ``` -3. If no plan references the `gap_id`, or the plan has no SUMMARY, leave the gap `status: failed` (still open). - -Read plan frontmatter directly in-context — do not pipe it through a shell parser. After reconciliation, announce: -``` -Reconciled gap-closure state: {resolved_count} gap(s) resolved by executed plans, {open_count} still open. -``` - -Resolved gaps are NOT re-diagnosed and do NOT spawn new gap plans. If the user later reports the same behavior as still broken, treat it as a new issue (a regression) with a fresh `gap_id`. +Exact YAML shape and the announcement line: `gsd-core/workflows/verify-work/detail/elaboration.md` § 1. -**Resume testing from UAT file:** - -**First run `reconcile_gaps`** (above) so gaps already fixed by `/gsd:execute-phase --gaps-only` are marked `resolved` before testing resumes (#1921). - -Read the full UAT file. +**Resume testing from UAT file:** first run `reconcile_gaps` (above), then read the full UAT file. Find first test with `result: [pending]`. If no `[pending]` test found → go to `complete_session`. -Announce: -``` -Resuming: Phase {phase} UAT -Progress: {passed + issues + skipped}/{total} -Issues found so far: {issues count} +Otherwise announce progress and continue from that test at `present_test`. -Continuing from Test {N}... -``` - -Update Current Test section with the pending test. -Proceed to `present_test`. +Exact resume-announcement wording: `gsd-core/workflows/verify-work/detail/elaboration.md` § 2. @@ -726,138 +698,29 @@ SECURITY: File paths in output are constructed from validated path components on -**Diagnose root causes before planning fixes:** - -``` ---- - -{N} issues found. Diagnosing root causes... - -Spawning parallel debug agents to investigate each issue. -``` - -- Load diagnose-issues workflow -- Follow @~/.claude/gsd-core/workflows/diagnose-issues.md -- Spawn parallel debug agents for each issue -- Collect root causes -- Update UAT.md with root causes -- Proceed to `plan_gap_closure` - -Diagnosis runs automatically - no user prompt. Parallel agents investigate simultaneously, so overhead is minimal and fixes are more accurate. +When UAT testing found issues, this sub-flow (diagnose_issues -> plan_gap_closure -> verify_gap_plans -> revision_loop) runs before present_ready; a session with zero issues never reaches it. Spawn parallel debug agents (one per issue, via diagnose-issues.md) to find root causes with no user prompt, then update UAT.md and proceed to plan_gap_closure. -**Auto-plan fixes from diagnosed gaps:** - -Display: -``` -### GSD ► PLANNING FIXES - -◆ Spawning planner for gap closure... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) -``` - -Spawn gsd-planner in --gaps mode: - - - -> **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`planner_model`, `checker_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md. - -```` -Agent( - prompt=""" - - -**Phase:** {phase_number} -**Mode:** gap_closure - - -- {phase_dir}/{phase_num}-UAT.md (UAT with diagnoses) -- {state_path} (Project State) -- {roadmap_path} (Roadmap) - - -${AGENT_SKILLS_PLANNER} - - - - -Output consumed by /gsd:execute-phase -Plans must be executable prompts. - - - -> **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md. - -**Gap linkage (#1921):** each created `*-PLAN.md` MUST list the UAT gap ids it addresses in its frontmatter: -```yaml ---- -gap_closure: true -gap_ids: [G-{phase}-{N}, ...] # the ## Gaps gap_id values this plan fixes ---- -``` -This lets `/gsd:verify-work` reconcile resolved gaps on resume (a gap whose plan has a matching `*-SUMMARY.md` is marked `status: resolved`, not re-diagnosed as a fresh blocker). - -""", - subagent_type="gsd-planner", - model="{planner_model}", - description="Plan gap fixes for Phase {phase}" -) -```` +Spawn gsd-planner in --gaps mode against the UAT (with diagnoses), `{state_path}` (Project State), and `{roadmap_path}` (Roadmap). Each created PLAN.md MUST carry `gap_closure: true` and `gap_ids: [...]` in its frontmatter (#1921) so a later verify-work resume can reconcile it. + > **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available. -On return: -- **PLANNING COMPLETE:** Proceed to `verify_gap_plans` -- **PLANNING INCONCLUSIVE:** Report and offer manual intervention +PLANNING COMPLETE proceeds to verify_gap_plans; PLANNING INCONCLUSIVE reports and offers manual intervention. -**Verify fix plans with checker:** - -Display: -``` -### GSD ► VERIFYING FIX PLANS - -◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) -``` - -Initialize: `iteration_count = 1` - -Spawn gsd-plan-checker: - -``` -Agent( - prompt=""" - - -**Phase:** {phase_number} -**Phase Goal:** Close diagnosed gaps from UAT - - -- {phase_dir}/*-PLAN.md (Plans to verify) - - -${AGENT_SKILLS_CHECKER} - - - - -Return one of: -- ## VERIFICATION PASSED — all checks pass -- ## ISSUES FOUND — structured issue list - -""", - subagent_type="gsd-plan-checker", - model="{checker_model}", - description="Verify Phase {phase} fix plans" -) -``` +Spawn gsd-plan-checker against the fix plans (iteration_count starts at 1), model="{checker_model}" (omit on inherit/empty, #2517). + > **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available. On return: - **VERIFICATION PASSED:** Proceed to `present_ready` - **ISSUES FOUND:** Count BLOCKER + WARNING entries in the YAML issues block; an entry whose severity is missing or unrecognized counts as a BLOCKER (fail closed). If zero — every entry is explicitly INFO — display `ℹ advisory — {dimension}: {description}` per entry and proceed to `present_ready`; INFO is advisory and never enters the loop (#3724). Otherwise proceed to `revision_loop` + +Exact Agent() prompt fields: `gsd-core/workflows/verify-work/detail/elaboration.md` § 2. @@ -869,26 +732,6 @@ Display: `Sending back to planner for revision... (iteration {N}/3)` Spawn gsd-planner with revision context: -``` -Agent( - prompt=""" - - -**Phase:** {phase_number} -**Mode:** revision - - -- {phase_dir}/*-PLAN.md (Existing plans) - - -${AGENT_SKILLS_PLANNER} - -**Checker issues:** -{structured_issues_from_checker} - - - - Read existing PLAN.md files. Make targeted updates to address checker issues. `required_property` + evidence + severity BIND. `fix_hint` is ONE non-binding example route: a @@ -900,14 +743,8 @@ the alternatives rather than applying or working around it. Full contract: `gsd-core/references/planner-revision.md`, which you load in revision mode. Do NOT replan from scratch unless issues are fundamental. - -""", - subagent_type="gsd-planner", - model="{planner_model}", - description="Revise Phase {phase} plans" -) -``` + > **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available. **If the planner returns `## REVISION_CONFLICT`:** do NOT increment `iteration_count` and do NOT @@ -938,9 +775,12 @@ Offer options: 2. Provide guidance (user gives direction, retry) 3. Abandon (exit, user runs /gsd:plan-phase manually) -Wait for user response. +Then wait for the user to pick one. + +Exact Agent() prompt fields (revision_context, required_reading): `gsd-core/workflows/verify-work/detail/elaboration.md` § 3. + **Present completion and next steps:** diff --git a/gsd-core/workflows/verify-work/detail/elaboration.md b/gsd-core/workflows/verify-work/detail/elaboration.md new file mode 100644 index 000000000..8f1cbc9a8 --- /dev/null +++ b/gsd-core/workflows/verify-work/detail/elaboration.md @@ -0,0 +1,230 @@ +# verify-work.md — deferred elaboration + +Read in full when `workflow.compact_content` is `false` (the default) — see +`gsd-core/references/compact-content-gate.md` for the check and resolution rule this +spine defers to. Each `§` below is the full text the spine condenses at the point it +names. + +## § 1 — reconcile_gaps + +**Reconcile diagnosed gaps against completed gap-closure plans (#1921):** + +When verify-work resumes after `/gsd:execute-phase --gaps-only`, the UAT `## Gaps` entries still read `status: failed` even though their fix plans have executed. Without reconciliation verify-work re-diagnoses them as fresh blockers and spawns new gap plans — losing the verification state. This step closes the loop. + +Read the UAT `## Gaps` section and the phase dir `*-PLAN.md` frontmatter. For each gap with `status: failed`: +1. Find a `*-PLAN.md` whose frontmatter `gap_ids` includes the gap's `gap_id` (`G-{phase}-{N}`). +2. If such a plan exists AND has a matching `*-SUMMARY.md` in the phase dir (the plan was executed by `--gaps-only`), the gap is **resolved** — update its YAML in place: + ```yaml + - gap_id: G-{phase}-{N} + status: resolved # was: failed + resolved_by: {plan basename} + resolved_at: {today} + ``` +3. If no plan references the `gap_id`, or the plan has no SUMMARY, leave the gap `status: failed` (still open). + +Read plan frontmatter directly in-context — do not pipe it through a shell parser. After reconciliation, announce: +``` +Reconciled gap-closure state: {resolved_count} gap(s) resolved by executed plans, {open_count} still open. +``` + +Resolved gaps are NOT re-diagnosed and do NOT spawn new gap plans. If the user later reports the same behavior as still broken, treat it as a new issue (a regression) with a fresh `gap_id`. + +## § 2 — resume_from_file + +**Resume testing from UAT file:** + +**First run `reconcile_gaps`** (above) so gaps already fixed by `/gsd:execute-phase --gaps-only` are marked `resolved` before testing resumes (#1921). + +Read the full UAT file. + +(The find-pending / zero-pending guard clause is stated verbatim in the spine — a pre-existing +drift guard, `tests/verify-work-auto-transition.test.cjs` bug #1716, requires the two sentences +adjacent with nothing between them.) + +Announce: +``` +Resuming: Phase {phase} UAT +Progress: {passed + issues + skipped}/{total} +Issues found so far: {issues count} + +Continuing from Test {N}... +``` + +Update Current Test section with the pending test. +Then continue to `present_test` with it. + +## § 3 — diagnose_issues, plan_gap_closure, verify_gap_plans, revision_loop (the gap-closure sub-flow) + +This whole sub-flow only runs when UAT testing found issues (`complete_session` routes here); a +session with zero issues never reaches it. + +### diagnose_issues + +**Diagnose root causes before planning fixes:** + +``` +--- + +{N} issues found. Diagnosing root causes... + +Spawning parallel debug agents to investigate each issue. +``` + +- Load diagnose-issues workflow +- Follow @~/.claude/gsd-core/workflows/diagnose-issues.md +- Spawn parallel debug agents for each issue +- Collect root causes +- Update UAT.md with root causes +- Proceed to `plan_gap_closure` + +Diagnosis runs automatically - no user prompt. Parallel agents investigate simultaneously, so overhead is minimal and fixes are more accurate. + +### plan_gap_closure + +**Auto-plan fixes from diagnosed gaps:** + +Display: +``` +### GSD ► PLANNING FIXES + +◆ Spawning planner for gap closure... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) +``` + +Spawn gsd-planner in --gaps mode: + + + +> **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`planner_model`, `checker_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md. + +```` +Agent( + prompt=""" + + +**Phase:** {phase_number} +**Mode:** gap_closure + + +- {phase_dir}/{phase_num}-UAT.md (UAT with diagnoses) +- {state_path} (Project State) +- {roadmap_path} (Roadmap) + + +${AGENT_SKILLS_PLANNER} + + + + +Output consumed by /gsd:execute-phase +Plans must be executable prompts. + + + +> **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md. + +**Gap linkage (#1921):** each created `*-PLAN.md` MUST list the UAT gap ids it addresses in its frontmatter: +```yaml +--- +gap_closure: true +gap_ids: [G-{phase}-{N}, ...] # the ## Gaps gap_id values this plan fixes +--- +``` +This lets `/gsd:verify-work` reconcile resolved gaps on resume (a gap whose plan has a matching `*-SUMMARY.md` is marked `status: resolved`, not re-diagnosed as a fresh blocker). + +""", + subagent_type="gsd-planner", + model="{planner_model}", + description="Plan gap fixes for Phase {phase}" +) +```` + +(The "stop working, wait for the subagent" orchestrator rule is stated in the spine, not repeated here.) + +On return: +- **PLANNING COMPLETE:** Proceed to `verify_gap_plans` +- **PLANNING INCONCLUSIVE:** Report and offer manual intervention + +### verify_gap_plans + +**Verify fix plans with checker:** + +Display: +``` +### GSD ► VERIFYING FIX PLANS + +◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) +``` + +Initialize: `iteration_count = 1` + +Spawn gsd-plan-checker: + +``` +Agent( + prompt=""" + + +**Phase:** {phase_number} +**Phase Goal:** Close diagnosed gaps from UAT + + +- {phase_dir}/*-PLAN.md (Plans to verify) + + +${AGENT_SKILLS_CHECKER} + + + + +Return one of: +- ## VERIFICATION PASSED — all checks pass +- ## ISSUES FOUND — structured issue list + +""", + subagent_type="gsd-plan-checker", + model="{checker_model}", + description="Verify Phase {phase} fix plans" +) +``` + +(The "stop working, wait for the subagent" orchestrator rule, and the on-return handling for +VERIFICATION PASSED / ISSUES FOUND, are stated in the spine — the ISSUES FOUND handler's exact +wording is pinned by `tests/plan-checker-coupling.test.cjs`.) + +### revision_loop + +The full conflict-handling contract (non-binding `fix_hint`, the BEFORE-editing constraint +re-check, `## REVISION_CONFLICT` routing, the same-property/THIRD-conflict stall bound, and the +max-iteration escalation) is stated verbatim in the spine — a pre-existing drift guard +(`tests/revision-remediation-binding.test.cjs`, #3771) pins it there across every revision +orchestrator in the repo. This section adds only the surrounding `Agent()` scaffolding: + +``` +Agent( + prompt=""" + + +**Phase:** {phase_number} +**Mode:** revision + + +- {phase_dir}/*-PLAN.md (Existing plans) + + +${AGENT_SKILLS_PLANNER} + +**Checker issues:** +{structured_issues_from_checker} + + + + +Read existing PLAN.md files. Make targeted updates to address checker issues. (See the spine for +the binding/non-binding contract and REVISION_CONFLICT handling stated above this point.) + +""", + subagent_type="gsd-planner", + model="{planner_model}", + description="Revise Phase {phase} plans" +) +``` diff --git a/scripts/lint-workflow-shellcheck-baseline.json b/scripts/lint-workflow-shellcheck-baseline.json index aef877e46..b0a488825 100644 --- a/scripts/lint-workflow-shellcheck-baseline.json +++ b/scripts/lint-workflow-shellcheck-baseline.json @@ -274,6 +274,26 @@ "code": "2086", "message": "Double quote to prevent globbing and word splitting." }, + { + "file": "gsd-core/workflows/complete-milestone/detail/elaboration.md", + "code": "2001", + "message": "See if you can use ${variable//search/replace} instead." + }, + { + "file": "gsd-core/workflows/complete-milestone/detail/elaboration.md", + "code": "2001", + "message": "See if you can use ${variable//search/replace} instead." + }, + { + "file": "gsd-core/workflows/complete-milestone/detail/elaboration.md", + "code": "2086", + "message": "Double quote to prevent globbing and word splitting." + }, + { + "file": "gsd-core/workflows/complete-milestone/detail/elaboration.md", + "code": "2086", + "message": "Double quote to prevent globbing and word splitting." + }, { "file": "gsd-core/workflows/debug.md", "code": "2010", @@ -369,6 +389,11 @@ "code": "2086", "message": "Double quote to prevent globbing and word splitting." }, + { + "file": "gsd-core/workflows/execute-phase/detail/elaboration.md", + "code": "2086", + "message": "Double quote to prevent globbing and word splitting." + }, { "file": "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", "code": "2016", @@ -479,6 +504,16 @@ "code": "2012", "message": "Use find instead of ls to better handle non-alphanumeric filenames." }, + { + "file": "gsd-core/workflows/new-project/detail/elaboration.md", + "code": "2012", + "message": "Use find instead of ls to better handle non-alphanumeric filenames." + }, + { + "file": "gsd-core/workflows/new-project/detail/elaboration.md", + "code": "2012", + "message": "Use find instead of ls to better handle non-alphanumeric filenames." + }, { "file": "gsd-core/workflows/new-workspace.md", "code": "2164", diff --git a/tests/fixtures/compact-content-benchmark-baseline.json b/tests/fixtures/compact-content-benchmark-baseline.json index b0808ee18..ef7d6483b 100644 --- a/tests/fixtures/compact-content-benchmark-baseline.json +++ b/tests/fixtures/compact-content-benchmark-baseline.json @@ -7,15 +7,40 @@ }, "label": "PROXY-TOKENIZER DELTA — gpt-tokenizer is a stand-in; Anthropic publishes no tokenizer for Claude 3+. The on/off COMPARISON is exact under this pinned tokenizer; absolute counts are not Claude's real token counts.", "splits": { + "complete-milestone": { + "offTokens": 12600, + "onTokens": 7760, + "reductionPct": 38.41 + }, + "docs-update": { + "offTokens": 14231, + "onTokens": 11881, + "reductionPct": 16.51 + }, + "execute-phase": { + "offTokens": 25631, + "onTokens": 23380, + "reductionPct": 8.78 + }, + "new-project": { + "offTokens": 14097, + "onTokens": 12153, + "reductionPct": 13.79 + }, "plan-phase": { "offTokens": 27637, "onTokens": 24347, "reductionPct": 11.9 + }, + "verify-work": { + "offTokens": 12112, + "onTokens": 10139, + "reductionPct": 16.29 } }, "aggregate": { - "offTokens": 27637, - "onTokens": 24347, - "reductionPct": 11.9 + "offTokens": 106308, + "onTokens": 89660, + "reductionPct": 15.66 } } diff --git a/tests/fixtures/install-tree/antigravity.json b/tests/fixtures/install-tree/antigravity.json index f0ac237e5..281f6db87 100644 --- a/tests/fixtures/install-tree/antigravity.json +++ b/tests/fixtures/install-tree/antigravity.json @@ -358,6 +358,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -379,10 +380,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -426,6 +429,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -504,6 +508,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/augment.json b/tests/fixtures/install-tree/augment.json index 30a73a900..c5f1cefa6 100644 --- a/tests/fixtures/install-tree/augment.json +++ b/tests/fixtures/install-tree/augment.json @@ -430,6 +430,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -451,10 +452,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -498,6 +501,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -576,6 +580,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/claude-local.json b/tests/fixtures/install-tree/claude-local.json index f69fbedeb..8b88b06bd 100644 --- a/tests/fixtures/install-tree/claude-local.json +++ b/tests/fixtures/install-tree/claude-local.json @@ -323,6 +323,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -344,10 +345,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -391,6 +394,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -469,6 +473,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/claude.json b/tests/fixtures/install-tree/claude.json index 4cc0fb0c5..c55e20b8d 100644 --- a/tests/fixtures/install-tree/claude.json +++ b/tests/fixtures/install-tree/claude.json @@ -358,6 +358,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -379,10 +380,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -426,6 +429,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -504,6 +508,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/cline.json b/tests/fixtures/install-tree/cline.json index d560221b9..85c714e36 100644 --- a/tests/fixtures/install-tree/cline.json +++ b/tests/fixtures/install-tree/cline.json @@ -360,6 +360,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -381,10 +382,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -428,6 +431,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -506,6 +510,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "scripts/changeset/README.md", diff --git a/tests/fixtures/install-tree/codebuddy.json b/tests/fixtures/install-tree/codebuddy.json index 0c11b577d..8af8e2566 100644 --- a/tests/fixtures/install-tree/codebuddy.json +++ b/tests/fixtures/install-tree/codebuddy.json @@ -430,6 +430,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -451,10 +452,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -498,6 +501,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -576,6 +580,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/codex.json b/tests/fixtures/install-tree/codex.json index 078b79408..1b62fc4db 100644 --- a/tests/fixtures/install-tree/codex.json +++ b/tests/fixtures/install-tree/codex.json @@ -394,6 +394,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -415,10 +416,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -462,6 +465,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -540,6 +544,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-check-update-worker.js", diff --git a/tests/fixtures/install-tree/copilot.json b/tests/fixtures/install-tree/copilot.json index b7cbaeab8..62527e2fb 100644 --- a/tests/fixtures/install-tree/copilot.json +++ b/tests/fixtures/install-tree/copilot.json @@ -359,6 +359,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -380,10 +381,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -427,6 +430,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -505,6 +509,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-session.json", diff --git a/tests/fixtures/install-tree/cursor.json b/tests/fixtures/install-tree/cursor.json index 00a5d48af..b3e099a91 100644 --- a/tests/fixtures/install-tree/cursor.json +++ b/tests/fixtures/install-tree/cursor.json @@ -358,6 +358,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -379,10 +380,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -426,6 +429,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -504,6 +508,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-cursor-post-tool.js", diff --git a/tests/fixtures/install-tree/hermes.json b/tests/fixtures/install-tree/hermes.json index e8ae05694..5d6c6b794 100644 --- a/tests/fixtures/install-tree/hermes.json +++ b/tests/fixtures/install-tree/hermes.json @@ -358,6 +358,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -379,10 +380,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -426,6 +429,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -504,6 +508,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/kilo.json b/tests/fixtures/install-tree/kilo.json index 8f1946035..6f08ecf18 100644 --- a/tests/fixtures/install-tree/kilo.json +++ b/tests/fixtures/install-tree/kilo.json @@ -430,6 +430,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -451,10 +452,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -498,6 +501,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -576,6 +580,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/kimi-code.json b/tests/fixtures/install-tree/kimi-code.json index 7db79bd6e..cda4c483c 100644 --- a/tests/fixtures/install-tree/kimi-code.json +++ b/tests/fixtures/install-tree/kimi-code.json @@ -359,6 +359,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -380,10 +381,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -427,6 +430,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -505,6 +509,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/kimi.json b/tests/fixtures/install-tree/kimi.json index 777917122..40551bbe4 100644 --- a/tests/fixtures/install-tree/kimi.json +++ b/tests/fixtures/install-tree/kimi.json @@ -395,6 +395,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -416,10 +417,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -463,6 +466,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -541,6 +545,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "scripts/changeset/README.md", diff --git a/tests/fixtures/install-tree/opencode.json b/tests/fixtures/install-tree/opencode.json index 91056c1d6..ee5eaf320 100644 --- a/tests/fixtures/install-tree/opencode.json +++ b/tests/fixtures/install-tree/opencode.json @@ -430,6 +430,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -451,10 +452,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -498,6 +501,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -576,6 +580,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/pi.json b/tests/fixtures/install-tree/pi.json index 364f12ac4..d7ee16a43 100644 --- a/tests/fixtures/install-tree/pi.json +++ b/tests/fixtures/install-tree/pi.json @@ -218,6 +218,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -239,10 +240,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -286,6 +289,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -364,6 +368,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "gsd-hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/qwen.json b/tests/fixtures/install-tree/qwen.json index 95bb924cf..2c7865ee1 100644 --- a/tests/fixtures/install-tree/qwen.json +++ b/tests/fixtures/install-tree/qwen.json @@ -358,6 +358,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -379,10 +380,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -426,6 +429,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -504,6 +508,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-agent-isolation-guard.js", diff --git a/tests/fixtures/install-tree/trae.json b/tests/fixtures/install-tree/trae.json index d8e20be28..e1857a26b 100644 --- a/tests/fixtures/install-tree/trae.json +++ b/tests/fixtures/install-tree/trae.json @@ -358,6 +358,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -379,10 +380,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -426,6 +429,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -504,6 +508,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "scripts/changeset/README.md", diff --git a/tests/fixtures/install-tree/windsurf.json b/tests/fixtures/install-tree/windsurf.json index 3a39db393..29d2d2426 100644 --- a/tests/fixtures/install-tree/windsurf.json +++ b/tests/fixtures/install-tree/windsurf.json @@ -286,6 +286,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -307,10 +308,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -354,6 +357,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -432,6 +436,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "hooks/gsd-windsurf-pre-command.js", diff --git a/tests/fixtures/install-tree/zcode.json b/tests/fixtures/install-tree/zcode.json index 16458449d..b48528e46 100644 --- a/tests/fixtures/install-tree/zcode.json +++ b/tests/fixtures/install-tree/zcode.json @@ -430,6 +430,7 @@ "gsd-core/workflows/code-review/steps/dispatch-fix.md", "gsd-core/workflows/code-review/steps/structural-pre-pass.md", "gsd-core/workflows/complete-milestone.md", + "gsd-core/workflows/complete-milestone/detail/elaboration.md", "gsd-core/workflows/complete-milestone/steps/git-tag.md", "gsd-core/workflows/debug.md", "gsd-core/workflows/diagnose-issues.md", @@ -451,10 +452,12 @@ "gsd-core/workflows/discuss-phase/templates/discussion-log.md", "gsd-core/workflows/do.md", "gsd-core/workflows/docs-update.md", + "gsd-core/workflows/docs-update/detail/elaboration.md", "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md", "gsd-core/workflows/edit-phase.md", "gsd-core/workflows/eval-review.md", "gsd-core/workflows/execute-phase.md", + "gsd-core/workflows/execute-phase/detail/elaboration.md", "gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md", "gsd-core/workflows/execute-phase/steps/completion-reconciliation.md", "gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md", @@ -498,6 +501,7 @@ "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md", "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md", "gsd-core/workflows/new-project.md", + "gsd-core/workflows/new-project/detail/elaboration.md", "gsd-core/workflows/new-project/steps/auto-mode-config.md", "gsd-core/workflows/new-project/steps/auto-mode-detection.md", "gsd-core/workflows/new-project/steps/codebase-map-offer.md", @@ -576,6 +580,7 @@ "gsd-core/workflows/update/steps/channel-banner.md", "gsd-core/workflows/validate-phase.md", "gsd-core/workflows/verify-work.md", + "gsd-core/workflows/verify-work/detail/elaboration.md", "gsd-core/workflows/verify-work/steps/automated-ui-verification.md", "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md", "scripts/changeset/README.md", diff --git a/tests/helpers/compact-content-split.cjs b/tests/helpers/compact-content-split.cjs index 20cd3db21..5860aa7b8 100644 --- a/tests/helpers/compact-content-split.cjs +++ b/tests/helpers/compact-content-split.cjs @@ -113,12 +113,31 @@ function discoverRegisteredSplits(workflowsDir = DEFAULT_WORKFLOWS_DIR) { * Ported verbatim from the pilot's `tests/plan-phase-compact-split.test.cjs`, * which this module supersedes as Phase 3's generalized version of the same * check. + * + * Bare shell block-closer/reopener keywords (#4405) are the same class of + * problem in a bash-heavy workflow like execute-phase.md: a lone `fi` or + * `done` line carries no content of its own — it is pure block-structure + * syntax that recurs once per `if`/`for`/`while` anywhere in the file. A + * split that extracts even one `if...fi` block will otherwise always collide + * with some unrelated `if...fi` block left in the spine, exactly the + * structure-not-content false positive this function exists to suppress. + * + * A bare XML-ish tag line (#4405) — ``, ``, + * `` — is the markup equivalent of the same problem: every + * workflow in this corpus repeats these tags once per step/template block, so + * any split that extracts even one such block collides with an unrelated one + * left in the spine. The tag NAME and attributes carry structure, never prose + * content, so treating the whole line as trivial is the same judgment call + * `isKnownSanctionedBoilerplate` already makes for the shared launcher line — + * generalized here since it recurs for any tag, not one specific string. */ function isTrivial(line) { if (/^`{3,}/.test(line)) return true; if (/^-{3,}$/.test(line)) return true; if (/^#+\s*$/.test(line)) return true; if (/^[A-Za-z][A-Za-z ]*:$/.test(line)) return true; // bare label lines like "Options:" + if (/^(fi|done|esac|else|then|do|\{|\})\s*;?\s*$/.test(line)) return true; // bare shell block syntax + if (/^<\/?[A-Za-z][\w-]*(\s+[^<>]*)?>$/.test(line)) return true; // bare open/close tag, alone on its own line return false; } @@ -139,11 +158,27 @@ function normalizeNonTrivialLines(content) { } /** - * Is `line` the canonical `gsd_run` launcher bootstrap preamble (see + * Known-boilerplate line prefixes (#4405), beyond the launcher preamble below: + * an exact, verbatim paragraph or call-opener this codebase repeats at every + * agent-spawn callsite across the ENTIRE corpus, not just within one file. + * `execute-phase.md`, `docs-update.md`, and `new-project.md` (at minimum) each + * spawn multiple agents and each carries its own copy of these — the same + * sanctioned-duplication shape as the launcher preamble, just keyed on a set + * of known strings instead of one. + */ +const KNOWN_BOILERPLATE_PREFIXES = [ + '> **ORCHESTRATOR RULE — CODEX RUNTIME**:', + 'Agent(prompt="', +]; + +/** + * Is `line` one of the corpus's known sanctioned cross-file duplicates: the + * canonical `gsd_run` launcher bootstrap preamble (see * `gsd-core/workflows/_runtime-launcher.snippet.sh`, - * `tests/runtime-launcher-parity.test.cjs`)? + * `tests/runtime-launcher-parity.test.cjs`), or one of the `KNOWN_BOILERPLATE_PREFIXES` + * above? * - * That other guard's OWN contract mandates exactly one inlined copy in every + * The launcher preamble's own guard mandates exactly one inlined copy in every * workflow/detail file that calls `gsd_run` — spine and detail both call it, * so both legitimately carry their own copy. That is sanctioned * cross-file duplication, not something the disjointness check should ever @@ -153,8 +188,9 @@ function normalizeNonTrivialLines(content) { * @param {string} line * @returns {boolean} */ -function isCanonicalLauncherPreamble(line) { - return line.startsWith('_GSD_SHIM_NAME="gsd-tools.cjs";'); +function isKnownSanctionedBoilerplate(line) { + if (line.startsWith('_GSD_SHIM_NAME="gsd-tools.cjs";')) return true; + return KNOWN_BOILERPLATE_PREFIXES.some((prefix) => line.startsWith(prefix)); } const PROTECTED_START = ''; @@ -411,7 +447,7 @@ function readBoundaryMoveTrailers({ baseRef, headRef = 'HEAD', cwd = REPO_ROOT, * back in after a split is made. * * The canonical `gsd_run` launcher preamble is excluded from both sides - * before comparing (`isCanonicalLauncherPreamble`) — it is sanctioned + * before comparing (`isKnownSanctionedBoilerplate`) — it is sanctioned * cross-file duplication under a different guard's contract, not a * violation of this one. * @@ -430,14 +466,14 @@ function checkDisjointness(splits) { for (const split of splits) { const spineLines = normalizeNonTrivialLines(fs.readFileSync(split.spinePath, 'utf8')) - .filter((l) => !isCanonicalLauncherPreamble(l)); + .filter((l) => !isKnownSanctionedBoilerplate(l)); const spineSet = new Set(spineLines); let reported = 0; for (const detailPath of split.detailPaths) { if (reported >= PER_SPLIT_CAP) break; const detailLines = normalizeNonTrivialLines(fs.readFileSync(detailPath, 'utf8')) - .filter((l) => !isCanonicalLauncherPreamble(l)); + .filter((l) => !isKnownSanctionedBoilerplate(l)); for (const line of detailLines) { if (reported >= PER_SPLIT_CAP) break; if (spineSet.has(line)) { @@ -544,7 +580,7 @@ module.exports = { DEFAULT_WORKFLOWS_DIR, discoverRegisteredSplits, normalizeNonTrivialLines, - isCanonicalLauncherPreamble, + isKnownSanctionedBoilerplate, extractProtectedBlocks, ACK_TRAILER_BOUNDARY_MOVE, readBoundaryMoveTrailers,