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,159 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { parseStrictJson } from "../strict-json.mjs";
5
+ import { deepFreeze, hashCanonical } from "./canonical-json.mjs";
6
+ import { defineOperation } from "./execution.mjs";
7
+ import { createManifest, createStableRef, createVersionId } from "./identity.mjs";
8
+ import {
9
+ createClaim,
10
+ createEvidenceLink,
11
+ createGovernanceDecision,
12
+ createPolicyDisposition,
13
+ createValidationReport,
14
+ } from "./knowledge.mjs";
15
+
16
+ export const EDITORIAL_PROFILE_REF = "sg:profile/editorial-reference-profile";
17
+ export const FIXTURE_MANIFEST_REF = "sg:manifest/agent-native-fixture";
18
+ export const REGISTRY_SOURCE_URL = new URL("../../consumer-reference/agent-native/registry.json", import.meta.url);
19
+
20
+ export class FixtureError extends TypeError {
21
+ constructor(code, message, recordPath = "") {
22
+ super(message);
23
+ this.name = "FixtureError";
24
+ this.code = code;
25
+ if (recordPath) this.path = recordPath;
26
+ }
27
+ }
28
+
29
+ function fail(code, message, recordPath = "") {
30
+ throw new FixtureError(code, message, recordPath);
31
+ }
32
+
33
+ function plainObject(value) {
34
+ return value !== null && typeof value === "object" && !Array.isArray(value);
35
+ }
36
+
37
+ function repositoryRoot() {
38
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
39
+ }
40
+
41
+ function safeRepositoryFile(relative) {
42
+ if (typeof relative !== "string" || relative.length === 0 || path.isAbsolute(relative)) {
43
+ fail("fixture_source_path_invalid", "repository_path must be a non-empty relative path", "repository_path");
44
+ }
45
+ const root = repositoryRoot();
46
+ const target = path.resolve(root, relative);
47
+ const local = path.relative(root, target);
48
+ if (local !== relative || local.startsWith(`..${path.sep}`)) {
49
+ fail("fixture_source_path_invalid", "repository_path must remain normalized inside the repository", relative);
50
+ }
51
+ let current = root;
52
+ for (const segment of relative.split("/")) {
53
+ current = path.join(current, segment);
54
+ if (!fs.existsSync(current)) fail("fixture_source_missing", "fixture source does not exist", relative);
55
+ if (fs.lstatSync(current).isSymbolicLink()) fail("fixture_source_symlink", "fixture source must not traverse a symlink", relative);
56
+ }
57
+ if (!fs.lstatSync(target).isFile()) fail("fixture_source_type_invalid", "fixture source must be a regular file", relative);
58
+ return target;
59
+ }
60
+
61
+ function verifyProjectedSource(seed) {
62
+ if (!Object.hasOwn(seed, "repository_path")) return;
63
+ const expected = seed.canonical_sha256;
64
+ if (typeof expected !== "string" || !/^[a-f0-9]{64}$/.test(expected)) {
65
+ fail("fixture_source_digest_invalid", "canonical_sha256 must be 64 lowercase hex characters", seed.stable_ref);
66
+ }
67
+ const file = safeRepositoryFile(seed.repository_path);
68
+ let value;
69
+ try {
70
+ value = parseStrictJson(fs.readFileSync(file, "utf8"));
71
+ } catch (error) {
72
+ fail("fixture_source_json_invalid", error instanceof Error ? error.message : String(error), seed.repository_path);
73
+ }
74
+ const actual = hashCanonical(value).slice("sha256:".length);
75
+ if (actual !== expected) fail("fixture_source_digest_mismatch", "projected source changed; create a new registry version", seed.repository_path);
76
+ }
77
+
78
+ function versionedGeneric(seed) {
79
+ const payload = structuredClone(seed);
80
+ payload.schema_version ??= "1.0";
81
+ delete payload.version_id;
82
+ delete payload.versionId;
83
+ const stableRef = createStableRef(payload.stable_ref);
84
+ return deepFreeze({ ...payload, version_id: createVersionId({ stableRef, payload }) });
85
+ }
86
+
87
+ function materialize(seed, sourceRecords = new Map()) {
88
+ verifyProjectedSource(seed);
89
+ switch (seed.record_kind) {
90
+ case "claim": return createClaim(seed);
91
+ case "evidence_link": return createEvidenceLink(seed);
92
+ case "validation_report": return createValidationReport(seed);
93
+ case "governance_decision": return createGovernanceDecision(seed);
94
+ case "policy_disposition": return createPolicyDisposition(seed);
95
+ case "operation": return defineOperation(seed);
96
+ case "execution_receipt": {
97
+ const source = sourceRecords.get(seed.fixture_record_ref);
98
+ if (!source || source.record_kind !== "executable_fixture") {
99
+ fail("fixture_receipt_source_missing", "execution receipt must reference an executable fixture", seed.stable_ref);
100
+ }
101
+ if (seed.fixture_version_id !== undefined && seed.fixture_version_id !== source.version_id) {
102
+ fail("fixture_receipt_version_mismatch", "execution receipt fixture_version_id must match the executable fixture VersionID", seed.stable_ref);
103
+ }
104
+ return versionedGeneric({ ...seed, fixture_version_id: source.version_id });
105
+ }
106
+ default: return versionedGeneric(seed);
107
+ }
108
+ }
109
+
110
+ function validateSeed(seed) {
111
+ if (!plainObject(seed)) fail("fixture_registry_invalid", "registry source must contain an object");
112
+ if (seed.schema_version !== "1.0") fail("fixture_schema_version_unsupported", "registry schema_version must be 1.0", "schema_version");
113
+ if (seed.fixture_ref !== FIXTURE_MANIFEST_REF) fail("fixture_manifest_ref_invalid", `fixture_ref must be ${FIXTURE_MANIFEST_REF}`, "fixture_ref");
114
+ if (!Array.isArray(seed.records)) fail("fixture_records_required", "registry records must be an array", "records");
115
+ const refs = seed.records.map((record, index) => {
116
+ if (!plainObject(record)) fail("fixture_record_invalid", "registry records must be objects", `records/${index}`);
117
+ try { return createStableRef(record.stable_ref); } catch (error) {
118
+ fail(error.code ?? "fixture_stable_ref_invalid", error.message, `records/${index}/stable_ref`);
119
+ }
120
+ });
121
+ if (new Set(refs).size !== refs.length) fail("fixture_stable_ref_duplicate", "fixture StableRefs must be unique", "records");
122
+ if (!refs.includes(EDITORIAL_PROFILE_REF)) fail("fixture_editorial_profile_missing", `fixture must contain ${EDITORIAL_PROFILE_REF}`, "records");
123
+ }
124
+
125
+ export function loadRegistrySeed(source = REGISTRY_SOURCE_URL) {
126
+ const file = source instanceof URL ? fileURLToPath(source) : path.resolve(String(source));
127
+ if (!fs.existsSync(file) || fs.lstatSync(file).isSymbolicLink() || !fs.lstatSync(file).isFile()) {
128
+ fail("fixture_registry_file_invalid", "registry source must be a regular non-symlink file", file);
129
+ }
130
+ let seed;
131
+ try { seed = parseStrictJson(fs.readFileSync(file, "utf8")); } catch (error) {
132
+ fail("fixture_registry_json_invalid", error instanceof Error ? error.message : String(error), file);
133
+ }
134
+ validateSeed(seed);
135
+ return deepFreeze(seed);
136
+ }
137
+
138
+ export function createAgentNativeFixture(input = {}) {
139
+ const seed = input.seed ?? loadRegistrySeed(input.source);
140
+ validateSeed(seed);
141
+ const materializedSources = new Map(seed.records
142
+ .filter((record) => record.record_kind !== "execution_receipt")
143
+ .map((record) => materialize(record))
144
+ .map((record) => [record.stable_ref, record]));
145
+ const records = seed.records.map((record) => record.record_kind === "execution_receipt"
146
+ ? materialize(record, materializedSources)
147
+ : materializedSources.get(record.stable_ref)).sort((left, right) => (
148
+ left.stable_ref < right.stable_ref ? -1 : left.stable_ref > right.stable_ref ? 1 : 0
149
+ ));
150
+ const entries = records.map((record) => ({
151
+ content_sha256: hashCanonical(record).slice("sha256:".length),
152
+ stable_ref: record.stable_ref,
153
+ version_id: record.version_id,
154
+ }));
155
+ const manifest = createManifest({ manifest_ref: seed.fixture_ref, entries, schema_version: seed.schema_version });
156
+ return deepFreeze({ fixture_ref: seed.fixture_ref, manifest, records, schema_version: seed.schema_version });
157
+ }
158
+
159
+ export const agentNativeFixture = createAgentNativeFixture();
@@ -0,0 +1,184 @@
1
+ import { deepFreeze, hashCanonical } from "./canonical-json.mjs";
2
+
3
+ export class IdentityError extends TypeError {
4
+ constructor(code, message, path = "") {
5
+ super(message);
6
+ this.name = "IdentityError";
7
+ this.code = code;
8
+ if (path) this.path = path;
9
+ }
10
+ }
11
+
12
+ const KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
13
+ export const STABLE_REF_KINDS = Object.freeze([
14
+ "profile", "artifact", "claim", "evidence", "validation", "governance", "operation", "task", "proposal", "view",
15
+ "agent", "validator", "observer", "governor", "capability", "receipt", "run", "effect", "context", "manifest", "policy", "connector", "event", "snapshot", "source", "skill",
16
+ ]);
17
+ const KIND_SET = new Set(STABLE_REF_KINDS);
18
+
19
+ function fail(code, message, path) {
20
+ throw new IdentityError(code, message, path);
21
+ }
22
+
23
+ function inputValue(input, names) {
24
+ if (typeof input === "string") return input;
25
+ if (!input || typeof input !== "object") return undefined;
26
+ for (const name of names) if (Object.hasOwn(input, name)) return input[name];
27
+ return undefined;
28
+ }
29
+
30
+ function requireRef(value, path = "stable_ref") {
31
+ if (typeof value !== "string") fail("stable_ref_required", "StableRef must be a string", path);
32
+ const match = /^sg:([a-z0-9-]+)\/([a-z0-9]+(?:-[a-z0-9]+)*)$/.exec(value);
33
+ if (!match || !KIND_SET.has(match[1])) fail("stable_ref_invalid", "StableRef must match the governed sg:<kind>/<kebab-id> grammar", path);
34
+ return { stable_ref: value, kind: match[1], id: match[2] };
35
+ }
36
+
37
+ export function createStableRef(input) {
38
+ const value = typeof input === "string" ? input : undefined;
39
+ if (value) return requireRef(value).stable_ref;
40
+ const kind = inputValue(input, ["kind", "namespace", "type"]);
41
+ const id = inputValue(input, ["id", "opaque_id", "opaqueId"]);
42
+ if (typeof kind !== "string" || typeof id !== "string") fail("stable_ref_parts_required", "createStableRef requires kind and id");
43
+ if (!KIND_SET.has(kind) || !KEBAB.test(kind)) fail("stable_ref_kind_invalid", `unsupported StableRef kind ${kind}`, "kind");
44
+ if (!KEBAB.test(id)) fail("stable_ref_id_invalid", `StableRef id must be kebab-case: ${id}`, "id");
45
+ return `sg:${kind}/${id}`;
46
+ }
47
+
48
+ export const stableRef = createStableRef;
49
+
50
+ export function parseStableRef(input) {
51
+ const value = inputValue(input, ["stable_ref", "stableRef", "ref", "value"]);
52
+ const parsed = requireRef(value, "stable_ref");
53
+ return deepFreeze({ ...parsed, stableRef: parsed.stable_ref, opaque_id: parsed.id, opaqueId: parsed.id });
54
+ }
55
+
56
+ export const parseRef = parseStableRef;
57
+
58
+ function payloadOf(input) {
59
+ if (!input || typeof input !== "object" || Array.isArray(input)) return input;
60
+ if (Object.hasOwn(input, "payload")) return input.payload;
61
+ if (Object.hasOwn(input, "version_payload")) return input.version_payload;
62
+ if (Object.hasOwn(input, "versionPayload")) return input.versionPayload;
63
+ return input;
64
+ }
65
+
66
+ function versionContent(stable_ref, payload) {
67
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
68
+ return { stable_ref, content: payload };
69
+ }
70
+ const copy = { ...payload };
71
+ delete copy.stable_ref;
72
+ delete copy.stableRef;
73
+ delete copy.ref;
74
+ delete copy.version_id;
75
+ delete copy.versionId;
76
+ return { stable_ref, ...copy };
77
+ }
78
+
79
+ function digestPayload(stable_ref, payload) {
80
+ return hashCanonical(versionContent(stable_ref, payload)).replace(/^sha256:/, "");
81
+ }
82
+
83
+ export function createVersionId(input) {
84
+ const payload = payloadOf(input);
85
+ const ref = inputValue(input, ["stable_ref", "stableRef", "ref"]) ?? (payload && typeof payload === "object" ? inputValue(payload, ["stable_ref", "stableRef", "ref"]) : undefined);
86
+ const stable_ref = createStableRef(ref);
87
+ return `${stable_ref}@sha256:${digestPayload(stable_ref, payload)}`;
88
+ }
89
+
90
+ export const versionId = createVersionId;
91
+
92
+ const VERSION_RE = /^(sg:[a-z0-9-]+\/[a-z0-9]+(?:-[a-z0-9]+)*)@sha256:([a-f0-9]{64})$/;
93
+
94
+ export function parseVersionId(input) {
95
+ const value = inputValue(input, ["version_id", "versionId", "id", "value"]);
96
+ if (typeof value !== "string") fail("version_id_required", "VersionID must be a string", "version_id");
97
+ const match = VERSION_RE.exec(value);
98
+ if (!match) fail("version_id_invalid", "VersionID must be StableRef@sha256:<64 lowercase hex>", "version_id");
99
+ const parsedRef = requireRef(match[1]);
100
+ const expectedRef = typeof input === "string" ? undefined : inputValue(input, ["stable_ref", "stableRef", "ref"]);
101
+ if (expectedRef !== undefined && createStableRef(expectedRef) !== parsedRef.stable_ref) fail("version_ref_mismatch", "VersionID StableRef does not match the supplied StableRef");
102
+ const payload = input && typeof input === "object" ? (Object.hasOwn(input, "payload") ? input.payload : Object.hasOwn(input, "version_payload") ? input.version_payload : undefined) : undefined;
103
+ if (payload !== undefined && digestPayload(parsedRef.stable_ref, payload) !== match[2]) fail("version_digest_mismatch", "VersionID digest does not match the supplied payload", "version_id");
104
+ return deepFreeze({ version_id: value, versionId: value, stable_ref: parsedRef.stable_ref, stableRef: parsedRef.stable_ref, digest: match[2], hash_suite: "sha256" });
105
+ }
106
+
107
+ export const parseVersion = parseVersionId;
108
+
109
+ function normalizeEntry(entry, index) {
110
+ const hasIdentity = entry && typeof entry === "object" && (Object.hasOwn(entry, "stable_ref") || Object.hasOwn(entry, "stableRef") || Object.hasOwn(entry, "ref"));
111
+ const source = hasIdentity ? entry : entry?.object ?? entry?.record ?? entry?.value ?? entry;
112
+ if (!source || typeof source !== "object") fail("manifest_entry_invalid", "manifest entries must be objects", `/entries/${index}`);
113
+ const stable_ref = createStableRef(inputValue(source, ["stable_ref", "stableRef", "ref"]));
114
+ const version_id = inputValue(source, ["version_id", "versionId"]);
115
+ const hasContent = Object.hasOwn(source, "content");
116
+ const content = hasContent ? source.content : undefined;
117
+ if (hasContent && content && typeof content === "object" && !Array.isArray(content)) {
118
+ const contentRef = inputValue(content, ["stable_ref", "stableRef", "ref"]);
119
+ if (contentRef !== undefined && createStableRef(contentRef) !== stable_ref) {
120
+ fail("manifest_content_ref_mismatch", "manifest entry StableRef does not match its supplied content", `/entries/${index}/content/stable_ref`);
121
+ }
122
+ const contentVersion = inputValue(content, ["version_id", "versionId"]);
123
+ if (contentVersion !== undefined && contentVersion !== version_id) {
124
+ fail("manifest_content_version_mismatch", "manifest entry VersionID does not match its supplied content", `/entries/${index}/content/version_id`);
125
+ }
126
+ }
127
+ parseVersionId({ version_id, stable_ref, ...(hasContent ? { payload: content } : {}) });
128
+ let content_sha256 = inputValue(source, ["content_sha256", "contentHash", "sha256"]);
129
+ const expectedContentSha = hasContent ? hashCanonical(content).replace(/^sha256:/, "") : undefined;
130
+ if (content_sha256 === undefined && hasContent) content_sha256 = expectedContentSha;
131
+ if (typeof content_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(content_sha256)) fail("manifest_content_hash_invalid", "manifest entry content_sha256 must be 64 lowercase hex characters", `/entries/${index}/content_sha256`);
132
+ if (hasContent && content_sha256 !== expectedContentSha) fail("manifest_content_digest_mismatch", "manifest entry content_sha256 does not match its supplied content", `/entries/${index}/content_sha256`);
133
+ return { stable_ref, version_id, content_sha256, ...(hasContent ? { content: structuredClone(content) } : {}) };
134
+ }
135
+
136
+ function manifestBody(manifest, includeVersion) {
137
+ const body = { schema_version: manifest.schema_version, manifest_ref: manifest.manifest_ref, entries: manifest.entries };
138
+ if (includeVersion) body.version_id = manifest.version_id;
139
+ return body;
140
+ }
141
+
142
+ export function createManifest(input = {}) {
143
+ const manifest_ref = createStableRef(inputValue(input, ["manifest_ref", "manifestRef", "ref"]));
144
+ const rawEntries = inputValue(input, ["entries", "objects", "records"]) ?? [];
145
+ if (!Array.isArray(rawEntries)) fail("manifest_entries_required", "manifest entries must be an array", "entries");
146
+ const entries = rawEntries.map(normalizeEntry).sort((left, right) => left.stable_ref < right.stable_ref ? -1 : left.stable_ref > right.stable_ref ? 1 : 0);
147
+ if (new Set(entries.map((entry) => entry.stable_ref)).size !== entries.length) fail("manifest_entry_duplicate", "manifest entries must have unique StableRefs", "entries");
148
+ const base = { schema_version: input.schema_version ?? input.schemaVersion ?? "1.0", manifest_ref, entries };
149
+ const version_id = `${manifest_ref}@sha256:${hashCanonical(base).replace(/^sha256:/, "")}`;
150
+ const sha256 = hashCanonical({ ...base, version_id }).replace(/^sha256:/, "");
151
+ return deepFreeze({ ...base, version_id, sha256 });
152
+ }
153
+
154
+ export const makeManifest = createManifest;
155
+
156
+ export function verifyManifest(input) {
157
+ const manifest = input?.manifest ?? input;
158
+ const failures = [];
159
+ if (!manifest || typeof manifest !== "object") return { ok: false, failures: [{ code: "manifest_required", message: "manifest must be an object" }] };
160
+ let ref;
161
+ try { ref = createStableRef(manifest.manifest_ref); } catch (error) { failures.push({ code: error.code ?? "manifest_ref_invalid", message: error.message, path: "manifest_ref" }); }
162
+ const entries = manifest.entries;
163
+ if (!Array.isArray(entries)) failures.push({ code: "manifest_entries_required", message: "manifest entries must be an array", path: "entries" });
164
+ else {
165
+ const refs = [];
166
+ for (let index = 0; index < entries.length; index += 1) {
167
+ for (const alias of ["stableRef", "ref", "versionId", "contentHash"]) {
168
+ if (Object.hasOwn(entries[index] ?? {}, alias)) failures.push({ code: "manifest_alias_forbidden", message: `${alias} is not a canonical manifest entry key`, path: `/entries/${index}/${alias}` });
169
+ }
170
+ try { refs.push(normalizeEntry(entries[index], index).stable_ref); } catch (error) { failures.push({ code: error.code ?? "manifest_entry_invalid", message: error.message, path: error.path ?? `/entries/${index}` }); }
171
+ }
172
+ if (new Set(refs).size !== refs.length) failures.push({ code: "manifest_entry_duplicate", message: "manifest entries must be unique", path: "entries" });
173
+ if (refs.some((entry, index) => index > 0 && refs[index - 1] > entry)) failures.push({ code: "manifest_entries_unsorted", message: "manifest entries must be sorted by StableRef", path: "entries" });
174
+ }
175
+ if (failures.length === 0) {
176
+ const expectedVersion = `${ref}@sha256:${hashCanonical(manifestBody(manifest, false)).replace(/^sha256:/, "")}`;
177
+ const expectedSha = hashCanonical(manifestBody({ ...manifest, version_id: expectedVersion }, true)).replace(/^sha256:/, "");
178
+ if (manifest.version_id !== expectedVersion) failures.push({ code: "manifest_version_mismatch", message: "manifest VersionID does not match canonical entries", path: "version_id" });
179
+ if (manifest.sha256 !== expectedSha) failures.push({ code: "manifest_digest_mismatch", message: "manifest sha256 does not match canonical bytes", path: "sha256" });
180
+ }
181
+ return deepFreeze({ ok: failures.length === 0, failures });
182
+ }
183
+
184
+ export const validateManifest = verifyManifest;
@@ -0,0 +1,102 @@
1
+ import { canonicalize, cloneAndFreeze, deepFreeze } from "./canonical-json.mjs";
2
+ import { parseStableRef, parseVersionId } from "./identity.mjs";
3
+
4
+ export class ImmutableStoreError extends TypeError {
5
+ constructor(code, message, path = "") {
6
+ super(message);
7
+ this.name = "ImmutableStoreError";
8
+ this.code = code;
9
+ if (path) this.path = path;
10
+ }
11
+ }
12
+
13
+ function fail(code, message, path) {
14
+ throw new ImmutableStoreError(code, message, path);
15
+ }
16
+
17
+ function recordRef(record) {
18
+ const stable_ref = record?.stable_ref ?? record?.stableRef;
19
+ const version_id = record?.version_id ?? record?.versionId;
20
+ if (typeof stable_ref !== "string") fail("store_stable_ref_required", "append requires a StableRef", "stable_ref");
21
+ if (typeof version_id !== "string") fail("store_version_id_required", "append requires a VersionID", "version_id");
22
+ parseStableRef(stable_ref);
23
+ parseVersionId({ version_id, stable_ref, payload: record });
24
+ return { stable_ref, version_id };
25
+ }
26
+
27
+ function cloneRecord(value) {
28
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail("store_record_invalid", "append requires an object record");
29
+ try { canonicalize(value); } catch (error) { fail(error.code ?? "store_record_invalid", error.message); }
30
+ return cloneAndFreeze(value);
31
+ }
32
+
33
+ function compare(left, right) {
34
+ if (left < right) return -1;
35
+ if (left > right) return 1;
36
+ return 0;
37
+ }
38
+
39
+ /** Create an append-only in-memory store exposing only frozen copies. */
40
+ export function createImmutableStore(input = {}) {
41
+ const byVersion = new Map();
42
+ const byStableRef = new Map();
43
+ const initial = input.entries ?? input.records ?? [];
44
+ if (!Array.isArray(initial)) fail("store_entries_required", "store entries must be an array", "entries");
45
+
46
+ const append = (candidate) => {
47
+ const value = !candidate?.stable_ref && !candidate?.stableRef
48
+ ? (candidate?.object ?? candidate?.record ?? candidate?.value ?? candidate)
49
+ : candidate;
50
+ const { stable_ref, version_id } = recordRef(value);
51
+ const frozen = cloneRecord(value);
52
+ const existing = byVersion.get(version_id);
53
+ if (existing) {
54
+ if (canonicalize(existing) !== canonicalize(frozen)) fail("store_version_conflict", "VersionID is already bound to different immutable bytes", "version_id");
55
+ return existing;
56
+ }
57
+ byVersion.set(version_id, frozen);
58
+ const versions = byStableRef.get(stable_ref) ?? [];
59
+ byStableRef.set(stable_ref, [...versions, frozen]);
60
+ return frozen;
61
+ };
62
+
63
+ for (const entry of initial) append(entry);
64
+
65
+ const entries = () => [...byVersion.values()].sort((left, right) => compare(left.stable_ref ?? left.stableRef, right.stable_ref ?? right.stableRef) || compare(left.version_id ?? left.versionId, right.version_id ?? right.versionId));
66
+ const resolve = (reference) => {
67
+ const value = typeof reference === "string" ? reference : reference?.stable_ref ?? reference?.stableRef ?? reference?.version_id ?? reference?.versionId;
68
+ if (typeof value !== "string") fail("store_reference_required", "resolve requires a StableRef or VersionID");
69
+ if (value.includes("@sha256:")) {
70
+ parseVersionId(value);
71
+ return byVersion.get(value);
72
+ }
73
+ try { parseStableRef(value); } catch (error) { fail(error.code ?? "store_reference_invalid", error.message); }
74
+ const versions = byStableRef.get(value) ?? [];
75
+ return versions.length === 0 ? undefined : versions[versions.length - 1];
76
+ };
77
+
78
+ const snapshot = () => deepFreeze({ entries: entries(), size: byVersion.size });
79
+ return deepFreeze({
80
+ append,
81
+ get: resolve,
82
+ resolve,
83
+ has: (reference) => resolve(reference) !== undefined,
84
+ entries: snapshot,
85
+ listEntries: snapshot,
86
+ all: snapshot,
87
+ snapshot,
88
+ get size() { return byVersion.size; },
89
+ });
90
+ }
91
+
92
+ export const createStore = createImmutableStore;
93
+
94
+ export function appendImmutable(input) {
95
+ if (!input || typeof input !== "object") fail("store_input_required", "appendImmutable requires {store, record}");
96
+ const store = input.store ?? input.target;
97
+ const record = input.record ?? input.object ?? input.value;
98
+ if (!store || typeof store.append !== "function") fail("store_required", "appendImmutable requires an immutable store", "store");
99
+ return store.append(record);
100
+ }
101
+
102
+ export const appendObject = appendImmutable;
@@ -0,0 +1,168 @@
1
+ import { createVersionId } from "./identity.mjs";
2
+ import { deepFreeze } from "./canonical-json.mjs";
3
+
4
+ const CONTROL_KEYS = new Set(["schema_version", "record_kind", "kind", "type", "version_id", "versionId"]);
5
+
6
+ function clone(value) {
7
+ return value === undefined ? value : structuredClone(value);
8
+ }
9
+
10
+ function own(input, ...keys) {
11
+ for (const key of keys) if (Object.hasOwn(input, key)) return input[key];
12
+ return undefined;
13
+ }
14
+
15
+ function requiredObject(input, name) {
16
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new TypeError(`${name} expects an object parameter`);
17
+ return input;
18
+ }
19
+
20
+ function stableRefOf(input, name) {
21
+ const stableRef = own(input, "stable_ref", "stableRef");
22
+ if (typeof stableRef !== "string" || stableRef.length === 0) throw new TypeError(`${name}.stable_ref is required`);
23
+ return stableRef;
24
+ }
25
+
26
+ function canonicalFields(input, stableRef, recordKind) {
27
+ const fields = {};
28
+ for (const [key, value] of Object.entries(input)) {
29
+ if (!CONTROL_KEYS.has(key) && key !== "stable_ref" && key !== "stableRef") fields[key] = clone(value);
30
+ }
31
+ return { schema_version: "1.0", record_kind: recordKind, stable_ref: stableRef, ...fields };
32
+ }
33
+
34
+ function createRecord(input, recordKind, name) {
35
+ const source = requiredObject(input, name);
36
+ const stableRef = stableRefOf(source, name);
37
+ const record = canonicalFields(source, stableRef, recordKind);
38
+ const versionId = createVersionId({ stableRef, payload: record });
39
+ if (typeof versionId !== "string" || !/^sg:[^@]+@sha256:[a-f0-9]{64}$/.test(versionId)) {
40
+ throw new TypeError(`${name} received an invalid VersionID from the identity kernel`);
41
+ }
42
+ const supplied = own(source, "version_id", "versionId");
43
+ if (supplied !== undefined && supplied !== versionId) throw new TypeError(`${name}.version_id does not match its content`);
44
+ return deepFreeze({ ...record, version_id: versionId });
45
+ }
46
+
47
+ export function createClaim(input) {
48
+ return createRecord(input, "claim", "createClaim");
49
+ }
50
+
51
+ export const makeClaim = createClaim;
52
+
53
+ export function createEvidenceLink(input) {
54
+ const source = requiredObject(input, "createEvidenceLink");
55
+ const normalized = { ...source };
56
+ const aliases = [
57
+ ["claim_ref", "claimRef"],
58
+ ["claim_version", "claimVersion"],
59
+ ["source_ref", "sourceRef"],
60
+ ["source_version", "sourceVersion"],
61
+ ["independence_group", "independenceGroup"],
62
+ ];
63
+ for (const [canonical, alias] of aliases) {
64
+ if (!Object.hasOwn(normalized, canonical) && Object.hasOwn(normalized, alias)) normalized[canonical] = normalized[alias];
65
+ delete normalized[alias];
66
+ }
67
+ const polarity = own(normalized, "polarity", "stance");
68
+ if (typeof polarity === "string") normalized.polarity = polarity.toUpperCase();
69
+ return createRecord(normalized, "evidence_link", "createEvidenceLink");
70
+ }
71
+
72
+ export const makeEvidenceLink = createEvidenceLink;
73
+ export const createEvidence = createEvidenceLink;
74
+ export const makeEvidence = createEvidenceLink;
75
+
76
+ export function createAttestation(input) {
77
+ const source = requiredObject(input, "createAttestation");
78
+ const normalized = { ...source };
79
+ if (!Object.hasOwn(normalized, "claim_ref") && Object.hasOwn(normalized, "claimRef")) normalized.claim_ref = normalized.claimRef;
80
+ if (!Object.hasOwn(normalized, "claim_version") && Object.hasOwn(normalized, "claimVersion")) normalized.claim_version = normalized.claimVersion;
81
+ if (typeof normalized.polarity === "string") normalized.polarity = normalized.polarity.toUpperCase();
82
+ delete normalized.claimRef;
83
+ delete normalized.claimVersion;
84
+ return createRecord(normalized, "attestation", "createAttestation");
85
+ }
86
+
87
+ export const makeAttestation = createAttestation;
88
+
89
+ export function createValidationReport(input) {
90
+ const source = requiredObject(input, "createValidationReport");
91
+ const normalized = { ...source };
92
+ if (!Object.hasOwn(normalized, "claim_ref") && Object.hasOwn(normalized, "claimRef")) normalized.claim_ref = normalized.claimRef;
93
+ if (!Object.hasOwn(normalized, "claim_version") && Object.hasOwn(normalized, "claimVersion")) normalized.claim_version = normalized.claimVersion;
94
+ if (!Object.hasOwn(normalized, "validator_version") && Object.hasOwn(normalized, "validatorVersion")) normalized.validator_version = normalized.validatorVersion;
95
+ if (typeof normalized.status === "string") normalized.status = normalized.status.toUpperCase();
96
+ delete normalized.claimRef;
97
+ delete normalized.claimVersion;
98
+ delete normalized.validatorVersion;
99
+ return createRecord(normalized, "validation_report", "createValidationReport");
100
+ }
101
+
102
+ export const makeValidationReport = createValidationReport;
103
+
104
+ export function createGovernanceDecision(input) {
105
+ const source = requiredObject(input, "createGovernanceDecision");
106
+ const normalized = { ...source };
107
+ if (!Object.hasOwn(normalized, "claim_ref") && Object.hasOwn(normalized, "claimRef")) normalized.claim_ref = normalized.claimRef;
108
+ if (!Object.hasOwn(normalized, "claim_version") && Object.hasOwn(normalized, "claimVersion")) normalized.claim_version = normalized.claimVersion;
109
+ if (!Object.hasOwn(normalized, "policy_version") && Object.hasOwn(normalized, "policyVersion")) normalized.policy_version = normalized.policyVersion;
110
+ if (typeof normalized.decision === "string") normalized.decision = normalized.decision.toUpperCase();
111
+ delete normalized.claimRef;
112
+ delete normalized.claimVersion;
113
+ delete normalized.policyVersion;
114
+ return createRecord(normalized, "governance_decision", "createGovernanceDecision");
115
+ }
116
+
117
+ export const makeGovernanceDecision = createGovernanceDecision;
118
+
119
+ export function createPolicyDisposition(input) {
120
+ const source = requiredObject(input, "createPolicyDisposition");
121
+ const normalized = { ...source };
122
+ if (!Object.hasOwn(normalized, "claim_ref") && Object.hasOwn(normalized, "claimRef")) normalized.claim_ref = normalized.claimRef;
123
+ if (!Object.hasOwn(normalized, "claim_version") && Object.hasOwn(normalized, "claimVersion")) normalized.claim_version = normalized.claimVersion;
124
+ if (!Object.hasOwn(normalized, "policy_version") && Object.hasOwn(normalized, "policyVersion")) normalized.policy_version = normalized.policyVersion;
125
+ delete normalized.claimRef;
126
+ delete normalized.claimVersion;
127
+ delete normalized.policyVersion;
128
+ return createRecord(normalized, "policy_disposition", "createPolicyDisposition");
129
+ }
130
+
131
+ export const makePolicyDisposition = createPolicyDisposition;
132
+
133
+ function linksFrom(input) {
134
+ if (Array.isArray(input)) return input;
135
+ const source = requiredObject(input, "aggregateEvidence");
136
+ return own(source, "evidence_links", "evidenceLinks", "evidence") ?? [];
137
+ }
138
+
139
+ function polarityOf(link) {
140
+ const value = own(link, "polarity", "stance", "direction");
141
+ if (typeof value !== "string") return undefined;
142
+ const polarity = value.toUpperCase();
143
+ if (["SUPPORT", "SUPPORTED", "POSITIVE", "FOR"].includes(polarity)) return "SUPPORT";
144
+ if (["REFUTE", "REFUTED", "NEGATIVE", "AGAINST", "CONTRADICT"].includes(polarity)) return "REFUTE";
145
+ return undefined;
146
+ }
147
+
148
+ export function aggregateEvidence(input) {
149
+ const source = Array.isArray(input) ? {} : requiredObject(input, "aggregateEvidence");
150
+ const links = linksFrom(input).filter((link) => link && typeof link === "object");
151
+ const support = links.filter((link) => polarityOf(link) === "SUPPORT");
152
+ const refute = links.filter((link) => polarityOf(link) === "REFUTE");
153
+ const state = support.length && refute.length ? "BOTH" : support.length ? "SUPPORTED" : refute.length ? "REFUTED" : "NEITHER";
154
+ const groups = (items) => [...new Set(items.map((link) => own(link, "independence_group", "independenceGroup")).filter((value) => typeof value === "string"))].sort();
155
+ return deepFreeze({
156
+ claim_ref: own(source.claim, "stable_ref", "stableRef") ?? own(source, "claim_ref", "claimRef"),
157
+ claim_version: own(source.claim, "version_id", "versionId") ?? own(source, "claim_version", "claimVersion"),
158
+ state,
159
+ evidence_state: state,
160
+ support_count: support.length,
161
+ refute_count: refute.length,
162
+ support_groups: groups(support),
163
+ refute_groups: groups(refute),
164
+ evidence_links: links,
165
+ });
166
+ }
167
+
168
+ export const aggregateEvidenceState = aggregateEvidence;