Raised while fixing #260 and #265, as "should a spec token carry the Figma key instead of the node id?"
Decided 2026-08-06: neither. A spec token carries a token name. Resolution is a lookup concern, not something the spec records.
The model
- The spec names a token. That name is the whole reference.
- The foundations / variables data — the fetched
*.variables.json, outside the spec — maps that name onto whatever a consumer needs: the Figma key for importing, the variable id, the resolved value.
- Resolution happens against that data at render time. Nothing about which Figma file a token came from, or how a given runtime imports it, belongs in the spec.
This also settles the provenance question the original framing raised. The name traces back to the foundations data, which is where the id and key live; the spec does not need to carry a second copy of either.
Already built
Render resolves by name and nothing else. The bridge builds a manifest from the fetched variables data mapping each token name to the handles it needs — the library key, and the id the variable has in the file it was fetched from — and resolution tries the key first, then the id.
Two things are deliberately not done, and both matter:
- The spec's
$extensions.com.figma.id is never read. Specs generated with TOKEN_FIGMA_EXTENSIONS or CUSTOM still carry it, but render ignores it, so every profile resolves identically.
- The target file is never searched by name. A file copied from the library holds duplicate variables under identical collection/name paths; matching those would bind the designer's copies instead of the library's own variables. The id fallback exists precisely so that rendering into the source file still works without name matching.
Open
- The reader must always emit a real token name. With name-only references this is a correctness precondition, not a quality goal. Every variable the reader cannot name is currently written as
[collection-name-unresolved]/Unavailable variable; a token carrying that path has nothing to resolve with and is simply unbindable. Today the recorded id rescues those cases — under this model nothing does. Needs a pass over why the reader fails to name a variable, and either a fix or an explicit, warned degradation.
- Styles.
Renderer.resolveStyleIds already resolves by name from the bridge manifest and imports by key, which makes it the accidental prototype of this model rather than an inconsistency. What remains is confirming REST exposes a usable key for every style type, and that the spec's inert id: 202:12 can be dropped without a consumer depending on it.
Out of scope
Token name collisions. Two collections sharing a name, or the same collection name across libraries, produce colliding paths. This is the responsibility of whoever authors or maintains the spec and the variable set, not something the pipeline will guard against.
Part of #204.
Raised while fixing #260 and #265, as "should a spec token carry the Figma key instead of the node id?"
Decided 2026-08-06: neither. A spec token carries a token name. Resolution is a lookup concern, not something the spec records.
The model
*.variables.json, outside the spec — maps that name onto whatever a consumer needs: the Figma key for importing, the variable id, the resolved value.This also settles the provenance question the original framing raised. The name traces back to the foundations data, which is where the id and key live; the spec does not need to carry a second copy of either.
Already built
Render resolves by name and nothing else. The bridge builds a manifest from the fetched variables data mapping each token name to the handles it needs — the library key, and the id the variable has in the file it was fetched from — and resolution tries the key first, then the id.
Two things are deliberately not done, and both matter:
$extensions.com.figma.idis never read. Specs generated withTOKEN_FIGMA_EXTENSIONSorCUSTOMstill carry it, but render ignores it, so every profile resolves identically.Open
[collection-name-unresolved]/Unavailable variable; a token carrying that path has nothing to resolve with and is simply unbindable. Today the recorded id rescues those cases — under this model nothing does. Needs a pass over why the reader fails to name a variable, and either a fix or an explicit, warned degradation.Renderer.resolveStyleIdsalready resolves by name from the bridge manifest and imports by key, which makes it the accidental prototype of this model rather than an inconsistency. What remains is confirming REST exposes a usable key for every style type, and that the spec's inertid: 202:12can be dropped without a consumer depending on it.Out of scope
Token name collisions. Two collections sharing a name, or the same collection name across libraries, produce colliding paths. This is the responsibility of whoever authors or maintains the spec and the variable set, not something the pipeline will guard against.
Part of #204.