← Omkar Khadamkar

AI drift, the field notes

AI-generated UI looks finished long before it is correct. These are field notes on the difference — four ways it goes wrong, and a fix for each.

REV 02 DATE 2026-07 FIVE CHAPTERS · EVERY INSIGHT ENDS IN SOMETHING YOU CAN TRY TODAY
In plain terms

A design system is the rulebook for how a product should look — what size a button is, how much space sits around a card. An AI can build screens from that rulebook. It gets the layout right and the details wrong: a button specified at 60 pixels comes out at 48. Plausible, proportional, wrong.

That gap is drift. On-system is its opposite — output that matches the rulebook. These notes cover the four ways drift shows up and what to do about each. Every card ends with something you can try today.

What gets missed is never layout. It is precision. Four classes of drift, each one invisible at a glance.

THREE OBSERVED IN PRACTICE · ONE MEASURED UNDER CONTROL · EVERY CARD SAYS WHICH

DRIFT CLASS 1 OF 4VALUE DRIFT

The number changes.

A control spec'd at 60px renders at 48px. Plausible, proportional, wrong — 48 is a believable height, which is exactly why nobody catches it.

Submit
SPEC · 60px
Submit
RENDER · 48px
OBSERVED IN PRACTICE — AND REPRODUCED UNDER CONTROL: CARD PADDING SPEC'D 20PX CAME BACK 16PX IN 3 OF 3 ISOLATED RUNS
TRY THIS

Bind every dimension to a named token the AI can point at — never a number it has to retype.

DRIFT CLASS 2 OF 4ATTRIBUTE DRIFT

The detail thins.

A 3px stroke returns as 2px. The component is right, the geometry is almost right — and almost compounds across every surface that inherits it.

Active tab
SPEC · 3px
Active tab
RENDER · 2px
OBSERVED IN PRACTICE — CLOSEST MEASURED ANALOGUE: A 4PX TAB GAP CAME BACK 8PX IN 2 OF 3 RUNS
TRY THIS

Give hairline values their own tokens. Thin details drift first, because a 1px miss looks like rendering, not error.

DRIFT CLASS 3 OF 4STYLE SUBSTITUTION

The treatment swaps.

A tint fill quietly becomes a solid. Same hue family, different treatment — a reviewer approves it at speed, an audit rejects it on sight.

SPEC · TINT
RENDER · SOLID
OBSERVED IN PRACTICE — NOT REPRODUCED UNDER CONTROL. THE CONTROLLED TEST PRODUCED NO COLOUR OR TREATMENT ERRORS AT ALL; EVERY FAILURE IN IT WAS A SPACING NEAR-MISS
TRY THIS

Name the treatment per state in the spec — "tint", not "light red". Adjectives are where treatments swap.

DRIFT CLASS 4 OF 4GAP FILLING

It never leaves a blank.

This is the one I could measure. Three isolated runs built the same five components from documentation that named zero values — 222 graded properties in total. Fifteen came out wrong. None came out missing, and not once did it say it was unsure. Every single gap got filled with something plausible.

222 properties15 wrong
VALUES IT GOT WRONG
left blank0
GAPS IT REFUSED TO LEAVE
flagged as unsure0
TIMES IT ASKED
MEASURED, NOT OBSERVED — 3 ISOLATED RUNS × 74 GRADED PROPERTIES. METHOD, DATA AND SOURCE AT OMKARUX.COM/DRIFT-LAB
TRY THIS

Take one property your docs never give a number for. Regenerate and look at what came back. It will not be empty — that is the whole problem.

Better prompts did not fix it. Documentation a machine cannot misread did.

THREE FINDINGS WITH TEETH

FIX 1 OF 3RULE COMPLETENESS

A rule without a value is not a rule.

"Never do X" says what to avoid and nothing about what to do, so the model fills the gap itself — confidently, and in the same wrong place every time. The exact value is what holds. A controlled reproduction isolated the two variables: naming the value moved first-build accuracy from 93% to 100%, while rephrasing the same information from prose into directives changed nothing at all. Negation may still cost you — models do get worse at negated instructions as they scale — but the failure I could measure was the missing value.

Never hardcode the badge height
NO VALUE · DRIFTS
badge height: var(--badge-h) = 24px, always
VALUE NAMED · HOLDS
ECHOED INDEPENDENTLY IN THE THREAD: JED SANFORD · MARIUSZ C. — CLAIM NARROWED AUG 2026 AFTER A CONTROLLED REPRODUCTION FOUND PHRASING MADE NO MEASURABLE DIFFERENCE
TRY THIS

Find one rule in your docs that names no value. Add the value, regenerate, and watch whether that property stops drifting.

FIX 2 OF 3VALUE BINDING

Named values beat typed numbers.

If the AI retypes "48" from memory, it can misremember. If it points at one named token, there is nothing to misremember. The discipline tokens always promised, now load-bearing.

retype"48"?"44"?
MEMORY · CAN MISREMEMBER
point--control-h60px
REFERENCE · NOTHING TO MISREMEMBER
CREDIT: SVETLANA HOLSTON — MY GUIDELINES WERE REWORKED BECAUSE OF THIS COMMENT
TRY THIS

Audit one component spec for retyped numbers. Replace each with its token name. Count what you find — the count is your risk surface.

FIX 3 OF 3NO BARE NUMBERS

A value with no token comes out right — as a number, not a name.

The two fixes above are about values that came out wrong. This one is worse, because the value is correct. Two of my components carry a shadow — different shadows, each used once. Neither cleared the bar for a token, because the bar is reuse. Writing the spec, the model flagged the gap and then wrote the raw number in anyway: 25 of that spec’s values point at a token, and this one is just a number. Correct, referenced by nothing, and re-emitted on every build. Failing the reuse test should mean “not a shared token yet” — not “a bare number”. Name every value; tokenise it when it turns up twice. And let the agent watch for the second time, because it reads the whole system on every pass and you do not.

no token0 2px 0 rgba(0,0,0,.1)
CORRECT · UNREACHABLE
--shadow-a0 2px 0 rgba(0,0,0,.1)
NAMED · FINDABLE
FROM A PRODUCTION SYSTEM, AUG 2026 — OBSERVED, NOT MEASURED. THE CONTROLLED TEST CANNOT PRODUCE THIS ONE: EVERY VALUE IN IT HAS A TOKEN
TRY THIS

Find one raw value in your specs that never earned a token. Give it a name now, even a local one — and add “tell me when a value appears in a second component” to your reviewing agent’s standing instructions. Let promotion be detected, not predicted.

The verification taught me more than the finding did. Five rules for testing without fooling yourself.

WHERE MOST OF MY REGENERATION BUDGET WENT

RULE 1 OF 5DON'T LEAD THE WITNESS

Never name the fix in a regeneration prompt.

Prompts name pages and point at the system — never the correction. If the fix lands unprompted, your documentation captured it and activated it. Name it, and you have tested your prompt, not your system.

"make the button 60px tall"
TESTS YOUR PROMPT
"rebuild the page from the current spec"
TESTS YOUR SYSTEM
TRY THIS

Regenerate one page from a generic "match the spec" prompt. Count what lands unprompted — that count is your documentation's real score.

RULE 2 OF 5TRIAGE

Every miss has one of two causes.

They need different repairs — diagnose before you fix, or you fix the wrong layer.

A MISS. DID THE MODEL TOUCH IT?
NO → RULE NEVER FIRED
ACTIVATION FAILED
STRENGTHEN THE TRIGGER
YES → STILL WRONG
CAPTURE FAILED
FILL THE SPEC GAP
TRY THIS

Label every miss in your next review "untouched" or "touched". Untouched: make the rule fire. Touched: fix the spec.

RULE 3 OF 5THE DELETION GAP

Addition is cheap. Deletion needs an explicit instruction.

In revision passes, models happily add what the spec defines but rarely remove what it does not. Stale UI survives every update unless subtraction is itself a positive directive.

ABC ← NOT IN SPEC
AFTER REVISION · C SURVIVES
AB+ "REMOVE WHAT THE SPEC DOES NOT DEFINE"
C IS GONE
TRY THIS

Add one line to every revision prompt: "remove whatever the spec does not define." Watch what disappears.

RULE 4 OF 5CHECKSUM TRIPWIRE

One control's width doubles as a checksum.

A single pixel off means the token chain broke somewhere upstream — long before a human eye would catch it.

69px ✓ CHAIN HELD
70px ✗ CHAIN BROKE
TRY THIS

Plant one odd-valued dimension as your tripwire. Check it first on every regeneration — it audits the whole chain in one glance.

RULE 5 OF 5ORDER OF REVIEW

Verify foundations first.

Color and token errors ripple into every downstream component. Settle upstream truth before judging components, or you will re-review everything twice.

1 · TOKENS & COLOR
2 · FOUNDATIONS
3 · COMPONENTS
4 · PAGES
TRY THIS

Re-order your next review bottom-up. If the color page is wrong, stop — nothing above it is worth judging yet.

Nobody disagrees drift exists. Teams disagree on where to kill it. Six enforcement points along one pipeline — tap each to see whether it fits your stack.

BUILT FROM 60 PUBLIC COMMENTS AND PRACTITIONER CALLS · EVERYONE CREDITED

DESIGN
SYSTEM
DOCS &
TOKENS
GENERATION
OUTPUT
PRODUCTION

A · Make the documentation machine-readable.

Acts at the docs & tokens stage. A guideline per component, positive directives, exact values, and a stylesheet generated from the production token file — one token truth. Verification is a library diff on every regeneration.

FITS IFYou cannot touch the repo. This is the point that works when everything downstream is fenced.
PRACTITIONER: THIS IS MY SETUP

B · The agent places the real component.

Acts at the generation stage. Drift dies at the source: the agent uses the actual coded component instead of re-creating it. "It IS the actual design system component."

FITS IFYou live in the repo, and your components are coded, current, and importable.
MATÍAS MARTÍNEZ BOYLSTON · AKSHAY KHURANA · ANGEL J. SÁNCHEZ

C · Point, don't describe.

Acts at the docs → generation seam. Tell the agent where to look — tokens in JSON, YAML, CSS, plus a usage spec — instead of rebuilding components from prose. Rebuild-from-description varies run to run no matter how stringent the text.

FITS IFYour sources are pointable: structured token files exist, even if the production code is off-limits.
JIMMY LEONARDSSON · NICK ANDREWS

D · A reviewer agent audits the generator.

Acts at the output stage. Generator agent plus a separate reviewer/validator agent — a common pattern in practice, and common is not a demotion: it is the most portable point on the map, because it asks nothing of your stack. A manual diff loop is the human-powered version.

FITS IFAnything. Start here if you can start nowhere else.
SVETLANA HOLSTON (WHO RIGHTLY CALLS IT COMMON) · MAYANK AGARWAL

E · AI edits the description. A machine builds the code.

Replaces the generation stage. What I would call a deterministic materializer: the AI works on a description of the system, and a deterministic factory generates the code — generation variance disappears entirely.

FITS IFYou can adopt a factory toolchain, or build one. Watch this space; it is where the vendors are heading.
ARCHITECTURE: ANDREI HERASIMCHUK'S SELDON · ADJACENT: REWEAVER, DEMBRANDT

F · Replace the token layer itself.

Acts at the design system root. Audit the production system, swap in an AI-legible token layer, confirm compatibility, ship. Authority is won by evidence, not negotiation: read-only access, a staging proof, dark mode as the diagnostic, then the CTO.

FITS IFToken authority is held — or winnable. Run in greenfield and legacy both.
SVETLANA HOLSTON · REDICA, METRC
THE META-PATTERNNO UNIVERSAL GUARDRAIL

Each stack and each generation point needs its own enforcement point, or a combination. The question is never "how do I stop drift" — it is "which enforcement points can my stack actually support."

CREDIT: MAURÍCIO JUBA
TRY THIS

Mark the two points your stack can support today. Run them in combination — one upstream, one downstream.

Two questions still open. This is where the notes grow next.

CORRECTED IN PUBLIC, TWICE — AND BETTER FOR IT

OPEN PROBLEMUNSOLVED

If the docs regenerate themselves, what stays true?

Self-supporting documentation loops: docs that produce their own next source. If output becomes input, drift compounds quietly — unless something outside the loop stays truth. My anchor today: the library, plus a diff on every regeneration.

DOCS
GENERATE
RE-INGEST
ANCHOR: THE LIBRARY
PROVOCATION: VARYA STEPANOVA · ANSWER: NOBODY HAS ONE YET
TRY THIS

If your docs feed their own regeneration, name your outside anchor now. No anchor, no truth — only momentum.

THE BOUNDARYSPLIT TRUTH

Split authority is not the problem. Split truth is.

The real boundary is not legacy vs greenfield — it is how token authority is held. Where it is shared, fidelity lives in the contract: one canonical, executable token layer — design owning semantic intent, engineering owning production integrity.

DESIGNSEMANTIC INTENT
ENGINEERINGPRODUCTION INTEGRITY
ONE CANONICAL, EXECUTABLE TOKEN LAYER
CONTRACT PHRASING: SVETLANA HOLSTON
TRY THIS

Write down, today, who owns semantic intent and who owns production integrity for your tokens. If two systems hold truth, drift already lives between them.

Is your enforcement point on this map?

If your team kills drift at a point not shown here — or you have watched one of these six fail — I want to hear about it. The notes grow as the conversations do.

FIELD NOTES v2 · OMKAR KHADAMKAR · DESIGN SYSTEMS × AI-NATIVE DOCUMENTATION
SOURCED FROM PUBLIC THREAD COMMENTS AND PRACTITIONER CALLS · EVERYONE CREDITED BY NAME · METHOD OVER INTERNALS THROUGHOUT
OMKARPKH@GMAIL.COM · OMKARUX.COM