@warpgogol/werkstatt-shared 0.14.1 → 0.15.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 (438) hide show
  1. package/README.md +23 -15
  2. package/README.uk.md +1 -1
  3. package/package.json +517 -766
  4. package/src/{share/agent → agent}/ard-catalog.ts +6 -1
  5. package/src/{share/agent → agent}/dns-aid.test.ts +2 -2
  6. package/src/{share/agent → agent}/manifest.ts +15 -1
  7. package/src/{share/agent → agent}/search.ts +6 -1
  8. package/src/{share → agent}/tests/api-catalog.test.ts +2 -2
  9. package/src/{share → agent}/tests/ard-catalog.test.ts +2 -2
  10. package/src/{share → agent}/tests/capability.test.ts +1 -1
  11. package/src/{share → agent}/tests/fleet-catalog.test.ts +1 -1
  12. package/src/{share → agent}/tests/knowledge.test.ts +2 -2
  13. package/src/{share → agent}/tests/manifest.test.ts +1 -1
  14. package/src/{share → agent}/tests/mcp-card.test.ts +2 -2
  15. package/src/{share → agent}/tests/openapi.test.ts +2 -2
  16. package/src/{share → agent}/tests/proof.test.ts +1 -1
  17. package/src/{share → attribution}/material-credits.ts +4 -4
  18. package/src/{share → checks}/check-hints.ts +6 -1
  19. package/src/checks/index.ts +4 -1
  20. package/src/checks/lib/astro-site-url.ts +6 -1
  21. package/src/checks/lib/i18n.ts +14 -0
  22. package/src/checks/result-helpers.ts +2 -2
  23. package/src/checks/tests/remediation-auto-populate.test.ts +1 -1
  24. package/src/{share → client-scripts}/counter-utils.ts +6 -2
  25. package/src/{share/scripts → client-scripts}/external-link-qr.ts +11 -16
  26. package/src/{share/scripts → client-scripts}/external-links.ts +12 -1
  27. package/src/{share/scripts → client-scripts}/gsap-counter.ts +8 -3
  28. package/src/client-scripts/index.ts +35 -0
  29. package/src/{share/scripts → client-scripts}/inline-number-animation.ts +1 -1
  30. package/src/{share/scripts → client-scripts}/lenis.ts +6 -1
  31. package/src/{share/scripts → client-scripts}/lordicon.ts +6 -1
  32. package/src/{share/scripts → client-scripts}/orchestrator.ts +10 -1
  33. package/src/{share/scripts → client-scripts}/scheduler.ts +6 -1
  34. package/src/client-scripts/scroll-spy.ts +106 -0
  35. package/src/client-scripts/tests/orchestrator.test.ts +47 -0
  36. package/src/client-scripts/tests/scroll-spy.test.ts +204 -0
  37. package/src/{share/scripts → client-scripts/tests}/video-player.test.ts +51 -31
  38. package/src/component/contracts.ts +206 -0
  39. package/src/component/index.ts +11 -0
  40. package/src/{share → content}/content-reference.ts +3 -3
  41. package/src/{share/content → content}/entity-id.ts +6 -3
  42. package/src/content/index.ts +3 -0
  43. package/src/{share/content → content}/merge.ts +6 -1
  44. package/src/content/system-manifest.test.ts +107 -0
  45. package/src/content/system-manifest.ts +12 -94
  46. package/src/{share → content}/tests/resolve-field-path.test.ts +1 -1
  47. package/src/{share → content}/tests/substitute-deep.test.ts +1 -1
  48. package/src/content-discipline/index.ts +19 -0
  49. package/src/{share/content-discipline → content-discipline}/parsers.ts +2 -2
  50. package/src/{share/env.d.ts → env.d.ts} +6 -3
  51. package/src/fingerprint/canonical-json.ts +731 -0
  52. package/src/fingerprint/index.ts +12 -0
  53. package/src/fingerprint/primitives.ts +72 -0
  54. package/src/{share/i18n → i18n}/localization.ts +6 -2
  55. package/src/integration/adapters.ts +10 -71
  56. package/src/integration/dispatch.ts +1 -1
  57. package/src/integration/funnel.ts +6 -1
  58. package/src/integration/index.ts +101 -4
  59. package/src/integration/lagebild-destination-adapter.ts +108 -0
  60. package/src/integration/lagebild-ingress.ts +7 -12
  61. package/src/integration/orchestration.ts +11 -10
  62. package/src/integration/tests/deliver-event.test.ts +1 -1
  63. package/src/integration/tests/dispatch.test.ts +4 -4
  64. package/src/integration/tests/hub-runtime.test.ts +22 -14
  65. package/src/integration/vitest.config.ts +1 -0
  66. package/src/kernel/desired-state.ts +227 -0
  67. package/src/kernel/diagnostic.ts +289 -0
  68. package/src/kernel/fs-atomic.ts +131 -0
  69. package/src/kernel/index.ts +15 -0
  70. package/src/kernel/tests/fs-atomic.test.ts +150 -0
  71. package/src/kernel/tests/workspace-io.test.ts +187 -0
  72. package/src/kernel/types.ts +459 -0
  73. package/src/kernel/workspace-io.ts +248 -0
  74. package/src/{share → knowledge}/tests/claim.test.ts +1 -1
  75. package/src/{share → knowledge}/tests/derivation.test.ts +1 -1
  76. package/src/{share → knowledge}/tests/freshness.test.ts +1 -1
  77. package/src/{share → knowledge}/tests/ledger.test.ts +1 -1
  78. package/src/{share → knowledge}/tests/plan.test.ts +1 -1
  79. package/src/{share → knowledge}/tests/source.test.ts +1 -1
  80. package/src/legal/index.ts +15 -0
  81. package/src/{share/middleware → middleware}/access-protection.ts +8 -8
  82. package/src/{share → node}/dev-props-validator.ts +6 -1
  83. package/src/{share → node}/import-scan.ts +23 -3
  84. package/src/{share → node}/semantic/derived-prices-loader.ts +4 -2
  85. package/src/{share → node}/semantic/markdown-twin-provenance.ts +1 -1
  86. package/src/{share/semantic → node}/tests/derived-prices-loader.test.ts +14 -1
  87. package/src/{share/semantic → node}/tests/markdown-twin-provenance.test.ts +1 -1
  88. package/src/observability/env-resolver.ts +6 -1
  89. package/src/observability/index.ts +6 -1
  90. package/src/observability/otlp-converter.ts +6 -1
  91. package/src/observability/otlp-transport.ts +6 -1
  92. package/src/observability/pusher.ts +6 -1
  93. package/src/observability/redact.ts +6 -1
  94. package/src/observability/typed-refs.ts +6 -2
  95. package/src/observability/vitest.config.ts +1 -0
  96. package/src/{share → offers}/formula-eval.ts +3 -3
  97. package/src/{share → offers}/tests/formula-eval.test.ts +2 -2
  98. package/src/onboarding/index.ts +1 -0
  99. package/src/{share → onboarding}/tests/onboarding-yaml.test.ts +1 -1
  100. package/src/ontology/archetype-registry.ts +6 -1
  101. package/src/ontology/archetypes/index.json +33 -1
  102. package/src/ontology/archetypes/index.yaml +27 -1
  103. package/src/ontology/archetypes/sections/entity-grid.yaml +46 -0
  104. package/src/ontology/archetypes/sections/exit-diagram.yaml +43 -0
  105. package/src/ontology/biome-token-projection.ts +6 -1
  106. package/src/ontology/cosmic/index.ts +6 -1
  107. package/src/ontology/cosmic/moon-catalog.ts +6 -1
  108. package/src/ontology/cosmic/planet-catalog.ts +6 -1
  109. package/src/ontology/cosmic/star-catalog.ts +6 -1
  110. package/src/ontology/enums.ts +6 -1
  111. package/src/ontology/external-surfaces/index.ts +1 -1
  112. package/src/ontology/index.ts +6 -2
  113. package/src/ontology/manifest.ts +7 -4
  114. package/src/ontology/operations/artifact-store.ts +45 -0
  115. package/src/ontology/operations/dht.ts +98 -0
  116. package/src/ontology/operations/handoff.ts +92 -0
  117. package/src/ontology/operations/index.ts +187 -1
  118. package/src/ontology/operations/leitstand.ts +99 -0
  119. package/src/ontology/operations/materialization.ts +90 -0
  120. package/src/ontology/operations/mission.ts +111 -0
  121. package/src/ontology/operations/naming-policy.ts +132 -0
  122. package/src/ontology/operations/notausgang.ts +64 -0
  123. package/src/ontology/operations/release.ts +121 -0
  124. package/src/ontology/operations/sternsystem.ts +192 -0
  125. package/src/ontology/operations/werkstatt.ts +48 -0
  126. package/src/ontology/schemas/biome.ts +7 -2
  127. package/src/ontology/schemas/capability.ts +1 -1
  128. package/src/ontology/schemas/constellation.ts +6 -1
  129. package/src/ontology/schemas/index.ts +6 -2
  130. package/src/ontology/schemas/manifest-resolver.ts +6 -1
  131. package/src/ontology/schemas/page-entry.ts +9 -5
  132. package/src/ontology/schemas/system/growth.ts +6 -1
  133. package/src/ontology/schemas/system/manifest.ts +40 -1
  134. package/src/ontology/schemas/system/page-output.ts +7 -6
  135. package/src/ontology/schemas/system/text.ts +15 -0
  136. package/src/ontology/schemas/system.ts +6 -2
  137. package/src/ontology/shared-section-props/body-fragments.ts +2 -0
  138. package/src/ontology/tests/sternsystem-owner.test.ts +1 -1
  139. package/src/ontology/vitest.config.ts +1 -0
  140. package/src/{share → page}/page.ts +13 -9
  141. package/src/{share → page}/tests/dev-props-validator.test.ts +1 -1
  142. package/src/{share → page}/tests/hero-section-numbering-parity.test.ts +1 -1
  143. package/src/passport/index.ts +6 -1
  144. package/src/passport/key-rotate.ts +10 -2
  145. package/src/passport/schema.ts +6 -1
  146. package/src/passport/sign.ts +8 -3
  147. package/src/passport/vitest.config.ts +1 -0
  148. package/src/pbt/index.test.ts +95 -0
  149. package/src/pbt/index.ts +251 -0
  150. package/src/plugin/plugin-contract.ts +1 -1
  151. package/src/{share → policy}/entitlement.ts +24 -4
  152. package/src/{share → policy}/feature-policy.ts +8 -3
  153. package/src/{share → policy}/visibility.ts +7 -2
  154. package/src/redirects/redirects.ts +117 -0
  155. package/src/{share → redirects/tests}/redirects.test.ts +1 -1
  156. package/src/remediation/remediation-catalog.ts +316 -0
  157. package/src/{share/routes → routes}/template-filter.ts +1 -1
  158. package/src/{share → runtime}/runtime-context.ts +7 -2
  159. package/src/{share → runtime}/shared-context.ts +1 -1
  160. package/src/{share/schemas → schemas}/claims.ts +2 -2
  161. package/src/{share/schemas → schemas}/effects.ts +6 -1
  162. package/src/{share/schemas → schemas}/features.ts +1 -1
  163. package/src/{share/schemas → schemas}/index.ts +3 -3
  164. package/src/{share/schemas → schemas}/navigation.ts +6 -1
  165. package/src/{share/schemas → schemas}/page-base.ts +6 -2
  166. package/src/{share/schemas → schemas}/person.ts +1 -1
  167. package/src/{share/schemas → schemas}/standard-list-item.ts +1 -1
  168. package/src/{share → sections}/rfc0042-utils.ts +1 -1
  169. package/src/{share/semantic → semantic}/block-extractors/index.ts +20 -1
  170. package/src/{share/semantic → semantic}/business-projection.ts +95 -1
  171. package/src/semantic/canonical-uri.ts +54 -0
  172. package/src/{share/semantic → semantic}/extract.ts +100 -1
  173. package/src/semantic/fact-extraction.test.ts +47 -0
  174. package/src/semantic/fact-extraction.ts +46 -0
  175. package/src/semantic/freshness.ts +67 -0
  176. package/src/{share/semantic → semantic}/ids.ts +6 -1
  177. package/src/{share/semantic → semantic}/index.ts +9 -2
  178. package/src/{share/semantic → semantic}/jsonld/breadcrumb.ts +6 -2
  179. package/src/{share/semantic → semantic}/jsonld/context.ts +6 -2
  180. package/src/{share/semantic → semantic}/jsonld/faq.ts +6 -2
  181. package/src/{share/semantic → semantic}/jsonld/initiative.ts +6 -1
  182. package/src/{share/semantic → semantic}/jsonld/organization.ts +6 -1
  183. package/src/{share/semantic → semantic}/jsonld/person.ts +6 -2
  184. package/src/{share/semantic → semantic}/jsonld/service.ts +6 -2
  185. package/src/semantic/jsonld/shared.ts +36 -0
  186. package/src/semantic/jsonld/types.ts +24 -0
  187. package/src/{share/semantic → semantic}/jsonld/webpage.ts +6 -1
  188. package/src/semantic/jsonld/website.ts +32 -0
  189. package/src/{share/semantic → semantic}/jsonld.ts +6 -1
  190. package/src/{share/semantic → semantic}/llms.ts +41 -3
  191. package/src/{share/semantic → semantic}/models.ts +26 -38
  192. package/src/{share/semantic → semantic}/organization-profile.ts +3 -0
  193. package/src/{share/semantic → semantic}/page-builders/markdown-page.ts +6 -3
  194. package/src/{share/semantic → semantic}/page-utils.ts +6 -3
  195. package/src/{share/tests/breadcrumbs.test.ts → semantic/tests/breadcrumbs-0229.test.ts} +1 -1
  196. package/src/{share → semantic}/tests/build-page-price-markers.test.ts +2 -2
  197. package/src/{share/semantic → semantic}/tests/business-projection.test.ts +179 -0
  198. package/src/semantic/tests/canonical-uri.test.ts +56 -0
  199. package/src/{share/semantic → semantic}/tests/extract.test.ts +83 -0
  200. package/src/semantic/tests/freshness.pbt.test.ts +108 -0
  201. package/src/{share/tests/image-sitemap.test.ts → semantic/tests/image-sitemap-0150.test.ts} +1 -1
  202. package/src/{share → semantic}/tests/jsonld-service.test.ts +2 -2
  203. package/src/{share → semantic}/tests/jsonld-video.test.ts +3 -3
  204. package/src/{share → semantic}/tests/jsonld-webpage.test.ts +3 -3
  205. package/src/{share → semantic}/tests/llms-0789.test.ts +2 -2
  206. package/src/{share/semantic → semantic}/tests/llms.test.ts +46 -0
  207. package/src/{share → semantic}/tests/organization-jsonld.test.ts +3 -3
  208. package/src/{share/tests/output-projection.test.ts → semantic/tests/output-projection-0143.test.ts} +1 -1
  209. package/src/{share/tests/price-marker-resolver.test.ts → semantic/tests/price-marker-resolver-0767.test.ts} +2 -2
  210. package/src/{share/semantic → semantic}/tests/price-marker-resolver.test.ts +4 -2
  211. package/src/semantic/tests/project-claims.pbt.test.ts +60 -0
  212. package/src/{share/semantic → semantic}/tests/split-sentences.test.ts +1 -1
  213. package/src/{share → semantic}/tests/twin-path.test.ts +1 -1
  214. package/src/signing/index.ts +13 -0
  215. package/src/signing/key.ts +139 -0
  216. package/src/signing/sign.ts +66 -0
  217. package/src/signing/types.ts +29 -0
  218. package/src/{share/slug → slug}/slug-url.ts +4 -1
  219. package/src/stack/run-tool.ts +108 -0
  220. package/src/stack/stack-checks.ts +124 -0
  221. package/src/stack/tests/run-tool.test.ts +138 -0
  222. package/src/stack/tests/stack-checks.test.ts +141 -0
  223. package/src/stack/tests/walk-files.test.ts +131 -0
  224. package/src/stack/walk-files.ts +114 -0
  225. package/src/surface/blueprint.ts +342 -66
  226. package/src/surface/decision-composer.ts +6 -1
  227. package/src/surface/geo.ts +6 -1
  228. package/src/surface/index.ts +11 -7
  229. package/src/surface/types.ts +2 -2
  230. package/src/surface/vitest.config.ts +1 -0
  231. package/src/tests/signing-core.test.ts +129 -0
  232. package/src/{share → text}/css-value-normalize.ts +6 -1
  233. package/src/text/string-utils.ts +24 -0
  234. package/src/{share → text}/tests/text-normalize.test.ts +5 -0
  235. package/src/{share → text}/text-normalize.ts +4 -3
  236. package/src/{share → text}/wrap-inline-numbers.ts +1 -1
  237. package/src/types/axiom-study.d.ts +36 -0
  238. package/src/types/hls-js-light.d.ts +21 -0
  239. package/src/types/qrcode-browser.d.ts +23 -0
  240. package/src/typography/fix.pbt.test.ts +208 -0
  241. package/src/typography/fix.ts +148 -0
  242. package/src/typography/index.ts +54 -0
  243. package/src/typography/locale-defaults.ts +97 -0
  244. package/src/typography/rules-tier1.test.ts +459 -0
  245. package/src/typography/rules-tier1.ts +925 -0
  246. package/src/typography/rules-tier2-locale.pbt.test.ts +190 -0
  247. package/src/typography/rules-tier2-locale.test.ts +286 -0
  248. package/src/typography/rules-tier2-locale.ts +399 -0
  249. package/src/typography/rules-tier2-structure.test.ts +330 -0
  250. package/src/typography/rules-tier2-structure.ts +378 -0
  251. package/src/typography/rules-tier3-advisory.test.ts +290 -0
  252. package/src/typography/rules-tier3-advisory.ts +239 -0
  253. package/src/typography/text-surface.test.ts +244 -0
  254. package/src/typography/text-surface.ts +448 -0
  255. package/src/index.ts +0 -14
  256. package/src/integration/port-barrel.ts +0 -105
  257. package/src/share/agent/tests/api-catalog.test.ts +0 -8
  258. package/src/share/agent/tests/ard-catalog.test.ts +0 -8
  259. package/src/share/agent/tests/capability.test.ts +0 -14
  260. package/src/share/agent/tests/fleet-catalog.test.ts +0 -15
  261. package/src/share/agent/tests/knowledge.test.ts +0 -20
  262. package/src/share/agent/tests/manifest.test.ts +0 -9
  263. package/src/share/agent/tests/mcp-card.test.ts +0 -8
  264. package/src/share/agent/tests/openapi.test.ts +0 -8
  265. package/src/share/agent/tests/proof.test.ts +0 -15
  266. package/src/share/content/index.ts +0 -16
  267. package/src/share/content-discipline/index.ts +0 -14
  268. package/src/share/index.ts +0 -86
  269. package/src/share/knowledge/tests/claim.test.ts +0 -11
  270. package/src/share/knowledge/tests/derivation.test.ts +0 -30
  271. package/src/share/knowledge/tests/freshness.test.ts +0 -18
  272. package/src/share/knowledge/tests/ledger.test.ts +0 -35
  273. package/src/share/knowledge/tests/plan.test.ts +0 -19
  274. package/src/share/knowledge/tests/source.test.ts +0 -19
  275. package/src/share/legal/index.ts +0 -1
  276. package/src/share/redirects.ts +0 -54
  277. package/src/share/remediation/remediation-catalog.ts +0 -196
  278. package/src/share/scripts/index.ts +0 -26
  279. package/src/share/scripts/tests/orchestrator.test.ts +0 -8
  280. package/src/share/semantic/jsonld/shared.ts +0 -31
  281. package/src/share/semantic/jsonld/types.ts +0 -19
  282. package/src/share/semantic/jsonld/website.ts +0 -27
  283. package/src/share/string-utils.ts +0 -19
  284. package/src/share/types/axiom-study.d.ts +0 -22
  285. package/src/share/types/hls-js-light.d.ts +0 -16
  286. package/src/share/types/qrcode-browser.d.ts +0 -18
  287. package/src/share/utility-registry.yaml +0 -50
  288. package/src/share/vitest.config.ts +0 -8
  289. package/src/surface/blueprint-schema.ts +0 -314
  290. package/src/surface/blueprint-types.ts +0 -256
  291. /package/src/{share/agent → agent}/api-catalog.ts +0 -0
  292. /package/src/{share/agent → agent}/capability.ts +0 -0
  293. /package/src/{share/agent → agent}/dns-aid.ts +0 -0
  294. /package/src/{share/agent → agent}/fleet-catalog.ts +0 -0
  295. /package/src/{share/agent → agent}/index.ts +0 -0
  296. /package/src/{share/agent → agent}/knowledge.ts +0 -0
  297. /package/src/{share/agent → agent}/mcp-card.ts +0 -0
  298. /package/src/{share/agent → agent}/omit-empty.ts +0 -0
  299. /package/src/{share/agent → agent}/openapi.ts +0 -0
  300. /package/src/{share/agent → agent}/proof.ts +0 -0
  301. /package/src/{share/agent → agent}/search.test.ts +0 -0
  302. /package/src/{share/agent → agent}/tests/omit-empty.test.ts +0 -0
  303. /package/src/{share → attribution}/attribution-display.ts +0 -0
  304. /package/src/{share/scripts → client-scripts}/gsap-mountain-journey.ts +0 -0
  305. /package/src/{share/scripts → client-scripts}/gsap-parallax.ts +0 -0
  306. /package/src/{share/scripts → client-scripts}/gsap-reveal.ts +0 -0
  307. /package/src/{share/scripts → client-scripts}/gsap-stagger.ts +0 -0
  308. /package/src/{share/scripts → client-scripts}/live-photos.ts +0 -0
  309. /package/src/{share/scripts → client-scripts}/tests/external-link-qr.test.ts +0 -0
  310. /package/src/{share/scripts → client-scripts}/tests/external-links.test.ts +0 -0
  311. /package/src/{share/scripts → client-scripts}/tests/gsap-counter.test.ts +0 -0
  312. /package/src/{share/scripts → client-scripts}/tests/gsap-mountain-journey.test.ts +0 -0
  313. /package/src/{share/scripts → client-scripts}/tests/gsap-parallax.test.ts +0 -0
  314. /package/src/{share/scripts → client-scripts}/tests/gsap-reveal.test.ts +0 -0
  315. /package/src/{share/scripts → client-scripts}/tests/gsap-stagger.test.ts +0 -0
  316. /package/src/{share/scripts → client-scripts}/tests/inline-number-animation.test.ts +0 -0
  317. /package/src/{share/scripts → client-scripts}/tests/lenis.test.ts +0 -0
  318. /package/src/{share/scripts → client-scripts}/tests/live-photos.test.ts +0 -0
  319. /package/src/{share/scripts → client-scripts}/tests/lordicon.test.ts +0 -0
  320. /package/src/{share/scripts → client-scripts}/tests/scheduler.test.ts +0 -0
  321. /package/src/{share/scripts → client-scripts}/video-player.ts +0 -0
  322. /package/src/{share/content → content}/resolve-field-path.ts +0 -0
  323. /package/src/{share/content → content}/substitute-deep.ts +0 -0
  324. /package/src/{share → content}/tests/content-ref-index.test.ts +0 -0
  325. /package/src/{share/content-discipline → content-discipline}/types.ts +0 -0
  326. /package/src/{share → image-provider}/image-provider.ts +0 -0
  327. /package/src/{share/knowledge → knowledge}/claim.ts +0 -0
  328. /package/src/{share/knowledge → knowledge}/derivation.ts +0 -0
  329. /package/src/{share/knowledge → knowledge}/freshness.ts +0 -0
  330. /package/src/{share/knowledge → knowledge}/index.ts +0 -0
  331. /package/src/{share/knowledge → knowledge}/ledger.ts +0 -0
  332. /package/src/{share/knowledge → knowledge}/plan.ts +0 -0
  333. /package/src/{share/knowledge → knowledge}/source.ts +0 -0
  334. /package/src/{share/legal → legal}/translation-policy.ts +0 -0
  335. /package/src/{share/middleware → middleware}/language-redirect.ts +0 -0
  336. /package/src/{share/middleware → middleware}/tests/access-protection.test.ts +0 -0
  337. /package/src/{share → node}/fs/index.ts +0 -0
  338. /package/src/{share/tests/markdown-twin-provenance.test.ts → node/tests/markdown-twin-provenance-0613.test.ts} +0 -0
  339. /package/src/{share → offers}/offer-capacity.ts +0 -0
  340. /package/src/{share/onboarding-yaml/index.ts → onboarding/yaml.ts} +0 -0
  341. /package/src/{share → ontology}/tests/ontology-role-by-cosmic-name.test.ts +0 -0
  342. /package/src/{share/routes → routes}/tests/template-filter.test.ts +0 -0
  343. /package/src/{share/schemas → schemas}/claim-records.ts +0 -0
  344. /package/src/{share/schemas → schemas}/horizontal-align.ts +0 -0
  345. /package/src/{share/schemas → schemas}/icon-color.ts +0 -0
  346. /package/src/{share/schemas → schemas}/live-photo.ts +0 -0
  347. /package/src/{share/schemas → schemas}/material-credit.ts +0 -0
  348. /package/src/{share/schemas → schemas}/media.ts +0 -0
  349. /package/src/{share/schemas → schemas}/participant.ts +0 -0
  350. /package/src/{share/schemas → schemas}/print.ts +0 -0
  351. /package/src/{share/schemas → schemas}/section-background.ts +0 -0
  352. /package/src/{share/schemas → schemas}/section-body.ts +0 -0
  353. /package/src/{share/schemas → schemas}/section-cards.ts +0 -0
  354. /package/src/{share/schemas → schemas}/section-cta.ts +0 -0
  355. /package/src/{share/schemas → schemas}/section-header.ts +0 -0
  356. /package/src/{share/schemas → schemas}/section-image.ts +0 -0
  357. /package/src/{share/schemas → schemas}/section-motion.ts +0 -0
  358. /package/src/{share/schemas → schemas}/section-shell.ts +0 -0
  359. /package/src/{share/schemas → schemas}/section-stats.ts +0 -0
  360. /package/src/{share/schemas → schemas}/site-background.ts +0 -0
  361. /package/src/{share/schemas → schemas}/tests/claim-records.test.ts +0 -0
  362. /package/src/{share/schemas → schemas}/tests/claims.test.ts +0 -0
  363. /package/src/{share/schemas → schemas}/tests/effects.test.ts +0 -0
  364. /package/src/{share/schemas → schemas}/tests/features.test.ts +0 -0
  365. /package/src/{share/schemas → schemas}/tests/horizontal-align.test.ts +0 -0
  366. /package/src/{share/schemas → schemas}/tests/icon-color.test.ts +0 -0
  367. /package/src/{share/schemas → schemas}/tests/live-photo.test.ts +0 -0
  368. /package/src/{share/schemas → schemas}/tests/material-credit.test.ts +0 -0
  369. /package/src/{share/schemas → schemas}/tests/media.test.ts +0 -0
  370. /package/src/{share/schemas → schemas}/tests/navigation.test.ts +0 -0
  371. /package/src/{share/schemas → schemas}/tests/page-base.test.ts +0 -0
  372. /package/src/{share/schemas → schemas}/tests/participant.test.ts +0 -0
  373. /package/src/{share/schemas → schemas}/tests/person.test.ts +0 -0
  374. /package/src/{share/schemas → schemas}/tests/print.test.ts +0 -0
  375. /package/src/{share/schemas → schemas}/tests/section-background.test.ts +0 -0
  376. /package/src/{share/schemas → schemas}/tests/section-body.test.ts +0 -0
  377. /package/src/{share/schemas → schemas}/tests/section-cards.test.ts +0 -0
  378. /package/src/{share/schemas → schemas}/tests/section-cta.test.ts +0 -0
  379. /package/src/{share/schemas → schemas}/tests/section-header.test.ts +0 -0
  380. /package/src/{share/schemas → schemas}/tests/section-image.test.ts +0 -0
  381. /package/src/{share/schemas → schemas}/tests/section-motion.test.ts +0 -0
  382. /package/src/{share/schemas → schemas}/tests/section-shell.test.ts +0 -0
  383. /package/src/{share/schemas → schemas}/tests/section-stats.test.ts +0 -0
  384. /package/src/{share/schemas → schemas}/tests/site-background.test.ts +0 -0
  385. /package/src/{share/schemas → schemas}/tests/standard-list-item.test.ts +0 -0
  386. /package/src/{share/semantic → semantic}/ai.ts +0 -0
  387. /package/src/{share/semantic → semantic}/block-extraction.ts +0 -0
  388. /package/src/{share/semantic → semantic}/breadcrumbs.ts +0 -0
  389. /package/src/{share/semantic → semantic}/build-page.ts +0 -0
  390. /package/src/{share/semantic → semantic}/feed.ts +0 -0
  391. /package/src/{share/semantic → semantic}/image-sitemap.ts +0 -0
  392. /package/src/{share/semantic → semantic}/jsonld/article.ts +0 -0
  393. /package/src/{share/semantic → semantic}/jsonld/collection-list.ts +0 -0
  394. /package/src/{share/semantic → semantic}/jsonld/tests/article.test.ts +0 -0
  395. /package/src/{share/semantic → semantic}/jsonld/tests/breadcrumb.test.ts +0 -0
  396. /package/src/{share/semantic → semantic}/jsonld/tests/collection-list.test.ts +0 -0
  397. /package/src/{share/semantic → semantic}/jsonld/tests/faq.test.ts +0 -0
  398. /package/src/{share/semantic → semantic}/jsonld/tests/initiative.test.ts +0 -0
  399. /package/src/{share/semantic → semantic}/jsonld/tests/organization.test.ts +0 -0
  400. /package/src/{share/semantic → semantic}/jsonld/tests/person.test.ts +0 -0
  401. /package/src/{share/semantic → semantic}/jsonld/tests/service.test.ts +0 -0
  402. /package/src/{share/semantic → semantic}/jsonld/tests/shared.test.ts +0 -0
  403. /package/src/{share/semantic → semantic}/jsonld/tests/video.test.ts +0 -0
  404. /package/src/{share/semantic → semantic}/jsonld/tests/webpage.test.ts +0 -0
  405. /package/src/{share/semantic → semantic}/jsonld/tests/website.test.ts +0 -0
  406. /package/src/{share/semantic → semantic}/jsonld/video.ts +0 -0
  407. /package/src/{share/semantic → semantic}/llms-policy.ts +0 -0
  408. /package/src/{share/semantic → semantic}/markdown-hygiene.ts +0 -0
  409. /package/src/{share/semantic → semantic}/output-projection.ts +0 -0
  410. /package/src/{share/semantic → semantic}/page-markdown.ts +0 -0
  411. /package/src/{share/semantic → semantic}/price-marker-resolver.ts +0 -0
  412. /package/src/{share/semantic → semantic}/robots.ts +0 -0
  413. /package/src/{share/semantic → semantic}/tests/ai.test.ts +0 -0
  414. /package/src/{share/semantic → semantic}/tests/block-extraction.test.ts +0 -0
  415. /package/src/{share/semantic → semantic}/tests/breadcrumbs.test.ts +0 -0
  416. /package/src/{share/semantic → semantic}/tests/build-page.test.ts +0 -0
  417. /package/src/{share/semantic → semantic}/tests/feed.test.ts +0 -0
  418. /package/src/{share/semantic → semantic}/tests/helpers.ts +0 -0
  419. /package/src/{share/semantic → semantic}/tests/ids.test.ts +0 -0
  420. /package/src/{share/semantic → semantic}/tests/image-sitemap.test.ts +0 -0
  421. /package/src/{share/semantic → semantic}/tests/jsonld.test.ts +0 -0
  422. /package/src/{share/semantic → semantic}/tests/llms-policy.test.ts +0 -0
  423. /package/src/{share/semantic → semantic}/tests/markdown-hygiene.test.ts +0 -0
  424. /package/src/{share/semantic → semantic}/tests/markdown-page.test.ts +0 -0
  425. /package/src/{share/semantic → semantic}/tests/organization-profile.test.ts +0 -0
  426. /package/src/{share/semantic → semantic}/tests/output-projection.test.ts +0 -0
  427. /package/src/{share/semantic → semantic}/tests/page-markdown.test.ts +0 -0
  428. /package/src/{share/semantic → semantic}/tests/page-utils.test.ts +0 -0
  429. /package/src/{share/semantic → semantic}/tests/robots.test.ts +0 -0
  430. /package/src/{share/semantic → semantic}/tests/update-stamp.test.ts +0 -0
  431. /package/src/{share/semantic → semantic}/update-stamp.ts +0 -0
  432. /package/src/{share/slug → slug}/heading-slugger.ts +0 -0
  433. /package/src/{share/slug → slug}/index.ts +0 -0
  434. /package/src/{share/slug → slug}/slug-id.ts +0 -0
  435. /package/src/{share/slug → slug}/strategies.ts +0 -0
  436. /package/src/{share/slug → slug}/tests/slug.test.ts +0 -0
  437. /package/src/{share → text}/tests/text-normalize.pbt.test.ts +0 -0
  438. /package/src/{share → text}/text-position.ts +0 -0
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
2
2
  import {
3
3
  extractEmail,
4
4
  extractListFacts,
5
+ extractLists,
5
6
  extractParagraphs,
6
7
  extractPostalAddress,
7
8
  mergePeople,
@@ -82,6 +83,88 @@ describe("extractListFacts", () => {
82
83
  });
83
84
  });
84
85
 
86
+ describe("extractLists", () => {
87
+ it("extracts a single bullet list with dash marker", () => {
88
+ const result = extractLists("- Item 1\n- Item 2\n- Item 3");
89
+ expect(result).toEqual([{ marker: "-", items: ["Item 1", "Item 2", "Item 3"] }]);
90
+ });
91
+
92
+ it("extracts a single bullet list with asterisk marker", () => {
93
+ const result = extractLists("* Item 1\n* Item 2");
94
+ expect(result).toEqual([{ marker: "*", items: ["Item 1", "Item 2"] }]);
95
+ });
96
+
97
+ it("extracts a single bullet list with plus marker", () => {
98
+ const result = extractLists("+ Item 1\n+ Item 2");
99
+ expect(result).toEqual([{ marker: "+", items: ["Item 1", "Item 2"] }]);
100
+ });
101
+
102
+ it("extracts an ordered list with digit marker", () => {
103
+ const result = extractLists("1. First\n2. Second\n3. Third");
104
+ expect(result).toEqual([{ marker: "digit", items: ["First", "Second", "Third"] }]);
105
+ });
106
+
107
+ it("groups consecutive list lines into a single list", () => {
108
+ const md = "- Alpha\n- Beta\n- Gamma";
109
+ const result = extractLists(md);
110
+ expect(result).toHaveLength(1);
111
+ expect(result[0].items).toHaveLength(3);
112
+ });
113
+
114
+ it("terminates list on blank line", () => {
115
+ const md = "- Item 1\n- Item 2\n\nProse text";
116
+ const result = extractLists(md);
117
+ expect(result).toHaveLength(1);
118
+ expect(result[0].items).toEqual(["Item 1", "Item 2"]);
119
+ });
120
+
121
+ it("terminates list on non-list line", () => {
122
+ const md = "- Item 1\n- Item 2\nSome prose line";
123
+ const result = extractLists(md);
124
+ expect(result).toHaveLength(1);
125
+ expect(result[0].items).toEqual(["Item 1", "Item 2"]);
126
+ });
127
+
128
+ it("separates lists broken by prose", () => {
129
+ const md = "- List 1 item\n\nSome prose\n\n- List 2 item";
130
+ const result = extractLists(md);
131
+ expect(result).toHaveLength(2);
132
+ expect(result[0].items).toEqual(["List 1 item"]);
133
+ expect(result[1].items).toEqual(["List 2 item"]);
134
+ });
135
+
136
+ it("skips lines inside fenced code blocks", () => {
137
+ const md = "- Real item\n\n```text\n- Not a list item\n- Also not\n```\n\n- Another real item";
138
+ const result = extractLists(md);
139
+ expect(result).toHaveLength(2);
140
+ expect(result[0].items).toEqual(["Real item"]);
141
+ expect(result[1].items).toEqual(["Another real item"]);
142
+ });
143
+
144
+ it("skips indented (nested) list lines — they do not start a new list", () => {
145
+ const md = "- Top level\n - Nested item\n - Another nested\n- Top level 2";
146
+ const result = extractLists(md);
147
+ expect(result).toHaveLength(1);
148
+ expect(result[0].items).toEqual(["Top level", "Top level 2"]);
149
+ });
150
+
151
+ it("returns empty array for empty input", () => {
152
+ expect(extractLists("")).toEqual([]);
153
+ });
154
+
155
+ it("returns empty array for prose with no lists", () => {
156
+ expect(extractLists("Just some prose. No lists here.")).toEqual([]);
157
+ });
158
+
159
+ it("separates lists with different marker types", () => {
160
+ const md = "- Dash item\n* Star item";
161
+ const result = extractLists(md);
162
+ expect(result).toHaveLength(2);
163
+ expect(result[0].marker).toBe("-");
164
+ expect(result[1].marker).toBe("*");
165
+ });
166
+ });
167
+
85
168
  describe("extractEmail", () => {
86
169
  it("extracts email from text", () => {
87
170
  expect(extractEmail("Contact us at info@example.com today")).toBe("info@example.com");
@@ -0,0 +1,108 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import fc from "fast-check";
3
+ import {
4
+ calculateAgeDays,
5
+ isAssessmentStale,
6
+ isItemStale,
7
+ DEFAULT_FRESHNESS_WINDOW_DAYS,
8
+ } from "../freshness.ts";
9
+
10
+ const validTimestamp = fc.integer({ min: 0, max: Date.now() });
11
+ const validDateFromTs = (ts: number) => new Date(ts);
12
+
13
+ describe("calculateAgeDays — property-based (AC-7)", () => {
14
+ it("is pure and deterministic — same input always produces same output", () => {
15
+ fc.assert(
16
+ fc.property(validTimestamp, validTimestamp, (observedTs, referenceTs) => {
17
+ const observed = validDateFromTs(observedTs);
18
+ const reference = validDateFromTs(referenceTs);
19
+ const a = calculateAgeDays(observed.toISOString(), reference);
20
+ const b = calculateAgeDays(observed.toISOString(), reference);
21
+ expect(a).toBe(b);
22
+ }),
23
+ { numRuns: 50 },
24
+ );
25
+ });
26
+
27
+ it("returns non-negative age when reference >= observed", () => {
28
+ fc.assert(
29
+ fc.property(
30
+ validTimestamp,
31
+ fc.integer({ min: 0, max: 365 * 10 }),
32
+ (observedTs, daysAfter) => {
33
+ const observed = validDateFromTs(observedTs);
34
+ const reference = new Date(observed.getTime() + daysAfter * 86400000);
35
+ const age = calculateAgeDays(observed.toISOString(), reference);
36
+ expect(age).toBeGreaterThanOrEqual(0);
37
+ },
38
+ ),
39
+ { numRuns: 50 },
40
+ );
41
+ });
42
+
43
+ it("returns negative age when reference < observed", () => {
44
+ fc.assert(
45
+ fc.property(
46
+ validTimestamp.filter((ts) => ts > 365 * 10 * 86400000),
47
+ fc.integer({ min: 1, max: 365 * 10 }),
48
+ (observedTs, daysBefore) => {
49
+ const observed = validDateFromTs(observedTs);
50
+ const reference = new Date(observed.getTime() - daysBefore * 86400000);
51
+ const age = calculateAgeDays(observed.toISOString(), reference);
52
+ expect(age).toBeLessThan(0);
53
+ },
54
+ ),
55
+ { numRuns: 50 },
56
+ );
57
+ });
58
+
59
+ it("returns Number.MAX_SAFE_INTEGER for invalid date strings", () => {
60
+ fc.assert(
61
+ fc.property(
62
+ fc.string().filter((s) => isNaN(new Date(s).getTime())),
63
+ (invalid) => {
64
+ expect(calculateAgeDays(invalid)).toBe(Number.MAX_SAFE_INTEGER);
65
+ },
66
+ ),
67
+ { numRuns: 50 },
68
+ );
69
+ });
70
+ });
71
+
72
+ describe("isAssessmentStale", () => {
73
+ it("returns true when age exceeds maxAgeDays", () => {
74
+ const reference = new Date("2026-09-12T00:00:00Z");
75
+ const assessment = {
76
+ observedAt: "2026-06-01T00:00:00Z",
77
+ freshness: { maxAgeDays: 90 },
78
+ };
79
+ expect(isAssessmentStale(assessment, reference)).toBe(true);
80
+ });
81
+
82
+ it("returns false when age is within maxAgeDays", () => {
83
+ const reference = new Date("2026-09-12T00:00:00Z");
84
+ const assessment = {
85
+ observedAt: "2026-08-01T00:00:00Z",
86
+ freshness: { maxAgeDays: 90 },
87
+ };
88
+ expect(isAssessmentStale(assessment, reference)).toBe(false);
89
+ });
90
+ });
91
+
92
+ describe("isItemStale", () => {
93
+ it("returns false when retrievedAt is missing", () => {
94
+ expect(isItemStale({}, DEFAULT_FRESHNESS_WINDOW_DAYS)).toBe(false);
95
+ });
96
+
97
+ it("returns true when age exceeds maxAgeDays", () => {
98
+ const reference = new Date("2026-09-12T00:00:00Z");
99
+ const item = { retrievedAt: "2026-01-01T00:00:00Z" };
100
+ expect(isItemStale(item, 180, reference)).toBe(true);
101
+ });
102
+
103
+ it("returns false when age is within maxAgeDays", () => {
104
+ const reference = new Date("2026-09-12T00:00:00Z");
105
+ const item = { retrievedAt: "2026-08-01T00:00:00Z" };
106
+ expect(isItemStale(item, 180, reference)).toBe(false);
107
+ });
108
+ });
@@ -5,7 +5,7 @@ import {
5
5
  isHtmlRedirectPage,
6
6
  isSyntheticPreviewUrl,
7
7
  type SitemapImageEntry,
8
- } from "../semantic/image-sitemap.ts";
8
+ } from "../image-sitemap.ts";
9
9
 
10
10
  const SITE = "https://example.org";
11
11
 
@@ -12,8 +12,8 @@
12
12
  */
13
13
 
14
14
  import { describe, expect, it } from "vitest";
15
- import { buildJsonLd } from "../semantic/jsonld.ts";
16
- import type { SemanticPageModel, SemanticOrganization } from "../semantic/models.ts";
15
+ import { buildJsonLd } from "../jsonld.ts";
16
+ import type { SemanticPageModel, SemanticOrganization } from "../models.ts";
17
17
 
18
18
  function makeOrg(): SemanticOrganization {
19
19
  return {
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { buildVideoObjectNodes } from "../semantic/jsonld/video.ts";
3
- import { createJsonLdContext } from "../semantic/jsonld/context.ts";
4
- import type { SemanticPageModel, VideoSeoData } from "../semantic/models.ts";
2
+ import { buildVideoObjectNodes } from "../jsonld/video.ts";
3
+ import { createJsonLdContext } from "../jsonld/context.ts";
4
+ import type { SemanticPageModel, VideoSeoData } from "../models.ts";
5
5
 
6
6
  /*
7
7
  <MODULE_CONTRACT>
@@ -8,9 +8,9 @@
8
8
  */
9
9
 
10
10
  import { describe, expect, it } from "vitest";
11
- import { buildWebPageNode } from "../semantic/jsonld/webpage.ts";
12
- import { createJsonLdContext } from "../semantic/jsonld/context.ts";
13
- import type { SemanticPageModel, SemanticOrganization } from "../semantic/models.ts";
11
+ import { buildWebPageNode } from "../jsonld/webpage.ts";
12
+ import { createJsonLdContext } from "../jsonld/context.ts";
13
+ import type { SemanticPageModel, SemanticOrganization } from "../models.ts";
14
14
 
15
15
  function makeModel(overrides: Partial<SemanticPageModel> = {}): SemanticPageModel {
16
16
  const org: SemanticOrganization = {
@@ -12,8 +12,8 @@
12
12
  */
13
13
 
14
14
  import { test, expect } from "vitest";
15
- import { buildLlmsIndex } from "../semantic/llms.ts";
16
- import type { SemanticSiteModel } from "../semantic/models.ts";
15
+ import { buildLlmsIndex } from "../llms.ts";
16
+ import type { SemanticSiteModel } from "../models.ts";
17
17
 
18
18
  function makeSite(overrides: Partial<SemanticSiteModel> = {}): SemanticSiteModel {
19
19
  return {
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
2
2
  import { buildLlmsFull, buildLlmsIndex } from "../llms.ts";
3
3
  import { makePage } from "./helpers.ts";
4
4
  import type { SemanticSiteModel } from "../models.ts";
5
+ import type { SemanticClaimProvenance } from "../business-projection.ts";
5
6
 
6
7
  function makeSite(overrides: Partial<SemanticSiteModel> = {}): SemanticSiteModel {
7
8
  const page = makePage();
@@ -115,4 +116,49 @@ describe("buildLlmsFull", () => {
115
116
  const result = buildLlmsFull(site);
116
117
  expect(result).toContain("## Organization facts");
117
118
  });
119
+
120
+ it("includes claims section when claims are present", () => {
121
+ const claims: SemanticClaimProvenance[] = [
122
+ {
123
+ id: "c1",
124
+ claimClass: "factual",
125
+ claimKind: "fact",
126
+ statement: "We are certified",
127
+ verificationLevel: "N2",
128
+ confidence: "high",
129
+ evidence: [{ id: "ev1", kind: "certificate", label: "ISO 27001", sha256: "abc123" }],
130
+ },
131
+ ];
132
+ const site = makeSite({ claims });
133
+ const result = buildLlmsFull(site);
134
+ expect(result).toContain("## Claims");
135
+ expect(result).toContain("### We are certified");
136
+ expect(result).toContain("- Class: factual");
137
+ expect(result).toContain("- Verification: N2");
138
+ expect(result).toContain("- Confidence: high");
139
+ expect(result).toContain("- Evidence:");
140
+ expect(result).toContain("certificate: ISO 27001");
141
+ expect(result).toContain("sha256: abc123");
142
+ });
143
+
144
+ it("omits claims section when no claims", () => {
145
+ const site = makeSite();
146
+ const result = buildLlmsFull(site);
147
+ expect(result).not.toContain("## Claims");
148
+ });
149
+
150
+ it("shows Evidence: none when claim has no evidence", () => {
151
+ const claims: SemanticClaimProvenance[] = [
152
+ {
153
+ id: "c1",
154
+ claimClass: "factual",
155
+ claimKind: "fact",
156
+ statement: "Unevidenced claim",
157
+ evidence: [],
158
+ },
159
+ ];
160
+ const site = makeSite({ claims });
161
+ const result = buildLlmsFull(site);
162
+ expect(result).toContain("- Evidence: none");
163
+ });
118
164
  });
@@ -8,9 +8,9 @@
8
8
  */
9
9
 
10
10
  import { describe, expect, it } from "vitest";
11
- import { buildOrganizationNode } from "../semantic/jsonld/organization.ts";
12
- import { createJsonLdContext } from "../semantic/jsonld/context.ts";
13
- import type { SemanticPageModel, SemanticOrganization } from "../semantic/models.ts";
11
+ import { buildOrganizationNode } from "../jsonld/organization.ts";
12
+ import { createJsonLdContext } from "../jsonld/context.ts";
13
+ import type { SemanticPageModel, SemanticOrganization } from "../models.ts";
14
14
 
15
15
  function makeOrgWithPrices(
16
16
  prices: Array<{ id: string; label: string; amount: string; currency?: string }>,
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { test, expect } from "vitest";
9
- import { resolvePageOutput } from "../semantic/output-projection.ts";
9
+ import { resolvePageOutput } from "../output-projection.ts";
10
10
 
11
11
  test("content defaults apply when no semanticType is given", () => {
12
12
  const result = resolvePageOutput(undefined, {});
@@ -12,8 +12,8 @@ import {
12
12
  OFFERING_URI_PREFIX,
13
13
  PRICE_MARKER_RE,
14
14
  type DerivedPriceEntry,
15
- } from "../semantic/price-marker-resolver.ts";
16
- import { loadDerivedPrices } from "../semantic/derived-prices-loader.ts";
15
+ } from "../price-marker-resolver.ts";
16
+ import { loadDerivedPrices } from "../../node/semantic/derived-prices-loader.ts";
17
17
 
18
18
  const fixturePrices: Record<string, DerivedPriceEntry[]> = {
19
19
  [`${OFFERING_URI_PREFIX}referral-fee`]: [
@@ -1,6 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import {
3
- AMOUNT_MARKER_RE,
4
3
  OFFERING_URI_PREFIX,
5
4
  PRICE_MARKER_RE,
6
5
  formatSourcePrice,
@@ -43,7 +42,10 @@ describe("resolvePriceMarkersForSemantic", () => {
43
42
  chargeRef: "monthly",
44
43
  targetCurrency: "USD",
45
44
  amount: { value: "15", currency: "USD" },
46
- trace: { source: { amount: "10", currency: "EUR" }, rate: { value: "0.9", pair: "EUR/USD" } },
45
+ trace: {
46
+ source: { amount: "10", currency: "EUR" },
47
+ rate: { value: "0.9", pair: "EUR/USD" },
48
+ },
47
49
  },
48
50
  ],
49
51
  };
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import fc from "fast-check";
3
+ import { projectClaims } from "../business-projection.ts";
4
+
5
+ describe("projectClaims — property-based (AC-4)", () => {
6
+ it("is deterministic — same input always produces same output", () => {
7
+ fc.assert(
8
+ fc.property(
9
+ fc.array(
10
+ fc.record({
11
+ id: fc.string({ minLength: 1 }),
12
+ status: fc.constant("published"),
13
+ statement: fc.string(),
14
+ claimClass: fc.string(),
15
+ claimKind: fc.string(),
16
+ }),
17
+ ),
18
+ (claims) => {
19
+ const a = projectClaims(claims, undefined);
20
+ const b = projectClaims(claims, undefined);
21
+ expect(a).toEqual(b);
22
+ },
23
+ ),
24
+ { numRuns: 50 },
25
+ );
26
+ });
27
+
28
+ it("only returns claims with status === published", () => {
29
+ fc.assert(
30
+ fc.property(
31
+ fc.array(
32
+ fc.record({
33
+ id: fc.string({ minLength: 1 }),
34
+ status: fc.oneof(fc.constant("published"), fc.constant("draft"), fc.constant("archived")),
35
+ statement: fc.string(),
36
+ claimClass: fc.string(),
37
+ claimKind: fc.string(),
38
+ }),
39
+ ),
40
+ (claims) => {
41
+ const result = projectClaims(claims, undefined);
42
+ for (const c of result) {
43
+ expect(c.id).toBeTruthy();
44
+ }
45
+ const publishedIds = claims.filter((c) => c.status === "published" && c.id).map((c) => c.id);
46
+ expect(result.map((c) => c.id).sort()).toEqual(publishedIds.sort());
47
+ },
48
+ ),
49
+ { numRuns: 50 },
50
+ );
51
+ });
52
+
53
+ it("returns empty array for empty or undefined input", () => {
54
+ fc.assert(
55
+ fc.property(fc.oneof(fc.constant(undefined), fc.constant([])), (input) => {
56
+ expect(projectClaims(input, undefined)).toEqual([]);
57
+ }),
58
+ );
59
+ });
60
+ });
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { splitSentences } from "@warpgogol/werkstatt-shared/share/semantic";
2
+ import { splitSentences } from "@warpgogol/werkstatt-shared/semantic";
3
3
 
4
4
  describe("splitSentences", () => {
5
5
  it("splits simple English sentences", () => {
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import { test, expect } from "vitest";
21
- import { markdownTwinRelPath, markdownTwinUrlPath } from "../semantic/ids.ts";
21
+ import { markdownTwinRelPath, markdownTwinUrlPath } from "../ids.ts";
22
22
 
23
23
  const supportedLangs = ["de", "en"] as const;
24
24
 
@@ -0,0 +1,13 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Barrel export for @warpgogol/werkstatt-shared/signing sub-path. Re-exports the Ed25519 signing core (key generation, sign/verify, signing types) sunk from @warpgogol/werkstatt-engine per RFC-1104.</purpose>
4
+ <non-goals>Does not re-export engine signing commands (signing-commands.ts stays in engine).</non-goals>
5
+ </MODULE_CONTRACT>
6
+ <CHANGE_SUMMARY>
7
+ <item>RFC-1104: initial barrel — signing core sunk from werkstatt-engine.</item>
8
+ </CHANGE_SUMMARY>
9
+ */
10
+
11
+ export * from "./types.ts";
12
+ export * from "./key.ts";
13
+ export * from "./sign.ts";
@@ -0,0 +1,139 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>RFC-0921: Shared Ed25519 key management — generation, loading, saving, and encoding helpers.</purpose>
4
+
5
+
6
+ <non-goals>
7
+ <item>Does not implement signing or verification — those live in sign.ts.</item>
8
+ <item>Does not manage key publication or key rotation — domain-specific concerns belong in consumers.</item>
9
+ </non-goals>
10
+ </MODULE_CONTRACT>
11
+ <CHANGE_SUMMARY>
12
+ <item>RFC-0921: initial signing key management module.</item>
13
+ <item>RFC-1097: step 6 — compass.migrate codemod run
14
+
15
+ Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.</item>
16
+ <item>RFC-1097: sweep — werkstatt-engine clean
17
+
18
+ Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.</item>
19
+ </CHANGE_SUMMARY>
20
+ */
21
+
22
+ import fs from "node:fs/promises";
23
+ import { existsSync } from "node:fs";
24
+ import path from "node:path";
25
+ import { createPrivateKey, createPublicKey } from "node:crypto";
26
+ import * as ed from "@noble/ed25519";
27
+ import type { KeyEncoding, SigningKeyPair } from "./types.ts";
28
+
29
+ export async function generateKeyPair(): Promise<SigningKeyPair> {
30
+ const privateKey = ed.utils.randomSecretKey();
31
+ const publicKey = await getPublicKey(privateKey);
32
+ return { privateKey, publicKey };
33
+ }
34
+
35
+ export async function getPublicKey(privateKey: Uint8Array): Promise<Uint8Array> {
36
+ return ed.getPublicKeyAsync(privateKey);
37
+ }
38
+
39
+ export async function loadPrivateKey(
40
+ source: { pem: string } | { hex: string } | { filePath: string; encoding: KeyEncoding },
41
+ ): Promise<Uint8Array> {
42
+ if ("pem" in source) {
43
+ return fromPem(source.pem);
44
+ }
45
+ if ("hex" in source) {
46
+ return fromHex(source.hex);
47
+ }
48
+ const raw = (await fs.readFile(source.filePath, "utf8")).trim();
49
+ if (source.encoding === "pem") {
50
+ return fromPem(raw);
51
+ }
52
+ return fromHex(raw);
53
+ }
54
+
55
+ export async function loadPublicKey(
56
+ source: { pem: string } | { hex: string } | { filePath: string; encoding: KeyEncoding },
57
+ ): Promise<Uint8Array> {
58
+ if ("pem" in source) {
59
+ return fromPem(source.pem);
60
+ }
61
+ if ("hex" in source) {
62
+ return fromHex(source.hex);
63
+ }
64
+ const raw = (await fs.readFile(source.filePath, "utf8")).trim();
65
+ if (source.encoding === "pem") {
66
+ return fromPem(raw);
67
+ }
68
+ return fromHex(raw);
69
+ }
70
+
71
+ export async function saveKeyPair(
72
+ keyPair: SigningKeyPair,
73
+ outputDir: string,
74
+ encoding: KeyEncoding,
75
+ ): Promise<{ privateKeyPath: string; publicKeyPath: string }> {
76
+ await fs.mkdir(outputDir, { recursive: true });
77
+
78
+ const privateKeyPath = path.join(outputDir, `signing.key${encoding === "pem" ? ".pem" : ""}`);
79
+ const publicKeyPath = path.join(outputDir, `signing.key.pub${encoding === "pem" ? ".pem" : ""}`);
80
+
81
+ if (encoding === "pem") {
82
+ await fs.writeFile(privateKeyPath, toPem(keyPair.privateKey, "private"), "utf8");
83
+ await fs.writeFile(publicKeyPath, toPem(keyPair.publicKey, "public"), "utf8");
84
+ } else {
85
+ await fs.writeFile(privateKeyPath, toHex(keyPair.privateKey), "utf8");
86
+ await fs.writeFile(publicKeyPath, toHex(keyPair.publicKey), "utf8");
87
+ }
88
+
89
+ return { privateKeyPath, publicKeyPath };
90
+ }
91
+
92
+ export function toHex(bytes: Uint8Array): string {
93
+ return Buffer.from(bytes).toString("hex");
94
+ }
95
+
96
+ export function fromHex(hex: string): Uint8Array {
97
+ return new Uint8Array(Buffer.from(hex, "hex"));
98
+ }
99
+
100
+ export function toPem(bytes: Uint8Array, type: "private" | "public"): string {
101
+ const label = type === "private" ? "PRIVATE KEY" : "PUBLIC KEY";
102
+ const base64 = Buffer.from(bytes).toString("base64");
103
+ const lines = base64.match(/.{1,64}/g) ?? [base64];
104
+ return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
105
+ }
106
+
107
+ export function fromPem(pem: string): Uint8Array {
108
+ const base64 = pem
109
+ .replace(/-----BEGIN [A-Z ]+-----/g, "")
110
+ .replace(/-----END [A-Z ]+-----/g, "")
111
+ .replace(/\s+/g, "");
112
+ return new Uint8Array(Buffer.from(base64, "base64"));
113
+ }
114
+
115
+ export function privateKeyPemToBytes(pem: string): Uint8Array {
116
+ const key = createPrivateKey(pem);
117
+ if (key.asymmetricKeyType !== "ed25519") {
118
+ throw new Error(
119
+ `Wrong private key type: ${key.asymmetricKeyType ?? "unknown"}. Expected ed25519.`,
120
+ );
121
+ }
122
+ const jwk = key.export({ format: "jwk" }) as { d: string };
123
+ return new Uint8Array(Buffer.from(jwk.d, "base64url"));
124
+ }
125
+
126
+ export function publicKeyPemToBytes(pem: string): Uint8Array {
127
+ const key = createPublicKey(pem);
128
+ if (key.asymmetricKeyType !== "ed25519") {
129
+ throw new Error(
130
+ `Wrong public key type: ${key.asymmetricKeyType ?? "unknown"}. Expected ed25519.`,
131
+ );
132
+ }
133
+ const jwk = key.export({ format: "jwk" }) as { x: string };
134
+ return new Uint8Array(Buffer.from(jwk.x, "base64url"));
135
+ }
136
+
137
+ export function keyExists(filePath: string): boolean {
138
+ return existsSync(filePath);
139
+ }