Skip to content

feat(design-data)!: nest token lifecycle fields under lifecycle object - #1325

Merged
GarthDB merged 2 commits into
mainfrom
feat/nest-token-lifecycle-fields
Jul 31, 2026
Merged

feat(design-data)!: nest token lifecycle fields under lifecycle object#1325
GarthDB merged 2 commits into
mainfrom
feat/nest-token-lifecycle-fields

Conversation

@GarthDB

@GarthDB GarthDB commented Jul 31, 2026

Copy link
Copy Markdown
Member

Description

Restructures cascade token deprecation/lifecycle metadata into a nested lifecycle
object (introduced, deprecatedIn, deprecatedComment, replacedBy,
plannedRemoval), matching the component schema's existing lifecycle pattern. The
old flat deprecated field held a version string but read as boolean-ish, and
diverged from the component schema's naming.

Also backfills the deprecated: "unknown" migration placeholder (left over from the
legacy→cascade migration) with the real @adobe/spectrum-tokens release version for
all 1,323 affected tokens, recovered from CHANGELOG history and git archaeology.

Changes

  • packages/design-data-spec/schemas/token.schema.json: new $defs.lifecycle,
    referenced from tokenWithValue/tokenWithRef in place of the 5 removed flat fields.
  • packages/design-data-spec/schemas/component.schema.json: renamed
    lifecycle.deprecatedlifecycle.deprecatedIn so token and component schemas match.
  • packages/design-data/tokens/*.tokens.json (7 of 8 files): 1,323 tokens migrated
    into the nested shape, with real deprecation versions backfilled.
  • packages/design-data/scripts/{backfill-deprecated-versions,migrate-lifecycle-nesting}.js:
    new re-runnable migration scripts.
  • sdk/core/src (legacy.rs, migrate.rs, diff.rs, authoring/{lifecycle,session}.rs,
    validate/rules/spec0{10,11,12,13,14,36,37}.rs): cascade↔legacy conversion, diff pairing,
    and validation rules retargeted to lifecycle.*.
  • packages/design-data-spec/spec/{token-format,authoring-workflow,evolution}.md:
    documentation updated for the nested shape.
  • ~23 conformance fixtures under packages/design-data-spec/conformance/ updated to match.

Legacy output (@adobe/spectrum-tokens) is unchangeddeprecated/
deprecated_comment/renamed stay flat there; verified byte-identical.

Motivation and Context

deprecated on a token is a version string (e.g. "14.5.0"), not a boolean, but the
flat name reads like one. The component schema already solved this with a nested
lifecycle object; tokens were the odd one out. The spec is still 1.0.0-draft, so
this breaking rename is cheapest now, before external consumers pin to it.

How Has This Been Tested?

  • cargo test --workspace — 1257 passed, 2 ignored (includes ~30 conformance-fixture
    test modules covering SPEC-010–014/036/037 validation, diff pairing, and legacy
    generation round-trips).
  • cargo clippy --workspace -- -D warnings — clean.
  • cargo fmt --all — applied.
  • moon run design-data:validate-dataset — clean (only pre-existing unrelated
    SPEC-050 warnings).
  • moon run design-data:legacy-output — byte-identical to the committed
    packages/tokens/src/*.json (verified via direct binary diff, bypassing moon cache).
  • moon run design-data:roundtrip-verify — "Roundtrip OK".
  • token-diff-generator and token-changeset-generator AVA suites — green, zero
    edits needed (they operate on legacy-format output, which is unaffected).
  • Changeset lint (node tools/changeset-linter/src/cli.js check --fail-on-warnings) — passes.

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change) — cascade schema only; legacy package output unaffected.

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • All new and existing tests passed.

Restructure cascade token deprecation/lifecycle metadata into a nested
`lifecycle` object (introduced, deprecatedIn, deprecatedComment,
replacedBy, plannedRemoval), matching the component schema's existing
nested pattern. The old flat `deprecated` field read as boolean-ish
even though it held a version string, and diverged from components.

Also backfills the `deprecated: "unknown"` migration placeholder left
over from the legacy->cascade migration with the real
@adobe/spectrum-tokens release version for all 1,323 affected tokens,
recovered from CHANGELOG history and git archaeology.

Legacy output (@adobe/spectrum-tokens) is unchanged (verified
byte-identical) — deprecated/deprecated_comment/renamed stay flat there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e8b6a68

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@adobe/spectrum-design-data Major
@adobe/spectrum-component-api-schemas Patch
@adobe/design-system-registry Patch
@adobe/design-data-agent-mcp Patch
@adobe/design-data-mcp Patch
markdown-generator Patch
@adobe/token-corpus-migrate Patch
@adobe/token-naming-audit Patch
component-options-editor Patch
@adobe/spectrum-design-data-mcp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Run report for e8b6a683

Total time: 3m 19s | Comparison time: 5m 12s | Estimated savings: 1m 53s (36.4% faster)

Action Time Status Info
⬛️ SetupProto(0.57.4) 0ms Skipped
🟩 SyncWorkspace 11.5ms Passed
🟩 SyncProject(sdk) 0.5ms Passed
🟩 SyncProject(design-data-spec) 0.9ms Passed
🟩 SyncProject(design-data) 1.1ms Passed
⬛️ SetupToolchain(node:20.17.0) 637.8ms Skipped
⬛️ SetupToolchain(rust:1.88.0) 1.3s Skipped
🟩 SetupEnvironment(rust, sdk) 148ms Passed
⬛️ InstallDependencies(rust, sdk) 3.2ms Skipped
⬛️ SetupToolchain(pnpm:10.17.1) 1.2s Skipped
⬛️ SetupToolchain(javascript) 0ms Skipped
🟩 SetupEnvironment(javascript) 1ms Passed
⬛️ SetupEnvironment(javascript, sdk) 1.1ms Skipped
⬛️ InstallDependencies(javascript) 0.3ms Skipped
🟩 RunTask(design-data:validate-registry) 365.1ms Passed
🟩 RunTask(sdk:fmt-check) 815.4ms Passed
🟩 InstallDependencies(javascript, sdk) 11.1s Passed
🟩 RunTask(sdk:codegen-check) 183.3ms Passed
🟩 RunTask(sdk:lint) 49.3s Passed
🟩 RunTask(sdk:build) 1m 41s Passed
And 5 more...
Expanded report
Action Time Status Info
🟩 RunTask(design-data:legacy-output) 1.2s Passed
🟩 RunTask(design-data:roundtrip-verify) 2.5s Passed
🟩 RunTask(design-data:validate-dataset) 6.4s Passed
🟩 RunTask(design-data:validate) 2.9s Passed
🟩 RunTask(sdk:test) 3m 5s Passed SLOW
Changed files
.changeset/backfill-deprecated-versions.md
.changeset/nest-token-lifecycle-fields.md
packages/design-data-spec/conformance/diff/cross-format/expected.json
packages/design-data-spec/conformance/diff/deprecated-new-token/new/tokens.tokens.json
packages/design-data-spec/conformance/diff/matched-gaining-deprecated/expected.json
packages/design-data-spec/conformance/diff/matched-gaining-deprecated/new/tokens.tokens.json
packages/design-data-spec/conformance/diff/replaced-by-pairing/expected.json
packages/design-data-spec/conformance/diff/replaced-by-pairing/old/tokens.tokens.json
packages/design-data-spec/conformance/diff/reverted-token/old/tokens.tokens.json
packages/design-data-spec/conformance/generation/deprecated-token/input/tokens.tokens.json
packages/design-data-spec/conformance/generation/mode-set-edit/input/tokens.tokens.json
packages/design-data-spec/conformance/generation/renamed-token/input/tokens.tokens.json
packages/design-data-spec/conformance/invalid/SPEC-010/dataset.json
packages/design-data-spec/conformance/invalid/SPEC-010/expected-errors.json
packages/design-data-spec/conformance/invalid/SPEC-010/tokens.tokens.json
packages/design-data-spec/conformance/invalid/SPEC-011/dataset.json
packages/design-data-spec/conformance/invalid/SPEC-011/expected-errors.json
packages/design-data-spec/conformance/invalid/SPEC-011/tokens.tokens.json
packages/design-data-spec/conformance/invalid/SPEC-012/dataset.json
packages/design-data-spec/conformance/invalid/SPEC-012/expected-errors.json
packages/design-data-spec/conformance/invalid/SPEC-012/tokens.tokens.json
packages/design-data-spec/conformance/invalid/SPEC-013/dataset.json
packages/design-data-spec/conformance/invalid/SPEC-013/tokens.tokens.json
packages/design-data-spec/conformance/invalid/SPEC-014/dataset.json
packages/design-data-spec/conformance/invalid/SPEC-014/tokens.tokens.json
packages/design-data-spec/conformance/invalid/SPEC-036/dataset.json
packages/design-data-spec/conformance/invalid/SPEC-037/dataset.json
packages/design-data-spec/conformance/valid/SPEC-014/dataset.json
packages/design-data-spec/conformance/valid/SPEC-036/dataset.json
packages/design-data-spec/conformance/valid/SPEC-037/dataset.json
packages/design-data-spec/conformance/valid/lifecycle-with-last-modified.json
packages/design-data-spec/schemas/component.schema.json
packages/design-data-spec/schemas/token.schema.json
packages/design-data-spec/spec/authoring-workflow.md
packages/design-data-spec/spec/evolution.md
packages/design-data-spec/spec/token-format.md
packages/design-data/scripts/backfill-deprecated-versions.js
packages/design-data/scripts/migrate-lifecycle-nesting.js
packages/design-data/tokens/color-aliases.tokens.json
packages/design-data/tokens/color-component.tokens.json
packages/design-data/tokens/icons.tokens.json
packages/design-data/tokens/layout-component.tokens.json
packages/design-data/tokens/layout.tokens.json
packages/design-data/tokens/semantic-color-palette.tokens.json
packages/design-data/tokens/typography.tokens.json
sdk/core/src/authoring/lifecycle.rs
sdk/core/src/authoring/session.rs
sdk/core/src/diff.rs
sdk/core/src/legacy.rs
sdk/core/src/lib.rs
sdk/core/src/migrate.rs
sdk/core/src/validate/rules/spec010.rs
sdk/core/src/validate/rules/spec011.rs
sdk/core/src/validate/rules/spec012.rs
sdk/core/src/validate/rules/spec013.rs
sdk/core/src/validate/rules/spec014.rs
sdk/core/src/validate/rules/spec036.rs
sdk/core/src/validate/rules/spec037.rs

@github-actions

Copy link
Copy Markdown
Contributor

🎨 Token Changes Report

Tokens Changed (0)

Original Branch: main


This comment was automatically generated by the token diff tool. 🤖

@github-actions

Copy link
Copy Markdown
Contributor

🧩 Component Schema Changes Report

No component schema changes detected.

This comment was automatically generated by the component schema diff tool. 🤖

…d objects

diff_recursive's Added/Deleted branches never recursed into nested
objects, reporting one blob change for the whole subtree instead of
per-field leaf paths. This violated spec/diff.md's own normative rule
("comparison MUST be recursive... changes reported at the leaf level
with full dot-separated paths") whenever an object-valued key was
wholly added or removed — previously latent since deprecated/
deprecated_comment/replaced_by were flat scalar fields, but now
material since #1325 nested them under one `lifecycle` object.

Add record_added/record_deleted helpers that decompose a wholly-new or
wholly-removed subtree into individual leaf-level property_changes
entries, matching the granularity already used for updated (both-sides-
present) keys. Updates the three affected diff conformance fixtures
accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@GarthDB

GarthDB commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

Thanks for the thorough review — responding to all three points:

1. Diff-report granularity change

Confirmed and fixed in e8b6a68, rather than just documented. Traced this down: it wasn't actually specific to lifecyclediff_recursive's Added/Deleted branches never recursed into nested objects at all, so any wholly-added/removed object-valued key was reported as one blob change. This directly violates spec/diff.md's own normative rule ("comparison MUST be recursive... changes reported at the leaf level with full dot-separated paths"). It was latent before this PR because deprecated/deprecated_comment/replaced_by were flat scalars, so there was nothing to recurse into — the lifecycle nesting just made it visible for the first time.

Fix: added record_added/record_deleted helpers that decompose a wholly-new/removed subtree into individual leaf-level property_changes entries, matching the granularity already used when a key exists on both sides. replaced-by-pairing and matched-gaining-deprecated fixtures now show the expected per-field paths again (lifecycle.deprecatedIn, lifecycle.deprecatedComment, lifecycle.replacedBy), and I found + fixed one more latent case in cross-format/expected.json (a wholly-new name object was collapsing to path: "name" instead of name.property). Added a dedicated unit test (added_and_deleted_objects_decompose_to_leaf_paths).

Also had an agent trace every consumer of property_changes[].path (WASM bindings, both MCP servers, CLI formatters, docs, token-diff-generator/token-changeset-generator) — confirmed nothing outside diff.rs and its own tests does exact/substring matching on path strings; everything either treats path as opaque or runs an entirely separate legacy-format JS diff engine. So this was safe either way, but worth fixing properly rather than leaving the spec violation in place.

2. has_deprecated_field's dual-format check

This is intentional and permanent, not transitional debt — spec/diff.md has a dedicated normative section, "Cross-format compatibility": "A conforming diff engine MUST accept both legacy format... and cascade format... as inputs for either the old or new dataset, including mixed-format comparisons." The cross-format conformance fixture exercises exactly this. No tracking issue needed.

3. Truthiness widening

Confirmed intentional and inert for real data — checked: jq over all 1,323 production tokens' lifecycle.deprecatedIn values shows zero non-string entries. The widened check (!matches!(dep, Bool(false) | Null)) exists specifically so the loose deprecatedIn: true boolean used in some test fixtures (and tolerated defensively for cross-format inputs) still converts correctly to legacy deprecated: true, without changing behavior for any real string-version value.

All changes verified: cargo test --workspace (678 passed, 2 ignored — one more than before, from the new leaf-decomposition test), clippy -D warnings clean, fmt applied, and the legacy-output/roundtrip-verify pipeline is unaffected (this fix is isolated to diff.rs, which doesn't participate in cascade↔legacy conversion).

@GarthDB
GarthDB merged commit 6ef9247 into main Jul 31, 2026
9 checks passed
@GarthDB
GarthDB deleted the feat/nest-token-lifecycle-fields branch July 31, 2026 21:12
@GarthDB GarthDB mentioned this pull request Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant