# Capability 3 (AI code → Figma) — bypass implementation

> **Status**: 2026-05-19. Documented for v1.0 release per user decision 2a: Figma plan stays Professional (no upgrade to Org/Ent), so the originally-planned `figma connect publish` route is permanently unavailable. This doc describes how the **same project goal** is achieved without that route — and where the gaps are.

---

## Background

[`PROJECT_GOAL.md`](./PROJECT_GOAL.md) §一句话目标 says:

> AI 拿 **代码页面** → 生成 Figma 效果图

The originally-planned implementation: Figma Code Connect (`figma connect publish` upload mappings + Figma Dev Mode renders code-side preview / round-trip). This requires a Figma Organization or Enterprise plan seat for the developer.

**Project constraint** ([`AGENTS.md` §项目约束](../AGENTS.md)): TVU's Figma plan is Professional. `figma connect publish` returns 401; Dev Mode code-side preview unavailable. Confirmed by [t4-spike-validated 复盘 2026-04-30](./internal/retrospection/2026-04-30-t4-spike-validated.md) Phase 6.

User decision 2026-05-19: **No plan upgrade planned**. Project ships v1.0 without Code Connect.

---

## Bypass — what we have instead

The bypass relies on **canonical SoT alignment** plus **deterministic mapping audit** — both already shipped.

### Key pieces (all in current code)

#### 1. Canonical layer alignment (AGENTS.md 硬规则 #6)

> "AI 工具从 Figma URL 生成代码必须用 `src/canonical/*` 作为 SoT"

Every public component has a `src/canonical/<Name>.vue` (or `<Name>Bridge.vue`) whose props are **explicitly the Figma 真源 variant axes**. Example: `ButtonBridge.vue` exposes 8 axes (`color`/`style`/`radius`/`fixedWidth`/`status`/`icon`/`size`/`theme`) matching Figma component property names 1:1.

This means: **given a code component instance, you can read off its Figma variant address by inspecting its canonical props**. The mapping is bi-directional and deterministic.

#### 2. Translation layer (`src/design-system/translation/`)

Four explicit truth files registered as v1-stable:

- `axis-implementation-map.json` — Figma axis name ↔ canonical prop name (16 instances)
- `prop-aliases.json` — code-side prop name aliases (102 entries)
- `divergences-decisions.json` — explicit Figma↔code differences with resolution status (26 decisions)
- `token-aliases.ts` — Figma style/Variable name ↔ CSS custom property name

Audit `audit:translation-completeness` (10th strict gate in `prepublishOnly`) verifies the maps stay coherent. Any code change that introduces a new prop without registering it → CI fails.

#### 3. Generators (`figma-sync/`)

- `generate-docs-figma-members.mjs` — produces structured component manifests in `figma-data/normalized/docs-figma-members/<component>.json`. Each manifest entry has Figma node ID + axes + variants. This is the **machine-readable contract** between Figma and code.
- `generate-canonical-specs.mjs` / `generate-canonical-wrappers.mjs` — produce canonical wrapper skeletons from Figma data.
- `figma-url-to-canonical.mjs` (Tier 2-E) — CLI that takes a Figma URL → finds the node → looks up the corresponding canonical component + axes.

#### 4. Audits

- `audit:published-vs-code` — verifies every Figma component published as remote library has a matching code component. Mismatches block release.
- `audit:figma-conformance` — verifies code prop names match Figma axis names per translation/* registry.

### How a consumer (AI agent or human) uses this for "code → Figma" intent

**Scenario**: someone has a code snippet `<Button canonicalStyle="ghost" canonicalColor="red" canonicalSize="M" canonicalRadius="round">Delete</Button>` and wants to know where in Figma this corresponds.

**Bypass workflow**:

1. Read the canonical props → axes are explicit: `style=ghost, color=red, size=M, radius=round`.
2. Look up `figma-data/normalized/components.manifest.json` → find `Button` family → find variant matching those axes → get Figma node ID + URL.
3. Open Figma file at that node ID — that's the canonical Figma representation.

This is fully scripted via `scripts/figma-url-to-canonical.mjs` in reverse direction (i.e., the same lookup table powers both directions). To formalize a reverse CLI:

```bash
# proposed (not yet implemented; ~30min wrap on existing script if desired)
pnpm exec node scripts/canonical-to-figma-url.mjs Button \
  --canonical-style ghost --canonical-color red \
  --canonical-size M --canonical-radius round
# Outputs: https://www.figma.com/design/<file>?node-id=<id>
```

### What the bypass does NOT give you

Compared to the full Code Connect path:

| Code Connect (unavailable on Pro) | Bypass (current) |
|---|---|
| Figma Dev Mode shows code snippet inline | ❌ — must open canonical .vue source separately |
| Visual diff between code render and Figma node | ⚠️ — handled by visual regression Playwright baseline, but only inside this repo (not as Figma plugin live diff) |
| Designers can `figma connect publish` to update mappings without running TS | ❌ — mapping is hand-maintained in `translation/*` + audited in CI |
| AI agent reads Figma node → component mapping from Figma itself | ⚠️ — agent reads it from `figma-data/normalized/components.manifest.json` instead (same data, different access path) |

The bypass covers the **machine-readable mapping** completely. The gap is **interactive Figma-side rendering of code**. For AI agents that don't need interactive rendering, the bypass is complete.

---

## v1.0 commitment

For v1.0.0:

- Capability 3 is **partially shipped via bypass** — sufficient for AI agents using the documented manifest + canonical layer.
- Full Code Connect (Figma Dev Mode live preview) is **not in scope for v1.0**.
- If Figma plan is upgraded to Org/Ent post-v1.0, a future minor (v1.x) may add Code Connect publish step as additive feature (non-breaking; opt-in).

This is explicitly noted in [`API_STABILITY.md`](./API_STABILITY.md) under "Promise scope — does not cover".

---

## Reverse direction (Figma → code) — already complete

For symmetry: capability 2 (AI Figma URL → code) is fully shipped:

```bash
pnpm exec node scripts/figma-url-to-canonical.mjs <figma-url>
# Outputs: { figmaName, codeNames, variantAxes, matchedVariantProps }
```

This is the harder direction and works without any plan upgrade. The forward direction (this doc) reuses the same data path.
