| name | design-consistency |
| description | Adversarial review of design-system fidelity in the plugin/ subtree (plugins/*/src/pages, plugins/*/src/components, packages/ui). Hunts editorial drift (facts surfaced twice, sections that dump tables instead of answering questions, lists that should be summary stats), atomic-unit drift (the same person row / item row / section header rendered with different markup), and proportional drift (arbitrary spacing values, ad-hoc letter-spacing, hand-rolled cards next to the Card primitive). Pairs with packages/ui/src/boundary.test.ts which enforces the deterministic patterns. Runs inline in the current chat (no subagent fork). Use when the user says "design review", "design audit", "consistency check", "is the design system holding", or after adding any UI surface. |
Design-system consistency review (inline)
Run inline in the current chat using the session's own Read, Grep, and Bash tools. No Agent fork.
This skill is the editor-in-chief of the surface, not just the gardener. It asks two kinds of question:
- Editorial. Does this surface tell a stakeholder a story? Does each fact appear once? Are sections justified by a question a reader would ask? Or is it a table dump with three sections that all show the same names with different chrome?
- Compositional. Does the surface compose the right primitives, or hand-roll markup that another surface already renders? Do its spacing, type, and color choices match the rest of the app, or has it drifted?
The deterministic offenses (arbitrary-value Tailwind, inline styles, bare <button>/<input>/<table>, hardcoded hex in JSX, stray app CSS) are caught by packages/ui/src/boundary.test.ts. That's the brick inspector. This skill is the editor + the garden master — it looks for the things a regex can't see.
The rulebook:
Read all three before producing findings.
When to invoke
- User says: "design review", "design audit", "consistency check", "is the design system holding", "review the design", "is the UI drifting".
- After adding ANY new surface (page, panel, drawer, modal).
- After adding or modifying a primitive in
packages/ui — the workshop changed; check the rooms it serves.
- Before merging a branch that touches UI.
Pre-flight — inspect the bricks
cd plugin
pnpm test boundary
Every failure must be resolved before the editorial / garden walk begins. The inspector catches the deterministic offenses (wrong brick, wrong mortar); this skill is for everything regex can't reach. If the inspector fails, stop and fix.
Setup — walk the grounds
cd plugin
find plugins/*/src/pages -name "*.tsx" 2>/dev/null
find apps/host/app -name "page.tsx" 2>/dev/null | grep -v node_modules
ls packages/ui/src/
ls packages/ui/src/primitives/ 2>/dev/null
find plugins/*/src/components -name "*.tsx" 2>/dev/null
sed -n '/^:root {/,/^}/p' packages/ui/src/tokens.css | head -200
What to hunt — the editor's pass first, then the garden walk
The editor's pass is new in iter 58. It runs FIRST — surface-by-surface editorial check — because most "drift" findings have an upstream editorial cause (the surface is dumping tables instead of answering questions, and the markup variants are downstream of that).
Editor's pass (per surface)
For each detail / drawer / modal / list:
E1. Name the questions. Can you name the 3–5 questions a stakeholder opens this surface to answer? If not, the surface has no editorial spine — it's a table dump. Critical finding.
E2. Each fact once. Walk the surface and list every (person, decision, document, timestamp) entity that appears. Does any entity appear in more than one section with different chrome? Each echo is a finding. High.
E3. Sections answer questions. For each section, can you point to which question (from E1) it answers? Sections that don't answer a question are noise — should be cut, demoted, or merged. High.
E4. Lists vs. summaries. Where the same set of entities is referenced by multiple sections, exactly one section shows the full list (the one whose question requires the columns). Other sections summarize ("4 of 4 attended", "3 decisions by Casey Liu"). Multiple full lists of the same set = finding. Medium.
E5. Sections rhyme. Within one surface, do the sections share row shape (avatar size, eyebrow style, trailing placement, card padding)? Different-looking-for-no-reason is the bug. Medium.
Garden walk (across surfaces)
The classic checks. Rank findings CRITICAL / HIGH / MEDIUM / LOW.
G1. Re-carved gates (primitive re-implementations). A page hand-carves visual the workshop already stocks:
<div className="rounded-md border p-4"> where <Card> exists.
<a className="inline-flex items-center justify-center rounded-md bg-..."> where <Button> exists.
- Inline avatar + name + role markup where
<PersonRow> exists (once iter 59 lands).
- Hand-rolled section eyebrow (uppercase + tracking) where
<SectionHeader> exists.
- Fix layer: use the existing primitive, or extend the workshop — never re-carve at the wall.
G2. One-off lattice (call-site variant spikes). A caller overrides a primitive to invent a new visual state in place:
<Button className="bg-orange-500 text-white"> — a new variant the workshop hasn't been told about.
<Card style={{ borderColor: "#abc123" }}> — a one-off color.
- Fix: a new variant on the primitive plus rework of existing callers to use it. The rework is the cost of admission.
G3. Proportional drift (spacing + type). The hierarchy is named (--ui-gap-tight / related / section / block / region). Flag every:
gap-5, gap-7, gap-9, p-7, m-13, arbitrary [20px] values.
- Inline letter-spacing other than
var(--ui-tracking-eyebrow) for uppercase eyebrows.
- Inline font-sizes other than the
--ui-text-* tokens for primitive contexts.
- Skipped heading levels (h1 → h3 with no h2).
G4. Twin courtyards. Two surfaces in different plugins doing the same kind of work — case manager's caseload and parent's children list, audit feed and message thread feed, person mentioned in decisions vs in attendance. Do they pull from one shared component, or have they diverged silently into near-twins? The visitor feels the wrongness but can't name it. Diff the surfaces; call out the divergence.
G5. Signal carried only by color. A red dot, an amber border, a green pill — with no shape, no label, no icon — is a flower whose only attribute is its color. A visitor who can't see that color walks past it. Status indicators must carry shape AND label AND icon in addition to hue.
G6. Cuts where there should be thresholds. A consequential action (delete, finalize, send, sign, revoke consent) with no gate is a doorway with no door. Conversely: a routine action wrapped in a modal is a gate over a sightline. Walk every interactive surface and ask: is the threshold matched to the moment?
G7. Outdoor rooms that have been filled in. Empty canvas, generous gutters, breath between cards — outdoor rooms doing real work. If a recent surface "filled the space" with a summary widget, a marketing card, a recap, or a CTA — and the empty space had been deliberate — that's a finding.
G8. Token-set gap. A real visual need that no token covers (e.g., the comp wants a soft warning surface but tokens.css has no --ui-warning-surface). Fix: add the token. Hardcoding a color at the call site introduces an off-palette plant.
G9. One-off CSS files. Any new *.css in plugins or apps beyond the framework's tokens.css. The boundary test catches this; the skill's job is to recommend the right home for whatever the CSS was trying to do — variant on a primitive, new token, removal as a re-carved gate.
Output format
Inline in the conversation, severity-grouped, editorial findings first, then garden findings. For each finding:
file:line reference.
- One-line description, framed as either an editorial issue ("echo: same four people listed in attendance AND people", "section dumps audit table; doesn't answer a stakeholder question") or a garden element ("re-carved gate", "one-off lattice", "twin courtyard", "proportional drift").
- Fix layer — name where the fix belongs:
- cut the echo; keep the more information-dense rendering
- demote to summary stat ("X of N")
- merge sections; they're answering the same question
- use the existing primitive
- extend the primitive (add slot or variant)
- rework existing callers to match
- add a token to tokens.css
- introduce a threshold (modal, confirmation, route step)
- remove the filler; restore the breath
- downgrade the surface to match what exists
No code fixes — diagnose only. End with one sentence on the surface's overall coherence: does it read as a chronicle a stakeholder follows, or as a database dump? Name the single most load-bearing thing to fix next.
Cleanup
You are the main session — no scratch files. Output findings inline. End with git status; nothing new should be staged.
What this skill is not
- Not a re-run of the brick inspector — that's
pnpm test boundary.
- Not an accessibility audit of the design system itself — use
red-team-accessibility-audit when applicable.
- Not a code-correctness review — use
red-team-review.
- Not a visual-regression diff — that's the snapshot scripts' job.
- Not a license to redesign. The editor + garden master notes what's off-key and names the layer where the fix belongs; the gardener fixes it.
Common findings shortcuts
If the surface you're reviewing is a detail drawer (HelpDrawer, JourneyEventDrawer, future drawers), check:
- E1: Can you name the 3–5 questions a reader opens the drawer to answer?
- E2: If the drawer has multiple "list" sections (People, Attendance, Activity, Decisions, Signatures), does the same entity appear in more than one? It's almost certainly a finding.
- G1: Is the drawer body composing
Drawer + PersonRow + SectionHeader + SummaryStat, or rolling its own? (Iter 59+ ships these.)
If the surface is a list page (StudentsPage, MeetingsListPage, FeedPage, BroadcastsPage):
- G1: Is each row composing
ItemRow / PersonRow, or hand-rolling card markup?
- G4: Is there a near-twin in another plugin? (Audit feed ↔ thread feed; meeting row ↔ broadcast row.)
If the surface is a detail page (StudentDetailPage, MeetingDetailPage, ThreadPage):
- G1: Does the header use
PageHeader (with breadcrumb / leading / chips slots as needed), or roll its own?
- E1–E3: Does the body decompose into sections matching the questions a stakeholder asks?