stylegallery 0.1.0 → 0.1.2

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 (230) hide show
  1. package/AGENTS.md +4 -1
  2. package/DOMAINS.md +2 -6
  3. package/GOVERNANCE.md +10 -9
  4. package/README.md +29 -127
  5. package/consumer-reference/agent-native/v2/admission-policy.json +646 -0
  6. package/consumer-reference/agent-native/v2/material-registry.json +1 -0
  7. package/consumer-reference/agent-native/v2/schema/admission-policy.schema.json +50 -0
  8. package/consumer-reference/agent-native/v2/schema/material-manifest.schema.json +17 -0
  9. package/consumer-reference/agent-native/v2/schema/material-record.schema.json +20 -0
  10. package/consumer-reference/contract.md +1 -1
  11. package/consumer-reference/index.md +5 -3
  12. package/layout/index.md +124 -0
  13. package/log.md +57 -0
  14. package/package.json +183 -22
  15. package/scripts/agent-native/v2/experimental-extension-registry.mjs +143 -0
  16. package/scripts/agent-native/v2/extensions/a2a-projection.mjs +264 -0
  17. package/scripts/agent-native/v2/extensions/agui-projection.mjs +191 -0
  18. package/scripts/agent-native/v2/material-admission.mjs +295 -0
  19. package/scripts/agent-native/v2/material-cli-adapter.mjs +134 -0
  20. package/scripts/agent-native/v2/material-context.mjs +251 -0
  21. package/scripts/agent-native/v2/material-identity.mjs +71 -0
  22. package/scripts/agent-native/v2/material-mcp-adapter.mjs +253 -0
  23. package/scripts/agent-native/v2/material-operation-registry.mjs +123 -0
  24. package/scripts/agent-native/v2/material-queries.mjs +398 -0
  25. package/scripts/agent-native/v2/material-registry.mjs +319 -0
  26. package/scripts/sg-material-mcp.mjs +98 -0
  27. package/scripts/sg-material.mjs +27 -0
  28. package/consumer-reference/adapters/style-dictionary.config.mjs +0 -36
  29. package/consumer-reference/baselines/calibration.json +0 -260
  30. package/consumer-reference/baselines/manifest.json +0 -21
  31. package/consumer-reference/fixtures/consumer-conformance/e2e-fixture.mjs +0 -282
  32. package/consumer-reference/fixtures/consumer-conformance/manifest.json +0 -102
  33. package/consumer-reference/fixtures/consumer-conformance/runtime-proof.mjs +0 -32
  34. package/consumer-reference/fixtures/consumer-conformance/valid-migration-result.json +0 -18
  35. package/consumer-reference/fixtures/consumer-conformance/valid-migration.json +0 -142
  36. package/consumer-reference/fixtures/invalid-stable-ended-support.json +0 -19
  37. package/consumer-reference/fixtures/promotion/invalid-blocking-evidence.json +0 -26
  38. package/consumer-reference/fixtures/promotion/invalid-count-only.json +0 -29
  39. package/consumer-reference/fixtures/promotion/invalid-missing-owner-migration.json +0 -25
  40. package/consumer-reference/fixtures/promotion/invalid-normative-bypass-no-regression.json +0 -25
  41. package/consumer-reference/fixtures/promotion/invalid-related-counted-independent.json +0 -45
  42. package/consumer-reference/fixtures/promotion/invalid-stable-ended-support.json +0 -26
  43. package/consumer-reference/fixtures/promotion/manifest.json +0 -15
  44. package/consumer-reference/fixtures/promotion/valid-deferred-example.json +0 -67
  45. package/consumer-reference/fixtures/promotion/valid-normative-bypass.json +0 -67
  46. package/consumer-reference/fixtures/token-portability/invalid-cycle.json +0 -7
  47. package/consumer-reference/fixtures/token-portability/invalid-dangling-alias.json +0 -6
  48. package/consumer-reference/fixtures/token-portability/invalid-extends.json +0 -4
  49. package/consumer-reference/fixtures/token-portability/invalid-json-pointer.json +0 -7
  50. package/consumer-reference/fixtures/token-portability/invalid-resolver.json +0 -9
  51. package/consumer-reference/fixtures/token-portability/invalid-type-mismatch.json +0 -10
  52. package/consumer-reference/fixtures/token-portability/invalid-unknown-reserved.json +0 -6
  53. package/consumer-reference/fixtures/token-portability/invalid-untested-type.json +0 -6
  54. package/consumer-reference/fixtures/token-portability/valid-border.json +0 -14
  55. package/consumer-reference/fixtures/token-portability/valid-reference.json +0 -32
  56. package/consumer-reference/fixtures/token-portability/valid-whole-alias.json +0 -7
  57. package/consumer-reference/fixtures/valid-deprecated.json +0 -21
  58. package/consumer-reference/fixtures/valid-experimental.json +0 -19
  59. package/consumer-reference/generated/manifest.json +0 -40
  60. package/consumer-reference/generated/tokens.css +0 -12
  61. package/consumer-reference/policies/shared-experimental.json +0 -37
  62. package/consumer-reference/schema/ax-evidence.schema.json +0 -30
  63. package/consumer-reference/schema/baseline-manifest.schema.json +0 -55
  64. package/consumer-reference/schema/calibration-record.schema.json +0 -190
  65. package/consumer-reference/schema/capture-session.schema.json +0 -161
  66. package/consumer-reference/schema/component-state.schema.json +0 -150
  67. package/consumer-reference/schema/consumer-conformance-record.schema.json +0 -365
  68. package/consumer-reference/schema/dom-evidence.schema.json +0 -38
  69. package/consumer-reference/schema/evidence-record.schema.json +0 -81
  70. package/consumer-reference/schema/fixture-manifest.schema.json +0 -32
  71. package/consumer-reference/schema/item.schema.json +0 -188
  72. package/consumer-reference/schema/page-evidence-manifest.schema.json +0 -216
  73. package/consumer-reference/schema/page-evidence-session.schema.json +0 -119
  74. package/consumer-reference/schema/portable-tokens.schema.json +0 -104
  75. package/consumer-reference/schema/promotion-rfc.schema.json +0 -219
  76. package/consumer-reference/schema/runtime-evidence-manifest.schema.json +0 -33
  77. package/consumer-reference/schema/visual-evidence.schema.json +0 -31
  78. package/design-engineering/consumer-migration-readiness.md +0 -124
  79. package/design-engineering/interface-craft.md +0 -107
  80. package/design-engineering/reference-profiles/external-adaptation/index.md +0 -12
  81. package/design-engineering/reference-profiles/governed-local/editorial/components/button.component.json +0 -125
  82. package/design-engineering/reference-profiles/governed-local/editorial/evidence/button.evidence.json +0 -3504
  83. package/design-engineering/reference-profiles/governed-local/editorial/fixtures/button.fixture.json +0 -65
  84. package/design-engineering/reference-profiles/governed-local/editorial/generated/evidence-coverage.md +0 -23
  85. package/design-engineering/reference-profiles/governed-local/editorial/generated/keyboard-matrix.md +0 -19
  86. package/design-engineering/reference-profiles/governed-local/editorial/generated/state-matrix.md +0 -21
  87. package/design-engineering/reference-profiles/governed-local/editorial/local-foundations.json +0 -12
  88. package/design-engineering/reference-profiles/governed-local/editorial/states/button.states.json +0 -195
  89. package/design-engineering/reference-profiles/governed-local/editorial/tokens.dtcg.json +0 -17
  90. package/design-engineering/reference-profiles/governed-local/terminal/components/button.component.json +0 -125
  91. package/design-engineering/reference-profiles/governed-local/terminal/evidence/button.evidence.json +0 -3504
  92. package/design-engineering/reference-profiles/governed-local/terminal/fixtures/button.fixture.json +0 -65
  93. package/design-engineering/reference-profiles/governed-local/terminal/generated/evidence-coverage.md +0 -23
  94. package/design-engineering/reference-profiles/governed-local/terminal/generated/keyboard-matrix.md +0 -19
  95. package/design-engineering/reference-profiles/governed-local/terminal/generated/state-matrix.md +0 -21
  96. package/design-engineering/reference-profiles/governed-local/terminal/local-foundations.json +0 -12
  97. package/design-engineering/reference-profiles/governed-local/terminal/profile.json +0 -43
  98. package/design-engineering/reference-profiles/governed-local/terminal/states/button.states.json +0 -195
  99. package/design-engineering/reference-profiles/governed-local/terminal/tokens.dtcg.json +0 -17
  100. package/game-ui/classification.md +0 -82
  101. package/game-ui/reference-record.md +0 -62
  102. package/game-ui/screen-hierarchy.md +0 -107
  103. package/game-ui/unity/architecture.md +0 -166
  104. package/game-ui/unity/cli-loop.md +0 -84
  105. package/game-ui/unity/data/unity-technologies-public-repositories.json +0 -26654
  106. package/game-ui/unity/org-wiki.md +0 -146
  107. package/game-ui/unity/repository-map.md +0 -102
  108. package/game-ui/unity/ui-systems.md +0 -85
  109. package/motion/practice-reference.md +0 -145
  110. package/motion/review-workflow.md +0 -86
  111. package/motion/vocabulary.md +0 -82
  112. package/platform-guides/apple-interaction.md +0 -89
  113. package/scripts/agent-native/events.mjs +0 -113
  114. package/scripts/agent-native/immutable-store.mjs +0 -102
  115. package/scripts/artifact-metadata.mjs +0 -20
  116. package/scripts/audit-evidence-freshness.mjs +0 -308
  117. package/scripts/baseline-contract.mjs +0 -189
  118. package/scripts/baseline-schema-parity.mjs +0 -40
  119. package/scripts/build-reference-artifacts.mjs +0 -127
  120. package/scripts/calibration-raw-contract.mjs +0 -172
  121. package/scripts/capture-session-contract.mjs +0 -187
  122. package/scripts/component-state-artifact-fixture.mjs +0 -44
  123. package/scripts/component-state-contract.mjs +0 -99
  124. package/scripts/component-state-semantics.mjs +0 -128
  125. package/scripts/component-state-workflow-contract.mjs +0 -67
  126. package/scripts/component-state-workflow-negative-cases.mjs +0 -64
  127. package/scripts/consumer-conformance-contract.mjs +0 -198
  128. package/scripts/consumer-evidence-governance-contract.mjs +0 -210
  129. package/scripts/consumer-reference-case-runner.mjs +0 -93
  130. package/scripts/consumer-reference-schema.mjs +0 -155
  131. package/scripts/create-component-state-session.mjs +0 -100
  132. package/scripts/create-page-evidence-session.mjs +0 -139
  133. package/scripts/domain-document-boundaries.mjs +0 -40
  134. package/scripts/domain-validation-checks.mjs +0 -141
  135. package/scripts/evidence-artifact-contract.mjs +0 -191
  136. package/scripts/finalize-component-state-evidence.mjs +0 -165
  137. package/scripts/finalize-page-evidence.mjs +0 -197
  138. package/scripts/generate-consumer-reference-evidence.mjs +0 -154
  139. package/scripts/generate-patterns.mjs +0 -218
  140. package/scripts/governance-matrix-contract.mjs +0 -73
  141. package/scripts/governance-matrix-negative-cases.mjs +0 -114
  142. package/scripts/governance-policy-contract.mjs +0 -49
  143. package/scripts/governance-test-fixture.mjs +0 -321
  144. package/scripts/governance-workflow-negative-cases.mjs +0 -241
  145. package/scripts/governed-profile-registry.mjs +0 -125
  146. package/scripts/json-schema-formats.mjs +0 -31
  147. package/scripts/markdown-structure.mjs +0 -45
  148. package/scripts/page-artifact-metadata.mjs +0 -124
  149. package/scripts/page-evidence-contract.mjs +0 -297
  150. package/scripts/page-evidence-fixture.mjs +0 -271
  151. package/scripts/pattern-data.mjs +0 -325
  152. package/scripts/profile-record-contract.mjs +0 -140
  153. package/scripts/promotion-attestation-contract.mjs +0 -91
  154. package/scripts/promotion-boundary-test-contract.mjs +0 -92
  155. package/scripts/promotion-fixture-inventory.mjs +0 -109
  156. package/scripts/promotion-governance-contract.mjs +0 -28
  157. package/scripts/reference-artifact-path-contract.mjs +0 -28
  158. package/scripts/reference-profile-contract.mjs +0 -114
  159. package/scripts/reference-token-contract.mjs +0 -156
  160. package/scripts/run-consumer-page-evidence-ci.mjs +0 -170
  161. package/scripts/summarize-sentinel-calibration.mjs +0 -98
  162. package/scripts/test-agent-adapter-conformance.mjs +0 -250
  163. package/scripts/test-agent-conformance-receipt.mjs +0 -106
  164. package/scripts/test-agent-events.mjs +0 -81
  165. package/scripts/test-agent-execution-effects.mjs +0 -114
  166. package/scripts/test-agent-execution-fixtures.mjs +0 -140
  167. package/scripts/test-agent-execution-kernel.mjs +0 -39
  168. package/scripts/test-agent-execution-learning.mjs +0 -130
  169. package/scripts/test-agent-execution-lifecycle.mjs +0 -143
  170. package/scripts/test-agent-execution-safety.mjs +0 -158
  171. package/scripts/test-agent-execution-views.mjs +0 -117
  172. package/scripts/test-agent-identity-integrity-cases.mjs +0 -160
  173. package/scripts/test-agent-knowledge-kernel.mjs +0 -183
  174. package/scripts/test-agent-learning.mjs +0 -246
  175. package/scripts/test-agent-mcp.mjs +0 -246
  176. package/scripts/test-agent-native-schemas.mjs +0 -215
  177. package/scripts/test-agent-projections.mjs +0 -249
  178. package/scripts/test-agent-registry-mcp-integrity.mjs +0 -215
  179. package/scripts/test-agent-registry-mutations.mjs +0 -110
  180. package/scripts/test-audit-evidence-freshness.mjs +0 -177
  181. package/scripts/test-component-state-sentinel.mjs +0 -35
  182. package/scripts/test-component-state-source-contract.mjs +0 -83
  183. package/scripts/test-consumer-conformance-sentinel.mjs +0 -165
  184. package/scripts/test-consumer-reference-sentinel.mjs +0 -31
  185. package/scripts/test-generate-consumer-reference-evidence.mjs +0 -124
  186. package/scripts/test-json-cli-output.mjs +0 -151
  187. package/scripts/test-json-schema-formats.mjs +0 -40
  188. package/scripts/test-reference-adapters.mjs +0 -199
  189. package/scripts/test-renderer-purity.mjs +0 -34
  190. package/scripts/test-sg-cli.mjs +0 -129
  191. package/scripts/test-summarize-sentinel-calibration.mjs +0 -226
  192. package/scripts/test-validate-baseline-manifest.mjs +0 -234
  193. package/scripts/test-validate-component-state-artifacts.mjs +0 -243
  194. package/scripts/test-validate-component-state.mjs +0 -106
  195. package/scripts/test-validate-consumer-conformance.mjs +0 -427
  196. package/scripts/test-validate-consumer-reference-cli.mjs +0 -69
  197. package/scripts/test-validate-consumer-reference.mjs +0 -263
  198. package/scripts/test-validate-domains.mjs +0 -357
  199. package/scripts/test-validate-governance.mjs +0 -292
  200. package/scripts/test-validate-ia.mjs +0 -262
  201. package/scripts/test-validate-okf.mjs +0 -73
  202. package/scripts/test-validate-page-evidence.mjs +0 -302
  203. package/scripts/test-validate-patterns.mjs +0 -157
  204. package/scripts/test-validate-promotion-rfc.mjs +0 -250
  205. package/scripts/test-validate-reference-profiles.mjs +0 -170
  206. package/scripts/test-validate-unity-org-wiki.mjs +0 -57
  207. package/scripts/test-validate-unity-source-contracts.mjs +0 -67
  208. package/scripts/test-validate-vocabulary.mjs +0 -174
  209. package/scripts/test-validate-webpage-workflow.mjs +0 -293
  210. package/scripts/validate-baseline-manifest.mjs +0 -44
  211. package/scripts/validate-catalog.mjs +0 -65
  212. package/scripts/validate-component-state.mjs +0 -141
  213. package/scripts/validate-consumer-conformance.mjs +0 -477
  214. package/scripts/validate-consumer-reference.mjs +0 -288
  215. package/scripts/validate-domains.mjs +0 -237
  216. package/scripts/validate-governance.mjs +0 -248
  217. package/scripts/validate-ia.mjs +0 -136
  218. package/scripts/validate-links.mjs +0 -66
  219. package/scripts/validate-okf.mjs +0 -104
  220. package/scripts/validate-page-evidence.mjs +0 -191
  221. package/scripts/validate-patterns.mjs +0 -246
  222. package/scripts/validate-promotion-rfc.mjs +0 -224
  223. package/scripts/validate-reference-artifacts.mjs +0 -125
  224. package/scripts/validate-renderer-purity.mjs +0 -56
  225. package/scripts/validate-unity-org-wiki.mjs +0 -146
  226. package/scripts/validate-unity-source-contracts.mjs +0 -107
  227. package/scripts/validate-vocabulary.mjs +0 -115
  228. package/scripts/validate-webpage-workflow.mjs +0 -254
  229. package/scripts/visual-expectation-contract.mjs +0 -20
  230. package/scripts/workflow-action-contract.mjs +0 -163
@@ -1,250 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import { spawnSync } from "node:child_process";
6
- import { fileURLToPath } from "node:url";
7
- import { copyTreeNoSymlinks, testPromotionBoundaries } from "./promotion-boundary-test-contract.mjs";
8
-
9
- const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
- const fixtureRoot = path.join(repositoryRoot, "consumer-reference/fixtures/promotion");
11
- const validator = path.join(repositoryRoot, "scripts/validate-promotion-rfc.mjs");
12
-
13
- function readJson(relative) {
14
- return JSON.parse(fs.readFileSync(path.join(fixtureRoot, relative), "utf8"));
15
- }
16
-
17
- function runArguments(args) {
18
- const child = spawnSync(process.execPath, [validator, ...args], { cwd: repositoryRoot, encoding: "utf8" });
19
- let report = { failures: [], ok: false, parse_error: true };
20
- try {
21
- report = JSON.parse(child.stdout);
22
- } catch (error) {
23
- if (!(error instanceof SyntaxError)) throw error;
24
- report = { failures: [], ok: false, parse_error: true };
25
- }
26
- return { report, status: child.status };
27
- }
28
-
29
- function run(file, profileRepositoryRoot) {
30
- const args = ["--file", file, "--json"];
31
- if (profileRepositoryRoot) args.push("--profile-repository-root", profileRepositoryRoot);
32
- return runArguments(args);
33
- }
34
-
35
- function runPolicy(file) {
36
- return runArguments(["--policy", file, "--json"]);
37
- }
38
-
39
- function writeMutation(name, source, mutate) {
40
- const value = structuredClone(readJson(source));
41
- mutate(value);
42
- const file = path.join(tempRoot, `${name}.json`);
43
- fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
44
- return file;
45
- }
46
-
47
- function writePolicyMutation(root, name, mutate) {
48
- const value = JSON.parse(fs.readFileSync(path.join(repositoryRoot, "consumer-reference/policies/shared-experimental.json"), "utf8"));
49
- mutate(value);
50
- const file = path.join(root, `${name}.json`);
51
- fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
52
- return file;
53
- }
54
-
55
- function copyProfileRepository(name) {
56
- const root = path.join(tempRoot, name);
57
- const source = path.join(repositoryRoot, "design-engineering/reference-profiles/governed-local");
58
- const target = path.join(root, "design-engineering/reference-profiles/governed-local");
59
- copyTreeNoSymlinks(source, target);
60
- return root;
61
- }
62
-
63
- function editProfileJson(root, relative, mutate) {
64
- const file = path.join(root, relative);
65
- const value = JSON.parse(fs.readFileSync(file, "utf8"));
66
- mutate(value);
67
- fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
68
- }
69
-
70
- const fixtureManifest = JSON.parse(fs.readFileSync(path.join(fixtureRoot, "manifest.json"), "utf8"));
71
- const invalidCases = fixtureManifest.invalid_records.map((entry) => [entry.file.replace(/\.json$/, ""), entry.file, entry.expected_code]);
72
-
73
- const tempRoot = fs.mkdtempSync(path.join(repositoryRoot, ".tmp-pr6-promotion-"));
74
- const repositoryTempRoot = fs.mkdtempSync(path.join(repositoryRoot, ".tmp-pr6-profile-"));
75
- const results = [];
76
- try {
77
- for (const [name, relative, expected] of invalidCases) {
78
- const child = run(path.join(fixtureRoot, relative));
79
- const codes = child.report.failures.map((failure) => failure.code);
80
- results.push({ actual: { codes, scaffold: child.report.scaffold === true, status: child.status }, expected, name, ok: child.status > 0 && codes.includes(expected) && child.report.scaffold !== true });
81
- }
82
-
83
- const editorial = writeMutation("editorial_counted", "valid-deferred-example.json", (value) => {
84
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "editorial", consumer_id: "editorial-reference-profile", count_toward_gate: true, organization_id: "stylegallery", profile_record: "design-engineering/reference-profiles/governed-local/editorial/profile.json", relationship: "independent" }];
85
- });
86
- const terminal = writeMutation("terminal_counted", "valid-deferred-example.json", (value) => {
87
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "terminal", consumer_id: "terminal-reference-profile", count_toward_gate: true, organization_id: "stylegallery", profile_record: "design-engineering/reference-profiles/governed-local/terminal/profile.json", relationship: "independent" }];
88
- });
89
- const booleanReview = writeMutation("boolean_review", "valid-deferred-example.json", (value) => { value.review_independence = false; });
90
- const fakeIndependence = writeMutation("fake_independence", "valid-deferred-example.json", (value) => {
91
- value.attestations = [
92
- { attested_by: "Synthetic owner", context_id: "one", consumer_id: "consumer-one", count_toward_gate: true, organization_id: "same-org", relationship: "independent" },
93
- { attested_by: "Synthetic owner", context_id: "two", consumer_id: "consumer-two", count_toward_gate: true, organization_id: "same-org", relationship: "independent" }
94
- ];
95
- });
96
- const duplicateIdentity = writeMutation("duplicate_identity", "valid-deferred-example.json", (value) => {
97
- const attestation = { attested_by: "Synthetic owner", context_id: "same", consumer_id: "same-consumer", count_toward_gate: true, organization_id: "same-org", relationship: "independent" };
98
- value.attestations = [attestation, structuredClone(attestation)];
99
- });
100
- const duplicateConsumer = writeMutation("duplicate_consumer", "valid-deferred-example.json", (value) => {
101
- value.attestations = [
102
- { attested_by: "Synthetic owner A", context_id: "one", consumer_id: "same-consumer", count_toward_gate: true, organization_id: "organization-a", relationship: "independent" },
103
- { attested_by: "Synthetic owner B", context_id: "two", consumer_id: "same-consumer", count_toward_gate: true, organization_id: "organization-b", relationship: "independent" }
104
- ];
105
- });
106
- const unknownConsumers = writeMutation("unknown_consumers", "valid-deferred-example.json", (value) => {
107
- value.attestations = [
108
- { attested_by: "Synthetic owner A", context_id: "one", consumer_id: "unknown-one", count_toward_gate: true, organization_id: "organization-a", relationship: "independent" },
109
- { attested_by: "Synthetic owner B", context_id: "two", consumer_id: "unknown-two", count_toward_gate: true, organization_id: "organization-b", relationship: "independent" }
110
- ];
111
- });
112
- const forgedOne = path.join(repositoryTempRoot, "forged-one.json");
113
- const forgedTwo = path.join(repositoryTempRoot, "forged-two.json");
114
- fs.writeFileSync(forgedOne, '{"fixture_independence":"independent","id":"forged-one"}\n');
115
- fs.writeFileSync(forgedTwo, '{"fixture_independence":"independent","id":"forged-two"}\n');
116
- const forgedProfiles = writeMutation("forged_profiles", "valid-deferred-example.json", (value) => {
117
- value.attestations = [
118
- { attested_by: "Synthetic owner A", context_id: "one", consumer_id: "forged-one", count_toward_gate: true, organization_id: "organization-a", profile_record: path.relative(repositoryRoot, forgedOne), relationship: "independent" },
119
- { attested_by: "Synthetic owner B", context_id: "two", consumer_id: "forged-two", count_toward_gate: true, organization_id: "organization-b", profile_record: path.relative(repositoryRoot, forgedTwo), relationship: "independent" }
120
- ];
121
- });
122
- const copiedProfileRoot = path.join(repositoryTempRoot, "copied");
123
- copyTreeNoSymlinks(path.join(repositoryRoot, "design-engineering/reference-profiles/governed-local/editorial"), copiedProfileRoot);
124
- const copiedUnregistered = writeMutation("copied_unregistered", "valid-deferred-example.json", (value) => {
125
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "copied", consumer_id: "editorial-reference-profile", count_toward_gate: true, organization_id: "copied-org", profile_record: path.relative(repositoryRoot, path.join(copiedProfileRoot, "profile.json")), relationship: "independent" }];
126
- });
127
- const invalidRegisteredRoot = copyProfileRepository("invalid-registered-root");
128
- editProfileJson(invalidRegisteredRoot, "design-engineering/reference-profiles/governed-local/editorial/profile.json", (value) => { delete value.owner; });
129
- const invalidRegistered = writeMutation("invalid_registered", "valid-deferred-example.json", (value) => {
130
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "invalid", consumer_id: "editorial-reference-profile", count_toward_gate: true, organization_id: "invalid-org", profile_record: "design-engineering/reference-profiles/governed-local/editorial/profile.json", relationship: "independent" }];
131
- });
132
- const duplicateRegistryRoot = copyProfileRepository("duplicate-registry-root");
133
- editProfileJson(duplicateRegistryRoot, "design-engineering/reference-profiles/governed-local/terminal/profile.json", (value) => { value.id = "editorial-reference-profile"; });
134
- const duplicateRegistryIdentity = writeMutation("duplicate_registry_identity", "valid-deferred-example.json", (value) => {
135
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "duplicate", consumer_id: "editorial-reference-profile", count_toward_gate: true, organization_id: "duplicate-org", profile_record: "design-engineering/reference-profiles/governed-local/editorial/profile.json", relationship: "independent" }];
136
- });
137
- const silentRelabel = writeMutation("silent_relabel", "valid-deferred-example.json", (value) => {
138
- value.transition = { from: "stable", to: "shared_experimental" };
139
- });
140
- const pathEscape = writeMutation("path_escape", "valid-deferred-example.json", (value) => {
141
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "escape", consumer_id: "escape", count_toward_gate: true, organization_id: "escape", profile_record: "../outside.json", relationship: "independent" }];
142
- });
143
- const profileMismatch = writeMutation("profile_mismatch", "valid-deferred-example.json", (value) => {
144
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "editorial", consumer_id: "different-profile", count_toward_gate: true, organization_id: "stylegallery", profile_record: "design-engineering/reference-profiles/governed-local/editorial/profile.json", relationship: "independent" }];
145
- });
146
- const profileLink = path.join(repositoryTempRoot, "profile.json");
147
- fs.symlinkSync(path.join(repositoryRoot, "design-engineering/reference-profiles/governed-local/editorial/profile.json"), profileLink);
148
- const profileSymlink = writeMutation("profile_symlink", "valid-deferred-example.json", (value) => {
149
- value.attestations = [{ attested_by: "Synthetic owner", context_id: "editorial", consumer_id: "editorial-reference-profile", count_toward_gate: true, organization_id: "stylegallery", profile_record: path.relative(repositoryRoot, profileLink), relationship: "independent" }];
150
- });
151
- const schemaExtra = writeMutation("schema_extra", "valid-deferred-example.json", (value) => { value.unexpected_promotion = true; });
152
- const acceptedDecision = writeMutation("accepted_decision", "valid-deferred-example.json", (value) => { value.decision = "accepted"; });
153
- const aestheticPromotion = writeMutation("aesthetic_promotion", "valid-deferred-example.json", (value) => { value.scope.aesthetic = true; });
154
- const breakingWithoutMigration = writeMutation("breaking_without_migration", "valid-deferred-example.json", (value) => { value.compatibility.breaking = true; value.migration.required = false; });
155
- const normativeWithoutRollback = writeMutation("normative_without_rollback", "valid-normative-bypass.json", (value) => { delete value.rollback; });
156
- const normativeEndedSupport = writeMutation("normative_ended_support", "valid-normative-bypass.json", (value) => {
157
- value.support = { capacity: "No active support capacity", status: "ended" };
158
- });
159
- const normativeMissingSupport = writeMutation("normative_missing_support", "valid-normative-bypass.json", (value) => { delete value.support; });
160
- const normativeMissingCapacity = writeMutation("normative_missing_capacity", "valid-normative-bypass.json", (value) => { delete value.support.capacity; });
161
- const weakCapacityCases = ["0", "Not available", "Absent", "Lapsed", "Negative capacity"].map((capacity, index) => [
162
- `weak_capacity_${index + 1}`,
163
- writeMutation(`weak_capacity_${index + 1}`, "valid-deferred-example.json", (value) => { value.support.capacity = capacity; }),
164
- "promotion_support_required",
165
- ]);
166
- const stableWithoutCount = writeMutation("stable_without_count", "valid-deferred-example.json", (value) => {
167
- value.transition = { from: "shared_experimental", to: "stable" };
168
- value.stable_scope_decision = true;
169
- value.provenance.debt_resolved = true;
170
- });
171
- const stableWithoutEvidence = writeMutation("stable_without_evidence", "valid-deferred-example.json", (value) => {
172
- value.transition = { from: "shared_experimental", to: "stable" };
173
- value.stable_scope_decision = true;
174
- value.provenance.debt_resolved = true;
175
- value.evidence = [{ id: "regression", kind: "regression", reference: "synthetic-fixture:regression", status: "failed" }];
176
- });
177
- const duplicateKey = path.join(tempRoot, "duplicate_key.json");
178
- fs.writeFileSync(duplicateKey, fs.readFileSync(path.join(fixtureRoot, "valid-deferred-example.json"), "utf8").replace('"decision": "deferred",', '"decision": "deferred",\n "decision": "rejected",'));
179
- for (const [name, file, expected, profileRepositoryRoot] of [
180
- ["editorial_counted", editorial, "promotion_related_consumer_counted"],
181
- ["terminal_counted", terminal, "promotion_related_consumer_counted"],
182
- ["boolean_review", booleanReview, "review_independence_boolean"],
183
- ["fake_independence", fakeIndependence, "promotion_independence_unproven"],
184
- ["duplicate_identity", duplicateIdentity, "promotion_attestation_duplicate"],
185
- ["duplicate_consumer", duplicateConsumer, "promotion_attestation_duplicate"],
186
- ["unknown_consumers", unknownConsumers, "promotion_independence_unproven"],
187
- ["forged_profiles", forgedProfiles, "promotion_independence_unproven"],
188
- ["copied_unregistered", copiedUnregistered, "promotion_independence_unproven"],
189
- ["invalid_registered", invalidRegistered, "promotion_independence_unproven", invalidRegisteredRoot],
190
- ["duplicate_registry_identity", duplicateRegistryIdentity, "promotion_independence_unproven", duplicateRegistryRoot],
191
- ["silent_relabel", silentRelabel, "promotion_stable_relabel_forbidden"],
192
- ["path_escape", pathEscape, "promotion_profile_path_invalid"],
193
- ["profile_mismatch", profileMismatch, "promotion_profile_identity_mismatch"],
194
- ["profile_symlink", profileSymlink, "promotion_profile_path_symlink"],
195
- ["schema_extra", schemaExtra, "promotion_schema_invalid"],
196
- ["accepted_decision", acceptedDecision, "promotion_schema_invalid"],
197
- ["aesthetic_promotion", aestheticPromotion, "promotion_schema_invalid"],
198
- ["breaking_without_migration", breakingWithoutMigration, "promotion_migration_required"],
199
- ["normative_without_rollback", normativeWithoutRollback, "promotion_schema_invalid"],
200
- ["normative_ended_support", normativeEndedSupport, "promotion_support_required"],
201
- ["normative_missing_support", normativeMissingSupport, "promotion_support_required"],
202
- ["normative_missing_capacity", normativeMissingCapacity, "promotion_support_required"],
203
- ["duplicate_key", duplicateKey, "promotion_json_invalid"],
204
- ["stable_without_evidence", stableWithoutEvidence, "promotion_stable_evidence_required"],
205
- ...weakCapacityCases,
206
- ]) {
207
- const child = run(file, profileRepositoryRoot);
208
- const codes = child.report.failures.map((failure) => failure.code);
209
- results.push({ actual: { codes, scaffold: child.report.scaffold === true, status: child.status }, expected, name, ok: child.status > 0 && codes.includes(expected) && child.report.scaffold !== true });
210
- }
211
-
212
- for (const relative of ["valid-deferred-example.json", "valid-normative-bypass.json"]) {
213
- const child = run(path.join(fixtureRoot, relative));
214
- results.push({ actual: { scaffold: child.report.scaffold === true, status: child.status }, expected: "ok:true", name: relative, ok: child.status === 0 && child.report.ok === true && child.report.scaffold !== true });
215
- }
216
- const stable = run(stableWithoutCount);
217
- results.push({ actual: { scaffold: stable.report.scaffold === true, status: stable.status }, expected: "stable_without_numeric_threshold", name: "stable_without_count", ok: stable.status === 0 && stable.report.ok === true && stable.report.scaffold !== true });
218
- const canonicalPolicy = runPolicy(path.join(repositoryRoot, "consumer-reference/policies/shared-experimental.json"));
219
- results.push({ actual: { status: canonicalPolicy.status }, expected: "canonical_policy_valid", name: "canonical_policy", ok: canonicalPolicy.status === 0 && canonicalPolicy.report.ok === true });
220
- results.push(...testPromotionBoundaries({ fixtureRoot, repositoryRoot, runArguments, tempRoot }));
221
- for (const [name, file, expected] of [
222
- ["stable_numeric_count", writePolicyMutation(tempRoot, "stable_numeric_count", (value) => { value.gateway.stable_numeric_threshold = 2; }), "promotion_stable_numeric_threshold_forbidden"],
223
- ["bypass_overreach", writePolicyMutation(tempRoot, "bypass_overreach", (value) => { value.normative_bypass.waives.push("regression_proof"); }), "promotion_normative_bypass_overreach"],
224
- ["fake_promotion_claim", writePolicyMutation(tempRoot, "fake_promotion_claim", (value) => { value.claims.promotion_occurred = true; }), "promotion_claim_forbidden"],
225
- ["policy_unknown_field", writePolicyMutation(tempRoot, "policy_unknown_field", (value) => { value.accepted = true; }), "promotion_policy_shape_invalid"],
226
- ["policy_nested_unknown", writePolicyMutation(tempRoot, "policy_nested_unknown", (value) => { value.gateway.stable_by_count = true; }), "promotion_policy_shape_invalid"],
227
- ["policy_promotes_palette", writePolicyMutation(tempRoot, "policy_promotes_palette", (value) => { value.boundaries.promoted_content.push("palette"); }), "promotion_aesthetic_boundary_required"],
228
- ["policy_promotes_alias", writePolicyMutation(tempRoot, "policy_promotes_alias", (value) => { value.boundaries.promoted_content.push("brand_identity"); }), "promotion_aesthetic_boundary_required"],
229
- ["policy_promoted_non_string", writePolicyMutation(tempRoot, "policy_promoted_non_string", (value) => { value.boundaries.promoted_content = [42]; }), "promotion_aesthetic_boundary_required"],
230
- ["policy_normative_aesthetic", writePolicyMutation(tempRoot, "policy_normative_aesthetic", (value) => { value.normative_bypass.categories = ["aesthetic"]; }), "promotion_normative_bypass_overreach"],
231
- ["policy_lifecycle_incomplete", writePolicyMutation(tempRoot, "policy_lifecycle_incomplete", (value) => { value.lifecycle.failure_actions = ["deprecate_with_migration"]; }), "promotion_lifecycle_invalid"],
232
- ["policy_excluded_non_string", writePolicyMutation(tempRoot, "policy_excluded_non_string", (value) => { value.boundaries.excluded_content.push(42); }), "promotion_aesthetic_boundary_required"],
233
- ["policy_retains_non_string", writePolicyMutation(tempRoot, "policy_retains_non_string", (value) => { value.normative_bypass.retains.push(42); }), "promotion_normative_bypass_overreach"],
234
- ["policy_verified_owner", writePolicyMutation(tempRoot, "policy_verified_owner", (value) => { value.owner.enforcement = "verified"; }), "promotion_policy_owner_invalid"],
235
- ["policy_promoted_status", writePolicyMutation(tempRoot, "policy_promoted_status", (value) => { value.status = "promoted"; }), "promotion_claim_forbidden"],
236
- ["policy_generated_mode", writePolicyMutation(tempRoot, "policy_generated_mode", (value) => { value.artifact_mode = "generated"; }), "promotion_policy_identity_invalid"],
237
- ]) {
238
- const child = runPolicy(file);
239
- const codes = child.report.failures.map((failure) => failure.code);
240
- results.push({ actual: { codes, status: child.status }, expected, name, ok: child.status > 0 && codes.includes(expected) });
241
- }
242
- } finally {
243
- fs.rmSync(tempRoot, { force: true, recursive: true });
244
- fs.rmSync(repositoryTempRoot, { force: true, recursive: true });
245
- }
246
-
247
- const failures = results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`);
248
- const report = { failures, ok: failures.length === 0, results };
249
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
250
- if (!report.ok) process.exitCode = 1;
@@ -1,170 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import { spawnSync } from "node:child_process";
7
- import { fileURLToPath } from "node:url";
8
-
9
- const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
- const validator = path.join(repositoryRoot, "scripts", "validate-consumer-reference.mjs");
11
- const profilePath = "design-engineering/reference-profiles/governed-local/editorial/profile.json";
12
- const terminalProfilePath = "design-engineering/reference-profiles/governed-local/terminal/profile.json";
13
- const aliasProfilePath = "design-engineering/reference-profiles/governed-local/alias/profile.json";
14
- const layoutSha = "775430bbaf4ee208a642220f440f6926d79c90a3";
15
-
16
- const baseProfile = {
17
- artifact_mode: "governed_local",
18
- component_records: ["components/button.component.json"],
19
- default: false,
20
- environment_assumptions: {
21
- reset: { body_margin: "0", box_sizing: "border-box", figure_margin: "0" },
22
- user_agent_styles: "Preserve browser defaults except for the declared reset.",
23
- },
24
- example_only: true,
25
- evidence_records: ["evidence/button.evidence.json"],
26
- fixture_records: ["fixtures/button.fixture.json"],
27
- fixture_independence: "related",
28
- handoff: { record: profilePath, status: "declared" },
29
- generated_records: ["generated/evidence-coverage.md", "generated/keyboard-matrix.md", "generated/state-matrix.md"],
30
- id: "editorial-reference-profile",
31
- layout_source_sha: layoutSha,
32
- local_foundations: "local-foundations.json",
33
- maturity: "experimental",
34
- owner: { enforcement: "placeholder", name: "Design Engineering owner" },
35
- profile_kind: "governed_local",
36
- related_fixture_set_id: "layout-identity-adversarial-pair",
37
- review_independence: "single_account",
38
- schema_version: "1.0",
39
- selection: { method: "profile_path", required: true },
40
- state_records: ["states/button.states.json"],
41
- support: { status: "active" },
42
- tokens: "tokens.dtcg.json",
43
- };
44
-
45
- const tokens = {
46
- color: {
47
- $type: "color",
48
- accent: { $value: { alpha: 1, colorSpace: "srgb", components: [0.55, 0.18, 0.1] } },
49
- },
50
- space: { $type: "dimension", page: { $value: { unit: "rem", value: 1.5 } } },
51
- };
52
- const terminalTokens = {
53
- color: {
54
- $type: "color",
55
- accent: { $value: { alpha: 1, colorSpace: "srgb", components: [0.1, 0.9, 0.5] } },
56
- },
57
- space: { $type: "dimension", page: { $value: { unit: "rem", value: 1 } } },
58
- };
59
- const editorialFoundations = { bindings: { accent: "{color.accent}" }, identity: "Warm editorial", profile_id: "editorial-reference-profile" };
60
- const terminalFoundations = { bindings: { accent: "{color.accent}" }, identity: "Dark terminal", profile_id: "terminal-reference-profile" };
61
-
62
- const cases = [
63
- { name: "valid_governed_local_profile" },
64
- { expect: "profile_artifact_mode_governed_local", name: "local_as_external", mutate: (item) => { item.artifact_mode = "external_consumer"; } },
65
- { expect: "profile_default_forbidden", name: "implicit_default", mutate: (item) => { item.default = true; } },
66
- { expect: "profile_layout_source_sha_required", name: "missing_layout_sha", mutate: (item) => { delete item.layout_source_sha; } },
67
- { expect: "profile_ua_assumption_required", name: "missing_ua_assumption", mutate: (item) => { delete item.environment_assumptions.user_agent_styles; } },
68
- { expect: "profile_reset_assumptions_required", name: "missing_reset_assumptions", mutate: (item) => { delete item.environment_assumptions.reset; } },
69
- { expect: "profile_explicit_selection_required", name: "implicit_selection", mutate: (item) => { item.selection.required = false; } },
70
- { expect: "fixture_independence_related", name: "independence_independent", mutate: (item) => { item.fixture_independence = "independent"; } },
71
- { expect: "reverse_import", name: "reverse_profile_import", patternData: "export const related_fixture_set_id = 'layout-identity-adversarial-pair';\n" },
72
- { expect: "external_adaptation_record_forbidden", externalRecord: true, name: "durable_external_adopter" },
73
- { equalTokens: true, expect: "profile_tokens_not_distinct", name: "single_profile_equal_pair_tokens" },
74
- { equalFoundations: true, expect: "profile_foundations_not_distinct", name: "single_profile_equal_pair_foundations" },
75
- { equalIdentity: true, expect: "profile_identity_not_distinct", name: "single_profile_equal_pair_identity" },
76
- { expect: "profile_related_set_mismatch", name: "single_profile_related_set_mismatch", mutateTerminal: (item) => { item.related_fixture_set_id = "unrelated-terminal-set"; } },
77
- { expect: "profile_tokens_symlink_escape", linkedArtifact: "tokens", name: "linked_outside_tokens" },
78
- { expect: "profile_foundations_symlink_escape", linkedArtifact: "foundations", name: "linked_outside_foundations" },
79
- { expect: "profile_environment_property_unknown", name: "unknown_environment_field", mutate: (item) => { item.environment_assumptions.viewport = "wide"; } },
80
- { expect: "profile_reset_property_unknown", name: "unknown_reset_field", mutate: (item) => { item.environment_assumptions.reset.list_margin = "0"; } },
81
- { expect: "profile_selection_property_unknown", name: "unknown_selection_field", mutate: (item) => { item.selection.fallback = "editorial"; } },
82
- { expect: "item_property_unknown", name: "unknown_profile_top_level", mutate: (item) => { item.profile_fallback = true; } },
83
- {
84
- equalTokens: true,
85
- expect: "profile_tokens_not_distinct",
86
- name: "normalized_equivalent_selector_loads_pair",
87
- selector: "design-engineering/reference-profiles/governed-local/editorial/../editorial/profile.json",
88
- },
89
- { absoluteSelector: true, expect: "item_unresolved", name: "absolute_profile_selector_rejected" },
90
- { expect: "item_unresolved", name: "scheme_profile_selector_rejected", selector: "https://example.com/editorial/profile.json" },
91
- { expect: "item_unresolved", name: "network_profile_selector_rejected", selector: "//server/editorial/profile.json" },
92
- { expect: "item_unresolved", name: "escaping_profile_selector_rejected", selector: "../editorial/profile.json" },
93
- { aliasSelector: true, expect: "item_redirect", name: "symlink_alias_profile_selector_rejected" },
94
- ];
95
-
96
- function writeFixture(testCase) {
97
- const root = fs.mkdtempSync(path.join(os.tmpdir(), `stylegallery-reference-profile-${testCase.name}-`));
98
- const profile = structuredClone(baseProfile);
99
- const terminalProfile = structuredClone(baseProfile);
100
- terminalProfile.handoff.record = terminalProfilePath;
101
- terminalProfile.id = "terminal-reference-profile";
102
- testCase.mutate?.(profile);
103
- testCase.mutateTerminal?.(terminalProfile);
104
- const terminalFoundation = structuredClone(terminalFoundations);
105
- if (testCase.equalIdentity) terminalFoundation.identity = editorialFoundations.identity;
106
- const files = {
107
- "CATALOG.md": "# Catalog\n",
108
- [profilePath]: `${JSON.stringify(profile, null, 2)}\n`,
109
- "design-engineering/reference-profiles/governed-local/editorial/local-foundations.json": `${JSON.stringify(editorialFoundations)}\n`,
110
- "design-engineering/reference-profiles/governed-local/editorial/tokens.dtcg.json": `${JSON.stringify(tokens, null, 2)}\n`,
111
- [terminalProfilePath]: `${JSON.stringify(terminalProfile, null, 2)}\n`,
112
- "design-engineering/reference-profiles/governed-local/terminal/local-foundations.json": `${JSON.stringify(testCase.equalFoundations ? editorialFoundations : terminalFoundation)}\n`,
113
- "design-engineering/reference-profiles/governed-local/terminal/tokens.dtcg.json": `${JSON.stringify(testCase.equalTokens ? tokens : terminalTokens, null, 2)}\n`,
114
- "layout/index.md": "# Layout\n",
115
- "patterns/index.md": "# Patterns\n",
116
- "quality/handoff.md": "Implementation handoff:\nConsumer reference: not_applicable\nConsumer reference reason: This fixture has no consumer-specific reference record.\n",
117
- "scripts/pattern-data.mjs": testCase.patternData ?? "export const patterns = [];\n",
118
- };
119
- if (testCase.externalRecord) files["design-engineering/reference-profiles/external-adaptation/adopter.json"] = "{}\n";
120
- for (const [relative, content] of Object.entries(files)) {
121
- const target = path.join(root, relative);
122
- fs.mkdirSync(path.dirname(target), { recursive: true });
123
- fs.writeFileSync(target, content);
124
- }
125
- let externalRoot;
126
- if (testCase.linkedArtifact) {
127
- externalRoot = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-reference-profile-outside-"));
128
- const filename = testCase.linkedArtifact === "tokens" ? "tokens.dtcg.json" : "local-foundations.json";
129
- const target = path.join(root, path.posix.dirname(profilePath), filename);
130
- const external = path.join(externalRoot, filename);
131
- fs.writeFileSync(external, files[path.posix.join(path.posix.dirname(profilePath), filename)]);
132
- fs.rmSync(target);
133
- fs.symlinkSync(external, target);
134
- }
135
- let selector = testCase.selector ?? profilePath;
136
- if (testCase.absoluteSelector) selector = path.join(root, profilePath);
137
- if (testCase.aliasSelector) {
138
- const alias = path.join(root, aliasProfilePath);
139
- fs.mkdirSync(path.dirname(alias), { recursive: true });
140
- fs.symlinkSync(path.join(root, profilePath), alias);
141
- selector = aliasProfilePath;
142
- }
143
- return { externalRoot, root, selector };
144
- }
145
-
146
- function runCase(testCase) {
147
- const fixture = writeFixture(testCase);
148
- try {
149
- const child = spawnSync(process.execPath, [validator, "--profile", fixture.selector, "--json"], { cwd: fixture.root, encoding: "utf8" });
150
- const output = JSON.parse(child.stdout);
151
- const codes = Array.isArray(output.failures) ? output.failures.map((failure) => failure.code) : [];
152
- const accepted = child.status === 0 && output.ok === true && output.scaffold !== true;
153
- const rejected = child.status !== 0 && output.ok === false && codes.includes(testCase.expect);
154
- return {
155
- actual: { codes, ok: output.ok, status: child.status },
156
- expected: testCase.expect ?? "ok:true and exit:0",
157
- name: testCase.name,
158
- ok: testCase.expect ? rejected : accepted,
159
- };
160
- } finally {
161
- fs.rmSync(fixture.root, { force: true, recursive: true });
162
- if (fixture.externalRoot) fs.rmSync(fixture.externalRoot, { force: true, recursive: true });
163
- }
164
- }
165
-
166
- const results = cases.map(runCase);
167
- const failures = results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`);
168
- const report = { failures, ok: failures.length === 0, results };
169
- console.log(JSON.stringify(report, null, 2));
170
- process.exitCode = report.ok ? 0 : 1;
@@ -1,57 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import { spawnSync } from "node:child_process";
6
-
7
- const root = path.resolve(import.meta.dirname, "..");
8
- const validator = path.join(root, "scripts", "validate-unity-org-wiki.mjs");
9
-
10
- function run(cwd) {
11
- return spawnSync(process.execPath, [validator, "--json"], { cwd, encoding: "utf8" });
12
- }
13
-
14
- async function main() {
15
- const inventoryPath = path.join(root, "game-ui", "unity", "data", "unity-technologies-public-repositories.json");
16
- const mapPath = path.join(root, "game-ui", "unity", "repository-map.md");
17
- const wikiPath = path.join(root, "game-ui", "unity", "org-wiki.md");
18
- const inventoryText = await readFile(inventoryPath, "utf8");
19
- const inventory = JSON.parse(inventoryText);
20
- const map = await readFile(mapPath, "utf8");
21
- const wiki = await readFile(wikiPath, "utf8");
22
- const tempRoot = await mkdtemp(path.join(os.tmpdir(), "unity-org-wiki-validator-"));
23
-
24
- try {
25
- await writeFile(path.join(tempRoot, "inventory.json"), inventoryText);
26
- await writeFile(path.join(tempRoot, "map.md"), map);
27
- await writeFile(path.join(tempRoot, "wiki.md"), wiki);
28
- const fixtures = [
29
- ["wrong count", inventory.slice(1), map, wiki, "expected 804 repositories"],
30
- ["duplicate repository", [...inventory.slice(0, -1), inventory[0]], map, wiki, "duplicate repository identity"],
31
- ["NGUI inside org", [...inventory.slice(0, -1), { ...inventory.at(-1), id: 999999999, full_name: "tasharen/ngui", html_url: "https://github.com/tasharen/ngui" }], map, wiki, "NGUI must remain external"],
32
- ["invalid cluster", [{ ...inventory[0], primary_cluster: "unverified" }, ...inventory.slice(1)], map, wiki, "invalid primary_cluster"],
33
- ["absolute path", inventory, map, `${wiki}\n/Users/example/private\n`, "absolute home path"],
34
- ["wiki data drift", inventory, map, wiki.replaceAll("**804**", "**803**"), "wiki must state the 804-row boundary"],
35
- ["repository map count drift", inventory, map.replace("804 classified rows", "803 classified rows"), wiki, "repository map must state the 804-row boundary"],
36
- ["repository map hash drift", inventory, map.replaceAll("b1438e6e8d438bab3eff2ff02f5a10e9374fdfb89b0581ffb936a42920fa2e05", "0000000000000000000000000000000000000000000000000000000000000000"), wiki, "repository map tracked inventory hash drift"],
37
- ["tracked inventory hash drift", [{ ...inventory[0], stargazers_count: inventory[0].stargazers_count + 1 }, ...inventory.slice(1)], map, wiki, "tracked inventory SHA-256 mismatch"],
38
- ];
39
-
40
- for (const [name, candidateInventory, candidateMap, candidateWiki, expected] of fixtures) {
41
- await writeFile(path.join(tempRoot, "inventory.json"), candidateInventory === inventory ? inventoryText : JSON.stringify(candidateInventory));
42
- await writeFile(path.join(tempRoot, "map.md"), candidateMap);
43
- await writeFile(path.join(tempRoot, "wiki.md"), candidateWiki);
44
- const result = spawnSync(process.execPath, [validator, "--json", "--inventory", path.join(tempRoot, "inventory.json"), "--map", path.join(tempRoot, "map.md"), "--wiki", path.join(tempRoot, "wiki.md")], { cwd: root, encoding: "utf8" });
45
- assert.notEqual(result.status, 0, `${name} fixture unexpectedly passed`);
46
- assert.match(`${result.stdout}${result.stderr}`, new RegExp(expected.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")), `${name} fixture failed for the wrong reason`);
47
- }
48
-
49
- const result = run(root);
50
- assert.equal(result.status, 0, result.stderr || result.stdout);
51
- console.log("Unity org wiki validator fixtures passed: 9 negative, 1 positive.");
52
- } finally {
53
- await rm(tempRoot, { recursive: true, force: true });
54
- }
55
- }
56
-
57
- await main();
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import assert from "node:assert/strict";
4
- import fs from "node:fs";
5
- import os from "node:os";
6
- import path from "node:path";
7
- import { spawnSync } from "node:child_process";
8
-
9
- const root = path.resolve(import.meta.dirname, "..");
10
- const validator = path.join(root, "scripts", "validate-unity-source-contracts.mjs");
11
- const documents = [
12
- "game-ui/unity/architecture.md",
13
- "game-ui/unity/cli-loop.md",
14
- "game-ui/unity/org-wiki.md",
15
- "game-ui/unity/repository-map.md",
16
- "game-ui/unity/ui-systems.md",
17
- ];
18
- const originals = Object.fromEntries(documents.map((relative) => [relative, fs.readFileSync(path.join(root, relative), "utf8")]));
19
- const fixtures = [
20
- {
21
- name: "missing CLI license attribution",
22
- relative: "game-ui/unity/cli-loop.md",
23
- mutate: (content) => content.replace(/- Source license:.*\n/, ""),
24
- expected: "game-ui/unity/cli-loop.md: missing pinned source license",
25
- },
26
- {
27
- name: "mutable CLI body citation",
28
- relative: "game-ui/unity/cli-loop.md",
29
- mutate: (content) => content.replace("https://github.com/hatayama/unity-cli-loop/blob/61a0fe6d7da0aa9d0bcbc6d95944dd069c483ff0/README.md", "https://github.com/hatayama/unity-cli-loop/blob/main/README.md"),
30
- expected: "game-ui/unity/cli-loop.md: GitHub body citation must use a non-zero 40-character revision: hatayama/unity-cli-loop",
31
- },
32
- {
33
- name: "zeroed uGUI body revisions",
34
- relative: "game-ui/unity/ui-systems.md",
35
- mutate: (content) => content.replaceAll("9edb4420267b6652090ece4c28c38bd98746a68e", "0000000000000000000000000000000000000000"),
36
- expected: "game-ui/unity/ui-systems.md: GitHub body citation must use a non-zero 40-character revision: Unity-Technologies/uGUI",
37
- },
38
- {
39
- name: "invalid reviewed date",
40
- relative: "game-ui/unity/architecture.md",
41
- mutate: (content) => content.replace("reviewed_on: 2026-07-14", "reviewed_on: never"),
42
- expected: "game-ui/unity/architecture.md: reviewed_on must be an ISO calendar date",
43
- },
44
- ];
45
-
46
- function writeFixture(fixture) {
47
- const directory = fs.mkdtempSync(path.join(os.tmpdir(), "unity-source-contracts-"));
48
- for (const [relative, content] of Object.entries(originals)) {
49
- const target = path.join(directory, relative);
50
- fs.mkdirSync(path.dirname(target), { recursive: true });
51
- fs.writeFileSync(target, relative === fixture.relative ? fixture.mutate(content) : content);
52
- }
53
- return directory;
54
- }
55
-
56
- for (const fixture of fixtures) {
57
- const directory = writeFixture(fixture);
58
- const result = spawnSync(process.execPath, [validator, "--json", "--root", directory], { encoding: "utf8" });
59
- fs.rmSync(directory, { force: true, recursive: true });
60
- assert.notEqual(result.status, 0, `${fixture.name} unexpectedly passed`);
61
- const output = JSON.parse(result.stdout);
62
- assert(output.failures.includes(fixture.expected), `${fixture.name} failed for the wrong reason: ${result.stdout}`);
63
- }
64
-
65
- const positive = spawnSync(process.execPath, [validator, "--json"], { cwd: root, encoding: "utf8" });
66
- assert.equal(positive.status, 0, positive.stderr || positive.stdout);
67
- console.log("Unity source contract fixtures passed: 4 negative, 1 positive.");