Critique — Parallel Rubric-Based Code Critique
Fan out parallel Explore agents to assess a codebase across multiple rubrics, then synthesize their findings into a unified critique document.
You ARE the orchestrator. Follow these phases in order.
Setup
Resolve script paths once before starting:
LIB=~/.claude/skills/lib/claude-lib.sh
RUBRICS=~/.claude/skills/critique/scripts/rubrics.sh
Argument Parsing
Parse the user's invocation. Arguments can appear in any order:
| Argument | Form | Default |
|---|---|---|
| target | Bare path (first non-flag argument) | . (current directory) |
--rubrics |
Comma-separated names or themes | auto-selected |
--depth |
light, medium, or deep |
medium |
--skip-negotiation |
Flag (no value) | false |
--output |
inline or file:<path> |
inline |
--max-agents |
Integer | 12 |
Phase 0: Survey
Run the survey script on the target:
bash "$LIB" survey <target>
Read the JSON output. This is your project profile — file counts by extension, line counts, directory tree, root filenames, and git stats. Use it to understand the project's shape, but do NOT show it to the user unless they ask.
If the project is very small (under 10 files or under 500 total lines), note this — you may want to reduce rubric count in Phase 1.
Phase 1: Rubric Negotiation
Skip this phase if --skip-negotiation was passed. Also skip by default if the survey shows a single contributor and fewer than 200 source files — the auto-selected rubrics are usually correct for small/solo projects. The user can override with --negotiate to force the negotiation step.
Step 1: Load rubrics
bash "$RUBRICS" list --json
This returns all rubrics from three tiers (built-in, user, project) merged with last-wins deduplication.
Step 2: Select applicable rubrics
Read each rubric's when field and the survey output. Select 8-12 rubrics that apply to this project. For example:
- "always" rubrics are always included
- "typed languages" applies if the survey shows
.ts,.rs,.go,.javafiles - "web applications" applies if root_files suggest a web framework
For each selected rubric, fill in a scope — the specific files and directories the agent should read. Use the survey's directories and root_files to assign scope. Be specific: name files, not just directories. Minimize overlap between rubrics.
Set each rubric's difficulty from the catalog default, overridden by the --depth flag if provided.
If --rubrics was specified, match the provided names/themes against the catalog and select those. Add scope and difficulty as usual.
Step 3: Present to user
Present the rubrics as a numbered list:
I've selected N rubrics for this project:
1. **Architecture** (medium) — src/, lib/
- Is there a clear, consistent structure?
- Are responsibilities well-separated?
...
2. **Error Handling** (medium) — src/api/, src/models/
...
Then ask: "Confirm these rubrics, or tell me which to drop, add, reweight, or rescope."
Wait for user confirmation before proceeding.
If the user adds a novel rubric that isn't in the catalog, offer to save it:
bash "$RUBRICS" add "<name>" --tier user --questions "q1;q2;q3" --when "<condition>" --difficulty <level>
Small projects
If the survey shows under 10 source files, reduce to 4-6 rubrics. Merge related rubrics (e.g., "Type System" + "Architecture" into "Design").
Phase 2: Dispatch & Collection
Fan out one agent per confirmed rubric using the Workflow tool, and collect their structured findings in one call.
Step 1: Build the fan-out script
Call Workflow with an inline script and pass the confirmed rubrics as args:
export const meta = {
name: 'critique-fanout',
description: 'Fan out rubric critiques and collect structured findings',
phases: [{ title: 'Critique' }],
}
const FINDINGS_SCHEMA = {
type: 'object',
properties: {
critique: { type: 'string' },
findings: {
type: 'array',
items: {
type: 'object',
properties: {
file: { type: 'string' },
line: { type: 'number' },
observation: { type: 'string' },
type: { type: 'string', enum: ['strength', 'weakness'] },
},
required: ['file', 'observation', 'type'],
},
},
},
required: ['critique', 'findings'],
}
const results = await parallel(args.rubrics.map(r => () => {
const opts = { label: r.name, agentType: 'Explore', schema: FINDINGS_SCHEMA }
if (r.model) opts.model = r.model
if (r.effort) opts.effort = r.effort
return agent(r.prompt, opts).then(result => result && ({ rubric: r.name, ...result }))
}))
return results.filter(Boolean)
Pass args: { rubrics: [{name, prompt, model?, effort?}, ...] } — one entry per confirmed rubric, where prompt is built from the template below and model/effort are set per the rule below.
Model selection
Set model and effort per rubric using your own judgment of its difficulty — not mechanically off --depth alone. Depth measures how much to read; it doesn't measure how hard the judgment is once read. A narrow-scope rubric (e.g., error handling across 3 files) can still demand subtle cross-file reasoning, while a wide-scope one can be mechanical throughout.
- Default: omit both fields. This is correct for most rubrics — the session's default model/effort is the safe fallback.
- Set
model: "haiku"only when you're confident the rubric is genuinely mechanical and low-stakes even after accounting for what it'll actually find — pure style/naming/formatting checks, not just a small file count. - Set
effort: "high"when the rubric demands nuanced judgment or covers high-stakes territory — security, concurrency, public API surface, error semantics — regardless of depth.
Bias toward omitting model over downgrading it: a wrong downgrade silently produces a shallower critique with no signal anything went wrong, while a wrong effort bump just costs more for no harm.
Agent prompt template
For each rubric, build the prompt string with exactly these sections:
Context: {2-3 sentences about what this project is, derived from the survey. Include the root path, primary languages, and approximate size.}
Rubric: {Name} Evaluate this codebase against the following questions:
- {question 1}
- {question 2}
- {question 3}
Scope: Read these files in order: {explicit file paths}. {If medium/deep: then explore related files — follow imports, check tests.}
Depth: {light: read 3-5 files | medium: read 8-15 files | deep: read exhaustively, follow import chains, check tests}
Write a
critique(under 600 words) — be specific, cite at least 5 file:line references, note both strengths and weaknesses. Do not include preamble or summarize the project. Do not suggest fixes — critique only. List every cited observation infindingstoo, so it survives cross-rubric deduplication.
Dispatch rules
- NEVER include more than
--max-agents(default 12) rubrics inargs.rubrics. If you have more, merge the lowest-priority ones first. - NEVER ask an agent to fix anything. Critique only.
- Minimize scope overlap. If two rubrics need to read the same file, that's fine — but they shouldn't have identical scope lists.
- Give explicit file paths in the scope, not vague instructions like "look at the codebase."
Step 2: Dispatch and wait
Call Workflow with the script and args. Tell the user briefly: "Dispatched N agents. I'll compile results once they report back." The call runs in the background — wait for its task notification, then read the returned array before moving to Phase 3.
Handling failures
parallel()resolves a failed rubric tonull; the script already filters these out, so any rubric missing from the returned array is incomplete.- NEVER re-launch a failed agent. Work with what you have.
Phase 3: Synthesis
Once all agents have reported (or all remaining are marked incomplete), synthesize in two steps.
Step 1: Consolidate
Before writing the final document, consolidate the raw agent outputs:
- Extract structured findings. Each result from Phase 2 already has a structured
findingsarray in the shape{file, line, observation, type}— concatenate all rubrics' arrays directly, tagging each entry with itsrubricname (already present on each result object). No text parsing needed. - Deduplicate. Pipe the findings through the group command:
This groups findings by file, merges rubric attribution, and flags files appearing in 3+ rubrics. The LLM then reviews each group to merge findings with different wording but the same underlying issue.echo '<findings_json>' | bash "$LIB" group --key file --merge rubrics --threshold 3 - Identify cross-cutting themes. The
above_thresholdoutput from the group command identifies files with systemic issues. Each is a candidate cross-cutting theme. - Draft ratings. Assign a preliminary rating per rubric based on the agent's findings (see calibration guide below).
This step is internal — do not output the consolidation to the user. It feeds Step 2.
Step 2: Write
Produce the unified critique document from the consolidated findings.
Document structure
# Critique: {project name}
## Executive Summary
{3-5 sentences. Overall assessment — what's strong, what's weak,
and the single most important thing to address.}
## Assessments
### {Rubric Name} — {Rating}/10
{Edited critique — 200-400 words. Preserve file:line citations.
Remove redundant preamble from agent output.}
**Key findings:**
- {finding}
- {finding}
### ...
{Repeat for each rubric}
## Cross-Cutting Themes
{Patterns that appeared in 3+ rubric assessments — these are systemic issues.}
- **{Theme}:** surfaced in {Rubric A}, {Rubric B}, {Rubric C}. {What to do about it.}
## Recommendations
{Priority-ordered, actionable, scoped. Not "improve error handling" but
"Add context fields to ApiError in src/errors.rs and propagate them through the handler chain."}
1. {Highest-impact fix} — affects {rubrics X, Y, Z}
2. ...
3. ...
## Scorecard
| Rubric | Rating | Key Issue |
|--------|--------|-----------|
| {name} | {N}/10 | {one-line summary} |
| ... | ... | ... |
| **Overall** | **{weighted average}/10** | |
Synthesis rules
- Edit, don't parrot. Rewrite agent output for consistency, brevity, and a unified voice. Remove duplicate observations across rubrics.
- Consolidate cross-cutting issues. If two or more agents identified the same problem (e.g., "no error context in API responses" appeared in Error Handling and API Design), consolidate it under Cross-Cutting Themes and reference it briefly in each rubric's assessment.
- Assign ratings using the calibration guide:
- 9-10: Exemplary. No significant issues found. The agent's findings are almost entirely strengths, with at most minor nitpicks.
- 7-8: Solid with minor gaps. The agent found mostly strengths with a few concrete weaknesses — missing validation in one handler, incomplete test coverage for an edge case.
- 4-6: Mixed. Roughly equal strengths and weaknesses. Functional but with clear, addressable problems — inconsistent patterns, missing error context, partial test coverage.
- 1-3: Significant issues. The agent's findings are dominated by weaknesses — no tests, unauthenticated endpoints, injection vectors, no input validation, no error handling strategy.
- Actionable recommendations. Every recommendation must name specific files, functions, or patterns. Vague advice is not useful.
- Mark incomplete rubrics. If an agent failed or timed out, include the rubric in the scorecard as "incomplete" with a note.
Output destination
If --output file:<path> was specified, write the synthesis document to that path using the Write tool, in addition to rendering it inline in the conversation.
Error Recovery
| Situation | Response |
|---|---|
| Survey script fails | Fall back to Glob + Read for manual project exploration. Continue to Phase 1. |
| Rubrics script fails | Use built-in rubric knowledge to propose rubrics directly. Continue to Phase 1. |
| All agents fail | Tell the user. Do not produce an empty synthesis. |
| User interrupts while the Workflow dispatch is running | Workflow calls run to completion in the background; synthesize with whatever rubrics have reported once it returns. |
| Project is a single file or design doc | Reduce to 3-4 rubrics. Do not fan out — use a single thorough agent instead. |