@adobe/design-data-spec 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/LICENSE +201 -0
  2. package/conformance/README.md +83 -10
  3. package/conformance/diff/cross-format/expected.json +27 -0
  4. package/conformance/diff/cross-format/new/tokens.tokens.json +8 -0
  5. package/conformance/diff/cross-format/old/tokens.json +7 -0
  6. package/conformance/diff/deprecated-new-token/expected.json +13 -0
  7. package/conformance/diff/deprecated-new-token/new/tokens.tokens.json +8 -0
  8. package/conformance/diff/deprecated-new-token/old/tokens.tokens.json +1 -0
  9. package/conformance/diff/deprecated-set-level/expected.json +13 -0
  10. package/conformance/diff/deprecated-set-level/new/tokens.tokens.json +10 -0
  11. package/conformance/diff/deprecated-set-level/old/tokens.tokens.json +1 -0
  12. package/conformance/diff/identical-tokens/expected.json +8 -0
  13. package/conformance/diff/identical-tokens/new/tokens.tokens.json +12 -0
  14. package/conformance/diff/identical-tokens/old/tokens.tokens.json +12 -0
  15. package/conformance/diff/matched-gaining-deprecated/expected.json +20 -0
  16. package/conformance/diff/matched-gaining-deprecated/new/tokens.tokens.json +8 -0
  17. package/conformance/diff/matched-gaining-deprecated/old/tokens.tokens.json +7 -0
  18. package/conformance/diff/property-nested-change/expected.json +27 -0
  19. package/conformance/diff/property-nested-change/new/tokens.tokens.json +10 -0
  20. package/conformance/diff/property-nested-change/old/tokens.tokens.json +10 -0
  21. package/conformance/diff/property-value-update/expected.json +21 -0
  22. package/conformance/diff/property-value-update/new/tokens.tokens.json +7 -0
  23. package/conformance/diff/property-value-update/old/tokens.tokens.json +7 -0
  24. package/conformance/diff/rename-by-uuid/expected.json +22 -0
  25. package/conformance/diff/rename-by-uuid/new/tokens.tokens.json +7 -0
  26. package/conformance/diff/rename-by-uuid/old/tokens.tokens.json +7 -0
  27. package/conformance/diff/rename-with-property-changes/expected.json +28 -0
  28. package/conformance/diff/rename-with-property-changes/new/tokens.tokens.json +7 -0
  29. package/conformance/diff/rename-with-property-changes/old/tokens.tokens.json +7 -0
  30. package/conformance/diff/replaced-by-pairing/expected.json +43 -0
  31. package/conformance/diff/replaced-by-pairing/new/tokens.tokens.json +7 -0
  32. package/conformance/diff/replaced-by-pairing/old/tokens.tokens.json +10 -0
  33. package/conformance/diff/reverted-token/expected.json +13 -0
  34. package/conformance/diff/reverted-token/new/tokens.tokens.json +7 -0
  35. package/conformance/diff/reverted-token/old/tokens.tokens.json +8 -0
  36. package/conformance/diff/simple-add-delete/expected.json +18 -0
  37. package/conformance/diff/simple-add-delete/new/tokens.tokens.json +7 -0
  38. package/conformance/diff/simple-add-delete/old/tokens.tokens.json +7 -0
  39. package/conformance/diff/uuid-backfill/expected.json +20 -0
  40. package/conformance/diff/uuid-backfill/new/tokens.tokens.json +7 -0
  41. package/conformance/diff/uuid-backfill/old/tokens.tokens.json +6 -0
  42. package/conformance/invalid/SPEC-008/dimension.json +5 -0
  43. package/conformance/invalid/SPEC-008/expected-errors.json +10 -0
  44. package/conformance/invalid/SPEC-008/tokens.tokens.json +15 -0
  45. package/conformance/invalid/SPEC-010/expected-errors.json +10 -0
  46. package/conformance/invalid/SPEC-010/tokens.tokens.json +9 -0
  47. package/conformance/invalid/SPEC-011/expected-errors.json +10 -0
  48. package/conformance/invalid/SPEC-011/tokens.tokens.json +22 -0
  49. package/conformance/invalid/SPEC-012/expected-errors.json +10 -0
  50. package/conformance/invalid/SPEC-012/tokens.tokens.json +13 -0
  51. package/conformance/invalid/SPEC-013/expected-errors.json +10 -0
  52. package/conformance/invalid/SPEC-013/tokens.tokens.json +8 -0
  53. package/conformance/invalid/SPEC-014/expected-errors.json +10 -0
  54. package/conformance/invalid/SPEC-014/tokens.tokens.json +9 -0
  55. package/conformance/query/and-conditions/expected.json +1 -0
  56. package/conformance/query/and-conditions/input/tokens.tokens.json +17 -0
  57. package/conformance/query/and-conditions/query.txt +1 -0
  58. package/conformance/query/and-or-precedence/expected.json +1 -0
  59. package/conformance/query/and-or-precedence/input/tokens.tokens.json +22 -0
  60. package/conformance/query/and-or-precedence/query.txt +1 -0
  61. package/conformance/query/empty-matches-all/expected.json +1 -0
  62. package/conformance/query/empty-matches-all/input/tokens.tokens.json +17 -0
  63. package/conformance/query/empty-matches-all/query.txt +1 -0
  64. package/conformance/query/negation/expected.json +1 -0
  65. package/conformance/query/negation/input/tokens.tokens.json +17 -0
  66. package/conformance/query/negation/query.txt +1 -0
  67. package/conformance/query/no-matches/expected.json +1 -0
  68. package/conformance/query/no-matches/input/tokens.tokens.json +7 -0
  69. package/conformance/query/no-matches/query.txt +1 -0
  70. package/conformance/query/or-conditions/expected.json +1 -0
  71. package/conformance/query/or-conditions/input/tokens.tokens.json +17 -0
  72. package/conformance/query/or-conditions/query.txt +1 -0
  73. package/conformance/query/schema-key/expected.json +1 -0
  74. package/conformance/query/schema-key/input/tokens.tokens.json +14 -0
  75. package/conformance/query/schema-key/query.txt +1 -0
  76. package/conformance/query/single-field/expected.json +1 -0
  77. package/conformance/query/single-field/input/tokens.tokens.json +17 -0
  78. package/conformance/query/single-field/query.txt +1 -0
  79. package/conformance/query/wildcard-prefix/expected.json +1 -0
  80. package/conformance/query/wildcard-prefix/input/tokens.tokens.json +17 -0
  81. package/conformance/query/wildcard-prefix/query.txt +1 -0
  82. package/conformance/query/wildcard-suffix/expected.json +1 -0
  83. package/conformance/query/wildcard-suffix/input/tokens.tokens.json +17 -0
  84. package/conformance/query/wildcard-suffix/query.txt +1 -0
  85. package/conformance/resolution/alias-resolved-after-cascade/dimensions/color-scheme.json +5 -0
  86. package/conformance/resolution/alias-resolved-after-cascade/expected.json +5 -0
  87. package/conformance/resolution/alias-resolved-after-cascade/input/tokens.tokens.json +12 -0
  88. package/conformance/resolution/alias-resolved-after-cascade/query.json +4 -0
  89. package/conformance/resolution/base-fallback/dimensions/color-scheme.json +5 -0
  90. package/conformance/resolution/base-fallback/expected.json +5 -0
  91. package/conformance/resolution/base-fallback/input/tokens.tokens.json +7 -0
  92. package/conformance/resolution/base-fallback/query.json +4 -0
  93. package/conformance/resolution/specificity-wins/dimensions/color-scheme.json +5 -0
  94. package/conformance/resolution/specificity-wins/expected.json +5 -0
  95. package/conformance/resolution/specificity-wins/input/tokens.tokens.json +12 -0
  96. package/conformance/resolution/specificity-wins/query.json +4 -0
  97. package/conformance/valid/lifecycle-with-last-modified.json +12 -0
  98. package/fields/anatomy.json +15 -0
  99. package/fields/color-scheme.json +15 -0
  100. package/fields/component.json +15 -0
  101. package/fields/contrast.json +15 -0
  102. package/fields/density.json +15 -0
  103. package/fields/object.json +15 -0
  104. package/fields/orientation.json +15 -0
  105. package/fields/position.json +15 -0
  106. package/fields/property.json +15 -0
  107. package/fields/scale.json +15 -0
  108. package/fields/shape.json +15 -0
  109. package/fields/size.json +15 -0
  110. package/fields/state.json +15 -0
  111. package/fields/structure.json +15 -0
  112. package/fields/substructure.json +15 -0
  113. package/fields/variant.json +15 -0
  114. package/package.json +4 -3
  115. package/rules/rules.yaml +65 -2
  116. package/schemas/cascade-file.schema.json +10 -0
  117. package/schemas/field.schema.json +85 -0
  118. package/schemas/manifest.schema.json +55 -1
  119. package/schemas/token.schema.json +97 -18
  120. package/spec/cascade.md +18 -2
  121. package/spec/diff.md +97 -0
  122. package/spec/dimensions.md +15 -6
  123. package/spec/evolution.md +99 -0
  124. package/spec/index.md +11 -8
  125. package/spec/manifest.md +18 -1
  126. package/spec/query.md +135 -0
  127. package/spec/taxonomy.md +189 -0
  128. package/spec/token-format.md +118 -19
@@ -15,30 +15,73 @@
15
15
  "$defs": {
16
16
  "nameObject": {
17
17
  "type": "object",
18
- "description": "Structured token identity; additional dimension keys MUST be strings.",
18
+ "description": "Structured token identity. Semantic fields describe identity and structure; dimension fields drive cascade resolution. See spec/taxonomy.md and spec/token-format.md. Additional dimension keys MUST be strings.",
19
19
  "required": ["property"],
20
20
  "properties": {
21
21
  "property": {
22
22
  "type": "string",
23
- "minLength": 1
23
+ "minLength": 1,
24
+ "description": "Semantic: the stylistic attribute being defined (e.g. color, width, padding, gap)."
24
25
  },
25
26
  "component": {
26
- "type": "string"
27
+ "type": "string",
28
+ "description": "Semantic: component scope (e.g. button, checkbox)."
29
+ },
30
+ "structure": {
31
+ "type": "string",
32
+ "description": "Semantic: reusable visual pattern (e.g. base, container, list). Distinct from component."
33
+ },
34
+ "substructure": {
35
+ "type": "string",
36
+ "description": "Semantic: child within a parent structure (e.g. item in list-item)."
37
+ },
38
+ "anatomy": {
39
+ "type": "string",
40
+ "description": "Semantic: visible named part of a component (e.g. handle, icon, label)."
41
+ },
42
+ "object": {
43
+ "type": "string",
44
+ "description": "Semantic: styling surface (e.g. background, border, edge, visual)."
27
45
  },
28
46
  "variant": {
29
- "type": "string"
47
+ "type": "string",
48
+ "description": "Semantic: variant within a component (e.g. accent, negative, primary)."
30
49
  },
31
50
  "state": {
32
- "type": "string"
51
+ "type": "string",
52
+ "description": "Semantic: interactive or semantic state (e.g. hover, focus, disabled)."
53
+ },
54
+ "orientation": {
55
+ "type": "string",
56
+ "description": "Semantic: direction or order (e.g. vertical, horizontal)."
57
+ },
58
+ "position": {
59
+ "type": "string",
60
+ "description": "Semantic: location relative to another object (e.g. affixed, top, bottom)."
61
+ },
62
+ "size": {
63
+ "type": "string",
64
+ "description": "Semantic: relative t-shirt sizing (e.g. small, medium, large). Distinct from dimension scale."
65
+ },
66
+ "density": {
67
+ "type": "string",
68
+ "description": "Semantic: space within or around component parts (e.g. spacious, compact)."
69
+ },
70
+ "shape": {
71
+ "type": "string",
72
+ "description": "Semantic: overall component shape (e.g. uniform)."
33
73
  },
34
74
  "colorScheme": {
35
- "type": "string"
75
+ "type": "string",
76
+ "description": "Dimension: color scheme mode (e.g. light, dark, wireframe)."
36
77
  },
37
78
  "scale": {
38
- "type": "string"
79
+ "type": "string",
80
+ "description": "Dimension: platform density scale (e.g. desktop, mobile)."
39
81
  },
40
82
  "contrast": {
41
- "type": "string"
83
+ "type": "string",
84
+ "description": "Dimension: contrast level (e.g. regular, high)."
42
85
  }
43
86
  },
44
87
  "additionalProperties": {
@@ -88,20 +131,38 @@
88
131
  "introduced": {
89
132
  "type": "string"
90
133
  },
134
+ "lastModified": {
135
+ "type": "string",
136
+ "description": "Spec version when token value or metadata last changed (e.g. \"2.1.0\"). MUST be >= introduced (see SPEC-014)."
137
+ },
91
138
  "deprecated": {
92
- "type": "boolean"
139
+ "type": "string",
140
+ "description": "Spec version when token was deprecated (e.g. \"3.2.0\"). Truthy = deprecated."
93
141
  },
94
142
  "deprecated_comment": {
95
143
  "type": "string"
96
144
  },
97
- "status": {
98
- "type": "string"
145
+ "replaced_by": {
146
+ "oneOf": [
147
+ { "type": "string", "format": "uuid" },
148
+ {
149
+ "type": "array",
150
+ "items": { "type": "string", "format": "uuid" },
151
+ "minItems": 1
152
+ }
153
+ ],
154
+ "description": "UUID(s) of the replacement token(s). Array form requires deprecated_comment."
99
155
  },
100
- "renamed": {
101
- "type": "string"
156
+ "plannedRemoval": {
157
+ "type": "string",
158
+ "description": "Spec version when token will be removed."
102
159
  },
103
160
  "private": {
104
161
  "type": "boolean"
162
+ },
163
+ "description": {
164
+ "type": "string",
165
+ "description": "Plain text describing the token's purpose (aligns with DTCG $description)."
105
166
  }
106
167
  },
107
168
  "additionalProperties": false
@@ -131,20 +192,38 @@
131
192
  "introduced": {
132
193
  "type": "string"
133
194
  },
195
+ "lastModified": {
196
+ "type": "string",
197
+ "description": "Spec version when token value or metadata last changed (e.g. \"2.1.0\"). MUST be >= introduced (see SPEC-014)."
198
+ },
134
199
  "deprecated": {
135
- "type": "boolean"
200
+ "type": "string",
201
+ "description": "Spec version when token was deprecated (e.g. \"3.2.0\"). Truthy = deprecated."
136
202
  },
137
203
  "deprecated_comment": {
138
204
  "type": "string"
139
205
  },
140
- "status": {
141
- "type": "string"
206
+ "replaced_by": {
207
+ "oneOf": [
208
+ { "type": "string", "format": "uuid" },
209
+ {
210
+ "type": "array",
211
+ "items": { "type": "string", "format": "uuid" },
212
+ "minItems": 1
213
+ }
214
+ ],
215
+ "description": "UUID(s) of the replacement token(s). Array form requires deprecated_comment."
142
216
  },
143
- "renamed": {
144
- "type": "string"
217
+ "plannedRemoval": {
218
+ "type": "string",
219
+ "description": "Spec version when token will be removed."
145
220
  },
146
221
  "private": {
147
222
  "type": "boolean"
223
+ },
224
+ "description": {
225
+ "type": "string",
226
+ "description": "Plain text describing the token's purpose (aligns with DTCG $description)."
148
227
  }
149
228
  },
150
229
  "additionalProperties": false
package/spec/cascade.md CHANGED
@@ -26,7 +26,12 @@ Design data is organized in three layers, ordered from lowest to highest precede
26
26
 
27
27
  **NORMATIVE:** When two candidates from the **same layer** match the context, the candidate with **higher** specificity **MUST** win.
28
28
 
29
- **NORMATIVE:** Ties on layer and specificity **MUST** be broken by a deterministic **document order** rule defined by the manifest or dataset index (implementation-defined in this draft; validators **SHOULD** emit a warning on ambiguous ties).
29
+ **NORMATIVE:** Ties on layer and specificity **MUST** be broken by **document order**:
30
+
31
+ 1. Within a single source file, the token that appears **earlier** in the array **MUST** win.
32
+ 2. Across multiple files, the file with the **lexicographically earlier path** within the dataset **MUST** win.
33
+
34
+ **NORMATIVE:** Validators **MUST** emit a SPEC-006 warning when a tie is detected, as ties indicate potential authoring mistakes.
30
35
 
31
36
  ## Context
32
37
 
@@ -40,10 +45,19 @@ The following outline is **RECOMMENDED** for conforming resolvers:
40
45
  2. Filter to candidates at or below the requested layer.
41
46
  3. Select the maximum **layer** precedence.
42
47
  4. Within that layer, select the maximum **specificity**.
43
- 5. Break remaining ties by deterministic ordering.
48
+ 5. Break remaining ties by document order (earlier in file wins; lexicographically earlier file path wins across files). Emit SPEC-006 warning.
49
+ 6. If the winning candidate is an alias (`$ref`), **resolve the alias chain** to a literal value (see [Alias resolution](#alias-resolution)).
44
50
 
45
51
  Exact matching rules for omitted dimensions are defined alongside dimension declarations in [Dimensions](dimensions.md).
46
52
 
53
+ ## Alias resolution
54
+
55
+ **NORMATIVE:** Alias (`$ref`) resolution **MUST** occur **after** cascade selection. The resolution algorithm selects the winning candidate using layer, specificity, and tie-breaking rules before any alias chain is followed.
56
+
57
+ **NORMATIVE:** If the winning candidate is an alias, its `$ref` chain **MUST** be resolved to a literal value using the standard alias-resolution rules (SPEC-001 through SPEC-003 in `rules/rules.yaml`). The alias target is resolved within the same dataset (i.e. the same merged layer stack), not relative to any single layer.
58
+
59
+ **RATIONALE:** Aliases participate in cascade as opaque references — their target values are not examined during specificity calculation or layer comparison. This keeps resolution deterministic and allows aliases to be valid candidates at any specificity level.
60
+
47
61
  ## Cross-dimensional overrides
48
62
 
49
63
  **NORMATIVE:** Overrides that combine multiple dimensions in a way not expressible by a single name object alone **MUST** use **explicit combination tokens** (tokens whose name object sets multiple non-default dimensions) as defined in the dataset; magic merging of unrelated tokens is **NOT** allowed.
@@ -52,3 +66,5 @@ Exact matching rules for omitted dimensions are defined alongside dimension decl
52
66
 
53
67
  * [#646 — Token Schema Structure and Validation System](https://github.com/adobe/spectrum-design-data/discussions/646)
54
68
  * [#714 — Design Data Specification](https://github.com/adobe/spectrum-design-data/discussions/714)
69
+ * [#757 — Phase 2: Cascade tie-breaking rule](https://github.com/adobe/spectrum-design-data/issues/757)
70
+ * [#758 — Phase 2: Alias resolution ordering in cascade context](https://github.com/adobe/spectrum-design-data/issues/758)
package/spec/diff.md ADDED
@@ -0,0 +1,97 @@
1
+ # Semantic diff
2
+
3
+ **Spec version:** `1.0.0-draft` (see [Overview](index.md))
4
+
5
+ This document defines the **semantic diff model**: how two versions of a design data dataset are compared to produce a structured change report with awareness of renames, deprecations, and property-level changes.
6
+
7
+ ## Token identity
8
+
9
+ A **token identity** determines whether a token in the old dataset and a token in the new dataset refer to the same logical token.
10
+
11
+ **NORMATIVE:** Implementations **MUST** use the following matching rules, in order:
12
+
13
+ 1. **UUID match** — If a token in the old dataset and a token in the new dataset share the same `uuid` value, they are the **same token** regardless of name.
14
+ 2. **Name-object equivalence** — When a UUID match is not found for a token — because the old token, the new token, or both lack a `uuid`, or because no counterpart with the matching `uuid` exists in the other dataset — two tokens are the same if their `name` objects are deeply equal (all fields present with identical values).
15
+ 3. **Replacement link** — When passes 1 and 2 leave an old token unpaired and it carries a `replaced_by` field whose UUID matches an unpaired new token, the pair is established. This enables diff classification as **renamed** for tokens that were deprecated with a machine-readable replacement pointer.
16
+
17
+ **NORMATIVE:** UUID matching **MUST** take precedence over name-object equivalence, which **MUST** take precedence over replacement link matching. A UUID match always identifies the token pair, even if name objects differ (which constitutes a rename).
18
+
19
+ **RATIONALE:** UUID-based identity allows tokens to be renamed without breaking continuity tracking. Name-object equivalence is a fallback for legacy datasets that predate UUID adoption. Replacement link matching is a tertiary fallback for deprecated tokens that carry an explicit `replaced_by` UUID pointing to their successor.
20
+
21
+ ## Change taxonomy
22
+
23
+ A semantic diff classifies every token into exactly one of six categories:
24
+
25
+ | Category | Definition |
26
+ | -------------- | ------------------------------------------------------------------------------------------------ |
27
+ | **renamed** | Token exists in both datasets (matched by identity) but the name has changed. |
28
+ | **deprecated** | Token exists only in the new dataset (unmatched) and carries a `deprecated` field. |
29
+ | **reverted** | Token existed with `deprecated` in the old dataset and no longer carries it in the new dataset. |
30
+ | **added** | Token exists only in the new dataset and is not renamed, deprecated, or pre-existing. |
31
+ | **deleted** | Token exists only in the old dataset and is not the source of a rename. |
32
+ | **updated** | Token exists in both datasets (matched by identity) with the same name but changed properties. |
33
+
34
+ **NORMATIVE:** Every token that appears in the old dataset, the new dataset, or both **MUST** be classified into exactly one category. Categories are mutually exclusive.
35
+
36
+ ## Category partitioning
37
+
38
+ **NORMATIVE:** Implementations **MUST** resolve categories in the following order to ensure mutual exclusivity:
39
+
40
+ 1. **Renamed** — Identify all identity-matched pairs where the name has changed. These tokens are removed from further classification as added or deleted.
41
+ 2. **Deprecated** — Among remaining unmatched new tokens, identify those carrying a `deprecated` field. These are removed from the "added" pool.
42
+ 3. **Reverted** — Among identity-matched pairs, identify tokens where `deprecated` was present in the old version and absent in the new. These are removed from the "updated" pool.
43
+ 4. **Added** — Remaining unmatched new tokens that are not renamed, deprecated, or pre-existing in the old dataset.
44
+ 5. **Deleted** — Remaining unmatched old tokens that are not the source of a rename.
45
+ 6. **Updated** — Remaining identity-matched pairs with unchanged names but differing properties.
46
+
47
+ **RATIONALE:** This ordering mirrors the pipeline in existing tooling and ensures that a renamed token does not also appear as "added" + "deleted", a deprecated token does not appear as "added", and so forth. A matched token that newly gains a `deprecated` field is classified as **updated** — the deprecation surfaces as a property-level change in the updated sub-categories, not as a new-token deprecation.
48
+
49
+ ## Deprecation normalization
50
+
51
+ **NORMATIVE:** When a token uses the legacy `sets` structure and **all** set entries carry `deprecated: true`, the token **MUST** be treated as deprecated at the token level for diff classification purposes, even if the top-level token object does not carry `deprecated`.
52
+
53
+ **RATIONALE:** Set-level deprecation that covers all variants is semantically equivalent to token-level deprecation. Normalizing this prevents diff noise from implementation-level differences in where the `deprecated` flag is placed.
54
+
55
+ ## Property-level changes
56
+
57
+ For tokens classified as **updated** (or **renamed** with additional property changes), a semantic diff **SHOULD** produce property-level change records.
58
+
59
+ ### Change record format
60
+
61
+ Each property-level change is described by:
62
+
63
+ | Field | Type | Description |
64
+ | ---------------- | ------ | ------------------------------------------------------- |
65
+ | `path` | string | Dot-separated path from the token root (e.g. `value`, `name.colorScheme`, `sets.light.value`). |
66
+ | `new_value` | any | The value in the new dataset. Present for additions and updates. |
67
+ | `original_value` | any | The value in the old dataset. Present for deletions and updates. |
68
+
69
+ ### Property change sub-categories
70
+
71
+ | Sub-category | Condition |
72
+ | --------------------- | ------------------------------------------------- |
73
+ | **added-properties** | Property exists in new but not in old. |
74
+ | **deleted-properties**| Property exists in old but not in new. |
75
+ | **updated-properties**| Property exists in both but with different values. |
76
+
77
+ **NORMATIVE:** Property comparison **MUST** be recursive: nested objects are traversed and changes reported at the leaf level with full dot-separated paths.
78
+
79
+ **NORMATIVE:** Property comparison **MUST** use deep equality for values. Two values are equal if their JSON serializations are identical.
80
+
81
+ ## Output ordering
82
+
83
+ **RECOMMENDED:** Diff output **SHOULD** be deterministic. Within each category, tokens **SHOULD** be sorted by their canonical name (or new name for renames) in lexicographic order.
84
+
85
+ **RATIONALE:** Deterministic output makes diff reports suitable for snapshot testing and human review.
86
+
87
+ ## Cross-format compatibility
88
+
89
+ **NORMATIVE:** A conforming diff engine **MUST** accept both legacy format (JSON object maps) and cascade format (JSON arrays with `.tokens.json` extension) as inputs for either the old or new dataset, including mixed-format comparisons (e.g. legacy old, cascade new).
90
+
91
+ **RATIONALE:** The diff operates on the token graph abstraction, which normalizes both formats into the same `TokenRecord` structure. This enables diffing across format migrations without special-case handling.
92
+
93
+ ## References
94
+
95
+ * [#623 — Token Lifecycle Metadata](https://github.com/adobe/spectrum-design-data/discussions/623)
96
+ * [#714 — Design Data Specification](https://github.com/adobe/spectrum-design-data/discussions/714)
97
+ * [#776 — Phase 3: Diff change taxonomy and token identity rules](https://github.com/adobe/spectrum-design-data/issues/776)
@@ -25,13 +25,21 @@ A **dimension declaration** is a JSON object describing one axis of variation. I
25
25
 
26
26
  ## Built-in dimensions
27
27
 
28
- These dimensions **SHOULD** be used consistently across Spectrum-compatible datasets:
28
+ These dimensions are declared in the `dimensions/` catalog (see [Dimension catalog](#dimension-catalog)) and **SHOULD** be used consistently across Spectrum-compatible datasets:
29
29
 
30
- | `name` | Typical `modes` | Notes |
31
- | ------------- | ------------------------------- | ----------------------------- |
32
- | `colorScheme` | `light`, `dark`, `wireframe`, | Theme / appearance. |
33
- | `scale` | `medium`, `large`, | Density / t-shirt scale. |
34
- | `contrast` | `regular`, `high`, … | Accessibility contrast level. |
30
+ | `name` | `modes` | `default` | Notes |
31
+ | ------------- | ---------------------------- | --------- | --------------------------------------------------------------------------------- |
32
+ | `colorScheme` | `light`, `dark`, `wireframe` | `light` | Theme / appearance. |
33
+ | `scale` | `desktop`, `mobile` | `desktop` | Density scale. Legacy names; desktop = medium, mobile = large in W3C terminology. |
34
+ | `contrast` | `regular`, `high` | `regular` | Accessibility contrast level. |
35
+
36
+ ## Dimension catalog
37
+
38
+ The Spectrum foundation publishes dimension declarations as JSON files under `packages/design-data-spec/dimensions/`. Each file conforms to [`dimension.schema.json`](../schemas/dimension.schema.json).
39
+
40
+ **NORMATIVE:** Tooling (validators, resolution engine) **MUST** load dimension declarations from the dataset’s dimension catalog before performing specificity calculations or coverage validation.
41
+
42
+ **RECOMMENDED:** The catalog directory is named `dimensions/` and is co-located with the dataset’s spec package or manifest.
35
43
 
36
44
  ## Optional dimensions
37
45
 
@@ -53,3 +61,4 @@ Additional dimensions (e.g. `language`, `motion`) **MAY** be declared in a datas
53
61
 
54
62
  * [#646 — Token Schema Structure and Validation System](https://github.com/adobe/spectrum-design-data/discussions/646)
55
63
  * [#714 — Design Data Specification](https://github.com/adobe/spectrum-design-data/discussions/714)
64
+ * [#746 — Phase 2: Dimension declarations (machine-readable)](https://github.com/adobe/spectrum-design-data/issues/746)
@@ -0,0 +1,99 @@
1
+ # Evolution
2
+
3
+ **Spec version:** `1.0.0-draft` (see [Overview](index.md))
4
+
5
+ This document defines the **evolution policy** for the Design Data Specification: how the spec, schemas, and token data change over time, with a focus on the token deprecation lifecycle and backward compatibility.
6
+
7
+ ## Token deprecation lifecycle
8
+
9
+ Tokens progress through the following lifecycle stages:
10
+
11
+ ```
12
+ introduced → active → deprecated → planned removal → removed
13
+ ```
14
+
15
+ | Stage | Token state |
16
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
17
+ | **Introduced** | Token first appears in the dataset. `introduced` field records the version. |
18
+ | **Active** | Token is current and recommended for use. No `deprecated` field. |
19
+ | **Deprecated** | Token is no longer recommended. `deprecated` records the version. Consumers receive warnings. `replaced_by` points to the successor token(s) when a direct replacement exists. |
20
+ | **Planned removal** | `plannedRemoval` records the target version. The token remains in the dataset but consumers should complete migration. |
21
+ | **Removed** | Token is deleted from the dataset. Consumers that still reference it will break. |
22
+
23
+ ### Lifecycle fields
24
+
25
+ See [Token format — Lifecycle and metadata](token-format.md#lifecycle-and-metadata) for the full field definitions and normative rules.
26
+
27
+ ### What `replaced_by` guarantees
28
+
29
+ When a token carries `replaced_by`:
30
+
31
+ * Each target UUID **MUST** resolve to an existing token in the dataset (rule `SPEC-010`).
32
+ * The target token **SHOULD NOT** itself be deprecated. Chains of replacements (A replaced by B, B replaced by C) are discouraged; authors should point directly to the final replacement.
33
+ * For a single UUID (1:1 replacement), consumers can mechanically rewrite references.
34
+ * For an array of UUIDs (one-to-many split), the `deprecated_comment` **MUST** explain which replacement applies in which context.
35
+
36
+ ## Migration windows
37
+
38
+ **NORMATIVE:** A deprecated token **MUST** remain in the published dataset for at least **two minor versions** after the version recorded in `deprecated` before it may be removed.
39
+
40
+ **EXAMPLE:** A token deprecated in `3.2.0` may not be removed until `3.4.0` at the earliest. Removal in a major version (e.g. `4.0.0`) is always permitted regardless of how recently the token was deprecated.
41
+
42
+ **RATIONALE:** Two minor versions gives consumers at least two release cycles to migrate. The major-version escape hatch allows accumulated deprecations to be cleaned up in a coordinated breaking release.
43
+
44
+ If `plannedRemoval` is set, it overrides the default window — the token will be removed in the specified version (which must not precede the `deprecated` version).
45
+
46
+ ## Change classification
47
+
48
+ The specification follows [Semantic Versioning](https://semver.org/) for its published artifacts (schemas, rule catalog, spec documents).
49
+
50
+ ### Minor changes (backward compatible)
51
+
52
+ * New tokens added to the dataset
53
+ * New optional fields added to token schema
54
+ * New validation rules added to the rule catalog
55
+ * Tokens deprecated (with `deprecated` field)
56
+ * Rule severity relaxed (error → warning)
57
+ * New enum values added to registries
58
+
59
+ ### Major changes (breaking)
60
+
61
+ * Tokens removed from the dataset
62
+ * Required fields added to token schema
63
+ * Existing fields removed or type-changed
64
+ * Rule severity tightened (warning → error)
65
+ * Enum values removed from registries
66
+ * Constraint tightening (e.g. stricter value validation)
67
+
68
+ ### Patch changes (clarifications)
69
+
70
+ * Typo fixes in spec prose
71
+ * Clarifications to normative text that do not change conformance behavior
72
+ * Test fixture additions or corrections
73
+
74
+ ## Legacy format contract
75
+
76
+ The `@adobe/spectrum-tokens` package continues to publish tokens in the **legacy format** (JSON object maps with `color-set`, `scale-set`, etc.) for backward compatibility with existing consumers.
77
+
78
+ **NORMATIVE:** The legacy format output **MUST** be generated from the cascade-format authoritative source. It is a **derived artifact**, not independently authored.
79
+
80
+ ### Lifecycle field mapping
81
+
82
+ | Cascade format | Legacy format |
83
+ | -------------------------------------- | ------------------------------------- |
84
+ | `deprecated: "3.2.0"` (version string) | `deprecated: true` (boolean) |
85
+ | `replaced_by: "<uuid>"` | `renamed: "<target-token-name>"` |
86
+ | `introduced` | Not emitted |
87
+ | `lastModified` | Not emitted |
88
+ | `plannedRemoval` | Not emitted |
89
+ | `deprecated_comment` | `deprecated_comment` (passed through) |
90
+
91
+ ### Coexistence during migration
92
+
93
+ Both formats are published simultaneously. The cascade format is the source of truth; the legacy format is generated from it. This dual-format period continues until platform consumers have migrated to the cascade format or to platform SDKs that consume it.
94
+
95
+ ## References
96
+
97
+ * [#623 — Token Lifecycle Metadata](https://github.com/adobe/spectrum-design-data/discussions/623)
98
+ * [#735 — RFC: Versioning and Evolution](https://github.com/adobe/spectrum-design-data/discussions/735)
99
+ * [#736 — Define spec evolution policy and migration contract](https://github.com/adobe/spectrum-design-data/issues/736)
package/spec/index.md CHANGED
@@ -10,14 +10,13 @@ This document is the top-level overview for the **Design Data Specification**: a
10
10
  The specification defines:
11
11
 
12
12
  1. **Token format** — structured token identity (`name`), literal `value` or alias `$ref`, and lifecycle metadata ([Token format](token-format.md)).
13
- 2. **Cascade and resolution** — layered data (foundation, platform, product), specificity, and how a context picks a winning value ([Cascade](cascade.md)).
14
- 3. **Dimensions** — declared modes, defaults, and coverage expectations ([Dimensions](dimensions.md)).
15
- 4. **Platform manifest** — how a platform repo pins foundation data, filters tokens, and applies typed overrides ([Manifest](manifest.md)).
16
-
17
- Out of scope for this draft (tracked elsewhere):
18
-
19
- * Exact query syntax for `include` / `exclude` in manifests (placeholder fields only).
20
- * Full evolution, migration windows, and SDK version negotiation — see [discussion #735](https://github.com/adobe/spectrum-design-data/discussions/735).
13
+ 2. **Taxonomy** — concept categories, token term vocabulary, formatting style, and the distinction between component anatomy and token objects ([Taxonomy](taxonomy.md)).
14
+ 3. **Cascade and resolution** — layered data (foundation, platform, product), specificity, and how a context picks a winning value ([Cascade](cascade.md)).
15
+ 4. **Dimensions** — declared modes, defaults, and coverage expectations ([Dimensions](dimensions.md)).
16
+ 5. **Platform manifest** — how a platform repo pins foundation data, filters tokens, and applies typed overrides ([Manifest](manifest.md)).
17
+ 6. **Semantic diff** — change taxonomy, token identity rules, and property-level change tracking for comparing dataset versions ([Diff](diff.md)).
18
+ 7. **Query notation** — filter syntax for selecting tokens by structured fields ([Query](query.md)).
19
+ 8. **Evolution** deprecation lifecycle, migration windows, change classification, and legacy format contract ([Evolution](evolution.md)).
21
20
 
22
21
  ## Conformance
23
22
 
@@ -54,9 +53,13 @@ Full governance (compatibility tiers, migration, CLI `--spec-version`) is discus
54
53
  | Document | Role |
55
54
  | ------------------------------- | ---------------------------------------------------------------- |
56
55
  | [Token format](token-format.md) | Token `name`, `value` / `$ref`, value types, lifecycle metadata. |
56
+ | [Taxonomy](taxonomy.md) | Concept categories, vocabulary, formatting, anatomy vs objects. |
57
57
  | [Cascade](cascade.md) | Layers, specificity, resolution algorithm. |
58
58
  | [Dimensions](dimensions.md) | Dimension declarations, built-in dimensions, coverage. |
59
59
  | [Manifest](manifest.md) | Platform manifest fields and validation expectations. |
60
+ | [Diff](diff.md) | Semantic diff change taxonomy, token identity, property changes. |
61
+ | [Query](query.md) | Filter notation for selecting tokens by structured fields. |
62
+ | [Evolution](evolution.md) | Deprecation lifecycle, migration windows, change classification. |
60
63
 
61
64
  ## JSON Schema `$id` and versioning
62
65
 
package/spec/manifest.md CHANGED
@@ -26,7 +26,9 @@ A manifest **MUST** conform to [`manifest.schema.json`](../schemas/manifest.sche
26
26
 
27
27
  ### `include` / `exclude`
28
28
 
29
- **NORMATIVE:** Entries **MUST** be non-empty strings. The **query language** is **not** normative in `1.0.0-draft`; treat values as opaque identifiers for tooling until a future spec version defines syntax.
29
+ **NORMATIVE:** Entries **MUST** be non-empty strings.
30
+
31
+ > **Note:** Query notation syntax is fully defined in [Query](query.md) and is normative for programmatic use. Its normative use in manifest `include`/`exclude` is deferred to a post-`1.0.0-draft` revision pending conformance fixtures; until then, implementations **MUST** treat manifest query values as opaque identifiers.
30
32
 
31
33
  ### `overrides`
32
34
 
@@ -38,6 +40,21 @@ Each override object **MUST** include enough information to identify a target to
38
40
 
39
41
  **RECOMMENDED:** `extensions` follows the same structural conventions as foundation token files (tokens, dimensions) and **SHOULD** be validated with the same Layer 1 and Layer 2 rules.
40
42
 
43
+ #### `extensions.formatting`
44
+
45
+ A platform **MAY** declare formatting rules that control how structured name objects are serialized into flat token name strings for that platform. See [Taxonomy — Platform formatting configuration](taxonomy.md#platform-formatting-configuration) for motivation and examples.
46
+
47
+ | Field | Type | Description |
48
+ | --------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `conceptOrder` | array of string | Ordered list of name object field names for serialization. Each entry **MUST** be a declared field name from the design system's [field catalog](../fields/) (see [Token format — Name object](token-format.md#name-object)). Omitted fields are appended in the default order defined by each field declaration's `serialization.position` (see [Taxonomy — Default serialization](taxonomy.md#default-serialization-legacy-format)). |
50
+ | `casing` | string | One of: `kebab-case`, `camelCase`, `PascalCase`, `SCREAMING_SNAKE_CASE`. Default: `kebab-case`. |
51
+ | `delimiter` | string | Character(s) separating concepts in the serialized string (e.g. `-`, `_`, `.`, `/`). Default: `-`. |
52
+ | `abbreviations` | object | Map of full term → abbreviated form (e.g. `{ "background": "bg" }`). Abbreviations are applied after concept ordering and before casing. |
53
+
54
+ **NORMATIVE:** When `extensions.formatting` is absent, the default serialization defined in [Taxonomy](taxonomy.md#default-serialization-legacy-format) is used.
55
+
56
+ **NORMATIVE:** A formatter applying `extensions.formatting` **MUST** produce deterministic output — the same name object and formatting configuration **MUST** always yield the same string.
57
+
41
58
  ## Validation
42
59
 
43
60
  **NORMATIVE:** Manifests **MUST** pass Layer 1 JSON Schema validation.