The changelog is prose; the spec file is a contract
Humans write changelogs late and loosely, in prose a build agent cannot reliably read. “Breaking: reworked the Button API” tells a human something; it tells an automation almost nothing, because intent is not a data structure.
If your design system maintains machine-readable component spec files — the approach described in /blog/2026-10-18-component-spec-files-agent-contract/ — then the version bump does not have to be chosen by hand. It can be derived: diff two versions of the spec file, classify every declared change, and emit the version bump mechanically.
This post proposes exactly that check. It sits on a third axis alongside two sibling checks on this site. Dead-token scoring measures design-system debt at a point in time. Build-output drift checking verifies token-to-CSS consistency between builds. This post checks version-to-version component API changes. Three checks, three axes, one shared philosophy: turn criteria that used to live in a maintainer’s head into something a machine can evaluate.
Thesis: A component spec file is a declared public API, so a breaking-change check can diff two versions of it and mechanically classify the change as MAJOR, MINOR, or PATCH under SemVer. For the rules covered below, that makes the changelog a computable contract rather than a prose promise.
The single computable claim: Given two versions of a component spec file, a rule set (R1–R7) derived from SemVer’s MAJOR/MINOR/PATCH definitions yields a deterministic version-bump classification for R1–R3, R6 and R7. R4 is conditional on a render check, and R5 gates everything at 0.y.z. This is a claim about rule logic only. It is not a claim that the check was run on a real design system.
Why SemVer applies to components at all
SemVer is not just for npm packages. The SemVer 2.0.0 specification item 1 requires that software using Semantic Versioning MUST declare a public API, which “could be declared in the code itself or exist strictly in documentation.” A component spec file is precisely that declaration, written down in a machine-readable format.
The specification then defines the three bumps, summarised here as a list:
MAJOR version when you make incompatible API changes, MINOR version when you add functionality in a backward compatible manner, and PATCH version when you make backward compatible bug fixes.
If the spec file is the declared public API, these definitions apply to it directly. Remove a prop: consumers break: MAJOR. Add an optional prop: nothing breaks: MINOR. Change a default without changing rendered output: no consumer-visible effect, and R4 assigns PATCH by assumption (see the R4 rule below). The mapping applies the specification to a file design systems already maintain.
SemVer item 4 adds one carve-out that matters for design systems: “Major version zero (0.y.z) is for initial development. Anything MAY change at any time.” Any check has to respect that carve-out.
The seven rules (AUTHOR PROPOSALS)
The spec file is the declared public API per SemVer item 1; these rules apply the MAJOR/MINOR/PATCH definitions to it. They are the author’s extrapolation — no cited source defines a component-spec changelog format, and no cited source publishes these rules. Treat them as a proposal to adopt, adapt, or reject.
Rule R1 (AUTHOR PROPOSAL) — Removed prop or variant value → MAJOR. Derived from the MAJOR clause: “MAJOR version when you make incompatible API changes.” A removed prop is the definition of an incompatible API change.
Rule R2 (AUTHOR PROPOSAL) — Renamed prop or variant, old name gone, no alias → MAJOR. Derived from the same MAJOR clause. A rename without an alias is a removal plus an addition; the removal dominates, because every consumer using the old name breaks. A rename that keeps the old name as an alias fires R3 (MINOR), plus R7 if the old name is marked deprecated.
Rule R3 (AUTHOR PROPOSAL) — New optional prop or variant value → MINOR. Derived from the MINOR clause: “MINOR version when you add functionality in a backward compatible manner.” Optional additions are backward compatible by construction — existing call sites are unaffected.
Rule R4 (AUTHOR PROPOSAL) — Default value changed → MAJOR if it changes rendered output, else PATCH. The MAJOR half derives from “MAJOR version when you make incompatible API changes.” The PATCH half is an assumption: it reads an output-neutral default change as a backward compatible fix, which SemVer does not state. This rule splits on a question the spec diff alone cannot always answer; see the limitations section.
Rule R5 (AUTHOR PROPOSAL) — The component spec’s declared version is 0.y.z → any change permitted; the check reports but does not fail. Derived from SemVer item 4: “Major version zero (0.y.z) is for initial development. Anything MAY change at any time.”
Rule R6 (AUTHOR PROPOSAL) — Required prop added, optional prop made required, or type narrowed → MAJOR. Derived from the MAJOR clause: each one makes a call site that was valid stop being valid, which is an incompatible API change.
Rule R7 (AUTHOR PROPOSAL) — Prop or variant value marked deprecated → MINOR. Derived from SemVer item 7: a MINOR increment “MUST be incremented if any public API functionality is marked as deprecated.” Removal of the deprecated item later is R1.
Precedence: the highest bump among the rules that fire wins (MAJOR over MINOR over PATCH), so one removal under R1 outweighs any number of optional additions under R3. R4 splits on rendered output, and R5 gates everything at 0.y.z.
The comparison table
| Change type | SemVer bump | Rendered effect | Can the check see it? |
|---|---|---|---|
| Removed prop / variant value | MAJOR (R1) | Existing consumers break | Yes — a removed declaration is visible in the diff |
| Renamed prop / variant, no alias | MAJOR (R2) | Existing consumers break | Yes — old name gone, new name added |
| New optional prop / variant value | MINOR (R3) | None for existing consumers | Yes — added declaration, backward compatible |
| Default value changed, output changes | MAJOR (R4) | Rendered output differs at same call site | Partially — the spec diff shows the change; the output change needs a render oracle |
| Default value changed, output unchanged | PATCH (R4) | None | Partially — same visibility problem, opposite classification |
| Required prop added, optional prop made required, or type narrowed | MAJOR (R6) | Existing call sites stop working | Yes — the spec diff shows the constraint change |
| Prop or variant value marked deprecated | MINOR (R7) | None yet; removal later is MAJOR (R1) | Yes — the deprecation is declared in the spec |
| Any change at 0.y.z | Permitted (R5) | Anything | Yes — reported, not failed |
The middle rows are the weak point of a pure spec diff, and the limitations section returns to them.
How you verify each rule yourself
The check is a diff plus seven rules, so verification is something you can do by hand. Here is a short example spec diff:
- name: tone
- values: [positive, negative]
+ values: [positive, negative, neutral] # R3 → MINOR
+ - name: emphasis # new, marked optional → R3 → MINOR
+ optional: true
+ values: [low, high]
- - name: legacySize # removed → R1 → MAJOR
- values: [sm, lg]
This diff is illustrative; no check was run against it.
Line-by-line verification steps:
- Paste your own two spec versions side by side and produce a diff.
- Apply R1 first: scan for removed props or variant values. Here,
legacySizewith values[sm, lg]is removed — R1 fires → MAJOR. - Apply R2: scan for renames with no alias. None here.
- Apply R3:
neutraladded totone, and a new optionalemphasisprop — both MINOR-class additions. - Apply R4: no default values changed in this diff.
- Apply R5 (R6 and R7 do not fire on this diff: no required prop, no narrowing, no deprecation): if the component spec’s declared version is 0.y.z, report everything but do not fail; otherwise, read off the dominant bump.
- Because R1 dominates R3, the expected outcome for this example is MAJOR.
The reader runs this on their own repo. The rules are small enough that a hand verification is feasible, which is what makes them checkable.
What existing tools already do (and don’t)
Before treating R1–R7 as novel, check what the ecosystem already provides — and where it stops.
Changesets is the closest tool in the release-tooling space. Per the current guide, a changeset’s YAML frontmatter “describes the packages that have changed and their respective semver bump types,” and the Markdown body is the change summary written to the changelog. The critical word is human-declared: the bump type in each changeset is chosen by a person at authoring time. The guide also notes that changes needing no release need no changeset, and that the project does not recommend blocking contributions without one. Changesets records and applies version intent; it does not derive it from a spec diff.
Storybook autodocs moves in the derived-artifact direction: Storybook “infers the relevant metadata (e.g., args, argTypes, parameters) and automatically generates a documentation page” when a story is tagged autodocs (and !autodocs opts out). The inference machinery underneath is real — argTypes can be inferred from static analysis of the component, and args are a JSON-serializable object passed to the component. But autodocs generates a snapshot of the current component. It is not a diff between two versions, and it emits no version classification.
The gap is straightforward: no cited tool classifies spec diffs automatically. Changesets wants a human to declare the bump; Storybook renders the present state. The check proposed here occupies the space between them — and because it produces a version type plus change descriptions, its natural output format is a changeset, which Changesets can then consume.
Vendor-neutrality and lock-in pressure
There is a second reason to run this check on spec files in your own repo, and it is about who controls the metadata.
The DTCG Format Module 2025.10 is a Final Community Group Report dated 28 October 2025, describing “a file format to exchange design tokens between different tools,” with status “considered stable.” Its own status page is explicit that it is not a W3C Standard. It is the best available neutral interchange format for tokens — but a Community Group Report is a commitment made by a community, not a standard enforced by a body.
Meanwhile, tool vendors are building their own metadata channels. Figma Code Connect is “a bridge between your codebase and Figma’s Dev Mode” that “enhance[s] the Figma MCP server’s ability to guide AI agents.” The Figma MCP server “gives AI agents the design context they need to generate code from your Figma files.” But the Code Connect docs state that on Professional plans, only one framework can be active at a time, and deactivated connections “aren’t returned by the Figma MCP server.” Your component metadata’s availability to agents depends on your plan tier and which framework you happened to activate. The docs also recommend template files as the CLI-friendly way to manage connection files — still inside the vendor’s pipeline.
A spec-file diff check runs on your repo, in your CI. The check reads a file in your repository, so it does not depend on a plan tier or on which Code Connect framework is active. The breaking-change contract lives where your code lives. The Model Context Protocol behind the Figma MCP server is one way to expose that metadata to agents; a repo-local spec file needs no MCP server at all.
Limitations — what this check cannot see
Four limits, stated plainly:
- No source defines a component-spec changelog format. Nothing in SemVer, Changesets, Storybook, DTCG, or Figma’s docs defines one; R1–R7 are the author’s extrapolation of SemVer onto the spec file. They are proposals, not a standard.
- The check only sees declared spec content. A visual change with an unchanged spec is invisible to it. If someone adjusts a style, spacing, or behavior that the spec does not declare, the diff is empty and the check passes — correctly, per its inputs, and incorrectly, per reality.
- No hands-on test was run. This post does not claim the check was run on a real design system; the only claim made is about the logic of the rules.
- R4’s rendered-output branch needs an oracle. Whether a changed default alters rendered output is not decidable from the spec diff alone; it needs a build or render check. That is the territory of the build-output drift check, which this post links to rather than re-explains.
FAQ
Does this replace Changesets? No. It can feed it. The check’s output — a version type plus per-change descriptions — is the shape of a changeset. The proposal is that the version type becomes derived rather than human-declared; Changesets remains the release machinery.
What about visual changes? Invisible, per limitation 2. An unchanged spec with changed rendering produces an empty diff. Catching that requires a visual or build-level check, not a spec diff.
Does 0.y.z break this? No — R5 handles it. Per SemVer item 4, anything may change at any time at major version zero. The check reports all changes but does not fail the build.
Is DTCG required? No. The rules R1–R7 apply to any spec file format that declares props, variants, and defaults. The DTCG format is relevant for token interchange specifically, but the rule logic is format-agnostic.
Why not just diff the built CSS? Because built CSS is the output, not the contract. Diffing built output catches some drift, but it mixes intentional API changes with build noise, so it is a weaker signal for choosing a version bump. That is the sibling build-output drift post’s job — a different axis of the same checking philosophy.
The Bottom Line
Verdict: a trial CI gate for spec files at ≥1.0.0, to validate on your own spec history before trusting it; report-only at 0.y.z. At 1.0.0 and above, R1–R7 turn the changelog from a prose promise into a derived artifact: diff the spec, apply seven rules, emit the bump. At 0.y.z, R5 keeps the gate from blocking legitimate churn while still surfacing what changed.
The deeper point ties back to this site’s thesis: design agents need computable criteria. A changelog that a machine can derive — from a declared contract, under a published versioning specification, using seven rules small enough to verify by hand — is a criterion an agent can check.
How This Guide Was Built
This post was built from published documentation, all fetched and verified on 2026-10-25: the SemVer 2.0.0 specification, the Changesets getting-started guide, Storybook’s autodocs, argTypes, and args documentation, the DTCG Format Module 2025.10 report, and Figma’s Code Connect and Figma MCP server docs. The rules R1–R7 are the author’s proposals, derived from SemVer’s definitions; no cited source defines a component-spec changelog format. No hands-on test was run — no check was executed against any real design system, and the only claim made is about rule logic, not a tested implementation. The example diff is illustrative.
