stylegallery 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/AGENTS.md +156 -0
  2. package/CATALOG.md +69 -0
  3. package/DOMAINS.md +65 -0
  4. package/GOVERNANCE.md +222 -0
  5. package/GUIDE.md +59 -0
  6. package/README.md +313 -0
  7. package/consumer-reference/adapters/style-dictionary.config.mjs +36 -0
  8. package/consumer-reference/agent-native/README.md +157 -0
  9. package/consumer-reference/agent-native/registry.json +640 -0
  10. package/consumer-reference/agent-native/schema/agent-native.schema.json +848 -0
  11. package/consumer-reference/agent-native/schema/epistemic.schema.json +14 -0
  12. package/consumer-reference/agent-native/schema/execution.schema.json +12 -0
  13. package/consumer-reference/agent-native/schema/identity.schema.json +10 -0
  14. package/consumer-reference/agent-native/schema/learning.schema.json +12 -0
  15. package/consumer-reference/agent-native/schema/manifest.schema.json +6 -0
  16. package/consumer-reference/agent-native/schema/operation.schema.json +12 -0
  17. package/consumer-reference/agent-native/schema/protocol-binding.schema.json +9 -0
  18. package/consumer-reference/agent-native/schema/retrieval.schema.json +10 -0
  19. package/consumer-reference/baselines/calibration.json +260 -0
  20. package/consumer-reference/baselines/manifest.json +21 -0
  21. package/consumer-reference/contract.md +83 -0
  22. package/consumer-reference/fixtures/consumer-conformance/e2e-fixture.mjs +282 -0
  23. package/consumer-reference/fixtures/consumer-conformance/manifest.json +102 -0
  24. package/consumer-reference/fixtures/consumer-conformance/runtime-proof.mjs +32 -0
  25. package/consumer-reference/fixtures/consumer-conformance/valid-migration-result.json +18 -0
  26. package/consumer-reference/fixtures/consumer-conformance/valid-migration.json +142 -0
  27. package/consumer-reference/fixtures/invalid-stable-ended-support.json +19 -0
  28. package/consumer-reference/fixtures/promotion/invalid-blocking-evidence.json +26 -0
  29. package/consumer-reference/fixtures/promotion/invalid-count-only.json +29 -0
  30. package/consumer-reference/fixtures/promotion/invalid-missing-owner-migration.json +25 -0
  31. package/consumer-reference/fixtures/promotion/invalid-normative-bypass-no-regression.json +25 -0
  32. package/consumer-reference/fixtures/promotion/invalid-related-counted-independent.json +45 -0
  33. package/consumer-reference/fixtures/promotion/invalid-stable-ended-support.json +26 -0
  34. package/consumer-reference/fixtures/promotion/manifest.json +15 -0
  35. package/consumer-reference/fixtures/promotion/valid-deferred-example.json +67 -0
  36. package/consumer-reference/fixtures/promotion/valid-normative-bypass.json +67 -0
  37. package/consumer-reference/fixtures/token-portability/invalid-cycle.json +7 -0
  38. package/consumer-reference/fixtures/token-portability/invalid-dangling-alias.json +6 -0
  39. package/consumer-reference/fixtures/token-portability/invalid-extends.json +4 -0
  40. package/consumer-reference/fixtures/token-portability/invalid-json-pointer.json +7 -0
  41. package/consumer-reference/fixtures/token-portability/invalid-resolver.json +9 -0
  42. package/consumer-reference/fixtures/token-portability/invalid-type-mismatch.json +10 -0
  43. package/consumer-reference/fixtures/token-portability/invalid-unknown-reserved.json +6 -0
  44. package/consumer-reference/fixtures/token-portability/invalid-untested-type.json +6 -0
  45. package/consumer-reference/fixtures/token-portability/valid-border.json +14 -0
  46. package/consumer-reference/fixtures/token-portability/valid-reference.json +32 -0
  47. package/consumer-reference/fixtures/token-portability/valid-whole-alias.json +7 -0
  48. package/consumer-reference/fixtures/valid-deprecated.json +21 -0
  49. package/consumer-reference/fixtures/valid-experimental.json +19 -0
  50. package/consumer-reference/generated/manifest.json +40 -0
  51. package/consumer-reference/generated/tokens.css +12 -0
  52. package/consumer-reference/index.md +32 -0
  53. package/consumer-reference/policies/shared-experimental.json +37 -0
  54. package/consumer-reference/schema/ax-evidence.schema.json +30 -0
  55. package/consumer-reference/schema/baseline-manifest.schema.json +55 -0
  56. package/consumer-reference/schema/calibration-record.schema.json +190 -0
  57. package/consumer-reference/schema/capture-session.schema.json +161 -0
  58. package/consumer-reference/schema/component-state.schema.json +150 -0
  59. package/consumer-reference/schema/consumer-conformance-record.schema.json +365 -0
  60. package/consumer-reference/schema/dom-evidence.schema.json +38 -0
  61. package/consumer-reference/schema/evidence-record.schema.json +81 -0
  62. package/consumer-reference/schema/fixture-manifest.schema.json +32 -0
  63. package/consumer-reference/schema/item.schema.json +188 -0
  64. package/consumer-reference/schema/page-evidence-manifest.schema.json +216 -0
  65. package/consumer-reference/schema/page-evidence-session.schema.json +119 -0
  66. package/consumer-reference/schema/portable-tokens.schema.json +104 -0
  67. package/consumer-reference/schema/promotion-rfc.schema.json +219 -0
  68. package/consumer-reference/schema/runtime-evidence-manifest.schema.json +33 -0
  69. package/consumer-reference/schema/visual-evidence.schema.json +31 -0
  70. package/design-engineering/consumer-migration-readiness.md +124 -0
  71. package/design-engineering/index.md +28 -0
  72. package/design-engineering/interface-craft.md +107 -0
  73. package/design-engineering/reference-profiles/external-adaptation/index.md +12 -0
  74. package/design-engineering/reference-profiles/governed-local/editorial/components/button.component.json +125 -0
  75. package/design-engineering/reference-profiles/governed-local/editorial/evidence/button.evidence.json +3504 -0
  76. package/design-engineering/reference-profiles/governed-local/editorial/fixtures/button.fixture.json +65 -0
  77. package/design-engineering/reference-profiles/governed-local/editorial/generated/evidence-coverage.md +23 -0
  78. package/design-engineering/reference-profiles/governed-local/editorial/generated/keyboard-matrix.md +19 -0
  79. package/design-engineering/reference-profiles/governed-local/editorial/generated/state-matrix.md +21 -0
  80. package/design-engineering/reference-profiles/governed-local/editorial/local-foundations.json +12 -0
  81. package/design-engineering/reference-profiles/governed-local/editorial/profile.json +43 -0
  82. package/design-engineering/reference-profiles/governed-local/editorial/states/button.states.json +195 -0
  83. package/design-engineering/reference-profiles/governed-local/editorial/tokens.dtcg.json +17 -0
  84. package/design-engineering/reference-profiles/governed-local/index.md +26 -0
  85. package/design-engineering/reference-profiles/governed-local/terminal/components/button.component.json +125 -0
  86. package/design-engineering/reference-profiles/governed-local/terminal/evidence/button.evidence.json +3504 -0
  87. package/design-engineering/reference-profiles/governed-local/terminal/fixtures/button.fixture.json +65 -0
  88. package/design-engineering/reference-profiles/governed-local/terminal/generated/evidence-coverage.md +23 -0
  89. package/design-engineering/reference-profiles/governed-local/terminal/generated/keyboard-matrix.md +19 -0
  90. package/design-engineering/reference-profiles/governed-local/terminal/generated/state-matrix.md +21 -0
  91. package/design-engineering/reference-profiles/governed-local/terminal/local-foundations.json +12 -0
  92. package/design-engineering/reference-profiles/governed-local/terminal/profile.json +43 -0
  93. package/design-engineering/reference-profiles/governed-local/terminal/states/button.states.json +195 -0
  94. package/design-engineering/reference-profiles/governed-local/terminal/tokens.dtcg.json +17 -0
  95. package/design-engineering/reference-profiles/index.md +19 -0
  96. package/game-ui/classification.md +82 -0
  97. package/game-ui/index.md +35 -0
  98. package/game-ui/reference-record.md +62 -0
  99. package/game-ui/screen-hierarchy.md +107 -0
  100. package/game-ui/unity/architecture.md +166 -0
  101. package/game-ui/unity/cli-loop.md +84 -0
  102. package/game-ui/unity/data/unity-technologies-public-repositories.json +26654 -0
  103. package/game-ui/unity/org-wiki.md +146 -0
  104. package/game-ui/unity/repository-map.md +102 -0
  105. package/game-ui/unity/ui-systems.md +85 -0
  106. package/guides/decision-tree.md +149 -0
  107. package/guides/documentation-mode-taxonomy.md +157 -0
  108. package/guides/layout-brief.md +220 -0
  109. package/guides/vocabulary.md +148 -0
  110. package/guides/webpage-generation-workflow.md +185 -0
  111. package/index.md +36 -0
  112. package/layout/index.md +26 -0
  113. package/motion/index.md +24 -0
  114. package/motion/practice-reference.md +145 -0
  115. package/motion/review-workflow.md +86 -0
  116. package/motion/vocabulary.md +82 -0
  117. package/package.json +78 -0
  118. package/patterns/centering/center.md +87 -0
  119. package/patterns/centering/index.md +6 -0
  120. package/patterns/centering/super-center.md +89 -0
  121. package/patterns/containment/box.md +87 -0
  122. package/patterns/containment/clamped-card.md +86 -0
  123. package/patterns/containment/content-limiter.md +87 -0
  124. package/patterns/containment/fluid-styles.md +86 -0
  125. package/patterns/containment/index.md +8 -0
  126. package/patterns/grid-repetition/card-grid.md +88 -0
  127. package/patterns/grid-repetition/columns.md +87 -0
  128. package/patterns/grid-repetition/dense-grid.md +88 -0
  129. package/patterns/grid-repetition/grid-wrapper.md +89 -0
  130. package/patterns/grid-repetition/index.md +12 -0
  131. package/patterns/grid-repetition/masonry-approx.md +87 -0
  132. package/patterns/grid-repetition/page-grid.md +90 -0
  133. package/patterns/grid-repetition/ram-grid.md +88 -0
  134. package/patterns/grid-repetition/twelve-span-grid.md +92 -0
  135. package/patterns/in-line-grouping/badge-list.md +94 -0
  136. package/patterns/in-line-grouping/breadcrumb.md +89 -0
  137. package/patterns/in-line-grouping/cluster.md +89 -0
  138. package/patterns/in-line-grouping/deconstructed-pancake.md +92 -0
  139. package/patterns/in-line-grouping/index.md +13 -0
  140. package/patterns/in-line-grouping/pagination.md +89 -0
  141. package/patterns/in-line-grouping/reel.md +90 -0
  142. package/patterns/in-line-grouping/split-nav.md +97 -0
  143. package/patterns/in-line-grouping/tab-strip.md +88 -0
  144. package/patterns/in-line-grouping/wrap-row.md +89 -0
  145. package/patterns/index.md +13 -0
  146. package/patterns/media-fit/frame.md +95 -0
  147. package/patterns/media-fit/icon-frame.md +87 -0
  148. package/patterns/media-fit/index.md +6 -0
  149. package/patterns/overlay-exception/imposter.md +95 -0
  150. package/patterns/overlay-exception/index.md +6 -0
  151. package/patterns/overlay-exception/overlay-stack.md +89 -0
  152. package/patterns/split-sidebar/index.md +12 -0
  153. package/patterns/split-sidebar/list-detail.md +95 -0
  154. package/patterns/split-sidebar/main-with-rail.md +95 -0
  155. package/patterns/split-sidebar/media-object.md +102 -0
  156. package/patterns/split-sidebar/sidebar.md +101 -0
  157. package/patterns/split-sidebar/split-screen.md +92 -0
  158. package/patterns/split-sidebar/sticky-aside.md +97 -0
  159. package/patterns/split-sidebar/supporting-pane.md +95 -0
  160. package/patterns/split-sidebar/switcher.md +93 -0
  161. package/patterns/stacking/feed.md +88 -0
  162. package/patterns/stacking/index.md +8 -0
  163. package/patterns/stacking/line-up.md +95 -0
  164. package/patterns/stacking/stack.md +87 -0
  165. package/patterns/stacking/step-nav.md +88 -0
  166. package/patterns/viewport-shell/cover.md +90 -0
  167. package/patterns/viewport-shell/fixed-sidenav-shell.md +97 -0
  168. package/patterns/viewport-shell/holy-grail.md +112 -0
  169. package/patterns/viewport-shell/index.md +11 -0
  170. package/patterns/viewport-shell/panel-layout.md +95 -0
  171. package/patterns/viewport-shell/scroll-body-shell.md +93 -0
  172. package/patterns/viewport-shell/sticky-footer.md +92 -0
  173. package/patterns/viewport-shell/sticky-header.md +86 -0
  174. package/platform-guides/apple-interaction.md +89 -0
  175. package/platform-guides/index.md +26 -0
  176. package/quality/claim-records/index.md +3 -0
  177. package/quality/claim-records/samples.md +92 -0
  178. package/quality/claim-records/stylegallery-multidomain-scope.md +42 -0
  179. package/quality/claims.md +77 -0
  180. package/quality/evidence/accessibility.md +58 -0
  181. package/quality/evidence/consumer-migration.md +42 -0
  182. package/quality/evidence/design-rationale.md +34 -0
  183. package/quality/evidence/executable-evidence.md +79 -0
  184. package/quality/evidence/families.md +56 -0
  185. package/quality/evidence/hci-evaluation.md +36 -0
  186. package/quality/evidence/index.md +16 -0
  187. package/quality/evidence/pattern-contract-audit.md +96 -0
  188. package/quality/evidence/search-metadata-polyhierarchy.md +86 -0
  189. package/quality/evidence/sources.md +43 -0
  190. package/quality/evidence/tokens.md +33 -0
  191. package/quality/evidence/visual-qa.md +41 -0
  192. package/quality/gates/accessibility-evidence.md +60 -0
  193. package/quality/gates/consumer-migration-evidence.md +53 -0
  194. package/quality/gates/design-claim.md +56 -0
  195. package/quality/gates/harmony-evaluation.md +78 -0
  196. package/quality/gates/index.md +13 -0
  197. package/quality/gates/layout.md +52 -0
  198. package/quality/gates/rationale.md +53 -0
  199. package/quality/gates/visual-evidence.md +47 -0
  200. package/quality/index.md +55 -0
  201. package/quality/principles.md +58 -0
  202. package/recipes/article-page.md +52 -0
  203. package/recipes/command-surface.md +55 -0
  204. package/recipes/dashboard.md +56 -0
  205. package/recipes/form-flow.md +55 -0
  206. package/recipes/homepage.md +89 -0
  207. package/recipes/index.md +13 -0
  208. package/recipes/list-detail.md +55 -0
  209. package/recipes/primitive-to-recipe-matrix.md +83 -0
  210. package/recipes/saas-settings.md +52 -0
  211. package/scripts/agent-native/a2a-projection.mjs +248 -0
  212. package/scripts/agent-native/agui-projection.mjs +178 -0
  213. package/scripts/agent-native/canonical-json.mjs +109 -0
  214. package/scripts/agent-native/cli-adapter.mjs +68 -0
  215. package/scripts/agent-native/events.mjs +113 -0
  216. package/scripts/agent-native/execution.mjs +227 -0
  217. package/scripts/agent-native/fixture.mjs +159 -0
  218. package/scripts/agent-native/identity.mjs +184 -0
  219. package/scripts/agent-native/immutable-store.mjs +102 -0
  220. package/scripts/agent-native/knowledge.mjs +168 -0
  221. package/scripts/agent-native/learning-records.mjs +244 -0
  222. package/scripts/agent-native/learning.mjs +247 -0
  223. package/scripts/agent-native/mcp-adapter.mjs +173 -0
  224. package/scripts/agent-native/queries.mjs +173 -0
  225. package/scripts/agent-native/registry.mjs +154 -0
  226. package/scripts/agent-native/retrieval.mjs +171 -0
  227. package/scripts/agent-native/self-description.mjs +160 -0
  228. package/scripts/artifact-metadata.mjs +20 -0
  229. package/scripts/audit-evidence-freshness.mjs +308 -0
  230. package/scripts/baseline-contract.mjs +189 -0
  231. package/scripts/baseline-schema-parity.mjs +40 -0
  232. package/scripts/build-reference-artifacts.mjs +127 -0
  233. package/scripts/calibration-raw-contract.mjs +172 -0
  234. package/scripts/capture-session-contract.mjs +187 -0
  235. package/scripts/component-state-artifact-fixture.mjs +44 -0
  236. package/scripts/component-state-contract.mjs +99 -0
  237. package/scripts/component-state-semantics.mjs +128 -0
  238. package/scripts/component-state-workflow-contract.mjs +67 -0
  239. package/scripts/component-state-workflow-negative-cases.mjs +64 -0
  240. package/scripts/consumer-conformance-contract.mjs +198 -0
  241. package/scripts/consumer-evidence-governance-contract.mjs +210 -0
  242. package/scripts/consumer-reference-case-runner.mjs +93 -0
  243. package/scripts/consumer-reference-schema.mjs +155 -0
  244. package/scripts/create-component-state-session.mjs +100 -0
  245. package/scripts/create-page-evidence-session.mjs +139 -0
  246. package/scripts/domain-document-boundaries.mjs +40 -0
  247. package/scripts/domain-validation-checks.mjs +141 -0
  248. package/scripts/evidence-artifact-contract.mjs +191 -0
  249. package/scripts/finalize-component-state-evidence.mjs +165 -0
  250. package/scripts/finalize-page-evidence.mjs +197 -0
  251. package/scripts/generate-consumer-reference-evidence.mjs +154 -0
  252. package/scripts/generate-patterns.mjs +218 -0
  253. package/scripts/governance-matrix-contract.mjs +73 -0
  254. package/scripts/governance-matrix-negative-cases.mjs +114 -0
  255. package/scripts/governance-policy-contract.mjs +49 -0
  256. package/scripts/governance-test-fixture.mjs +321 -0
  257. package/scripts/governance-workflow-negative-cases.mjs +241 -0
  258. package/scripts/governed-profile-registry.mjs +125 -0
  259. package/scripts/json-schema-formats.mjs +31 -0
  260. package/scripts/markdown-structure.mjs +45 -0
  261. package/scripts/page-artifact-metadata.mjs +124 -0
  262. package/scripts/page-evidence-contract.mjs +297 -0
  263. package/scripts/page-evidence-fixture.mjs +271 -0
  264. package/scripts/pattern-data.mjs +325 -0
  265. package/scripts/profile-record-contract.mjs +140 -0
  266. package/scripts/promotion-attestation-contract.mjs +91 -0
  267. package/scripts/promotion-boundary-test-contract.mjs +92 -0
  268. package/scripts/promotion-fixture-inventory.mjs +109 -0
  269. package/scripts/promotion-governance-contract.mjs +28 -0
  270. package/scripts/reference-artifact-path-contract.mjs +28 -0
  271. package/scripts/reference-profile-contract.mjs +114 -0
  272. package/scripts/reference-token-contract.mjs +156 -0
  273. package/scripts/run-consumer-page-evidence-ci.mjs +170 -0
  274. package/scripts/sg-mcp.mjs +16 -0
  275. package/scripts/sg.mjs +27 -0
  276. package/scripts/strict-json.mjs +72 -0
  277. package/scripts/summarize-sentinel-calibration.mjs +98 -0
  278. package/scripts/test-agent-adapter-conformance.mjs +250 -0
  279. package/scripts/test-agent-conformance-receipt.mjs +106 -0
  280. package/scripts/test-agent-events.mjs +81 -0
  281. package/scripts/test-agent-execution-effects.mjs +114 -0
  282. package/scripts/test-agent-execution-fixtures.mjs +140 -0
  283. package/scripts/test-agent-execution-kernel.mjs +39 -0
  284. package/scripts/test-agent-execution-learning.mjs +130 -0
  285. package/scripts/test-agent-execution-lifecycle.mjs +143 -0
  286. package/scripts/test-agent-execution-safety.mjs +158 -0
  287. package/scripts/test-agent-execution-views.mjs +117 -0
  288. package/scripts/test-agent-identity-integrity-cases.mjs +160 -0
  289. package/scripts/test-agent-knowledge-kernel.mjs +183 -0
  290. package/scripts/test-agent-learning.mjs +246 -0
  291. package/scripts/test-agent-mcp.mjs +246 -0
  292. package/scripts/test-agent-native-schemas.mjs +215 -0
  293. package/scripts/test-agent-projections.mjs +249 -0
  294. package/scripts/test-agent-registry-mcp-integrity.mjs +215 -0
  295. package/scripts/test-agent-registry-mutations.mjs +110 -0
  296. package/scripts/test-audit-evidence-freshness.mjs +177 -0
  297. package/scripts/test-component-state-sentinel.mjs +35 -0
  298. package/scripts/test-component-state-source-contract.mjs +83 -0
  299. package/scripts/test-consumer-conformance-sentinel.mjs +165 -0
  300. package/scripts/test-consumer-reference-sentinel.mjs +31 -0
  301. package/scripts/test-generate-consumer-reference-evidence.mjs +124 -0
  302. package/scripts/test-json-cli-output.mjs +151 -0
  303. package/scripts/test-json-schema-formats.mjs +40 -0
  304. package/scripts/test-reference-adapters.mjs +199 -0
  305. package/scripts/test-renderer-purity.mjs +34 -0
  306. package/scripts/test-sg-cli.mjs +129 -0
  307. package/scripts/test-summarize-sentinel-calibration.mjs +226 -0
  308. package/scripts/test-validate-baseline-manifest.mjs +234 -0
  309. package/scripts/test-validate-component-state-artifacts.mjs +243 -0
  310. package/scripts/test-validate-component-state.mjs +106 -0
  311. package/scripts/test-validate-consumer-conformance.mjs +427 -0
  312. package/scripts/test-validate-consumer-reference-cli.mjs +69 -0
  313. package/scripts/test-validate-consumer-reference.mjs +263 -0
  314. package/scripts/test-validate-domains.mjs +357 -0
  315. package/scripts/test-validate-governance.mjs +292 -0
  316. package/scripts/test-validate-ia.mjs +262 -0
  317. package/scripts/test-validate-okf.mjs +73 -0
  318. package/scripts/test-validate-page-evidence.mjs +302 -0
  319. package/scripts/test-validate-patterns.mjs +157 -0
  320. package/scripts/test-validate-promotion-rfc.mjs +250 -0
  321. package/scripts/test-validate-reference-profiles.mjs +170 -0
  322. package/scripts/test-validate-unity-org-wiki.mjs +57 -0
  323. package/scripts/test-validate-unity-source-contracts.mjs +67 -0
  324. package/scripts/test-validate-vocabulary.mjs +174 -0
  325. package/scripts/test-validate-webpage-workflow.mjs +293 -0
  326. package/scripts/validate-baseline-manifest.mjs +44 -0
  327. package/scripts/validate-catalog.mjs +65 -0
  328. package/scripts/validate-component-state.mjs +141 -0
  329. package/scripts/validate-consumer-conformance.mjs +477 -0
  330. package/scripts/validate-consumer-reference.mjs +288 -0
  331. package/scripts/validate-domains.mjs +237 -0
  332. package/scripts/validate-governance.mjs +248 -0
  333. package/scripts/validate-ia.mjs +136 -0
  334. package/scripts/validate-links.mjs +66 -0
  335. package/scripts/validate-okf.mjs +104 -0
  336. package/scripts/validate-page-evidence.mjs +191 -0
  337. package/scripts/validate-patterns.mjs +246 -0
  338. package/scripts/validate-promotion-rfc.mjs +224 -0
  339. package/scripts/validate-reference-artifacts.mjs +125 -0
  340. package/scripts/validate-renderer-purity.mjs +56 -0
  341. package/scripts/validate-unity-org-wiki.mjs +146 -0
  342. package/scripts/validate-unity-source-contracts.mjs +107 -0
  343. package/scripts/validate-vocabulary.mjs +115 -0
  344. package/scripts/validate-webpage-workflow.mjs +254 -0
  345. package/scripts/visual-expectation-contract.mjs +20 -0
  346. package/scripts/workflow-action-contract.mjs +163 -0
@@ -0,0 +1,226 @@
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 { BASELINE_ENVIRONMENT, BASELINE_REFERENCE, sha256 } from "./baseline-contract.mjs";
9
+
10
+ const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
+ const summarizer = path.join(repositoryRoot, "scripts", "summarize-sentinel-calibration.mjs");
12
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-pr4-summary-"));
13
+ const png = fs.readFileSync(path.join(repositoryRoot, BASELINE_REFERENCE.baseline.path));
14
+ const dom = Buffer.from("<section class=card_grid></section>\n");
15
+ const ax = Buffer.from("- region \"Project cards\"\n");
16
+ const pngHash = sha256(png);
17
+ const metadataHash = sha256(JSON.stringify({ environment: BASELINE_ENVIRONMENT, reference: BASELINE_REFERENCE }));
18
+
19
+ function playwrightReport() {
20
+ return {
21
+ config: { projects: [{ id: "chromium", name: "chromium" }], version: "1.61.0", workers: 1 },
22
+ errors: [],
23
+ stats: { expected: 1, flaky: 0, skipped: 0, unexpected: 0 },
24
+ suites: [{
25
+ file: "consumer-reference-sentinels.spec.mjs",
26
+ specs: [{
27
+ ok: true,
28
+ tests: [{
29
+ expectedStatus: "passed",
30
+ projectId: "chromium",
31
+ projectName: "chromium",
32
+ results: [{ errors: [], status: "passed" }],
33
+ status: "expected",
34
+ }],
35
+ title: "canonical consumer reference preserves computed layout before its locator screenshot",
36
+ }],
37
+ }],
38
+ };
39
+ }
40
+
41
+ function writeJson(file, value) {
42
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
43
+ }
44
+
45
+ function writeRun(root, run) {
46
+ const runRoot = path.join(root, `run-${String(run).padStart(2, "0")}`);
47
+ fs.mkdirSync(runRoot, { recursive: true });
48
+ fs.writeFileSync(path.join(runRoot, "actual.png"), png);
49
+ fs.writeFileSync(path.join(runRoot, "dom.html"), dom);
50
+ fs.writeFileSync(path.join(runRoot, "ax.txt"), ax);
51
+ writeJson(path.join(runRoot, "playwright.json"), playwrightReport());
52
+ writeJson(path.join(runRoot, "exit.json"), { exit_code: 0, run, schema_version: "1.0" });
53
+ writeJson(path.join(runRoot, "comparison.json"), {
54
+ actual_sha256: pngHash,
55
+ assertion: "visual_geometry_matches_proposed_baseline",
56
+ diff_pixels: 0,
57
+ expected_sha256: BASELINE_REFERENCE.baseline.sha256,
58
+ max_diff_pixels: 0,
59
+ run,
60
+ schema_version: "1.0",
61
+ status: "passed",
62
+ threshold: 0,
63
+ });
64
+ writeJson(path.join(runRoot, "metadata.json"), {
65
+ architecture: "amd64",
66
+ ax_sha256: sha256(ax),
67
+ dom_sha256: sha256(dom),
68
+ environment: BASELINE_ENVIRONMENT,
69
+ metadata_sha256: metadataHash,
70
+ png_sha256: pngHash,
71
+ reference: BASELINE_REFERENCE,
72
+ run,
73
+ schema_version: "1.0",
74
+ });
75
+ return runRoot;
76
+ }
77
+
78
+ function invoke(name, mutate, identity = {}) {
79
+ const input = path.join(tempRoot, name, "raw");
80
+ const outputFile = path.join(input, "calibration.json");
81
+ for (let run = 1; run <= 20; run += 1) writeRun(input, run);
82
+ mutate?.(path.join(input, "run-01"));
83
+ const sha = identity.sha ?? "0".repeat(40);
84
+ const checkoutSha = identity.checkoutSha ?? sha;
85
+ const executionRepository = identity.executionRepository ?? "changeroa/StyleGallery";
86
+ const headSha = identity.headSha ?? "1".repeat(40);
87
+ const repository = identity.repository ?? "changeroa/StyleGallery";
88
+ const runId = identity.runId ?? "123";
89
+ const artifactName = identity.artifactName ?? `chromium-sentinel-calibration-${runId}-${sha}`;
90
+ const child = spawnSync(process.execPath, [
91
+ summarizer,
92
+ "--input", input,
93
+ "--output", outputFile,
94
+ "--repository", repository,
95
+ "--execution-repository", executionRepository,
96
+ "--workflow", ".github/workflows/validate.yml",
97
+ "--run-id", runId,
98
+ "--run-attempt", "1",
99
+ "--sha", sha,
100
+ "--checkout-sha", checkoutSha,
101
+ "--head-sha", headSha,
102
+ "--artifact-name", artifactName,
103
+ "--json",
104
+ ], { cwd: repositoryRoot, encoding: "utf8" });
105
+ return {
106
+ output: fs.existsSync(outputFile) && fs.lstatSync(outputFile).isFile() ? JSON.parse(fs.readFileSync(outputFile, "utf8")) : null,
107
+ report: JSON.parse(child.stdout),
108
+ status: child.status,
109
+ };
110
+ }
111
+
112
+ const results = [];
113
+ try {
114
+ const valid = invoke("valid");
115
+ results.push({
116
+ actual: { outputRuns: valid.output?.runs.length, report: valid.report, status: valid.status },
117
+ expected: "20 truth-derived stable runs awaiting external verification and zero diff statistics",
118
+ name: "valid_raw_calibration",
119
+ ok: valid.status === 0 && valid.report.ok === true && valid.report.status === "awaiting_external_verification"
120
+ && valid.output?.status === "awaiting_external_verification"
121
+ && valid.output?.committed_ci.external_verification === null
122
+ && valid.output?.runs.length === 20 && new Set(valid.output.runs.map((run) => run.png_sha256)).size === 1,
123
+ });
124
+ const pullRequestMergeSha = "11f4668fe5988720c27e88ec7203ecd1685a40df";
125
+ const pullRequestHeadSha = "8b8eaed41094286138973157b581a1d9ab9957a8";
126
+ const validFork = invoke("valid_explicit_fork", undefined, {
127
+ executionRepository: "ark-jo/StyleGallery",
128
+ headSha: pullRequestHeadSha,
129
+ runId: "29258810962",
130
+ sha: pullRequestMergeSha,
131
+ });
132
+ results.push({
133
+ actual: { committedCi: validFork.output?.committed_ci, report: validFork.report, status: validFork.status },
134
+ expected: "canonical upstream plus explicit execution fork with distinct merge/head SHAs",
135
+ name: "valid_explicit_fork_pull_request_identity",
136
+ ok: validFork.status === 0 && validFork.report.ok === true
137
+ && validFork.report.status === "awaiting_external_verification"
138
+ && validFork.output?.status === "awaiting_external_verification"
139
+ && validFork.output?.committed_ci.external_verification === null
140
+ && validFork.output?.committed_ci.repository === "changeroa/StyleGallery"
141
+ && validFork.output?.committed_ci.execution_repository === "ark-jo/StyleGallery"
142
+ && validFork.output?.committed_ci.sha === pullRequestMergeSha
143
+ && validFork.output?.committed_ci.checkout_sha === pullRequestMergeSha
144
+ && validFork.output?.committed_ci.head_sha === pullRequestHeadSha,
145
+ });
146
+ const invalidCases = [
147
+ ["missing_exit", (root) => fs.rmSync(path.join(root, "exit.json")), "calibration_raw_file_missing"],
148
+ ["nonzero_exit", (root) => writeJson(path.join(root, "exit.json"), { exit_code: 1, run: 1, schema_version: "1.0" }), "calibration_exit_unsuccessful"],
149
+ ["fake_playwright", (root) => writeJson(path.join(root, "playwright.json"), {}), "calibration_playwright_identity"],
150
+ ["duplicate_metadata", (root) => fs.copyFileSync(path.join(root, "metadata.json"), path.join(root, "metadata-copy.json")), "calibration_raw_file_unknown"],
151
+ ["unknown_metadata", (root) => {
152
+ const file = path.join(root, "metadata.json");
153
+ const metadata = JSON.parse(fs.readFileSync(file, "utf8"));
154
+ writeJson(file, { ...metadata, completed: true });
155
+ }, "calibration_metadata_property_unknown"],
156
+ ["duplicate_metadata_property", (root) => {
157
+ const file = path.join(root, "metadata.json");
158
+ const metadata = fs.readFileSync(file, "utf8").replace('"schema_version": "1.0"', '"schema_version": "1.0",\n "schema_version": "1.0"');
159
+ fs.writeFileSync(file, metadata);
160
+ }, "calibration_metadata_missing_invalid"],
161
+ ["missing_comparison", (root) => fs.rmSync(path.join(root, "comparison.json")), "calibration_raw_file_missing"],
162
+ ["relaxed_comparison_threshold", (root) => {
163
+ const file = path.join(root, "comparison.json");
164
+ const comparison = JSON.parse(fs.readFileSync(file, "utf8"));
165
+ writeJson(file, { ...comparison, threshold: 0.1 });
166
+ }, "calibration_comparison_mismatch"],
167
+ ["invalid_run_directory", (root) => {
168
+ fs.rmSync(root, { recursive: true });
169
+ fs.writeFileSync(root, "not a directory\n");
170
+ }, "calibration_run_directory_invalid"],
171
+ ["invalid_input_file", (root) => {
172
+ const input = path.dirname(root);
173
+ fs.rmSync(input, { recursive: true });
174
+ fs.writeFileSync(input, "not a directory\n");
175
+ }, "calibration_input_invalid"],
176
+ ["symlinked_input_directory", (root) => {
177
+ const input = path.dirname(root);
178
+ const external = path.join(tempRoot, "external-input");
179
+ fs.cpSync(input, external, { recursive: true });
180
+ fs.rmSync(input, { recursive: true });
181
+ fs.symlinkSync(external, input);
182
+ }, "calibration_input_invalid"],
183
+ ["invalid_output_directory", (root) => fs.mkdirSync(path.join(path.dirname(root), "calibration.json")), "calibration_output_invalid"],
184
+ ["unknown_root_file", (root) => fs.writeFileSync(path.join(path.dirname(root), "forged.json"), "{}\n"), "calibration_run_directory_unknown"],
185
+ ["symlinked_run_directory", (root) => {
186
+ const external = path.join(tempRoot, "external-run");
187
+ fs.cpSync(path.join(path.dirname(root), "run-02"), external, { recursive: true });
188
+ fs.rmSync(root, { recursive: true });
189
+ fs.symlinkSync(external, root);
190
+ }, "calibration_run_directory_invalid"],
191
+ ["artifact_directory", (root) => {
192
+ fs.rmSync(path.join(root, "actual.png"));
193
+ fs.mkdirSync(path.join(root, "actual.png"));
194
+ }, "calibration_artifact_hash_mismatch"],
195
+ ["oversized_metadata", (root) => fs.appendFileSync(path.join(root, "metadata.json"), " ".repeat(1024 * 1024)), "calibration_raw_file_oversized"],
196
+ ["oversized_png", (root) => fs.appendFileSync(path.join(root, "actual.png"), Buffer.alloc(5 * 1024 * 1024)), "calibration_raw_file_oversized"],
197
+ ];
198
+ for (const [name, mutate, expected] of invalidCases) {
199
+ const actual = invoke(name, mutate);
200
+ const codes = actual.report.failures.map((failure) => failure.code);
201
+ results.push({ actual: { codes, outputExists: actual.output !== null, status: actual.status }, expected, name, ok: actual.status !== 0 && actual.output === null && actual.report.status === "incomplete" && codes.includes(expected) });
202
+ }
203
+ const identityCases = [
204
+ ["checkout_sha_not_tested_sha", { checkoutSha: "1".repeat(40) }],
205
+ ["artifact_bound_to_head_sha", { artifactName: `chromium-sentinel-calibration-123-${"1".repeat(40)}` }],
206
+ ["unrecognized_execution_repository", { executionRepository: "untrusted/StyleGallery" }],
207
+ ["swapped_canonical_and_execution_repository", { executionRepository: "changeroa/StyleGallery", repository: "ark-jo/StyleGallery" }],
208
+ ];
209
+ for (const [name, identity] of identityCases) {
210
+ const actual = invoke(name, undefined, identity);
211
+ const codes = actual.report.failures.map((failure) => failure.code);
212
+ results.push({
213
+ actual: { codes, outputExists: actual.output !== null, status: actual.status },
214
+ expected: "calibration_committed_ci_invalid",
215
+ name,
216
+ ok: actual.status !== 0 && actual.output === null && codes.includes("calibration_committed_ci_invalid"),
217
+ });
218
+ }
219
+ } finally {
220
+ fs.rmSync(tempRoot, { force: true, recursive: true });
221
+ }
222
+
223
+ const failures = results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`);
224
+ const report = { failures, ok: failures.length === 0, results };
225
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
226
+ if (!report.ok) process.exitCode = 1;
@@ -0,0 +1,234 @@
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 { BASELINE_METADATA_SHA256, BASELINE_REFERENCE, sha256 } from "./baseline-contract.mjs";
9
+ import { baselineSchemaParity } from "./baseline-schema-parity.mjs";
10
+
11
+ const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
12
+ const validator = path.join(repositoryRoot, "scripts", "validate-baseline-manifest.mjs");
13
+ const canonical = JSON.parse(fs.readFileSync(path.join(repositoryRoot, "consumer-reference", "baselines", "calibration.json"), "utf8"));
14
+ const canonicalManifest = JSON.parse(fs.readFileSync(path.join(repositoryRoot, "consumer-reference", "baselines", "manifest.json"), "utf8"));
15
+ const schema = JSON.parse(fs.readFileSync(path.join(repositoryRoot, "consumer-reference", "schema", "calibration-record.schema.json"), "utf8"));
16
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "stylegallery-pr4-calibration-"));
17
+ const completedEvidence = {
18
+ artifactId: "8283099324",
19
+ artifactName: "chromium-sentinel-calibration-29260372260-18229be570766d3b42f5600955120bfcba690b76",
20
+ artifactApiDigest: "sha256:3f11a517b447e1b5a1da17d9ee66ba2dd947fc8a0a482c447556ef00652e6074",
21
+ artifactExpiresAt: "2026-07-27T15:01:36Z",
22
+ artifactSize: 70310,
23
+ checkoutSha: "18229be570766d3b42f5600955120bfcba690b76",
24
+ headSha: "3bfdd25ec9a2df4ca84e19541365d48842a73f59",
25
+ rawEvidenceSha256: "1f125d5b321063b364e19283897c78c126d73eb1b3368d14686c494b1296dfab",
26
+ runId: "29260372260",
27
+ source: "https://github.com/ark-jo/StyleGallery/actions/runs/29260372260",
28
+ verifiedAt: "2026-07-13T15:04:41Z",
29
+ };
30
+
31
+ function externalVerification() {
32
+ return {
33
+ artifact: {
34
+ api_digest: completedEvidence.artifactApiDigest,
35
+ expires_at: completedEvidence.artifactExpiresAt,
36
+ id: completedEvidence.artifactId,
37
+ name: completedEvidence.artifactName,
38
+ size_in_bytes: completedEvidence.artifactSize,
39
+ },
40
+ repository_relationship: {
41
+ execution_is_fork: true,
42
+ parent: "changeroa/StyleGallery",
43
+ source: "changeroa/StyleGallery",
44
+ },
45
+ source: completedEvidence.source,
46
+ verified_at: completedEvidence.verifiedAt,
47
+ };
48
+ }
49
+
50
+ function completeRecord(committedCi = {}) {
51
+ const runs = Array.from({ length: 20 }, (_, index) => ({
52
+ architecture: "amd64",
53
+ ax_sha256: "a".repeat(64),
54
+ completed: true,
55
+ dom_sha256: "b".repeat(64),
56
+ metadata_sha256: BASELINE_METADATA_SHA256,
57
+ png_sha256: BASELINE_REFERENCE.baseline.sha256,
58
+ run: index + 1,
59
+ screenshot_diff_pixels: 0,
60
+ }));
61
+ return {
62
+ ...canonical,
63
+ committed_ci: {
64
+ artifact_name: completedEvidence.artifactName,
65
+ checkout_sha: completedEvidence.checkoutSha,
66
+ execution_repository: "ark-jo/StyleGallery",
67
+ external_verification: externalVerification(),
68
+ head_sha: completedEvidence.headSha,
69
+ raw_evidence_sha256: completedEvidence.rawEvidenceSha256,
70
+ repository: "changeroa/StyleGallery",
71
+ run_attempt: "1",
72
+ run_id: completedEvidence.runId,
73
+ sha: completedEvidence.checkoutSha,
74
+ workflow: ".github/workflows/validate.yml",
75
+ ...committedCi,
76
+ },
77
+ status: "completed",
78
+ runs,
79
+ };
80
+ }
81
+
82
+ const cases = [
83
+ ["nineteen_of_twenty", (record) => ({ ...record, runs: record.runs.slice(0, 19) }), "calibration_run_count"],
84
+ ["duplicate_run", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, run: 19 } : run) }), "calibration_run_duplicate"],
85
+ ["missing_run", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, run: 21 } : run) }), "calibration_run_set"],
86
+ ["architecture_variance", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, architecture: "arm64" } : run) }), "calibration_architecture_variance"],
87
+ ["environment_mismatch", (record) => ({ ...record, environment: { ...record.environment, architecture: "arm64" } }), "calibration_environment_mismatch"],
88
+ ["hash_variance", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, png_sha256: "e".repeat(64) } : run) }), "calibration_hash_variance"],
89
+ ["metadata_variance", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, metadata_sha256: "f".repeat(64) } : run) }), "calibration_metadata_variance"],
90
+ ["incomplete_run", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, completed: false } : run) }), "calibration_run_incomplete"],
91
+ ["nonzero_diff", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, screenshot_diff_pixels: 1 } : run) }), "calibration_diff_nonzero"],
92
+ ["malformed_hash", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, png_sha256: "x" } : run) }), "calibration_hash_invalid"],
93
+ ["wrong_stable_baseline_hash", (record) => ({ ...record, runs: record.runs.map((run) => ({ ...run, png_sha256: "d".repeat(64) })) }), "calibration_baseline_hash_mismatch"],
94
+ ["wrong_stable_metadata_hash", (record) => ({ ...record, runs: record.runs.map((run) => ({ ...run, metadata_sha256: "c".repeat(64) })) }), "calibration_metadata_hash_mismatch"],
95
+ ["unknown_run_property", (record) => ({ ...record, runs: record.runs.map((run, index) => index === 19 ? { ...run, forged: true } : run) }), "calibration_run_property_unknown"],
96
+ ["checkout_sha_not_tested_sha", (record) => ({ ...record, committed_ci: { ...record.committed_ci, checkout_sha: record.committed_ci.head_sha } }), "calibration_committed_ci_invalid"],
97
+ ["artifact_bound_to_head_sha", (record) => ({ ...record, committed_ci: { ...record.committed_ci, artifact_name: `chromium-sentinel-calibration-${record.committed_ci.run_id}-${record.committed_ci.head_sha}` } }), "calibration_committed_ci_invalid"],
98
+ ["unrecognized_execution_repository", (record) => ({ ...record, committed_ci: { ...record.committed_ci, execution_repository: "untrusted/StyleGallery" } }), "calibration_committed_ci_invalid"],
99
+ ["swapped_canonical_and_execution_repository", (record) => ({ ...record, committed_ci: { ...record.committed_ci, execution_repository: "changeroa/StyleGallery", repository: "ark-jo/StyleGallery" } }), "calibration_committed_ci_invalid"],
100
+ ["missing_execution_repository", (record) => {
101
+ const { execution_repository: omitted, ...committedCi } = record.committed_ci;
102
+ return { ...record, committed_ci: committedCi };
103
+ }, "calibration_committed_ci_invalid"],
104
+ ["completed_missing_external_verification", (record) => {
105
+ const { external_verification: omitted, ...committedCi } = record.committed_ci;
106
+ return { ...record, committed_ci: committedCi };
107
+ }, "calibration_external_verification_missing"],
108
+ ["forged_external_artifact_id", (record) => ({ ...record, committed_ci: { ...record.committed_ci, external_verification: { ...record.committed_ci.external_verification, artifact: { ...record.committed_ci.external_verification.artifact, id: "8283099325" } } } }), "calibration_external_verification_invalid"],
109
+ ["forged_external_artifact_digest", (record) => ({ ...record, committed_ci: { ...record.committed_ci, external_verification: { ...record.committed_ci.external_verification, artifact: { ...record.committed_ci.external_verification.artifact, api_digest: `sha256:${"f".repeat(64)}` } } } }), "calibration_external_verification_invalid"],
110
+ ["raw_digest_substituted_for_artifact_digest", (record) => ({ ...record, committed_ci: { ...record.committed_ci, external_verification: { ...record.committed_ci.external_verification, artifact: { ...record.committed_ci.external_verification.artifact, api_digest: `sha256:${record.committed_ci.raw_evidence_sha256}` } } } }), "calibration_external_verification_invalid"],
111
+ ["forged_external_source", (record) => ({ ...record, committed_ci: { ...record.committed_ci, external_verification: { ...record.committed_ci.external_verification, source: "https://github.com/ark-jo/StyleGallery/actions/runs/1" } } }), "calibration_external_verification_invalid"],
112
+ ["forged_fork_parent_relationship", (record) => ({ ...record, committed_ci: { ...record.committed_ci, external_verification: { ...record.committed_ci.external_verification, repository_relationship: { ...record.committed_ci.external_verification.repository_relationship, parent: "untrusted/StyleGallery" } } } }), "calibration_external_verification_invalid"],
113
+ ["invalid_committed_ci", (record) => ({ ...record, committed_ci: { artifact_name: "forged", extra: true, run_id: "abc", sha: "x" } }), "calibration_committed_ci_property_unknown"],
114
+ ["owner_approval_preclaim", (record) => ({ ...record, baseline_owner_approval: "approved" }), "baseline_owner_approval_invalid"],
115
+ ["pending_evidence_preclaim", (record) => ({ ...record, status: "awaiting_committed_ci" }), "calibration_pending_evidence_forbidden"],
116
+ ];
117
+
118
+ const results = [];
119
+ try {
120
+ const schemaParity = baselineSchemaParity(schema, canonical, completedEvidence);
121
+ results.push({ actual: { schemaParity }, expected: "recursive schema/runtime identity parity", name: "schema_runtime_parity", ok: schemaParity });
122
+ const validFixture = path.join(tempRoot, "valid-completed.json");
123
+ fs.writeFileSync(validFixture, `${JSON.stringify(completeRecord(), null, 2)}\n`);
124
+ const validChild = spawnSync(process.execPath, [validator, "--calibration", validFixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
125
+ const validOutput = JSON.parse(validChild.stdout);
126
+ results.push({ actual: { codes: validOutput.failures.map((failure) => failure.code), status: validChild.status }, expected: "completed calibration and exit:0", name: "valid_completed_calibration", ok: validChild.status === 0 && validOutput.ok === true });
127
+ const awaitingExternalFixture = path.join(tempRoot, "valid-awaiting-external-verification.json");
128
+ fs.writeFileSync(awaitingExternalFixture, `${JSON.stringify({
129
+ ...completeRecord({ external_verification: null }),
130
+ status: "awaiting_external_verification",
131
+ }, null, 2)}\n`);
132
+ const awaitingExternalChild = spawnSync(process.execPath, [validator, "--calibration", awaitingExternalFixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
133
+ const awaitingExternalOutput = JSON.parse(awaitingExternalChild.stdout);
134
+ results.push({
135
+ actual: { codes: awaitingExternalOutput.failures.map((failure) => failure.code), status: awaitingExternalChild.status },
136
+ expected: "pre-upload calibration awaits external verification and exits 0",
137
+ name: "valid_awaiting_external_verification",
138
+ ok: awaitingExternalChild.status === 0 && awaitingExternalOutput.ok === true,
139
+ });
140
+ const pendingFixture = path.join(tempRoot, "valid-awaiting-committed-ci.json");
141
+ fs.writeFileSync(pendingFixture, `${JSON.stringify({ ...canonical, committed_ci: null, runs: [], status: "awaiting_committed_ci" }, null, 2)}\n`);
142
+ const pendingChild = spawnSync(process.execPath, [validator, "--calibration", pendingFixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
143
+ const pendingOutput = JSON.parse(pendingChild.stdout);
144
+ results.push({
145
+ actual: { codes: pendingOutput.failures.map((failure) => failure.code), status: pendingChild.status },
146
+ expected: "initial pending record and exit:0",
147
+ name: "valid_awaiting_committed_ci",
148
+ ok: pendingChild.status === 0 && pendingOutput.ok === true,
149
+ });
150
+ const pullRequestFixture = path.join(tempRoot, "valid-pull-request-merge-head.json");
151
+ const mergeSha = "11f4668fe5988720c27e88ec7203ecd1685a40df";
152
+ const headSha = "8b8eaed41094286138973157b581a1d9ab9957a8";
153
+ fs.writeFileSync(pullRequestFixture, `${JSON.stringify({
154
+ ...completeRecord({
155
+ artifact_name: `chromium-sentinel-calibration-29258810962-${mergeSha}`,
156
+ checkout_sha: mergeSha,
157
+ execution_repository: "ark-jo/StyleGallery",
158
+ external_verification: null,
159
+ head_sha: headSha,
160
+ repository: "changeroa/StyleGallery",
161
+ run_id: "29258810962",
162
+ sha: mergeSha,
163
+ }),
164
+ status: "awaiting_external_verification",
165
+ }, null, 2)}\n`);
166
+ const pullRequestChild = spawnSync(process.execPath, [validator, "--calibration", pullRequestFixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
167
+ const pullRequestOutput = JSON.parse(pullRequestChild.stdout);
168
+ results.push({
169
+ actual: { codes: pullRequestOutput.failures.map((failure) => failure.code), status: pullRequestChild.status },
170
+ expected: "distinct canonical pull-request merge checkout and head SHAs",
171
+ name: "valid_pull_request_merge_and_head_identity",
172
+ ok: pullRequestChild.status === 0 && pullRequestOutput.ok === true,
173
+ });
174
+ for (const [name, mutate, expected] of cases) {
175
+ const fixture = path.join(tempRoot, `${name}.json`);
176
+ fs.writeFileSync(fixture, `${JSON.stringify(mutate(completeRecord()), null, 2)}\n`);
177
+ const child = spawnSync(process.execPath, [validator, "--calibration", fixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
178
+ const output = JSON.parse(child.stdout);
179
+ const codes = Array.isArray(output.failures) ? output.failures.map((failure) => failure.code) : [];
180
+ results.push({ actual: { codes, status: child.status }, expected, name, ok: child.status !== 0 && codes.includes(expected) });
181
+ }
182
+ const manifestFixture = path.join(tempRoot, "unknown-manifest-nested.json");
183
+ fs.writeFileSync(manifestFixture, `${JSON.stringify({
184
+ ...canonicalManifest,
185
+ calibration: { ...canonicalManifest.calibration, forged: true },
186
+ source: { ...canonicalManifest.source, forged: true },
187
+ }, null, 2)}\n`);
188
+ const manifestChild = spawnSync(process.execPath, [validator, "--manifest", manifestFixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
189
+ const manifestOutput = JSON.parse(manifestChild.stdout);
190
+ const manifestCodes = manifestOutput.failures.map((failure) => failure.code);
191
+ results.push({
192
+ actual: { codes: manifestCodes, status: manifestChild.status },
193
+ expected: "baseline_calibration_property_unknown and baseline_source_property_unknown",
194
+ name: "unknown_manifest_nested_properties",
195
+ ok: manifestChild.status !== 0 && manifestCodes.includes("baseline_calibration_property_unknown") && manifestCodes.includes("baseline_source_property_unknown"),
196
+ });
197
+ const alternateFile = path.join(repositoryRoot, "package.json");
198
+ const alternateManifestFixture = path.join(tempRoot, "alternate-baseline.json");
199
+ fs.writeFileSync(alternateManifestFixture, `${JSON.stringify({
200
+ ...canonicalManifest,
201
+ baseline: { path: "package.json", sha256: sha256(fs.readFileSync(alternateFile)) },
202
+ }, null, 2)}\n`);
203
+ const alternateChild = spawnSync(process.execPath, [validator, "--manifest", alternateManifestFixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
204
+ const alternateOutput = JSON.parse(alternateChild.stdout);
205
+ const alternateCodes = alternateOutput.failures.map((failure) => failure.code);
206
+ results.push({
207
+ actual: { codes: alternateCodes, status: alternateChild.status },
208
+ expected: "baseline_baseline_reference_mismatch",
209
+ name: "alternate_in_repo_baseline",
210
+ ok: alternateChild.status !== 0 && alternateCodes.includes("baseline_baseline_reference_mismatch"),
211
+ });
212
+ const directoryManifestFixture = path.join(tempRoot, "directory-baseline.json");
213
+ fs.writeFileSync(directoryManifestFixture, `${JSON.stringify({ ...canonicalManifest, baseline: { path: "tests", sha256: "a".repeat(64) } }, null, 2)}\n`);
214
+ const directoryChild = spawnSync(process.execPath, [validator, "--manifest", directoryManifestFixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
215
+ const directoryOutput = JSON.parse(directoryChild.stdout);
216
+ results.push({ actual: { codes: directoryOutput.failures.map((failure) => failure.code), status: directoryChild.status }, expected: "baseline_baseline_file_invalid", name: "directory_manifest_target", ok: directoryChild.status !== 0 && directoryOutput.failures.some((failure) => failure.code === "baseline_baseline_file_invalid") });
217
+ for (const [name, content, expected] of [
218
+ ["null_calibration", "null\n", "calibration_record_invalid"],
219
+ ["duplicate_calibration_property", '{"schema_version":"1.0","schema_version":"1.0"}\n', "baseline_json_invalid"],
220
+ ]) {
221
+ const fixture = path.join(tempRoot, `${name}.json`);
222
+ fs.writeFileSync(fixture, content);
223
+ const child = spawnSync(process.execPath, [validator, "--calibration", fixture, "--json"], { cwd: repositoryRoot, encoding: "utf8" });
224
+ const output = JSON.parse(child.stdout);
225
+ results.push({ actual: { codes: output.failures.map((failure) => failure.code), status: child.status }, expected, name, ok: child.status !== 0 && output.failures.some((failure) => failure.code === expected) });
226
+ }
227
+ } finally {
228
+ fs.rmSync(tempRoot, { force: true, recursive: true });
229
+ }
230
+
231
+ const failures = results.filter((result) => !result.ok).map((result) => `missing_semantic:${result.name}:${result.expected}`);
232
+ const report = { failures, ok: failures.length === 0, results };
233
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
234
+ if (!report.ok) process.exitCode = 1;