<!--
Responsibilities:
- Register the affordance/synonym layer as a project SoT distinct from Figma-derived layers.
- Clarify that the layer's primary consumer is AI (not designer / not dev), and that AI authors with user review.
- Tell auditors which file to inspect + which consumer paths are valid.
-->

# Affordance / Synonym Layer — Project SoT Registration

**SoT location**: [`docs/internal/affordance-categories/`](../../../docs/internal/affordance-categories/) — a **sharded directory** (the former single `affordance-categories.json` grew to ~14.8k lines and was split by Figma icon folder):

- `_index.json` — the non-icon sections (`_meta` + `affordance_categories` taxonomy)
- `<Folder>.json` — one shard per Figma icon folder (the `path` top segment, e.g. `Edit/Add.svg` → `Edit.json`), holding that folder's icon entries

The **directory is the single logical SoT**. All consumers read it through [`figma-sync/lib/affordance-sot.mjs`](../../../figma-sync/lib/affordance-sot.mjs) → `loadAffordanceSot()`, which reconstructs the legacy merged shape `{ _meta, affordance_categories, icons }` so call sites that read `sot.icons` / `sot._meta` are unchanged. `writeAffordanceSot()` is the inverse (re-shards by folder, idempotent round-trip). Do not hand-edit shards in a way that bypasses the loader's grouping (one `path` folder → one shard).

**Layer purpose**: AI-consumption metadata for icon (eventually component) selection — when AI needs to pick an icon for an affordance, this is the lookup table. Maps natural-language intent ("I need a delete button") → canonical icon binding for both code-side (`<TvuIcon name="...">`) and Figma-mockup-side (`figma.importComponentByKeyAsync(...)`) consumer paths.

---

## Why this layer is NOT Figma-derived

Three of the four translation/ files here (`icon-aliases.ts`, `prop-aliases.*`, `token-aliases.ts`, `divergences.*`) bridge **designer-owned semantics** in Figma to code consumption. The affordance/synonym layer is different:

| Layer | Owned by | Stored in | Why |
|---|---|---|---|
| visual / token / variant / SVG path | designer | Figma library | Designer is the authority on visual decisions |
| icon-aliases.ts (name translation) | dev | code | Code-side cleanup of Figma's name idioms |
| **affordance / synonym / when-to-use** | **AI (authoring) + user (review)** | **`docs/internal/affordance-categories.json`** | **AI is the primary consumer; designers haven't filled the Figma `description` field for 650+ icons (extract shows description="" for all)** |

The Figma `description` field could in principle hold this data — but it's empty by historical practice and demanding designer backfill would block this layer indefinitely. The layer is registered here as a **code-side bridge artifact**: AI authors candidate synonyms / categories / usage notes, user reviews + accepts, the file becomes canonical. No Figma dependency for authorship.

---

## What's in the SoT file

Per-icon fields fall into 3 groups:

| Group | Fields | Consumer |
|---|---|---|
| Binding (where this icon lives) | `path` · `registry_name` · `figma_component_key` · `figma_name` · `figma_node_id` | code AI (uses `registry_name`) + mockup AI (uses `figma_component_key`) |
| Shared semantics (one text, dual use) | `visual_signature` · `affordance_category` · `synonyms_en` · `synonyms_zh` · `when_to_use` · `notes` | both AI consumers |
| Schema metadata | `_meta.schema_version` · `_meta.consumer_paths` · `_meta.future_extensions` | maintainers |

🟡 **marker convention**: synonyms suffixed `🟡 ai-generated` are AI-authored awaiting user review. Once user accepts, the marker is removed. The marker does NOT mean "Figma description missing" — that framing was over-corrected away in v0.4.0.

---

## Consumer paths

### Code AI

1. Match intent → entry in `icons[]`
2. Read `entry.registry_name` — if non-null, write `<TvuIcon name="<registry_name>" />`
3. If `registry_name === null`, the icon is a manifest-expansion candidate (see `entry.notes` for proposed canonical name); gate on plan-owner promoting the entry into `src/icons/manifest.ts` first

### Mockup AI (`use_figma` plugin context)

1. Match intent → same entry
2. Read `entry.figma_component_key` → `await figma.importComponentByKeyAsync(key)`
3. `.createInstance()` and place in frame; `entry.figma_name` is preserved automatically as instance name

Both paths share `entry.when_to_use` / `entry.affordance_category` / `entry.synonyms_*` for intent matching upstream of the binding lookup.

---

## Audit hooks

This file is a translation-layer SoT and follows the same audit treatment as `divergences.md`:

- Adding a new icon entry without filling shared-semantic fields → audit warning (future)
- Promoting an entry to `src/icons/manifest.ts` requires the entry's `registry_name` to flip from null to the canonical manifest name in the same change
- 🟡-tagged synonyms are tolerated indefinitely (they don't break consumers), but a sweep batch should run alongside every schema bump

---

## Future extensions (not in scope yet)

Schema v0.4.0 is icon-only (1 component → 1 variant). Extending to Notification / Button / Tag-class multi-variant components will require a `figma_variant_props` field mapping code props ↔ Figma variant props. See `_meta.future_extensions` in the SoT file for the trigger marker.

---

## Related

- [`icon-aliases.ts`](./icon-aliases.ts) — name-translation layer (different concern: maps code-facing alias → Figma component name; doesn't carry affordance/synonym semantics)
- [`divergences.md`](./divergences.md) — Figma↔Code value-shape divergences (different concern: explains intentional mismatches)
- [`docs/FIGMA_AS_SOURCE_OF_TRUTH.md`](../../../docs/FIGMA_AS_SOURCE_OF_TRUTH.md) — the higher-order principle; this layer is a registered code-side carve-out, not a violation
