feat(review): rebuild the review as a skill plus two second-eye subagents - #30
Conversation
…ents
commands/flutter-review.md was a 29-line checklist that ran in the same
context as the code it reviewed. Reviewing your own work in the context
that produced it is the one thing a review cannot do: the assumptions that
caused the defect are already in the window.
The review now runs in a subagent with its own context, which never saw the
conversation that wrote the code.
- agents/flutter-reviewer.md (sonnet, medium): four gated stages. Magic
compliance (bootstrap, IoC, controller/view/state, models, HTTP,
validation, auth, routing), Wind compliance (dark pairing, states over
interpolation, layout contracts, dead tokens, widget choice), correctness
plus OOP and style, then reuse. Read-only.
- agents/flutter-reviewer-deep.md (opus, high): the same four, plus a fifth
that reads the feature as one path rather than as files: controller to
view to state, model to HTTP to validation, route to middleware to
authorization, and the app's own conventions.
- skills/flutter-review/SKILL.md: resolves scope from the active work
(uncommitted, else branch diff), picks the tier, spawns the reviewer,
verifies every returned finding against the code before showing it,
reports through ReportFindings, then fixes on approval and re-reviews.
- references/flutter-review-core.md: the shared stages, the severity and
confidence rule, the not-a-finding list, the report shape. One copy, so
two reviewers cannot drift into two definitions of the same check.
- references/dart-style-core.md: the style floor for sessions with no
personal style skill loaded. my-coding is listed in each agent's skills:
and Claude Code skips a missing skill with a warning, so it applies on
machines that have it and costs nothing on machines that do not.
The core file names which rulebook decides each check instead of restating
the rules, and the agents read the live wind-ui and magic-framework skills.
A rule copied into a review prompt would be the one thing here guaranteed
to rot; this session spent most of its length fixing exactly that failure
in the skills themselves.
Two anti-noise mechanisms carried from the reviewers that work: severity
plus confidence with a reporting floor, and a disproof pass that tries to
kill each candidate finding before it ships. A clean scope reports NONE.
CI gains two gates. Frontmatter now parses in CI (an unquoted description
containing ": " silently disabled both agents while I was writing them,
which is invisible at runtime: the component just never loads), and every
${CLAUDE_PLUGIN_ROOT} path is checked to exist.
skills/flutter-review/ stays out of skills/index.json on purpose: it
orchestrates subagents, which the other tools the registry serves cannot
spawn.
Dry-run on magic_example: the dark-pairing check flagged 38 of 76 className strings, and every one was a semantic alias. WindThemeData.aliases maps bg-surface to 'bg-[#FFFFFF] dark:bg-[#030712]', so the alias used alone is correctly paired and the finding is false. A check with a ~50% false-positive rate on the first real app is worse than no check: it is how a reviewer stops being read. Stage 2 now resolves the alias map first (grep for aliases:, or lib/config/wind_theme.g.dart in a design-first project) and treats an alias whose value carries a dark: token as paired. Found by running the catalog by hand against a real app rather than trusting that it would behave.
|
Dry-run against Stage 1 behaved as intended. Two candidates, one real and one correctly killed:
Stage 2 found a defect in my own check. The dark-pairing rule flagged 38 of 76 className strings. Every one was a semantic alias: A check that is wrong half the time on the first real app is worse than no check. Pushed a fix: Stage 2 now resolves The wiring itself still needs an install to exercise: plugin agents load only where the plugin is installed, so the first live |
A new skill plus two subagents is a structural addition, not a content refresh, so it takes the minor rather than the patch the skill syncs use.
There was a problem hiding this comment.
Pull request overview
This PR replaces the old /fluttersdk:flutter-review command checklist with a first-class flutter-review skill that orchestrates two read-only “second-eye” reviewer subagents, supported by shared review-core and style-core references, and adds CI gates to prevent silent plugin-load failures.
Changes:
- Add
skills/flutter-review/SKILL.mdorchestrator plus two reviewer subagents (flutter-reviewer,flutter-reviewer-deep) and shared rule/reference docs. - Remove the legacy
commands/flutter-review.mdchecklist and update docs to reflect the new skill/subagent model. - Add CI checks to (1) parse frontmatter and validate supported agent fields, and (2) ensure
${CLAUDE_PLUGIN_ROOT}references resolve.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| skills/flutter-review/SKILL.md | New orchestrator skill for scoping, spawning reviewer agents, verifying findings, and coordinating fixes + re-review |
| agents/flutter-reviewer.md | New read-only standard reviewer agent (4-stage gated review contract) |
| agents/flutter-reviewer-deep.md | New read-only deep reviewer agent (adds cross-layer adaptation stage) |
| references/flutter-review-core.md | Shared staged review rubric, severity/confidence rules, and output contract |
| references/dart-style-core.md | Baseline Dart/Flutter style floor when no personal style skill is loaded |
| commands/flutter-review.md | Removes legacy checklist-based command |
| README.md | Documents the new review skill and reviewer subagents in plugin auto-registration list |
| CLAUDE.md | Updates repo architecture docs for agents/references and clarifies skills/flutter-review ownership/indexing |
| .github/workflows/ci.yml | Adds CI gates for frontmatter parsing/field validation and ${CLAUDE_PLUGIN_ROOT} reference existence |
The two plugin skills were listed unprefixed in each agent's skills: field. Nothing in the docs establishes that this resolves. What the docs do say: plugin skills live in a plugin-name:skill-name namespace (skills doc, Skill locations), and the bare name is documented only as an invocation fallback, 'unless another command already uses that name'. The skills: preload field is a different resolution path and the docs never describe it for a plugin's own skill, in either form. The failure mode decides it: a name that does not resolve is skipped with a warning in the debug log and nowhere else. The reviewer would start with no magic and no Wind rulebook, review from memory, and report confident nonsense with no signal that anything was wrong. Both candidate forms carry that risk and neither is documented away. Reading /skills/<name>/SKILL.md by path either resolves or errors, and the agents already open those skills' references/ files during a review, so this is their normal mode rather than a workaround. skills: keeps my-coding, where the bare name is the documented identity for a user skill and the ac plugin's agents prove that path works from inside a plugin agent.
|
Corrected the skill-preload assumption after a direct question about whether unprefixed names resolve for a plugin's own skills. They are not documented to. The skills doc says plugin skills use a What settles it is the failure mode, not the odds: a name that does not resolve is skipped with a warning in the debug log and nowhere else. The reviewer would have started with no rulebook, reviewed magic and Wind from memory, and reported confident nonsense with nothing anywhere saying why. Both agents now read |
…oes not list Review feedback on #30. PyYAML: the gate imports yaml and the job never installed it. It passed because the runner image happens to ship it, which is exactly the kind of inherited dependency that turns a gate red one image update later with nothing wrong in the repo. Declared explicitly. color: the plugins reference enumerates what plugin agents support and color is not in it, even though the general subagent docs carry the field. Both agents used it and the gate's allow-list tolerated it, which contradicted the field list this repo's own CLAUDE.md quotes. An ignored field is indistinguishable from a working one, and preventing exactly that is why the gate exists, so the field is gone from both agents and from the allow-list. Cost of being wrong: two agents render in the default color.
|
Both addressed, one of them with a correction to the premise. PyYAML. The prediction was wrong but the concern was right. The gate ran and passed in CI (four
|
What
commands/flutter-review.mdwas a 29-line checklist that ran in the same context as the code it reviewed. It is replaced by:skills/flutter-review/SKILL.mdagents/flutter-reviewer.mdsonnet/effort: medium, read-only. Four gated stagesagents/flutter-reviewer-deep.mdopus/effort: high, read-only. The same four plus a cross-layer stagereferences/flutter-review-core.mdreferences/dart-style-core.mdStages. 1 magic compliance (bootstrap, IoC, controller/view/state, models, HTTP, validation, auth, routing) gates the rest; 2 Wind compliance (dark pairing,
states:over interpolation, layout contracts, dead tokens, widget choice); 3 correctness, OOP, style; 4 reuse and simplification. The deep reviewer adds stage 5: the feature read as one path (controller to view to state, model to HTTP to validation, route to middleware to authorization) plus a comparison against an existing feature in the same app.Fix loop. The reviewer never edits. The skill shows each fix before making it, batches the mechanical ones into a single approval, asks separately for anything behavioral, applies only inside the reviewed scope, runs the analyzer and tests, then re-spawns the reviewer once. It stops after two rounds.
Why this shape
file:line, re-checks the rule against the skill that owns it, and traces the failure scenario before showing anything, then says how many it dropped. A reviewer is as fluent when it is wrong as when it is right.wind-uiandmagic-frameworkskills. A rulebook copied into a review prompt is the one thing here guaranteed to rot, and most of this session was spent repairing exactly that failure in the skills themselves.NONE; there is no minimum finding count.my-codingwithout a hard dependency. It is listed in each agent'sskills:. Claude Code skips a missing skill with a debug warning, so the personal rules apply on machines that have them and cost nothing elsewhere, withreferences/dart-style-core.mdas the floor either way.Research behind the design: Anthropic's plugin and subagent references for the
agents/contract and the field list, its own/security-review(find, then a parallel per-finding false-positive filter, then a confidence gate) and/code-review(background subagent,ReportFindings), and the community implementations that hold up, whose recurring anti-noise levers are a severity floor and a numeric confidence threshold applied before dedup.Testing
hooks,mcpServers,permissionModeare ignored for plugin agents, so they are absent).${CLAUDE_PLUGIN_ROOT}path referenced by the skill, both agents, and the core file resolves to a file that exists.actionlintclean on the modified workflow; both new gates dry-run green locally.Two new CI gates, both from defects hit while writing this:
descriptioncontaining": "silently disabled both agents: YAML reads it as a nested mapping, the component never loads, and nothing errors at runtime.${CLAUDE_PLUGIN_ROOT}path exists. A bad one fails inside a subagent as a file-not-found the user never sees.Note on
skills/index.jsonskills/flutter-review/is deliberately absent from the index: it orchestrates Claude Code subagents, which the other tools the registry serves cannot spawn. Documented inCLAUDE.md. Every other directory underskills/remains a mirror and stays in the index.Not yet exercised end to end
The wiring cannot be tested from this repo: plugin agents load only where the plugin is installed. The stages were dry-run against a real magic + Wind app to check they produce actionable findings rather than noise; the first live
/fluttersdk:flutter-reviewafter install is the real test.