@nurix/etna 0.19.11 → 0.19.13

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 (494) hide show
  1. package/README.md +6 -4
  2. package/data/harness-index.json +1 -1
  3. package/data/harnesses/code/agents/maintenance.md +1 -1
  4. package/data/harnesses/code/harness.json +1 -1
  5. package/data/harnesses/code/rules/frontend.md +5 -5
  6. package/data/harnesses/code/skills/design/SKILL.md +70 -50
  7. package/data/harnesses/code/skills/design/components/charts.md +76 -0
  8. package/data/harnesses/code/skills/design/components/collections.md +132 -0
  9. package/data/harnesses/code/skills/design/components/feedback.md +47 -0
  10. package/data/harnesses/code/skills/design/components/hooks.md +35 -0
  11. package/data/harnesses/code/skills/design/components/index.md +143 -0
  12. package/data/harnesses/code/skills/design/components/inputs.md +327 -0
  13. package/data/harnesses/code/skills/design/components/layout.md +52 -0
  14. package/data/harnesses/code/skills/design/components/media.md +66 -0
  15. package/data/harnesses/code/skills/design/components/motion.md +82 -0
  16. package/data/harnesses/code/skills/design/components/navigation.md +29 -0
  17. package/data/harnesses/code/skills/design/components/overlays.md +72 -0
  18. package/data/harnesses/code/skills/design/components/setup.md +40 -0
  19. package/data/harnesses/code/skills/design/components/typography.md +46 -0
  20. package/data/harnesses/code/skills/design/components/utils.md +37 -0
  21. package/data/harnesses/code/skills/design/data/blueprints/browse.md +38 -0
  22. package/data/harnesses/code/skills/design/data/blueprints/creation-flow.md +38 -0
  23. package/data/harnesses/code/skills/design/data/blueprints/dashboard.md +38 -0
  24. package/data/harnesses/code/skills/design/data/blueprints/grid.md +38 -0
  25. package/data/harnesses/code/skills/design/data/blueprints/queue.md +38 -0
  26. package/data/harnesses/code/skills/design/data/blueprints/record-detail.md +38 -0
  27. package/data/harnesses/code/skills/design/data/blueprints/settings.md +38 -0
  28. package/data/harnesses/code/skills/design/data/chart-styling/Readme.md +33 -0
  29. package/data/harnesses/code/skills/design/data/chart-styling/carbon-accessible-alert-semantic.md +19 -0
  30. package/data/harnesses/code/skills/design/data/chart-styling/carbon-monochrome-sequential-trend.md +19 -0
  31. package/data/harnesses/code/skills/design/data/chart-styling/datadog-classic-six-aligned.md +19 -0
  32. package/data/harnesses/code/skills/design/data/chart-styling/datawrapper-tiered-gray-hierarchy.md +19 -0
  33. package/data/harnesses/code/skills/design/data/chart-styling/econ-red-rule-horizontal.md +19 -0
  34. package/data/harnesses/code/skills/design/data/chart-styling/financial-diverging-threshold-overlay.md +19 -0
  35. package/data/harnesses/code/skills/design/data/chart-styling/ft-vocabulary-ranked-muted.md +19 -0
  36. package/data/harnesses/code/skills/design/data/chart-styling/grafana-dark-threshold-bands.md +19 -0
  37. package/data/harnesses/code/skills/design/data/chart-styling/material-rounded-explicit-label.md +19 -0
  38. package/data/harnesses/code/skills/design/data/chart-styling/nyt-ghost-grid-direct-label.md +19 -0
  39. package/data/harnesses/code/skills/design/data/chart-styling/plot-currentcolor-inherit.md +19 -0
  40. package/data/harnesses/code/skills/design/data/chart-styling/saas-gradient-glow-area.md +19 -0
  41. package/data/harnesses/code/skills/design/data/chart-styling/shadcn-composable-css-vars.md +19 -0
  42. package/data/harnesses/code/skills/design/data/charts/census.csv +58 -0
  43. package/data/harnesses/code/skills/design/data/checklists/browse.md +21 -0
  44. package/data/harnesses/code/skills/design/data/checklists/creation-flow.md +20 -0
  45. package/data/harnesses/code/skills/design/data/checklists/dashboard.md +21 -0
  46. package/data/harnesses/code/skills/design/data/checklists/grid.md +21 -0
  47. package/data/harnesses/code/skills/design/data/checklists/queue.md +21 -0
  48. package/data/harnesses/code/skills/design/data/checklists/record-detail.md +21 -0
  49. package/data/harnesses/code/skills/design/data/checklists/settings.md +21 -0
  50. package/data/harnesses/code/skills/design/data/chrome/board-column.md +68 -0
  51. package/data/harnesses/code/skills/design/data/chrome/data-table.md +63 -0
  52. package/data/harnesses/code/skills/design/data/chrome/side-panel.md +60 -0
  53. package/data/harnesses/code/skills/design/data/chrome/sidebar.md +78 -0
  54. package/data/harnesses/code/skills/design/data/chrome/stat-cards.md +62 -0
  55. package/data/harnesses/code/skills/design/data/chrome/topbar.md +65 -0
  56. package/data/harnesses/code/skills/design/data/color/accent-presets.md +90 -0
  57. package/data/harnesses/code/skills/design/data/color/aesop-earth-mute.md +91 -0
  58. package/data/harnesses/code/skills/design/data/color/arc-peach-play.md +90 -0
  59. package/data/harnesses/code/skills/design/data/color/attio-hunter-teal.md +90 -0
  60. package/data/harnesses/code/skills/design/data/color/carbon-cyan-signal.md +90 -0
  61. package/data/harnesses/code/skills/design/data/color/clay-parchment.md +90 -0
  62. package/data/harnesses/code/skills/design/data/color/duolingo-feather.md +90 -0
  63. package/data/harnesses/code/skills/design/data/color/geist-mono-signal.md +90 -0
  64. package/data/harnesses/code/skills/design/data/color/linear-graphite-violet.md +90 -0
  65. package/data/harnesses/code/skills/design/data/color/mercury-onyx-cobalt.md +90 -0
  66. package/data/harnesses/code/skills/design/data/color/notion-paper-ink.md +90 -0
  67. package/data/harnesses/code/skills/design/data/color/radix-slate-iris.md +90 -0
  68. package/data/harnesses/code/skills/design/data/color/raycast-void-spectrum.md +90 -0
  69. package/data/harnesses/code/skills/design/data/color/spectrum-celery-fuchsia.md +90 -0
  70. package/data/harnesses/code/skills/design/data/color/stripe-blurple-ruby.md +90 -0
  71. package/data/harnesses/code/skills/design/data/color/superhuman-cod-picton.md +89 -0
  72. package/data/harnesses/code/skills/design/data/corpora/operations-review/companies.csv +15 -0
  73. package/data/harnesses/code/skills/design/data/corpora/operations-review/corpus.md +87 -0
  74. package/data/harnesses/code/skills/design/data/corpora/operations-review/people.csv +25 -0
  75. package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/companies.csv +15 -0
  76. package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/corpus.md +83 -0
  77. package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/people.csv +25 -0
  78. package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/companies.csv +15 -0
  79. package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/corpus.md +89 -0
  80. package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/people.csv +25 -0
  81. package/data/harnesses/code/skills/design/data/elevation/ant-design-directional-shadow.md +19 -0
  82. package/data/harnesses/code/skills/design/data/elevation/apple-liquid-glass-material.md +18 -0
  83. package/data/harnesses/code/skills/design/data/elevation/atlassian-surface-shadow-pairing.md +20 -0
  84. package/data/harnesses/code/skills/design/data/elevation/fluent-2-key-ambient-ramp.md +20 -0
  85. package/data/harnesses/code/skills/design/data/elevation/github-primer-sparing-shadow.md +19 -0
  86. package/data/harnesses/code/skills/design/data/elevation/ibm-carbon-layer-and-border.md +19 -0
  87. package/data/harnesses/code/skills/design/data/elevation/linear-dark-surface-ladder.md +17 -0
  88. package/data/harnesses/code/skills/design/data/elevation/material-2-key-ambient-dp-ramp.md +19 -0
  89. package/data/harnesses/code/skills/design/data/elevation/material-3-tonal-elevation.md +21 -0
  90. package/data/harnesses/code/skills/design/data/elevation/neobrutalist-hard-offset.md +19 -0
  91. package/data/harnesses/code/skills/design/data/elevation/neumorphic-soft-dual-shadow.md +18 -0
  92. package/data/harnesses/code/skills/design/data/elevation/notion-warm-minimal-hairline.md +17 -0
  93. package/data/harnesses/code/skills/design/data/elevation/polaris-elevation-inset-bevel.md +21 -0
  94. package/data/harnesses/code/skills/design/data/elevation/radix-themes-alpha-ring-scale.md +19 -0
  95. package/data/harnesses/code/skills/design/data/elevation/shadcn-neutral-derived-radius.md +19 -0
  96. package/data/harnesses/code/skills/design/data/elevation/stripe-brand-tinted-dual-shadow.md +18 -0
  97. package/data/harnesses/code/skills/design/data/elevation/tailwind-default-utility-scale.md +19 -0
  98. package/data/harnesses/code/skills/design/data/elevation/vercel-geist-shadow-as-border.md +20 -0
  99. package/data/harnesses/code/skills/design/data/gradients/amber-attention-fill.md +16 -0
  100. package/data/harnesses/code/skills/design/data/gradients/cloud-shadow-wash.md +16 -0
  101. package/data/harnesses/code/skills/design/data/gradients/confident-lavender-wash.md +16 -0
  102. package/data/harnesses/code/skills/design/data/gradients/ember-ship-flare.md +18 -0
  103. package/data/harnesses/code/skills/design/data/gradients/full-metal-wash.md +16 -0
  104. package/data/harnesses/code/skills/design/data/gradients/indigo-ruby-cta-band.md +18 -0
  105. package/data/harnesses/code/skills/design/data/gradients/linear-graphite-wash.md +18 -0
  106. package/data/harnesses/code/skills/design/data/gradients/marble-quartz-wash.md +16 -0
  107. package/data/harnesses/code/skills/design/data/gradients/midnight-aurora-panel.md +16 -0
  108. package/data/harnesses/code/skills/design/data/gradients/neutral-volume-fill.md +16 -0
  109. package/data/harnesses/code/skills/design/data/gradients/revenue-uptrend-fill.md +16 -0
  110. package/data/harnesses/code/skills/design/data/gradients/risk-decline-fill.md +16 -0
  111. package/data/harnesses/code/skills/design/data/gradients/signal-blue-develop.md +18 -0
  112. package/data/harnesses/code/skills/design/data/gradients/slate-mist-wash.md +16 -0
  113. package/data/harnesses/code/skills/design/data/gradients/stripe-atmosphere-mesh.md +18 -0
  114. package/data/harnesses/code/skills/design/data/gradients/teal-signal-accent.md +16 -0
  115. package/data/harnesses/code/skills/design/data/gradients/violet-magenta-promo.md +16 -0
  116. package/data/harnesses/code/skills/design/data/iconography.md +199 -0
  117. package/data/harnesses/code/skills/design/data/motion/accordion-expand.md +18 -0
  118. package/data/harnesses/code/skills/design/data/motion/destructive-hold-confirm.md +18 -0
  119. package/data/harnesses/code/skills/design/data/motion/drag-dismiss-momentum.md +18 -0
  120. package/data/harnesses/code/skills/design/data/motion/empty-state-arrive.md +18 -0
  121. package/data/harnesses/code/skills/design/data/motion/hover-lift.md +18 -0
  122. package/data/harnesses/code/skills/design/data/motion/modal-center-arrive.md +18 -0
  123. package/data/harnesses/code/skills/design/data/motion/number-ticker-roll.md +18 -0
  124. package/data/harnesses/code/skills/design/data/motion/onboarding-bounce.md +18 -0
  125. package/data/harnesses/code/skills/design/data/motion/panel-slide.md +18 -0
  126. package/data/harnesses/code/skills/design/data/motion/press-feedback.md +18 -0
  127. package/data/harnesses/code/skills/design/data/motion/progress-linear-loop.md +18 -0
  128. package/data/harnesses/code/skills/design/data/motion/route-crossfade.md +18 -0
  129. package/data/harnesses/code/skills/design/data/motion/rubber-band-overscroll.md +18 -0
  130. package/data/harnesses/code/skills/design/data/motion/skeleton-shimmer.md +18 -0
  131. package/data/harnesses/code/skills/design/data/motion/stagger-arrive.md +18 -0
  132. package/data/harnesses/code/skills/design/data/motion/toast-enter-stack.md +18 -0
  133. package/data/harnesses/code/skills/design/data/motion/tooltip-chain-instant.md +18 -0
  134. package/data/harnesses/code/skills/design/data/motion/trigger-anchored-reveal.md +18 -0
  135. package/data/harnesses/code/skills/design/data/tells.csv +34 -0
  136. package/data/harnesses/code/skills/design/data/typography/anton-work-sans.md +17 -0
  137. package/data/harnesses/code/skills/design/data/typography/archivo-black-archivo.md +17 -0
  138. package/data/harnesses/code/skills/design/data/typography/bitter-karla.md +17 -0
  139. package/data/harnesses/code/skills/design/data/typography/dm-serif-dm-sans.md +17 -0
  140. package/data/harnesses/code/skills/design/data/typography/fraunces-inter.md +17 -0
  141. package/data/harnesses/code/skills/design/data/typography/geist-geist-mono.md +17 -0
  142. package/data/harnesses/code/skills/design/data/typography/hubot-sans-mona-sans.md +17 -0
  143. package/data/harnesses/code/skills/design/data/typography/ibm-plex-sans-operator-single-family.md +17 -0
  144. package/data/harnesses/code/skills/design/data/typography/instrument-serif-instrument-sans.md +17 -0
  145. package/data/harnesses/code/skills/design/data/typography/inter-operator-single-family.md +17 -0
  146. package/data/harnesses/code/skills/design/data/typography/libre-baskerville-libre-franklin.md +17 -0
  147. package/data/harnesses/code/skills/design/data/typography/newsreader-inter.md +17 -0
  148. package/data/harnesses/code/skills/design/data/typography/playfair-display-source-sans.md +17 -0
  149. package/data/harnesses/code/skills/design/data/typography/public-sans-operator-single-family.md +17 -0
  150. package/data/harnesses/code/skills/design/data/typography/sohne-inter-fintech.md +17 -0
  151. package/data/harnesses/code/skills/design/data/typography/source-serif-source-sans.md +17 -0
  152. package/data/harnesses/code/skills/design/data/typography/space-grotesk-work-sans-space-mono.md +17 -0
  153. package/data/harnesses/code/skills/design/data/typography/spectral-manrope.md +17 -0
  154. package/data/harnesses/code/skills/design/data/voices/bright-collaborator.md +29 -0
  155. package/data/harnesses/code/skills/design/data/voices/direct-operator.md +29 -0
  156. package/data/harnesses/code/skills/design/data/voices/earnest-helper.md +29 -0
  157. package/data/harnesses/code/skills/design/data/voices/editorial-composed.md +29 -0
  158. package/data/harnesses/code/skills/design/data/voices/quiet-concierge.md +29 -0
  159. package/data/harnesses/code/skills/design/data/voices/warm-guide.md +29 -0
  160. package/data/harnesses/code/skills/design/design-md/SCHEMA.md +179 -0
  161. package/data/harnesses/code/skills/design/design-md/index.csv +67 -0
  162. package/data/harnesses/code/skills/design/design-md/library/airbnb.md +207 -0
  163. package/data/harnesses/code/skills/design/design-md/library/airtable.md +205 -0
  164. package/data/harnesses/code/skills/design/design-md/library/anthropic-claude.md +203 -0
  165. package/data/harnesses/code/skills/design/design-md/library/apple.md +206 -0
  166. package/data/harnesses/code/skills/design/design-md/library/binance.md +210 -0
  167. package/data/harnesses/code/skills/design/design-md/library/bmw.md +210 -0
  168. package/data/harnesses/code/skills/design/design-md/library/cal.md +209 -0
  169. package/data/harnesses/code/skills/design/design-md/library/clay.md +204 -0
  170. package/data/harnesses/code/skills/design/design-md/library/clickhouse.md +205 -0
  171. package/data/harnesses/code/skills/design/design-md/library/cohere.md +205 -0
  172. package/data/harnesses/code/skills/design/design-md/library/coinbase.md +198 -0
  173. package/data/harnesses/code/skills/design/design-md/library/composio.md +198 -0
  174. package/data/harnesses/code/skills/design/design-md/library/cursor.md +197 -0
  175. package/data/harnesses/code/skills/design/design-md/library/elevenlabs.md +199 -0
  176. package/data/harnesses/code/skills/design/design-md/library/expo.md +200 -0
  177. package/data/harnesses/code/skills/design/design-md/library/figma.md +199 -0
  178. package/data/harnesses/code/skills/design/design-md/library/framer.md +202 -0
  179. package/data/harnesses/code/skills/design/design-md/library/hashicorp.md +205 -0
  180. package/data/harnesses/code/skills/design/design-md/library/ibm.md +205 -0
  181. package/data/harnesses/code/skills/design/design-md/library/intercom.md +204 -0
  182. package/data/harnesses/code/skills/design/design-md/library/kraken.md +200 -0
  183. package/data/harnesses/code/skills/design/design-md/library/linear-app.md +198 -0
  184. package/data/harnesses/code/skills/design/design-md/library/lovable.md +199 -0
  185. package/data/harnesses/code/skills/design/design-md/library/meta.md +204 -0
  186. package/data/harnesses/code/skills/design/design-md/library/minimax.md +204 -0
  187. package/data/harnesses/code/skills/design/design-md/library/mintlify.md +210 -0
  188. package/data/harnesses/code/skills/design/design-md/library/miro.md +209 -0
  189. package/data/harnesses/code/skills/design/design-md/library/mistral-ai.md +201 -0
  190. package/data/harnesses/code/skills/design/design-md/library/mongodb.md +201 -0
  191. package/data/harnesses/code/skills/design/design-md/library/notion.md +204 -0
  192. package/data/harnesses/code/skills/design/design-md/library/nvidia.md +215 -0
  193. package/data/harnesses/code/skills/design/design-md/library/ollama.md +213 -0
  194. package/data/harnesses/code/skills/design/design-md/library/opencode-ai.md +216 -0
  195. package/data/harnesses/code/skills/design/design-md/library/posthog.md +218 -0
  196. package/data/harnesses/code/skills/design/design-md/library/raycast.md +214 -0
  197. package/data/harnesses/code/skills/design/design-md/library/replicate.md +202 -0
  198. package/data/harnesses/code/skills/design/design-md/library/resend.md +203 -0
  199. package/data/harnesses/code/skills/design/design-md/library/revolut.md +203 -0
  200. package/data/harnesses/code/skills/design/design-md/library/runwayml.md +203 -0
  201. package/data/harnesses/code/skills/design/design-md/library/sanity.md +200 -0
  202. package/data/harnesses/code/skills/design/design-md/library/sentry.md +201 -0
  203. package/data/harnesses/code/skills/design/design-md/library/shopify.md +202 -0
  204. package/data/harnesses/code/skills/design/design-md/library/slack.md +202 -0
  205. package/data/harnesses/code/skills/design/design-md/library/spacex.md +206 -0
  206. package/data/harnesses/code/skills/design/design-md/library/spotify.md +206 -0
  207. package/data/harnesses/code/skills/design/design-md/library/stripe.md +196 -0
  208. package/data/harnesses/code/skills/design/design-md/library/supabase.md +196 -0
  209. package/data/harnesses/code/skills/design/design-md/library/superhuman.md +194 -0
  210. package/data/harnesses/code/skills/design/design-md/library/tesla.md +198 -0
  211. package/data/harnesses/code/skills/design/design-md/library/theverge.md +199 -0
  212. package/data/harnesses/code/skills/design/design-md/library/together-ai.md +193 -0
  213. package/data/harnesses/code/skills/design/design-md/library/uber.md +195 -0
  214. package/data/harnesses/code/skills/design/design-md/library/vercel.md +195 -0
  215. package/data/harnesses/code/skills/design/design-md/library/voltagent.md +197 -0
  216. package/data/harnesses/code/skills/design/design-md/library/warp.md +196 -0
  217. package/data/harnesses/code/skills/design/design-md/library/webflow.md +197 -0
  218. package/data/harnesses/code/skills/design/design-md/library/wired.md +197 -0
  219. package/data/harnesses/code/skills/design/design-md/library/wise.md +198 -0
  220. package/data/harnesses/code/skills/design/design-md/library/x-ai.md +199 -0
  221. package/data/harnesses/code/skills/design/design-md/library/zapier.md +198 -0
  222. package/data/harnesses/code/skills/design/design-md/nurix.md +214 -0
  223. package/data/harnesses/code/skills/design/design-md/variants/editorial-luxury.md +184 -0
  224. package/data/harnesses/code/skills/design/design-md/variants/expressive-saas.md +216 -0
  225. package/data/harnesses/code/skills/design/design-md/variants/quiet-premium.md +211 -0
  226. package/data/harnesses/code/skills/design/design-md/variants/terse-dense-operator.md +212 -0
  227. package/data/harnesses/code/skills/design/design-md/variants/warm-approachable.md +216 -0
  228. package/data/harnesses/code/skills/design/references/compose.md +23 -0
  229. package/data/harnesses/code/skills/design/references/content.md +28 -0
  230. package/data/harnesses/code/skills/design/references/emission.md +21 -0
  231. package/data/harnesses/code/skills/design/references/existing-apps.md +27 -0
  232. package/data/harnesses/code/skills/design/references/intake.md +36 -0
  233. package/data/harnesses/code/skills/design/references/object-pass.md +24 -0
  234. package/data/harnesses/code/skills/design/references/ship-gate.md +29 -0
  235. package/data/harnesses/code/skills/design/references/ux-patterns/Readme.md +195 -0
  236. package/data/harnesses/code/skills/design/references/ux-patterns/ai-layer.md +49 -0
  237. package/data/harnesses/code/skills/design/references/ux-patterns/attributes-lifecycle.md +43 -0
  238. package/data/harnesses/code/skills/design/references/ux-patterns/container-choice.md +44 -0
  239. package/data/harnesses/code/skills/design/references/ux-patterns/creation-paths.md +46 -0
  240. package/data/harnesses/code/skills/design/references/ux-patterns/dashboards.md +42 -0
  241. package/data/harnesses/code/skills/design/references/ux-patterns/modes.md +42 -0
  242. package/data/harnesses/code/skills/design/references/ux-patterns/object-model.md +46 -0
  243. package/data/harnesses/code/skills/design/references/ux-patterns/page-archetype.md +48 -0
  244. package/data/harnesses/code/skills/design/references/ux-patterns/promotion-demotion.md +37 -0
  245. package/data/harnesses/code/skills/design/references/ux-patterns/settings.md +42 -0
  246. package/data/harnesses/code/skills/design/references/ux-patterns/standard-pages.md +46 -0
  247. package/data/harnesses/code/skills/design/references/ux-patterns/state-matrix.md +41 -0
  248. package/data/harnesses/code/skills/design/references/ux-patterns/table-mechanics.md +48 -0
  249. package/data/harnesses/code/skills/design/references/ux-patterns/verbs.md +45 -0
  250. package/data/harnesses/code/skills/design/references/ux-patterns/viewer-context.md +40 -0
  251. package/data/harnesses/code/skills/design/scripts/README-validator.md +27 -0
  252. package/data/harnesses/code/skills/design/scripts/select-chart.mjs +596 -0
  253. package/data/harnesses/code/skills/design/scripts/validate-design-md.mjs +1198 -0
  254. package/data/harnesses/discussion/agents/discussion-baseline.md +1 -1
  255. package/data/harnesses/discussion/harness.json +1 -1
  256. package/data/harnesses/etna/agents/maintenance.md +1 -1
  257. package/data/harnesses/etna/harness.json +1 -1
  258. package/data/harnesses/hr/CLAUDE.md +1 -1
  259. package/data/harnesses/hr/harness.json +1 -1
  260. package/package.json +1 -1
  261. package/data/harnesses/code/skills/design/data/catalog/rules.csv +0 -137
  262. package/data/harnesses/code/skills/design/data/combinations/Readme.md +0 -33
  263. package/data/harnesses/code/skills/design/data/combinations/accent-presets.md +0 -113
  264. package/data/harnesses/code/skills/design/data/combinations/charts.csv +0 -33
  265. package/data/harnesses/code/skills/design/data/combinations/fonts-known.json +0 -448
  266. package/data/harnesses/code/skills/design/data/combinations/icons.csv +0 -157
  267. package/data/harnesses/code/skills/design/data/combinations/landing.csv +0 -38
  268. package/data/harnesses/code/skills/design/data/combinations/lucide-names.json +0 -2001
  269. package/data/harnesses/code/skills/design/data/combinations/manifest.json +0 -330
  270. package/data/harnesses/code/skills/design/data/combinations/motion.csv +0 -10
  271. package/data/harnesses/code/skills/design/data/combinations/palettes.csv +0 -92
  272. package/data/harnesses/code/skills/design/data/combinations/product-router.csv +0 -80
  273. package/data/harnesses/code/skills/design/data/combinations/styles.csv +0 -67
  274. package/data/harnesses/code/skills/design/data/combinations/typography.csv +0 -64
  275. package/data/harnesses/code/skills/design/data/design-md/SCHEMA.md +0 -106
  276. package/data/harnesses/code/skills/design/data/design-md/library/airbnb.md +0 -154
  277. package/data/harnesses/code/skills/design/data/design-md/library/airtable.md +0 -151
  278. package/data/harnesses/code/skills/design/data/design-md/library/anthropic-claude.md +0 -139
  279. package/data/harnesses/code/skills/design/data/design-md/library/apple.md +0 -139
  280. package/data/harnesses/code/skills/design/data/design-md/library/binance.md +0 -140
  281. package/data/harnesses/code/skills/design/data/design-md/library/bmw.md +0 -140
  282. package/data/harnesses/code/skills/design/data/design-md/library/cal.md +0 -140
  283. package/data/harnesses/code/skills/design/data/design-md/library/clay.md +0 -139
  284. package/data/harnesses/code/skills/design/data/design-md/library/clickhouse.md +0 -139
  285. package/data/harnesses/code/skills/design/data/design-md/library/cohere.md +0 -147
  286. package/data/harnesses/code/skills/design/data/design-md/library/coinbase.md +0 -156
  287. package/data/harnesses/code/skills/design/data/design-md/library/composio.md +0 -138
  288. package/data/harnesses/code/skills/design/data/design-md/library/cursor.md +0 -138
  289. package/data/harnesses/code/skills/design/data/design-md/library/elevenlabs.md +0 -137
  290. package/data/harnesses/code/skills/design/data/design-md/library/expo.md +0 -148
  291. package/data/harnesses/code/skills/design/data/design-md/library/figma.md +0 -141
  292. package/data/harnesses/code/skills/design/data/design-md/library/framer.md +0 -156
  293. package/data/harnesses/code/skills/design/data/design-md/library/hashicorp.md +0 -140
  294. package/data/harnesses/code/skills/design/data/design-md/library/ibm.md +0 -153
  295. package/data/harnesses/code/skills/design/data/design-md/library/intercom.md +0 -158
  296. package/data/harnesses/code/skills/design/data/design-md/library/kraken.md +0 -156
  297. package/data/harnesses/code/skills/design/data/design-md/library/linear-app.md +0 -122
  298. package/data/harnesses/code/skills/design/data/design-md/library/lovable.md +0 -140
  299. package/data/harnesses/code/skills/design/data/design-md/library/meta.md +0 -139
  300. package/data/harnesses/code/skills/design/data/design-md/library/minimax.md +0 -139
  301. package/data/harnesses/code/skills/design/data/design-md/library/mintlify.md +0 -139
  302. package/data/harnesses/code/skills/design/data/design-md/library/miro.md +0 -153
  303. package/data/harnesses/code/skills/design/data/design-md/library/mistral-ai.md +0 -140
  304. package/data/harnesses/code/skills/design/data/design-md/library/mongodb.md +0 -155
  305. package/data/harnesses/code/skills/design/data/design-md/library/notion.md +0 -140
  306. package/data/harnesses/code/skills/design/data/design-md/library/nvidia.md +0 -141
  307. package/data/harnesses/code/skills/design/data/design-md/library/ollama.md +0 -145
  308. package/data/harnesses/code/skills/design/data/design-md/library/opencode-ai.md +0 -142
  309. package/data/harnesses/code/skills/design/data/design-md/library/posthog.md +0 -152
  310. package/data/harnesses/code/skills/design/data/design-md/library/raycast.md +0 -137
  311. package/data/harnesses/code/skills/design/data/design-md/library/replicate.md +0 -140
  312. package/data/harnesses/code/skills/design/data/design-md/library/resend.md +0 -170
  313. package/data/harnesses/code/skills/design/data/design-md/library/revolut.md +0 -145
  314. package/data/harnesses/code/skills/design/data/design-md/library/runwayml.md +0 -137
  315. package/data/harnesses/code/skills/design/data/design-md/library/sanity.md +0 -168
  316. package/data/harnesses/code/skills/design/data/design-md/library/sentry.md +0 -163
  317. package/data/harnesses/code/skills/design/data/design-md/library/shopify.md +0 -147
  318. package/data/harnesses/code/skills/design/data/design-md/library/slack.md +0 -158
  319. package/data/harnesses/code/skills/design/data/design-md/library/spacex.md +0 -171
  320. package/data/harnesses/code/skills/design/data/design-md/library/spotify.md +0 -166
  321. package/data/harnesses/code/skills/design/data/design-md/library/stripe.md +0 -160
  322. package/data/harnesses/code/skills/design/data/design-md/library/supabase.md +0 -173
  323. package/data/harnesses/code/skills/design/data/design-md/library/superhuman.md +0 -145
  324. package/data/harnesses/code/skills/design/data/design-md/library/tesla.md +0 -153
  325. package/data/harnesses/code/skills/design/data/design-md/library/theverge.md +0 -167
  326. package/data/harnesses/code/skills/design/data/design-md/library/together-ai.md +0 -140
  327. package/data/harnesses/code/skills/design/data/design-md/library/uber.md +0 -140
  328. package/data/harnesses/code/skills/design/data/design-md/library/vercel.md +0 -140
  329. package/data/harnesses/code/skills/design/data/design-md/library/voltagent.md +0 -130
  330. package/data/harnesses/code/skills/design/data/design-md/library/warp.md +0 -140
  331. package/data/harnesses/code/skills/design/data/design-md/library/webflow.md +0 -140
  332. package/data/harnesses/code/skills/design/data/design-md/library/wired.md +0 -139
  333. package/data/harnesses/code/skills/design/data/design-md/library/wise.md +0 -137
  334. package/data/harnesses/code/skills/design/data/design-md/library/x-ai.md +0 -138
  335. package/data/harnesses/code/skills/design/data/design-md/library/zapier.md +0 -158
  336. package/data/harnesses/code/skills/design/data/design-md/nurix.md +0 -138
  337. package/data/harnesses/code/skills/design/data/design-tokens/SCHEMA.md +0 -695
  338. package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.resolver.json +0 -20
  339. package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.tokens.json +0 -1272
  340. package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.resolver.json +0 -20
  341. package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.tokens.json +0 -1268
  342. package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.resolver.json +0 -20
  343. package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.tokens.json +0 -1273
  344. package/data/harnesses/code/skills/design/data/design-tokens/apple/design.resolver.json +0 -20
  345. package/data/harnesses/code/skills/design/data/design-tokens/apple/design.tokens.json +0 -1262
  346. package/data/harnesses/code/skills/design/data/design-tokens/binance/design.resolver.json +0 -20
  347. package/data/harnesses/code/skills/design/data/design-tokens/binance/design.tokens.json +0 -1257
  348. package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.resolver.json +0 -20
  349. package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.tokens.json +0 -1270
  350. package/data/harnesses/code/skills/design/data/design-tokens/cal/design.resolver.json +0 -20
  351. package/data/harnesses/code/skills/design/data/design-tokens/cal/design.tokens.json +0 -1270
  352. package/data/harnesses/code/skills/design/data/design-tokens/clay/design.resolver.json +0 -20
  353. package/data/harnesses/code/skills/design/data/design-tokens/clay/design.tokens.json +0 -1261
  354. package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.resolver.json +0 -20
  355. package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.tokens.json +0 -1268
  356. package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.resolver.json +0 -20
  357. package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.tokens.json +0 -1281
  358. package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.resolver.json +0 -20
  359. package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.tokens.json +0 -1262
  360. package/data/harnesses/code/skills/design/data/design-tokens/composio/design.resolver.json +0 -20
  361. package/data/harnesses/code/skills/design/data/design-tokens/composio/design.tokens.json +0 -1271
  362. package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.resolver.json +0 -20
  363. package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.tokens.json +0 -1276
  364. package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.resolver.json +0 -20
  365. package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.tokens.json +0 -1293
  366. package/data/harnesses/code/skills/design/data/design-tokens/expo/design.resolver.json +0 -20
  367. package/data/harnesses/code/skills/design/data/design-tokens/expo/design.tokens.json +0 -1270
  368. package/data/harnesses/code/skills/design/data/design-tokens/figma/design.resolver.json +0 -20
  369. package/data/harnesses/code/skills/design/data/design-tokens/figma/design.tokens.json +0 -1276
  370. package/data/harnesses/code/skills/design/data/design-tokens/framer/design.resolver.json +0 -20
  371. package/data/harnesses/code/skills/design/data/design-tokens/framer/design.tokens.json +0 -1267
  372. package/data/harnesses/code/skills/design/data/design-tokens/goldens.json +0 -251
  373. package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.resolver.json +0 -20
  374. package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.tokens.json +0 -1273
  375. package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.resolver.json +0 -20
  376. package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.tokens.json +0 -1286
  377. package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.resolver.json +0 -20
  378. package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.tokens.json +0 -1290
  379. package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.resolver.json +0 -20
  380. package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.tokens.json +0 -1270
  381. package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.resolver.json +0 -23
  382. package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.dark.json +0 -599
  383. package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.json +0 -1272
  384. package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.resolver.json +0 -20
  385. package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.tokens.json +0 -1267
  386. package/data/harnesses/code/skills/design/data/design-tokens/meta/design.resolver.json +0 -20
  387. package/data/harnesses/code/skills/design/data/design-tokens/meta/design.tokens.json +0 -1289
  388. package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.resolver.json +0 -20
  389. package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.tokens.json +0 -1258
  390. package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.resolver.json +0 -20
  391. package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.tokens.json +0 -1284
  392. package/data/harnesses/code/skills/design/data/design-tokens/miro/design.resolver.json +0 -20
  393. package/data/harnesses/code/skills/design/data/design-tokens/miro/design.tokens.json +0 -1291
  394. package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.resolver.json +0 -20
  395. package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.tokens.json +0 -1263
  396. package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.resolver.json +0 -20
  397. package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.tokens.json +0 -1270
  398. package/data/harnesses/code/skills/design/data/design-tokens/notion/design.resolver.json +0 -20
  399. package/data/harnesses/code/skills/design/data/design-tokens/notion/design.tokens.json +0 -1261
  400. package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.resolver.json +0 -20
  401. package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.tokens.json +0 -1209
  402. package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.resolver.json +0 -20
  403. package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.tokens.json +0 -1265
  404. package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.resolver.json +0 -20
  405. package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.tokens.json +0 -1253
  406. package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.resolver.json +0 -20
  407. package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.tokens.json +0 -1268
  408. package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.resolver.json +0 -20
  409. package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.tokens.json +0 -1261
  410. package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.resolver.json +0 -20
  411. package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.tokens.json +0 -1263
  412. package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.resolver.json +0 -20
  413. package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.tokens.json +0 -1266
  414. package/data/harnesses/code/skills/design/data/design-tokens/resend/design.resolver.json +0 -23
  415. package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.dark.json +0 -623
  416. package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.json +0 -1276
  417. package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.resolver.json +0 -20
  418. package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.tokens.json +0 -1264
  419. package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.resolver.json +0 -20
  420. package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.tokens.json +0 -1268
  421. package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.resolver.json +0 -20
  422. package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.tokens.json +0 -1270
  423. package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.resolver.json +0 -20
  424. package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.tokens.json +0 -1288
  425. package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.resolver.json +0 -20
  426. package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.tokens.json +0 -1272
  427. package/data/harnesses/code/skills/design/data/design-tokens/slack/design.resolver.json +0 -20
  428. package/data/harnesses/code/skills/design/data/design-tokens/slack/design.tokens.json +0 -1265
  429. package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.resolver.json +0 -20
  430. package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.tokens.json +0 -1287
  431. package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.resolver.json +0 -20
  432. package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.tokens.json +0 -1286
  433. package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.resolver.json +0 -20
  434. package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.tokens.json +0 -1285
  435. package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.resolver.json +0 -20
  436. package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.tokens.json +0 -1287
  437. package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.resolver.json +0 -20
  438. package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.tokens.json +0 -1278
  439. package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.resolver.json +0 -20
  440. package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.tokens.json +0 -1269
  441. package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.resolver.json +0 -20
  442. package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.tokens.json +0 -1285
  443. package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.resolver.json +0 -20
  444. package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.tokens.json +0 -1303
  445. package/data/harnesses/code/skills/design/data/design-tokens/uber/design.resolver.json +0 -20
  446. package/data/harnesses/code/skills/design/data/design-tokens/uber/design.tokens.json +0 -1274
  447. package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.resolver.json +0 -20
  448. package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.tokens.json +0 -1296
  449. package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.resolver.json +0 -23
  450. package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.dark.json +0 -612
  451. package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.json +0 -1282
  452. package/data/harnesses/code/skills/design/data/design-tokens/warp/design.resolver.json +0 -20
  453. package/data/harnesses/code/skills/design/data/design-tokens/warp/design.tokens.json +0 -1287
  454. package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.resolver.json +0 -20
  455. package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.tokens.json +0 -1268
  456. package/data/harnesses/code/skills/design/data/design-tokens/wired/design.resolver.json +0 -20
  457. package/data/harnesses/code/skills/design/data/design-tokens/wired/design.tokens.json +0 -1276
  458. package/data/harnesses/code/skills/design/data/design-tokens/wise/design.resolver.json +0 -20
  459. package/data/harnesses/code/skills/design/data/design-tokens/wise/design.tokens.json +0 -1273
  460. package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.resolver.json +0 -20
  461. package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.tokens.json +0 -1296
  462. package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.resolver.json +0 -20
  463. package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.tokens.json +0 -1286
  464. package/data/harnesses/code/skills/design/data/operator-pack/exemplars/command-center.tsx +0 -249
  465. package/data/harnesses/code/skills/design/data/operator-pack/exemplars/create-edit.tsx +0 -249
  466. package/data/harnesses/code/skills/design/data/operator-pack/exemplars/dashboard.tsx +0 -225
  467. package/data/harnesses/code/skills/design/data/operator-pack/exemplars/entity-detail.tsx +0 -249
  468. package/data/harnesses/code/skills/design/data/operator-pack/exemplars/list-detail.tsx +0 -224
  469. package/data/harnesses/code/skills/design/data/operator-pack/exemplars/log-stream.tsx +0 -249
  470. package/data/harnesses/code/skills/design/data/operator-pack/fixtures.md +0 -88
  471. package/data/harnesses/code/skills/design/data/operator-pack/surface-catalog.md +0 -132
  472. package/data/harnesses/code/skills/design/data/operator-pack/trust-rubric.md +0 -76
  473. package/data/harnesses/code/skills/design/references/create.md +0 -173
  474. package/data/harnesses/code/skills/design/references/diagnosis.md +0 -108
  475. package/data/harnesses/code/skills/design/references/hard-rules.md +0 -177
  476. package/data/harnesses/code/skills/design/references/improve.md +0 -116
  477. package/data/harnesses/code/skills/design/references/interaction.md +0 -77
  478. package/data/harnesses/code/skills/design/references/introspect.md +0 -100
  479. package/data/harnesses/code/skills/design/references/lifecycle.md +0 -73
  480. package/data/harnesses/code/skills/design/references/personality.md +0 -575
  481. package/data/harnesses/code/skills/design/references/structure.md +0 -159
  482. package/data/harnesses/code/skills/design/scripts/convert-design-md.mjs +0 -1312
  483. package/data/harnesses/code/skills/design/scripts/derive-personality.mjs +0 -1498
  484. package/data/harnesses/code/skills/design/scripts/generate-fixtures.mjs +0 -551
  485. package/data/harnesses/code/skills/design/scripts/lib/checks.mjs +0 -1092
  486. package/data/harnesses/code/skills/design/scripts/lib/golden.mjs +0 -26
  487. package/data/harnesses/code/skills/design/scripts/lib/vocabulary.mjs +0 -65
  488. package/data/harnesses/code/skills/design/scripts/phase-gate.mjs +0 -538
  489. package/data/harnesses/code/skills/design/scripts/preship-sweep.mjs +0 -312
  490. package/data/harnesses/code/skills/design/scripts/render-rung.mjs +0 -890
  491. package/data/harnesses/code/skills/design/scripts/scaffold.mjs +0 -769
  492. package/data/harnesses/code/skills/design/scripts/self-integrity.mjs +0 -675
  493. package/data/harnesses/code/skills/design/scripts/snapshot-goldens.mjs +0 -164
  494. package/data/harnesses/code/skills/design/scripts/validate-combinations.mjs +0 -439
@@ -1,890 +0,0 @@
1
- // skills/design/scripts/render-rung.mjs — verification-ladder rung 2: deterministic render checks + pixel instruments (D32). --require makes the rung a gate (create's ladder: fail-closed when no headless browser is importable — emission contract 4); without it a skip degrades gracefully but always lands as a named finding, never a one-line notice. --screenshot <outdir> saves one PNG per viewport.
2
- import { createRequire } from 'node:module';
3
- import { pathToFileURL } from 'node:url';
4
- import { inflateSync } from 'node:zlib';
5
- import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
6
- import { join, resolve } from 'node:path';
7
- import { MOTION_STAGGER_CLASS, STAGGER_CHILD_CAP } from './lib/vocabulary.mjs';
8
-
9
- // The four viewports the ladder sweeps; width is the breakpoint, height is a fixed tall canvas.
10
- const VIEWPORTS = [375, 768, 1024, 1440];
11
- const VIEWPORT_HEIGHT = 900;
12
-
13
- // Axe rule ids the render rung gates on: the accessible-name family and the ARIA-validity family (int-17 / int-18).
14
- const AXE_ACCESSIBLE_NAME = ['button-name', 'select-name', 'image-alt', 'link-name'];
15
- const AXE_ARIA_VALIDITY = ['aria-allowed-attr', 'aria-required-attr', 'aria-required-children', 'aria-valid-attr-value'];
16
-
17
- // Common local dev-server ports probed when no --url is given.
18
- const DEV_PORTS = [5173, 4173, 3000, 8080];
19
-
20
- // Severity rank for the delivered (severity-ranked) list; auto-critical is never minted by machine render rules.
21
- const SEVERITY_RANK = { 'auto-critical': 0, critical: 1, major: 2, minor: 3 };
22
-
23
- // The exact install command --require prints when no headless browser is importable (contract 4).
24
- const BROWSER_INSTALL_CMD = 'npm install -D playwright && npx playwright install chromium';
25
-
26
- // The token trio cod-15 measures per declared theme context.
27
- const THEME_DRIFT_PROPS = ['--background', '--foreground', '--primary'];
28
-
29
- // ----------------------------------------------------------------------------
30
- // Pure helpers (exported for unit testing — no browser required)
31
- // ----------------------------------------------------------------------------
32
-
33
- // Decode an 8-bit non-interlaced RGB/RGBA PNG into { width, height, channels, data } using only node:zlib.
34
- export function decodePng(buffer) {
35
- const sig = [137, 80, 78, 71, 13, 10, 26, 10];
36
- for (let i = 0; i < 8; i++) if (buffer[i] !== sig[i]) throw new Error('not a PNG (bad signature)');
37
- let pos = 8;
38
- let width = 0, height = 0, bitDepth = 0, colorType = 0, interlace = 0;
39
- const idat = [];
40
- while (pos < buffer.length) {
41
- const len = buffer.readUInt32BE(pos); pos += 4;
42
- const type = buffer.toString('ascii', pos, pos + 4); pos += 4;
43
- const chunk = buffer.subarray(pos, pos + len); pos += len;
44
- pos += 4; // skip CRC
45
- if (type === 'IHDR') {
46
- width = chunk.readUInt32BE(0);
47
- height = chunk.readUInt32BE(4);
48
- bitDepth = chunk[8];
49
- colorType = chunk[9];
50
- interlace = chunk[12];
51
- } else if (type === 'IDAT') {
52
- idat.push(chunk);
53
- } else if (type === 'IEND') {
54
- break;
55
- }
56
- }
57
- if (bitDepth !== 8) throw new Error(`unsupported bit depth ${bitDepth} (need 8)`);
58
- if (interlace !== 0) throw new Error('interlaced PNG unsupported');
59
- const channels = colorType === 2 ? 3 : colorType === 6 ? 4 : 0;
60
- if (!channels) throw new Error(`unsupported color type ${colorType} (need 2 or 6)`);
61
- const raw = inflateSync(Buffer.concat(idat));
62
- const stride = width * channels;
63
- const out = new Uint8Array(height * stride);
64
- let rp = 0;
65
- for (let y = 0; y < height; y++) {
66
- const filter = raw[rp++];
67
- const rowStart = y * stride;
68
- for (let i = 0; i < stride; i++) {
69
- const x = raw[rp++];
70
- const a = i >= channels ? out[rowStart + i - channels] : 0;
71
- const b = y > 0 ? out[rowStart - stride + i] : 0;
72
- const c = y > 0 && i >= channels ? out[rowStart - stride + i - channels] : 0;
73
- let recon;
74
- switch (filter) {
75
- case 0: recon = x; break;
76
- case 1: recon = x + a; break;
77
- case 2: recon = x + b; break;
78
- case 3: recon = x + ((a + b) >> 1); break;
79
- case 4: recon = x + paethPredictor(a, b, c); break;
80
- default: throw new Error(`unknown PNG filter ${filter}`);
81
- }
82
- out[rowStart + i] = recon & 0xff;
83
- }
84
- }
85
- return { width, height, channels, data: out };
86
- }
87
-
88
- // The PNG Paeth predictor: pick the neighbour (left/up/upper-left) closest to the initial estimate.
89
- function paethPredictor(a, b, c) {
90
- const p = a + b - c;
91
- const pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
92
- if (pa <= pb && pa <= pc) return a;
93
- if (pb <= pc) return b;
94
- return c;
95
- }
96
-
97
- // inst-01: Sobel gradient-magnitude edge density — edge pixels over interior pixels (evidence, not a verdict).
98
- export function edgeDensity(img, threshold = 110) {
99
- const { width, height, channels, data } = img;
100
- if (width < 3 || height < 3) return 0;
101
- const gray = new Float64Array(width * height);
102
- for (let i = 0, p = 0; i < gray.length; i++, p += channels) {
103
- gray[i] = 0.299 * data[p] + 0.587 * data[p + 1] + 0.114 * data[p + 2];
104
- }
105
- let edges = 0;
106
- let interior = 0;
107
- for (let y = 1; y < height - 1; y++) {
108
- for (let x = 1; x < width - 1; x++) {
109
- const i = y * width + x;
110
- const tl = gray[i - width - 1], t = gray[i - width], tr = gray[i - width + 1];
111
- const l = gray[i - 1], r = gray[i + 1];
112
- const bl = gray[i + width - 1], bb = gray[i + width], br = gray[i + width + 1];
113
- const gx = (tr + 2 * r + br) - (tl + 2 * l + bl);
114
- const gy = (bl + 2 * bb + br) - (tl + 2 * t + tr);
115
- const mag = Math.sqrt(gx * gx + gy * gy);
116
- if (mag >= threshold) edges++;
117
- interior++;
118
- }
119
- }
120
- return interior === 0 ? 0 : edges / interior;
121
- }
122
-
123
- // inst-05: white-space ratio — fraction of pixels within a small Euclidean distance of the page background colour.
124
- export function whiteSpaceRatio(img, bg = [255, 255, 255], distance = 20) {
125
- const { width, height, channels, data } = img;
126
- const total = width * height;
127
- if (total === 0) return 0;
128
- const d2 = distance * distance;
129
- let near = 0;
130
- for (let p = 0; p < total * channels; p += channels) {
131
- const dr = data[p] - bg[0], dg = data[p + 1] - bg[1], db = data[p + 2] - bg[2];
132
- if (dr * dr + dg * dg + db * db <= d2) near++;
133
- }
134
- return near / total;
135
- }
136
-
137
- // inst-10: distinct-RGB count after quantizing each channel to its top 5 bits.
138
- export function distinctRgbCount(img) {
139
- const { width, height, channels, data } = img;
140
- const total = width * height;
141
- const seen = new Set();
142
- for (let p = 0; p < total * channels; p += channels) {
143
- const key = ((data[p] >> 3) << 10) | ((data[p + 1] >> 3) << 5) | (data[p + 2] >> 3);
144
- seen.add(key);
145
- }
146
- return seen.size;
147
- }
148
-
149
- // inst-09: Hasler-Susstrunk colourfulness of the surface.
150
- export function colorfulness(img) {
151
- const { width, height, channels, data } = img;
152
- const total = width * height;
153
- if (total === 0) return 0;
154
- let sumRg = 0, sumYb = 0, sumRg2 = 0, sumYb2 = 0;
155
- for (let p = 0; p < total * channels; p += channels) {
156
- const rg = data[p] - data[p + 1];
157
- const yb = 0.5 * (data[p] + data[p + 1]) - data[p + 2];
158
- sumRg += rg; sumYb += yb; sumRg2 += rg * rg; sumYb2 += yb * yb;
159
- }
160
- const meanRg = sumRg / total, meanYb = sumYb / total;
161
- const stdRg = Math.sqrt(Math.max(0, sumRg2 / total - meanRg * meanRg));
162
- const stdYb = Math.sqrt(Math.max(0, sumYb2 / total - meanYb * meanYb));
163
- return Math.sqrt(stdRg * stdRg + stdYb * stdYb) + 0.3 * Math.sqrt(meanRg * meanRg + meanYb * meanYb);
164
- }
165
-
166
- // Parse an "rgb(...)" / "rgba(...)" string into [r, g, b, a]; returns null if not parseable.
167
- export function parseRgb(str) {
168
- if (!str) return null;
169
- const m = String(str).match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:[,/\s]+([\d.]+))?/i);
170
- if (!m) return null;
171
- return [Math.round(+m[1]), Math.round(+m[2]), Math.round(+m[3]), m[4] === undefined ? 1 : +m[4]];
172
- }
173
-
174
- // sRGB relative luminance per WCAG 2.x.
175
- export function relativeLuminance([r, g, b]) {
176
- const lin = (v) => {
177
- const s = v / 255;
178
- return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
179
- };
180
- return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
181
- }
182
-
183
- // WCAG contrast ratio between two [r,g,b] colours.
184
- export function wcagContrast(rgb1, rgb2) {
185
- const l1 = relativeLuminance(rgb1), l2 = relativeLuminance(rgb2);
186
- const hi = Math.max(l1, l2), lo = Math.min(l1, l2);
187
- return (hi + 0.05) / (lo + 0.05);
188
- }
189
-
190
- // Extract colour-valued custom-property values from a personality.css text (skips var()-referencing values).
191
- export function extractCssColorValues(cssText) {
192
- const values = [];
193
- const re = /--[\w-]+\s*:\s*([^;{}]+);/g;
194
- let m;
195
- const colorish = /(#[0-9a-f]{3,8}\b)|\b(rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color)\s*\(/i;
196
- while ((m = re.exec(cssText)) !== null) {
197
- const value = m[1].trim();
198
- if (value.includes('var(')) continue;
199
- if (colorish.test(value)) values.push(value);
200
- }
201
- return [...new Set(values)];
202
- }
203
-
204
- // Normalize an [r,g,b] triple to the "r,g,b" set key.
205
- function rgbKey(rgb) {
206
- return `${rgb[0]},${rgb[1]},${rgb[2]}`;
207
- }
208
-
209
- // ----------------------------------------------------------------------------
210
- // CLI plumbing
211
- // ----------------------------------------------------------------------------
212
-
213
- // Parse argv into { project, url, require, screenshot }.
214
- function parseArgs(argv) {
215
- const out = { project: process.cwd(), url: null, require: false, screenshot: null };
216
- for (let i = 0; i < argv.length; i++) {
217
- const a = argv[i];
218
- if (a === '--project') out.project = resolve(argv[++i] ?? '.');
219
- else if (a.startsWith('--project=')) out.project = resolve(a.slice('--project='.length));
220
- else if (a === '--url') out.url = argv[++i] ?? null;
221
- else if (a.startsWith('--url=')) out.url = a.slice('--url='.length);
222
- else if (a === '--require') out.require = true;
223
- else if (a === '--screenshot') out.screenshot = resolve(argv[++i] ?? '.');
224
- else if (a.startsWith('--screenshot=')) out.screenshot = resolve(a.slice('--screenshot='.length));
225
- }
226
- return out;
227
- }
228
-
229
- // Report a skipped rung and return the process exit code. Never a one-line notice: without
230
- // --require the skip lands as a named finding in the fix-or-classify register (exit 0 — improve
231
- // keeps graceful degradation); with --require (create's ladder) the rung is a gate and fails closed
232
- // with the exact remedy (emission contract 4 — a create run cannot complete without having rendered).
233
- function reportSkip(requireMode, reason, remedy) {
234
- console.log(`[${requireMode ? 'critical' : 'major'}] render-rung Verification — render rung skipped: ${reason}`);
235
- console.log(` fix · ${remedy}`);
236
- console.log(' consequence · the render-only rules (cod-15, spc-16, mot-08 DOM, spc-10, typ-01, int-17/18, cod-12) did not run; the surface ships unrendered.');
237
- if (requireMode) {
238
- console.log(' --require: this rung is a gate on create runs — fix the remedy above and re-run.');
239
- return 1;
240
- }
241
- console.log(' classify this skip in the ship report or fix it — never ignore silently.');
242
- console.log('');
243
- return 0;
244
- }
245
-
246
- // cod-15 ground truth: the authored --background/--foreground/--primary byte forms per theme
247
- // context the resolver declares, read from the project-root design-tokens artifact. Returns
248
- // { light: { '--background': css, … }, dark?: … } or null when no artifact is readable.
249
- export function loadAuthoredThemeColors(projectDir) {
250
- const readJson = (name) => {
251
- try { return JSON.parse(readFileSync(join(resolve(projectDir), name), 'utf8')); } catch { return null; }
252
- };
253
- const base = readJson('design.tokens.json');
254
- const resolver = readJson('design.resolver.json');
255
- if (!base || !base.color || !resolver) return null;
256
- const themeModifier = (resolver.modifiers || []).find((m) => m && m.name === 'theme');
257
- const contexts = Object.keys((themeModifier && themeModifier.contexts) || { light: [] });
258
- const dark = readJson('design.tokens.dark.json');
259
- const out = {};
260
- for (const context of contexts) {
261
- // The dark overlay carries the full color group (rule 10), so a token-level merge is the overlay.
262
- const colorGroup = context === 'dark' && dark && dark.color ? { ...base.color, ...dark.color } : base.color;
263
- const cssOf = (role) => {
264
- let token = colorGroup[role];
265
- for (let hops = 0; token && hops < 10; hops++) {
266
- const ext = token.$extensions && token.$extensions['com.nurix.design'];
267
- if (ext && typeof ext.css === 'string') return ext.css;
268
- const v = token.$value;
269
- if (typeof v === 'string' && /^\{color\.[^}]+\}$/.test(v)) token = colorGroup[v.slice(7, -1)];
270
- else return null;
271
- }
272
- return null;
273
- };
274
- const values = {};
275
- for (const prop of THEME_DRIFT_PROPS) {
276
- const css = cssOf(prop.slice(2));
277
- if (css) values[prop] = css;
278
- }
279
- if (Object.keys(values).length) out[context] = values;
280
- }
281
- return Object.keys(out).length ? out : null;
282
- }
283
-
284
- // Try to import playwright, playwright-core, then puppeteer from the target project; return a driver adapter or null.
285
- async function loadBrowserDriver(projectDir) {
286
- const require = createRequire(join(resolve(projectDir), 'package.json'));
287
- for (const name of ['playwright', 'playwright-core', 'puppeteer']) {
288
- let resolved;
289
- try { resolved = require.resolve(name); } catch { continue; }
290
- let mod;
291
- try { mod = await import(pathToFileURL(resolved).href); } catch { continue; }
292
- const ns = mod && mod.default && (mod.default.chromium || typeof mod.default.launch === 'function') ? mod.default : mod;
293
- if (ns.chromium) return { kind: 'playwright', name, launcher: ns.chromium };
294
- if (typeof ns.launch === 'function') return { kind: 'puppeteer', name, launcher: ns };
295
- }
296
- return null;
297
- }
298
-
299
- // Install a self-contained init script (runs before page scripts on every navigation) across the playwright / puppeteer split.
300
- async function addInitScript(driver, page, fn) {
301
- if (driver.kind === "playwright") await page.addInitScript(fn);
302
- else await page.evaluateOnNewDocument(fn);
303
- }
304
-
305
- // Set the page viewport across the playwright / puppeteer API split.
306
- async function setViewport(driver, page, width) {
307
- if (driver.kind === 'playwright') await page.setViewportSize({ width, height: VIEWPORT_HEIGHT });
308
- else await page.setViewport({ width, height: VIEWPORT_HEIGHT });
309
- }
310
-
311
- // Probe common localhost dev ports; return the first reachable URL or null.
312
- async function detectDevServer() {
313
- for (const port of DEV_PORTS) {
314
- const url = `http://localhost:${port}`;
315
- const controller = new AbortController();
316
- const timer = setTimeout(() => controller.abort(), 400);
317
- try {
318
- await fetch(url, { signal: controller.signal, redirect: 'manual' });
319
- clearTimeout(timer);
320
- return url;
321
- } catch {
322
- clearTimeout(timer);
323
- }
324
- }
325
- return null;
326
- }
327
-
328
- // ----------------------------------------------------------------------------
329
- // In-page collectors (serialized into the browser; must be self-contained)
330
- // ----------------------------------------------------------------------------
331
-
332
- // Return true if the page scrolls horizontally at the current viewport.
333
- function inPageOverflow() {
334
- const el = document.scrollingElement || document.documentElement;
335
- return { scrollWidth: el.scrollWidth, innerWidth: window.innerWidth, overflow: el.scrollWidth > window.innerWidth };
336
- }
337
-
338
- // Walk visible text nodes, compute effective contrast against the nearest opaque ancestor background, return offenders.
339
- function inPageTextContrast(cap) {
340
- const parseRgb = (s) => {
341
- const m = String(s).match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:[,/\s]+([\d.]+))?/i);
342
- return m ? [Math.round(+m[1]), Math.round(+m[2]), Math.round(+m[3]), m[4] === undefined ? 1 : +m[4]] : null;
343
- };
344
- const lum = ([r, g, b]) => {
345
- const f = (v) => { const s = v / 255; return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4); };
346
- return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
347
- };
348
- const ratio = (a, b) => { const l1 = lum(a), l2 = lum(b); const hi = Math.max(l1, l2), lo = Math.min(l1, l2); return (hi + 0.05) / (lo + 0.05); };
349
- const path = (el) => {
350
- let s = el.tagName.toLowerCase();
351
- if (el.id) s += '#' + el.id;
352
- else if (el.classList.length) s += '.' + el.classList[0];
353
- return s;
354
- };
355
- const effectiveBg = (el) => {
356
- let node = el;
357
- while (node && node.nodeType === 1) {
358
- const bg = parseRgb(getComputedStyle(node).backgroundColor);
359
- if (bg && bg[3] > 0) return [bg[0], bg[1], bg[2]];
360
- node = node.parentElement;
361
- }
362
- return [255, 255, 255];
363
- };
364
- const offenders = [];
365
- const seen = new Set();
366
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
367
- let n;
368
- while ((n = walker.nextNode())) {
369
- if (!n.nodeValue || !n.nodeValue.trim()) continue;
370
- const el = n.parentElement;
371
- if (!el) continue;
372
- const cs = getComputedStyle(el);
373
- if (cs.visibility === 'hidden' || cs.display === 'none' || +cs.opacity === 0) continue;
374
- const rect = el.getBoundingClientRect();
375
- if (rect.width < 1 || rect.height < 1) continue;
376
- const fg = parseRgb(cs.color);
377
- if (!fg || fg[3] === 0) continue;
378
- const bg = effectiveBg(el);
379
- const size = parseFloat(cs.fontSize) || 16;
380
- const weight = parseInt(cs.fontWeight, 10) || 400;
381
- const large = size >= 24 || (size >= 18.66 && weight >= 700);
382
- const threshold = large ? 3 : 4.5;
383
- const r = ratio([fg[0], fg[1], fg[2]], bg);
384
- if (r < threshold) {
385
- const key = path(el) + '|' + cs.color;
386
- if (seen.has(key)) continue;
387
- seen.add(key);
388
- offenders.push({ selector: path(el), color: cs.color, bg: `rgb(${bg.join(', ')})`, ratio: +r.toFixed(2), threshold, large });
389
- if (offenders.length >= cap) break;
390
- }
391
- }
392
- return offenders;
393
- }
394
-
395
- // Return the geometry of every visible interactive element for target-size analysis.
396
- function inPageTargets() {
397
- const sel = 'a[href], button, input:not([type=hidden]), select, textarea, [role=button], [role=link], [role=menuitem], [role=tab], [role=checkbox], [role=switch], [tabindex]:not([tabindex="-1"])';
398
- const path = (el) => {
399
- let s = el.tagName.toLowerCase();
400
- if (el.id) s += '#' + el.id;
401
- else if (el.classList.length) s += '.' + el.classList[0];
402
- return s;
403
- };
404
- const out = [];
405
- for (const el of document.querySelectorAll(sel)) {
406
- const cs = getComputedStyle(el);
407
- if (cs.visibility === 'hidden' || cs.display === 'none' || +cs.opacity === 0) continue;
408
- if (el.disabled) continue;
409
- const r = el.getBoundingClientRect();
410
- if (r.width < 1 || r.height < 1) continue;
411
- out.push({ selector: path(el), x: r.x, y: r.y, w: r.width, h: r.height });
412
- }
413
- return out;
414
- }
415
-
416
- // Resolve a list of CSS colour strings to canonical "rgb(...)" via a probe element.
417
- function inPageResolveColors(colorStrings) {
418
- const probe = document.createElement('span');
419
- probe.style.display = 'none';
420
- document.body.appendChild(probe);
421
- const out = [];
422
- for (const s of colorStrings) {
423
- probe.style.color = '';
424
- probe.style.color = s;
425
- out.push(getComputedStyle(probe).color);
426
- }
427
- probe.remove();
428
- return out;
429
- }
430
-
431
- // Sample computed foreground / background / border colours across visible elements for drift analysis.
432
- function inPageComputedColors(cap) {
433
- const path = (el) => {
434
- let s = el.tagName.toLowerCase();
435
- if (el.id) s += '#' + el.id;
436
- else if (el.classList.length) s += '.' + el.classList[0];
437
- return s;
438
- };
439
- const out = [];
440
- const all = document.querySelectorAll('body *');
441
- const step = Math.max(1, Math.floor(all.length / cap));
442
- for (let i = 0; i < all.length; i += step) {
443
- const el = all[i];
444
- const cs = getComputedStyle(el);
445
- if (cs.visibility === 'hidden' || cs.display === 'none') continue;
446
- const rect = el.getBoundingClientRect();
447
- if (rect.width < 1 || rect.height < 1) continue;
448
- const entry = { selector: path(el), colors: [] };
449
- if (el.textContent && el.textContent.trim()) entry.colors.push(['color', cs.color]);
450
- if (cs.backgroundColor && cs.backgroundColor !== 'rgba(0, 0, 0, 0)' && cs.backgroundColor !== 'transparent') entry.colors.push(['background', cs.backgroundColor]);
451
- for (const side of ['Top', 'Right', 'Bottom', 'Left']) {
452
- if (parseFloat(cs['border' + side + 'Width']) > 0) entry.colors.push(['border', cs['border' + side + 'Color']]);
453
- }
454
- if (entry.colors.length) out.push(entry);
455
- }
456
- return out;
457
- }
458
-
459
- // Install PerformanceObservers (cod-12) before any page script runs, accumulating CLS and the latest LCP on window.
460
- function inPagePerfInit() {
461
- window.__perf = { cls: 0, lcp: 0 };
462
- try {
463
- new PerformanceObserver((list) => {
464
- for (const e of list.getEntries()) if (!e.hadRecentInput) window.__perf.cls += e.value;
465
- }).observe({ type: "layout-shift", buffered: true });
466
- } catch (e) { /* layout-shift not supported in this build */ }
467
- try {
468
- new PerformanceObserver((list) => {
469
- const entries = list.getEntries();
470
- const last = entries[entries.length - 1];
471
- if (last) window.__perf.lcp = last.renderTime || last.loadTime || last.startTime || 0;
472
- }).observe({ type: "largest-contentful-paint", buffered: true });
473
- } catch (e) { /* largest-contentful-paint not supported in this build */ }
474
- }
475
-
476
- // Read the accumulated perf metrics installed by inPagePerfInit.
477
- function inPagePerfRead() {
478
- return window.__perf || { cls: 0, lcp: 0 };
479
- }
480
-
481
- // cod-15: flip data-theme through each declared context and read the rendered token trio off the
482
- // root element, restoring the boot attribute afterwards.
483
- function inPageThemeTokens(payload) {
484
- const html = document.documentElement;
485
- const previous = html.getAttribute('data-theme');
486
- const out = {};
487
- for (const context of payload.contexts) {
488
- html.setAttribute('data-theme', context);
489
- const cs = getComputedStyle(html);
490
- const read = {};
491
- for (const prop of payload.props) read[prop] = cs.getPropertyValue(prop).trim();
492
- out[context] = read;
493
- }
494
- if (previous === null) html.removeAttribute('data-theme');
495
- else html.setAttribute('data-theme', previous);
496
- return out;
497
- }
498
-
499
- // spc-16: fixed-width table/grid cells whose rendered text exceeds the cell (scrollWidth >
500
- // clientWidth) with no truncate or scroll affordance on the cell or a descendant wrapper.
501
- function inPageCellTextFit(cap) {
502
- const path = (el) => {
503
- let s = el.tagName.toLowerCase();
504
- if (el.id) s += '#' + el.id;
505
- else if (el.classList.length) s += '.' + el.classList[0];
506
- return s;
507
- };
508
- const offenders = [];
509
- const cells = document.querySelectorAll('td, th, [role="cell"], [role="gridcell"], [role="columnheader"]');
510
- for (const cell of cells) {
511
- const cs = getComputedStyle(cell);
512
- if (cs.display === 'none' || cs.visibility === 'hidden') continue;
513
- if (cell.scrollWidth <= cell.clientWidth + 1) continue; // content fits — an auto-layout cell grows instead of overflowing
514
- let excused = false;
515
- for (const node of [cell, ...cell.querySelectorAll('*')]) {
516
- const ns = getComputedStyle(node);
517
- if (/(auto|scroll)/.test(ns.overflowX)) { excused = true; break; }
518
- if (ns.textOverflow === 'ellipsis' && /(hidden|clip)/.test(ns.overflowX)) { excused = true; break; }
519
- if (ns.webkitLineClamp && ns.webkitLineClamp !== 'none') { excused = true; break; }
520
- }
521
- if (excused) continue;
522
- offenders.push({ selector: path(cell), scrollWidth: Math.round(cell.scrollWidth), clientWidth: Math.round(cell.clientWidth) });
523
- if (offenders.length >= cap) break;
524
- }
525
- return offenders;
526
- }
527
-
528
- // mot-08 (DOM half): every rendered stagger container's child count and its total sequence length,
529
- // the latter as the largest resolved child animation-delay.
530
- function inPageStaggerAudit(payload) {
531
- const path = (el) => {
532
- let s = el.tagName.toLowerCase();
533
- if (el.id) s += '#' + el.id;
534
- else if (el.classList.length) s += '.' + el.classList[0];
535
- return s;
536
- };
537
- const out = [];
538
- for (const container of document.querySelectorAll('.' + payload.className)) {
539
- let maxDelayMs = 0;
540
- for (const child of container.children) {
541
- for (const raw of getComputedStyle(child).animationDelay.split(',')) {
542
- const t = raw.trim();
543
- const ms = t.endsWith('ms') ? parseFloat(t) : parseFloat(t) * 1000;
544
- if (!Number.isNaN(ms)) maxDelayMs = Math.max(maxDelayMs, ms);
545
- }
546
- }
547
- out.push({ selector: path(container), childCount: container.children.length, maxDelayMs: Math.round(maxDelayMs) });
548
- }
549
- return out;
550
- }
551
-
552
- // Read the page's effective background colour for the white-space instrument.
553
- function inPageBackground() {
554
- const parse = (s) => {
555
- const m = String(s).match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:[,/\s]+([\d.]+))?/i);
556
- return m ? [+m[1], +m[2], +m[3], m[4] === undefined ? 1 : +m[4]] : null;
557
- };
558
- for (const el of [document.body, document.documentElement]) {
559
- const bg = parse(getComputedStyle(el).backgroundColor);
560
- if (bg && bg[3] > 0) return [Math.round(bg[0]), Math.round(bg[1]), Math.round(bg[2])];
561
- }
562
- return [255, 255, 255];
563
- }
564
-
565
- // ----------------------------------------------------------------------------
566
- // Finding accumulation + output
567
- // ----------------------------------------------------------------------------
568
-
569
- // Register a machine-rule finding, deduped by anchor+evidence, tracking which viewports saw it.
570
- function addFinding(findings, viewport, finding) {
571
- const key = `${finding.anchor}|${finding.evidenceKey}`;
572
- const existing = findings.get(key);
573
- if (existing) existing.viewports.add(viewport);
574
- else findings.set(key, { ...finding, viewports: new Set([viewport]) });
575
- }
576
-
577
- // Print one finding in the check engine's fix-or-classify format.
578
- function printFinding(f) {
579
- const vp = [...f.viewports].sort((a, b) => a - b).join(', ');
580
- console.log(`[${f.severity}] ${f.anchor} ${f.dimension} — ${f.statement}`);
581
- console.log(` viewport ${vp} · ${f.where}`);
582
- console.log(` before -> after · ${f.before} -> ${f.after}`);
583
- console.log(` consequence · ${f.consequence}`);
584
- console.log(' fix it now, or classify why the rule does not apply and record that classification in docs/memory/design-decay.md — never ignore silently.');
585
- console.log('');
586
- }
587
-
588
- // ----------------------------------------------------------------------------
589
- // Main
590
- // ----------------------------------------------------------------------------
591
-
592
- // Run the render rung: load a browser, sweep viewports for machine findings, compute pixel instruments.
593
- async function main() {
594
- const { project, url: urlArg, require: requireMode, screenshot: screenshotDir } = parseArgs(process.argv.slice(2));
595
-
596
- const driver = await loadBrowserDriver(project);
597
- if (!driver) {
598
- return reportSkip(requireMode, 'no headless browser importable from this project (tried playwright, playwright-core, puppeteer)', `install one: ${BROWSER_INSTALL_CMD}`);
599
- }
600
-
601
- let url = urlArg;
602
- if (!url) {
603
- url = await detectDevServer();
604
- if (url) console.log(`render rung: no --url given; using detected dev server ${url}`);
605
- }
606
- if (!url) {
607
- return reportSkip(requireMode, 'no --url given and no dev server detected on common ports', 'start the dev server (npm run dev) or pass --url <dev-server-url>');
608
- }
609
-
610
- // Resolve axe-core from the project if present; gate only the accessible-name + ARIA-validity families.
611
- let axeSource = null;
612
- try {
613
- const require = createRequire(join(resolve(project), 'package.json'));
614
- axeSource = readFileSync(require.resolve('axe-core'), 'utf8');
615
- } catch {
616
- console.log('render rung: axe-core not installed in this project; the accessible-name and ARIA-validity checks (int-17/int-18) are skipped.');
617
- }
618
-
619
- // Parse the project's personality.css token colours for the drift check (cod-02).
620
- let tokenColorStrings = null;
621
- const cssPath = join(resolve(project), 'src', 'styles', 'personality.css');
622
- if (existsSync(cssPath)) {
623
- tokenColorStrings = extractCssColorValues(readFileSync(cssPath, 'utf8'));
624
- } else {
625
- console.log('render rung: no src/styles/personality.css found; the rendered token-drift check (cod-02) is skipped.');
626
- }
627
-
628
- // Load the authored per-theme trio for cod-15 from the project-root design-tokens artifact.
629
- const authoredThemes = loadAuthoredThemeColors(project);
630
- if (!authoredThemes) {
631
- console.log('render rung: no readable design-tokens artifact at the project root; the per-theme rendered token drift check (cod-15) is skipped.');
632
- }
633
- let themeDriftChecked = false;
634
-
635
- const findings = new Map();
636
- const instruments = {};
637
- const axeRules = [...(axeSource ? AXE_ACCESSIBLE_NAME.concat(AXE_ARIA_VALIDITY) : [])];
638
- let axeSeen = new Set();
639
-
640
- const browser = await driver.launcher.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
641
- try {
642
- const page = await browser.newPage();
643
- // cod-12: register the perf observers before any navigation so LCP/CLS are captured from first paint.
644
- try { await addInitScript(driver, page, inPagePerfInit); } catch (err) { /* init-script API unavailable; cod-12 perf skipped */ }
645
- for (const width of VIEWPORTS) {
646
- await setViewport(driver, page, width);
647
- try {
648
- await page.goto(url, { waitUntil: 'load', timeout: 30000 });
649
- } catch (err) {
650
- await browser.close();
651
- return reportSkip(requireMode, `could not load ${url} at ${width}px (${err.message})`, 'start the dev server (npm run dev) or pass --url <dev-server-url>');
652
- }
653
- await new Promise((r) => setTimeout(r, 150));
654
-
655
- // (1) spc-10 — horizontal overflow.
656
- const ov = await page.evaluate(inPageOverflow);
657
- if (ov.overflow) {
658
- addFinding(findings, width, {
659
- anchor: 'spc-10', dimension: 'P3 Spatial', statement: 'horizontal scroll at this viewport means the design is broken',
660
- evidenceKey: 'overflow', where: `document scrollWidth ${ov.scrollWidth}px > innerWidth ${ov.innerWidth}px`,
661
- before: `${ov.scrollWidth}px content`, after: `<= ${ov.innerWidth}px (no horizontal scroll)`,
662
- consequence: 'the operator scrolls sideways to reach controls; layout reads as broken', severity: 'critical',
663
- });
664
- }
665
-
666
- // (2) typ-01 — effective text contrast.
667
- const contrast = await page.evaluate(inPageTextContrast, 60);
668
- for (const o of contrast) {
669
- addFinding(findings, width, {
670
- anchor: 'typ-01', dimension: 'P1 Typography', statement: 'text contrast below the readable floor',
671
- evidenceKey: `${o.selector}|${o.color}`, where: `${o.selector} · ${o.color} on ${o.bg}`,
672
- before: `${o.ratio}:1`, after: `>= ${o.threshold}:1 (${o.large ? 'large' : 'body'})`,
673
- consequence: 'the operator cannot read the text; the surface reads as broken', severity: 'critical',
674
- });
675
- }
676
-
677
- // (3) spc-03 — target geometry with the int-11 >=8px spacing exception.
678
- const targets = await page.evaluate(inPageTargets);
679
- for (const t of targets) {
680
- if (t.w >= 24 && t.h >= 24) continue;
681
- if (isSpacingExcepted(t, targets)) continue;
682
- addFinding(findings, width, {
683
- anchor: 'spc-03', dimension: 'P3 Spatial', statement: 'interactive target below the 24x24 CSS-px minimum (WCAG 2.5.8) and not covered by the >=8px spacing exception',
684
- evidenceKey: t.selector, where: `${t.selector} · ${Math.round(t.w)}x${Math.round(t.h)}px`,
685
- before: `${Math.round(t.w)}x${Math.round(t.h)}px`, after: '>= 24x24px, or >= 8px clearance from every neighbour',
686
- consequence: 'the target is hard to hit; mis-clicks cost the operator time and trust', severity: 'major',
687
- });
688
- }
689
-
690
- // (4) cod-02 — rendered token drift.
691
- if (tokenColorStrings && tokenColorStrings.length) {
692
- const resolved = await page.evaluate(inPageResolveColors, tokenColorStrings);
693
- const allowed = new Set();
694
- for (const s of resolved) { const rgb = parseRgb(s); if (rgb) allowed.add(rgbKey(rgb)); }
695
- const sampled = await page.evaluate(inPageComputedColors, 400);
696
- for (const el of sampled) {
697
- for (const [prop, colStr] of el.colors) {
698
- const rgb = parseRgb(colStr);
699
- if (!rgb || rgb[3] === 0) continue;
700
- if (allowed.has(rgbKey(rgb))) continue;
701
- addFinding(findings, width, {
702
- anchor: 'cod-02', dimension: 'S3 Design Tokens', statement: 'rendered colour resolves outside the personality.css token vocabulary (drift)',
703
- evidenceKey: `${prop}|${rgbKey(rgb)}`, where: `${el.selector} · ${prop} ${colStr} not among the emitted tokens`,
704
- before: colStr, after: 'a value from the emitted token surface',
705
- consequence: 'an off-manifest colour erodes token discipline; the look drifts from the resolved personality', severity: 'minor',
706
- });
707
- }
708
- }
709
- }
710
-
711
- // (5) cod-15 — per-theme rendered token drift (viewport-independent; measured once, with
712
- // data-theme flipped through each context the resolver declares).
713
- if (authoredThemes && !themeDriftChecked) {
714
- themeDriftChecked = true;
715
- try {
716
- const rendered = await page.evaluate(inPageThemeTokens, { contexts: Object.keys(authoredThemes), props: THEME_DRIFT_PROPS });
717
- const normalize = (s) => String(s || '').replace(/\s+/g, ' ').trim().toLowerCase();
718
- for (const [context, roles] of Object.entries(authoredThemes)) {
719
- for (const [prop, authored] of Object.entries(roles)) {
720
- const got = (rendered[context] && rendered[context][prop]) || '';
721
- if (normalize(got) === normalize(authored)) continue;
722
- addFinding(findings, width, {
723
- anchor: 'cod-15', dimension: 'S3 Design Tokens', statement: 'rendered token drifts from the authored value in this theme context',
724
- evidenceKey: `${context}|${prop}`, where: `data-theme="${context}" · ${prop} renders ${JSON.stringify(got || '(empty)')}`,
725
- before: got || '(empty)', after: `${authored} (the ${context} context's authored value)`,
726
- consequence: "the theme context is cascade-dead — the library's later :root block wins and the surface silently reverts to shadcn gray", severity: 'critical',
727
- });
728
- }
729
- }
730
- } catch (err) {
731
- console.log(`render rung: the per-theme token drift check (cod-15) failed to run (${err.message}); continuing.`);
732
- }
733
- }
734
-
735
- // (6) spc-16 — fixed-width cell text fit: rendered text exceeds the cell with no truncate or
736
- // scroll affordance (document-level overflow cannot see intra-row column paint-over).
737
- const cellOffenders = await page.evaluate(inPageCellTextFit, 40);
738
- for (const cell of cellOffenders) {
739
- addFinding(findings, width, {
740
- anchor: 'spc-16', dimension: 'P3 Spatial', statement: 'rendered text exceeds its fixed-width cell with no truncate or scroll affordance',
741
- evidenceKey: cell.selector, where: `${cell.selector} · content ${cell.scrollWidth}px > cell ${cell.clientWidth}px`,
742
- before: `${cell.scrollWidth}px of content in a ${cell.clientWidth}px cell`, after: 'text fits, truncates (truncate), or scrolls (overflow-x-auto)',
743
- consequence: 'cell text paints over or clips against the neighbouring column; the row reads as broken data', severity: 'major',
744
- });
745
- }
746
-
747
- // (7) mot-08 (DOM half) — rendered stagger containers stay within the child cap and the
748
- // 1s total-sequence budget (the source shell owns the per-item band and the budget stamp).
749
- const staggerContainers = await page.evaluate(inPageStaggerAudit, { className: MOTION_STAGGER_CLASS });
750
- for (const s of staggerContainers) {
751
- if (s.childCount > STAGGER_CHILD_CAP) {
752
- addFinding(findings, width, {
753
- anchor: 'mot-08', dimension: 'B2 Motion', statement: `arrival cascade exceeds the ${STAGGER_CHILD_CAP}-child cap`,
754
- evidenceKey: `count|${s.selector}`, where: `${s.selector} · ${s.childCount} children in a ${MOTION_STAGGER_CLASS} container`,
755
- before: `${s.childCount} staggered children`, after: `<= ${STAGGER_CHILD_CAP} (children past the cap arrive with the last delayed one)`,
756
- consequence: 'a long cascade turns arrival feedback into a wait; the operator reads it as slowness', severity: 'major',
757
- });
758
- }
759
- if (s.maxDelayMs >= 1000) {
760
- addFinding(findings, width, {
761
- anchor: 'mot-08', dimension: 'B2 Motion', statement: 'total stagger sequence at or over the 1s budget',
762
- evidenceKey: `sequence|${s.selector}`, where: `${s.selector} · last child arrives at ${s.maxDelayMs}ms`,
763
- before: `${s.maxDelayMs}ms total sequence`, after: '< 1000ms (per-item delay x child count under the budget)',
764
- consequence: 'content the operator came for is still arriving after a second; the cascade reads as lag, not polish', severity: 'major',
765
- });
766
- }
767
- }
768
-
769
- // (8) int-17 / int-18 — axe accessible-name + ARIA-validity families.
770
- if (axeSource) {
771
- try {
772
- await page.addScriptTag({ content: axeSource });
773
- const violations = await page.evaluate(async (rules) => {
774
- const res = await window.axe.run(document, { runOnly: { type: 'rule', values: rules }, resultTypes: ['violations'] });
775
- return res.violations.map((v) => ({ id: v.id, impact: v.impact, count: v.nodes.length, target: v.nodes[0] ? String(v.nodes[0].target[0]) : '' }));
776
- }, axeRules);
777
- for (const v of violations) {
778
- if (axeSeen.has(v.id)) continue;
779
- axeSeen.add(v.id);
780
- const isName = AXE_ACCESSIBLE_NAME.includes(v.id);
781
- addFinding(findings, width, {
782
- anchor: isName ? 'int-17' : 'int-18', dimension: 'S1 Semantic Markup',
783
- statement: isName ? `axe ${v.id}: element lacks a discernible accessible name` : `axe ${v.id}: invalid ARIA usage`,
784
- evidenceKey: v.id, where: `${v.count} node(s), e.g. ${v.target || '(unnamed)'}`,
785
- before: `axe ${v.id} violation (${v.impact || 'serious'})`, after: 'zero violations in this rule',
786
- consequence: isName ? 'assistive tech announces nothing actionable; keyboard and screen-reader operators are blocked' : 'invalid ARIA misleads assistive tech about state and role', severity: 'critical',
787
- });
788
- }
789
- } catch (err) {
790
- console.log(`render rung: axe run failed at ${width}px (${err.message}); continuing without the accessible-name/ARIA checks.`);
791
- axeSource = null;
792
- }
793
- }
794
-
795
- // (9) cod-12 — runtime perf budgets measurable in a passive headless load: LCP and CLS.
796
- // INP and frame time require real interaction and belong to the interactive rung (3), not this one.
797
- try {
798
- const perf = await page.evaluate(inPagePerfRead);
799
- if (perf && typeof perf.lcp === 'number' && perf.lcp > 2500) {
800
- addFinding(findings, width, {
801
- anchor: 'cod-12', dimension: 'S4 Performance', statement: 'largest-contentful-paint over the 2.5s budget',
802
- evidenceKey: 'lcp', where: `LCP ${Math.round(perf.lcp)}ms at load`,
803
- before: `${Math.round(perf.lcp)}ms`, after: '<= 2500ms',
804
- consequence: 'the operator waits on first meaningful paint; the surface feels slow to arrive', severity: 'major',
805
- });
806
- }
807
- if (perf && typeof perf.cls === 'number' && perf.cls > 0.1) {
808
- addFinding(findings, width, {
809
- anchor: 'cod-12', dimension: 'S4 Performance', statement: 'cumulative layout shift over the 0.1 budget',
810
- evidenceKey: 'cls', where: `CLS ${perf.cls.toFixed(3)} across load`,
811
- before: perf.cls.toFixed(3), after: '<= 0.1',
812
- consequence: 'content jumps as it loads; the operator mis-clicks and loses their place', severity: 'major',
813
- });
814
- }
815
- } catch (err) { /* PerformanceObserver entries unsupported; cod-12 skipped, rungs 0-1 still hold */ }
816
-
817
- // Instruments — pixel evidence, never a verdict.
818
- try {
819
- const shot = Buffer.from(await page.screenshot({ type: 'png' }));
820
- if (screenshotDir) {
821
- mkdirSync(screenshotDir, { recursive: true });
822
- writeFileSync(join(screenshotDir, `${width}.png`), shot);
823
- }
824
- const bg = await page.evaluate(inPageBackground);
825
- const img = decodePng(shot);
826
- instruments[width] = {
827
- edgeDensity: edgeDensity(img),
828
- whiteSpace: whiteSpaceRatio(img, bg),
829
- colorfulness: colorfulness(img),
830
- distinctRgb: distinctRgbCount(img),
831
- };
832
- } catch (err) {
833
- instruments[width] = { error: err.message };
834
- }
835
- }
836
- } finally {
837
- await browser.close();
838
- }
839
-
840
- return report(findings, instruments);
841
- }
842
-
843
- // Return true when an undersized target is covered by the int-11 spacing exception (>=8px clearance from every neighbour).
844
- export function isSpacingExcepted(t, all) {
845
- const GAP = 8;
846
- for (const o of all) {
847
- if (o === t) continue;
848
- const overlapX = t.x < o.x + o.w && o.x < t.x + t.w;
849
- const overlapY = t.y < o.y + o.h && o.y < t.y + t.h;
850
- if (overlapX && overlapY) return false; // overlapping targets — not excepted
851
- const gapX = Math.max(o.x - (t.x + t.w), t.x - (o.x + o.w));
852
- const gapY = Math.max(o.y - (t.y + t.h), t.y - (o.y + o.h));
853
- if (overlapY && gapX < GAP) return false; // same row band, too close horizontally
854
- if (overlapX && gapY < GAP) return false; // same column band, too close vertically
855
- }
856
- return true;
857
- }
858
-
859
- // Print the severity-ranked findings and the instruments block; return the process exit code.
860
- function report(findings, instruments) {
861
- const list = [...findings.values()].sort((a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]);
862
- if (list.length === 0) {
863
- console.log('render rung: no machine-rule findings across 375/768/1024/1440.');
864
- } else {
865
- console.log(`render rung: ${list.length} machine-rule finding(s) — fix or classify each before ship.`);
866
- console.log('');
867
- for (const f of list) printFinding(f);
868
- }
869
-
870
- console.log('instruments (evidence, not verdicts — dense is not bad)');
871
- console.log(' these numbers are evidence for a judged finding, never a verdict; a high reading is a prompt to look, not proof of a problem.');
872
- for (const width of VIEWPORTS) {
873
- const m = instruments[width];
874
- if (!m) { console.log(` ${width}px: not computed`); continue; }
875
- if (m.error) { console.log(` ${width}px: not computed (${m.error})`); continue; }
876
- console.log(` ${width}px: inst-01 edge-density ${m.edgeDensity.toFixed(3)} · inst-05 white-space ${m.whiteSpace.toFixed(3)} · inst-09 colorfulness ${m.colorfulness.toFixed(1)} · inst-10 distinct-rgb ${m.distinctRgb}`);
877
- }
878
- console.log(' not computed: inst-02 feature-congestion, inst-03 subband-entropy, inst-04 grid-quality, inst-06 symmetry, inst-07 figure-ground, inst-08 contour-congestion, inst-11 klm-operator-cost');
879
- console.log(' cod-12: LCP and CLS measured on the passive load; INP and 16ms frame time need real interaction — the interactive rung (3) covers those.');
880
-
881
- return list.length > 0 ? 1 : 0;
882
- }
883
-
884
- const isMain = process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url;
885
- if (isMain) {
886
- main().then((code) => process.exit(code)).catch((err) => {
887
- console.error(`render rung: unexpected error — ${err.stack || err.message}`);
888
- process.exit(1);
889
- });
890
- }