Why code alone can't teach agents design

Coding agents are good at producing UI that matches your components and conventions. What they can't reproduce is judgment: the reasoning that made one component, phrase, or interaction the standard. That lives in design reviews, PR comments, Slack threads, and with the people who were in the room. If context isn't in the codebase, an agent can't see it.

Vercel treats accepted product decisions like code — kept in the repository, reviewed, and available to every agent. The system is called product-design and has three parts:

  1. An agent skill that supplies the context behind decisions requiring product or codebase judgment.
  2. Linters that enforce clear-cut rules automatically.
  3. A review loop that gathers evidence from Slack, Figma, and GitHub, then prepares guideline updates for review.

The same structure can be built around anyone's standards.

How the skill routes work

The skill sits in the repository next to the code it governs. Its top-level AGENTS.md tells agents when to load it; the skill-local AGENTS.md defines load order, validation, and governance. SKILL.md runs the workflow. References are kept by theme — product judgment, interface quality, resilience, copy, canonical product names, interaction patterns, surface-specific decisions — with exemplars/ for shipped decisions worth repeating and mistakes to avoid, and coverage-gaps.md for areas with no standard yet. A separate copywriting-eval/ tests copy behavior only.

SKILL.md first resolves the requested mode: shape, implement, review, copy, or harden. That keeps an audit from turning into an edit, or a copy pass into a redesign. It skips backend-only work, telemetry, console errors, generated files, and tests with no shipped UI impact. It routes to canonical sources rather than duplicating them — component APIs, design-system rules, accessibility criteria, and interaction guidance stay with their owners.

Routing is specific to task and surface. Material changes load product-judgment and interface-quality references first. Copy, component, layout, interaction, accessibility, and resilience work route to focused references. A modal loads destructive-action patterns and canonical verbs; a settings form loads labels, validation, progressive disclosure, and accessible-name guidance.

// Simplified SKILL.md structure from Vercel's implementation
mode = resolve(request)
if mode in ["backend", "telemetry", "generated", "no-ui"]: skip
refs = route(mode, surface)
load(refs)
validate = agent-run(sandbox: repo build + tests + linters)

Traceability is built in. Copy rules carry stable IDs and point to canonical sources, so when Vercel Agent proposes a patch it validates the change in a secure Vercel Sandbox with the repository's builds, tests, and linters before posting the suggestion.

Prefer linters when code can decide

Deterministic checks are preferred when a linter can enforce a rule reliably. They're fast, so developers and agents get feedback while working, instead of at a later review step. Code can count static options, so a linter can recommend radio buttons; naming a destructive action's object and consequence needs product context, so the skill handles that.

Vercel's rules catch patterns like:

  • Nested modals, which break focus management, keyboard navigation, and layering.
  • Selects with two or three static options instead of radio buttons, so choices stay visible.
  • Icon buttons and form controls without accessible names, plus custom focus rings that bypass shared tokens.
  • className overriding a design-system component's color, radius, or shadow, while layout classes are still allowed.
  • Long modal content not wrapped in Modal.Body, so scroll behavior and sticky headers/footers work.
  • Raw shadows replacing theme-aware Material classes, and borders duplicating a Material's built-in style.
  • Arbitrary spacing that falls off the 4px grid, suggesting a standard utility when one exists.

Each rule explains the problem and suggests a concrete fix; some autofix safely, like replacing deprecated Tailwind utilities. Accepted decisions take one of three forms: human-readable guidance next to the relevant Geist component (e.g., Checkbox best practices), agent guidance in product-design, or a lint rule when code can check it reliably.

Evals and keeping guidance current

Lint rules are deterministic, but agent behavior isn't. Vercel tests the skill against interfaces it hasn't seen: an agent edits a before state, a judge compares results against a rubric. Evals come from shipped examples documented in the skill; holdouts hide their expected edits to test generalization. Fixtures run without the skill to measure whether it changes behavior. Rule correctness is scored separately from similarity to shipped code, since shipped code may contain a flaw worth improving rather than reproducing.

Standards change as components, names, workflows, and failure states shift, so a weekly evidence-intake workflow collects design feedback from Slack, linking Figma files, PRs, review comments, and previews. It separates collection from judgment:

  1. A collector gathers messages, links, and nearby context, proposing no rules.
  2. A separate judge groups evidence, verifies sources, and notes open questions.
  3. The job creates a review packet with candidates, rejected topics, follow-ups, and coverage gaps.

Every candidate links to its source and stays pending until a human decides whether it becomes agent guidance, a lint rule, an example, an eval, or a no-change. A senior reviewer's comment can raise priority, but evidence is still required. Automation stops at the review packet; accepted changes go into the narrowest relevant file and pass checks before merging.

Building your own version

Start with repeated decisions

Pick one surface where the same review comments recur — destructive actions, error states, settings forms, empty states, navigation. Collect examples from shipped code and real reviews, and write down the decision, why it matters, exceptions, and the source. Avoid broad adjectives like clear or intuitive; agents need observable criteria. Destructive actions use Verb + Noun works; Buttons should be clear doesn't.

Add explicit triggers and boundaries

Tell agents when to load the skill in persistent repository instructions, and define covered files and surfaces alongside those to skip. In separate Next.js evals, agents failed to invoke an available skill in 56% of cases. Test the trigger separately from the guidance — failing to load and failing to follow are different problems. Ask agents to report which surfaces and references they loaded, then verify that findings cite them.

Separate routing, rules, and evidence

Keep a short entry point for identifying the surface and loading focused references. Organize details around surfaces reviewers already discuss: forms, modals, navigation, product vocabulary, workflow states, cross-surface patterns. Give rules stable IDs linked to examples and sources; record shipped examples with both good decisions and known flaws; maintain an explicit coverage-gap list so missing guidance stays visible.

Decide between linter and guidance

Use a lint rule when code can identify a problem reliably. Use agent guidance when the decision needs product or codebase context. Keep new standards, policy choices, and unresolved product decisions with people. Build training fixtures from documented examples and holdouts whose expected edits don't appear in the skill; test retrieval and application separately. If a rule accumulates exceptions or drifts unreliable, move it back to agent guidance.

Own the update loop

Review new evidence regularly, but require human approval before changing guidance or checks. Keep a decision log of what changed, why, and which source supported it. Treat new rules as product changes — review and test each, and retire those that stop helping.

What separates useful guidance from noise is choosing the first surface and encoding decisions the team already repeats. The pattern works once those decisions move from someone's head into a place agents can find them.