Skip to content

MVP: Validate the default-slot gate matrix across CLI and plugin #671

Description

@nathanacurtis

Problem

Default-slot nesting is implemented across every surface — the CLI reads the convention, the engine captures the nesting, both transforms emit it, the render direction puts it back, and the plugin panel now declares it (#665, #668). What has actually been run is narrower than that: one CLI path, against one library, on components whose siblings happen to be distinctly named.

The behaviour is also gated in a way that is easy to misread as broken. Three independent conditions decide whether nesting happens, one of them differs between a component and a composition, and a fourth output — the defaultSlot: true marker — is deliberately ungated and appears even when nesting does not. So "I turned it on and nothing changed" is an expected outcome in several cells of the matrix, and there is currently no way to tell that apart from a defect.

Two specific gaps that make this urgent rather than tidy:

  • The two switches live in different places and neither mentions the other. Naming the default slot is necessary but not sufficient for a component: spec.defaultSlotContent must also be on. In the plugin those are two checkboxes in two different sections (Props and Examples). Nothing in either surface tells you the other exists.
  • Nothing has compared the two surfaces. The parity claim — same node, same declared convention, same spec whether generated by the CLI or the plugin — is the whole point of the plugin change, and it has never been checked.

Potential solution(s)

  • Walk the gate matrix below on one component and one composition, recording actual output per cell, so each "nothing changed" is either confirmed as designed or becomes a bug report.
  • Compare CLI and plugin output for the same node under the same declared convention, and diff them.
  • Where a cell's expected outcome is surprising on reflection, treat that as a finding about the design rather than only about the implementation — particularly the component/composition asymmetry.

Acceptance criteria

The gate matrix

Each row is a configuration; the last two columns are what the spec should contain. defaultSlot: true on the slot prop and the nesting itself are separate outputs with different gates — that separation is the thing most likely to read as a bug.

Convention declared Pro defaultSlotContent Host kind defaultSlot: true? Nesting?
no — — either no no
yes, empty list — — either no no
yes no on either yes no
yes yes off component yes no
yes yes on component yes yes
yes yes off composition yes yes
yes yes on composition yes yes
  • Every row above produces the stated output, on the CLI
  • The two bolded "yes marker, no nesting" rows are confirmed as designed, and a reader of the spec can tell why — or filed as a usability defect if they cannot
  • The composition rows confirm the exemption: a composition nests regardless of defaultSlotContent, because composed content is the reason it exists

Structural preconditions

These decide whether a given slot flattens, independently of the gates above:

  • A nested instance's default slot, filled → nests as that instance's children
  • The host's own default slot, filled → unchanged, still a slotContentExamples entry pointed at from the binding. The host declares that slot, so content in it is the slot's authored default
  • A nested instance's non-default slot, filled → unchanged, still an explicit reference
  • An instance filling both kinds → both shapes in one spec, side by side
  • A hidden nested instance with a filled default slot → nothing captured
  • An instance whose default slot is bound to a prop rather than filled → the binding survives; no content is substituted for it
  • A default slot reached only by passing through a non-default slot's content → nests inside that entry, not into the host
  • A chain two instances deep where the inner one fills a non-default slot → the reference anchors on the inner instance, not as a $nested path on the outer one

CLI ↔ plugin parity

  • The same component, with the same declared convention and the same two switches, produces the same spec through specs generate and through the plugin
  • metadata.conventions.platforms.figma.slots is present in both, so the render direction can resolve the slot either way
  • A diff of the two specs is empty, or every difference is explained and expected

Round trip

  • A spec carrying a nested hierarchy renders back into Figma with the content in the right slot
  • Regenerating from the rendered node reproduces the nesting rather than flattening it back out
  • A multi-slot component's nested children land in the slot the convention names, not in a sibling slot

Case data

  • Fixture: an action-list component and a card component for the component rows — both verified through the CLI and both carry a nested instance with a filled default slot. A composition for the composition rows; one of the canonical screens works, though its layout levels are frames rather than instances
  • Workspace: eg for the CLI side and the plugin side; a test library for the structural rows that need deliberately odd frames
  • Territory: testing, crossing cli / specs-from-figma / plugin-settings
  • Size: m

Notes

Several structural rows need frames no design file currently contains — the hidden instance, the bound-rather-than-filled slot, the two-deep chain. #656 is the authoring work and already carries most of them; this issue is the verification that consumes them, so the two are worth sequencing together rather than merging.

What is already known, so nobody re-derives it: the engine behaviour is proven on the CLI path against a real library, where one screen's slot-content entries dropped from 20 to 3 and an action-list component's from 24 to 8. Every gate has unit coverage. What has never run is the plugin path, the render round trip, and every structural row above except the plain nested-instance case.

One known limitation that will show up while testing and is not this issue's to fix: sibling instances sharing a Figma name collapse to a single anatomy element, because anatomy and elements key by layer name. ADR-044 is the accepted answer and is unimplemented. Expect three identically-named cards to read as one, and do not file it again.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

clispecs-cli commandspluginFigma pluginspecs-from-figmaTransformer from Figma into specstestingspecs-testing parity validation

Type

No type

Fields

Priority

None yet

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions