es:figma-craft — Figma canvas engineering, project-agnostic
This file is the router. It holds the pipeline, the laws, and a load-table. Details live in exactly one reference each — load on demand.
Two-part architecture (how this stays generic)
- This skill = the universal layer. Laws, pipeline, Plugin-API failure modes, component-system rules, verification protocol, knowledge-sync protocol. Nothing here names a specific file, token, or font.
- A per-project adapter = the specific layer. Each project ships a thin skill/doc declaring: Figma file name+key, page layout, token collections, fonts, component registry path, copy rules, overlay idioms. Template: project-adapter-template.md. No adapter → create one from the template before the first mutation.
Bridge selection (which hand touches the canvas)
| Bridge | Use for | Notes |
|---|---|---|
design:os figma-agent CLI (Ease Design Figma Agent plugin) | ALL mutations when the project is design:os-onboarded | Preferred canonical hand. figma-agent status must show plugin.connected:true for the RIGHT file, checked 2× ~15s apart before long batches. Prefer typed ops (clone-traits, inspect) over raw exec-js. |
use_figma MCP | Mutations where no figma-agent is wired; reads | MANDATORY: load the figma-use skill first, every call. |
html_to_design (DesignAgent) | Bulk HTML→Figma imports of authored screens | HTML-first authoring pipeline; not for surgical edits. |
MCP read tools (get_metadata/get_screenshot/get_design_context) | Diagnosis, recon, verification | Cheapest-first: metadata → screenshot → design_context (last resort, 25K-token cap). |
Every mutation script opens with a file guard: if (figma.root.name !== '<expected file>') throw — multi-plugin brokers can route to the wrong file.
The pipeline (5 phases — every build)
0. PREFLIGHT Adapter loaded · bridge alive (status ×2) · registry/docs read · reconcile if user changed the file 1. RECON Cheapest-first read of source + target. Element→component mapping table BEFORE any code. 2. ENHANCE Close component gaps by the creation ladder (reuse → override → variant → new; owner approval for shared-master changes) 3. BUILD Sequential, one section per call, idempotent scripts, tag every created node, position explicitly (never (0,0)) 4. VERIFY 3 layers: node-read asserts → numeric geometry asserts → FRESH PNG read by eye. Structural ops verified in a NEW call. 5. CLOSE Reconcile registry/docs in the same change · distill ≤1 durable lesson · report honestly (misses as misses)
Detail per phase: pipeline.md.
The 13 laws (gates, not advice — each one is a paid-for scar)
- Verified = measured. Screenshots are for humans; geometry gets numeric asserts, structure gets metadata reads. Node-read AND rendered-PNG can BOTH lie — when they disagree, measure bounding boxes.
- Fresh PNG is the final arbiter of every mutation batch. Numeric asserts passing is not done; look at the image with your eyes. Verify against FRESH artifacts (check mtime / re-export).
- Read before write; capture before destroy. Dump content to JSON before replacing/instancing anything carrying data. Never destroy a rollback you haven't examined.
- Resolve by NAME (or plugin-data tag), never cached id. Node ids renumber on sync and can be transient within a session. Target by parent chain, not bare name, when names repeat.
- A failed/timed-out call may have mutated the file. Re-read state before ANY retry. After a mid-batch disconnect: inventory first, rerun second. Scripts must be idempotent.
- Structural ops (create/combine/reparent) are only proven by a NEW call. Same-call re-fetch can report success for a mutation that never persisted. Verify set child counts + variant names, then sweep for orphan byproducts.
- Fix the class, not the symptom; fix at the master. Defect in an instance → suspect the master; then sweep siblings for the same latent bug. Audit an atom's internals before composing with it. Sweep everything once — not per-complaint.
- One origin per geometry. Never generate the same geometry from two code paths. One spec constant, one script.
- Real instances only + the creation ladder (reuse → instance-override → add-variant → create-new last). Shared-master/component changes need OWNER APPROVAL before minting. Normalize construction BEFORE componentizing (no spacer frames — spacing = auto-layout gap).
- No try-catch that swallows mutation errors. Sizing/props failures throw or collect into the returned output. A green gate that is blind is worse than a red gate.
- Sequential canvas, single hand. One file, one plugin connection, rate limits: never fan out parallel agents against a Figma file. Multi-screen work = a checklist driven step-by-step.
- Respect the owner. A value the owner re-changed 2× after you set it = owner intent — stop, check the decision ledger, ask once. Never delete content you didn't author without confirmation. Their DS/conventions win over your spec.
- No instance-level style overrides. Fills, strokes, effects, corner radius, typography and text-case on an instance (root or sublayer) come from the master/variant/token — never set them on the instance to fake a look. Need a different look → climb, don't override: missing token → mint the token; missing variant → add the variant (owner approval for shared masters, per law 9); missing component → build one. Instance overrides are for DATA only: text characters, icon swaps, variant switches (
setProperties), visibility of layers designed to toggle, and per-instance geometry already codified as a pattern (e.g. detached meter fills — and a detach still gets declared as debt). Verify: compare instance paints against master paints (root + text sublayers) — the diff must be zero.
Full thinking layer (8 operating gates + rationale): operating-gates.md.
Reference docs — load by task signal
| Doc | When to load |
|---|---|
| operating-gates.md | Every session, before the first mutation — the thinking layer |
| pipeline.md | Every build — per-phase detail, MCP/call budgeting |
| plugin-api-gotchas.md | Before writing ANY exec-js / use_figma script — 6 classes of API failure modes with fixes |
| component-system.md | Any componentize / variant / table / shell decision — ladder, extraction heuristics, column-first tables, shell+slot, template-first |
| screen-composition.md | Building any full screen — shell + slot anatomy, lift masters from live instances, state axes, overlays in wrapper frames, icon-swap carve-out, helper-library contract |
| drawer-decision-tree.md | Composing any drawer/dialog body — derive the tree from the file, width tiers, purpose rows A–E, forbidden list |
| section-presentation.md | Placing screens in a section, notes, tags, archiving, data-coherence gate, owner-mockup → DS rebuild |
| verification-protocol.md | Closing any batch — the 3-layer verify, false-green catalog, sweep disciplines |
| build-script-standard.md | Writing any mutation script — the canonical script anatomy (guards → idempotency → spec constants → mutate → assert → structured return) + anti-pattern table |
| script-helpers.md | Writing any mutation script — copy the fail-loud helper block + ordering laws + the guards (effective-visibility, live counts, safe delete, blast radius) |
| quality-gate-system.md | Setting up or evolving quality gates — executable gate suite, "no defect escapes twice", 4 catch layers, gate honesty rules, convention-DNA extraction |
| gate-authoring.md | Writing or converting any gate script — node-id scope + findAllWithCriteria, skipInvisibleInstanceChildren, one page.loadAsync(), per-gate time budget, KNOWN-RED header, concealed text; starts from scoped-gate-template.js |
| campaign-orchestration.md | Multi-screen campaigns with agents — 1-agent-1-job, batch-by-budget, study-first, backup-edit, canvas organization, wave close |
| knowledge-sync.md | User says they changed the file, or your docs picture may be stale — URL-drop protocol, drift channels, reconcile triggers |
| project-adapter-template.md | Onboarding this discipline to a new project |
Non-negotiables inherited from hard experience
- Icons: use the project's icon library as real instances — never text glyphs (
×,→) or hand-drawn vectors. Stroke-based icon sets (Lucide): recolorstrokesonly, neverfills. - Overlay idiom: DRAWER = editing panels over content/lists; centered MODAL = single action / confirm / short form (≤3 fields).
- Screen-state demos = variant SET on the page-content master (
Stateaxis), never per-instance structural overrides. Instance overrides are for DATA only. - Every created node gets
setSharedPluginData(<ns>, 'run_id'/'key', …)for idempotent cleanup and reliable re-location. - Report honestly: errors as errors, skipped as skipped, "should work" is banned — verified or listed as NOT verified.


