enhance(#3777): opt-in concurrent per-plan planners in chunked mode (#4346)

* test(#3777): add failing-first coverage for concurrent per-plan planner dispatch

Extracts and executes the real bash blocks this PR is about to add to
plan-phase.md and chunked-planning-mode.md (CHUNKED_PARALLEL resolution and
the BATCH_PLAN_IDS dedup guard), plus config-set/config-get coverage for the
new planning.chunked_parallel key. Expected RED against the current shipped
workflow text — the extraction anchors do not exist yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat(#3777): dispatch chunked mode's per-plan planners concurrently within a Wave

Adds opt-in planning.chunked_parallel (default false, byte-identical to the
existing serial loop). When true and the runtime's negotiated dispatch
capacity (dispatch-capacity, #3673) is greater than 1, chunked planning's
per-plan Tasks that share one outline Wave are issued together instead of
one at a time; a later Wave still waits for the current one to be verified
on disk and committed. A host with no declared maxConcurrency (most
non-Claude runtimes today) stays serial regardless of the setting.

Resolution and the Plan-ID dedup guard live in chunked-planning-mode.md
itself (gated on the section's own CHUNKED_MODE skip-check) rather than in
plan-phase.md, so a non-chunked run pays no extra gsd_run calls.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test(#3777): repoint extraction at chunked-planning-mode.md after the move

CHUNKED_PARALLEL resolution moved out of plan-phase.md into
chunked-planning-mode.md itself (see the preceding commit); update the
test's extraction path and header comment to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(#3777): relocate the canonical runtime-launcher preamble before its first use

The CHUNKED_PARALLEL resolution block's two gsd_run calls landed earlier in
the file than the sole existing preamble (in the commit step), which
tests/runtime-launcher-parity.test.cjs's (B) check requires to precede every
gsd_run call in the file. Move the preamble (not duplicate it) to the top of
the resolution block; the commit step's fenced block now just calls
gsd_run directly.

Caught by the GREEN checkpoint gsd-test run before push.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(#3777): strip the canonical preamble from the extracted resolution block

The CHUNKED_PARALLEL resolution fence now carries the relocated
runtime-launcher preamble as its first line (previous commit). Extracting
the whole fence and running it after the test's own gsd_run stub let the
embedded preamble's own resolver logic `unset -f gsd_run` and exit 1 before
reaching the resolution logic, since no real gsd-tools.cjs exists in the
temp script dir — every test calling runChunkedParallelResolution() failed.

Strip the preamble (sourced from gsd-core/workflows/_runtime-launcher.snippet.sh,
the same file scripts/sync-runtime-launcher.cjs treats as canonical) before
splicing in the stub, so this suite tests only the resolution logic it is
actually about.

Caught by the post-rebase gsd-test run before push.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(#3777): add the How-To page the phase gate requires

Enablement is 2 commands (config-set, then --chunked), which this repo's
own doc-quadrant gate flags as how-to-owed: a reference table cannot carry
a sequence. Covers enablement, the dispatch-capacity gate's honest
"most runtimes today: no effect" case, and the two accepted trade-offs.

An earlier reasoning pass (recorded in .gsd/phase/.../70-docs.json before
this commit) had incorrectly claimed #3034 shipped with no equivalent
how-to page, as precedent for skipping one here. That claim was false —
docs/how-to/enable-parallel-reviewer-lanes.md exists and is indexed. The
phase gate caught the omission before merge; corrected here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(#3777): backfill changeset PR number

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-09-05 18:57:17 -04:00
committed by GitHub
parent 1db726ebbf
commit f9f72cb54c
9 changed files with 573 additions and 16 deletions

View File

@@ -527,7 +527,7 @@ All workflow toggles follow the **absent = enabled** pattern. If a key is missin
| `workflow.plan_bounce_passes` | number | `2` | Number of sequential bounce passes to run. Each pass feeds the previous pass's output back into the validator. Higher values increase rigor at the cost of latency. Added in v1.36 |
| `workflow.post_planning_gaps` | boolean | `true` | Unified post-planning gap report (#2493). After all plans are generated and committed, scans REQUIREMENTS.md and CONTEXT.md `<decisions>` against every PLAN.md in the phase directory, then prints one `Source \| Item \| Status` table. Word-boundary matching (REQ-1 vs REQ-10) and natural sort (REQ-02 before REQ-10). Non-blocking — informational report only. Set to `false` to skip Step 13e of plan-phase. |
| `workflow.plan_review_convergence` | boolean | `false` | Enable the `/gsd-plan-review-convergence` command. Disabled by default — the command exits with an enable instruction when this key is `false`. The command automates the manual plan→review→replan loop: it spawns configured reviewers (Codex, Gemini, Claude, OpenCode, Ollama, LM Studio, llama.cpp), counts unresolved HIGH concerns and actionable MEDIUM/LOW findings via the CYCLE_SUMMARY contract, replans with `--reviews` feedback, and repeats until converged or max cycles reached. Enable with `gsd config-set workflow.plan_review_convergence true`. Added in v1.39 |
| `workflow.plan_chunked` | boolean | `false` | Enable chunked planning mode. When `true` (or when `--chunked` flag is passed to `/gsd-plan-phase`), the orchestrator splits the single long-lived planner Task into a short outline Task followed by N short per-plan Tasks (~3-5 min each). Each plan is committed individually for crash resilience. If a Task hangs and the terminal is force-killed, rerunning with `--chunked` resumes from the last completed plan. Particularly useful on Windows where long-lived Tasks may hang on stdio. Added in v1.38 |
| `workflow.plan_chunked` | boolean | `false` | Enable chunked planning mode. When `true` (or when `--chunked` flag is passed to `/gsd-plan-phase`), the orchestrator splits the single long-lived planner Task into a short outline Task followed by N short per-plan Tasks (~3-5 min each). Each plan is committed individually for crash resilience. If a Task hangs and the terminal is force-killed, rerunning with `--chunked` resumes from the last completed plan. Particularly useful on Windows where long-lived Tasks may hang on stdio. See [`planning.chunked_parallel`](#planning-settings) to dispatch the per-plan Tasks concurrently instead of one at a time. Added in v1.38 |
| `workflow.code_review_command` | string | (none) | Shell command for external code review integration in `/gsd-ship`. Receives changed file paths via stdin. Non-zero exit blocks the ship workflow. Added in v1.36 |
| `workflow.tdd_mode` | boolean | `false` | Enable TDD pipeline as a first-class execution mode. When `true`, the planner aggressively applies `type: tdd` to eligible tasks (business logic, APIs, validations, algorithms) and the executor enforces RED/GREEN/REFACTOR gate sequence. An end-of-phase collaborative review checkpoint verifies gate compliance. Added in v1.36 |
| `workflow.mvp_mode` | boolean | `false` | Persist the MVP-mode flag in config so every phase defaults to MVP framing without requiring `--mvp` on the CLI. Resolved via the precedence chain: `--mvp` CLI flag → ROADMAP.md `**Mode:** mvp` field → this config value → `false`. When `true`, the planner, executor, verifier, and discovery surfaces treat the phase as an MVP vertical slice (UI → API → DB) of one user-visible capability instead of a horizontal layer. |
@@ -664,6 +664,47 @@ The following combinations of `mode`, `granularity`, `model_profile`, and workfl
| `planning.pr_strict` | boolean | `false` | Filter mode for [`/gsd-pr-branch`](COMMANDS.md#gsd-pr-branch). `false` — the generated PR branch keeps structural planning state (`STATE.md`, `ROADMAP.md`, `MILESTONES.md`, `PROJECT.md`, `REQUIREMENTS.md`, `milestones/**`) and drops the transient subdirectories. `true` — every `.planning/` path is dropped, structural files included, and a commit is carried over only when it touches at least one file outside `.planning/`. Applies to the root repository's PR branch only; `planning.sub_repos` companion branches are unaffected |
| `planning.search_gitignored` | boolean | `false` | Add `--no-ignore` to broad searches to include `.planning/` |
| `planning.sub_repos` | array of strings | `[]` | Paths of nested sub-repos relative to the project root. When set, GSD-aware tooling scopes phase-lookup, path-resolution, and commit operations per sub-repo instead of treating the outer repo as a monorepo |
| `planning.chunked_parallel` | boolean | `false` | Opt-in concurrent per-plan planners in chunked mode. See [Concurrent per-plan planners in chunked mode](#concurrent-per-plan-planners-in-chunked-mode-3777) below. |
### Concurrent per-plan planners in chunked mode (#3777)
[`workflow.plan_chunked`](#workflow-toggles) splits a phase's planning into a short outline Task
followed by N short per-plan Tasks, committing each plan individually for crash resilience. By
default those per-plan Tasks still run **one at a time** — a phase with 6 plans at ~3-5 minutes
each pays roughly the sum of their runtimes, even though each plan writes a disjoint
`{plan_id}-PLAN.md` file with no data dependency on its siblings.
```json
{
"planning": {
"chunked_parallel": true
}
}
```
```bash
gsd config-set planning.chunked_parallel true
/gsd-plan-phase 3 --chunked
```
When `true`, the runnable per-plan planners that share one outline Wave are dispatched together
(one message, `run_in_background=true` on each) instead of one at a time; a later Wave still waits
for every plan in the current Wave to be verified on disk and committed, honoring the outline's
Wave column as the schedule. `Depends On` itself is not separately parsed — it is expected to name
only a plan in an earlier Wave, so batching strictly by Wave already respects it; a same-Wave
`Depends On` would be a defect in the outline, not something this dispatch mechanism detects.
**This is gated, not unconditional.** Concurrent dispatch only fires when the runtime's negotiated
dispatch capacity (`gsd-tools query dispatch-capacity`, #3673) is greater than `1`. A runtime that
declares no `maxConcurrency` — most non-Claude runtimes today — resolves to the fail-closed floor
of `1` and stays serial regardless of this setting, exactly like the pre-#3777 loop. Claude Code
declares a capacity of 20, so this setting has an effect there out of the box.
**Trade-offs accepted by this setting.** Per-plan commits interleave within a batch instead of
landing strictly one-at-a-time, and a stalled plan's retry no longer blocks sibling plans in the
same batch that already completed and committed — a mid-batch interrupt leaves whichever plans
finished first already committed, rather than stopping after the last plan in strict outline
order. Default `false` keeps the original serial behavior byte-for-byte.
### Project-Root Resolution in Multi-Repo Workspaces

View File

@@ -47,6 +47,7 @@ Language versions: [English](README.md) · [Português (pt-BR)](pt-BR/README.md)
- [Verify a dependency-compatibility claim](how-to/verify-a-dependency-compatibility-claim.md) — act on a compatibility claim the researcher left `[ASSUMED]`, and tell "nothing declared" apart from "a constraint is declared" and "the lookup failed"
- [Execute a phase](how-to/execute-a-phase.md) — run plans in parallel waves with fresh-context subagents
- [Enable parallel reviewer lanes](how-to/enable-parallel-reviewer-lanes.md) — cut a multi-reviewer `/gsd-review` pass toward its slowest lane, and tell a rate-limited lane apart from one that was never selected
- [Enable concurrent per-plan planners in chunked mode](how-to/enable-concurrent-chunked-planning.md) — dispatch chunked `/gsd-plan-phase`'s per-plan Tasks together within one outline Wave instead of one at a time, and know when the setting has no effect
- [Verify and ship](how-to/verify-and-ship.md) — walk through completed work, diagnose failures, and create the PR
- [Catch complexity before it compounds](how-to/act-on-a-refactor-proposal.md) — enable the post-execute refactor hook, read a proposal's score vs. anchor delta, and accept or decline it
- [Run phases autonomously](how-to/run-phases-autonomously.md) — use autonomous mode for unattended phase execution

View File

@@ -0,0 +1,80 @@
# How to enable concurrent per-plan planners in chunked mode
Speed up a multi-plan phase's chunked planning run by letting independent per-plan Tasks run at
the same time instead of one after another.
---
## Before you start
This only matters if you already use chunked mode (`workflow.plan_chunked: true`, or
`/gsd-plan-phase {N} --chunked`). Chunked mode splits planning into a short outline Task followed
by one short Task per plan, committing each plan individually so an interrupted run resumes from
the last committed plan. By default those per-plan Tasks still run one at a time.
## Enable it
```bash
gsd config-set planning.chunked_parallel true
```
```json
{
"planning": {
"chunked_parallel": true
}
}
```
Then run chunked planning as usual:
```bash
/gsd-plan-phase {N} --chunked
```
That's it — no other setting to touch, and no other capability's config is involved.
## What actually changes
Nothing changes for a phase whose outline has only one plan per Wave — there is nothing to run
concurrently. For a phase with several plans in the same Wave, those plans' Tasks are now issued
together instead of one at a time; a later Wave still waits for the current one to finish and
commit before starting, so cross-Wave ordering is unaffected.
## Whether it actually does anything on your runtime
This setting is gated on the runtime's own negotiated concurrency ceiling
(`gsd-tools query dispatch-capacity`), not on the config value alone:
- **Claude Code** declares a capacity above 1, so turning this on has a real effect there.
- **Every other runtime today** (Codex, Cursor, OpenCode, ...) declares no concurrency ceiling and
falls back to the safe floor of `1` — turning this setting on has no effect there; chunked
planning stays exactly as serial as it was before. This is not a bug to work around: it means
the setting never fires concurrent dispatch on a host that cannot usefully run it.
There is no per-runtime flag to set yourself — the gate is automatic and always correct for the
runtime you're on.
## Trade-offs to know before turning this on
- **Per-plan commits interleave.** With serial dispatch, plans commit strictly in outline order.
With concurrent dispatch, whichever plans in a batch finish first commit first — still one
commit per plan, never a combined commit, but not necessarily in outline order within a batch.
- **Crash-resume granularity is coarser.** If a run is interrupted mid-batch, every plan that
already finished and committed stays committed — but "already finished" is no longer
necessarily "everything up to the last plan in outline order," the way serial mode guarantees.
Resuming the run picks up exactly where it left off either way; only the *shape* of what's
already on disk when you resume can differ.
If either of those matters more to you than the speed-up, leave this setting at its default
(`false`) — chunked mode's crash resilience story is otherwise unchanged.
## Turn it off again
```bash
gsd config-set planning.chunked_parallel false
```
Or just remove the key from `.planning/config.json` — `false` is the default.
See also: [Configuration reference — Concurrent per-plan planners in chunked mode](../CONFIGURATION.md#concurrent-per-plan-planners-in-chunked-mode-3777).