@fuaran-ui/conformance 0.8.0 → 0.18.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 (97) hide show
  1. package/corpus/lenient/lenient-image-caption-envelope.expected.json +1 -0
  2. package/corpus/lenient/lenient-image-caption-envelope.json +1 -0
  3. package/corpus/lenient/lenient-image-empty-srcset.expected.json +1 -0
  4. package/corpus/lenient/lenient-image-empty-srcset.json +1 -0
  5. package/corpus/lenient/lenient-image-explicit-defaults.expected.json +1 -0
  6. package/corpus/lenient/lenient-image-explicit-defaults.json +1 -0
  7. package/corpus/lenient/lenient-image-explicit-expandable-false.expected.json +1 -0
  8. package/corpus/lenient/lenient-image-explicit-expandable-false.json +1 -0
  9. package/corpus/lenient/lenient-shape-a11y-label-bare-scalar.expected.json +1 -0
  10. package/corpus/lenient/lenient-shape-a11y-label-bare-scalar.json +1 -0
  11. package/corpus/manifest.json +674 -9
  12. package/corpus/nodes/a11y-alert-assertive.json +1 -0
  13. package/corpus/nodes/a11y-button-named.json +1 -0
  14. package/corpus/nodes/a11y-image-decorative.json +1 -0
  15. package/corpus/nodes/a11y-link-labelled.json +1 -0
  16. package/corpus/nodes/a11y-wrapper-all-slots.json +1 -0
  17. package/corpus/nodes/a11y-wrapper-state-bound.json +1 -0
  18. package/corpus/nodes/drawing-nonfinite-sentinels.json +1 -0
  19. package/corpus/nodes/form-field-rules.json +1 -0
  20. package/corpus/nodes/frag-stdlib-confirm-action-pair-ref.json +1 -0
  21. package/corpus/nodes/frag-stdlib-confirm-action-pair.json +1 -0
  22. package/corpus/nodes/frag-stdlib-empty-state-panel-ref.json +1 -0
  23. package/corpus/nodes/frag-stdlib-empty-state-panel.json +1 -0
  24. package/corpus/nodes/frag-stdlib-filter-bar-ref.json +1 -0
  25. package/corpus/nodes/frag-stdlib-filter-bar.json +1 -0
  26. package/corpus/nodes/frag-stdlib-kpi-strip-ref.json +1 -0
  27. package/corpus/nodes/frag-stdlib-kpi-strip.json +1 -0
  28. package/corpus/nodes/frag-stdlib-labelled-metric-row-ref.json +1 -0
  29. package/corpus/nodes/frag-stdlib-labelled-metric-row.json +1 -0
  30. package/corpus/nodes/frag-stdlib-loading-placeholder-ref.json +1 -0
  31. package/corpus/nodes/frag-stdlib-loading-placeholder.json +1 -0
  32. package/corpus/nodes/frag-stdlib-metric-card-ref.json +1 -0
  33. package/corpus/nodes/frag-stdlib-metric-card.json +1 -0
  34. package/corpus/nodes/frag-stdlib-section-header-ref.json +1 -0
  35. package/corpus/nodes/frag-stdlib-section-header.json +1 -0
  36. package/corpus/nodes/grid-reorderable.json +1 -0
  37. package/corpus/nodes/image-caption-1.json +1 -0
  38. package/corpus/nodes/image-caption-i18n-1.json +1 -0
  39. package/corpus/nodes/image-expandable-1.json +1 -0
  40. package/corpus/nodes/image-expandable-figure-1.json +1 -0
  41. package/corpus/nodes/image-presentation-1.json +1 -0
  42. package/corpus/nodes/image-srcset-1.json +1 -0
  43. package/corpus/nodes/limit-node-depth-at-max.json +1 -0
  44. package/corpus/nodes/masonry-1.json +1 -0
  45. package/corpus/nodes/masonry-gap.json +1 -0
  46. package/corpus/nodes/media-audio-1.json +1 -0
  47. package/corpus/nodes/media-video-1.json +1 -0
  48. package/corpus/nodes/media-video-autoplay-1.json +1 -0
  49. package/corpus/nodes/media-video-poster-1.json +1 -0
  50. package/corpus/nodes/metric-inverted-polarity.json +1 -0
  51. package/corpus/nodes/metric-nonfinite-sentinel.json +1 -0
  52. package/corpus/nodes/shared-source-seeded-pair.json +1 -0
  53. package/corpus/nodes/spark-nonfinite-sentinel.json +1 -0
  54. package/corpus/reject/reject-a11y-hidden-nonbool.json +1 -0
  55. package/corpus/reject/reject-a11y-labelledby-nonstring.json +1 -0
  56. package/corpus/reject/reject-a11y-liveregion-nonstring.json +1 -0
  57. package/corpus/reject/reject-a11y-liveregion-unknown.json +1 -0
  58. package/corpus/reject/reject-a11y-not-object.json +1 -0
  59. package/corpus/reject/reject-a11y-role-nonstring.json +1 -0
  60. package/corpus/reject/reject-binding-float-bool.json +1 -0
  61. package/corpus/reject/reject-binding-float-sentinel-case.json +1 -0
  62. package/corpus/reject/reject-binding-float-string.json +1 -0
  63. package/corpus/reject/reject-binding-int-bool.json +1 -0
  64. package/corpus/reject/reject-binding-int-sentinel-string.json +1 -0
  65. package/corpus/reject/reject-box-masonry-nonpositive-cols.json +1 -0
  66. package/corpus/reject/reject-fieldrule-empty.json +1 -0
  67. package/corpus/reject/reject-fieldrule-length-unordered.json +1 -0
  68. package/corpus/reject/reject-formfield-near-miss-validation.json +1 -0
  69. package/corpus/reject/reject-image-expandable-nonbool.json +1 -0
  70. package/corpus/reject/reject-image-srcset-nonpositive-width.json +1 -0
  71. package/corpus/reject/reject-image-srcset-null.json +1 -0
  72. package/corpus/reject/reject-limit-json-depth-at-max.json +1 -0
  73. package/corpus/reject/reject-limit-json-depth.json +1 -0
  74. package/corpus/reject/reject-limit-node-depth.json +1 -0
  75. package/corpus/reject/reject-limit-op-depth.json +1 -0
  76. package/corpus/reject/reject-media-autoplay-nonbool.json +1 -0
  77. package/corpus/reject/reject-media-missing-label.json +1 -0
  78. package/corpus/reject/reject-nearmiss-a11y-aria-hidden.json +1 -0
  79. package/corpus/reject/reject-nearmiss-a11y-arialabel.json +1 -0
  80. package/corpus/reject/reject-nearmiss-a11y-live.json +1 -0
  81. package/corpus/reject/reject-nearmiss-a11y-liveregion-case.json +1 -0
  82. package/corpus/reject/reject-op-insertchild-empty-childid.json +1 -1
  83. package/corpus/reject/reject-op-insertchild-missing-parentid.json +1 -1
  84. package/corpus/reject/reject-op-insertchild-retired-position.json +1 -0
  85. package/corpus/reject/reject-op-movenode-retired-newposition.json +1 -0
  86. package/corpus/reject/reject-unknown-image-aspect.json +1 -0
  87. package/corpus/reject/reject-unknown-media-kind.json +1 -0
  88. package/corpus/reject/reject-unknown-trend-polarity.json +1 -0
  89. package/corpus/schema.json +3476 -115
  90. package/dist/cli.js +7 -1
  91. package/dist/cli.js.map +1 -1
  92. package/dist/index.cjs +7 -1
  93. package/dist/index.cjs.map +1 -1
  94. package/dist/index.js +7 -1
  95. package/dist/index.js.map +1 -1
  96. package/package.json +3 -3
  97. package/corpus/reject/reject-transform-source-empty-wrapper.json +0 -1
@@ -30,6 +30,7 @@
30
30
  "Map",
31
31
  "Markdown",
32
32
  "Math",
33
+ "Media",
33
34
  "Metric",
34
35
  "Modal",
35
36
  "Mount",
@@ -627,6 +628,38 @@
627
628
  "expectedFile": "lenient/lenient-grid-transform-param-compact.expected.json",
628
629
  "description": "Phase 719 — the compact authoring twin of `grid-transform-param`: all-valid columns as bare arrays (Core#88) and the inferable schema omitted; the canonical envelope stays the encoder's output. The pack teaches this form by example — the exemplar/prose contradiction measurably taught the envelope (claude-opus 81% envelope share, 2026-07-28 census)."
629
630
  },
631
+ {
632
+ "id": "lenient-image-caption-envelope",
633
+ "kind": "lenient-accept",
634
+ "decoder": "node",
635
+ "inputFile": "lenient/lenient-image-caption-envelope.json",
636
+ "expectedFile": "lenient/lenient-image-caption-envelope.expected.json",
637
+ "description": "Image — the enveloped `{\"$type\":\"Literal\"}` TextSource form on `caption` decodes and canonicalises to the bare string, exactly as it does on `alt` (Phase 1078). The slot is a full `TextSource`, so a host that narrowed it to a plain string cannot decode this input at all."
638
+ },
639
+ {
640
+ "id": "lenient-image-empty-srcset",
641
+ "kind": "lenient-accept",
642
+ "decoder": "node",
643
+ "inputFile": "lenient/lenient-image-empty-srcset.json",
644
+ "expectedFile": "lenient/lenient-image-empty-srcset.expected.json",
645
+ "description": "Image — an explicitly EMPTY `srcSet` decodes and canonicalises away to the omitted form (Phase 1080). Absent and `[]` denote the same document, so an image that declares no alternate renditions costs no key on the wire"
646
+ },
647
+ {
648
+ "id": "lenient-image-explicit-defaults",
649
+ "kind": "lenient-accept",
650
+ "decoder": "node",
651
+ "inputFile": "lenient/lenient-image-explicit-defaults.json",
652
+ "expectedFile": "lenient/lenient-image-explicit-defaults.expected.json",
653
+ "description": "Image — explicit-default `fit`/`aspectRatio`/`loading` decode and canonicalise away (omitted-when-default on both boundaries from day one, Phase 1077), and the bare-string TextSource shorthand applies to `alt`. The canonical minimal Image is exactly the pre-phase three-field form."
654
+ },
655
+ {
656
+ "id": "lenient-image-explicit-expandable-false",
657
+ "kind": "lenient-accept",
658
+ "decoder": "node",
659
+ "inputFile": "lenient/lenient-image-explicit-expandable-false.json",
660
+ "expectedFile": "lenient/lenient-image-explicit-expandable-false.expected.json",
661
+ "description": "Image — an explicit `expandable: false` decodes and canonicalises away to the omitted form (Phase 1079). `false` is the identity: not declaring an expansion and declaring that there is none are the same document"
662
+ },
630
663
  {
631
664
  "id": "lenient-master-detail-preselected-compact",
632
665
  "kind": "lenient-accept",
@@ -683,6 +716,14 @@
683
716
  "expectedFile": "lenient/lenient-scalar-transform-composition-compact.expected.json",
684
717
  "description": "Phase 719 — the compact authoring twin of `scalar-transform-composition`: all-valid columns as bare arrays (Core#88) and the inferable schema omitted; the canonical envelope stays the encoder's output. The pack teaches this form by example — the exemplar/prose contradiction measurably taught the envelope (claude-opus 81% envelope share, 2026-07-28 census)."
685
718
  },
719
+ {
720
+ "id": "lenient-shape-a11y-label-bare-scalar",
721
+ "kind": "lenient-accept",
722
+ "decoder": "node",
723
+ "inputFile": "lenient/lenient-shape-a11y-label-bare-scalar.json",
724
+ "expectedFile": "lenient/lenient-shape-a11y-label-bare-scalar.expected.json",
725
+ "description": "fuaran#957 ruling — the Accessibility trait's `label` (Binding<string>) and `hidden` (Binding<bool>) are ordinary Binding slots: a bare string / bool coerces to `Static` per the general §3.6 scalar rule (decode-only, canonical envelope on re-encode). Pinned at this position because two hosts once implemented the slot as bare-string-ONLY — the opposite error."
726
+ },
686
727
  {
687
728
  "id": "lenient-shape-binding-scalar-fraction",
688
729
  "kind": "lenient-accept",
@@ -867,6 +908,54 @@
867
908
  "expectedFile": "lenient/lenient-window-cumsum-legacy.expected.json",
868
909
  "description": "2026-07-19 rename — the legacy cumSum window-fn tag coerces to the canonical cumulSum"
869
910
  },
911
+ {
912
+ "id": "a11y-alert-assertive",
913
+ "kind": "node-round-trip",
914
+ "decoder": "node",
915
+ "inputFile": "nodes/a11y-alert-assertive.json",
916
+ "expectedFile": "nodes/a11y-alert-assertive.json",
917
+ "description": "Accessibility (Phase 955 — the announcement pair: role alert + liveRegion assertive)"
918
+ },
919
+ {
920
+ "id": "a11y-button-named",
921
+ "kind": "node-round-trip",
922
+ "decoder": "node",
923
+ "inputFile": "nodes/a11y-button-named.json",
924
+ "expectedFile": "nodes/a11y-button-named.json",
925
+ "description": "Accessibility (Phase 955 — Button: an explicit named role on a kind that already has a native one)"
926
+ },
927
+ {
928
+ "id": "a11y-image-decorative",
929
+ "kind": "node-round-trip",
930
+ "decoder": "node",
931
+ "inputFile": "nodes/a11y-image-decorative.json",
932
+ "expectedFile": "nodes/a11y-image-decorative.json",
933
+ "description": "Accessibility (Phase 955 — Image: the decorative shape, empty alt + hidden Static true)"
934
+ },
935
+ {
936
+ "id": "a11y-link-labelled",
937
+ "kind": "node-round-trip",
938
+ "decoder": "node",
939
+ "inputFile": "nodes/a11y-link-labelled.json",
940
+ "expectedFile": "nodes/a11y-link-labelled.json",
941
+ "description": "Accessibility (Phase 955 — Link: the accessible name overriding the visible 'Read more' text)"
942
+ },
943
+ {
944
+ "id": "a11y-wrapper-all-slots",
945
+ "kind": "node-round-trip",
946
+ "decoder": "node",
947
+ "inputFile": "nodes/a11y-wrapper-all-slots.json",
948
+ "expectedFile": "nodes/a11y-wrapper-all-slots.json",
949
+ "description": "Accessibility (Phase 955 — all six trait slots at once on a wrapper; Static label, named role, polite)"
950
+ },
951
+ {
952
+ "id": "a11y-wrapper-state-bound",
953
+ "kind": "node-round-trip",
954
+ "decoder": "node",
955
+ "inputFile": "nodes/a11y-wrapper-state-bound.json",
956
+ "expectedFile": "nodes/a11y-wrapper-state-bound.json",
957
+ "description": "Accessibility (Phase 955 — State-bound label + hidden, mixed-case custom role doc-pageFooter, off)"
958
+ },
870
959
  {
871
960
  "id": "badge-1",
872
961
  "kind": "node-round-trip",
@@ -1115,6 +1204,14 @@
1115
1204
  "expectedFile": "nodes/drawing-empty.json",
1116
1205
  "description": "Display/Drawing (degenerate — empty)"
1117
1206
  },
1207
+ {
1208
+ "id": "drawing-nonfinite-sentinels",
1209
+ "kind": "node-round-trip",
1210
+ "decoder": "node",
1211
+ "inputFile": "nodes/drawing-nonfinite-sentinels.json",
1212
+ "expectedFile": "nodes/drawing-nonfinite-sentinels.json",
1213
+ "description": "Display/Drawing (Phase 1063 — §5/§7 non-finite sentinels at typed float scalars and a nested shape coordinate)"
1214
+ },
1118
1215
  {
1119
1216
  "id": "drawing-rotated-labels",
1120
1217
  "kind": "node-round-trip",
@@ -1227,6 +1324,14 @@
1227
1324
  "expectedFile": "nodes/form-declarative-minimal.json",
1228
1325
  "description": "Input/Form (Phase 596 — symmetric auto-bind, omitted-value fields)"
1229
1326
  },
1327
+ {
1328
+ "id": "form-field-rules",
1329
+ "kind": "node-round-trip",
1330
+ "decoder": "node",
1331
+ "inputFile": "nodes/form-field-rules.json",
1332
+ "expectedFile": "nodes/form-field-rules.json",
1333
+ "description": "Input/Form (Phase 864 — declared field rules: format / pattern / length pair / cross-field compare)"
1334
+ },
1230
1335
  {
1231
1336
  "id": "form-local-1",
1232
1337
  "kind": "node-round-trip",
@@ -1307,6 +1412,134 @@
1307
1412
  "expectedFile": "nodes/frag-ref-args.json",
1308
1413
  "description": "FragmentRef (parameterised — value + slot args)"
1309
1414
  },
1415
+ {
1416
+ "id": "frag-stdlib-confirm-action-pair",
1417
+ "kind": "node-round-trip",
1418
+ "decoder": "node",
1419
+ "inputFile": "nodes/frag-stdlib-confirm-action-pair.json",
1420
+ "expectedFile": "nodes/frag-stdlib-confirm-action-pair.json",
1421
+ "description": "FragmentDecl (stdlib 'confirm-action-pair' — A primary confirm and a secondary cancel, both writing one declared state key.)"
1422
+ },
1423
+ {
1424
+ "id": "frag-stdlib-confirm-action-pair-ref",
1425
+ "kind": "node-round-trip",
1426
+ "decoder": "node",
1427
+ "inputFile": "nodes/frag-stdlib-confirm-action-pair-ref.json",
1428
+ "expectedFile": "nodes/frag-stdlib-confirm-action-pair-ref.json",
1429
+ "description": "FragmentRef (stdlib 'confirm-action-pair' — a representative application)"
1430
+ },
1431
+ {
1432
+ "id": "frag-stdlib-empty-state-panel",
1433
+ "kind": "node-round-trip",
1434
+ "decoder": "node",
1435
+ "inputFile": "nodes/frag-stdlib-empty-state-panel.json",
1436
+ "expectedFile": "nodes/frag-stdlib-empty-state-panel.json",
1437
+ "description": "FragmentDecl (stdlib 'empty-state-panel' — A titled nothing-to-show card whose call to action is a caller-supplied Button slot.)"
1438
+ },
1439
+ {
1440
+ "id": "frag-stdlib-empty-state-panel-ref",
1441
+ "kind": "node-round-trip",
1442
+ "decoder": "node",
1443
+ "inputFile": "nodes/frag-stdlib-empty-state-panel-ref.json",
1444
+ "expectedFile": "nodes/frag-stdlib-empty-state-panel-ref.json",
1445
+ "description": "FragmentRef (stdlib 'empty-state-panel' — a representative application)"
1446
+ },
1447
+ {
1448
+ "id": "frag-stdlib-filter-bar",
1449
+ "kind": "node-round-trip",
1450
+ "decoder": "node",
1451
+ "inputFile": "nodes/frag-stdlib-filter-bar.json",
1452
+ "expectedFile": "nodes/frag-stdlib-filter-bar.json",
1453
+ "description": "FragmentDecl (stdlib 'filter-bar' — A free-text chip and a status chip, both bound to their own host filter keys.)"
1454
+ },
1455
+ {
1456
+ "id": "frag-stdlib-filter-bar-ref",
1457
+ "kind": "node-round-trip",
1458
+ "decoder": "node",
1459
+ "inputFile": "nodes/frag-stdlib-filter-bar-ref.json",
1460
+ "expectedFile": "nodes/frag-stdlib-filter-bar-ref.json",
1461
+ "description": "FragmentRef (stdlib 'filter-bar' — a representative application)"
1462
+ },
1463
+ {
1464
+ "id": "frag-stdlib-kpi-strip",
1465
+ "kind": "node-round-trip",
1466
+ "decoder": "node",
1467
+ "inputFile": "nodes/frag-stdlib-kpi-strip.json",
1468
+ "expectedFile": "nodes/frag-stdlib-kpi-strip.json",
1469
+ "description": "FragmentDecl (stdlib 'kpi-strip' — A headed horizontal strip of one to six figures — the dashboard's top band.)"
1470
+ },
1471
+ {
1472
+ "id": "frag-stdlib-kpi-strip-ref",
1473
+ "kind": "node-round-trip",
1474
+ "decoder": "node",
1475
+ "inputFile": "nodes/frag-stdlib-kpi-strip-ref.json",
1476
+ "expectedFile": "nodes/frag-stdlib-kpi-strip-ref.json",
1477
+ "description": "FragmentRef (stdlib 'kpi-strip' — a representative application)"
1478
+ },
1479
+ {
1480
+ "id": "frag-stdlib-labelled-metric-row",
1481
+ "kind": "node-round-trip",
1482
+ "decoder": "node",
1483
+ "inputFile": "nodes/frag-stdlib-labelled-metric-row.json",
1484
+ "expectedFile": "nodes/frag-stdlib-labelled-metric-row.json",
1485
+ "description": "FragmentDecl (stdlib 'labelled-metric-row' — A label and its figure on one line — the smallest read-only composite in the set.)"
1486
+ },
1487
+ {
1488
+ "id": "frag-stdlib-labelled-metric-row-ref",
1489
+ "kind": "node-round-trip",
1490
+ "decoder": "node",
1491
+ "inputFile": "nodes/frag-stdlib-labelled-metric-row-ref.json",
1492
+ "expectedFile": "nodes/frag-stdlib-labelled-metric-row-ref.json",
1493
+ "description": "FragmentRef (stdlib 'labelled-metric-row' — a representative application)"
1494
+ },
1495
+ {
1496
+ "id": "frag-stdlib-loading-placeholder",
1497
+ "kind": "node-round-trip",
1498
+ "decoder": "node",
1499
+ "inputFile": "nodes/frag-stdlib-loading-placeholder.json",
1500
+ "expectedFile": "nodes/frag-stdlib-loading-placeholder.json",
1501
+ "description": "FragmentDecl (stdlib 'loading-placeholder' — One to eight skeleton rows — the shape a list takes while its query is in flight.)"
1502
+ },
1503
+ {
1504
+ "id": "frag-stdlib-loading-placeholder-ref",
1505
+ "kind": "node-round-trip",
1506
+ "decoder": "node",
1507
+ "inputFile": "nodes/frag-stdlib-loading-placeholder-ref.json",
1508
+ "expectedFile": "nodes/frag-stdlib-loading-placeholder-ref.json",
1509
+ "description": "FragmentRef (stdlib 'loading-placeholder' — a representative application)"
1510
+ },
1511
+ {
1512
+ "id": "frag-stdlib-metric-card",
1513
+ "kind": "node-round-trip",
1514
+ "decoder": "node",
1515
+ "inputFile": "nodes/frag-stdlib-metric-card.json",
1516
+ "expectedFile": "nodes/frag-stdlib-metric-card.json",
1517
+ "description": "FragmentDecl (stdlib 'metric-card' — A card carrying one headline figure and the caption that qualifies it.)"
1518
+ },
1519
+ {
1520
+ "id": "frag-stdlib-metric-card-ref",
1521
+ "kind": "node-round-trip",
1522
+ "decoder": "node",
1523
+ "inputFile": "nodes/frag-stdlib-metric-card-ref.json",
1524
+ "expectedFile": "nodes/frag-stdlib-metric-card-ref.json",
1525
+ "description": "FragmentRef (stdlib 'metric-card' — a representative application)"
1526
+ },
1527
+ {
1528
+ "id": "frag-stdlib-section-header",
1529
+ "kind": "node-round-trip",
1530
+ "decoder": "node",
1531
+ "inputFile": "nodes/frag-stdlib-section-header.json",
1532
+ "expectedFile": "nodes/frag-stdlib-section-header.json",
1533
+ "description": "FragmentDecl (stdlib 'section-header' — An eyebrow above a title, at a caller-chosen heading level.)"
1534
+ },
1535
+ {
1536
+ "id": "frag-stdlib-section-header-ref",
1537
+ "kind": "node-round-trip",
1538
+ "decoder": "node",
1539
+ "inputFile": "nodes/frag-stdlib-section-header-ref.json",
1540
+ "expectedFile": "nodes/frag-stdlib-section-header-ref.json",
1541
+ "description": "FragmentRef (stdlib 'section-header' — a representative application)"
1542
+ },
1310
1543
  {
1311
1544
  "id": "glayout-1",
1312
1545
  "kind": "node-round-trip",
@@ -1395,6 +1628,14 @@
1395
1628
  "expectedFile": "nodes/grid-paged-sorted.json",
1396
1629
  "description": "Visualisation/Grid (Phase 862 — paging and sorting composed: two behaviours, two state keys, one rule)"
1397
1630
  },
1631
+ {
1632
+ "id": "grid-reorderable",
1633
+ "kind": "node-round-trip",
1634
+ "decoder": "node",
1635
+ "inputFile": "nodes/grid-reorderable.json",
1636
+ "expectedFile": "nodes/grid-reorderable.json",
1637
+ "description": "Visualisation/Grid (Phase 934 — declarative row reorder: omit-when-false flag; edits and reorders share one destination)"
1638
+ },
1398
1639
  {
1399
1640
  "id": "grid-sort-state-key",
1400
1641
  "kind": "node-round-trip",
@@ -1451,6 +1692,62 @@
1451
1692
  "expectedFile": "nodes/image-1.json",
1452
1693
  "description": "Display/Image (Avatar variant)"
1453
1694
  },
1695
+ {
1696
+ "id": "image-caption-1",
1697
+ "kind": "node-round-trip",
1698
+ "decoder": "node",
1699
+ "inputFile": "nodes/image-caption-1.json",
1700
+ "expectedFile": "nodes/image-caption-1.json",
1701
+ "description": "Display/Image (Phase 1078 — literal caption)"
1702
+ },
1703
+ {
1704
+ "id": "image-caption-i18n-1",
1705
+ "kind": "node-round-trip",
1706
+ "decoder": "node",
1707
+ "inputFile": "nodes/image-caption-i18n-1.json",
1708
+ "expectedFile": "nodes/image-caption-i18n-1.json",
1709
+ "description": "Display/Image (Phase 1078 — i18n caption with args)"
1710
+ },
1711
+ {
1712
+ "id": "image-expandable-1",
1713
+ "kind": "node-round-trip",
1714
+ "decoder": "node",
1715
+ "inputFile": "nodes/image-expandable-1.json",
1716
+ "expectedFile": "nodes/image-expandable-1.json",
1717
+ "description": "Display/Image (Phase 1079 — expandable, the declaration alone)"
1718
+ },
1719
+ {
1720
+ "id": "image-expandable-figure-1",
1721
+ "kind": "node-round-trip",
1722
+ "decoder": "node",
1723
+ "inputFile": "nodes/image-expandable-figure-1.json",
1724
+ "expectedFile": "nodes/image-expandable-figure-1.json",
1725
+ "description": "Display/Image (Phase 1079 — expandable + caption + srcSet, the gallery thumbnail)"
1726
+ },
1727
+ {
1728
+ "id": "image-presentation-1",
1729
+ "kind": "node-round-trip",
1730
+ "decoder": "node",
1731
+ "inputFile": "nodes/image-presentation-1.json",
1732
+ "expectedFile": "nodes/image-presentation-1.json",
1733
+ "description": "Display/Image (Phase 1077 — fit / aspectRatio / loading all off-default)"
1734
+ },
1735
+ {
1736
+ "id": "image-srcset-1",
1737
+ "kind": "node-round-trip",
1738
+ "decoder": "node",
1739
+ "inputFile": "nodes/image-srcset-1.json",
1740
+ "expectedFile": "nodes/image-srcset-1.json",
1741
+ "description": "Display/Image (Phase 1080 — three-candidate srcSet, authored descending)"
1742
+ },
1743
+ {
1744
+ "id": "limit-node-depth-at-max",
1745
+ "kind": "node-round-trip",
1746
+ "decoder": "node",
1747
+ "inputFile": "nodes/limit-node-depth-at-max.json",
1748
+ "expectedFile": "nodes/limit-node-depth-at-max.json",
1749
+ "description": "§21 max node depth — a tree at EXACTLY the limit (24 levels). Rule 1: every conformant host MUST decode this; refusing it is non-conformance, not conservatism"
1750
+ },
1454
1751
  {
1455
1752
  "id": "link-1",
1456
1753
  "kind": "node-round-trip",
@@ -1499,6 +1796,22 @@
1499
1796
  "expectedFile": "nodes/markdown-1.json",
1500
1797
  "description": "Display/Markdown"
1501
1798
  },
1799
+ {
1800
+ "id": "masonry-1",
1801
+ "kind": "node-round-trip",
1802
+ "decoder": "node",
1803
+ "inputFile": "nodes/masonry-1.json",
1804
+ "expectedFile": "nodes/masonry-1.json",
1805
+ "description": "Layout/Masonry (Phase 1082 — column-fill, three columns)"
1806
+ },
1807
+ {
1808
+ "id": "masonry-gap",
1809
+ "kind": "node-round-trip",
1810
+ "decoder": "node",
1811
+ "inputFile": "nodes/masonry-gap.json",
1812
+ "expectedFile": "nodes/masonry-gap.json",
1813
+ "description": "Layout/Masonry (Phase 1082 — explicit gap)"
1814
+ },
1502
1815
  {
1503
1816
  "id": "master-detail-multi-field",
1504
1817
  "kind": "node-round-trip",
@@ -1531,6 +1844,38 @@
1531
1844
  "expectedFile": "nodes/math-1.json",
1532
1845
  "description": "Display/Math (block LaTeX)"
1533
1846
  },
1847
+ {
1848
+ "id": "media-audio-1",
1849
+ "kind": "node-round-trip",
1850
+ "decoder": "node",
1851
+ "inputFile": "nodes/media-audio-1.json",
1852
+ "expectedFile": "nodes/media-audio-1.json",
1853
+ "description": "Display/Media (Phase 1076 — the Audio variant, whose payload is the discriminator alone)"
1854
+ },
1855
+ {
1856
+ "id": "media-video-1",
1857
+ "kind": "node-round-trip",
1858
+ "decoder": "node",
1859
+ "inputFile": "nodes/media-video-1.json",
1860
+ "expectedFile": "nodes/media-video-1.json",
1861
+ "description": "Display/Media (Phase 1076 — video at its minimum, both bool defaults omitted)"
1862
+ },
1863
+ {
1864
+ "id": "media-video-autoplay-1",
1865
+ "kind": "node-round-trip",
1866
+ "decoder": "node",
1867
+ "inputFile": "nodes/media-video-autoplay-1.json",
1868
+ "expectedFile": "nodes/media-video-autoplay-1.json",
1869
+ "description": "Display/Media (Phase 1076 — autoplay declared, both shared bools off-default)"
1870
+ },
1871
+ {
1872
+ "id": "media-video-poster-1",
1873
+ "kind": "node-round-trip",
1874
+ "decoder": "node",
1875
+ "inputFile": "nodes/media-video-poster-1.json",
1876
+ "expectedFile": "nodes/media-video-poster-1.json",
1877
+ "description": "Display/Media (Phase 1076 — video with a poster frame in the case payload)"
1878
+ },
1534
1879
  {
1535
1880
  "id": "metric-1",
1536
1881
  "kind": "node-round-trip",
@@ -1579,6 +1924,14 @@
1579
1924
  "expectedFile": "nodes/metric-float-exp-pos.json",
1580
1925
  "description": "Display/Metric (float divergence-zone — 1e21 scientific)"
1581
1926
  },
1927
+ {
1928
+ "id": "metric-inverted-polarity",
1929
+ "kind": "node-round-trip",
1930
+ "decoder": "node",
1931
+ "inputFile": "nodes/metric-inverted-polarity.json",
1932
+ "expectedFile": "nodes/metric-inverted-polarity.json",
1933
+ "description": "Display/Metric (Phase 867 — inverted trendPolarity: a falling wait time is an improvement)"
1934
+ },
1582
1935
  {
1583
1936
  "id": "metric-invoke",
1584
1937
  "kind": "node-round-trip",
@@ -1587,6 +1940,14 @@
1587
1940
  "expectedFile": "nodes/metric-invoke.json",
1588
1941
  "description": "Display/Metric (Phase 283 — Binding.Invoke capability source)"
1589
1942
  },
1943
+ {
1944
+ "id": "metric-nonfinite-sentinel",
1945
+ "kind": "node-round-trip",
1946
+ "decoder": "node",
1947
+ "inputFile": "nodes/metric-nonfinite-sentinel.json",
1948
+ "expectedFile": "nodes/metric-nonfinite-sentinel.json",
1949
+ "description": "Display/Metric (Phase 1063 — §5/§7 non-finite sentinels behind a Binding.Static envelope)"
1950
+ },
1590
1951
  {
1591
1952
  "id": "modal-1",
1592
1953
  "kind": "node-round-trip",
@@ -1667,6 +2028,14 @@
1667
2028
  "expectedFile": "nodes/select-1.json",
1668
2029
  "description": "Input/Select"
1669
2030
  },
2031
+ {
2032
+ "id": "shared-source-seeded-pair",
2033
+ "kind": "node-round-trip",
2034
+ "decoder": "node",
2035
+ "inputFile": "nodes/shared-source-seeded-pair.json",
2036
+ "expectedFile": "nodes/shared-source-seeded-pair.json",
2037
+ "description": "Visualisation/Grid + Display/Badge (Phase 1075 — the seeded shared source: one declared table, two readers)"
2038
+ },
1670
2039
  {
1671
2040
  "id": "skel-1",
1672
2041
  "kind": "node-round-trip",
@@ -1683,6 +2052,14 @@
1683
2052
  "expectedFile": "nodes/spark-1.json",
1684
2053
  "description": "Display/Sparkline"
1685
2054
  },
2055
+ {
2056
+ "id": "spark-nonfinite-sentinel",
2057
+ "kind": "node-round-trip",
2058
+ "decoder": "node",
2059
+ "inputFile": "nodes/spark-nonfinite-sentinel.json",
2060
+ "expectedFile": "nodes/spark-nonfinite-sentinel.json",
2061
+ "description": "Display/Sparkline (Phase 1063 — §5/§7 non-finite sentinels as float SEQUENCE elements)"
2062
+ },
1686
2063
  {
1687
2064
  "id": "split-1",
1688
2065
  "kind": "node-round-trip",
@@ -1963,6 +2340,114 @@
1963
2340
  "expectedFile": "ops/op-updatestyle (phase 147 role=eyebrow + voice=structural).json",
1964
2341
  "description": "UpdateStyle (Phase 147 Role=Eyebrow + Voice=Structural)"
1965
2342
  },
2343
+ {
2344
+ "id": "reject-a11y-hidden-nonbool",
2345
+ "kind": "reject",
2346
+ "decoder": "node",
2347
+ "inputFile": "reject/reject-a11y-hidden-nonbool.json",
2348
+ "expectedErrorCode": "WRONG_TYPE",
2349
+ "expectedPath": "$.accessibility.hidden",
2350
+ "description": "accessibility.hidden bare scalar of the wrong type — §3.6 leniency is about SHAPE, not type (Phase 955)"
2351
+ },
2352
+ {
2353
+ "id": "reject-a11y-labelledby-nonstring",
2354
+ "kind": "reject",
2355
+ "decoder": "node",
2356
+ "inputFile": "reject/reject-a11y-labelledby-nonstring.json",
2357
+ "expectedErrorCode": "WRONG_TYPE",
2358
+ "expectedPath": "$.accessibility.labelledBy",
2359
+ "description": "accessibility.labelledBy not a NodeId string (Phase 955)"
2360
+ },
2361
+ {
2362
+ "id": "reject-a11y-liveregion-nonstring",
2363
+ "kind": "reject",
2364
+ "decoder": "node",
2365
+ "inputFile": "reject/reject-a11y-liveregion-nonstring.json",
2366
+ "expectedErrorCode": "WRONG_TYPE",
2367
+ "expectedPath": "$.accessibility.liveRegion",
2368
+ "description": "accessibility.liveRegion not a JSON string (Phase 955)"
2369
+ },
2370
+ {
2371
+ "id": "reject-a11y-liveregion-unknown",
2372
+ "kind": "reject",
2373
+ "decoder": "node",
2374
+ "inputFile": "reject/reject-a11y-liveregion-unknown.json",
2375
+ "expectedErrorCode": "UNKNOWN_DU_CASE",
2376
+ "expectedPath": "$.accessibility.liveRegion",
2377
+ "description": "accessibility.liveRegion outside the closed set — the message names polite | assertive | off (Phase 955)"
2378
+ },
2379
+ {
2380
+ "id": "reject-a11y-not-object",
2381
+ "kind": "reject",
2382
+ "decoder": "node",
2383
+ "inputFile": "reject/reject-a11y-not-object.json",
2384
+ "expectedErrorCode": "WRONG_TYPE",
2385
+ "expectedPath": "$.accessibility",
2386
+ "description": "accessibility is not an object — the trait is a record, not a bare label (Phase 955)"
2387
+ },
2388
+ {
2389
+ "id": "reject-a11y-role-nonstring",
2390
+ "kind": "reject",
2391
+ "decoder": "node",
2392
+ "inputFile": "reject/reject-a11y-role-nonstring.json",
2393
+ "expectedErrorCode": "WRONG_TYPE",
2394
+ "expectedPath": "$.accessibility.role",
2395
+ "description": "accessibility.role not a JSON string — the slot is open to any role NAME, not to any value (Phase 955)"
2396
+ },
2397
+ {
2398
+ "id": "reject-binding-float-bool",
2399
+ "kind": "reject",
2400
+ "decoder": "node",
2401
+ "inputFile": "reject/reject-binding-float-bool.json",
2402
+ "expectedErrorCode": "WRONG_TYPE",
2403
+ "expectedPath": "$.kind.trend",
2404
+ "description": "Binding<float> Static payload is a JSON boolean — a second float slot on the same spec, so a host guarding `value` and not `trend` is caught (Phase 1064)"
2405
+ },
2406
+ {
2407
+ "id": "reject-binding-float-sentinel-case",
2408
+ "kind": "reject",
2409
+ "decoder": "node",
2410
+ "inputFile": "reject/reject-binding-float-sentinel-case.json",
2411
+ "expectedErrorCode": "WRONG_TYPE",
2412
+ "expectedPath": "$.kind.fraction",
2413
+ "description": "Binding<float> bare scalar is a MIS-CASED sentinel (\"nan\") — §7's three tokens are exact, and a case-folding host would accept a document no encoder emits (Phase 1064)"
2414
+ },
2415
+ {
2416
+ "id": "reject-binding-float-string",
2417
+ "kind": "reject",
2418
+ "decoder": "node",
2419
+ "inputFile": "reject/reject-binding-float-string.json",
2420
+ "expectedErrorCode": "WRONG_TYPE",
2421
+ "expectedPath": "$.kind.value",
2422
+ "description": "Binding<float> Static payload is a non-sentinel string — §7's float accept set is JSON number plus exactly \"NaN\" / \"Infinity\" / \"-Infinity\" (Phase 1064)"
2423
+ },
2424
+ {
2425
+ "id": "reject-binding-int-bool",
2426
+ "kind": "reject",
2427
+ "decoder": "node",
2428
+ "inputFile": "reject/reject-binding-int-bool.json",
2429
+ "expectedErrorCode": "WRONG_TYPE",
2430
+ "expectedPath": "$.kind.activeIndex",
2431
+ "description": "Binding<int> Static payload is a JSON boolean — §7 gives an integer slot no non-numeric form (Phase 1064)"
2432
+ },
2433
+ {
2434
+ "id": "reject-binding-int-sentinel-string",
2435
+ "kind": "reject",
2436
+ "decoder": "node",
2437
+ "inputFile": "reject/reject-binding-int-sentinel-string.json",
2438
+ "expectedErrorCode": "WRONG_TYPE",
2439
+ "expectedPath": "$.kind.activeStep",
2440
+ "description": "Binding<int> Static payload is a §7 non-finite sentinel — the sentinels are FLOAT-slot only; an integer slot has no non-finite form (Phase 1064)"
2441
+ },
2442
+ {
2443
+ "id": "reject-box-masonry-nonpositive-cols",
2444
+ "kind": "reject",
2445
+ "decoder": "node",
2446
+ "inputFile": "reject/reject-box-masonry-nonpositive-cols.json",
2447
+ "expectedErrorCode": "WRONG_TYPE",
2448
+ "expectedPath": "$.kind.layout.cols",
2449
+ "description": "Masonry cols 0 — refused: a column count names how many columns the children fill, so a non-positive one describes a layout no renderer can realise. Refused rather than canonicalised to `Auto`, unlike a column-less `Grid`: that leniency works because `Auto` already means the browser's own choice, and masonry has no such case for the rewrite to land on"
2450
+ },
1966
2451
  {
1967
2452
  "id": "reject-daterange-unordered",
1968
2453
  "kind": "reject",
@@ -1981,6 +2466,60 @@
1981
2466
  "expectedPath": "$.id",
1982
2467
  "description": "Empty top-level id"
1983
2468
  },
2469
+ {
2470
+ "id": "reject-fieldrule-empty",
2471
+ "kind": "reject",
2472
+ "decoder": "node",
2473
+ "inputFile": "reject/reject-fieldrule-empty.json",
2474
+ "expectedErrorCode": "WRONG_TYPE",
2475
+ "expectedPath": "$.kind.fields[0].rule",
2476
+ "description": "FieldRule declaring no constraint — a message alone is not a rule (Phase 864)"
2477
+ },
2478
+ {
2479
+ "id": "reject-fieldrule-length-unordered",
2480
+ "kind": "reject",
2481
+ "decoder": "node",
2482
+ "inputFile": "reject/reject-fieldrule-length-unordered.json",
2483
+ "expectedErrorCode": "WRONG_TYPE",
2484
+ "expectedPath": "$.kind.fields[0].rule",
2485
+ "description": "FieldRule minLength above maxLength — the ordered-pair rule on a length pair (Phase 864)"
2486
+ },
2487
+ {
2488
+ "id": "reject-formfield-near-miss-validation",
2489
+ "kind": "reject",
2490
+ "decoder": "node",
2491
+ "inputFile": "reject/reject-formfield-near-miss-validation.json",
2492
+ "expectedErrorCode": "WRONG_TYPE",
2493
+ "expectedPath": "$.kind.fields[0].validation",
2494
+ "description": "'validation' near-miss on a FormField — the canonical key is 'rule' (Phase 864)"
2495
+ },
2496
+ {
2497
+ "id": "reject-image-expandable-nonbool",
2498
+ "kind": "reject",
2499
+ "decoder": "node",
2500
+ "inputFile": "reject/reject-image-expandable-nonbool.json",
2501
+ "expectedErrorCode": "WRONG_TYPE",
2502
+ "expectedPath": "$.kind.expandable",
2503
+ "description": "Image expandable `\"true\"` — the stringified boolean, refused rather than coerced. A truthiness rule would have to rule on `\"false\"` and `\"\"` too, and two hosts ruling differently would disagree about whether the document declares an affordance at all"
2504
+ },
2505
+ {
2506
+ "id": "reject-image-srcset-nonpositive-width",
2507
+ "kind": "reject",
2508
+ "decoder": "node",
2509
+ "inputFile": "reject/reject-image-srcset-nonpositive-width.json",
2510
+ "expectedErrorCode": "WRONG_TYPE",
2511
+ "expectedPath": "$.kind.srcSet[1].width",
2512
+ "description": "SrcSetEntry width 0 — refused: a `w` descriptor names the intrinsic pixel width a browser selects on, so a non-positive value describes a candidate that can never be selected. The first entry is well-formed on purpose, so the path has to identify the second"
2513
+ },
2514
+ {
2515
+ "id": "reject-image-srcset-null",
2516
+ "kind": "reject",
2517
+ "decoder": "node",
2518
+ "inputFile": "reject/reject-image-srcset-null.json",
2519
+ "expectedErrorCode": "WRONG_TYPE",
2520
+ "expectedPath": "$.kind.srcSet",
2521
+ "description": "Image srcSet `null` — refused. Absent means the EMPTY LIST (the missing-list-field decode class); a present null is a second spelling for an absence that already has one, and admitting it would let two hosts disagree about the canonical bytes of the same document"
2522
+ },
1984
2523
  {
1985
2524
  "id": "reject-invalid-empty",
1986
2525
  "kind": "reject",
@@ -2008,6 +2547,60 @@
2008
2547
  "expectedPath": "$",
2009
2548
  "description": "truncated object"
2010
2549
  },
2550
+ {
2551
+ "id": "reject-limit-json-depth",
2552
+ "kind": "reject",
2553
+ "decoder": "node",
2554
+ "inputFile": "reject/reject-limit-json-depth.json",
2555
+ "expectedErrorCode": "LIMIT_EXCEEDED",
2556
+ "expectedPath": "$",
2557
+ "description": "§21 max JSON depth — 257 levels of bare array nesting, ONE past the limit. Well-formed and merely too deep, so LIMIT_EXCEEDED rather than INVALID_JSON (rule 2). Pins the boundary exactly; its at-the-limit twin below pins the other side"
2558
+ },
2559
+ {
2560
+ "id": "reject-limit-json-depth-at-max",
2561
+ "kind": "reject",
2562
+ "decoder": "node",
2563
+ "inputFile": "reject/reject-limit-json-depth-at-max.json",
2564
+ "expectedErrorCode": "WRONG_TYPE",
2565
+ "expectedPath": "$",
2566
+ "description": "§21 max JSON depth — 256 levels, EXACTLY at the limit. Not a valid node, so it must fail; the point is that it fails on SHAPE and not as a limit breach. Rule 1 in the one form the reject machinery can express for a syntactic bound: a host whose guard sits one level too tight answers LIMIT_EXCEEDED here and fails"
2567
+ },
2568
+ {
2569
+ "id": "reject-limit-node-depth",
2570
+ "kind": "reject",
2571
+ "decoder": "node",
2572
+ "inputFile": "reject/reject-limit-node-depth.json",
2573
+ "expectedErrorCode": "LIMIT_EXCEEDED",
2574
+ "expectedPath": "$",
2575
+ "description": "§21 max node depth — one level past the limit (25). Refused with LIMIT_EXCEEDED, never INVALID_JSON (rule 2) and never a throw (rule 3)"
2576
+ },
2577
+ {
2578
+ "id": "reject-limit-op-depth",
2579
+ "kind": "reject",
2580
+ "decoder": "op",
2581
+ "inputFile": "reject/reject-limit-op-depth.json",
2582
+ "expectedErrorCode": "LIMIT_EXCEEDED",
2583
+ "expectedPath": "$",
2584
+ "description": "§21.5 the op axis — 25 nested TreeOp.Batch. Counted separately from the node axis; the syntactic bound only LOOKS like cover for it"
2585
+ },
2586
+ {
2587
+ "id": "reject-media-autoplay-nonbool",
2588
+ "kind": "reject",
2589
+ "decoder": "node",
2590
+ "inputFile": "reject/reject-media-autoplay-nonbool.json",
2591
+ "expectedErrorCode": "WRONG_TYPE",
2592
+ "expectedPath": "$.kind.kind.autoplay",
2593
+ "description": "Media autoplay `\"true\"` — the stringified boolean, refused rather than coerced. A truthiness rule would have to rule on `\"false\"` and `\"\"` too, and two hosts ruling differently would disagree about whether a page starts playing by itself"
2594
+ },
2595
+ {
2596
+ "id": "reject-media-missing-label",
2597
+ "kind": "reject",
2598
+ "decoder": "node",
2599
+ "inputFile": "reject/reject-media-missing-label.json",
2600
+ "expectedErrorCode": "MISSING_FIELD",
2601
+ "expectedPath": "$.kind.label",
2602
+ "description": "Media with no `label` — refused. The accessible name is mandatory because a transport has no decorative case, and there is no value to default to that would not be a fabricated name for someone else's recording"
2603
+ },
2011
2604
  {
2012
2605
  "id": "reject-missing-binding-type",
2013
2606
  "kind": "reject",
@@ -2098,6 +2691,42 @@
2098
2691
  "expectedPath": "$.kind.stateKey",
2099
2692
  "description": "Switch missing stateKey"
2100
2693
  },
2694
+ {
2695
+ "id": "reject-nearmiss-a11y-aria-hidden",
2696
+ "kind": "reject",
2697
+ "decoder": "node",
2698
+ "inputFile": "reject/reject-nearmiss-a11y-aria-hidden.json",
2699
+ "expectedErrorCode": "WRONG_TYPE",
2700
+ "expectedPath": "$.accessibility.aria-hidden",
2701
+ "description": "accessibility 'aria-hidden' — the ARIA ATTRIBUTE name where the wire wants the slot name 'hidden'; the projection slot two hosts once dropped entirely (Phase 959)"
2702
+ },
2703
+ {
2704
+ "id": "reject-nearmiss-a11y-arialabel",
2705
+ "kind": "reject",
2706
+ "decoder": "node",
2707
+ "inputFile": "reject/reject-nearmiss-a11y-arialabel.json",
2708
+ "expectedErrorCode": "WRONG_TYPE",
2709
+ "expectedPath": "$.accessibility.ariaLabel",
2710
+ "description": "accessibility 'ariaLabel' — the camelCase JSX prior for the accessible name; MEASURED in the emission corpora, and the whole declaration vanished (Phase 959)"
2711
+ },
2712
+ {
2713
+ "id": "reject-nearmiss-a11y-live",
2714
+ "kind": "reject",
2715
+ "decoder": "node",
2716
+ "inputFile": "reject/reject-nearmiss-a11y-live.json",
2717
+ "expectedErrorCode": "WRONG_TYPE",
2718
+ "expectedPath": "$.accessibility.live",
2719
+ "description": "accessibility 'live' — the sharpest of the set: six measured emissions against liveRegion's twelve, and the HTML idiom it comes from also spells a BOOLEAN, so it is not a safe alias (Phase 959)"
2720
+ },
2721
+ {
2722
+ "id": "reject-nearmiss-a11y-liveregion-case",
2723
+ "kind": "reject",
2724
+ "decoder": "node",
2725
+ "inputFile": "reject/reject-nearmiss-a11y-liveregion-case.json",
2726
+ "expectedErrorCode": "WRONG_TYPE",
2727
+ "expectedPath": "$.accessibility.liveregion",
2728
+ "description": "accessibility 'liveregion' — the canonical slot name un-cased to the ARIA attribute spelling; the wire is camelCase (Phase 959)"
2729
+ },
2101
2730
  {
2102
2731
  "id": "reject-nearmiss-column-readonly",
2103
2732
  "kind": "reject",
@@ -2206,6 +2835,15 @@
2206
2835
  "expectedPath": "$.parentId",
2207
2836
  "description": "InsertChild missing parentId"
2208
2837
  },
2838
+ {
2839
+ "id": "reject-op-insertchild-retired-position",
2840
+ "kind": "reject",
2841
+ "decoder": "op",
2842
+ "inputFile": "reject/reject-op-insertchild-retired-position.json",
2843
+ "expectedErrorCode": "WRONG_TYPE",
2844
+ "expectedPath": "$.position",
2845
+ "description": "InsertChild carries the retired 'position' — the migration window is closed (Phase 687)"
2846
+ },
2209
2847
  {
2210
2848
  "id": "reject-op-missing-type",
2211
2849
  "kind": "reject",
@@ -2215,6 +2853,15 @@
2215
2853
  "expectedPath": "$.$type",
2216
2854
  "description": "Op missing $type entirely"
2217
2855
  },
2856
+ {
2857
+ "id": "reject-op-movenode-retired-newposition",
2858
+ "kind": "reject",
2859
+ "decoder": "op",
2860
+ "inputFile": "reject/reject-op-movenode-retired-newposition.json",
2861
+ "expectedErrorCode": "WRONG_TYPE",
2862
+ "expectedPath": "$.newPosition",
2863
+ "description": "MoveNode carries the retired 'newPosition' — the migration window is closed (Phase 687)"
2864
+ },
2218
2865
  {
2219
2866
  "id": "reject-op-reorderchildren-wrongtype",
2220
2867
  "kind": "reject",
@@ -2260,15 +2907,6 @@
2260
2907
  "expectedPath": "$.kind.columns[0].kind.map.Delayed",
2261
2908
  "description": "TonedPill tone-map value outside ToneVariant — the message names the seven legal tones (Phase 750)"
2262
2909
  },
2263
- {
2264
- "id": "reject-transform-source-empty-wrapper",
2265
- "kind": "reject",
2266
- "decoder": "node",
2267
- "inputFile": "reject/reject-transform-source-empty-wrapper.json",
2268
- "expectedErrorCode": "WRONG_TYPE",
2269
- "expectedPath": "$.kind.source.source",
2270
- "description": "fuaran#815 — a State wrapper carrying only a key (no defaultValue/value) is NOT unwrappable: the Transform source stays a binding object and the columnar decode refuses it"
2271
- },
2272
2910
  {
2273
2911
  "id": "reject-unknown-binding",
2274
2912
  "kind": "reject",
@@ -2296,6 +2934,15 @@
2296
2934
  "expectedPath": "$.kind.shapes[0].$type",
2297
2935
  "description": "Drawing Shape $type 'Blob' — the closed-shape default-deny (Phase 524)"
2298
2936
  },
2937
+ {
2938
+ "id": "reject-unknown-image-aspect",
2939
+ "kind": "reject",
2940
+ "decoder": "node",
2941
+ "inputFile": "reject/reject-unknown-image-aspect.json",
2942
+ "expectedErrorCode": "UNKNOWN_DU_CASE",
2943
+ "expectedPath": "$.kind.aspectRatio",
2944
+ "description": "ImageAspect value '16/9' — the CSS ratio spelling, refused: the slot is a closed token vocabulary (Square | FourThree | ThreeTwo | SixteenNine), and admitting a numeric pair would reintroduce the free-form escape the tokens replace"
2945
+ },
2299
2946
  {
2300
2947
  "id": "reject-unknown-link-protection",
2301
2948
  "kind": "reject",
@@ -2305,6 +2952,15 @@
2305
2952
  "expectedPath": "$.kind.protection",
2306
2953
  "description": "LinkProtection value 'rot13' — the closed protection DU default-denies (Phase 812)"
2307
2954
  },
2955
+ {
2956
+ "id": "reject-unknown-media-kind",
2957
+ "kind": "reject",
2958
+ "decoder": "node",
2959
+ "inputFile": "reject/reject-unknown-media-kind.json",
2960
+ "expectedErrorCode": "UNKNOWN_DU_CASE",
2961
+ "expectedPath": "$.kind.kind.$type",
2962
+ "description": "MediaKind case 'Stream' — refused. The variant set is closed at Video | Audio; a third surface is an admission, not a spelling a decoder may guess at. The path carries `.$type` because MediaKind is discriminated, unlike the bare-enum slots"
2963
+ },
2308
2964
  {
2309
2965
  "id": "reject-unknown-mount-direction",
2310
2966
  "kind": "reject",
@@ -2341,6 +2997,15 @@
2341
2997
  "expectedPath": "$.style.tone",
2342
2998
  "description": "ToneVariant value 'Magenta'"
2343
2999
  },
3000
+ {
3001
+ "id": "reject-unknown-trend-polarity",
3002
+ "kind": "reject",
3003
+ "decoder": "node",
3004
+ "inputFile": "reject/reject-unknown-trend-polarity.json",
3005
+ "expectedErrorCode": "UNKNOWN_DU_CASE",
3006
+ "expectedPath": "$.kind.trendPolarity",
3007
+ "description": "TrendPolarity value 'Neutral' — RESERVED, not admitted (§3.6.1 clause 5); refused exactly like a name nobody has proposed, so a later admission is an ADDITION rather than a re-meaning of shipped bytes"
3008
+ },
2344
3009
  {
2345
3010
  "id": "reject-wrongnodekind-sparkler",
2346
3011
  "kind": "reject",