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,177 +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
- import { cleanupFixture, completeSession, initializeConsumer } from "./page-evidence-fixture.mjs";
9
-
10
- const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- const auditor = path.join(repositoryRoot, "scripts", "audit-evidence-freshness.mjs");
12
- const calibration = path.join(repositoryRoot, "consumer-reference", "baselines", "calibration.json");
13
- const selectedCase = process.argv.find((argument) => argument === "--case") ? process.argv[process.argv.indexOf("--case") + 1] : null;
14
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-freshness-"));
15
-
16
- function writeJson(file, value) {
17
- fs.mkdirSync(path.dirname(file), { recursive: true });
18
- fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
19
- }
20
-
21
- function run(args) {
22
- const child = spawnSync(process.execPath, [auditor, ...args, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
23
- let report;
24
- try {
25
- report = JSON.parse(child.stdout);
26
- } catch (error) {
27
- report = null;
28
- }
29
- return { child, report };
30
- }
31
-
32
- function result(name, expected, actual, ok) {
33
- return { actual, expected, name, ok };
34
- }
35
-
36
- function pageSchema() {
37
- return {
38
- $schema: "https://json-schema.org/draft/2020-12/schema",
39
- $id: "https://stylegallery.test/page-evidence-manifest.schema.json",
40
- additionalProperties: false,
41
- properties: {
42
- record_kind: { const: "page_evidence_manifest" },
43
- review_by: { format: "date-time", type: "string" },
44
- schema_version: { const: "1.0" },
45
- },
46
- required: ["record_kind", "review_by", "schema_version"],
47
- type: "object",
48
- };
49
- }
50
-
51
- function pageFixture(file, reviewBy) {
52
- writeJson(file, { record_kind: "page_evidence_manifest", review_by: reviewBy, schema_version: "1.0" });
53
- }
54
-
55
- function selected(cases) {
56
- if (!selectedCase) return cases;
57
- const match = cases.filter((testCase) => testCase.name === selectedCase);
58
- if (match.length === 0) {
59
- return [result(selectedCase, "known --case", { cases: cases.map((testCase) => testCase.name) }, false)];
60
- }
61
- return match;
62
- }
63
-
64
- const pageSchemaPath = path.join(tempRoot, "page-evidence-manifest.schema.json");
65
- writeJson(pageSchemaPath, pageSchema());
66
- const pagePreDeadline = path.join(tempRoot, "review-window.json");
67
- pageFixture(pagePreDeadline, "2026-07-27T00:00:00Z");
68
- const pagePastDeadline = path.join(tempRoot, "review-past-deadline.json");
69
- pageFixture(pagePastDeadline, "2026-07-20T00:00:00Z");
70
- const pageAtDeadline = path.join(tempRoot, "review-at-deadline.json");
71
- pageFixture(pageAtDeadline, "2026-07-21T00:00:00Z");
72
- const pageFresh = path.join(tempRoot, "review-fresh.json");
73
- pageFixture(pageFresh, "2026-08-20T00:00:00Z");
74
- const malformed = path.join(tempRoot, "malformed.json");
75
- fs.writeFileSync(malformed, '{"record_kind":"page_evidence_manifest","review_by":"not-a-date","schema_version":"1.0"}\n');
76
- const unknown = path.join(tempRoot, "unknown.json");
77
- writeJson(unknown, { record_kind: "unsupported_evidence_manifest", schema_version: "1.0" });
78
- const missing = path.join(tempRoot, "missing.json");
79
- const missingPageSchema = path.join(tempRoot, "missing-page-evidence-manifest.schema.json");
80
- const completedPageFixture = initializeConsumer();
81
- const completedPage = completeSession(completedPageFixture);
82
- if (completedPage.validate?.status !== 0) throw new Error(`full page fixture failed: ${JSON.stringify(completedPage.validate?.report ?? completedPage.finalize?.report)}`);
83
- const completeManifest = JSON.parse(fs.readFileSync(path.join(completedPage.session.artifactRoot, "page-evidence-manifest.json"), "utf8"));
84
- const impossibleDateRecords = [
85
- ["impossible-session-started-at", (value) => { value.session.started_at = "2023-02-30T12:00:00Z"; }],
86
- ["impossible-session-completed-at", (value) => { value.session.completed_at = "2023-02-30T12:00:00Z"; }],
87
- ["impossible-manifest-completed-at", (value) => { value.completed_at = "2023-02-30T12:00:00Z"; }],
88
- ["impossible-scenario-recorded-at", (value) => { value.scenarios[0].recorded_at = "2023-02-30T12:00:00Z"; }],
89
- ].map(([name, mutate]) => {
90
- const value = structuredClone(completeManifest);
91
- mutate(value);
92
- const file = path.join(tempRoot, `${name}.json`);
93
- writeJson(file, value);
94
- return { file, name };
95
- });
96
-
97
- const cases = [
98
- (() => {
99
- const actual = run(["--record", pageFresh, "--page-schema", pageSchemaPath, "--as-of", "2023-02-30T12:00:00Z", "--mode", "advisory"]);
100
- return result("impossible-as-of", "exit nonzero with as_of_invalid", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status }, actual.child.status !== 0 && actual.report?.failures?.some((item) => item.code === "as_of_invalid"));
101
- })(),
102
- ...impossibleDateRecords.map(({ file, name }) => (() => {
103
- const actual = run(["--record", file, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
104
- return result(name, "exit nonzero with freshness_record_invalid", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status }, actual.child.status !== 0 && actual.report?.failures?.some((item) => item.code === "freshness_record_invalid"));
105
- })()),
106
- (() => {
107
- const actual = run(["--record", calibration, "--as-of", "2026-07-01T00:00:00Z", "--mode", "advisory"]);
108
- return result("fresh-calibration", "exit 0 with status fresh and no warnings", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status === 0 && actual.report?.ok === true && actual.report.records[0]?.status === "fresh" && actual.report.warnings.length === 0);
109
- })(),
110
- (() => {
111
- const actual = run(["--record", calibration, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
112
- return result("expiring-calibration-advisory", "exit 0 with expiring status and evidence_expiring warning", { codes: actual.report?.warnings?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status === 0 && actual.report?.ok === true && actual.report.records[0]?.status === "expiring" && actual.report.warnings.some((item) => item.code === "evidence_expiring"));
113
- })(),
114
- (() => {
115
- const actual = run(["--record", calibration, "--as-of", "2026-07-28T00:00:00Z", "--mode", "advisory"]);
116
- return result("expired-calibration-advisory", "exit 0 with expired status and warning", { codes: actual.report?.warnings?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status === 0 && actual.report?.ok === true && actual.report.records[0]?.status === "expired" && actual.report.warnings.some((item) => item.code === "evidence_expired"));
117
- })(),
118
- (() => {
119
- const actual = run(["--record", pagePreDeadline, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
120
- return result("pre-deadline-page-advisory", "exit 0 with expiring status and evidence_expiring warning", { codes: actual.report?.warnings?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status === 0 && actual.report?.ok === true && actual.report.records[0]?.status === "expiring" && actual.report.warnings.some((item) => item.code === "evidence_expiring"));
121
- })(),
122
- (() => {
123
- const actual = run(["--record", pagePreDeadline, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "blocking"]);
124
- return result("pre-deadline-review-window-blocking", "exit 0 with expiring status, advisory evidence_expiring warning, and no blocking failure", { failureCodes: actual.report?.failures?.map((item) => item.code), warningCodes: actual.report?.warnings?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status === 0 && actual.report?.ok === true && actual.report.records[0]?.status === "expiring" && actual.report.warnings.some((item) => item.code === "evidence_expiring") && actual.report.failures.length === 0);
125
- })(),
126
- (() => {
127
- const actual = run(["--record", pagePastDeadline, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
128
- return result("review-due-page-advisory", "exit 0 with review_due status and evidence_review_due warning", { codes: actual.report?.warnings?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status === 0 && actual.report?.ok === true && actual.report.records[0]?.status === "review_due" && actual.report.warnings.some((item) => item.code === "evidence_review_due"));
129
- })(),
130
- (() => {
131
- const before = fs.readFileSync(calibration);
132
- const actual = run(["--record", calibration, "--record", pagePreDeadline, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
133
- const after = fs.readFileSync(calibration);
134
- return result("repeated-record-and-source-immutability", "two records are audited and calibration bytes remain unchanged", { bytesUnchanged: before.equals(after), count: actual.report?.records?.length, status: actual.child.status }, actual.child.status === 0 && actual.report?.records?.length === 2 && before.equals(after));
135
- })(),
136
- (() => {
137
- const actual = run(["--record", pageFresh, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
138
- return result("fresh-page-review", "exit 0 with fresh page evidence", { status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status === 0 && actual.report?.records[0]?.status === "fresh");
139
- })(),
140
- (() => {
141
- const actual = run(["--record", malformed, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
142
- return result("malformed-advisory", "exit nonzero with freshness_record_invalid", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status }, actual.child.status !== 0 && actual.report?.ok === false && actual.report.failures.some((item) => item.code === "freshness_record_invalid"));
143
- })(),
144
- (() => {
145
- const actual = run(["--record", unknown, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
146
- return result("unknown-record-kind", "exit nonzero with freshness_record_invalid", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status }, actual.child.status !== 0 && actual.report?.failures.some((item) => item.code === "freshness_record_invalid"));
147
- })(),
148
- (() => {
149
- const actual = run(["--record", missing, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
150
- return result("unreadable-record", "exit nonzero with freshness_record_unreadable", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status }, actual.child.status !== 0 && actual.report?.failures.some((item) => item.code === "freshness_record_unreadable"));
151
- })(),
152
- (() => {
153
- const actual = run(["--record", pagePreDeadline, "--page-schema", missingPageSchema, "--as-of", "2026-07-21T00:00:00Z", "--mode", "advisory"]);
154
- return result("missing-page-schema", "exit nonzero with freshness_schema_missing", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status }, actual.child.status !== 0 && actual.report?.failures.some((item) => item.code === "freshness_schema_missing"));
155
- })(),
156
- (() => {
157
- const actual = run(["--record", calibration, "--as-of", "2026-07-28T00:00:00Z", "--mode", "blocking"]);
158
- return result("expired-calibration-blocking", "exit nonzero with evidence_expired", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status }, actual.child.status !== 0 && actual.report?.failures.some((item) => item.code === "evidence_expired"));
159
- })(),
160
- (() => {
161
- const actual = run(["--record", pagePastDeadline, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "blocking"]);
162
- return result("review-due-blocking", "exit nonzero with evidence_review_due", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status !== 0 && actual.report?.ok === false && actual.report.records[0]?.status === "review_due" && actual.report.failures.some((item) => item.code === "evidence_review_due"));
163
- })(),
164
- (() => {
165
- const actual = run(["--record", pageAtDeadline, "--page-schema", pageSchemaPath, "--as-of", "2026-07-21T00:00:00Z", "--mode", "blocking"]);
166
- return result("page-at-review-deadline-blocking", "exit nonzero with review_due/evidence_review_due at the deadline", { codes: actual.report?.failures?.map((item) => item.code), status: actual.child.status, statuses: actual.report?.records?.map((item) => item.status) }, actual.child.status !== 0 && actual.report?.ok === false && actual.report.records[0]?.status === "review_due" && actual.report.failures.some((item) => item.code === "evidence_review_due"));
167
- })(),
168
- ];
169
-
170
- try {
171
- const report = { ok: selected(cases).every((testCase) => testCase.ok), results: selected(cases) };
172
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
173
- process.exitCode = report.ok ? 0 : 1;
174
- } finally {
175
- cleanupFixture(completedPageFixture);
176
- fs.rmSync(tempRoot, { force: true, recursive: true });
177
- }
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import path from "node:path";
4
- import { spawnSync } from "node:child_process";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
- const cases = [
9
- ["key_runtime_mismatch", "runtime-key-mismatch", "state_activation_key_parity", "action-focused"],
10
- ["pressed_false", "runtime-pressed-false", "pressed_surface_mismatch", "toggle-focused-pressed"],
11
- ["visual_ready", "runtime-visual-ready", "visual_surface_parity", "toggle-focused-pressed"],
12
- ["unknown_scenario", "runtime-unknown-scenario", "state_runtime_unknown_scenario", "action-focused"],
13
- ["expanded_ax_mismatch", "runtime-expanded-ax-mismatch", "expanded_surface_mismatch", "disclosure-expanded-loading"],
14
- ["disabled_activation", "runtime-disabled-activation", "state_activation_key_parity", "action-disabled-busy"],
15
- ];
16
-
17
- const results = cases.map(([name, mutation, expected, scenario]) => {
18
- const child = spawnSync(
19
- path.join(repositoryRoot, "node_modules", ".bin", "playwright"),
20
- ["test", "tests/component-state-evidence.spec.mjs", "--project=chromium", "--reporter=line", "--grep", scenario],
21
- { cwd: repositoryRoot, encoding: "utf8", env: { ...process.env, STATE_MUTATION: mutation } },
22
- );
23
- const output = `${child.stdout}\n${child.stderr}`;
24
- return {
25
- actual: { named: output.includes(expected), status: child.status },
26
- expected,
27
- name,
28
- ok: child.status !== 0 && output.includes(expected),
29
- };
30
- });
31
-
32
- const failures = results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`);
33
- const report = { failures, ok: failures.length === 0, results };
34
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
35
- if (!report.ok) process.exitCode = 1;
@@ -1,83 +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 { execFileSync } from "node:child_process";
8
- import { fileURLToPath } from "node:url";
9
- import {
10
- canonicalSourceManifest,
11
- repositoryGitArgs,
12
- sourceManifestMatches,
13
- } from "./capture-session-contract.mjs";
14
- import { compileSchemas } from "./component-state-contract.mjs";
15
-
16
- const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
17
- const profileRoot = path.join(repositoryRoot, "design-engineering/reference-profiles/governed-local");
18
- const schemaRoot = path.join(repositoryRoot, "consumer-reference/schema");
19
- const first = canonicalSourceManifest(repositoryRoot, profileRoot);
20
- const second = canonicalSourceManifest(repositoryRoot, profileRoot);
21
-
22
- assert.equal(first.sha256, second.sha256, "source manifest digest must be deterministic");
23
- assert.deepEqual(first.files, second.files, "source manifest files must be stable");
24
- assert(first.files.some((entry) => entry.path === "tests/component-state-evidence.spec.mjs"), "capture browser code must be bound");
25
- assert(first.files.some((entry) => entry.path === "playwright.config.mjs"), "Playwright capture configuration must be bound");
26
- assert(first.files.some((entry) => entry.path === "package.json"), "capture dependency declarations must be bound");
27
- assert(first.files.some((entry) => entry.path === "package-lock.json"), "exact capture dependencies must be bound");
28
- assert(first.files.some((entry) => entry.path === "scripts/artifact-metadata.mjs"), "capture artifact metadata code must be bound");
29
- assert(first.files.some((entry) => entry.path === "scripts/visual-expectation-contract.mjs"), "visual expectation selection must be bound");
30
- assert(!first.files.some((entry) => entry.path === "scripts/finalize-component-state-evidence.mjs"), "post-capture finalization must not invalidate captured source identity");
31
- assert(!first.files.some((entry) => entry.path === "scripts/validate-component-state.mjs"), "post-capture enforcement must not invalidate captured source identity");
32
- assert(first.files.some((entry) => entry.path.endsWith("/profile.json")), "profile declarations must be bound");
33
- assert(first.files.some((entry) => entry.path.endsWith("/tokens.dtcg.json")), "profile token inputs must be bound");
34
- assert.equal(sourceManifestMatches(first, repositoryRoot, profileRoot), true, "canonical source manifest must match itself");
35
- const revisionArgs = repositoryGitArgs(repositoryRoot, "rev-parse", "HEAD");
36
- assert.deepEqual(revisionArgs, ["-c", `safe.directory=${repositoryRoot}`, "rev-parse", "HEAD"], "repository Git trust must be scoped to the exact checkout");
37
- assert.equal(
38
- execFileSync("git", revisionArgs, {
39
- cwd: repositoryRoot,
40
- encoding: "utf8",
41
- env: { ...process.env, GIT_TEST_ASSUME_DIFFERENT_OWNER: "1" },
42
- }).trim(),
43
- execFileSync("git", revisionArgs, { cwd: repositoryRoot, encoding: "utf8" }).trim(),
44
- "repository Git reads must trust only the exact checkout when container ownership differs",
45
- );
46
-
47
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-source-contract-"));
48
- try {
49
- const copiedProfiles = path.join(tempRoot, "profiles");
50
- const copiedRepository = path.join(tempRoot, "repository");
51
- fs.cpSync(profileRoot, copiedProfiles, { recursive: true });
52
- for (const entry of first.files.filter((candidate) => !candidate.path.startsWith("profiles/"))) {
53
- const source = path.join(repositoryRoot, entry.path);
54
- const target = path.join(copiedRepository, entry.path);
55
- fs.mkdirSync(path.dirname(target), { recursive: true });
56
- fs.copyFileSync(source, target);
57
- }
58
- const copied = canonicalSourceManifest(copiedRepository, copiedProfiles);
59
- assert.equal(copied.sha256, first.sha256, "profile-root location must not change the source digest");
60
- const tokenFile = path.join(copiedProfiles, "editorial/tokens.dtcg.json");
61
- fs.appendFileSync(tokenFile, "\n");
62
- assert.equal(sourceManifestMatches(copied, copiedRepository, copiedProfiles), false, "profile source drift must invalidate the manifest");
63
- fs.copyFileSync(path.join(profileRoot, "editorial/tokens.dtcg.json"), tokenFile);
64
- const configFile = path.join(copiedRepository, "playwright.config.mjs");
65
- const config = fs.readFileSync(configFile, "utf8").replace("viewport: { height: 768, width: 1024 },", "deviceScaleFactor: 2,\n viewport: { height: 768, width: 1024 },");
66
- fs.writeFileSync(configFile, config);
67
- assert.equal(sourceManifestMatches(copied, copiedRepository, copiedProfiles), false, "deviceScaleFactor drift must invalidate the source manifest before it can double screenshot pixels");
68
-
69
- const copiedSchemas = path.join(tempRoot, "schemas");
70
- fs.cpSync(schemaRoot, copiedSchemas, { recursive: true });
71
- const captureSchema = path.join(copiedSchemas, "capture-session.schema.json");
72
- const schemaSource = fs.readFileSync(captureSchema, "utf8");
73
- fs.writeFileSync(captureSchema, schemaSource.replace('"title":', '"title": "shadowed title",\n "title":'));
74
- assert.throws(
75
- () => compileSchemas(copiedSchemas),
76
- (error) => error?.code === "component_schema_json_invalid" && error?.path === captureSchema,
77
- "duplicate component schema properties must fail strict compilation with a named error",
78
- );
79
- } finally {
80
- fs.rmSync(tempRoot, { force: true, recursive: true });
81
- }
82
-
83
- process.stdout.write(`${JSON.stringify({ files: first.files.length, ok: true, sha256: first.sha256 }, null, 2)}\n`);
@@ -1,165 +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 availableCases = new Map([
11
- ["overflow", { expected: "document_no_horizontal_overflow", grep: "@sentinel-overflow", mutation: "overflow" }],
12
- ["clipped-focus", { expected: "focus_geometry_visible", grep: "@sentinel-clipped-focus", mutation: "clipped-focus" }],
13
- ["low-contrast", { expected: "color-contrast", grep: "@sentinel-low-contrast", mutation: "low-contrast" }],
14
- ["dialog-focus-leak", { expected: "dialog_focus_trap", grep: "@sentinel-dialog-focus-leak", mutation: "dialog-focus-leak" }],
15
- ["console-error", { expected: "console_error_free", grep: "@sentinel-console-error", mutation: "console-error" }],
16
- ["page-evidence-symlink", { expected: "page_evidence_output_symlink", pageEvidence: "symlink" }],
17
- ["page-evidence-existing-output", { expected: "page_evidence_output_exists", pageEvidence: "existing-output" }],
18
- ["ci-cleanup-symlink", { ciCleanup: true, expected: ".tmp must be a real directory" }],
19
- ]);
20
- const selected = [];
21
- let json = false;
22
- const argumentFailures = [];
23
-
24
- for (let index = 2; index < process.argv.length; index += 1) {
25
- const argument = process.argv[index];
26
- if (argument === "--json") json = true;
27
- else if (argument === "--case") {
28
- const name = process.argv[index + 1];
29
- if (!name || name.startsWith("--")) argumentFailures.push("--case requires a value");
30
- else {
31
- selected.push(name);
32
- index += 1;
33
- }
34
- } else argumentFailures.push(`unsupported argument ${argument}`);
35
- }
36
-
37
- const names = selected.length > 0 ? [...new Set(selected)] : [...availableCases.keys()];
38
- for (const name of names) if (!availableCases.has(name)) argumentFailures.push(`unknown case ${name}`);
39
-
40
- function runPlaywright(grep, environment) {
41
- return spawnSync(
42
- path.join(repositoryRoot, "node_modules", ".bin", "playwright"),
43
- ["test", "tests/consumer-conformance.spec.mjs", "--project=chromium", "--reporter=line", "--grep", grep],
44
- {
45
- cwd: repositoryRoot,
46
- encoding: "utf8",
47
- env: { ...process.env, ...environment },
48
- maxBuffer: 4 * 1024 * 1024,
49
- },
50
- );
51
- }
52
-
53
- function runMutationControl(name, control) {
54
- const child = runPlaywright(control.grep, { CONSUMER_CONFORMANCE_MUTATION: control.mutation });
55
- const output = `${child.stdout}\n${child.stderr}`;
56
- return {
57
- actual: { named: output.includes(control.expected), status: child.status },
58
- expected: control.expected,
59
- name,
60
- ok: child.status !== 0 && output.includes(control.expected),
61
- };
62
- }
63
-
64
- function writeReceipt(root) {
65
- const receipt = {
66
- attempt: 1,
67
- intended_scenario_ids: ["responsive-layout"],
68
- nonce: "a".repeat(64),
69
- repository: "changeroa/StyleGallery",
70
- revision: "b".repeat(40),
71
- run_id: "consumer-conformance-sentinel-run",
72
- session_id: "consumer-conformance-sentinel-session",
73
- source: { sha256: "c".repeat(64) },
74
- };
75
- fs.writeFileSync(path.join(root, "page-evidence-session.json"), `${JSON.stringify(receipt)}\n`, { flag: "wx" });
76
- }
77
-
78
- function runPageEvidenceControl(name, control) {
79
- const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-page-evidence-sentinel-"));
80
- const observations = [];
81
- try {
82
- for (const outputKind of ["captures", "runner"]) {
83
- const caseRoot = path.join(temporaryRoot, outputKind);
84
- const artifactRoot = path.join(caseRoot, "artifacts");
85
- const outside = path.join(caseRoot, "outside");
86
- fs.mkdirSync(artifactRoot, { recursive: true });
87
- fs.mkdirSync(outside, { recursive: true });
88
- writeReceipt(artifactRoot);
89
-
90
- const filename = outputKind === "captures"
91
- ? "responsive-layout-state-w1024-focus.png"
92
- : "responsive-layout.json";
93
- const outputPath = path.join(artifactRoot, outputKind, filename);
94
- const outsidePath = path.join(outside, filename);
95
- const marker = "do-not-overwrite";
96
- if (control.pageEvidence === "symlink") fs.symlinkSync(outside, path.join(artifactRoot, outputKind), "dir");
97
- else {
98
- fs.mkdirSync(path.dirname(outputPath), { recursive: true });
99
- fs.writeFileSync(outputPath, marker, { flag: "wx" });
100
- }
101
-
102
- const child = runPlaywright("state-w1024-focus", {
103
- CONSUMER_CONFORMANCE_CAPTURE_DIR: "",
104
- CONSUMER_CONFORMANCE_MUTATION: "none",
105
- PAGE_EVIDENCE_ARTIFACT_ROOT: artifactRoot,
106
- PAGE_EVIDENCE_CASE_ID: "state-w1024-focus",
107
- PAGE_EVIDENCE_SCENARIO_ID: "responsive-layout",
108
- });
109
- const output = `${child.stdout}\n${child.stderr}`;
110
- observations.push({
111
- named: output.includes(control.expected),
112
- output_kind: outputKind,
113
- outside_write: fs.existsSync(outsidePath),
114
- preserved: control.pageEvidence === "existing-output" ? fs.readFileSync(outputPath, "utf8") === marker : true,
115
- status: child.status,
116
- });
117
- }
118
- } finally {
119
- fs.rmSync(temporaryRoot, { force: true, recursive: true });
120
- }
121
-
122
- return {
123
- actual: { observations },
124
- expected: control.expected,
125
- name,
126
- ok: observations.every((observation) => observation.status !== 0 && observation.named && !observation.outside_write && observation.preserved),
127
- };
128
- }
129
-
130
- function runCiCleanupControl(name, control) {
131
- const workspace = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-ci-cleanup-sentinel-"));
132
- const outside = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-ci-cleanup-outside-"));
133
- const marker = path.join(outside, "consumer", "preserve.txt");
134
- try {
135
- fs.mkdirSync(path.dirname(marker), { recursive: true });
136
- fs.writeFileSync(marker, "preserve\n", { flag: "wx" });
137
- fs.symlinkSync(outside, path.join(workspace, ".tmp"), "dir");
138
- const child = spawnSync(process.execPath, [path.join(repositoryRoot, "scripts", "run-consumer-page-evidence-ci.mjs"), "cleanup", "--consumer-root", ".tmp/consumer"], { cwd: workspace, encoding: "utf8" });
139
- const output = `${child.stdout}\n${child.stderr}`;
140
- return {
141
- actual: { markerPreserved: fs.readFileSync(marker, "utf8") === "preserve\n", named: output.includes(control.expected), status: child.status },
142
- expected: control.expected,
143
- name,
144
- ok: child.status !== 0 && output.includes(control.expected) && fs.readFileSync(marker, "utf8") === "preserve\n",
145
- };
146
- } finally {
147
- fs.rmSync(workspace, { force: true, recursive: true });
148
- fs.rmSync(outside, { force: true, recursive: true });
149
- }
150
- }
151
-
152
- const results = argumentFailures.length === 0 ? names.map((name) => {
153
- const control = availableCases.get(name);
154
- return control.pageEvidence ? runPageEvidenceControl(name, control) : control.ciCleanup ? runCiCleanupControl(name, control) : runMutationControl(name, control);
155
- }) : [];
156
-
157
- const failures = [
158
- ...argumentFailures.map((message) => `argument_invalid:${message}`),
159
- ...results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`),
160
- ];
161
- const report = { failures, ok: failures.length === 0, results };
162
- if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
163
- else if (report.ok) process.stdout.write(`consumer conformance sentinel passed ${results.length} mutation controls\n`);
164
- else process.stderr.write(`${failures.join("\n")}\n`);
165
- if (!report.ok) process.exitCode = 1;
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import path from "node:path";
4
- import { spawnSync } from "node:child_process";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
- const cases = [
9
- ["hidden_layout", "hidden-layout", "computed_layout_visible"],
10
- ["long_content_reflow", "long-content-reflow", "computed_long_content_reflow"],
11
- ];
12
-
13
- const results = cases.map(([name, mutation, expected]) => {
14
- const child = spawnSync(
15
- path.join(repositoryRoot, "node_modules", ".bin", "playwright"),
16
- ["test", "tests/consumer-reference-sentinels.spec.mjs", "--project=chromium", "--reporter=line"],
17
- { cwd: repositoryRoot, encoding: "utf8", env: { ...process.env, SENTINEL_MUTATION: mutation } },
18
- );
19
- const output = `${child.stdout}\n${child.stderr}`;
20
- return {
21
- actual: { named: output.includes(expected), status: child.status },
22
- expected,
23
- name,
24
- ok: child.status !== 0 && output.includes(expected),
25
- };
26
- });
27
-
28
- const failures = results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`);
29
- const report = { failures, ok: failures.length === 0, results };
30
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
31
- if (!report.ok) process.exitCode = 1;
@@ -1,124 +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
- import { fileURLToPath } from "node:url";
9
-
10
- const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- const sourceRoot = path.join(repositoryRoot, "design-engineering/reference-profiles/governed-local");
12
- const generator = path.join(repositoryRoot, "scripts/generate-consumer-reference-evidence.mjs");
13
-
14
- function run(root, check = false, environment = {}) {
15
- const args = [generator, "--root", root, "--json"];
16
- if (check) args.push("--check");
17
- const child = spawnSync(process.execPath, args, { cwd: repositoryRoot, encoding: "utf8", env: { ...process.env, ...environment } });
18
- let report = { failures: [], ok: false, parse_error: true };
19
- try { report = JSON.parse(child.stdout); } catch { report = { failures: [], ok: false, parse_error: true }; }
20
- return { report, status: child.status };
21
- }
22
-
23
- function editJson(file, mutate) {
24
- const value = JSON.parse(fs.readFileSync(file, "utf8"));
25
- mutate(value);
26
- fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
27
- }
28
-
29
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-pr5-generator-"));
30
- const results = [];
31
-
32
- function freshRoot(name) {
33
- const root = path.join(tempRoot, name);
34
- fs.cpSync(sourceRoot, root, { recursive: true });
35
- return root;
36
- }
37
-
38
- function expectFailure(name, expected, root) {
39
- const child = run(root, true);
40
- results.push({ expected, name, ok: child.status > 0 && child.report.failures.some((failure) => failure.code === expected) });
41
- }
42
-
43
- function mutateGenerated(name, expected, mutate) {
44
- const root = freshRoot(name);
45
- run(root);
46
- const file = path.join(root, "editorial/generated/state-matrix.md");
47
- const lines = fs.readFileSync(file, "utf8").split("\n");
48
- mutate(lines);
49
- fs.writeFileSync(file, lines.join("\n"));
50
- expectFailure(name, expected, root);
51
- }
52
-
53
- try {
54
- const canonicalRoot = freshRoot("canonical");
55
- const generated = run(canonicalRoot);
56
- results.push({ expected: "canonical_generation", name: "canonical", ok: generated.status === 0 && generated.report.ok && generated.report.scaffold !== true });
57
- const checked = run(canonicalRoot, true);
58
- results.push({ expected: "deterministic_zero_drift", name: "check", ok: checked.status === 0 && checked.report.ok && checked.report.scaffold !== true });
59
-
60
- mutateGenerated("stale", "generated_stale", (lines) => { lines.push("stale"); });
61
- mutateGenerated("generated_unsorted", "generated_unsorted", (lines) => {
62
- const first = lines.findIndex((line) => line.startsWith("| <code>"));
63
- [lines[first], lines[first + 1]] = [lines[first + 1], lines[first]];
64
- });
65
- mutateGenerated("generated_duplicate", "generated_duplicate_row", (lines) => {
66
- const first = lines.findIndex((line) => line.startsWith("| <code>"));
67
- lines.splice(first + 1, 0, lines[first]);
68
- });
69
- mutateGenerated("generated_missing", "generated_missing_row", (lines) => {
70
- const first = lines.findIndex((line) => line.startsWith("| <code>"));
71
- lines.splice(first, 1);
72
- });
73
- mutateGenerated("source_count", "generated_source_count_mismatch", (lines) => {
74
- const first = lines.findIndex((line) => line.startsWith("| <code>"));
75
- lines.splice(first, 1);
76
- });
77
-
78
- const unsortedRoot = freshRoot("source_unsorted");
79
- editJson(path.join(unsortedRoot, "editorial/states/button.states.json"), (value) => { value.scenarios.reverse(); });
80
- expectFailure("source_unsorted", "source_unsorted", unsortedRoot);
81
-
82
- const duplicateRoot = freshRoot("scenario_duplicate");
83
- editJson(path.join(duplicateRoot, "editorial/states/button.states.json"), (value) => { value.scenarios.push({ ...value.scenarios[0] }); });
84
- expectFailure("scenario_duplicate", "scenario_duplicate", duplicateRoot);
85
-
86
- const missingRoot = freshRoot("fixture_scenario_missing");
87
- editJson(path.join(missingRoot, "editorial/states/button.states.json"), (value) => { value.scenarios = value.scenarios.filter((scenario) => scenario.id !== "action-focused"); });
88
- expectFailure("fixture_scenario_missing", "fixture_scenario_missing", missingRoot);
89
-
90
- const injectionRoot = freshRoot("markdown_injection");
91
- editJson(path.join(injectionRoot, "editorial/components/button.component.json"), (value) => { value.semantic_modes[0].element = "<script>alert(1)</script>|`\nnext"; });
92
- const injection = run(injectionRoot);
93
- const injectionOutput = fs.readFileSync(path.join(injectionRoot, "editorial/generated/keyboard-matrix.md"), "utf8");
94
- results.push({
95
- expected: "escaped_markdown_injection",
96
- name: "markdown_injection",
97
- ok: injection.status === 0
98
- && injectionOutput.includes("&lt;script&gt;alert(1)&lt;/script&gt;&#124;&#96;<br>next")
99
- && !injectionOutput.includes("<script>alert(1)</script>"),
100
- });
101
-
102
- const referenceRoot = freshRoot("canonical_reference_discovery");
103
- fs.copyFileSync(path.join(referenceRoot, "editorial/components/button.component.json"), path.join(referenceRoot, "editorial/components/alternate.component.json"));
104
- editJson(path.join(referenceRoot, "editorial/components/alternate.component.json"), (value) => { value.semantic_modes[0].element = "canonical alternate element"; });
105
- editJson(path.join(referenceRoot, "editorial/profile.json"), (value) => { value.component_records = ["components/alternate.component.json"]; });
106
- editJson(path.join(referenceRoot, "editorial/fixtures/button.fixture.json"), (value) => { value.component_record = "../components/alternate.component.json"; });
107
- const reference = run(referenceRoot);
108
- const referenceOutput = fs.readFileSync(path.join(referenceRoot, "editorial/generated/keyboard-matrix.md"), "utf8");
109
- results.push({ expected: "canonical_profile_reference", name: "canonical_reference_discovery", ok: reference.status === 0 && referenceOutput.includes("canonical alternate element") });
110
-
111
- const localeRoot = freshRoot("locale_determinism");
112
- const firstLocale = run(localeRoot, false, { LANG: "C", LC_ALL: "C" });
113
- const firstBytes = fs.readFileSync(path.join(localeRoot, "editorial/generated/state-matrix.md"));
114
- const secondLocale = run(localeRoot, false, { LANG: "tr_TR.UTF-8", LC_ALL: "tr_TR.UTF-8" });
115
- const secondBytes = fs.readFileSync(path.join(localeRoot, "editorial/generated/state-matrix.md"));
116
- results.push({ expected: "locale_independent_bytes", name: "locale_determinism", ok: firstLocale.status === 0 && secondLocale.status === 0 && firstBytes.equals(secondBytes) });
117
- } finally {
118
- fs.rmSync(tempRoot, { recursive: true, force: true });
119
- }
120
-
121
- const failures = results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`);
122
- const report = { failures, ok: failures.length === 0, results };
123
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
124
- assert.equal(report.ok, true);