@adobe/design-data-spec 0.2.0 → 0.3.0

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 (37) hide show
  1. package/README.md +3 -3
  2. package/components/button.json +70 -0
  3. package/conformance/README.md +26 -26
  4. package/conformance/invalid/SPEC-014/expected-errors.json +10 -0
  5. package/conformance/invalid/SPEC-014/tokens.tokens.json +9 -0
  6. package/conformance/invalid/SPEC-018/dataset.json +9 -0
  7. package/conformance/invalid/SPEC-018/expected-errors.json +10 -0
  8. package/conformance/invalid/SPEC-019/dataset.json +29 -0
  9. package/conformance/invalid/SPEC-019/expected-errors.json +10 -0
  10. package/conformance/invalid/SPEC-020/dataset.json +27 -0
  11. package/conformance/invalid/SPEC-020/expected-errors.json +10 -0
  12. package/conformance/invalid/SPEC-021/dataset.json +18 -0
  13. package/conformance/invalid/SPEC-021/expected-errors.json +10 -0
  14. package/conformance/invalid/SPEC-022/dataset.json +33 -0
  15. package/conformance/invalid/SPEC-022/expected-errors.json +10 -0
  16. package/conformance/invalid/SPEC-023/dataset.json +18 -0
  17. package/conformance/invalid/SPEC-023/expected-errors.json +10 -0
  18. package/conformance/invalid/SPEC-024/dataset.json +18 -0
  19. package/conformance/invalid/SPEC-024/expected-errors.json +10 -0
  20. package/conformance/valid/component-refs/dataset.json +63 -0
  21. package/conformance/valid/lifecycle-with-last-modified.json +12 -0
  22. package/package.json +18 -6
  23. package/rules/rules.yaml +81 -0
  24. package/schemas/anatomy-part.schema.json +35 -0
  25. package/schemas/component.schema.json +267 -0
  26. package/schemas/state-declaration.schema.json +36 -0
  27. package/spec/agent-surface.md +116 -0
  28. package/spec/anatomy-format.md +167 -0
  29. package/spec/component-format.md +326 -0
  30. package/spec/evolution.md +32 -32
  31. package/spec/index.md +27 -21
  32. package/spec/manifest.md +3 -1
  33. package/spec/state-model.md +245 -0
  34. package/spec/taxonomy.md +17 -1
  35. package/spec/token-format.md +1 -1
  36. package/src/canonical.js +61 -0
  37. package/src/validate.js +166 -0
package/README.md CHANGED
@@ -16,9 +16,9 @@ Normative **Design Data Specification** artifacts for Spectrum: human-readable s
16
16
 
17
17
  ## Tasks
18
18
 
19
- - `moon run designDataSpec:check` — verify required paths exist (layout guard).
19
+ * `moon run design-data-spec:check` — verify required paths exist (layout guard).
20
20
 
21
21
  ## References
22
22
 
23
- - [Design Data Specification project](https://github.com/orgs/adobe/projects/89)
24
- - Discussion [#714](https://github.com/adobe/spectrum-design-data/discussions/714) — umbrella spec
23
+ * [Design Data Specification project](https://github.com/orgs/adobe/projects/89)
24
+ * Discussion [#714](https://github.com/adobe/spectrum-design-data/discussions/714) — umbrella spec
@@ -0,0 +1,70 @@
1
+ {
2
+ "$schema": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/component.schema.json",
3
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
4
+ "specVersion": "1.0.0-draft",
5
+ "name": "button",
6
+ "displayName": "Button",
7
+ "description": "Buttons allow users to perform an action or to navigate to another page.",
8
+ "meta": {
9
+ "category": "actions",
10
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
11
+ },
12
+ "options": {
13
+ "variant": {
14
+ "type": "string",
15
+ "enum": ["accent", "negative", "primary", "secondary"],
16
+ "default": "accent",
17
+ "description": "Visual emphasis level."
18
+ },
19
+ "style": {
20
+ "type": "string",
21
+ "enum": ["fill", "outline"],
22
+ "default": "fill"
23
+ },
24
+ "size": {
25
+ "type": "string",
26
+ "enum": ["s", "m", "l", "xl"],
27
+ "default": "m"
28
+ },
29
+ "isDisabled": { "type": "boolean", "default": false },
30
+ "isPending": { "type": "boolean", "default": false },
31
+ "isLabelHidden": { "type": "boolean", "default": false },
32
+ "icon": {
33
+ "$ref": "https://opensource.adobe.com/spectrum-design-data/schemas/types/workflow-icon.json",
34
+ "description": "Icon placed at the start of the button. Required when isLabelHidden is true."
35
+ },
36
+ "staticColor": {
37
+ "type": "string",
38
+ "enum": ["white", "black"],
39
+ "description": "Static color for use on colored backgrounds."
40
+ }
41
+ },
42
+ "slots": [
43
+ {
44
+ "name": "default",
45
+ "description": "Text label of the button.",
46
+ "required": false
47
+ },
48
+ {
49
+ "name": "icon",
50
+ "description": "Icon placed at the start of the button."
51
+ }
52
+ ],
53
+ "anatomy": [
54
+ { "name": "icon", "description": "Leading icon." },
55
+ { "name": "label", "description": "Button text.", "required": true }
56
+ ],
57
+ "states": [
58
+ { "name": "hover", "trigger": "interaction", "precedence": 50 },
59
+ {
60
+ "name": "focus",
61
+ "trigger": "interaction",
62
+ "precedence": 60,
63
+ "layered": true
64
+ },
65
+ { "name": "disabled", "trigger": "prop", "precedence": 100 }
66
+ ],
67
+ "lifecycle": {
68
+ "introduced": "1.0.0-draft"
69
+ }
70
+ }
@@ -48,20 +48,20 @@ Each **diff** case lives under `diff/<name>/` with:
48
48
  * `new/` — new token dataset
49
49
  * `expected.json` — full `DiffReport` structure (six category arrays: renamed, deprecated, reverted, added, deleted, updated)
50
50
 
51
- | Folder | Intent |
52
- | ------------------------------------- | -------------------------------------------------------------------------------- |
53
- | `diff/identical-tokens` | Two identical datasets MUST produce an empty diff (all arrays empty). |
54
- | `diff/simple-add-delete` | One old-only token → deleted; one new-only token → added. |
55
- | `diff/rename-by-uuid` | Same UUID, different name objects → renamed (not add + delete). |
56
- | `diff/deprecated-new-token` | Unmatched new token with `deprecated: true` → deprecated (not added). |
57
- | `diff/deprecated-set-level` | All set entries `deprecated: true` normalizes to token-level deprecated. |
58
- | `diff/reverted-token` | Matched token that loses `deprecated` → reverted (not updated). |
59
- | `diff/matched-gaining-deprecated` | Matched token that gains `deprecated` → updated (not deprecated). |
60
- | `diff/property-value-update` | Matched token with changed `value` → updated with property change. |
61
- | `diff/property-nested-change` | Nested object change reported at leaf path (e.g. `sets.light.value`). |
62
- | `diff/uuid-backfill` | Old lacks UUID, new gains it with same name object → paired (not add + delete). |
63
- | `diff/cross-format` | Legacy old + cascade new, paired by UUID across formats. |
64
- | `diff/rename-with-property-changes` | Renamed token with additional value changes populates `property_changes`. |
51
+ | Folder | Intent |
52
+ | ----------------------------------- | ------------------------------------------------------------------------------- |
53
+ | `diff/identical-tokens` | Two identical datasets MUST produce an empty diff (all arrays empty). |
54
+ | `diff/simple-add-delete` | One old-only token → deleted; one new-only token → added. |
55
+ | `diff/rename-by-uuid` | Same UUID, different name objects → renamed (not add + delete). |
56
+ | `diff/deprecated-new-token` | Unmatched new token with `deprecated: true` → deprecated (not added). |
57
+ | `diff/deprecated-set-level` | All set entries `deprecated: true` normalizes to token-level deprecated. |
58
+ | `diff/reverted-token` | Matched token that loses `deprecated` → reverted (not updated). |
59
+ | `diff/matched-gaining-deprecated` | Matched token that gains `deprecated` → updated (not deprecated). |
60
+ | `diff/property-value-update` | Matched token with changed `value` → updated with property change. |
61
+ | `diff/property-nested-change` | Nested object change reported at leaf path (e.g. `sets.light.value`). |
62
+ | `diff/uuid-backfill` | Old lacks UUID, new gains it with same name object → paired (not add + delete). |
63
+ | `diff/cross-format` | Legacy old + cascade new, paired by UUID across formats. |
64
+ | `diff/rename-with-property-changes` | Renamed token with additional value changes populates `property_changes`. |
65
65
 
66
66
  The Rust SDK drives these fixtures in `sdk/core/src/lib.rs` (`diff_conformance` module, closes [#788](https://github.com/adobe/spectrum-design-data/issues/788)).
67
67
 
@@ -75,17 +75,17 @@ Each **query** case lives under `query/<name>/` with:
75
75
  * `query.txt` — plain-text filter expression
76
76
  * `expected.json` — sorted array of matched token UUIDs
77
77
 
78
- | Folder | Intent |
79
- | ----------------------------- | ------------------------------------------------------------------------- |
80
- | `query/single-field` | Basic `key=value` equality filter. |
81
- | `query/and-conditions` | `,` (AND) requires all conditions to match. |
82
- | `query/or-conditions` | `\|` (OR) matches if any alternative matches. |
83
- | `query/negation` | `!=` matches non-equal values and absent fields. |
84
- | `query/wildcard-suffix` | Glob `*` at end of value matches prefix. |
85
- | `query/wildcard-prefix` | Glob `*` at start of value matches suffix. |
86
- | `query/empty-matches-all` | Empty filter expression is a universal match. |
87
- | `query/no-matches` | Filter with no matching tokens returns empty result. |
88
- | `query/schema-key` | `$schema` key queries the top-level `$schema` field. |
89
- | `query/and-or-precedence` | AND binds tighter than OR: `a,b\|c` = `(a AND b) OR c`. |
78
+ | Folder | Intent |
79
+ | ------------------------- | ------------------------------------------------------- |
80
+ | `query/single-field` | Basic `key=value` equality filter. |
81
+ | `query/and-conditions` | `,` (AND) requires all conditions to match. |
82
+ | `query/or-conditions` | `\|` (OR) matches if any alternative matches. |
83
+ | `query/negation` | `!=` matches non-equal values and absent fields. |
84
+ | `query/wildcard-suffix` | Glob `*` at end of value matches prefix. |
85
+ | `query/wildcard-prefix` | Glob `*` at start of value matches suffix. |
86
+ | `query/empty-matches-all` | Empty filter expression is a universal match. |
87
+ | `query/no-matches` | Filter with no matching tokens returns empty result. |
88
+ | `query/schema-key` | `$schema` key queries the top-level `$schema` field. |
89
+ | `query/and-or-precedence` | AND binds tighter than OR: `a,b\|c` = `(a AND b) OR c`. |
90
90
 
91
91
  The Rust SDK drives these fixtures in `sdk/core/src/lib.rs` (`query_conformance` module, closes [#788](https://github.com/adobe/spectrum-design-data/issues/788)).
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-014",
6
+ "severity": "error",
7
+ "message_pattern": "lastModified .* earlier than introduced"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,9 @@
1
+ [
2
+ {
3
+ "name": { "property": "last-modified-before-introduced" },
4
+ "value": "#ffffff",
5
+ "uuid": "aaaaaaaa-0001-4000-8000-000000000001",
6
+ "introduced": "2.0.0",
7
+ "lastModified": "1.5.0"
8
+ }
9
+ ]
@@ -0,0 +1,9 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": { "component": "nonexistent", "property": "background-color" },
5
+ "value": "#ff0000"
6
+ }
7
+ ],
8
+ "components": []
9
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-018",
6
+ "severity": "error",
7
+ "message_pattern": ".*undeclared component.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "button",
6
+ "variant": "electric",
7
+ "property": "background-color"
8
+ },
9
+ "value": "#0000ff"
10
+ }
11
+ ],
12
+ "components": [
13
+ {
14
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
15
+ "name": "button",
16
+ "displayName": "Button",
17
+ "meta": {
18
+ "category": "actions",
19
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
20
+ },
21
+ "options": {
22
+ "variant": {
23
+ "type": "string",
24
+ "enum": ["accent", "negative", "primary", "secondary"]
25
+ }
26
+ }
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-019",
6
+ "severity": "error",
7
+ "message_pattern": ".*variant.*electric.*not declared.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "button",
6
+ "anatomy": "capsule",
7
+ "property": "background-color"
8
+ },
9
+ "value": "#ff0000"
10
+ }
11
+ ],
12
+ "components": [
13
+ {
14
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
15
+ "name": "button",
16
+ "displayName": "Button",
17
+ "meta": {
18
+ "category": "actions",
19
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
20
+ },
21
+ "anatomy": [
22
+ { "name": "icon", "description": "Leading icon." },
23
+ { "name": "label", "description": "Button text.", "required": true }
24
+ ]
25
+ }
26
+ ]
27
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-020",
6
+ "severity": "error",
7
+ "message_pattern": ".*undeclared anatomy part.*capsule.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "tokens": [],
3
+ "components": [
4
+ {
5
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
6
+ "name": "button",
7
+ "displayName": "Button",
8
+ "meta": {
9
+ "category": "actions",
10
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
11
+ },
12
+ "slots": [
13
+ { "name": "default", "description": "Text label of the button." },
14
+ { "name": "badge" }
15
+ ]
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-021",
6
+ "severity": "warning",
7
+ "message_pattern": ".*custom slot.*badge.*no description.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "button",
6
+ "state": "jello",
7
+ "property": "background-color"
8
+ },
9
+ "value": "#ff0000"
10
+ }
11
+ ],
12
+ "components": [
13
+ {
14
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
15
+ "name": "button",
16
+ "displayName": "Button",
17
+ "meta": {
18
+ "category": "actions",
19
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
20
+ },
21
+ "states": [
22
+ { "name": "hover", "trigger": "interaction", "precedence": 50 },
23
+ {
24
+ "name": "focus",
25
+ "trigger": "interaction",
26
+ "precedence": 60,
27
+ "layered": true
28
+ },
29
+ { "name": "disabled", "trigger": "prop", "precedence": 100 }
30
+ ]
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-022",
6
+ "severity": "error",
7
+ "message_pattern": ".*undeclared state.*jello.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "tokens": [],
3
+ "components": [
4
+ {
5
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
6
+ "name": "button",
7
+ "displayName": "Button",
8
+ "meta": {
9
+ "category": "actions",
10
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
11
+ },
12
+ "anatomy": [
13
+ { "name": "label", "description": "Button text.", "required": true },
14
+ { "name": "shimmer" }
15
+ ]
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-023",
6
+ "severity": "warning",
7
+ "message_pattern": ".*custom anatomy part.*shimmer.*no description.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "tokens": [],
3
+ "components": [
4
+ {
5
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
6
+ "name": "button",
7
+ "displayName": "Button",
8
+ "meta": {
9
+ "category": "actions",
10
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
11
+ },
12
+ "states": [
13
+ { "name": "hover", "trigger": "interaction", "precedence": 50 },
14
+ { "name": "wobble" }
15
+ ]
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "layer": 2,
3
+ "errors": [
4
+ {
5
+ "rule_id": "SPEC-024",
6
+ "severity": "warning",
7
+ "message_pattern": ".*custom state.*wobble.*no description.*"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "tokens": [
3
+ {
4
+ "name": {
5
+ "component": "button",
6
+ "variant": "accent",
7
+ "property": "background-color"
8
+ },
9
+ "value": "#0265dc"
10
+ },
11
+ {
12
+ "name": {
13
+ "component": "button",
14
+ "anatomy": "label",
15
+ "property": "color"
16
+ },
17
+ "value": "#ffffff"
18
+ },
19
+ {
20
+ "name": {
21
+ "component": "button",
22
+ "state": "hover",
23
+ "property": "background-color"
24
+ },
25
+ "value": "#0255be"
26
+ }
27
+ ],
28
+ "components": [
29
+ {
30
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/components/button.json",
31
+ "name": "button",
32
+ "displayName": "Button",
33
+ "meta": {
34
+ "category": "actions",
35
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
36
+ },
37
+ "options": {
38
+ "variant": {
39
+ "type": "string",
40
+ "enum": ["accent", "negative", "primary", "secondary"]
41
+ }
42
+ },
43
+ "slots": [
44
+ { "name": "default", "description": "Text label of the button." },
45
+ { "name": "icon", "description": "Leading icon." }
46
+ ],
47
+ "anatomy": [
48
+ { "name": "icon", "description": "Leading icon." },
49
+ { "name": "label", "description": "Button text.", "required": true }
50
+ ],
51
+ "states": [
52
+ { "name": "hover", "trigger": "interaction", "precedence": 50 },
53
+ {
54
+ "name": "focus",
55
+ "trigger": "interaction",
56
+ "precedence": 60,
57
+ "layered": true
58
+ },
59
+ { "name": "disabled", "trigger": "prop", "precedence": 100 }
60
+ ]
61
+ }
62
+ ]
63
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": {
3
+ "component": "button",
4
+ "object": "background",
5
+ "property": "color",
6
+ "variant": "primary"
7
+ },
8
+ "value": "#0265dc",
9
+ "uuid": "aaaaaaaa-0001-4000-8000-000000000001",
10
+ "introduced": "1.0.0",
11
+ "lastModified": "2.1.0"
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-spec",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Design Data Specification — prose, JSON Schemas, rule catalog, and conformance fixtures for Spectrum design data",
5
5
  "type": "module",
6
6
  "repository": {
@@ -25,15 +25,27 @@
25
25
  "./schemas/field.schema.json": "./schemas/field.schema.json",
26
26
  "./schemas/manifest.schema.json": "./schemas/manifest.schema.json",
27
27
  "./schemas/value-types/color.schema.json": "./schemas/value-types/color.schema.json",
28
- "./rules/rules.yaml": "./rules/rules.yaml"
28
+ "./schemas/component.schema.json": "./schemas/component.schema.json",
29
+ "./schemas/anatomy-part.schema.json": "./schemas/anatomy-part.schema.json",
30
+ "./schemas/state-declaration.schema.json": "./schemas/state-declaration.schema.json",
31
+ "./rules/rules.yaml": "./rules/rules.yaml",
32
+ "./src/validate.js": "./src/validate.js",
33
+ "./src/canonical.js": "./src/canonical.js"
29
34
  },
30
35
  "files": [
31
- "spec",
32
- "schemas",
36
+ "components",
37
+ "conformance",
33
38
  "fields",
34
39
  "rules",
35
- "conformance",
40
+ "schemas",
41
+ "spec",
42
+ "src",
36
43
  "README.md"
37
44
  ],
38
- "scripts": {}
45
+ "devDependencies": {
46
+ "ava": "^6.0.1"
47
+ },
48
+ "scripts": {
49
+ "test": "ava"
50
+ }
39
51
  }
package/rules/rules.yaml CHANGED
@@ -154,3 +154,84 @@ rules:
154
154
  message: 'Token "{name}" uses a string name instead of a name object — treat as tech debt and plan remediation'
155
155
  spec_ref: spec/token-format.md#string-name-escape-hatch
156
156
  introduced_in: "1.0.0-draft"
157
+
158
+ - id: SPEC-018
159
+ name: component-name-exists
160
+ severity: error
161
+ category: reference-integrity
162
+ assert: Token name-object 'component' field value MUST match the 'name' of a declared component in the dataset.
163
+ message: "Token '{token}' references undeclared component '{component}'"
164
+ spec_ref: spec/component-format.md#name
165
+ introduced_in: "1.0.0-draft"
166
+
167
+ - id: SPEC-019
168
+ name: component-variant-valid
169
+ severity: error
170
+ category: reference-integrity
171
+ assert: Token name-object 'variant' field value MUST match a value in the declared 'variant' option enum for the referenced component, when that enum exists.
172
+ message: "Token '{token}' has variant '{variant}' which is not declared on component '{component}'"
173
+ spec_ref: spec/component-format.md#options
174
+ introduced_in: "1.0.0-draft"
175
+
176
+ - id: SPEC-020
177
+ name: component-anatomy-valid
178
+ severity: error
179
+ category: reference-integrity
180
+ assert: Token name-object 'anatomy' field value MUST match the 'name' of a declared anatomy part on the referenced component.
181
+ message: "Token '{token}' references undeclared anatomy part '{anatomy}' on component '{component}'"
182
+ spec_ref: spec/component-format.md#anatomy-stub
183
+ introduced_in: "1.0.0-draft"
184
+
185
+ - id: SPEC-021
186
+ name: component-slot-vocabulary
187
+ severity: warning
188
+ category: component-contract
189
+ assert: Component slot declarations with a 'name' outside the canonical slot vocabulary SHOULD include a 'description' field documenting the custom slot's purpose.
190
+ message: "Component '{component}' has custom slot '{slot}' with no description — add a description or use a canonical slot name"
191
+ spec_ref: spec/component-format.md#canonical-slot-vocabulary
192
+ introduced_in: "1.0.0-draft"
193
+
194
+ - id: SPEC-022
195
+ name: component-state-valid
196
+ severity: error
197
+ category: reference-integrity
198
+ assert: Token name-object 'state' field value MUST match the 'name' of a declared state on the referenced component, when state declarations are present.
199
+ message: "Token '{token}' references undeclared state '{state}' on component '{component}'"
200
+ spec_ref: spec/component-format.md#spec-rules
201
+ introduced_in: "1.0.0-draft"
202
+
203
+ - id: SPEC-023
204
+ name: anatomy-custom-part-documented
205
+ severity: warning
206
+ category: component-contract
207
+ assert: Anatomy part declarations with a 'name' outside the canonical anatomy vocabulary SHOULD include a 'description' field.
208
+ message: "Component '{component}' has custom anatomy part '{part}' with no description"
209
+ spec_ref: spec/anatomy-format.md#canonical-anatomy-vocabulary
210
+ introduced_in: "1.0.0-draft"
211
+
212
+ - id: SPEC-024
213
+ name: anatomy-part-name-unique
214
+ severity: error
215
+ category: component-contract
216
+ assert: Anatomy part names within a single component's anatomy array MUST be unique.
217
+ message: "Component '{component}' declares duplicate anatomy part name '{part}'"
218
+ spec_ref: spec/anatomy-format.md#name
219
+ introduced_in: "1.0.0-draft"
220
+
221
+ - id: SPEC-025
222
+ name: anatomy-requires-component
223
+ severity: error
224
+ category: reference-integrity
225
+ assert: A token name object MUST NOT include an 'anatomy' field unless a 'component' field is also present.
226
+ message: "Token '{token}' has 'anatomy' field without a 'component' field"
227
+ spec_ref: spec/anatomy-format.md#cross-reference-with-token-name-objects
228
+ introduced_in: "1.0.0-draft"
229
+
230
+ - id: SPEC-026
231
+ name: state-custom-name-documented
232
+ severity: warning
233
+ category: component-contract
234
+ assert: State declarations with a 'name' outside the canonical state vocabulary SHOULD include a 'description' field.
235
+ message: "Component '{component}' has custom state '{state}' with no description"
236
+ spec_ref: spec/state-model.md#canonical-state-vocabulary
237
+ introduced_in: "1.0.0-draft"