Skip to content

feat(code-review): parallelize GitHub-mode reviewer/verifier fleets - #178

Merged
wongk merged 2 commits into
mainfrom
feat/code-review-parallel-github-fleet
Jul 10, 2026
Merged

feat(code-review): parallelize GitHub-mode reviewer/verifier fleets#178
wongk merged 2 commits into
mainfrom
feat/code-review-parallel-github-fleet

Conversation

@wongk

@wongk wongk commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Problem

Shallow code reviews take ~30 minutes in GitHub CI. Root cause (traced from a real CI log): GitHub/headless mode dispatched the reviewer fleet (stage_20) and verifier fleet (stage_23) synchronously, one agent at a time, so wall-clock = SUM of every agent instead of MAX. A shallow run's 3 reviewers ran serially (~6m + ~10.5m + ~6m ≈ 22.5m) plus 2 serial verifiers (~5m) ≈ 30m. Local reviews don't have this because they spawn the fleet as parallel background tasks.

The synchronous rule was correct once: headless claude -p used to terminate before outstanding background subagents finished. That constraint is stale.

Why it's safe now (verified)

Per the Claude Code headless docs: since v2.1.182, claude -p "waits for [background subagents] to complete" because their output is part of the final result (10-min-per-agent cap, tunable via CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS). A blocking TaskOutput is a pending synchronous call, so the turn never ends with a reviewer outstanding. claude-code-action@v1 already ships v2.1.206.

Change

Unify both modes on the local parallel pattern — spawn every standard-flow reviewer/verifier with run_in_background: true, collect all with blocking TaskOutput before advancing. Fleet stages now take the slowest agent, not the sum → roughly halves shallow-tier CI review time (deep tier gains more).

  • skills/spawn-reviewers/SKILL.md — unified Spawn + Collection Contract; headless note rewritten for v2.1.182+ await; legacy synchronous fallback documented for CLI < v2.1.182; wait-loop substitutes (watcher files / sleep / polling) still banned.
  • skills/verify-findings/SKILL.md — explicit parallel-in-both-modes dispatch (stops the orchestrator serializing verifiers).
  • commands/start.md — stage-20 walker guard reframed around blocking TaskOutput collection.
  • test_code_review_helpers.py — replaced the FEA-2162 synchronous-GitHub test class with the parallel-dispatch contract (11 tests).

Validation

  • Full code-review suite: 1308 passed, 3 skipped; ruff + pyright clean.
  • Prose/skill change (no runtime code path); the real validation is a live CI run — see the companion changes below.

⚠️ Companion changes required in the consuming CI workflow

The plugin change alone isn't enough — the GitHub Actions workflow that runs /code-review must also:

  1. Add TaskOutput to claude_args' --allowedTools (currently only Task is allowed → the collection call would be denied).
  2. Set CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS above 10 min (e.g. 1500000, or 0) — a single reviewer already ran 10.5m serially, so a slow parallel reviewer could otherwise be orphaned at the 10-min default.

Recommend validating on one real CI review before relying on the speedup.

🤖 Generated with Claude Code

wongk and others added 2 commits July 10, 2026 09:06
GitHub/headless reviews dispatched the reviewer fleet (stage_20) and
verifier fleet (stage_23) synchronously one agent at a time, so CI
wall-clock was the SUM of every agent — a shallow review with 3 serial
reviewers + 2 serial verifiers took ~30 min. The synchronous rule
existed because headless `claude -p` used to terminate before background
subagents finished. As of Claude Code v2.1.182+ (shipped by
claude-code-action@v1), `claude -p` waits for background subagents to
complete because their output is part of the final result, so a blocking
TaskOutput keeps the turn alive until reviewers finish.

Unify both modes on the local parallel pattern: spawn every standard-flow
reviewer/verifier with run_in_background: true and collect all with
blocking TaskOutput before advancing. Fleet stages now take the SLOWEST
agent, not the sum — roughly halving shallow-tier CI review time. A
synchronous one-at-a-time fallback stays documented for Claude Code
< v2.1.182, and CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS can raise the 10-min
per-agent wait cap for long reviewers.

Updates spawn-reviewers + verify-findings skills and start.md; replaces
the FEA-2162 synchronous-GitHub test class with the parallel-dispatch
contract. Full code-review suite green (1308 passed), ruff + pyright
clean. Bump 3.4.1 -> 3.5.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ncern

Address deep-review findings on the parallel-fleet contract:

- The "Legacy fallback (Claude Code < v2.1.182)" branch was written as if the
  orchestrating agent picks between parallel and synchronous dispatch at runtime,
  but the agent cannot introspect its own CLI version from inside the prompt, so
  the fallback was an unreachable branch. Reframe it in spawn-reviewers/SKILL.md
  and start.md as an operator/environment concern (claude-code-action@v1 ships
  >= v2.1.182, so CI is always parallel; an operator on an older CLI must edit
  the skill), not a path the live agent resolves.
- Add the matching legacy-fallback note to verify-findings/SKILL.md so the
  reviewer and verifier fleets document the same degradation on older CLIs.
- Scope the CHANGELOG entry: only stage_20 removed a synchronous-GitHub branch;
  stage_23 was already background-dispatched in both modes and is now only
  documented explicitly. Drop the inflated "halving" estimate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wongk
wongk merged commit d7362f0 into main Jul 10, 2026
5 checks passed
@wongk
wongk deleted the feat/code-review-parallel-github-fleet branch July 10, 2026 15:39
peterulsteen added a commit that referenced this pull request Jul 14, 2026
…nous headless dispatch (FEA-3085) (#179)

* fix(code-review): revert #178 parallel GitHub fleet; restore synchronous headless dispatch (FEA-3085)

Reverts d7362f0 (v3.5.0, #178) and bumps 3.4.1 -> 3.6.0.

#178 unified GitHub and local reviewer-fleet dispatch (stage_20) on
run_in_background: true + blocking TaskOutput, on the premise that
headless `claude -p` (Claude Code >= v2.1.182) reliably awaits background
subagents as long as the orchestrator issues the blocking collection.

Empirically the orchestrator does NOT reliably issue it. In ~18% of
symphony-alpha claude-code-review.yml pull_request runs on 2026-07-13
(9/50, up from 1/10 the day #178 went live) it launched the fleet
(Bug Hunter A/B, Unified Auditor) as background agents and then ended its
turn ('I'll wait for the harness to notify me') with no pending
synchronous tool call, so claude -p exited (terminal_reason: completed)
before stage_21_collect_findings..stage_30_footer ran. No findings were
collected, no .closedloop-ai/code-review-* artifacts were written, and the
workflow's 'Validate migrated review artifacts' guard failed the job.
Runs that took the synchronous fast-path branch were unaffected, which is
why the failure was intermittent.

Restore the known-good split: GitHub/headless standard flow dispatches
reviewers synchronously one-at-a-time (run_in_background: false, wait for
each 'DONE findings=N file=...' before the next), which keeps the turn
alive by construction rather than depending on the model choosing to
issue a blocking TaskOutput; local mode keeps parallel background dispatch
+ blocking TaskOutput. Restores the FEA-2162 synchronous-GitHub reviewer
contract and its test class. Trade-off: GitHub review wall-clock returns
to sum-of-reviewers -- correctness over speed for a merge-adjacent gate.

Full code-review suite green (2079 passed, 3 skipped), ruff + pyright clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(code-review): restore v3.5.0 changelog entry; add v3.6.0 above it (not replace)

Address review: v3.5.0 shipped to main, so its changelog block must stay
(entries are newest-first + treated as released on merge). Put v3.6.0 on
top of the retained v3.5.0 Changed block instead of deleting it. Also note
in v3.6.0 that stage_23 verifiers are unchanged and their headless
exposure is tracked in FEA-3154.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant