Skip to content

Article

Hooks as Design QA Gates

How to turn Claude Code and Gemini CLI hooks into design QA gates that block ship when token drift or accessibility checks fail — so taste and compliance are enforced by the runtime, not remembered in a prompt.

Last reviewed1 Aug 2026

Reading time8 min read

Section 01

Prompts ask; hooks decide

A design harness tells an agent what good looks like. Hooks decide whether the agent is allowed to finish. That distinction matters once you stop treating agent sessions as demos and start treating them as production work. Prompt text is advisory. The model can skip a checklist, invent a color, or declare done while contrast still fails. A hook is a shell command the runtime runs at a known lifecycle point. If the command fails closed, the session does not quietly ship the bad state.

Claude Code and Gemini CLI both expose this control plane. Claude Code configures hooks in `.claude/settings.json` (or user-level settings) and fires events such as `PreToolUse`, `PostToolUse`, and `Stop`. Gemini CLI configures hooks in `.gemini/settings.json` and fires events such as `BeforeTool`, `AfterTool`, and `AfterAgent`. The names differ; the job is the same: run deterministic checks at the moment a write is about to happen, after a write lands, or when the agent claims the turn is finished.

For design teams, the highest-leverage gates are not generic linters alone. They are the checks that catch the failure modes agents invent under time pressure: hardcoded brand colors that bypass tokens, spacing that ignores the scale, purple-gradient “AI default” chrome, missing focus states, and contrast that only looks fine on a designer’s calibrated display. This article shows how to wire those checks as hooks so token drift and accessibility issues block completion instead of becoming a comment on a pull request two days later.

Section 02

Where gates sit in the agent turn

Think of three enforcement moments. Before a tool runs, you can refuse the action: Claude’s `PreToolUse` and Gemini’s `BeforeTool` are the right place to block destructive or out-of-policy commands, and to refuse writes into paths that should never be agent-edited (token source files, locked brand assets). After a successful write, Claude’s `PostToolUse` and Gemini’s `AfterTool` are the right place to format, lint, and run cheap file-scoped audits on the paths just touched. When the agent tries to stop, Claude’s `Stop` hook and Gemini’s `AfterAgent` are the right place for package-level proof: token audit, route accessibility scan, and any custom design verify script you already run in CI.

Exit codes are part of the contract. In Claude Code, exit code 2 is the blocking signal for most events — including preventing a tool call on `PreToolUse` and preventing the model from stopping on `Stop` so it can act on the failure feedback. Exit 0 with structured JSON is the alternative path when you need a precise `decision: "block"` and a reason. Treat exit 1 as a non-blocking hook error unless you have verified your tool’s semantics. Gemini CLI waits for matching hooks to complete before continuing the loop; configure deny decisions through the documented hook output schema so a failed design check cannot be ignored by a confident summary message.

Scope matchers tightly. Running a full accessibility sweep after every keystroke-level edit burns time and tokens. Match Write/Edit (Claude) or `write_file|replace` (Gemini) for file-local checks, and reserve the full `npm run verify` / axe sweep for the stop/after-agent gate. The diagram below is the mental model you should keep on the wall of the studio.

diagramHook gate timeline
1

Design decision

Brief + harness loaded — DESIGN.md, AGENTS.md, skills (not hooks yet)

2

Design decision

PreToolUse / BeforeTool — block out-of-policy paths and destructive Bash

3

Design decision

Write/Edit lands — agent changes components or styles

4

Design decision

PostToolUse / AfterTool — format + file-scoped token/a11y lint

5

Design decision

Stop / AfterAgent — run package verify; fail closed on drift or axe P0

6

Design decision

Human review — hierarchy, taste, and copy still require eyes

Three enforcement moments in one agent turn: refuse the write, audit the file, then block stop until package-level design QA passes.

Section 03

Gate 1: token drift that cannot ship

Token drift is the quiet failure of agentic UI. The page compiles. The colors are almost right. Somewhere in a component, the agent wrote `text-[#4F46E5]` or `bg-purple-500` because that pattern is overrepresented in training data. Your DESIGN.md said semantic tokens only. CI might catch it later — or it might not, if the only check is TypeScript. A hook makes the rule executable at the moment of authorship.

The practical pattern is a small audit script the repo already owns (this school uses an agentic design audit in verify). The hook’s job is not to reimplement the audit; it is to refuse completion when the audit fails. On Claude Code, a `Stop` hook that runs the audit and exits 2 on failure keeps the turn open and feeds stderr back to the model. On Gemini CLI, an `AfterAgent` (or equivalent post-turn) hook that denies continuation with the audit output does the same job. Pair that with a `PostToolUse` / `AfterTool` matcher on style and component paths so the agent gets a fast signal after each write, not only at the end.

Keep the drift rules concrete. Ban raw hex and arbitrary Tailwind palette colors in product UI files. Require semantic classes (`bg-primary`, `text-muted-foreground`, `border`). If you transform DTCG tokens through Style Dictionary into CSS variables, the audit should compare used custom properties against the generated set — not against a designer’s memory. The code below is a minimal Claude Code project hook sketch; adapt the command to your real audit script.

  • File-scoped lint after Write/Edit: catch hardcoded colors in the touched file fast.
  • Package audit on Stop: catch cross-file drift and missing token usage.
  • Fail closed (exit 2 / deny): the agent must fix or explain before the turn ends.
  • Human still owns taste: hooks catch drift and policy, not whether the hierarchy feels right.
.claude/settings.json (Stop gate for design audit)
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "node scripts/design-file-lint.mjs"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "npm run agentic:audit"
          }
        ]
      }
    ]
  }
}

Section 04

Gate 2: accessibility in the loop, not after launch

Accessibility checks after merge are how teams accumulate debt. Agents make that worse because they can generate large surfaces quickly: unlabeled icon buttons, missing landmarks, focus rings removed “for cleanliness,” and contrast that fails WCAG on the paper background you actually ship. Put axe (or an equivalent CLI) on the stop gate for the routes the agent touched, and keep a cheaper static check on the write path for obvious anti-patterns (click handlers on non-buttons, images without alt in JSX, `outline-none` without a replacement focus style).

Evidence beats opinion. Prefer a Playwright-driven axe run against localhost for the affected routes, writing JSON to disk so the hook can fail on any serious violations. Feed the summary on stderr so the model sees countable issues, not a vague “check a11y.” If a full browser sweep is too slow for every stop, split the gates: PostToolUse runs eslint-plugin-jsx-a11y or a custom AST pass; Stop runs axe on a short allowlist of routes declared in the brief or in a `qa-routes.txt` file the agent is required to update.

Claude Code’s documented behavior matters here: a blocking `Stop` hook prevents the model from ending the turn and continues the conversation with the error feedback. That is exactly what you want when axe reports a P0 contrast failure on the membership CTA. Gemini’s synchronous hook model likewise lets you hold the loop until the check completes. Neither tool replaces a human accessibility review for complex interactions — but both can refuse the fiction that “done” includes known serious violations.

scripts/a11y-gate.sh (sketch)
#!/usr/bin/env bash
set -euo pipefail
# Fail closed: any serious axe finding blocks Stop / AfterAgent.
npx playwright test tests/a11y-smoke.spec.ts --reporter=line
node scripts/summarize-axe.mjs --fail-on serious,critical
tableToken drift and a11y gate matrix
1Hardcoded hex / palette class

PostToolUse / AfterTool on Write

2Semantic token missing from CSS build

Stop / AfterAgent

3outline-none without focus ring

PostToolUse / AfterTool

4Contrast / label / landmark failures

Stop / AfterAgent

5Agent edits DESIGN.md tokens casually

PreToolUse / BeforeTool

Match the failure mode to the earliest hook that can catch it without burning the whole suite on every keystroke.

Section 05

A Gemini CLI twin for the same policy

Teams rarely standardize on one agent. The policy should be the scripts; the hooks should be thin adapters. Put `scripts/design-file-lint.mjs`, `npm run agentic:audit`, and `scripts/a11y-gate.sh` in the repo once. Then declare Claude and Gemini hook configs that call the same commands at the analogous lifecycle points. Gemini CLI documents hooks under `.gemini/settings.json` with matchers such as `write_file|replace` on `BeforeTool` / tool events, and agent-level hooks for post-response validation.

Start with one BeforeTool path deny for token sources, one after-write lint, and one after-agent package gate. Resist the urge to port twenty hooks on day one. A single fail-closed stop gate that runs your existing verify script already changes studio behavior: agents stop claiming victory while the audit is red, and humans stop arguing about whether the model “should have remembered” the brand rules.

.gemini/settings.json (mirror policy)
{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "write_file|replace",
        "hooks": [
          {
            "name": "protect-token-sources",
            "type": "command",
            "command": "$GEMINI_PROJECT_DIR/.gemini/hooks/deny-token-path.sh"
          }
        ]
      }
    ],
    "AfterTool": [
      {
        "matcher": "write_file|replace",
        "hooks": [
          {
            "name": "design-file-lint",
            "type": "command",
            "command": "node $GEMINI_PROJECT_DIR/scripts/design-file-lint.mjs"
          }
        ]
      }
    ],
    "AfterAgent": [
      {
        "hooks": [
          {
            "name": "design-qa-gate",
            "type": "command",
            "command": "npm run agentic:audit && $GEMINI_PROJECT_DIR/scripts/a11y-gate.sh"
          }
        ]
      }
    ]
  }
}

Section 06

Studio practice: fail closed, review with eyes

Hooks are not a substitute for critique. They remove the cheap mistakes so review time can go to hierarchy, density, copy, and whether the page does the user job. Write the gate list into the brief: which routes must pass axe, which directories are token-audited, which files are off-limits. If the agent updates a route list as part of the task, the stop gate becomes honest evidence instead of a stale smoke test.

Measure the cost. A stop gate that adds ninety seconds is usually cheaper than a round of design QA that rediscovers purple buttons. A stop gate that re-runs the full suite on every turn will be disabled by the first impatient operator — and a disabled gate is worse than no gate because the team believes protection exists. Keep PostToolUse cheap; keep Stop thorough but scoped; keep PreToolUse reserved for policy denies.

Ship the pattern as team infrastructure. Commit hook configs and scripts. Document the exit-code contract in AGENTS.md in three lines. When someone asks why the agent will not stop, the answer should be a command output, not a vibe. That is what it means for design QA to become part of the control plane.

  • Encode anti-slop and token rules in scripts the hooks call — not only in Markdown.
  • Use PreToolUse/BeforeTool for path policy; PostToolUse/AfterTool for fast file lint; Stop/AfterAgent for package proof.
  • Fail closed on serious a11y and token drift; leave taste judgments to humans.
  • Share one script surface across Claude Code and Gemini CLI adapters.

Sources

Sources & further reading

Related articles

Keep reading on Design QA.

35

Multi-CLI Literacy for Designers

Design teams will not standardize on one coding agent. This field note covers the shared literacy across Claude Code, Cursor, Codex, and Gemini-class CLIs: instruction files, skills, MCP, permissions, and verification habits that travel when the binary changes.

Reviewed
30 Jul 2026
Reading time
3 min
Read article
34

Agent FinOps for Design Orgs

A field note on keeping design-agent spend under control: where critique fleets burn tokens, how to budget capture and judge lanes, and a lightweight cost review ritual that does not require a finance degree.

Reviewed
28 Jul 2026
Reading time
3 min
Read article
33

Authorship, Disclosure, and Studio Governance for Agentic Design

A practical governance kit for design studios and campus programs: when to disclose agent assistance, how to keep critique logs that assign accountability, and why verification rituals matter more than generation speed when work carries a human signature.

Reviewed
8 Aug 2026
Reading time
5 min
Read article
Newsletter

Get the next harness and hook-gate templates by email.

The newsletter is the update channel for article revisions, tool changes, and field-tested workflows.

Processed by Buttondown. You can unsubscribe from any email.

Further reading

For deeper reading, explore the books behind the Agentic Design School curriculum.

The Agentic Designer cover
Curriculum

The Agentic Designer

How AI agents are transforming product design.

The operating model for product designers, design leads, and builders who need to understand what changes when agents join design work.

Claude Code for Designers cover
Curriculum

Claude Code for Designers

A designer's guide to AI-assisted workflows.

A practical guide for designers who want to work directly with coding agents without turning it into a programming manual.

Open Design cover
Curriculum

Open Design

Local-First, Agent-Native Design-as-Code — The Open-Source AI Design Alternative

A practical field guide to running local-first, agent-native design-as-code workflows without surrendering brand quality or vendor independence.