[research@ai4se] : ~ $
cd ../
[methodology] | | 14 min

# Lean Design-Dev Handoff: Cutting AI Codegen Token Cost by an Order of Magnitude

Traditional design-dev handoff relies on screenshots and natural language, so agents re-parse, re-translate, and re-emit redundant styles every turn. This article starts from three root waste patterns, proposes a tool-agnostic Design-to-Code DSL stack and HCP tiering, and uses Figma Dev Mode + MCP + Code Connect as a validation instance.

[agentic-engineering][context-engineering][dsl][handoff][mcp-protocol][cost]

A developer drops a design screenshot into Cursor with “implement this page using @corp/ui.” One pass burns 8K input and 5K output; two correction rounds add another 20K. The result is <div> soup until someone rewires it to the enterprise component library.

The bottleneck is usually not the model. It is that handoff has no reusable structured semantics—design outputs one-off visual facts, engineering re-translates every time, and the agent re-derives styles and component choices on every run. Using LLM cost fundamentals: bill = unit price × tokens × workflow amplification; handoff amplifies repeated work.

This article proposes a tool-agnostic lean design-dev handoff strategy: a Design-to-Code DSL stack targets three token waste roots; HCP (Handoff Context Pack) tiers DSL for agents. The body is abstract theory plus practice; Figma appears only at the end as a validation instance—Dev Mode + MCP + Code Connect show why the abstract approach cuts tokens.

Three root causes of token waste

Traditional handoff (screenshots, annotations, verbal specs) is expensive because three things are not reusable:

Root wasteTraditional handoffToken impact
① Design-layer info not reusableColors, spacing, type live as pixels/hex in layers; tokens not bound to code semanticsAgent re-parses visuals or re-explains “which variable is 4px?” every turn
② Repeated dev-side translationNo stable design component → @corp/ui mapping; every handoff re-aligns “which component, which props?”Architecture re-explained each chat; output is divs first, refactor later
③ Repeated redundant style generationNo global cache; one button change resends full page; agent re-emits existing layout/CSSFull input resend; output duplicates known structure

These compound: ① inflates input, ② inflates input and output, ③ makes incremental work look like full-page regen. A fourth pain—late validation (props wrong at CR)—follows from missing structure: no grammar, no compile-time checks, ×2–4 correction rounds.

Abstract fix: a reusable, cacheable, diffable Design-to-Code DSL stack:

① Non-reusable design info  →  Token Registry (design tokens bound to code syntax, globally cached)
② Repeated translation      →  Component Registry + Intent DSL (map once, hand off semantics)
③ Redundant regen           →  HCP tiering + Delta-First (L0/L1 cache, L3 sends only change)

Below, domain-specific languages (DSL) make these three mechanisms executable. DSL delivers high signal density, clarity, diffability, validationcontext engineering applied to handoff.

Practice—same Hero CTA, natural language vs Intent DSL:

# Natural language (~150 tokens, drifts each turn)
"Primary large button, loading, label 'Get Started', use company Button,
 import @corp/ui, track home_cta_click, i18n home.cta.start"

# Intent DSL (~40 tokens, deterministic, diffable)
component: ECR.corp.button
props: { variant: primary, size: lg, loading: false }
slots: { label: i18n:home.cta.start }
analytics: { event: home_cta_click }

Design-to-Code DSL Stack: the handoff domain language

Theory: handoff is not translation—it is a missing domain language stack. Five DSL layers address one or more waste roots; together they form UDHP (Universal Design Handoff Protocol).

Design ──► Handoff ──► AI gen ──► Verify
   │           │          │          │
Component DSL  Intent DSL Generation Verification
Architecture DSL  │        DSL        DSL

            HCP serialization
DSL layerArtifactWaste addressedQuestion
Component DSLECR ManifestWhich code component? props/slots?
Architecture DSLArchitecture ProfilePage templates, paths, cross-cutting rules?
Token RegistryDesign token → code syntaxDoes primary map to var(--color-brand)?
Intent DSLDID②③What should this screen implement? (diffable)
Generation DSLEmission Profile②③How does ECR id become enterprise code?
Verification DSLContract SpecLate validationIs output compliant?

Token Registry can live inside ECR or stand alone; the rule is tokens must bind code syntax, not just hex.

Agent role: from “vision + guess architecture + codegen” to parse Intent DSL → lookup Generation DSL → fill props.

Practice: minimal YAML skeleton:

tokens:
  color.brand: { figma: "primary/500", code: "var(--color-brand)" }
  space.16: { figma: "spacing/md", code: "var(--spacer-4)" }

components:
  corp.button:
    import: "@corp/ui/CorpButton"
    props:
      variant: { type: enum, values: [primary, default, text], default: default }
      size: { type: enum, values: [sm, md, lg], default: md }
    slots:
      label: { type: i18n-key, required: true }

nodes:
  - id: hero-cta
    component: corp.button
    props: { variant: primary, size: lg }
    slots: { label: i18n:home.cta.start }
    tokens: { background: color.brand }

emission:
  components:
    corp.button:
      import: "import { CorpButton } from '@corp/ui'"
      template: |
        <CorpButton variant="{props.variant}" size="{props.size}">
          {t('{slots.label}')}
        </CorpButton>

contract:
  nodeId: hero-cta
  mustImport: ["@corp/ui"]
  props: { variant: primary, size: lg }

HCP: lean transport for the three wastes

Theory: HCP is not another design format—it tiers DSL for agents, directly targeting ③ with ①② cache reuse.

In Harness Engineering: Token Registry + Component DSL = Guides; Verification DSL = Sensors; HCP = Lazy Loading for Context.

HCP tierContentWasteTransport
L0Token Registry + Component DSL summary①② global reuseMCP Resource, content-addressed cache
L1Generation + Architecture DSL② project reuseMCP Resource, project cache
L2Intent DSL subtree③ scope sliceinline, task nodes only
L3Delta + instruction③ incrementalinline, since → current diff

Three rules: Reference-not-Inline (L0/L1); Scope Slicing (L2); Delta-First (L3).

Practice: HCP sketch + agent hook:

hcpVersion: "1.0"
cacheKeys: { l0: "ecr:sha256:a3f8...", l1: "profile:corp-web:v2" }
task: { scope: "node:hero-cta", since: "did:v3" }
context:
  l0Ref: "ecr:sha256:a3f8..."
  l1Ref: "profile:corp-web:v2"
  l2Inline:
    nodes:
      - id: hero-cta
        component: corp.button
        props: { variant: primary, size: lg }
        slots: { label: i18n:home.cta.start }
  l3Inline:
    delta: { props.size: { from: md, to: lg } }
async function implementFromDesign(task: Task) {
  const ecr = await mcp.readResource(`ecr://${task.ecrHash}`);
  const profile = await mcp.readResource(`profile://${task.projectId}`);
  const hcp = await udhp.pack({ did: task.did, scope: task.scope, since: task.since });
  const code = await agent.generate({ ecr, profile, ...hcp });
  return udhp.verify({ code, contract: hcp.verification.contract });
}

Design-phase constraints: private architecture before handoff

Theory: “Use @corp/ui” in a prompt cannot stop freeform rectangles or div output. DSL grammar + design-time lint moves ② translation left.

MechanismWastePractice
Constrained paletteDesigners only instantiate ECR components
Token → code syntaxTokens store code:, not hex
Props bindingPicking props = writing an API call
Design-time lintLate validationudhp lint did.yaml --ecr ecr.yaml
Emission Profile②③Lookup, don’t guess imports

Three gates: design lint → handoff lint → contract verify. Any failure stops before AI spend.

udhp ecr extract --from "./node_modules/@corp/ui/dist/index.d.ts" --out ./handoff/ecr.yaml
udhp emission init --from "./src/pages/Home/index.tsx" --ecr ./handoff/ecr.yaml --out ./handoff/emission.yaml

Token math: traditional vs DSL + HCP

ModeApproachTotal tokensThree wastes
V1 TraditionalScreenshot + NL11K–28K/pageAll three
V2 Semi-structuredFull JSON + arch notes5K–13K① partial
V3 DSL + HCPL0/L1 cache + L2/L3 inline0.8K–3K/pageTargeted removal
V3 IncrementalL3 delta only80–250/run③ eliminated

MVP: zero design-tool APIs

udhp ecr init --out ./handoff/ecr.yaml
udhp did import --asset ./design/home.png --map ./handoff/mapping.yaml --out ./handoff/did-home.yaml
udhp lint ./handoff/did-home.yaml --ecr ./handoff/ecr.yaml
udhp pack ./handoff/did-home.yaml --scope "node:hero-cta" --profile ./handoff/emission.yaml -o ./handoff/hcp.yaml
udhp verify --code ./src/pages/Home/HeroCta.tsx --contract ./handoff/contracts/hero-cta.contract.yaml

Designers annotate via a neutral mapping UI; tool adapters are P2.

Anti-patterns and action list

Anti-patternWaste
Screenshot handoff①②③
Tokens as hex only
Re-explain component library each prompt
One prop change, full page resend
Merge without contract verifyLate validation

Five steps: extract ECR → Emission Profile → annotate one DID → lint → pack → verify → register L0/L1 on MCP.


Validation instance: Figma Dev Mode + MCP + Code Connect

Figma is not a prerequisite. It shows why its handoff mode beats traditional delivery on tokens—it implements the three abstract fixes inside one product.

Abstract fixFigma implementationWhat it validates
① Reusable tokensDev Mode Variables + code syntax: inspect shows var(--spacer-2) not 4px; tokens bound to codeDefine once, reference in dev—no agent re-parsing pixels
② Translate onceCode Connect: Figma instances map to production components; Dev Mode shows real imports and prop mappingsTranslation at config time; handoff carries semantics
③ Tiered contextMCP Server: get_metadata then scoped get_design_context; structured tree replaces screenshotsNo full dump; Scope Slicing controls tokens

Figma’s internal DS team reported the same for ①: before Dev Mode, devs guessed which CSS variable matched a style; now inspect shows var(--color-icon-onbrand) directly—repeat translation token cost → zero.

vs UDHP: Figma validates that all three fixes work and compose; it does not offer tool-agnostic Intent DSL, full HCP tiered cache, or open Generation DSL for private @corp/ui (MCP defaults to React+Tailwind). UDHP generalizes Figma’s pattern—any tool exporting raw assets can run the MVP; Figma users treat Code Connect as a Component DSL adapter and layer HCP tiering on MCP.

Close

Design-dev handoff token pain looks like “AI is expensive”; the roots are three non-reusable layers: tokens, component translation, generation context. The lean strategy is Design-to-Code DSL stack + HCP tiering so agents consume structure, not screenshots.

Theory: three wastes → three abstract mechanisms → five DSL layers + HCP.

Practice: extract ECR from @corp/ui, write Emission Profile, run lint → pack → verify, register L0/L1 on MCP. Figma Dev Mode + Code Connect + MCP is a worked example that the road is real—the protocol still belongs to your team.

References