frontend-quality-loop
A stack-aware, self-correcting quality loop for frontend code. It detects which frameworks and libraries a project uses, runs the relevant review lenses, applies fixes, then re-runs the lenses until two consecutive rounds find nothing actionable — finishing with a build/typecheck/test verification gate.
This skill is provider-portable (Claude Code, Codex, Gemini). On Claude Code it can accelerate by dispatching each lens to a specialist subagent and persisting via the ralph loop; everywhere else it runs the same lenses inline. The procedure below is the source of truth — host wrappers only trigger it.
Inputs
| Variable |
Description |
Example |
${input:target} |
File(s), directory, or glob to grade |
src/components/ |
${input:bar} |
Quality bar to stop at: standard or strict |
strict |
${input:lenses} |
Comma-separated lens override (optional; default = auto from stack) |
a11y,perf,types |
${input:max_rounds} |
Safety cap on loop iterations (optional, default 5) |
5 |
Step 1: Detect the stack (do this first, every run)
Read package.json (and tsconfig.json, angular.json, next.config.*, tailwind.config.* if present). Classify into a profile. Do not assume — read the manifest.
| Signal in dependencies |
Profile flag |
react |
react |
next |
next (implies react) |
@angular/core |
angular |
typescript / tsconfig.json exists |
ts |
@tanstack/react-query or react-query |
react-query |
zustand |
zustand |
tailwindcss |
tailwind |
@playwright/test |
e2e-playwright |
vitest / jest |
unit-tests |
State the detected profile in one line before proceeding, e.g.:
Detected: next + ts + react-query + tailwind + e2e-playwright.
If no package.json exists or it is not a frontend project, stop and say so.
Step 2: Select lenses
Default lens set = the four quality dimensions, refined by profile. Honor ${input:lenses} if given.
| Lens |
Runs when |
What it checks |
Claude subagent (acceleration) |
Portable skill to load |
| a11y |
always |
WCAG 2.1/2.2, ARIA, keyboard nav, contrast, focus management |
frontend-accessibility-expert |
audit-a11y-code |
| perf |
react/next/angular |
re-render churn, memoization, bundle weight, Core Web Vitals |
react-performance-expert |
chrome-audit-performance, chrome-audit-bundles |
| types |
ts |
strictness, any leakage, unsound casts, runtime-validation gaps |
typescript-type-expert |
typescript-type-safety, zod-repair |
| patterns |
react/next |
hooks correctness, component API, impossible states, idioms |
react-expert |
audit-react-component, react-component-patterns |
| state |
react-query/zustand |
query keys, cache invalidation, store shape, selector stability |
react-expert |
react-query-patterns, zustand-patterns |
| styling |
tailwind or CSS-in-JS |
class duplication, responsive correctness, design-token drift |
frontend-css-styling-expert |
frontend-design |
| e2e |
e2e-playwright + ${input:bar}=strict |
critical-path coverage, flaky selectors, real-browser behavior |
e2e-playwright-expert |
agent-browser, chrome-run-flow |
standard bar runs a11y + perf + types + patterns (+ state/styling if present). strict adds the e2e lens and a Lighthouse check.
Step 3: The loop (auto-fix + verify)
round = 1
while round <= max_rounds:
findings = run all selected lenses # parallel on Claude; sequential elsewhere
# each finding = {lens, file, line, severity, fix}
actionable = findings where severity in {high, medium}
if actionable is empty:
clean_streak += 1
if clean_streak >= 2: break # two clean rounds = satisfied
else:
clean_streak = 0
apply fixes for actionable, highest-severity first
(Claude: dispatch fixes to executor; else edit inline)
round += 1
# Verification gate — never skip, never claim done without it
run typecheck (tsc --noEmit / ng build)
run affected unit tests
run build
if strict: run Lighthouse / e2e on critical path
Rules of the loop:
- A lens reviews; a separate fix pass changes code. Never let the same pass both grade and self-approve its own fix — re-grade in the next round.
- Stop at
max_rounds even if not clean; report what remains rather than looping forever.
- If a fix introduces a new failure (typecheck/test regression), revert that fix and log it as a finding for human review.
- Every round, print a one-line scoreboard:
Round 2 — a11y:0 perf:2 types:0 patterns:1 → fixing 3.
Step 4: Output
📋 Stack & lenses
Detected profile and which lenses ran.
🔁 Round log
Per round: the scoreboard line and what was fixed.
✅ Verification
Typecheck / tests / build / (Lighthouse|e2e) results with actual output — not "should pass".
⚠️ Remaining
Anything left unfixed at max_rounds, or fixes reverted due to regressions, with file:line and why.
Claude Code acceleration (optional, auto-detected)
When running inside Claude Code with oh-my-claudecode:
- Fire all lenses for a round in parallel as subagents (see table), passing
model explicitly (sonnet standard, opus for strict/architectural).
- Wrap the whole loop in the
ralph persistence skill so it survives across iterations and enforces the verification gate before declaring completion — this is the "copy an OMC loop, don't edit the plugin" path. The thin /frontend-quality command does this wiring.
- Use
chrome-* MCP audit skills for live perf/a11y/Lighthouse evidence.
On Codex / Gemini / other hosts: skip subagents and ralph; run each lens's portable skill inline, apply fixes directly, and self-run the verification commands. Behavior and output format are identical — only the engine differs.
Common Mistakes
- Skipping stack detection — running React lenses on an Angular repo (or vice versa) produces noise. Always read
package.json first.
- Grading and approving in one pass — a fix must be re-graded in a fresh round, never blessed by the pass that wrote it. This is how partial fixes get declared "done".
- Looping without a cap — without
max_rounds a stubborn finding (or two lenses disagreeing) loops forever. Cap it and report remainder.
- Declaring done without the verification gate — "fixed the a11y issues" means nothing if
tsc now fails. Always run typecheck/tests/build at the end with real output.
- Treating scaffolding as a quality lens — adding feature flags, env vars, or endpoints is code generation, not code grading. Use the
frontend-scaffold skill for that; mixing them lets generation bugs slip past the grader.
- Hardcoding paths — pass the target via
${input:target}; never bake a repo-specific path into the loop.
1---2name: frontend-quality-loop3description: Use when you want to iteratively raise the quality of frontend code to a satisfactory bar — auto-detects the stack from package.json, runs accessibility, performance, type-safety, and patterns/correctness lenses, applies fixes, and loops until the code is clean and verified.4---56# frontend-quality-loop78A stack-aware, self-correcting quality loop for frontend code. It detects which frameworks and libraries a project uses, runs the relevant review **lenses**, applies fixes, then re-runs the lenses until two consecutive rounds find nothing actionable — finishing with a build/typecheck/test verification gate.910This skill is **provider-portable** (Claude Code, Codex, Gemini). On Claude Code it can accelerate by dispatching each lens to a specialist subagent and persisting via the `ralph` loop; everywhere else it runs the same lenses inline. The procedure below is the source of truth — host wrappers only trigger it.1112## Inputs1314| Variable | Description | Example |15|----------|-------------|---------|16| `${input:target}` | File(s), directory, or glob to grade | `src/components/` |17| `${input:bar}` | Quality bar to stop at: `standard` or `strict` | `strict` |18| `${input:lenses}` | Comma-separated lens override (optional; default = auto from stack) | `a11y,perf,types` |19| `${input:max_rounds}` | Safety cap on loop iterations (optional, default 5) | `5` |2021## Step 1: Detect the stack (do this first, every run)2223Read `package.json` (and `tsconfig.json`, `angular.json`, `next.config.*`, `tailwind.config.*` if present). Classify into a **profile**. Do not assume — read the manifest.2425| Signal in dependencies | Profile flag |26|---|---|27| `react` | `react` |28| `next` | `next` (implies `react`) |29| `@angular/core` | `angular` |30| `typescript` / `tsconfig.json` exists | `ts` |31| `@tanstack/react-query` or `react-query` | `react-query` |32| `zustand` | `zustand` |33| `tailwindcss` | `tailwind` |34| `@playwright/test` | `e2e-playwright` |35| `vitest` / `jest` | `unit-tests` |3637State the detected profile in one line before proceeding, e.g.:38`Detected: next + ts + react-query + tailwind + e2e-playwright`.3940If no `package.json` exists or it is not a frontend project, stop and say so.4142## Step 2: Select lenses4344Default lens set = the four quality dimensions, refined by profile. Honor `${input:lenses}` if given.4546| Lens | Runs when | What it checks | Claude subagent (acceleration) | Portable skill to load |47|---|---|---|---|---|48| **a11y** | always | WCAG 2.1/2.2, ARIA, keyboard nav, contrast, focus management | `frontend-accessibility-expert` | `audit-a11y-code` |49| **perf** | `react`/`next`/`angular` | re-render churn, memoization, bundle weight, Core Web Vitals | `react-performance-expert` | `chrome-audit-performance`, `chrome-audit-bundles` |50| **types** | `ts` | strictness, `any` leakage, unsound casts, runtime-validation gaps | `typescript-type-expert` | `typescript-type-safety`, `zod-repair` |51| **patterns** | `react`/`next` | hooks correctness, component API, impossible states, idioms | `react-expert` | `audit-react-component`, `react-component-patterns` |52| **state** | `react-query`/`zustand` | query keys, cache invalidation, store shape, selector stability | `react-expert` | `react-query-patterns`, `zustand-patterns` |53| **styling** | `tailwind` or CSS-in-JS | class duplication, responsive correctness, design-token drift | `frontend-css-styling-expert` | `frontend-design` |54| **e2e** | `e2e-playwright` + `${input:bar}=strict` | critical-path coverage, flaky selectors, real-browser behavior | `e2e-playwright-expert` | `agent-browser`, `chrome-run-flow` |5556`standard` bar runs a11y + perf + types + patterns (+ state/styling if present). `strict` adds the e2e lens and a Lighthouse check.5758## Step 3: The loop (auto-fix + verify)5960```61round = 162while round <= max_rounds:63 findings = run all selected lenses # parallel on Claude; sequential elsewhere64 # each finding = {lens, file, line, severity, fix}65 actionable = findings where severity in {high, medium}66 if actionable is empty:67 clean_streak += 168 if clean_streak >= 2: break # two clean rounds = satisfied69 else:70 clean_streak = 071 apply fixes for actionable, highest-severity first72 (Claude: dispatch fixes to executor; else edit inline)73 round += 17475# Verification gate — never skip, never claim done without it76run typecheck (tsc --noEmit / ng build)77run affected unit tests78run build79if strict: run Lighthouse / e2e on critical path80```8182**Rules of the loop:**83- A lens **reviews**; a separate fix pass **changes** code. Never let the same pass both grade and self-approve its own fix — re-grade in the next round.84- Stop at `max_rounds` even if not clean; report what remains rather than looping forever.85- If a fix introduces a new failure (typecheck/test regression), revert that fix and log it as a finding for human review.86- Every round, print a one-line scoreboard: `Round 2 — a11y:0 perf:2 types:0 patterns:1 → fixing 3`.8788## Step 4: Output8990### 📋 Stack & lenses91Detected profile and which lenses ran.9293### 🔁 Round log94Per round: the scoreboard line and what was fixed.9596### ✅ Verification97Typecheck / tests / build / (Lighthouse|e2e) results with actual output — not "should pass".9899### ⚠️ Remaining100Anything left unfixed at `max_rounds`, or fixes reverted due to regressions, with file:line and why.101102## Claude Code acceleration (optional, auto-detected)103104When running inside Claude Code with oh-my-claudecode:105- Fire all lenses for a round **in parallel** as subagents (see table), passing `model` explicitly (`sonnet` standard, `opus` for `strict`/architectural).106- Wrap the whole loop in the `ralph` persistence skill so it survives across iterations and enforces the verification gate before declaring completion — this is the "copy an OMC loop, don't edit the plugin" path. The thin `/frontend-quality` command does this wiring.107- Use `chrome-*` MCP audit skills for live perf/a11y/Lighthouse evidence.108109On Codex / Gemini / other hosts: skip subagents and `ralph`; run each lens's portable skill inline, apply fixes directly, and self-run the verification commands. Behavior and output format are identical — only the engine differs.110111## Common Mistakes112- **Skipping stack detection** — running React lenses on an Angular repo (or vice versa) produces noise. Always read `package.json` first.113- **Grading and approving in one pass** — a fix must be re-graded in a fresh round, never blessed by the pass that wrote it. This is how partial fixes get declared "done".114- **Looping without a cap** — without `max_rounds` a stubborn finding (or two lenses disagreeing) loops forever. Cap it and report remainder.115- **Declaring done without the verification gate** — "fixed the a11y issues" means nothing if `tsc` now fails. Always run typecheck/tests/build at the end with real output.116- **Treating scaffolding as a quality lens** — adding feature flags, env vars, or endpoints is code *generation*, not code *grading*. Use the `frontend-scaffold` skill for that; mixing them lets generation bugs slip past the grader.117- **Hardcoding paths** — pass the target via `${input:target}`; never bake a repo-specific path into the loop.