Skip to content

Article

Hooks as Design QA Gates

How to turn Claude Code and Antigravity 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 reviewed9 Oct 2026

Reading time9 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 exposes this control plane, and Antigravity CLI is the individual peer that does too. Gemini CLI remains for enterprise Code Assist and paid API keys. It is not the individual adapter. Claude Code configures hooks in `.claude/settings.json` (or user-level settings) and fires `PreToolUse`, `PostToolUse`, and `Stop`. Antigravity CLI configures hooks in `.agents/hooks.json` at the project root, or in `~/.gemini/config/hooks.json` globally, and fires those same three names plus `PreInvocation` and `PostInvocation`. Enterprise Gemini CLI still uses `.gemini/settings.json` with `BeforeTool`, `AfterTool`, and `AfterAgent`. The names differ. The job is the same: run deterministic checks before a write, 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, Claude Code and Antigravity CLI both use `PreToolUse` 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, both use `PostToolUse` to format, lint, and run cheap file-scoped audits on the paths just touched. When the agent tries to stop, both use `Stop` for package-level proof: token audit, route accessibility scan, and any custom design verify script you already run in CI. Enterprise Gemini CLI still maps those three moments to `BeforeTool`, `AfterTool`, and `AfterAgent`.

Exit codes are part of Claude Code’s contract. 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. Antigravity CLI does not use that exit code. Its `Stop` hook reads JSON on stdout, and `decision: "continue"` plus a `reason` is what keeps the turn open. Enterprise Gemini CLI waits for matching hooks to complete before continuing the loop; configure deny decisions through its 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 on Claude Code, or `write_to_file|replace_file_content|multi_replace_file_content` on Antigravity CLI, for file-local checks, and reserve the full `npm run verify` / axe sweep for the stop gate. Enterprise Gemini CLI still matches `write_file|replace`. The diagram below is the mental model you should keep on the wall of the studio.

diagramHook gate before the agent continues
Gate diagram. The agent has written and wants to continue. A hook checks that the path is in policy, that colors are tokens, and that the accessibility command passes. Pass means the agent continues. Fail means the agent stops.

After a write, the hook checks path, tokens, and accessibility. A pass lets the agent continue. A failure stops the turn and reports the check.

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 Antigravity CLI, a `Stop` hook that prints a JSON decision of continue, plus a reason, does the same job. Enterprise Gemini CLI still uses `AfterAgent`. Pair that with a `PostToolUse` 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 on Write or Antigravity file tools

2Semantic token missing from CSS build

Stop. Enterprise Gemini CLI: AfterAgent

3outline-none without focus ring

PostToolUse

4Contrast / label / landmark failures

Stop. Enterprise Gemini CLI: AfterAgent

5Agent edits DESIGN.md tokens casually

PreToolUse. Enterprise Gemini CLI: BeforeTool

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

Section 05

An Antigravity 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 a thin adapter per CLI that calls the same commands at the analogous lifecycle points. For individual Google accounts that adapter is Antigravity CLI, in `.agents/hooks.json`. Checked 9 October 2026 at antigravity.google/docs/hooks: file writes match `write_to_file`, `replace_file_content`, and `multi_replace_file_content`. A `Stop` handler blocks by printing `decision: "continue"`. It does not use Claude Code’s exit code 2.

Enterprise Gemini CLI and paid API keys are a different product. Their adapter is still `.gemini/settings.json`, with `BeforeTool`, `AfterTool`, and `AfterAgent`, and matchers such as `write_file|replace`. Do not point an individual account at that file and expect it to run.

Start with one `PreToolUse` path deny for token sources, one after-write lint, and one stop 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.

.agents/hooks.json (Antigravity CLI, individual)
{
  "design-file-lint": {
    "PostToolUse": [
      {
        "matcher": "write_to_file|replace_file_content|multi_replace_file_content",
        "hooks": [
          {
            "type": "command",
            "command": "node scripts/design-file-lint.mjs"
          }
        ]
      }
    ]
  },
  "design-qa-gate": {
    "Stop": [
      {
        "type": "command",
        "command": "npm run agentic:audit"
      }
    ]
  }
}

Section 06

Enterprise Gemini CLI still uses the older event names

If the account is Gemini Code Assist Standard or Enterprise, or a paid Gemini API key, the hook file is still `.gemini/settings.json`. The events are `BeforeTool`, `AfterTool`, and `AfterAgent`. That config does not run on Antigravity CLI, and Antigravity’s `.agents/hooks.json` does not run on enterprise Gemini CLI. Keep the scripts shared. Keep the adapters separate.

.gemini/settings.json (enterprise Gemini CLI and paid API keys)
{
  "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 07

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 block contract in AGENTS.md in three lines: exit code 2 on Claude Code, and decision continue on Antigravity CLI. 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 for path policy, PostToolUse for fast file lint, and Stop for package proof. Enterprise Gemini CLI still names those BeforeTool, AfterTool, and AfterAgent.
  • Fail closed on serious a11y and token drift; leave taste judgments to humans.
  • Share one script surface across Claude Code and Antigravity CLI adapters.

Sources

Sources & further reading

Related articles

Keep reading on Design QA.

38

Three decisions a CEO owes the design function

The design function you already fund can produce more than it can judge. You do not need the tools. You need three decisions: the judgement budget, the systems an agent may touch, and the human who answers when the work ships.

Reviewed
10 Oct 2026
Reading time
8 min
Read article
37

The page you sign when volume rises

If agents raise the volume and critique hours stay flat, the quality bar falls in silence. The head of design's job is the constraint: review capacity, the system as the instruction set, a research line that refuses synthetic users, and a page with a signature.

Reviewed
10 Oct 2026
Reading time
8 min
Read article
36

The design organisation's job once first drafts are cheap

A head of design does not answer agents with a tool list. First drafts got cheap. Judgement did not. This is the operating model: which capability may run, what stays human in each practice, and the one page a CEO can use.

Reviewed
10 Oct 2026
Reading time
9 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.