docs/adr/0011-review-default-reviewers-prd.md
review.default_reviewers config key for /gsd-review reviewer selection#30790011-review-default-reviewers.mdThis PRD is filed alongside its ADR under
docs/adr/for co-location. The repo does not yet have adocs/prd/directory; if maintainers prefer one, this file can move there with the0011-prefix preserved.
/gsd-review with no flags fans out to every detected CLI reviewer (Claude, Codex, Cursor, Gemini, OpenCode, plus local model servers such as ollama, lm-studio, llama.cpp). For users with many backends installed, this wastes wall-clock on timeouts and burns tokens on reviewers they don't want for routine work. Add a review.default_reviewers key under the existing review.* namespace in .planning/config.json that scopes the no-flag default to a user-chosen subset. Absent key preserves today's behavior. --all and individual flags continue to work unchanged. Follows GSD's absent = enabled convention.
GSD's /gsd-review workflow treats "no flags" as "invoke every CLI we can detect" (workflows/review.md line 52). That default is fine at install time — it makes the feature discoverable — but it's the wrong default for any user who has accumulated multiple reviewer CLIs plus local model servers. Each review probes up to ~10 backends, including ones that are slow, expensive, redundant for the change at hand, or not actually running (timeout waits on ollama, lm-studio, llama.cpp when the daemon is off).
The only existing workaround is editing workflows/review.md in place. That patch gets clobbered on every /gsd-update, requiring /gsd-update --reapply to restore. There is no machine-readable record of the user's intent — every machine the user works on needs the same patch reapplied. The issue reporter (#3079) and presumably others are paying a "tax" on every review that is purely a default-selection problem.
This is a small change with broad reach: it lands in a hot-path workflow that power users run many times per day.
/gsd-review on multi-CLI machines (target: ≥40% reduction for users with ≥4 detected CLIs).review.models.*, review.*_host, absent = enabled, namespacing under review.*) so users don't have to learn a new pattern.--all semantics or the individual flag set (--gemini, --codex, --cursor, …). They keep their current meaning.review.* namespace./gsd-settings / /gsd-config --integrations if maintainers choose to support it later) is sufficient.A developer who has installed multiple coding CLIs (e.g., Claude Code, Codex, Gemini CLI, Cursor, OpenCode, Kilo) plus one or more local inference servers. They run /gsd-review frequently — sometimes dozens of times per day during a sprint — and have a stable mental model of which 1–3 reviewers actually add signal for their day-to-day work.
Has Claude + ollama installed. Wants reviews from Claude every time, and from ollama only when explicitly asked. Today, every /gsd-review pays the ollama timeout cost when ollama isn't running.
Routine reviews should hit cheap/local reviewers; pre-merge reviews should hit the expensive ones via --all or explicit flags. Wants a config-level expression of "the cheap subset is my default."
review.default_reviewers: ["gemini", "codex"].["claude"]./gsd-review --all.Grouped by persona, ordered roughly by frequency.
Multi-CLI power user
/gsd-review doesn't probe backends I don't use./gsd-update so I don't have to keep re-patching workflows/review.md.--all to still work so I can opt into a full review pre-merge without un-setting my config.--gemini, --cursor, …) to keep working regardless of my default so ad-hoc runs aren't constrained by the default.Single-CLI user with sometimes-on local server
Cost-sensitive team lead
.planning/config.json to the repo so everyone on the team gets the same review defaults.New user
Edge cases
review.default_reviewers is string[]. Each element validates against the existing slug pattern ^[a-zA-Z0-9_-]+$. Schema parser accepts the key; rejects non-array or non-string-element values with a clear error. Empty array [] behavior is decided per Q-1.["gemini", "codex"] and both are detected, running /gsd-review invokes only Gemini and Codex./gsd-review runs every detected reviewer, identical to today.--all overrides the config. Given the key is ["gemini"], running /gsd-review --all invokes every detected reviewer. Verbose mode shows which reviewers came from --all vs. the default.["gemini"], running /gsd-review --cursor invokes only Cursor. Running /gsd-review --gemini --codex invokes exactly those two regardless of the default.docs/CONFIGURATION.md gets a review.* subsection (or an extension of an existing one) covering the new key. workflows/review.md references the key in the no-flag branch. Schema example at the top of docs/CONFIGURATION.md includes the key./gsd-config --integrations extends to set review.default_reviewers interactively, aligning with the existing interactive config flow for reviewers.--no-default flag that runs the full detected set without --all semantics — slightly different intent expression. Drop if equivalent to --all (Q-2)./gsd-review output: Running reviewers (default): gemini, codex (set in .planning/config.json).GSD_REVIEW_DEFAULT=...) for CI scenarios where mutating config.json is undesirable.review.profiles.frontend: ["claude", "cursor"]). The shape of default_reviewers is deliberately chosen not to foreclose this — a future review.profiles.* map can coexist.review.groups.cheap = ["ollama", "gemini-flash"]). Same — leave room under review.*.--gemini, --codex, --cursor, …) — always win.--all — full detected set, ignores config.review.default_reviewers in config — subset, intersected with detected set.This matches the principle of least surprise: explicit user input (flags) always wins over persisted preference (config), and persisted preference only fills the gap when the user hasn't said anything else.
detected = detect_clis() # unchanged
if any individual flag passed:
selected = flags_to_set(flags) ∩ detected
elif --all:
selected = detected
elif config.review.default_reviewers is set:
valid = filter(config.review.default_reviewers, is_known_slug)
# warn on each invalid slug
selected = valid ∩ detected
# info on each valid-but-undetected slug
if selected is empty:
error with actionable message # see Q-1
else:
selected = detected # today's behavior
^[a-zA-Z0-9_-]+$ (already in use for review.models.<cli>)./gsd-review start identifying the source of selection (default config / --all / explicit flags / no config). Surfaced under --verbose per Q-5..planning/config.json files containing review.default_reviewers (only countable if/when GSD ever ships opt-in telemetry; otherwise qualitative via Discussions).#3079 and zero new issues reporting the same wipe-on-update problem within 60 days.workflows/review.md defaults within 60 days./gsd-review on machines with ≥4 detected CLIs (self-reported or telemetry). Target: ≥40% reduction for users who opt in.GSD doesn't ship usage telemetry today. Most of these metrics rely on qualitative signal: issue activity, Discussion threads, and a follow-up on #3079. That's appropriate for a config addition of this size — we don't need a metrics pipeline to validate it.
[] → schema error per proposed Q-1 resolution. Message: review.default_reviewers is empty; remove the key to use the default-all behavior or list at least one reviewer.All configured default reviewers are missing on this host: [...]. Install at least one, or pass --all / specific flags.--all and individual flags together → existing behavior preserved; this change does not alter that interaction. Confirm in tests.review.default_reviewers: [] be a schema error, or should it fall back to "all detected"? Proposal: schema error. Blocking. Affects schema validation and tests.--no-default flag. Is this meaningfully different from --all? Proposal: drop unless implementation surfaces a concrete difference./gsd-config --integrations integration. Land in this pass or as a fast follow? Proposal: fast follow; depends on contributor bandwidth.--verbose? Proposal: --verbose only.resolve_model_ids: "omit" pattern? Proposal: none expected — the change operates on detection, not runtime; add at least one non-Claude integration test to confirm.This is a small, additive, non-breaking change. No migration is required.
--all and individual flags still behave.#3079; show the two-line config example.#3079 and any new issues mentioning "default reviewers" or "review.md patch" for 60 days./gsd-config --integrations integration) if there's contributor bandwidth.--all or individual flag semantics.{
"review": {
"default_reviewers": ["gemini", "codex"]
}
}
With this config, /gsd-review invokes only Gemini and Codex. /gsd-review --all invokes every detected reviewer. /gsd-review --cursor invokes only Cursor.
detect_clis finds on the current host at review time.gemini, codex).review.default_reviewers means "all detected."#3079docs/CONFIGURATION.md — review.models.<cli>, review.*_host, and the absent = enabled patternworkflows/review.md (line 52)0011-review-default-reviewers.md