@warpgogol/werkstatt-shared 0.14.1 → 0.16.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 (451) hide show
  1. package/README.md +23 -15
  2. package/README.uk.md +1 -1
  3. package/package.json +534 -766
  4. package/src/{share/agent → agent}/api-catalog.ts +14 -1
  5. package/src/{share/agent → agent}/ard-catalog.ts +6 -1
  6. package/src/{share/agent → agent}/dns-aid.test.ts +2 -2
  7. package/src/agent/guidance.ts +52 -0
  8. package/src/agent/health.ts +68 -0
  9. package/src/{share/agent → agent}/index.ts +5 -1
  10. package/src/{share/agent → agent}/manifest.ts +83 -6
  11. package/src/{share/agent → agent}/openapi.ts +97 -4
  12. package/src/agent/receipt.ts +123 -0
  13. package/src/{share/agent → agent}/search.test.ts +3 -0
  14. package/src/{share/agent → agent}/search.ts +31 -5
  15. package/src/{share → agent}/tests/api-catalog.test.ts +8 -2
  16. package/src/{share → agent}/tests/ard-catalog.test.ts +11 -2
  17. package/src/{share → agent}/tests/capability.test.ts +1 -1
  18. package/src/{share → agent}/tests/fleet-catalog.test.ts +1 -1
  19. package/src/{share → agent}/tests/knowledge.test.ts +2 -2
  20. package/src/{share → agent}/tests/manifest.test.ts +72 -1
  21. package/src/{share → agent}/tests/mcp-card.test.ts +6 -2
  22. package/src/{share → agent}/tests/openapi.test.ts +119 -2
  23. package/src/{share → agent}/tests/proof.test.ts +1 -1
  24. package/src/{share → attribution}/material-credits.ts +4 -4
  25. package/src/{share → checks}/check-hints.ts +6 -1
  26. package/src/checks/index.ts +4 -1
  27. package/src/checks/lib/astro-site-url.ts +6 -1
  28. package/src/checks/lib/i18n.ts +14 -0
  29. package/src/checks/result-helpers.ts +2 -2
  30. package/src/checks/tests/remediation-auto-populate.test.ts +1 -1
  31. package/src/{share → client-scripts}/counter-utils.ts +6 -2
  32. package/src/{share/scripts → client-scripts}/external-link-qr.ts +11 -16
  33. package/src/{share/scripts → client-scripts}/external-links.ts +12 -1
  34. package/src/{share/scripts → client-scripts}/gsap-counter.ts +8 -3
  35. package/src/client-scripts/index.ts +35 -0
  36. package/src/{share/scripts → client-scripts}/inline-number-animation.ts +1 -1
  37. package/src/{share/scripts → client-scripts}/lenis.ts +6 -1
  38. package/src/{share/scripts → client-scripts}/lordicon.ts +6 -1
  39. package/src/{share/scripts → client-scripts}/orchestrator.ts +10 -1
  40. package/src/{share/scripts → client-scripts}/scheduler.ts +6 -1
  41. package/src/client-scripts/scroll-spy.ts +106 -0
  42. package/src/client-scripts/tests/orchestrator.test.ts +47 -0
  43. package/src/client-scripts/tests/scroll-spy.test.ts +204 -0
  44. package/src/{share/scripts → client-scripts/tests}/video-player.test.ts +51 -31
  45. package/src/component/contracts.ts +206 -0
  46. package/src/component/index.ts +13 -0
  47. package/src/{share → content}/content-reference.ts +3 -3
  48. package/src/{share/content → content}/entity-id.ts +6 -3
  49. package/src/content/index.ts +3 -0
  50. package/src/{share/content → content}/merge.ts +6 -1
  51. package/src/content/system-manifest.test.ts +107 -0
  52. package/src/content/system-manifest.ts +12 -94
  53. package/src/{share → content}/tests/resolve-field-path.test.ts +1 -1
  54. package/src/{share → content}/tests/substitute-deep.test.ts +1 -1
  55. package/src/content-discipline/index.ts +19 -0
  56. package/src/{share/content-discipline → content-discipline}/parsers.ts +2 -2
  57. package/src/{share/env.d.ts → env.d.ts} +6 -3
  58. package/src/fingerprint/canonical-json.ts +731 -0
  59. package/src/fingerprint/index.ts +14 -0
  60. package/src/fingerprint/primitives.ts +72 -0
  61. package/src/{share/i18n → i18n}/localization.ts +6 -2
  62. package/src/integration/adapters.ts +10 -71
  63. package/src/integration/dispatch.ts +1 -1
  64. package/src/integration/funnel.ts +6 -1
  65. package/src/integration/idempotency.ts +88 -0
  66. package/src/integration/index.ts +105 -4
  67. package/src/integration/lagebild-destination-adapter.ts +108 -0
  68. package/src/integration/lagebild-ingress.ts +7 -12
  69. package/src/integration/orchestration.ts +11 -10
  70. package/src/integration/tests/deliver-event.test.ts +1 -1
  71. package/src/integration/tests/dispatch.test.ts +4 -4
  72. package/src/integration/tests/hub-runtime.test.ts +22 -14
  73. package/src/integration/tests/idempotency.test.ts +81 -0
  74. package/src/integration/vitest.config.ts +1 -0
  75. package/src/kernel/desired-state.ts +227 -0
  76. package/src/kernel/diagnostic.ts +289 -0
  77. package/src/kernel/drift-guard.ts +166 -0
  78. package/src/kernel/fs-atomic.ts +131 -0
  79. package/src/kernel/index.ts +22 -0
  80. package/src/kernel/tests/drift-guard.test.ts +112 -0
  81. package/src/kernel/tests/fs-atomic.test.ts +150 -0
  82. package/src/kernel/tests/workpiece-env.test.ts +44 -0
  83. package/src/kernel/tests/workspace-io.test.ts +187 -0
  84. package/src/kernel/types.ts +518 -0
  85. package/src/kernel/workpiece-env.ts +48 -0
  86. package/src/kernel/workspace-io.ts +248 -0
  87. package/src/{share → knowledge}/tests/claim.test.ts +1 -1
  88. package/src/{share → knowledge}/tests/derivation.test.ts +1 -1
  89. package/src/{share → knowledge}/tests/freshness.test.ts +1 -1
  90. package/src/{share → knowledge}/tests/ledger.test.ts +1 -1
  91. package/src/{share → knowledge}/tests/plan.test.ts +1 -1
  92. package/src/{share → knowledge}/tests/source.test.ts +1 -1
  93. package/src/legal/index.ts +15 -0
  94. package/src/{share/middleware → middleware}/access-protection.ts +11 -8
  95. package/src/{share/middleware → middleware}/language-redirect.ts +3 -0
  96. package/src/{share → node}/dev-props-validator.ts +6 -1
  97. package/src/{share → node}/import-scan.ts +23 -3
  98. package/src/{share → node}/semantic/derived-prices-loader.ts +4 -2
  99. package/src/{share → node}/semantic/markdown-twin-provenance.ts +1 -1
  100. package/src/{share/semantic → node}/tests/derived-prices-loader.test.ts +14 -1
  101. package/src/{share/semantic → node}/tests/markdown-twin-provenance.test.ts +1 -1
  102. package/src/observability/env-resolver.ts +6 -1
  103. package/src/observability/index.ts +14 -1
  104. package/src/observability/otlp-converter.ts +6 -1
  105. package/src/observability/otlp-transport.ts +6 -1
  106. package/src/observability/pipeline-log.ts +81 -0
  107. package/src/observability/pusher.ts +6 -1
  108. package/src/observability/redact.ts +6 -1
  109. package/src/observability/typed-refs.ts +6 -2
  110. package/src/observability/vitest.config.ts +1 -0
  111. package/src/{share → offers}/formula-eval.ts +3 -3
  112. package/src/{share → offers}/tests/formula-eval.test.ts +2 -2
  113. package/src/onboarding/index.ts +1 -0
  114. package/src/{share → onboarding}/tests/onboarding-yaml.test.ts +1 -1
  115. package/src/ontology/archetype-registry.ts +6 -1
  116. package/src/ontology/archetypes/index.json +33 -1
  117. package/src/ontology/archetypes/index.yaml +27 -1
  118. package/src/ontology/archetypes/sections/entity-grid.yaml +46 -0
  119. package/src/ontology/archetypes/sections/exit-diagram.yaml +43 -0
  120. package/src/ontology/biome-token-projection.ts +6 -1
  121. package/src/ontology/capabilities/lead.prepare.yaml +44 -0
  122. package/src/ontology/capabilities/lead.submit.yaml +20 -3
  123. package/src/ontology/cosmic/index.ts +6 -1
  124. package/src/ontology/cosmic/moon-catalog.ts +6 -1
  125. package/src/ontology/cosmic/planet-catalog.ts +6 -1
  126. package/src/ontology/cosmic/star-catalog.ts +6 -1
  127. package/src/ontology/enums.ts +6 -1
  128. package/src/ontology/external-surfaces/index.ts +1 -1
  129. package/src/ontology/index.ts +6 -2
  130. package/src/ontology/manifest.ts +7 -4
  131. package/src/ontology/operations/artifact-store.ts +45 -0
  132. package/src/ontology/operations/dht.ts +98 -0
  133. package/src/ontology/operations/handoff.ts +92 -0
  134. package/src/ontology/operations/index.ts +193 -1
  135. package/src/ontology/operations/leitstand.ts +99 -0
  136. package/src/ontology/operations/materialization.ts +90 -0
  137. package/src/ontology/operations/mission.ts +111 -0
  138. package/src/ontology/operations/naming-policy.ts +132 -0
  139. package/src/ontology/operations/notausgang.ts +108 -0
  140. package/src/ontology/operations/release.ts +121 -0
  141. package/src/ontology/operations/sternsystem.ts +192 -0
  142. package/src/ontology/operations/werkstatt.ts +48 -0
  143. package/src/ontology/schemas/biome.ts +7 -2
  144. package/src/ontology/schemas/capability.ts +56 -4
  145. package/src/ontology/schemas/constellation.ts +6 -1
  146. package/src/ontology/schemas/index.ts +6 -2
  147. package/src/ontology/schemas/manifest-resolver.ts +6 -1
  148. package/src/ontology/schemas/page-entry.ts +9 -5
  149. package/src/ontology/schemas/system/growth.ts +6 -1
  150. package/src/ontology/schemas/system/manifest.ts +40 -1
  151. package/src/ontology/schemas/system/page-output.ts +7 -6
  152. package/src/ontology/schemas/system/text.ts +15 -0
  153. package/src/ontology/schemas/system.ts +6 -2
  154. package/src/ontology/shared-section-props/body-fragments.ts +2 -0
  155. package/src/ontology/tests/sternsystem-owner.test.ts +1 -1
  156. package/src/ontology/vitest.config.ts +1 -0
  157. package/src/{share → page}/page.ts +13 -9
  158. package/src/{share → page}/tests/dev-props-validator.test.ts +1 -1
  159. package/src/{share → page}/tests/hero-section-numbering-parity.test.ts +1 -1
  160. package/src/passport/claim-sign.ts +120 -0
  161. package/src/passport/index.ts +9 -1
  162. package/src/passport/key-rotate.ts +10 -2
  163. package/src/passport/schema.ts +6 -1
  164. package/src/passport/sign.ts +8 -3
  165. package/src/passport/vitest.config.ts +1 -0
  166. package/src/pbt/index.test.ts +95 -0
  167. package/src/pbt/index.ts +251 -0
  168. package/src/plugin/plugin-contract.ts +2 -2
  169. package/src/{share → policy}/entitlement.ts +24 -4
  170. package/src/{share → policy}/feature-policy.ts +8 -3
  171. package/src/{share → policy}/visibility.ts +7 -2
  172. package/src/redirects/redirects.ts +117 -0
  173. package/src/{share → redirects/tests}/redirects.test.ts +1 -1
  174. package/src/remediation/remediation-catalog.ts +316 -0
  175. package/src/{share/routes → routes}/template-filter.ts +1 -1
  176. package/src/{share → runtime}/runtime-context.ts +7 -2
  177. package/src/{share → runtime}/shared-context.ts +1 -1
  178. package/src/{share/schemas → schemas}/claims.ts +2 -2
  179. package/src/{share/schemas → schemas}/effects.ts +6 -1
  180. package/src/{share/schemas → schemas}/features.ts +1 -1
  181. package/src/{share/schemas → schemas}/index.ts +3 -3
  182. package/src/{share/schemas → schemas}/navigation.ts +6 -1
  183. package/src/{share/schemas → schemas}/page-base.ts +6 -2
  184. package/src/{share/schemas → schemas}/person.ts +1 -1
  185. package/src/{share/schemas → schemas}/standard-list-item.ts +1 -1
  186. package/src/{share → sections}/rfc0042-utils.ts +1 -1
  187. package/src/{share/semantic → semantic}/block-extractors/index.ts +20 -1
  188. package/src/{share/semantic → semantic}/business-projection.ts +95 -1
  189. package/src/semantic/canonical-uri.ts +74 -0
  190. package/src/{share/semantic → semantic}/extract.ts +100 -1
  191. package/src/semantic/fact-extraction.test.ts +47 -0
  192. package/src/semantic/fact-extraction.ts +46 -0
  193. package/src/semantic/freshness.ts +67 -0
  194. package/src/{share/semantic → semantic}/ids.ts +6 -1
  195. package/src/{share/semantic → semantic}/index.ts +9 -2
  196. package/src/{share/semantic → semantic}/jsonld/breadcrumb.ts +6 -2
  197. package/src/{share/semantic → semantic}/jsonld/context.ts +6 -2
  198. package/src/{share/semantic → semantic}/jsonld/faq.ts +6 -2
  199. package/src/{share/semantic → semantic}/jsonld/initiative.ts +6 -1
  200. package/src/{share/semantic → semantic}/jsonld/organization.ts +6 -1
  201. package/src/{share/semantic → semantic}/jsonld/person.ts +6 -2
  202. package/src/{share/semantic → semantic}/jsonld/service.ts +6 -2
  203. package/src/semantic/jsonld/shared.ts +36 -0
  204. package/src/semantic/jsonld/types.ts +24 -0
  205. package/src/{share/semantic → semantic}/jsonld/webpage.ts +6 -1
  206. package/src/semantic/jsonld/website.ts +32 -0
  207. package/src/{share/semantic → semantic}/jsonld.ts +6 -1
  208. package/src/{share/semantic → semantic}/llms.ts +51 -5
  209. package/src/{share/semantic → semantic}/models.ts +26 -38
  210. package/src/{share/semantic → semantic}/organization-profile.ts +3 -0
  211. package/src/{share/semantic → semantic}/page-builders/markdown-page.ts +6 -3
  212. package/src/{share/semantic → semantic}/page-utils.ts +6 -3
  213. package/src/{share/tests/breadcrumbs.test.ts → semantic/tests/breadcrumbs-0229.test.ts} +1 -1
  214. package/src/{share → semantic}/tests/build-page-price-markers.test.ts +2 -2
  215. package/src/{share/semantic → semantic}/tests/business-projection.test.ts +179 -0
  216. package/src/semantic/tests/canonical-uri.test.ts +68 -0
  217. package/src/{share/semantic → semantic}/tests/extract.test.ts +83 -0
  218. package/src/semantic/tests/freshness.pbt.test.ts +108 -0
  219. package/src/{share/tests/image-sitemap.test.ts → semantic/tests/image-sitemap-0150.test.ts} +1 -1
  220. package/src/{share → semantic}/tests/jsonld-service.test.ts +2 -2
  221. package/src/{share → semantic}/tests/jsonld-video.test.ts +3 -3
  222. package/src/{share → semantic}/tests/jsonld-webpage.test.ts +3 -3
  223. package/src/{share → semantic}/tests/llms-0789.test.ts +2 -2
  224. package/src/{share/semantic → semantic}/tests/llms.test.ts +46 -0
  225. package/src/{share → semantic}/tests/organization-jsonld.test.ts +3 -3
  226. package/src/{share/tests/output-projection.test.ts → semantic/tests/output-projection-0143.test.ts} +1 -1
  227. package/src/{share/tests/price-marker-resolver.test.ts → semantic/tests/price-marker-resolver-0767.test.ts} +2 -2
  228. package/src/{share/semantic → semantic}/tests/price-marker-resolver.test.ts +4 -2
  229. package/src/semantic/tests/project-claims.pbt.test.ts +60 -0
  230. package/src/{share/semantic → semantic}/tests/split-sentences.test.ts +1 -1
  231. package/src/{share → semantic}/tests/twin-path.test.ts +1 -1
  232. package/src/signing/index.ts +15 -0
  233. package/src/signing/key.ts +139 -0
  234. package/src/signing/sign.ts +66 -0
  235. package/src/signing/types.ts +29 -0
  236. package/src/{share/slug → slug}/slug-url.ts +4 -1
  237. package/src/stack/run-tool.ts +106 -0
  238. package/src/stack/stack-checks.ts +124 -0
  239. package/src/stack/tests/run-tool.test.ts +138 -0
  240. package/src/stack/tests/stack-checks.test.ts +141 -0
  241. package/src/stack/tests/walk-files.test.ts +131 -0
  242. package/src/stack/walk-files.ts +114 -0
  243. package/src/surface/blueprint.ts +342 -66
  244. package/src/surface/decision-composer.ts +6 -1
  245. package/src/surface/geo.ts +6 -1
  246. package/src/surface/index.ts +11 -7
  247. package/src/surface/types.ts +2 -2
  248. package/src/surface/vitest.config.ts +1 -0
  249. package/src/tests/signing-core.test.ts +129 -0
  250. package/src/{share → text}/css-value-normalize.ts +6 -1
  251. package/src/text/string-utils.ts +24 -0
  252. package/src/{share → text}/tests/text-normalize.test.ts +5 -0
  253. package/src/{share → text}/text-normalize.ts +4 -3
  254. package/src/{share → text}/wrap-inline-numbers.ts +1 -1
  255. package/src/types/axiom-study.d.ts +36 -0
  256. package/src/types/hls-js-light.d.ts +21 -0
  257. package/src/types/qrcode-browser.d.ts +23 -0
  258. package/src/typography/fix.pbt.test.ts +208 -0
  259. package/src/typography/fix.ts +148 -0
  260. package/src/typography/index.ts +54 -0
  261. package/src/typography/locale-defaults.ts +97 -0
  262. package/src/typography/rules-tier1.test.ts +459 -0
  263. package/src/typography/rules-tier1.ts +925 -0
  264. package/src/typography/rules-tier2-locale.pbt.test.ts +190 -0
  265. package/src/typography/rules-tier2-locale.test.ts +286 -0
  266. package/src/typography/rules-tier2-locale.ts +399 -0
  267. package/src/typography/rules-tier2-structure.test.ts +330 -0
  268. package/src/typography/rules-tier2-structure.ts +378 -0
  269. package/src/typography/rules-tier3-advisory.test.ts +290 -0
  270. package/src/typography/rules-tier3-advisory.ts +239 -0
  271. package/src/typography/text-surface.test.ts +244 -0
  272. package/src/typography/text-surface.ts +448 -0
  273. package/src/index.ts +0 -14
  274. package/src/integration/port-barrel.ts +0 -105
  275. package/src/share/agent/tests/api-catalog.test.ts +0 -8
  276. package/src/share/agent/tests/ard-catalog.test.ts +0 -8
  277. package/src/share/agent/tests/capability.test.ts +0 -14
  278. package/src/share/agent/tests/fleet-catalog.test.ts +0 -15
  279. package/src/share/agent/tests/knowledge.test.ts +0 -20
  280. package/src/share/agent/tests/manifest.test.ts +0 -9
  281. package/src/share/agent/tests/mcp-card.test.ts +0 -8
  282. package/src/share/agent/tests/openapi.test.ts +0 -8
  283. package/src/share/agent/tests/proof.test.ts +0 -15
  284. package/src/share/content/index.ts +0 -16
  285. package/src/share/content-discipline/index.ts +0 -14
  286. package/src/share/index.ts +0 -86
  287. package/src/share/knowledge/tests/claim.test.ts +0 -11
  288. package/src/share/knowledge/tests/derivation.test.ts +0 -30
  289. package/src/share/knowledge/tests/freshness.test.ts +0 -18
  290. package/src/share/knowledge/tests/ledger.test.ts +0 -35
  291. package/src/share/knowledge/tests/plan.test.ts +0 -19
  292. package/src/share/knowledge/tests/source.test.ts +0 -19
  293. package/src/share/legal/index.ts +0 -1
  294. package/src/share/redirects.ts +0 -54
  295. package/src/share/remediation/remediation-catalog.ts +0 -196
  296. package/src/share/scripts/index.ts +0 -26
  297. package/src/share/scripts/tests/orchestrator.test.ts +0 -8
  298. package/src/share/semantic/jsonld/shared.ts +0 -31
  299. package/src/share/semantic/jsonld/types.ts +0 -19
  300. package/src/share/semantic/jsonld/website.ts +0 -27
  301. package/src/share/string-utils.ts +0 -19
  302. package/src/share/types/axiom-study.d.ts +0 -22
  303. package/src/share/types/hls-js-light.d.ts +0 -16
  304. package/src/share/types/qrcode-browser.d.ts +0 -18
  305. package/src/share/utility-registry.yaml +0 -50
  306. package/src/share/vitest.config.ts +0 -8
  307. package/src/surface/blueprint-schema.ts +0 -314
  308. package/src/surface/blueprint-types.ts +0 -256
  309. /package/src/{share/agent → agent}/capability.ts +0 -0
  310. /package/src/{share/agent → agent}/dns-aid.ts +0 -0
  311. /package/src/{share/agent → agent}/fleet-catalog.ts +0 -0
  312. /package/src/{share/agent → agent}/knowledge.ts +0 -0
  313. /package/src/{share/agent → agent}/mcp-card.ts +0 -0
  314. /package/src/{share/agent → agent}/omit-empty.ts +0 -0
  315. /package/src/{share/agent → agent}/proof.ts +0 -0
  316. /package/src/{share/agent → agent}/tests/omit-empty.test.ts +0 -0
  317. /package/src/{share → attribution}/attribution-display.ts +0 -0
  318. /package/src/{share/scripts → client-scripts}/gsap-mountain-journey.ts +0 -0
  319. /package/src/{share/scripts → client-scripts}/gsap-parallax.ts +0 -0
  320. /package/src/{share/scripts → client-scripts}/gsap-reveal.ts +0 -0
  321. /package/src/{share/scripts → client-scripts}/gsap-stagger.ts +0 -0
  322. /package/src/{share/scripts → client-scripts}/live-photos.ts +0 -0
  323. /package/src/{share/scripts → client-scripts}/tests/external-link-qr.test.ts +0 -0
  324. /package/src/{share/scripts → client-scripts}/tests/external-links.test.ts +0 -0
  325. /package/src/{share/scripts → client-scripts}/tests/gsap-counter.test.ts +0 -0
  326. /package/src/{share/scripts → client-scripts}/tests/gsap-mountain-journey.test.ts +0 -0
  327. /package/src/{share/scripts → client-scripts}/tests/gsap-parallax.test.ts +0 -0
  328. /package/src/{share/scripts → client-scripts}/tests/gsap-reveal.test.ts +0 -0
  329. /package/src/{share/scripts → client-scripts}/tests/gsap-stagger.test.ts +0 -0
  330. /package/src/{share/scripts → client-scripts}/tests/inline-number-animation.test.ts +0 -0
  331. /package/src/{share/scripts → client-scripts}/tests/lenis.test.ts +0 -0
  332. /package/src/{share/scripts → client-scripts}/tests/live-photos.test.ts +0 -0
  333. /package/src/{share/scripts → client-scripts}/tests/lordicon.test.ts +0 -0
  334. /package/src/{share/scripts → client-scripts}/tests/scheduler.test.ts +0 -0
  335. /package/src/{share/scripts → client-scripts}/video-player.ts +0 -0
  336. /package/src/{share/content → content}/resolve-field-path.ts +0 -0
  337. /package/src/{share/content → content}/substitute-deep.ts +0 -0
  338. /package/src/{share → content}/tests/content-ref-index.test.ts +0 -0
  339. /package/src/{share/content-discipline → content-discipline}/types.ts +0 -0
  340. /package/src/{share → image-provider}/image-provider.ts +0 -0
  341. /package/src/{share/knowledge → knowledge}/claim.ts +0 -0
  342. /package/src/{share/knowledge → knowledge}/derivation.ts +0 -0
  343. /package/src/{share/knowledge → knowledge}/freshness.ts +0 -0
  344. /package/src/{share/knowledge → knowledge}/index.ts +0 -0
  345. /package/src/{share/knowledge → knowledge}/ledger.ts +0 -0
  346. /package/src/{share/knowledge → knowledge}/plan.ts +0 -0
  347. /package/src/{share/knowledge → knowledge}/source.ts +0 -0
  348. /package/src/{share/legal → legal}/translation-policy.ts +0 -0
  349. /package/src/{share/middleware → middleware}/tests/access-protection.test.ts +0 -0
  350. /package/src/{share → node}/fs/index.ts +0 -0
  351. /package/src/{share/tests/markdown-twin-provenance.test.ts → node/tests/markdown-twin-provenance-0613.test.ts} +0 -0
  352. /package/src/{share → offers}/offer-capacity.ts +0 -0
  353. /package/src/{share/onboarding-yaml/index.ts → onboarding/yaml.ts} +0 -0
  354. /package/src/{share → ontology}/tests/ontology-role-by-cosmic-name.test.ts +0 -0
  355. /package/src/{share/routes → routes}/tests/template-filter.test.ts +0 -0
  356. /package/src/{share/schemas → schemas}/claim-records.ts +0 -0
  357. /package/src/{share/schemas → schemas}/horizontal-align.ts +0 -0
  358. /package/src/{share/schemas → schemas}/icon-color.ts +0 -0
  359. /package/src/{share/schemas → schemas}/live-photo.ts +0 -0
  360. /package/src/{share/schemas → schemas}/material-credit.ts +0 -0
  361. /package/src/{share/schemas → schemas}/media.ts +0 -0
  362. /package/src/{share/schemas → schemas}/participant.ts +0 -0
  363. /package/src/{share/schemas → schemas}/print.ts +0 -0
  364. /package/src/{share/schemas → schemas}/section-background.ts +0 -0
  365. /package/src/{share/schemas → schemas}/section-body.ts +0 -0
  366. /package/src/{share/schemas → schemas}/section-cards.ts +0 -0
  367. /package/src/{share/schemas → schemas}/section-cta.ts +0 -0
  368. /package/src/{share/schemas → schemas}/section-header.ts +0 -0
  369. /package/src/{share/schemas → schemas}/section-image.ts +0 -0
  370. /package/src/{share/schemas → schemas}/section-motion.ts +0 -0
  371. /package/src/{share/schemas → schemas}/section-shell.ts +0 -0
  372. /package/src/{share/schemas → schemas}/section-stats.ts +0 -0
  373. /package/src/{share/schemas → schemas}/site-background.ts +0 -0
  374. /package/src/{share/schemas → schemas}/tests/claim-records.test.ts +0 -0
  375. /package/src/{share/schemas → schemas}/tests/claims.test.ts +0 -0
  376. /package/src/{share/schemas → schemas}/tests/effects.test.ts +0 -0
  377. /package/src/{share/schemas → schemas}/tests/features.test.ts +0 -0
  378. /package/src/{share/schemas → schemas}/tests/horizontal-align.test.ts +0 -0
  379. /package/src/{share/schemas → schemas}/tests/icon-color.test.ts +0 -0
  380. /package/src/{share/schemas → schemas}/tests/live-photo.test.ts +0 -0
  381. /package/src/{share/schemas → schemas}/tests/material-credit.test.ts +0 -0
  382. /package/src/{share/schemas → schemas}/tests/media.test.ts +0 -0
  383. /package/src/{share/schemas → schemas}/tests/navigation.test.ts +0 -0
  384. /package/src/{share/schemas → schemas}/tests/page-base.test.ts +0 -0
  385. /package/src/{share/schemas → schemas}/tests/participant.test.ts +0 -0
  386. /package/src/{share/schemas → schemas}/tests/person.test.ts +0 -0
  387. /package/src/{share/schemas → schemas}/tests/print.test.ts +0 -0
  388. /package/src/{share/schemas → schemas}/tests/section-background.test.ts +0 -0
  389. /package/src/{share/schemas → schemas}/tests/section-body.test.ts +0 -0
  390. /package/src/{share/schemas → schemas}/tests/section-cards.test.ts +0 -0
  391. /package/src/{share/schemas → schemas}/tests/section-cta.test.ts +0 -0
  392. /package/src/{share/schemas → schemas}/tests/section-header.test.ts +0 -0
  393. /package/src/{share/schemas → schemas}/tests/section-image.test.ts +0 -0
  394. /package/src/{share/schemas → schemas}/tests/section-motion.test.ts +0 -0
  395. /package/src/{share/schemas → schemas}/tests/section-shell.test.ts +0 -0
  396. /package/src/{share/schemas → schemas}/tests/section-stats.test.ts +0 -0
  397. /package/src/{share/schemas → schemas}/tests/site-background.test.ts +0 -0
  398. /package/src/{share/schemas → schemas}/tests/standard-list-item.test.ts +0 -0
  399. /package/src/{share/semantic → semantic}/ai.ts +0 -0
  400. /package/src/{share/semantic → semantic}/block-extraction.ts +0 -0
  401. /package/src/{share/semantic → semantic}/breadcrumbs.ts +0 -0
  402. /package/src/{share/semantic → semantic}/build-page.ts +0 -0
  403. /package/src/{share/semantic → semantic}/feed.ts +0 -0
  404. /package/src/{share/semantic → semantic}/image-sitemap.ts +0 -0
  405. /package/src/{share/semantic → semantic}/jsonld/article.ts +0 -0
  406. /package/src/{share/semantic → semantic}/jsonld/collection-list.ts +0 -0
  407. /package/src/{share/semantic → semantic}/jsonld/tests/article.test.ts +0 -0
  408. /package/src/{share/semantic → semantic}/jsonld/tests/breadcrumb.test.ts +0 -0
  409. /package/src/{share/semantic → semantic}/jsonld/tests/collection-list.test.ts +0 -0
  410. /package/src/{share/semantic → semantic}/jsonld/tests/faq.test.ts +0 -0
  411. /package/src/{share/semantic → semantic}/jsonld/tests/initiative.test.ts +0 -0
  412. /package/src/{share/semantic → semantic}/jsonld/tests/organization.test.ts +0 -0
  413. /package/src/{share/semantic → semantic}/jsonld/tests/person.test.ts +0 -0
  414. /package/src/{share/semantic → semantic}/jsonld/tests/service.test.ts +0 -0
  415. /package/src/{share/semantic → semantic}/jsonld/tests/shared.test.ts +0 -0
  416. /package/src/{share/semantic → semantic}/jsonld/tests/video.test.ts +0 -0
  417. /package/src/{share/semantic → semantic}/jsonld/tests/webpage.test.ts +0 -0
  418. /package/src/{share/semantic → semantic}/jsonld/tests/website.test.ts +0 -0
  419. /package/src/{share/semantic → semantic}/jsonld/video.ts +0 -0
  420. /package/src/{share/semantic → semantic}/llms-policy.ts +0 -0
  421. /package/src/{share/semantic → semantic}/markdown-hygiene.ts +0 -0
  422. /package/src/{share/semantic → semantic}/output-projection.ts +0 -0
  423. /package/src/{share/semantic → semantic}/page-markdown.ts +0 -0
  424. /package/src/{share/semantic → semantic}/price-marker-resolver.ts +0 -0
  425. /package/src/{share/semantic → semantic}/robots.ts +0 -0
  426. /package/src/{share/semantic → semantic}/tests/ai.test.ts +0 -0
  427. /package/src/{share/semantic → semantic}/tests/block-extraction.test.ts +0 -0
  428. /package/src/{share/semantic → semantic}/tests/breadcrumbs.test.ts +0 -0
  429. /package/src/{share/semantic → semantic}/tests/build-page.test.ts +0 -0
  430. /package/src/{share/semantic → semantic}/tests/feed.test.ts +0 -0
  431. /package/src/{share/semantic → semantic}/tests/helpers.ts +0 -0
  432. /package/src/{share/semantic → semantic}/tests/ids.test.ts +0 -0
  433. /package/src/{share/semantic → semantic}/tests/image-sitemap.test.ts +0 -0
  434. /package/src/{share/semantic → semantic}/tests/jsonld.test.ts +0 -0
  435. /package/src/{share/semantic → semantic}/tests/llms-policy.test.ts +0 -0
  436. /package/src/{share/semantic → semantic}/tests/markdown-hygiene.test.ts +0 -0
  437. /package/src/{share/semantic → semantic}/tests/markdown-page.test.ts +0 -0
  438. /package/src/{share/semantic → semantic}/tests/organization-profile.test.ts +0 -0
  439. /package/src/{share/semantic → semantic}/tests/output-projection.test.ts +0 -0
  440. /package/src/{share/semantic → semantic}/tests/page-markdown.test.ts +0 -0
  441. /package/src/{share/semantic → semantic}/tests/page-utils.test.ts +0 -0
  442. /package/src/{share/semantic → semantic}/tests/robots.test.ts +0 -0
  443. /package/src/{share/semantic → semantic}/tests/update-stamp.test.ts +0 -0
  444. /package/src/{share/semantic → semantic}/update-stamp.ts +0 -0
  445. /package/src/{share/slug → slug}/heading-slugger.ts +0 -0
  446. /package/src/{share/slug → slug}/index.ts +0 -0
  447. /package/src/{share/slug → slug}/slug-id.ts +0 -0
  448. /package/src/{share/slug → slug}/strategies.ts +0 -0
  449. /package/src/{share/slug → slug}/tests/slug.test.ts +0 -0
  450. /package/src/{share → text}/tests/text-normalize.pbt.test.ts +0 -0
  451. /package/src/{share → text}/text-position.ts +0 -0
@@ -22,8 +22,8 @@ import type {
22
22
  Diagnostic,
23
23
  KernelCommandResult,
24
24
  KernelNextStep,
25
- } from "@warpgogol/werkstatt-engine/kernel";
26
- import { lookupRemediation } from "../share/remediation/remediation-catalog.ts";
25
+ } from "@warpgogol/werkstatt-shared/kernel";
26
+ import { lookupRemediation } from "../remediation/remediation-catalog.ts";
27
27
 
28
28
  function defaultFailNextSteps(command: string): KernelNextStep[] {
29
29
  return [
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { test, expect } from "vitest";
11
11
  import { diagnosticsResult } from "../result-helpers.ts";
12
- import type { Diagnostic } from "@warpgogol/werkstatt-engine/kernel";
12
+ import type { Diagnostic } from "@warpgogol/werkstatt-shared/kernel";
13
13
 
14
14
  test("diagnosticsResult: auto-populates remediation from catalog for known ruleId", () => {
15
15
  const diagnostics: Diagnostic[] = [
@@ -7,8 +7,12 @@
7
7
  </non-goals>
8
8
  </MODULE_CONTRACT>
9
9
  <CHANGE_SUMMARY>
10
- <item>Created shared counter utilities to eliminate duplication between hero and impact sections.</item>
11
- <item>getStartValue now generates "1" in highest digit (e.g., "1,000" instead of "0,000") for better visual feedback during count-up.</item>
10
+ <item>RFC-1097: step 6 — compass.migrate codemod run
11
+
12
+ 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>
13
+ <item>RFC-1097: sweep — werkstatt-engine clean
14
+
15
+ 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>
12
16
  </CHANGE_SUMMARY>
13
17
  */
14
18
 
@@ -114,29 +114,27 @@ export function initExternalLinkQr(options?: ExternalLinkQrOptions): void {
114
114
  return !modal.hidden;
115
115
  }
116
116
 
117
- function handleTriggerClick(e: Event): void {
118
- const trigger = e.currentTarget as HTMLElement;
119
- const anchor = trigger.closest("a");
117
+ function handleDelegatedClick(e: Event): void {
118
+ const anchor = (e.target as HTMLElement)?.closest?.("a[data-external-link]");
120
119
  if (!anchor) return;
121
120
 
122
121
  const href = anchor.getAttribute("href");
123
122
  if (!href) return;
124
123
 
125
124
  e.preventDefault();
126
- e.stopPropagation();
127
- openModal(href, trigger);
125
+ openModal(href, anchor as HTMLElement);
128
126
  }
129
127
 
130
- function handleTriggerKeydown(e: KeyboardEvent): void {
128
+ function handleDelegatedKeydown(e: KeyboardEvent): void {
131
129
  if (e.key !== "Enter" && e.key !== " ") return;
132
- e.preventDefault();
133
- e.stopPropagation();
134
- const trigger = e.currentTarget as HTMLElement;
135
- const anchor = trigger.closest("a");
130
+ const anchor = (e.target as HTMLElement)?.closest?.("a[data-external-link]");
136
131
  if (!anchor) return;
132
+
137
133
  const href = anchor.getAttribute("href");
138
134
  if (!href) return;
139
- openModal(href, trigger);
135
+
136
+ e.preventDefault();
137
+ openModal(href, anchor as HTMLElement);
140
138
  }
141
139
 
142
140
  function handleKeydown(e: KeyboardEvent): void {
@@ -173,11 +171,8 @@ export function initExternalLinkQr(options?: ExternalLinkQrOptions): void {
173
171
  }
174
172
 
175
173
  function attachTriggers(): void {
176
- const triggers = document.querySelectorAll<HTMLElement>("[data-qr-trigger]");
177
- for (const trigger of triggers) {
178
- trigger.addEventListener("click", handleTriggerClick);
179
- trigger.addEventListener("keydown", handleTriggerKeydown);
180
- }
174
+ document.addEventListener("click", handleDelegatedClick);
175
+ document.addEventListener("keydown", handleDelegatedKeydown);
181
176
  }
182
177
 
183
178
  overlay.addEventListener("click", closeModal);
@@ -10,8 +10,13 @@ external-link-qr entitlement is active, also injects a QR trigger span into each
10
10
  </MODULE_CONTRACT>
11
11
  <CHANGE_SUMMARY>
12
12
  <item>RFC-0011 Phase 2: Extracted from layout.astro inline script into src/scripts canonical pattern.</item>
13
- <item>Migrated to @warpgogol/werkstatt-shared/share/scripts for platform-wide reuse.</item>
14
13
  <item>RFC-0932: accept ExternalLinkBehaviorOptions, inject QR trigger span when entitled.</item>
14
+ <item>RFC-1097: step 6 — compass.migrate codemod run
15
+
16
+ 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>
17
+ <item>RFC-1097: sweep — werkstatt-engine clean
18
+
19
+ 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>
15
20
  </CHANGE_SUMMARY>
16
21
  */
17
22
 
@@ -52,6 +57,12 @@ export function applyExternalLinkBehavior(options?: ExternalLinkBehaviorOptions)
52
57
  anchor.setAttribute("rel", "noopener noreferrer");
53
58
 
54
59
  if (qrEntitled && anchor.getAttribute("data-external-link-qr") !== "off") {
60
+ // Skip QR trigger for anchors wrapping media elements — a text arrow
61
+ // appended after an image/SVG/canvas looks visually wrong.
62
+ const hasMediaChild = anchor.querySelector("img, svg, canvas, picture");
63
+ if (hasMediaChild) {
64
+ continue;
65
+ }
55
66
  if (!anchor.querySelector("[data-qr-trigger]")) {
56
67
  const trigger = document.createElement("span");
57
68
  trigger.className = "external-link-qr-trigger";
@@ -8,15 +8,20 @@
8
8
  </non-goals>
9
9
  </MODULE_CONTRACT>
10
10
  <CHANGE_SUMMARY>
11
- <item>RFC-0040: Created as shared GSAP counter initialization module in @warpgogol/werkstatt-shared/share/scripts.</item>
12
- <item>Extended selector to support .hero__stat in addition to .impact-section__stat.</item>
11
+ <item>RFC-0040: Created as shared GSAP counter initialization module in @warpgogol/werkstatt-shared/client-scripts.</item>
13
12
  <item>RFC-0758: Extended selector to support .dynamic-status-block__stat.</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>
14
19
  </CHANGE_SUMMARY>
15
20
  */
16
21
 
17
22
  // @ai-invariant: RFC-0040. Do not import gsap at the top level. Always dynamic-import inside initGsapCounter.
18
23
 
19
- import { parseNumeric } from "../counter-utils.ts";
24
+ import { parseNumeric } from "./counter-utils.ts";
20
25
 
21
26
  export interface GsapCounterOptions {
22
27
  prefersReducedMotion?: boolean;
@@ -0,0 +1,35 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>scripts index — central export point for the script modules used in dynamic loading contexts.</purpose>
4
+
5
+
6
+ <non-goals>
7
+ <item>Do not define new modules or components.</item>
8
+ <item>Do not manage application state or business logic.</item>
9
+ </non-goals>
10
+ </MODULE_CONTRACT>
11
+
12
+ <CHANGE_SUMMARY>
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 — tail packages clean
17
+
18
+ Sweep batch 3: rewrote ~95 purposes across werkstatt-knowledge, werkstatt-shared, godot-game, phaser-game, lifecycle-core, projektarchiv-*, portal-*, billing-*, typescript (CONTRACT-02/PURPOSE-02). Real KEY_DECISIONS on 5 godot utils, non-goals on 5 CONTRACT-03 files, headers on 4 headerless files, CS-07 history literal fix on 2 files. Policy: vitest.config.ts + test-fixtures testPatterns, worker-configuration.d.ts excludedPath. All non-site/engine packages now 0 diagnostics.</item>
19
+ <item>RFC-1097: sweep — werkstatt-engine clean
20
+
21
+ 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>
22
+ </CHANGE_SUMMARY>
23
+ */
24
+
25
+ // scheduler.ts is loaded dynamically by orchestrator.ts and lordicon.ts
26
+ export * from "./lordicon.ts";
27
+ export * from "./external-links.ts";
28
+ export * from "./external-link-qr.ts";
29
+ export * from "./lenis.ts";
30
+ export * from "./scroll-spy.ts";
31
+ export * from "./orchestrator.ts";
32
+ // gsap-counter.ts (RFC-0040) is loaded dynamically by orchestrator.ts
33
+ // inline-number-animation.ts (RFC-0041) is loaded dynamically by orchestrator.ts
34
+ // gsap-reveal.ts / gsap-parallax.ts / gsap-stagger.ts (RFC-0106) are loaded
35
+ // dynamically by orchestrator.ts via the reveal / parallax / stagger opt-ins.
@@ -8,7 +8,7 @@
8
8
  </non-goals>
9
9
  </MODULE_CONTRACT>
10
10
  <CHANGE_SUMMARY>
11
- <item>RFC-0041: Created as shared GSAP inline number animation module in @warpgogol/werkstatt-shared/share/scripts.</item>
11
+ <item>RFC-0041: Created as shared GSAP inline number animation module in @warpgogol/werkstatt-shared/client-scripts.</item>
12
12
  </CHANGE_SUMMARY>
13
13
  */
14
14
 
@@ -6,7 +6,12 @@
6
6
  </non-goals>
7
7
  </MODULE_CONTRACT>
8
8
  <CHANGE_SUMMARY>
9
- <item>Migrated Lenis initialization to @warpgogol/werkstatt-shared/share/scripts for platform-wide reuse.</item>
9
+ <item>RFC-1097: step 6 — compass.migrate codemod run
10
+
11
+ 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>
12
+ <item>RFC-1097: sweep — werkstatt-engine clean
13
+
14
+ 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>
10
15
  </CHANGE_SUMMARY>
11
16
  */
12
17
 
@@ -11,7 +11,12 @@
11
11
  </MODULE_CONTRACT>
12
12
 
13
13
  <CHANGE_SUMMARY>
14
- <item>Tidied by compass.changesummary.tidy; see git history for prior entries.</item>
14
+ <item>RFC-1097: step 6 — compass.migrate codemod run
15
+
16
+ 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>
17
+ <item>RFC-1097: sweep — werkstatt-engine clean
18
+
19
+ 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>
15
20
  </CHANGE_SUMMARY> ***************/
16
21
 
17
22
  async function initLordIcon(): Promise<void> {
@@ -6,7 +6,12 @@
6
6
  </non-goals>
7
7
  </MODULE_CONTRACT>
8
8
  <CHANGE_SUMMARY>
9
- <item>Created standard layout orchestrator in @warpgogol/werkstatt-shared/share/scripts to reduce code duplication in apps.</item>
9
+ <item>RFC-1097: step 6 — compass.migrate codemod run
10
+
11
+ 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>
12
+ <item>RFC-1097: sweep — werkstatt-engine clean
13
+
14
+ 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>
10
15
  </CHANGE_SUMMARY>
11
16
  */
12
17
 
@@ -15,6 +20,7 @@
15
20
  import { applyExternalLinkBehavior } from "./external-links.ts";
16
21
  import { initLordIconOnDemand } from "./lordicon.ts";
17
22
  import { initLenis } from "./lenis.ts";
23
+ import { initScrollSpy } from "./scroll-spy.ts";
18
24
 
19
25
  export interface OrchestrationOptions {
20
26
  headerOffset?: number;
@@ -152,4 +158,7 @@ export async function runStandardLayoutOrchestration(
152
158
  const { initExternalLinkQr } = await import("./external-link-qr.ts");
153
159
  initExternalLinkQr();
154
160
  }
161
+
162
+ // 12. RFC-1061: scroll-spy URL hash updates (always-on, zero-dependency).
163
+ initScrollSpy();
155
164
  }
@@ -8,7 +8,12 @@
8
8
  </non-goals>
9
9
  </MODULE_CONTRACT>
10
10
  <CHANGE_SUMMARY>
11
- <item>Tidied by compass.changesummary.tidy; see git history for prior entries.</item>
11
+ <item>RFC-1097: step 6 — compass.migrate codemod run
12
+
13
+ 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>
14
+ <item>RFC-1097: sweep — werkstatt-engine clean
15
+
16
+ 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>
12
17
  </CHANGE_SUMMARY>
13
18
  ***************************************************************/
14
19
 
@@ -0,0 +1,106 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>scroll-spy — watches section elements via IntersectionObserver and updates the URL hash via history.replaceState as the user scrolls (RFC-1061).</purpose>
4
+ <singleton-state>
5
+ <item>Module-level `activeObserver` and `activeCleanup` enforce single-instance idempotency (AC-9). Calling `initScrollSpy` twice disconnects the previous observer before creating a new one.</item>
6
+ </singleton-state>
7
+ <non-goals>
8
+ <item>Do not add navigation highlight UI — that is a separate concern.</item>
9
+ <item>Do not use pushState — replaceState only to avoid history pollution.</item>
10
+ <item>Do not modify section rendering or section id resolution.</item>
11
+ </non-goals>
12
+ </MODULE_CONTRACT>
13
+ <CHANGE_SUMMARY>
14
+ <item>RFC-1061: Created scroll-spy script with IntersectionObserver-based section tracking and URL hash updates.</item>
15
+ <item>RFC-1097: sweep — tail packages clean
16
+
17
+ Sweep batch 3: rewrote ~95 purposes across werkstatt-knowledge, werkstatt-shared, godot-game, phaser-game, lifecycle-core, projektarchiv-*, portal-*, billing-*, typescript (CONTRACT-02/PURPOSE-02). Real KEY_DECISIONS on 5 godot utils, non-goals on 5 CONTRACT-03 files, headers on 4 headerless files, CS-07 history literal fix on 2 files. Policy: vitest.config.ts + test-fixtures testPatterns, worker-configuration.d.ts excludedPath. All non-site/engine packages now 0 diagnostics.</item>
18
+ </CHANGE_SUMMARY>
19
+ */
20
+
21
+ export interface ScrollSpyOptions {
22
+ /** CSS selector for elements to observe. Default: "section[id]" */
23
+ selector?: string;
24
+ /** IntersectionObserver rootMargin. Default: "-20% 0px -80% 0px" */
25
+ rootMargin?: string;
26
+ /** IntersectionObserver threshold. Default: 0 */
27
+ threshold?: number;
28
+ /** Whether to clear the hash when scrolled above the first section. Default: true */
29
+ clearAtTop?: boolean;
30
+ }
31
+
32
+ let activeCleanup: (() => void) | null = null;
33
+
34
+ export function initScrollSpy(options?: ScrollSpyOptions): () => void {
35
+ const {
36
+ selector = "section[id]",
37
+ rootMargin = "-20% 0px -80% 0px",
38
+ threshold = 0,
39
+ clearAtTop = true,
40
+ } = options ?? {};
41
+
42
+ if (activeCleanup) {
43
+ activeCleanup();
44
+ activeCleanup = null;
45
+ }
46
+
47
+ if (!("IntersectionObserver" in window)) {
48
+ return () => {};
49
+ }
50
+
51
+ const sections = Array.from(document.querySelectorAll<HTMLElement>(selector)).filter(
52
+ (el) => !el.closest(".wl-modal"),
53
+ );
54
+
55
+ if (sections.length === 0) {
56
+ return () => {};
57
+ }
58
+
59
+ let currentId: string | null = null;
60
+
61
+ const updateHash = (id: string | null) => {
62
+ if (id === currentId) return;
63
+ currentId = id;
64
+ if (id) {
65
+ history.replaceState(null, "", `#${id}`);
66
+ } else if (clearAtTop) {
67
+ history.replaceState(null, "", location.pathname + location.search);
68
+ }
69
+ };
70
+
71
+ const observer = new IntersectionObserver(
72
+ (entries) => {
73
+ let topmostVisible: HTMLElement | null = null;
74
+ let topmostTop = Infinity;
75
+
76
+ for (const entry of entries) {
77
+ if (!entry.isIntersecting) continue;
78
+ const el = entry.target as HTMLElement;
79
+ const rect = el.getBoundingClientRect();
80
+ if (rect.top < topmostTop) {
81
+ topmostTop = rect.top;
82
+ topmostVisible = el;
83
+ }
84
+ }
85
+
86
+ if (topmostVisible) {
87
+ updateHash(topmostVisible.id);
88
+ } else if (clearAtTop) {
89
+ updateHash(null);
90
+ }
91
+ },
92
+ { rootMargin, threshold },
93
+ );
94
+
95
+ for (const section of sections) {
96
+ observer.observe(section);
97
+ }
98
+
99
+ const cleanup = () => {
100
+ observer.disconnect();
101
+ activeCleanup = null;
102
+ };
103
+
104
+ activeCleanup = cleanup;
105
+ return cleanup;
106
+ }
@@ -0,0 +1,47 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+
3
+ vi.mock("../scroll-spy.ts", () => ({
4
+ initScrollSpy: vi.fn(() => () => {}),
5
+ }));
6
+
7
+ import { runStandardLayoutOrchestration } from "../orchestrator.ts";
8
+ import { initScrollSpy } from "../scroll-spy.ts";
9
+
10
+ describe("runStandardLayoutOrchestration", () => {
11
+ beforeEach(() => {
12
+ class MockElement {}
13
+ vi.stubGlobal("Element", MockElement);
14
+ vi.stubGlobal("window", {
15
+ matchMedia: vi.fn(() => ({ matches: false })),
16
+ addEventListener: vi.fn(),
17
+ });
18
+ vi.stubGlobal("document", {
19
+ readyState: "complete",
20
+ querySelector: vi.fn(() => null),
21
+ addEventListener: vi.fn(),
22
+ });
23
+ });
24
+
25
+ afterEach(() => {
26
+ vi.unstubAllGlobals();
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ it("is an async function", () => {
31
+ expect(typeof runStandardLayoutOrchestration).toBe("function");
32
+ });
33
+
34
+ it("AC-6: calls initScrollSpy unconditionally as a new step", async () => {
35
+ await runStandardLayoutOrchestration();
36
+ expect(initScrollSpy).toHaveBeenCalled();
37
+ });
38
+
39
+ it("AC-6: calls initScrollSpy even when all opt-in flags are false", async () => {
40
+ await runStandardLayoutOrchestration({
41
+ counters: false,
42
+ reveal: false,
43
+ smoothScroll: false,
44
+ });
45
+ expect(initScrollSpy).toHaveBeenCalled();
46
+ });
47
+ });
@@ -0,0 +1,204 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { initScrollSpy } from "../scroll-spy.ts";
3
+
4
+ interface MockRect {
5
+ top: number;
6
+ }
7
+ interface MockElement {
8
+ id: string;
9
+ closest: (sel: string) => object | null;
10
+ getBoundingClientRect: () => MockRect;
11
+ }
12
+ interface MockEntry {
13
+ target: MockElement;
14
+ isIntersecting: boolean;
15
+ boundingClientRect: MockRect;
16
+ }
17
+ interface MockObserverInstance {
18
+ observe: ReturnType<typeof vi.fn>;
19
+ unobserve: ReturnType<typeof vi.fn>;
20
+ disconnect: ReturnType<typeof vi.fn>;
21
+ takeRecords: ReturnType<typeof vi.fn>;
22
+ }
23
+ interface MockDocument {
24
+ body: { innerHTML: string };
25
+ readyState: string;
26
+ querySelector: ReturnType<typeof vi.fn>;
27
+ querySelectorAll: ReturnType<typeof vi.fn>;
28
+ getElementById: ReturnType<typeof vi.fn>;
29
+ addEventListener: ReturnType<typeof vi.fn>;
30
+ }
31
+
32
+ let observerCallbacks: Array<(entries: MockEntry[]) => void>;
33
+ let observerInstances: MockObserverInstance[];
34
+ let mockHistory: { replaceState: ReturnType<typeof vi.fn> };
35
+ let mockDocument: MockDocument;
36
+ let sectionCache: Map<string, MockElement>;
37
+
38
+ function parseSections(html: string, selector: string): MockElement[] {
39
+ if (selector !== "section[id]") return [];
40
+ const sections: MockElement[] = [];
41
+ const sectionRegex = /<section\s+id="([^"]+)"[^>]*>([\s\S]*?)<\/section>/gi;
42
+ let match;
43
+ while ((match = sectionRegex.exec(html)) !== null) {
44
+ const modalBefore = html.substring(0, match.index);
45
+ const lastModalOpen = modalBefore.lastIndexOf('class="wl-modal"');
46
+ const lastModalClose = modalBefore.lastIndexOf("</div>");
47
+ const inModal = lastModalOpen > lastModalClose;
48
+ const el = {
49
+ id: match[1],
50
+ closest: (sel: string) => (sel === ".wl-modal" && inModal ? {} : null),
51
+ getBoundingClientRect: () => ({ top: 0 }),
52
+ };
53
+ sectionCache.set(match[1], el);
54
+ sections.push(el);
55
+ }
56
+ return sections;
57
+ }
58
+
59
+ describe("initScrollSpy", () => {
60
+ beforeEach(() => {
61
+ observerCallbacks = [];
62
+ observerInstances = [];
63
+ sectionCache = new Map();
64
+
65
+ class MockIntersectionObserver {
66
+ constructor(cb: (entries: MockEntry[]) => void) {
67
+ observerCallbacks.push(cb);
68
+ const instance = {
69
+ observe: vi.fn(),
70
+ unobserve: vi.fn(),
71
+ disconnect: vi.fn(),
72
+ takeRecords: vi.fn(() => []),
73
+ };
74
+ observerInstances.push(instance);
75
+ return instance;
76
+ }
77
+ }
78
+
79
+ mockHistory = { replaceState: vi.fn() };
80
+ mockDocument = {
81
+ body: { innerHTML: "" },
82
+ readyState: "complete",
83
+ querySelector: vi.fn(() => null),
84
+ querySelectorAll: vi.fn((selector: string) => {
85
+ return parseSections(mockDocument.body.innerHTML, selector);
86
+ }),
87
+ getElementById: vi.fn((id: string) => sectionCache.get(id) ?? null),
88
+ addEventListener: vi.fn(),
89
+ };
90
+
91
+ vi.stubGlobal("window", {
92
+ IntersectionObserver: MockIntersectionObserver,
93
+ addEventListener: vi.fn(),
94
+ });
95
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
96
+ vi.stubGlobal("history", mockHistory);
97
+ vi.stubGlobal("location", {
98
+ pathname: "/page",
99
+ search: "",
100
+ hash: "",
101
+ });
102
+ vi.stubGlobal("document", mockDocument);
103
+ });
104
+
105
+ afterEach(() => {
106
+ vi.unstubAllGlobals();
107
+ });
108
+
109
+ it("AC-1: updates URL hash via history.replaceState when a section is in view", () => {
110
+ mockDocument.body.innerHTML = '<section id="approach">Content</section>';
111
+ const cleanup = initScrollSpy();
112
+ const section = sectionCache.get("approach")!;
113
+
114
+ observerCallbacks[0]([
115
+ {
116
+ target: section,
117
+ isIntersecting: true,
118
+ boundingClientRect: { top: 100 },
119
+ },
120
+ ]);
121
+
122
+ expect(mockHistory.replaceState).toHaveBeenCalledWith(null, "", "#approach");
123
+ cleanup();
124
+ });
125
+
126
+ it("AC-2: clears URL hash when scrolled above the first section", () => {
127
+ mockDocument.body.innerHTML = '<section id="hero">Hero</section>';
128
+ const cleanup = initScrollSpy();
129
+ const section = sectionCache.get("hero")!;
130
+
131
+ observerCallbacks[0]([
132
+ {
133
+ target: section,
134
+ isIntersecting: true,
135
+ boundingClientRect: { top: 100 },
136
+ },
137
+ ]);
138
+ expect(mockHistory.replaceState).toHaveBeenCalledWith(null, "", "#hero");
139
+
140
+ observerCallbacks[0]([
141
+ {
142
+ target: section,
143
+ isIntersecting: false,
144
+ boundingClientRect: { top: 500 },
145
+ },
146
+ ]);
147
+ expect(mockHistory.replaceState).toHaveBeenCalledWith(null, "", "/page");
148
+
149
+ cleanup();
150
+ });
151
+
152
+ it("AC-3: returns a cleanup callback that disconnects the observer", () => {
153
+ mockDocument.body.innerHTML = '<section id="test">Test</section>';
154
+ const cleanup = initScrollSpy();
155
+
156
+ expect(typeof cleanup).toBe("function");
157
+ cleanup();
158
+
159
+ expect(observerInstances[0].disconnect).toHaveBeenCalled();
160
+ });
161
+
162
+ it("AC-4: returns immediately without creating an observer when no section[id] elements exist", () => {
163
+ mockDocument.body.innerHTML = "<div>No sections here</div>";
164
+ const cleanup = initScrollSpy();
165
+
166
+ expect(observerInstances.length).toBe(0);
167
+ expect(typeof cleanup).toBe("function");
168
+ cleanup();
169
+ });
170
+
171
+ it("AC-5: returns silently without throwing when IntersectionObserver is not supported", () => {
172
+ vi.unstubAllGlobals();
173
+ vi.stubGlobal("window", { addEventListener: vi.fn() });
174
+ vi.stubGlobal("history", mockHistory);
175
+ vi.stubGlobal("location", { pathname: "/page", search: "", hash: "" });
176
+ vi.stubGlobal("document", mockDocument);
177
+ mockDocument.body.innerHTML = '<section id="test">Test</section>';
178
+ sectionCache.clear();
179
+
180
+ expect(() => initScrollSpy()).not.toThrow();
181
+ });
182
+
183
+ it("AC-9: is idempotent — calling twice disconnects the first observer before creating a new one", () => {
184
+ mockDocument.body.innerHTML = '<section id="a">A</section><section id="b">B</section>';
185
+ initScrollSpy();
186
+ expect(observerInstances.length).toBe(1);
187
+
188
+ initScrollSpy();
189
+ expect(observerInstances.length).toBe(2);
190
+ expect(observerInstances[0].disconnect).toHaveBeenCalled();
191
+ });
192
+
193
+ it("excludes sections inside .wl-modal elements", () => {
194
+ mockDocument.body.innerHTML =
195
+ '<div class="wl-modal"><section id="modal-section">Modal</section></div>' +
196
+ '<section id="page-section">Page</section>';
197
+
198
+ initScrollSpy();
199
+
200
+ expect(observerInstances.length).toBe(1);
201
+ expect(observerInstances[0].observe).toHaveBeenCalledTimes(1);
202
+ expect(observerInstances[0].observe).toHaveBeenCalledWith(sectionCache.get("page-section"));
203
+ });
204
+ });