Skip to content

Releases: sandroleks/spec-layer

Spec Layer v7.0.0

Choose a tag to compare

@github-actions github-actions released this 14 Oct 11:08
b57901f

Plugin 7.0.0 is a major release because every existing document asks to
be updated once. EXTRACTOR_VERSION moves from '3' to '4' for Figma
slots, so a component doc built by 6.0.0 or earlier reads "Rebuild
needed". Annotate in Dev Mode, motion variables and the Motion section,
Figma slots, placeholders for empty writing sections and the Library's
incremental source check all ship here, with Foundation Context v5 schema
5.2.0 and Component Context v5 schema 5.4.0. It is the first plugin built
and attested by release-plugin.yml. The proxy it needs deploys from
main ahead of it, and spec-layer@0.13.0 is latest on npm, which a
pulled Markdown page needs before it says what the canvas says about a
slot.

The CLI portion of this section ships as 0.12.0, a minor release because
pull, status and list gain --json, and because --help and
--version now print to stdout and exit 0. It is also the first CLI that can
pull a library with an EASING variable: 0.11.0 does not know the easing
token type, refuses the Foundation as failing schema validation, and asks for
a republish that cannot help. A plugin build that exports easing tokens must
not reach the listing before 0.12.0 is latest on npm. A repository on 0.11.0
re-projects once on its first pull with 0.12.0 through the
manifest.cliVersion check. 0.12.0 is the first version published by the
release workflow, so it is the first to carry npm provenance.

The CLI changes merged after 0.12.0 ship as 0.13.0, a minor release: pulled
Markdown prints each Figma slot's facts (preferred components, "only
preferred", the limits and default content) where 0.12.0 lists a slot with
its type alone, and spec-layer skill says what a slot asks of code (Figma
slots, under Added). A plugin build that documents slots should not reach the
listing before 0.13.0 is latest on npm, so a pulled Markdown page says what
the canvas says.

Added

  • Annotate in Dev Mode. A Library row's menu and Settings, Export can now write a
    component doc back into Figma's own fields, so Dev Mode and the REST API
    show what the canvas shows. The Usage text the doc shows (Overview, When to
    use, When not to use) goes into the component description with one
    provenance line, and is cleared again once the doc no longer has it. When a
    file link is saved in Settings, the documentation link opens the doc's
    Section, follows it when an Update rebuilds the Section, and is cleared when
    the doc is deleted. When the doc draws its Anatomy legend, each top-level
    part gets a native annotation numbered as the legend numbers it, with
    padding, spacing, radius and type pinned as live properties, so no number is
    ever typed; an annotation on a part the legend no longer numbers is removed,
    and a doc whose parts changed since it was built places no annotation until
    it is updated. Every run you start asks first and names AI text nobody has
    edited: the confirmation says what it writes in one line and lists each
    caution as its own point, naming which kinds of value (description, link,
    annotations) a change made in Figma keeps. A description, link or annotation set, changed or removed in Figma
    is kept, and replacing it is a separate, labelled step that also names AI
    text: it is offered once, when a run finds a value newly changed in
    Figma, and again from a component's Library row; a run over every
    component names what it kept in a toast instead. A part shown by a
    property is labelled "Shown when … is true", as the legend says. A record
    of what was written lives in the component's plugin data,
    and the serializer reads a description or link that matches it as absent,
    so an annotated doc never reports drift against its own words and the AI
    prompt is never fed the sync's own text. A description a person edits in
    Figma becomes theirs and reads as source like any other, without the
    provenance line. A record from a newer build is left untouched. Nothing it
    writes enters a hash or an artifact, and EXTRACTOR_VERSION is unchanged.
    An optional switch annotates again after an Update, only for components
    already annotated by hand, never writes text that was edited in Figma or
    written with AI, and names a component it held back once, not on every
    Update. Whether Figma's MCP server or Make reads the descriptions is not
    verified.

  • Motion variables in the Foundation. Figma's TIMING and EASING
    variable types (Plugin API update 133) are read as duration tokens in
    seconds and a new easing token type (a named preset, a cubic bezier, a
    spring bounce, or hold). Foundation rows read "0.3 s" or the easing name,
    and a cubic bezier draws its curve. Foundation Context v5 moves to schema
    5.2.0 (foundation-5.2.0.json); the envelope sits outside the content
    hash, so no artifact identity moves for a file without these variables.
    DTCG projects duration and a cubic bezier cubicBezier; a preset,
    spring or hold is omitted and named in report.json, never expanded to
    numbers the API did not return. Before this, a TIMING variable exported
    as a unitless number and an EASING variable read "Not resolved".
    @figma/plugin-typings moves to 1.140.0.

  • Motion section for variant transitions. Prototype interactions that
    change a component set's variant (hover, press, click, delay) appear as a
    Motion section after States, with From, To, Trigger, Transition, Duration
    and Easing, and as motion.transitions in the YAML brief, Component
    Context v5 (since schema 5.3.0), Markdown and the AI
    profile. A CHANGE_TO outside the set is reported as
    transition-target-outside-set. The section is marked Beta in the
    section picker; existing documents gain it when it is ticked on. The
    motion key exists only when a component has transitions, so every
    document without them keeps a byte-identical hash: the content hash of an
    affected document moves on its own, which is what drift detection
    reports. (The release still moves EXTRACTOR_VERSION to '4', for Figma
    slots, below.) An easing on a prototype
    transition that Figma stores as a physical spring (mass, stiffness,
    damping) is reported as not supported, never converted to a bounce,
    because the conversion is Figma's own and not available to a plugin.

  • Figma slots. A component's slot properties and slot layers are read
    and documented. Each slot is a property under api.slots with
    type: slot, and Figma's own facts about it: the designer's
    description, the preferred components (named from the instances inside
    the component, then the file's own components, and never over the
    network, so a drift check does not depend on library access), "only
    preferred", the minimum and maximum, "display empty
    by default" and "stretch child on insert". Each slot layer is one anatomy
    part carrying slot (its property) and default_content, what it holds
    in the default variant. Default content is an example, so it is no longer
    documented as anatomy parts, related components, token bindings or
    unbound values; a slot inside a nested instance keeps its content, which
    is fixed content of the component. On canvas, Properties reads Type
    Slot with the preferred names, "only these", the limits and the
    default content, and the Anatomy legend marks each slot. A preferred key
    the document cannot name is counted, never named, and reported as
    unresolved-slot-content; a slot linked to no property is reported as
    unlinked-slot; a property type the extractor does not know is left out
    and reported as unsupported-property-type. The Library's change list
    itemizes each slot change. Component Context v5 schema 5.4.0
    (component-5.4.0.json) gains anatomy_node.slot,
    anatomy_node.default_content and a typed api.slots entry. 5.3.0,
    the motion contract above, was published on spec-layer.com before
    slots and stays there unchanged; no release writes it.
    EXTRACTOR_VERSION moves to '4', so every document built by 6.0.0
    reads Rebuild needed once. Annotate in Dev Mode never
    pins a corner radius on a slot, which Figma refuses. Each api.slots slot
    entry also carries default_content, so the Markdown page prints the same
    Values and Default text as the canvas, adds a Description column only for
    a slot's own description, and says in Anatomy what each slot holds. A
    published library versions a slot by direction: narrowing what may go in
    it (only preferred turned on, a raised minimum, a lowered or new maximum, a
    preferred component removed while only preferred is on) is major,
    widening is minor, and its default content and display settings are
    patch; each is a property change in the history, named by its facet, so a
    6.0.0 plugin's history pane still reads it. The AI writing prompt gets
    each slot's facts in the same words. An instance swap property now reads
    "Instance swap" in Markdown rather than "InstanceSwap". The agent guide
    spec-layer skill writes, and the plugin's skill download, say what a
    slot asks of code: build it as the framework's children or a named slot,
    accept only the preferred components when only preferred is on, keep to
    the limits, and treat default content as an example, never as fixed
    markup. The guide uses the YAML keys or the Markdown page's words,
    whichever format the components were written in.

Changed

  • CI's dependency review exempts @figma/plugin-typings from the licence
    allow-list. The package ships a verbatim MIT licence but declares it as the
    non-SPDX string "MIT License", which GitHub reports as
    LicenseRef-bad-mit-license, so every version bump failed the gate. The
    terms are unchanged; only the metadata is exempted.
  • The component screen's main button says Update docs (and
    **Updating d...
Read more

Spec Layer v6.0.0

Choose a tag to compare

@sandroleks sandroleks released this 24 Sep 13:51
2230550

Plugin 6.0.0 ships everything merged since 5.1.0 (#59 to #89). It pairs with spec-layer@0.11.0 on npm: pull with npx spec-layer@latest.

This is a major release because every existing document asks to be updated once. EXTRACTOR_VERSION moves from '2' to '3', so a component doc built by 5.1.0 or earlier reads "Rebuild needed", and a foundation doc built by 5.1.0 reads "Update available" with one "New layout" item. The proxy still accepts the prose requests 5.1.0 sends, so a plugin that has not updated keeps generating.

Highlights

Documents

  • Component documents read as authored pages. The component's own Figma description becomes the overview, a Properties table replaces the Configuration section, and empty sections are left out, with the result saying so.
  • AI writing is one structured call per component. When to use and Do and don't answer different questions, and rebuilding a doc written by an earlier version fills in only what is missing.
  • Foundation docs draw number tokens to scale, show text styles as specimens, carry reference names, and get one overview per collection. Effect styles are a new foundation source, with their own row, document and Copy for AI target.
  • Stale docs are explained once, in a banner, and "Update all docs" now rebuilds docs made by an earlier version.

Copy for AI and outputs

  • Components can copy, snapshot and pull as Markdown as well as YAML. Choose the format in Settings, or use spec-layer pull --component-format md.
  • The DTCG output describes itself: a census of what each file holds, a hash of the options that produced it, and a sidecar recording each value's transform and what each alias resolves to. A reference is written only into a file where it resolves, and a token that is also a group is omitted and reported rather than dropped silently.
  • Foundation Context artifacts are stamped with schema 5.1.1, which pins the envelope, diagnostics and statistics. spec-layer.com/schemas serves it.

Publishing

  • Published libraries have a semantic version. Every changed publish is diffed over Figma facts, and the Publish screen proposes the minimum bump, which you can raise but never lower. Token bindings are diffed per variant, and a Version history pane lists what changed.
  • Every doc frame carries a publish pill. Publish refuses a file with nothing in it, and a library can be downloaded as a self-contained agent skill.

Reliability

  • The Library always replies to a refresh, says so when it could not read part of the file, and only one canvas build runs at a time. Publish and Library show only what they actually know.
  • Proxy hardening: one origin, request bodies capped as they stream in, atomic publishes, and a response cache per key.

The full list, including the CLI 0.9.0 to 0.11.0 changes, is in the changelog.

Known limits

  • The manual Figma test matrix in packages/plugin/TESTING.md has no recorded run for this release. Rows 16 to 36 cover the new documents and none has been run, so unit tests are the only evidence for how they draw.
  • The real size and paste behaviour of the DTCG clipboard has not been measured in Figma.
  • The first publish of an existing library from 6.0.0 records one patch version with no listed changes, because the library bundle hashes the new 5.1.1 envelope.
  • An artifact from 5.1.0 still says schema 5.1.0 and validates against the 5.1.0 download, not v5.json. spec-layer pull accepts both.
  • The setup command the plugin copies still passes the pull key on the command line. CLI 0.11.0 can read it from stdin with --key -, but the plugin does not use that yet.

Spec Layer v5.1.0

Choose a tag to compare

@sandroleks sandroleks released this 16 Sep 21:26
28a55c9

Published to the Figma Community listing on 2026-09-10. Tagged retroactively on 2026-09-16, so this release records what already shipped.

Pull the matching CLI with npx spec-layer@latest.

Fixed

  • Token bindings on a hidden part are no longer dropped. Token extraction
    pruned every invisible subtree, so a layer a boolean property reveals had its
    bindings discarded before any consumer could ask for them: with "Document
    hidden elements" on, the revealed icons were drawn but the Tokens section
    showed nothing for them. Extraction now prunes only what no consumer could
    ever reveal, marks a rule from a revealed subtree with shownBy, and filters
    through tokensFor, the token counterpart of anatomyFor. Anatomy and
    token extraction share one definition of "hidden but documentable"
    (hiddenPartRules). With the option off, every rendered table and every
    drift hash is byte-identical to before. The exported YAML brief and Component
    Context v5 artifact still omit these rules, because a v5 token rule has no
    field in which to say it applies only once a boolean property is on;
    carrying shown_by onto token rules needs schema 5.3.0. One export does
    change: the brief's unbound list weighs gaps against the unfiltered rules,
    so a hidden layer whose fill is bound is no longer reported as having no
    token binding.
  • Anatomy callouts read in order. Depth-0 parts are now listed in real
    layer order, hidden ones interleaved where they sit, instead of visible-first;
    a Chip's pins read 1, 2, 3 left to right rather than 2, 1, 4. Naming is
    decided separately from ordering, on the visible-first basis, so a hidden
    sibling cannot take a visible part's name and a doc with the option off keeps
    the anatomy, names and specContentHash it already had.
  • Anatomy callout numbers are hierarchical. A nested legend row is now
    "2.1" rather than consuming the next whole number, so the pinned depth-0 set
    counts 1, 2, 3 with no holes. The number badge is a pill with a floor of its
    old diameter, so a single digit still draws the same circle.
  • "Document hidden elements" moved out of the Anatomy row. It is a switch
    above "Sections to include", because the flag feeds Anatomy, States,
    Variants, the per-variant token pane and Measurements alike. It no longer
    disappears, or silently disarms, when Anatomy is unchecked. Its caption is
    gone from the page: it was two sentences set in an uppercase micro-caps
    style built for two-word labels, so the explanation moved to the same
    info-icon tooltip the AI writing switch uses. The two switches now share one
    set of CSS rules, which is what the label's weight disagreeing with "AI
    writing" was about: the second row's own copy of those rules set the font on
    the wrapper, so the inner strong fell back to the browser's bolder.
  • Four quadratic regexes are now linear scans. cleanPartName,
    stateBaseName, and the prose prompt's dash and code-fence rules each ran
    in time quadratic in their input, so a long run of hashes, parentheses, or
    spaces could stall the plugin's UI thread: 40,000 trailing hashes in a layer
    name measured 6754 ms. The two prose rules read model output arriving
    through the proxy, the one input here that nobody in this repository
    controls, where an unclosed code fence was enough to hang the UI. Output is
    unchanged for every input, and packages/extractor/test/redos.test.ts keeps
    the old regexes as executable oracles to prove it, which is load-bearing
    because cleanPartName and stateBaseName feed canvas drift hashes. Closes
    CodeQL alerts 1 to 4 (js/polynomial-redos).

Added

  • Hidden elements in component docs. A layer that a boolean component
    property hides by default (a Chip's Icon left) is now an anatomy part.
    The YAML brief and the Component Context v5 artifact always include such
    parts, each carrying shown_by, the property that reveals it. Component
    Context schema 5.2.0 adds shown_by as an optional string on anatomy
    nodes; the schema URL is unchanged. The envelope now requires
    schema_version 5.2.0, so once the site redeploys, an artifact still
    stamped 5.1.0 no longer validates against the published schema. This
    moves the semantic content hash of
    every component artifact that has such parts, and nothing else: hidden
    layers with no property binding stay excluded, canvas drift hashes are
    unchanged, and EXTRACTOR_VERSION is unchanged.
  • "Document hidden elements", a per-component switch on the component
    screen, shown only when the component has a layer that a boolean property
    hides by default. On, the doc lists those parts in Anatomy with
    "Shown when is true", sets every boolean property to true on the
    instances placed in Anatomy, States, Variants, and Measurements, and hashes
    the revealed parts so drift detection covers them. Off, or on any doc
    generated before this release, nothing changes: rendered output and the
    drift hash are identical. Update keeps the stored choice. Anatomy pins are
    now measured on the placed instance rather than the source component, which
    is what makes a revealed part pinnable. The per-variant token pane preview
    in the Variants section also reveals when the toggle is on. When the option
    is on, the Measurements diagram measures its size rails, padding bands, and
    spacing from the revealed instance rather than the source component, so the
    overlay matches what is drawn.
  • A self-describing DTCG output (CLI 0.8.2). The document extension now
    carries a census of what each emitted file holds and a config_hash of the
    projection options that produced it, and resolver.json carries that
    extension instead of only the clipboard document doing so. The sidecar
    records the transform behind each token's value in each mode, and the value
    each alias resolves to. Every field is descriptive: no canvas hash and no
    artifact identity moved, and EXTRACTOR_VERSION is unchanged, so no document
    needs regenerating. A repository pulling with an earlier CLI gets the same
    files without these fields. The census reports what the projection produced;
    report.json keeps its own job of naming what it could not express.

Removed

  • The non-public trees. apps/website (the marketing site), docs/ (the
    prose specs, plans, reviews, brand system, and writing guides),
    project-docs/ (the archived vault), and screenshots/ (the capture
    library) are no longer part of this repository; they are maintained
    privately. This repository is the open-source product: the plugin, the
    extractor, the proxy, the CLI, the shared brand package, their tests, and
    the published JSON Schemas. No shipping code changed.
  • The two tests asserting that the website's schema copies stayed
    byte-identical to the extractor's. There is no second copy left to drift, so
    the assertion had nothing to compare. The contract they protected is
    unchanged and is now checked end to end by npm run check:site-live, which
    compares the live spec-layer.com/schemas/** bytes against
    packages/extractor/src/v5/schema/*.json directly. The site is deployed
    from the private repository, so a schema change here is not live until that
    side redeploys, and the live check is what proves it.

Changed

  • "Document hidden elements" now starts on for a component that has such
    layers, and stays off for one that does not. A layer a boolean property
    reveals is part of the component someone is trying to understand, so leaving
    it out by default made the common case the one that needed a click.
    defaultIncludeHidden is the one source for that default, seeded once
    extraction has produced facts, so a fresh screen never draws the switch on
    for a component with nothing to reveal. Documents generated before this
    release are untouched: Update keeps each doc's stored choice.
  • CI no longer builds the website; verify is npm run check:ci alone. The
    job id is unchanged, so the required status check on main still applies.
  • npm run check:nul scans packages/ and scripts/. The prose trees it also
    covered, where the NUL trap bit three times, are scanned in the private
    repository instead.

Spec Layer v5.0.0

Choose a tag to compare

@sandroleks sandroleks released this 09 Sep 18:56
e8b91d3

Spec Layer v5.0.0

Documentation you generate in Figma, and context a coding agent can actually use.

Highlights

  • Pulled tokens are files your build compiles. spec-layer pull writes a tokens/ directory with one CSS file per collection and mode, plus an index.css that imports them in resolver order, so you can take a single mode and wire prefers-color-scheme yourself. Component briefs sit beside it in component-specs/, byte-identical to Copy for AI. Names come from the designer's code_syntax.WEB when Figma declares one. Two tokens that would collide are both omitted and reported rather than guessed.
  • Foundations copy as DTCG. A Foundation copies as a Design Tokens Format Module 2025.10 resolver document. What the format cannot express is omitted and written to the report, never replaced with a plausible default or a fake reference.
  • Free plans can publish a library. One Figma file, 10 changed publishes a UTC month. A publish counts only when it commits, and republishing unchanged content is answered as unchanged without a write or a count. Pro keeps 10 libraries.
  • Publish screen, rebuilt. A status pill, the last publish date in local time, the free plan's updates left, and the two things you came for: the developer setup command and the AI agent prompt, each in full with a Copy. A library published by an earlier build reads "date not recorded" rather than a guessed date.
  • Your coding agent gets a guide. spec-layer skill --install writes instructions into whichever agent host your repository uses, built from the command catalogue, your last pull, and a reading of your stack. spec-layer tools prints every command with whether it reaches the network, whether it needs the pull key, what it writes, and what each exit code means.
  • Settings has an About section, giving the plugin version and the extractor version as separate rows. They answer different questions: a Library reporting a rebuild on every row is reacting to an extractor bump, which the plugin version alone cannot show.
  • Component and Foundation Context v5, schema 5.1.0, with stable ids, complete alias chains, external references, composite typography and effect styles, and optional code_syntax. A component copy embeds only its own dependency closure.

Getting Started

Install from the Figma Community listing.

The CLI is on npm:

npx spec-layer setup

To run this release from source, with Node.js 22 or newer:

npm ci
npm run build:plugin

Then in Figma desktop choose Plugins → Development → Import plugin from manifest and select packages/plugin/manifest.json.

Security Model

Deterministic sections run entirely inside the plugin and send nothing. AI writing routes through the Spec Layer proxy, which holds the Anthropic credential; no user API key is involved. Component requests include a derived summary and, when it fits the limits, a rendered image. The proxy validates raw license keys with Lemon Squeezy but uses SHA-256 digests for its own cache keys, quota identities, and logs. Keep credentials and private design-system data out of Git.

Verification

This release candidate passed, on 2026-09-09:

npm ci
npm run check:ci          # lint, typecheck, NUL scan, coverage, builds,
                          # CLI bundle smoke test, sandbox scan,
                          # proxy deploy dry run, dependency audit
git diff --check
npm run check:site-live

npm audit reports 0 vulnerabilities. npm run check:site-live confirms that https://spec-layer.com/schemas/foundation-context/v5.json and its component counterpart serve exactly the bytes committed in packages/extractor/src/v5/schema/, with the expected $id, and that a missing path returns 404.

The plugin's networkAccess and packages/plugin/src/ui/proxy.ts both point at https://api.spec-layer.com.

The manual Figma pass in packages/plugin/TESTING.md covers what unit tests cannot reach: component docs, Foundation docs, Library, and Copy for AI against a real file.

Known Limitations

  • Workspace packages other than the CLI are not published to npm.
  • AI prose is the only part of the product that uses a model. Extraction, rendering, drift detection, and Copy for AI are deterministic, and that split is deliberate.
  • Free publishing is budgeted per self-asserted Figma identity, so the per-IP rate limiter is the only ceiling against a client that misreports it. See the accepted risks in packages/proxy/README.md.

Full Changelog

See CHANGELOG.md.