@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.
- package/README.md +6 -4
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/agents/maintenance.md +1 -1
- package/data/harnesses/code/harness.json +1 -1
- package/data/harnesses/code/rules/frontend.md +5 -5
- package/data/harnesses/code/skills/design/SKILL.md +70 -50
- package/data/harnesses/code/skills/design/components/charts.md +76 -0
- package/data/harnesses/code/skills/design/components/collections.md +132 -0
- package/data/harnesses/code/skills/design/components/feedback.md +47 -0
- package/data/harnesses/code/skills/design/components/hooks.md +35 -0
- package/data/harnesses/code/skills/design/components/index.md +143 -0
- package/data/harnesses/code/skills/design/components/inputs.md +327 -0
- package/data/harnesses/code/skills/design/components/layout.md +52 -0
- package/data/harnesses/code/skills/design/components/media.md +66 -0
- package/data/harnesses/code/skills/design/components/motion.md +82 -0
- package/data/harnesses/code/skills/design/components/navigation.md +29 -0
- package/data/harnesses/code/skills/design/components/overlays.md +72 -0
- package/data/harnesses/code/skills/design/components/setup.md +40 -0
- package/data/harnesses/code/skills/design/components/typography.md +46 -0
- package/data/harnesses/code/skills/design/components/utils.md +37 -0
- package/data/harnesses/code/skills/design/data/blueprints/browse.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/creation-flow.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/dashboard.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/grid.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/queue.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/record-detail.md +38 -0
- package/data/harnesses/code/skills/design/data/blueprints/settings.md +38 -0
- package/data/harnesses/code/skills/design/data/chart-styling/Readme.md +33 -0
- package/data/harnesses/code/skills/design/data/chart-styling/carbon-accessible-alert-semantic.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/carbon-monochrome-sequential-trend.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/datadog-classic-six-aligned.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/datawrapper-tiered-gray-hierarchy.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/econ-red-rule-horizontal.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/financial-diverging-threshold-overlay.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/ft-vocabulary-ranked-muted.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/grafana-dark-threshold-bands.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/material-rounded-explicit-label.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/nyt-ghost-grid-direct-label.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/plot-currentcolor-inherit.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/saas-gradient-glow-area.md +19 -0
- package/data/harnesses/code/skills/design/data/chart-styling/shadcn-composable-css-vars.md +19 -0
- package/data/harnesses/code/skills/design/data/charts/census.csv +58 -0
- package/data/harnesses/code/skills/design/data/checklists/browse.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/creation-flow.md +20 -0
- package/data/harnesses/code/skills/design/data/checklists/dashboard.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/grid.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/queue.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/record-detail.md +21 -0
- package/data/harnesses/code/skills/design/data/checklists/settings.md +21 -0
- package/data/harnesses/code/skills/design/data/chrome/board-column.md +68 -0
- package/data/harnesses/code/skills/design/data/chrome/data-table.md +63 -0
- package/data/harnesses/code/skills/design/data/chrome/side-panel.md +60 -0
- package/data/harnesses/code/skills/design/data/chrome/sidebar.md +78 -0
- package/data/harnesses/code/skills/design/data/chrome/stat-cards.md +62 -0
- package/data/harnesses/code/skills/design/data/chrome/topbar.md +65 -0
- package/data/harnesses/code/skills/design/data/color/accent-presets.md +90 -0
- package/data/harnesses/code/skills/design/data/color/aesop-earth-mute.md +91 -0
- package/data/harnesses/code/skills/design/data/color/arc-peach-play.md +90 -0
- package/data/harnesses/code/skills/design/data/color/attio-hunter-teal.md +90 -0
- package/data/harnesses/code/skills/design/data/color/carbon-cyan-signal.md +90 -0
- package/data/harnesses/code/skills/design/data/color/clay-parchment.md +90 -0
- package/data/harnesses/code/skills/design/data/color/duolingo-feather.md +90 -0
- package/data/harnesses/code/skills/design/data/color/geist-mono-signal.md +90 -0
- package/data/harnesses/code/skills/design/data/color/linear-graphite-violet.md +90 -0
- package/data/harnesses/code/skills/design/data/color/mercury-onyx-cobalt.md +90 -0
- package/data/harnesses/code/skills/design/data/color/notion-paper-ink.md +90 -0
- package/data/harnesses/code/skills/design/data/color/radix-slate-iris.md +90 -0
- package/data/harnesses/code/skills/design/data/color/raycast-void-spectrum.md +90 -0
- package/data/harnesses/code/skills/design/data/color/spectrum-celery-fuchsia.md +90 -0
- package/data/harnesses/code/skills/design/data/color/stripe-blurple-ruby.md +90 -0
- package/data/harnesses/code/skills/design/data/color/superhuman-cod-picton.md +89 -0
- package/data/harnesses/code/skills/design/data/corpora/operations-review/companies.csv +15 -0
- package/data/harnesses/code/skills/design/data/corpora/operations-review/corpus.md +87 -0
- package/data/harnesses/code/skills/design/data/corpora/operations-review/people.csv +25 -0
- package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/companies.csv +15 -0
- package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/corpus.md +83 -0
- package/data/harnesses/code/skills/design/data/corpora/procurement-approvals/people.csv +25 -0
- package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/companies.csv +15 -0
- package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/corpus.md +89 -0
- package/data/harnesses/code/skills/design/data/corpora/revenue-analytics/people.csv +25 -0
- package/data/harnesses/code/skills/design/data/elevation/ant-design-directional-shadow.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/apple-liquid-glass-material.md +18 -0
- package/data/harnesses/code/skills/design/data/elevation/atlassian-surface-shadow-pairing.md +20 -0
- package/data/harnesses/code/skills/design/data/elevation/fluent-2-key-ambient-ramp.md +20 -0
- package/data/harnesses/code/skills/design/data/elevation/github-primer-sparing-shadow.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/ibm-carbon-layer-and-border.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/linear-dark-surface-ladder.md +17 -0
- package/data/harnesses/code/skills/design/data/elevation/material-2-key-ambient-dp-ramp.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/material-3-tonal-elevation.md +21 -0
- package/data/harnesses/code/skills/design/data/elevation/neobrutalist-hard-offset.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/neumorphic-soft-dual-shadow.md +18 -0
- package/data/harnesses/code/skills/design/data/elevation/notion-warm-minimal-hairline.md +17 -0
- package/data/harnesses/code/skills/design/data/elevation/polaris-elevation-inset-bevel.md +21 -0
- package/data/harnesses/code/skills/design/data/elevation/radix-themes-alpha-ring-scale.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/shadcn-neutral-derived-radius.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/stripe-brand-tinted-dual-shadow.md +18 -0
- package/data/harnesses/code/skills/design/data/elevation/tailwind-default-utility-scale.md +19 -0
- package/data/harnesses/code/skills/design/data/elevation/vercel-geist-shadow-as-border.md +20 -0
- package/data/harnesses/code/skills/design/data/gradients/amber-attention-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/cloud-shadow-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/confident-lavender-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/ember-ship-flare.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/full-metal-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/indigo-ruby-cta-band.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/linear-graphite-wash.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/marble-quartz-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/midnight-aurora-panel.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/neutral-volume-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/revenue-uptrend-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/risk-decline-fill.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/signal-blue-develop.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/slate-mist-wash.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/stripe-atmosphere-mesh.md +18 -0
- package/data/harnesses/code/skills/design/data/gradients/teal-signal-accent.md +16 -0
- package/data/harnesses/code/skills/design/data/gradients/violet-magenta-promo.md +16 -0
- package/data/harnesses/code/skills/design/data/iconography.md +199 -0
- package/data/harnesses/code/skills/design/data/motion/accordion-expand.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/destructive-hold-confirm.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/drag-dismiss-momentum.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/empty-state-arrive.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/hover-lift.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/modal-center-arrive.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/number-ticker-roll.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/onboarding-bounce.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/panel-slide.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/press-feedback.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/progress-linear-loop.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/route-crossfade.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/rubber-band-overscroll.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/skeleton-shimmer.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/stagger-arrive.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/toast-enter-stack.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/tooltip-chain-instant.md +18 -0
- package/data/harnesses/code/skills/design/data/motion/trigger-anchored-reveal.md +18 -0
- package/data/harnesses/code/skills/design/data/tells.csv +34 -0
- package/data/harnesses/code/skills/design/data/typography/anton-work-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/archivo-black-archivo.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/bitter-karla.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/dm-serif-dm-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/fraunces-inter.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/geist-geist-mono.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/hubot-sans-mona-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/ibm-plex-sans-operator-single-family.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/instrument-serif-instrument-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/inter-operator-single-family.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/libre-baskerville-libre-franklin.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/newsreader-inter.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/playfair-display-source-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/public-sans-operator-single-family.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/sohne-inter-fintech.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/source-serif-source-sans.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/space-grotesk-work-sans-space-mono.md +17 -0
- package/data/harnesses/code/skills/design/data/typography/spectral-manrope.md +17 -0
- package/data/harnesses/code/skills/design/data/voices/bright-collaborator.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/direct-operator.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/earnest-helper.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/editorial-composed.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/quiet-concierge.md +29 -0
- package/data/harnesses/code/skills/design/data/voices/warm-guide.md +29 -0
- package/data/harnesses/code/skills/design/design-md/SCHEMA.md +179 -0
- package/data/harnesses/code/skills/design/design-md/index.csv +67 -0
- package/data/harnesses/code/skills/design/design-md/library/airbnb.md +207 -0
- package/data/harnesses/code/skills/design/design-md/library/airtable.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/anthropic-claude.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/apple.md +206 -0
- package/data/harnesses/code/skills/design/design-md/library/binance.md +210 -0
- package/data/harnesses/code/skills/design/design-md/library/bmw.md +210 -0
- package/data/harnesses/code/skills/design/design-md/library/cal.md +209 -0
- package/data/harnesses/code/skills/design/design-md/library/clay.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/clickhouse.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/cohere.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/coinbase.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/composio.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/cursor.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/elevenlabs.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/expo.md +200 -0
- package/data/harnesses/code/skills/design/design-md/library/figma.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/framer.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/hashicorp.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/ibm.md +205 -0
- package/data/harnesses/code/skills/design/design-md/library/intercom.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/kraken.md +200 -0
- package/data/harnesses/code/skills/design/design-md/library/linear-app.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/lovable.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/meta.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/minimax.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/mintlify.md +210 -0
- package/data/harnesses/code/skills/design/design-md/library/miro.md +209 -0
- package/data/harnesses/code/skills/design/design-md/library/mistral-ai.md +201 -0
- package/data/harnesses/code/skills/design/design-md/library/mongodb.md +201 -0
- package/data/harnesses/code/skills/design/design-md/library/notion.md +204 -0
- package/data/harnesses/code/skills/design/design-md/library/nvidia.md +215 -0
- package/data/harnesses/code/skills/design/design-md/library/ollama.md +213 -0
- package/data/harnesses/code/skills/design/design-md/library/opencode-ai.md +216 -0
- package/data/harnesses/code/skills/design/design-md/library/posthog.md +218 -0
- package/data/harnesses/code/skills/design/design-md/library/raycast.md +214 -0
- package/data/harnesses/code/skills/design/design-md/library/replicate.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/resend.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/revolut.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/runwayml.md +203 -0
- package/data/harnesses/code/skills/design/design-md/library/sanity.md +200 -0
- package/data/harnesses/code/skills/design/design-md/library/sentry.md +201 -0
- package/data/harnesses/code/skills/design/design-md/library/shopify.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/slack.md +202 -0
- package/data/harnesses/code/skills/design/design-md/library/spacex.md +206 -0
- package/data/harnesses/code/skills/design/design-md/library/spotify.md +206 -0
- package/data/harnesses/code/skills/design/design-md/library/stripe.md +196 -0
- package/data/harnesses/code/skills/design/design-md/library/supabase.md +196 -0
- package/data/harnesses/code/skills/design/design-md/library/superhuman.md +194 -0
- package/data/harnesses/code/skills/design/design-md/library/tesla.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/theverge.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/together-ai.md +193 -0
- package/data/harnesses/code/skills/design/design-md/library/uber.md +195 -0
- package/data/harnesses/code/skills/design/design-md/library/vercel.md +195 -0
- package/data/harnesses/code/skills/design/design-md/library/voltagent.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/warp.md +196 -0
- package/data/harnesses/code/skills/design/design-md/library/webflow.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/wired.md +197 -0
- package/data/harnesses/code/skills/design/design-md/library/wise.md +198 -0
- package/data/harnesses/code/skills/design/design-md/library/x-ai.md +199 -0
- package/data/harnesses/code/skills/design/design-md/library/zapier.md +198 -0
- package/data/harnesses/code/skills/design/design-md/nurix.md +214 -0
- package/data/harnesses/code/skills/design/design-md/variants/editorial-luxury.md +184 -0
- package/data/harnesses/code/skills/design/design-md/variants/expressive-saas.md +216 -0
- package/data/harnesses/code/skills/design/design-md/variants/quiet-premium.md +211 -0
- package/data/harnesses/code/skills/design/design-md/variants/terse-dense-operator.md +212 -0
- package/data/harnesses/code/skills/design/design-md/variants/warm-approachable.md +216 -0
- package/data/harnesses/code/skills/design/references/compose.md +23 -0
- package/data/harnesses/code/skills/design/references/content.md +28 -0
- package/data/harnesses/code/skills/design/references/emission.md +21 -0
- package/data/harnesses/code/skills/design/references/existing-apps.md +27 -0
- package/data/harnesses/code/skills/design/references/intake.md +36 -0
- package/data/harnesses/code/skills/design/references/object-pass.md +24 -0
- package/data/harnesses/code/skills/design/references/ship-gate.md +29 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/Readme.md +195 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/ai-layer.md +49 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/attributes-lifecycle.md +43 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/container-choice.md +44 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/creation-paths.md +46 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/dashboards.md +42 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/modes.md +42 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/object-model.md +46 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/page-archetype.md +48 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/promotion-demotion.md +37 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/settings.md +42 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/standard-pages.md +46 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/state-matrix.md +41 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/table-mechanics.md +48 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/verbs.md +45 -0
- package/data/harnesses/code/skills/design/references/ux-patterns/viewer-context.md +40 -0
- package/data/harnesses/code/skills/design/scripts/README-validator.md +27 -0
- package/data/harnesses/code/skills/design/scripts/select-chart.mjs +596 -0
- package/data/harnesses/code/skills/design/scripts/validate-design-md.mjs +1198 -0
- package/data/harnesses/discussion/agents/discussion-baseline.md +1 -1
- package/data/harnesses/discussion/harness.json +1 -1
- package/data/harnesses/etna/agents/maintenance.md +1 -1
- package/data/harnesses/etna/harness.json +1 -1
- package/data/harnesses/hr/CLAUDE.md +1 -1
- package/data/harnesses/hr/harness.json +1 -1
- package/package.json +1 -1
- package/data/harnesses/code/skills/design/data/catalog/rules.csv +0 -137
- package/data/harnesses/code/skills/design/data/combinations/Readme.md +0 -33
- package/data/harnesses/code/skills/design/data/combinations/accent-presets.md +0 -113
- package/data/harnesses/code/skills/design/data/combinations/charts.csv +0 -33
- package/data/harnesses/code/skills/design/data/combinations/fonts-known.json +0 -448
- package/data/harnesses/code/skills/design/data/combinations/icons.csv +0 -157
- package/data/harnesses/code/skills/design/data/combinations/landing.csv +0 -38
- package/data/harnesses/code/skills/design/data/combinations/lucide-names.json +0 -2001
- package/data/harnesses/code/skills/design/data/combinations/manifest.json +0 -330
- package/data/harnesses/code/skills/design/data/combinations/motion.csv +0 -10
- package/data/harnesses/code/skills/design/data/combinations/palettes.csv +0 -92
- package/data/harnesses/code/skills/design/data/combinations/product-router.csv +0 -80
- package/data/harnesses/code/skills/design/data/combinations/styles.csv +0 -67
- package/data/harnesses/code/skills/design/data/combinations/typography.csv +0 -64
- package/data/harnesses/code/skills/design/data/design-md/SCHEMA.md +0 -106
- package/data/harnesses/code/skills/design/data/design-md/library/airbnb.md +0 -154
- package/data/harnesses/code/skills/design/data/design-md/library/airtable.md +0 -151
- package/data/harnesses/code/skills/design/data/design-md/library/anthropic-claude.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/apple.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/binance.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/bmw.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/cal.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/clay.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/clickhouse.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/cohere.md +0 -147
- package/data/harnesses/code/skills/design/data/design-md/library/coinbase.md +0 -156
- package/data/harnesses/code/skills/design/data/design-md/library/composio.md +0 -138
- package/data/harnesses/code/skills/design/data/design-md/library/cursor.md +0 -138
- package/data/harnesses/code/skills/design/data/design-md/library/elevenlabs.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/expo.md +0 -148
- package/data/harnesses/code/skills/design/data/design-md/library/figma.md +0 -141
- package/data/harnesses/code/skills/design/data/design-md/library/framer.md +0 -156
- package/data/harnesses/code/skills/design/data/design-md/library/hashicorp.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/ibm.md +0 -153
- package/data/harnesses/code/skills/design/data/design-md/library/intercom.md +0 -158
- package/data/harnesses/code/skills/design/data/design-md/library/kraken.md +0 -156
- package/data/harnesses/code/skills/design/data/design-md/library/linear-app.md +0 -122
- package/data/harnesses/code/skills/design/data/design-md/library/lovable.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/meta.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/minimax.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/mintlify.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/miro.md +0 -153
- package/data/harnesses/code/skills/design/data/design-md/library/mistral-ai.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/mongodb.md +0 -155
- package/data/harnesses/code/skills/design/data/design-md/library/notion.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/nvidia.md +0 -141
- package/data/harnesses/code/skills/design/data/design-md/library/ollama.md +0 -145
- package/data/harnesses/code/skills/design/data/design-md/library/opencode-ai.md +0 -142
- package/data/harnesses/code/skills/design/data/design-md/library/posthog.md +0 -152
- package/data/harnesses/code/skills/design/data/design-md/library/raycast.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/replicate.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/resend.md +0 -170
- package/data/harnesses/code/skills/design/data/design-md/library/revolut.md +0 -145
- package/data/harnesses/code/skills/design/data/design-md/library/runwayml.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/sanity.md +0 -168
- package/data/harnesses/code/skills/design/data/design-md/library/sentry.md +0 -163
- package/data/harnesses/code/skills/design/data/design-md/library/shopify.md +0 -147
- package/data/harnesses/code/skills/design/data/design-md/library/slack.md +0 -158
- package/data/harnesses/code/skills/design/data/design-md/library/spacex.md +0 -171
- package/data/harnesses/code/skills/design/data/design-md/library/spotify.md +0 -166
- package/data/harnesses/code/skills/design/data/design-md/library/stripe.md +0 -160
- package/data/harnesses/code/skills/design/data/design-md/library/supabase.md +0 -173
- package/data/harnesses/code/skills/design/data/design-md/library/superhuman.md +0 -145
- package/data/harnesses/code/skills/design/data/design-md/library/tesla.md +0 -153
- package/data/harnesses/code/skills/design/data/design-md/library/theverge.md +0 -167
- package/data/harnesses/code/skills/design/data/design-md/library/together-ai.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/uber.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/vercel.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/voltagent.md +0 -130
- package/data/harnesses/code/skills/design/data/design-md/library/warp.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/webflow.md +0 -140
- package/data/harnesses/code/skills/design/data/design-md/library/wired.md +0 -139
- package/data/harnesses/code/skills/design/data/design-md/library/wise.md +0 -137
- package/data/harnesses/code/skills/design/data/design-md/library/x-ai.md +0 -138
- package/data/harnesses/code/skills/design/data/design-md/library/zapier.md +0 -158
- package/data/harnesses/code/skills/design/data/design-md/nurix.md +0 -138
- package/data/harnesses/code/skills/design/data/design-tokens/SCHEMA.md +0 -695
- package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/airbnb/design.tokens.json +0 -1272
- package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/airtable/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/anthropic-claude/design.tokens.json +0 -1273
- package/data/harnesses/code/skills/design/data/design-tokens/apple/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/apple/design.tokens.json +0 -1262
- package/data/harnesses/code/skills/design/data/design-tokens/binance/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/binance/design.tokens.json +0 -1257
- package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/bmw/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/cal/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/cal/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/clay/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/clay/design.tokens.json +0 -1261
- package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/clickhouse/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/cohere/design.tokens.json +0 -1281
- package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/coinbase/design.tokens.json +0 -1262
- package/data/harnesses/code/skills/design/data/design-tokens/composio/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/composio/design.tokens.json +0 -1271
- package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/cursor/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/elevenlabs/design.tokens.json +0 -1293
- package/data/harnesses/code/skills/design/data/design-tokens/expo/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/expo/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/figma/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/figma/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/framer/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/framer/design.tokens.json +0 -1267
- package/data/harnesses/code/skills/design/data/design-tokens/goldens.json +0 -251
- package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/hashicorp/design.tokens.json +0 -1273
- package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/ibm/design.tokens.json +0 -1286
- package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/intercom/design.tokens.json +0 -1290
- package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/kraken/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.resolver.json +0 -23
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.dark.json +0 -599
- package/data/harnesses/code/skills/design/data/design-tokens/linear-app/design.tokens.json +0 -1272
- package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/lovable/design.tokens.json +0 -1267
- package/data/harnesses/code/skills/design/data/design-tokens/meta/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/meta/design.tokens.json +0 -1289
- package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/minimax/design.tokens.json +0 -1258
- package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/mintlify/design.tokens.json +0 -1284
- package/data/harnesses/code/skills/design/data/design-tokens/miro/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/miro/design.tokens.json +0 -1291
- package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/mistral-ai/design.tokens.json +0 -1263
- package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/mongodb/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/notion/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/notion/design.tokens.json +0 -1261
- package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/nurix/design.tokens.json +0 -1209
- package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/nvidia/design.tokens.json +0 -1265
- package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/ollama/design.tokens.json +0 -1253
- package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/opencode-ai/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/posthog/design.tokens.json +0 -1261
- package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/raycast/design.tokens.json +0 -1263
- package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/replicate/design.tokens.json +0 -1266
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.resolver.json +0 -23
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.dark.json +0 -623
- package/data/harnesses/code/skills/design/data/design-tokens/resend/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/revolut/design.tokens.json +0 -1264
- package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/runwayml/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/sanity/design.tokens.json +0 -1270
- package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/sentry/design.tokens.json +0 -1288
- package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/shopify/design.tokens.json +0 -1272
- package/data/harnesses/code/skills/design/data/design-tokens/slack/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/slack/design.tokens.json +0 -1265
- package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/spacex/design.tokens.json +0 -1287
- package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/spotify/design.tokens.json +0 -1286
- package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/stripe/design.tokens.json +0 -1285
- package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/supabase/design.tokens.json +0 -1287
- package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/superhuman/design.tokens.json +0 -1278
- package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/tesla/design.tokens.json +0 -1269
- package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/theverge/design.tokens.json +0 -1285
- package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/together-ai/design.tokens.json +0 -1303
- package/data/harnesses/code/skills/design/data/design-tokens/uber/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/uber/design.tokens.json +0 -1274
- package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/vercel/design.tokens.json +0 -1296
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.resolver.json +0 -23
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.dark.json +0 -612
- package/data/harnesses/code/skills/design/data/design-tokens/voltagent/design.tokens.json +0 -1282
- package/data/harnesses/code/skills/design/data/design-tokens/warp/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/warp/design.tokens.json +0 -1287
- package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/webflow/design.tokens.json +0 -1268
- package/data/harnesses/code/skills/design/data/design-tokens/wired/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/wired/design.tokens.json +0 -1276
- package/data/harnesses/code/skills/design/data/design-tokens/wise/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/wise/design.tokens.json +0 -1273
- package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/x-ai/design.tokens.json +0 -1296
- package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.resolver.json +0 -20
- package/data/harnesses/code/skills/design/data/design-tokens/zapier/design.tokens.json +0 -1286
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/command-center.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/create-edit.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/dashboard.tsx +0 -225
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/entity-detail.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/list-detail.tsx +0 -224
- package/data/harnesses/code/skills/design/data/operator-pack/exemplars/log-stream.tsx +0 -249
- package/data/harnesses/code/skills/design/data/operator-pack/fixtures.md +0 -88
- package/data/harnesses/code/skills/design/data/operator-pack/surface-catalog.md +0 -132
- package/data/harnesses/code/skills/design/data/operator-pack/trust-rubric.md +0 -76
- package/data/harnesses/code/skills/design/references/create.md +0 -173
- package/data/harnesses/code/skills/design/references/diagnosis.md +0 -108
- package/data/harnesses/code/skills/design/references/hard-rules.md +0 -177
- package/data/harnesses/code/skills/design/references/improve.md +0 -116
- package/data/harnesses/code/skills/design/references/interaction.md +0 -77
- package/data/harnesses/code/skills/design/references/introspect.md +0 -100
- package/data/harnesses/code/skills/design/references/lifecycle.md +0 -73
- package/data/harnesses/code/skills/design/references/personality.md +0 -575
- package/data/harnesses/code/skills/design/references/structure.md +0 -159
- package/data/harnesses/code/skills/design/scripts/convert-design-md.mjs +0 -1312
- package/data/harnesses/code/skills/design/scripts/derive-personality.mjs +0 -1498
- package/data/harnesses/code/skills/design/scripts/generate-fixtures.mjs +0 -551
- package/data/harnesses/code/skills/design/scripts/lib/checks.mjs +0 -1092
- package/data/harnesses/code/skills/design/scripts/lib/golden.mjs +0 -26
- package/data/harnesses/code/skills/design/scripts/lib/vocabulary.mjs +0 -65
- package/data/harnesses/code/skills/design/scripts/phase-gate.mjs +0 -538
- package/data/harnesses/code/skills/design/scripts/preship-sweep.mjs +0 -312
- package/data/harnesses/code/skills/design/scripts/render-rung.mjs +0 -890
- package/data/harnesses/code/skills/design/scripts/scaffold.mjs +0 -769
- package/data/harnesses/code/skills/design/scripts/self-integrity.mjs +0 -675
- package/data/harnesses/code/skills/design/scripts/snapshot-goldens.mjs +0 -164
- package/data/harnesses/code/skills/design/scripts/validate-combinations.mjs +0 -439
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// skills/design/scripts/snapshot-goldens.mjs — snapshot golden hashes of the CSS emitted for every design-tokens artifact (data/design-tokens/<slug>/) into data/design-tokens/goldens.json, the byte-match regression oracle for the emission pipeline, then run the Tailwind compile smoke (emission contract 5): one emitted globals.css + personality.css + fonts.css set is actually compiled by @tailwindcss/vite (the fonts.css @fontsource imports resolve against local stubs) — goldens hash bytes and never compile them, so a byte-green gate can hide unbuildable CSS (a derivation comment containing a Tailwind utility literal closes the comment early). Emission uses --provisional: hint-carrying library twins are hashed at their provisional values (--emit alone still refuses on hints).
|
|
3
|
-
|
|
4
|
-
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { spawnSync } from "node:child_process";
|
|
9
|
-
import { goldenHash } from "./lib/golden.mjs";
|
|
10
|
-
|
|
11
|
-
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
const skillDir = dirname(scriptsDir);
|
|
13
|
-
const derivePersonalityScript = join(scriptsDir, "derive-personality.mjs");
|
|
14
|
-
const tokensDir = join(skillDir, "data", "design-tokens");
|
|
15
|
-
const goldensJsonPath = join(tokensDir, "goldens.json");
|
|
16
|
-
|
|
17
|
-
// Where full emitted outputs are kept (one subdir per slug) — GOLDENS_EMIT_ROOT to pin it, else a fresh temp dir.
|
|
18
|
-
const emitRoot = process.env.GOLDENS_EMIT_ROOT || mkdtempSync(join(tmpdir(), "design-goldens-"));
|
|
19
|
-
mkdirSync(emitRoot, { recursive: true });
|
|
20
|
-
|
|
21
|
-
// Every artifact directory under data/design-tokens/ (a directory holding design.tokens.json).
|
|
22
|
-
const entries = readdirSync(tokensDir, { withFileTypes: true })
|
|
23
|
-
.filter((d) => d.isDirectory() && existsSync(join(tokensDir, d.name, "design.tokens.json")))
|
|
24
|
-
.map((d) => ({ slug: d.name, path: join(tokensDir, d.name) }))
|
|
25
|
-
.sort((a, b) => (a.slug < b.slug ? -1 : 1));
|
|
26
|
-
|
|
27
|
-
const goldens = {};
|
|
28
|
-
const failures = [];
|
|
29
|
-
const provisional = [];
|
|
30
|
-
|
|
31
|
-
for (const { slug, path } of entries) {
|
|
32
|
-
const outdir = join(emitRoot, slug);
|
|
33
|
-
const run = spawnSync(process.execPath, [derivePersonalityScript, path, "--emit", outdir, "--provisional"], { encoding: "utf8" });
|
|
34
|
-
if (run.status !== 0) {
|
|
35
|
-
failures.push({ slug, error: (run.stderr || run.stdout || `exit status ${run.status}`).trim() });
|
|
36
|
-
console.log(`FAIL ${slug}`);
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
const wasProvisional = /^emit PROVISIONAL/m.test(run.stdout || "");
|
|
40
|
-
if (wasProvisional) provisional.push(slug);
|
|
41
|
-
try {
|
|
42
|
-
const personalityCss = readFileSync(join(outdir, "src", "styles", "personality.css"), "utf8");
|
|
43
|
-
const globalsCss = readFileSync(join(outdir, "src", "styles", "globals.css"), "utf8");
|
|
44
|
-
goldens[slug] = { personality: goldenHash(personalityCss), globals: goldenHash(globalsCss) };
|
|
45
|
-
console.log(`OK ${slug}${wasProvisional ? " (provisional)" : ""}`);
|
|
46
|
-
} catch (err) {
|
|
47
|
-
failures.push({ slug, error: String((err && err.message) || err) });
|
|
48
|
-
console.log(`FAIL ${slug}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Assemble the sorted-key document; "_meta" sorts first (ASCII "_" < "a").
|
|
53
|
-
const document = {
|
|
54
|
-
_meta: {
|
|
55
|
-
description: "Post-carrier golden hashes of the CSS emitted by derive-personality.mjs --emit --provisional for every design-tokens artifact (data/design-tokens/<slug>/), dark-inclusive: an artifact shipping design.tokens.dark.json hashes its emitted :root[data-theme=\"dark\"] block too. Hint-carrying twins are hashed at their provisional $values (--emit without --provisional still refuses on hints). Each entry records the sha256 of the comment-stripped personality.css and globals.css (see scripts/lib/golden.mjs). The byte-match regression oracle for the emission pipeline; the Tailwind compile smoke (emission contract 5) runs after every refresh.",
|
|
56
|
-
generator: "scripts/snapshot-goldens.mjs",
|
|
57
|
-
strip: "css-comments",
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
for (const slug of Object.keys(goldens).sort()) document[slug] = goldens[slug];
|
|
61
|
-
|
|
62
|
-
mkdirSync(dirname(goldensJsonPath), { recursive: true });
|
|
63
|
-
writeFileSync(goldensJsonPath, JSON.stringify(document, null, 2) + "\n");
|
|
64
|
-
|
|
65
|
-
console.log(`\n${entries.length} entries: ${Object.keys(goldens).length} OK (${provisional.length} provisional), ${failures.length} FAIL`);
|
|
66
|
-
console.log(`goldens: ${goldensJsonPath}`);
|
|
67
|
-
console.log(`emitted outputs: ${emitRoot}`);
|
|
68
|
-
if (failures.length > 0) {
|
|
69
|
-
for (const f of failures) console.error(`\nFAIL ${f.slug}:\n${f.error}`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
// Tailwind compile smoke (emission contract 5) — compile one emitted pair for real
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
|
|
77
|
-
// True when a spawn result reads as "npm cannot reach the registry" (the only degradable failure).
|
|
78
|
-
function isNetworkFailure(res) {
|
|
79
|
-
const text = `${res.stderr || ""}${res.stdout || ""}`;
|
|
80
|
-
return res.error !== undefined || /ENOTFOUND|ETIMEDOUT|EAI_AGAIN|ECONNREFUSED|ENETUNREACH|network|offline/i.test(text);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Compile the emitted pair of one slug with @tailwindcss/vite in a scratch project; exits non-zero
|
|
84
|
-
// on a compile failure (the smoke is a gate). Degrades with a notice only when npm is unreachable.
|
|
85
|
-
function runCompileSmoke(slug) {
|
|
86
|
-
const src = join(emitRoot, slug, "src", "styles");
|
|
87
|
-
if (!existsSync(join(src, "globals.css"))) {
|
|
88
|
-
console.error(`\ncompile smoke: no emitted output for ${slug} — cannot smoke`);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
const smokeDir = process.env.GOLDENS_SMOKE_DIR || join(tmpdir(), "design-goldens-smoke");
|
|
92
|
-
mkdirSync(join(smokeDir, "src", "styles"), { recursive: true });
|
|
93
|
-
|
|
94
|
-
writeFileSync(join(smokeDir, "package.json"), JSON.stringify({
|
|
95
|
-
name: "design-goldens-smoke",
|
|
96
|
-
private: true,
|
|
97
|
-
type: "module",
|
|
98
|
-
devDependencies: { vite: "^7.0.0", tailwindcss: "^4.0.0", "@tailwindcss/vite": "^4.0.0" },
|
|
99
|
-
}, null, 2) + "\n");
|
|
100
|
-
writeFileSync(join(smokeDir, "vite.config.mjs"), `// design-goldens-smoke vite.config.mjs — @tailwindcss/vite over the emitted pair
|
|
101
|
-
import { defineConfig } from "vite";
|
|
102
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
103
|
-
export default defineConfig({ plugins: [tailwindcss()], logLevel: "error" });
|
|
104
|
-
`);
|
|
105
|
-
// Utility classes in the content so the @theme-driven utilities (ramp, shadows, ladder roles,
|
|
106
|
-
// motion classes) actually generate — a bad @theme value fails here, not just at import.
|
|
107
|
-
writeFileSync(join(smokeDir, "index.html"), `<!doctype html>
|
|
108
|
-
<html lang="en" data-style="${slug}" data-theme="light" data-density="standard">
|
|
109
|
-
<head><meta charset="utf-8"><title>design-goldens-smoke</title></head>
|
|
110
|
-
<body>
|
|
111
|
-
<div class="text-h1 tracking-tight p-4 gap-2 rounded-lg shadow-sm bg-surface-raised motion-lift">smoke</div>
|
|
112
|
-
<div class="text-display shadow-overlay bg-surface-overlay motion-stagger"><span>a</span><span>b</span></div>
|
|
113
|
-
<script type="module" src="/src/main.js"></script>
|
|
114
|
-
</body>
|
|
115
|
-
</html>
|
|
116
|
-
`);
|
|
117
|
-
writeFileSync(join(smokeDir, "src", "main.js"), `// design-goldens-smoke entry — pulls the emitted pair through the Tailwind build
|
|
118
|
-
import "./styles/globals.css";
|
|
119
|
-
`);
|
|
120
|
-
for (const f of ["globals.css", "personality.css", "fonts.css"]) {
|
|
121
|
-
writeFileSync(join(smokeDir, "src", "styles", f), readFileSync(join(src, f), "utf8"));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!existsSync(join(smokeDir, "node_modules", "vite"))) {
|
|
125
|
-
const install = spawnSync("npm", ["install", "--no-audit", "--no-fund"], { cwd: smokeDir, encoding: "utf8" });
|
|
126
|
-
if (install.status !== 0) {
|
|
127
|
-
if (isNetworkFailure(install)) {
|
|
128
|
-
console.log(`\ncompile smoke SKIPPED — npm unreachable (${(install.stderr || "").split("\n")[0] || "no network"}); the goldens are hashed but this refresh is NOT compile-verified. Re-run snapshot-goldens.mjs online.`);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
console.error(`\ncompile smoke: npm install failed in ${smokeDir}:\n${(install.stderr || install.stdout || "").slice(0, 2000)}`);
|
|
132
|
-
process.exit(1);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// The library import resolves against a local stub: the smoke gates OUR emitted bytes compiling,
|
|
136
|
-
// not restricted-registry availability. Written after install so npm pruning cannot drop it.
|
|
137
|
-
const stubDir = join(smokeDir, "node_modules", "@nurix", "components");
|
|
138
|
-
mkdirSync(stubDir, { recursive: true });
|
|
139
|
-
writeFileSync(join(stubDir, "package.json"), JSON.stringify({ name: "@nurix/components", version: "0.0.0-smoke-stub", exports: { "./styles.css": "./styles.css" } }, null, 2) + "\n");
|
|
140
|
-
writeFileSync(join(stubDir, "styles.css"), `/* @nurix/components/styles.css — compile-smoke stub: import resolution + cascade shape only */
|
|
141
|
-
:root { --background: oklch(1 0 0); --radius-sm: calc(var(--radius) - 4px); }
|
|
142
|
-
`);
|
|
143
|
-
// The emitted fonts.css imports resolve against local @fontsource stubs for the same reason:
|
|
144
|
-
// the smoke gates OUR bytes compiling, never per-package registry availability.
|
|
145
|
-
const fontsCss = readFileSync(join(smokeDir, "src", "styles", "fonts.css"), "utf8");
|
|
146
|
-
for (const m of fontsCss.matchAll(/@import\s+['"]((@fontsource(?:-variable)?\/[^'"/]+)(?:\/([^'"]+))?)['"]/g)) {
|
|
147
|
-
const [, spec, pkgName, relFile] = m;
|
|
148
|
-
const rel = relFile || "index.css";
|
|
149
|
-
const pkgDir = join(smokeDir, "node_modules", ...pkgName.split("/"));
|
|
150
|
-
mkdirSync(dirname(join(pkgDir, rel)), { recursive: true });
|
|
151
|
-
writeFileSync(join(pkgDir, "package.json"), JSON.stringify({ name: pkgName, version: "0.0.0-smoke-stub" }, null, 2) + "\n");
|
|
152
|
-
writeFileSync(join(pkgDir, rel), `/* ${spec} — compile-smoke stub: import resolution only */\n`);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const build = spawnSync("npm", ["exec", "--no", "--", "vite", "build"], { cwd: smokeDir, encoding: "utf8" });
|
|
156
|
-
if (build.status !== 0) {
|
|
157
|
-
console.error(`\ncompile smoke FAILED — the emitted ${slug} pair does not compile under @tailwindcss/vite:`);
|
|
158
|
-
console.error((build.stderr || build.stdout || "").slice(0, 4000));
|
|
159
|
-
process.exit(1);
|
|
160
|
-
}
|
|
161
|
-
console.log(`compile smoke PASSED — emitted ${slug} pair compiles under @tailwindcss/vite (${smokeDir})`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
runCompileSmoke(goldens.nurix ? "nurix" : Object.keys(goldens).sort()[0]);
|
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
// skills/design/scripts/validate-combinations.mjs — schema validator for the combination datasets (data/combinations/): file presence, byte-exact headers, sequential ids, tier/density/surface/theme/budget vocabularies, symbol-glyph ban, palette hex + WCAG contrast floors, style payload + checklist discipline, typography @fontsource binding, motion token-name discipline, chart token discipline, lucide/font registry membership, accent-preset scale shape, and manifest row/tier-count parity. Importable (validateCombinations(dir)) and runnable as a CLI.
|
|
2
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
-
import { join, dirname, resolve } from "node:path";
|
|
4
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
|
|
6
|
-
const SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const SKILL_ROOT = join(SCRIPTS_DIR, "..");
|
|
8
|
-
const DEFAULT_DIR = join(SKILL_ROOT, "data/combinations");
|
|
9
|
-
|
|
10
|
-
const CSV_FILES = ["typography.csv", "palettes.csv", "styles.csv", "motion.csv", "product-router.csv", "charts.csv", "icons.csv", "landing.csv"];
|
|
11
|
-
const REQUIRED_FILES = [...CSV_FILES, "manifest.json", "lucide-names.json", "fonts-known.json", "accent-presets.md", "Readme.md"];
|
|
12
|
-
|
|
13
|
-
// Byte-exact, order-exact header contract per dataset.
|
|
14
|
-
const HEADERS = {
|
|
15
|
-
"typography.csv": ["id", "pairing", "tier", "heading_family", "body_family", "mono_family", "contrast", "mood", "best_for", "heading_weights", "body_weights", "google_fonts_css", "notes", "css-import", "tailwind-config", "display-size-hint"],
|
|
16
|
-
"palettes.csv": ["id", "palette", "tier", "product_affinity", "theme", "mood", "background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "muted", "muted-foreground", "border", "input", "ring", "primary", "primary-foreground", "secondary", "secondary-foreground", "accent", "accent-foreground", "destructive", "destructive-foreground", "success", "warning", "chart-1", "chart-2", "chart-3", "chart-4", "chart-5", "sidebar", "sidebar-foreground", "sidebar-primary", "sidebar-primary-foreground", "sidebar-accent", "sidebar-accent-foreground", "sidebar-border", "sidebar-ring", "notes"],
|
|
17
|
-
"styles.csv": ["id", "movement", "tier", "era", "keywords", "description", "palette_stance", "type_stance", "shape_stance", "motion_stance", "density_affinity", "best_for", "avoid_for", "token_leanings", "operator_note", "theme", "voice", "css-recipe", "checklist", "tokens-hint"],
|
|
18
|
-
"motion.csv": ["id", "moment", "tier", "keywords", "trigger", "duration_token", "easing_token", "transform_delta", "archetypes", "budget", "do", "dont", "performance_notes"],
|
|
19
|
-
"product-router.csv": ["id", "product_type", "tier", "keywords", "style_priority", "palette_stance", "type_stance", "density_tier", "key_surfaces", "decision_rules", "anti_patterns", "notes"],
|
|
20
|
-
"charts.csv": ["id", "data_shape", "tier", "keywords", "best_chart", "alternatives", "when", "when_not", "volume_guidance", "series_tokens", "a11y_notes", "fallback", "interaction"],
|
|
21
|
-
"icons.csv": ["id", "intent", "tier", "keywords", "lucide_name", "import_name", "usage", "notes"],
|
|
22
|
-
"landing.csv": ["id", "pattern", "tier", "keywords", "section_order", "cta_placement", "color_strategy", "effects", "conversion_notes"],
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const VALID_TIERS = new Set(["operator-active", "dormant-marketing"]);
|
|
26
|
-
const VALID_DENSITIES = new Set(["spacious", "standard", "dense"]);
|
|
27
|
-
const VALID_TYPE_CONTRASTS = new Set(["serif+sans", "sans+sans", "sans+serif", "serif+serif", "display+sans", "mono+sans"]);
|
|
28
|
-
// The closed six surface archetypes — product-router key_surfaces and motion archetypes both draw from it.
|
|
29
|
-
const VALID_ARCHETYPES = new Set(["list-detail", "dashboard", "entity-detail", "create-edit", "log-stream", "command-center"]);
|
|
30
|
-
const VALID_STYLE_THEMES = new Set(["light", "dark", "both"]);
|
|
31
|
-
const VALID_PALETTE_THEMES = new Set(["light", "dark"]);
|
|
32
|
-
const VALID_BUDGETS = new Set(["baseline", "brand-forward"]);
|
|
33
|
-
// Legal temporal token names per design-tokens SCHEMA.md: the stored duration primitives (+ v2 stagger-unit) and ease-default plus its emit-derived ease-enter / ease-exit pair.
|
|
34
|
-
const MOTION_DURATION_TOKENS = new Set(["duration-unit", "duration-fast", "duration-normal", "duration-slow", "stagger-unit"]);
|
|
35
|
-
const MOTION_EASING_TOKENS = new Set(["ease-default", "ease-enter", "ease-exit"]);
|
|
36
|
-
// The four executable style payload columns — non-empty on every operator-active row.
|
|
37
|
-
const STYLE_PAYLOAD_COLUMNS = ["voice", "css-recipe", "checklist", "tokens-hint"];
|
|
38
|
-
// One @fontsource import statement, as the css-import cell carries them ;-separated.
|
|
39
|
-
const FONTSOURCE_IMPORT = /^@import '@fontsource(?:-variable)?\/([a-z0-9-]+)\/[a-z0-9.-]+\.css'$/;
|
|
40
|
-
const ACCENT_PRESET_SCALES = 12;
|
|
41
|
-
const ACCENT_PRESET_STEPS = 7;
|
|
42
|
-
|
|
43
|
-
const UPPER_HEX = /^#[0-9A-F]{6}$/;
|
|
44
|
-
const RAW_HEX_ANYWHERE = /#[0-9a-fA-F]{3,8}\b/;
|
|
45
|
-
// Box drawing, geometric shapes, misc symbols, dingbats, misc symbols-and-arrows, VS16, and emoji — covers ✓ ⚡ ◐ ☐ and kin.
|
|
46
|
-
const BANNED_GLYPHS = new RegExp("[\\u2500-\\u25FF\\u2600-\\u27BF\\u2B00-\\u2BFF\\uFE0F]|\\p{Extended_Pictographic}", "u");
|
|
47
|
-
|
|
48
|
-
// Foreground-on-base pairs that must clear WCAG AA text contrast (>= 4.5:1).
|
|
49
|
-
const AA_TEXT_PAIRS = [
|
|
50
|
-
["background", "foreground"], ["card", "card-foreground"], ["popover", "popover-foreground"],
|
|
51
|
-
["muted", "muted-foreground"], ["primary", "primary-foreground"], ["secondary", "secondary-foreground"],
|
|
52
|
-
["accent", "accent-foreground"], ["destructive", "destructive-foreground"],
|
|
53
|
-
["sidebar", "sidebar-foreground"], ["sidebar-primary", "sidebar-primary-foreground"],
|
|
54
|
-
["sidebar-accent", "sidebar-accent-foreground"],
|
|
55
|
-
];
|
|
56
|
-
// Status/series colors that must clear >= 3:1 against background.
|
|
57
|
-
const GRAPHIC_VS_BACKGROUND = ["success", "warning", "chart-1", "chart-2", "chart-3", "chart-4", "chart-5"];
|
|
58
|
-
// The 34 color cells: everything between background and sidebar-ring inclusive in the palettes header.
|
|
59
|
-
const PALETTE_COLOR_COLUMNS = HEADERS["palettes.csv"].slice(
|
|
60
|
-
HEADERS["palettes.csv"].indexOf("background"),
|
|
61
|
-
HEADERS["palettes.csv"].indexOf("sidebar-ring") + 1
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// Read a UTF-8 file, returning null when it does not exist.
|
|
65
|
-
function readSafe(path) {
|
|
66
|
-
return existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Parse RFC4180-style CSV text into an array of field arrays (quotes + doubled-quote escapes honored).
|
|
70
|
-
function parseCsv(text) {
|
|
71
|
-
const rows = [];
|
|
72
|
-
let field = "";
|
|
73
|
-
let record = [];
|
|
74
|
-
let inQuotes = false;
|
|
75
|
-
for (let i = 0; i < text.length; i++) {
|
|
76
|
-
const ch = text[i];
|
|
77
|
-
if (inQuotes) {
|
|
78
|
-
if (ch === '"') {
|
|
79
|
-
if (text[i + 1] === '"') { field += '"'; i++; }
|
|
80
|
-
else inQuotes = false;
|
|
81
|
-
} else field += ch;
|
|
82
|
-
} else if (ch === '"') inQuotes = true;
|
|
83
|
-
else if (ch === ",") { record.push(field); field = ""; }
|
|
84
|
-
else if (ch === "\n") { record.push(field); rows.push(record); record = []; field = ""; }
|
|
85
|
-
else if (ch === "\r") { /* skip */ }
|
|
86
|
-
else field += ch;
|
|
87
|
-
}
|
|
88
|
-
if (field.length > 0 || record.length > 0) { record.push(field); rows.push(record); }
|
|
89
|
-
return rows;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// WCAG 2.x relative luminance of an uppercase #RRGGBB hex.
|
|
93
|
-
function relativeLuminance(hex) {
|
|
94
|
-
const chan = (v) => {
|
|
95
|
-
const c = v / 255;
|
|
96
|
-
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
97
|
-
};
|
|
98
|
-
const r = parseInt(hex.slice(1, 3), 16);
|
|
99
|
-
const g = parseInt(hex.slice(3, 5), 16);
|
|
100
|
-
const b = parseInt(hex.slice(5, 7), 16);
|
|
101
|
-
return 0.2126 * chan(r) + 0.7152 * chan(g) + 0.0722 * chan(b);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// WCAG contrast ratio between two #RRGGBB hex colors.
|
|
105
|
-
function contrastRatio(hexA, hexB) {
|
|
106
|
-
const la = relativeLuminance(hexA);
|
|
107
|
-
const lb = relativeLuminance(hexB);
|
|
108
|
-
const [hi, lo] = la >= lb ? [la, lb] : [lb, la];
|
|
109
|
-
return (hi + 0.05) / (lo + 0.05);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Load one CSV into { header, rows: [{...byExpectedColumn, _cells, _line}] }, or null (recording an error) when absent.
|
|
113
|
-
function loadCsv(dir, file, errors) {
|
|
114
|
-
const text = readSafe(join(dir, file));
|
|
115
|
-
if (text === null) return null;
|
|
116
|
-
const raw = parseCsv(text).filter((r) => r.length > 1 || (r.length === 1 && r[0].trim() !== ""));
|
|
117
|
-
if (raw.length === 0) { errors.push(`${file}: file is empty`); return null; }
|
|
118
|
-
const expected = HEADERS[file];
|
|
119
|
-
const rows = raw.slice(1).map((cells, i) => {
|
|
120
|
-
const obj = {};
|
|
121
|
-
expected.forEach((k, idx) => { obj[k] = cells[idx] !== undefined ? cells[idx] : ""; });
|
|
122
|
-
obj._cells = cells;
|
|
123
|
-
obj._line = i + 2; // 1-based, after the header line
|
|
124
|
-
return obj;
|
|
125
|
-
});
|
|
126
|
-
return { header: raw[0], rows };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Generic invariants shared by every dataset: exact header, column counts, sequential ids, unique names, valid tier, no symbol glyphs.
|
|
130
|
-
function checkGeneric(file, csv, errors) {
|
|
131
|
-
const expected = HEADERS[file];
|
|
132
|
-
if (csv.header.length !== expected.length || expected.some((h, i) => csv.header[i] !== h))
|
|
133
|
-
errors.push(`${file}: header mismatch: expected [${expected.join(",")}] got [${csv.header.join(",")}]`);
|
|
134
|
-
|
|
135
|
-
const nameCol = expected[1];
|
|
136
|
-
const seenNames = new Map();
|
|
137
|
-
csv.rows.forEach((row, i) => {
|
|
138
|
-
if (row._cells.length !== expected.length)
|
|
139
|
-
errors.push(`${file} row ${row._line}: has ${row._cells.length} columns, expected ${expected.length}`);
|
|
140
|
-
|
|
141
|
-
if (!/^\d+$/.test(row.id) || parseInt(row.id, 10) !== i + 1)
|
|
142
|
-
errors.push(`${file} row ${row._line}: id "${row.id}" breaks 1..N sequence (expected ${i + 1})`);
|
|
143
|
-
|
|
144
|
-
const name = row[nameCol];
|
|
145
|
-
if (seenNames.has(name)) errors.push(`${file} row ${row._line}: duplicate ${nameCol} "${name}" (first at row ${seenNames.get(name)})`);
|
|
146
|
-
else seenNames.set(name, row._line);
|
|
147
|
-
|
|
148
|
-
if (!VALID_TIERS.has(row.tier))
|
|
149
|
-
errors.push(`${file} row ${row._line}: invalid tier "${row.tier}" (want operator-active|dormant-marketing)`);
|
|
150
|
-
|
|
151
|
-
for (const cell of row._cells) {
|
|
152
|
-
const m = BANNED_GLYPHS.exec(cell);
|
|
153
|
-
if (m) { errors.push(`${file} row ${row._line}: cell contains banned symbol glyph "${m[0]}"`); break; }
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// palettes.csv: theme enum, uppercase 6-digit hex in all 34 color cells + WCAG contrast floors.
|
|
159
|
-
function checkPalettes(csv, errors) {
|
|
160
|
-
for (const row of csv.rows) {
|
|
161
|
-
if (!VALID_PALETTE_THEMES.has(row.theme))
|
|
162
|
-
errors.push(`palettes.csv row ${row._line} (${row.palette}): invalid theme "${row.theme}" (want light|dark)`);
|
|
163
|
-
const valid = {};
|
|
164
|
-
for (const col of PALETTE_COLOR_COLUMNS) {
|
|
165
|
-
const v = row[col];
|
|
166
|
-
if (!UPPER_HEX.test(v)) errors.push(`palettes.csv row ${row._line} (${row.palette}): ${col} "${v}" is not uppercase #RRGGBB`);
|
|
167
|
-
else valid[col] = v;
|
|
168
|
-
}
|
|
169
|
-
for (const [base, fg] of AA_TEXT_PAIRS) {
|
|
170
|
-
if (!valid[base] || !valid[fg]) continue;
|
|
171
|
-
const ratio = contrastRatio(valid[base], valid[fg]);
|
|
172
|
-
if (ratio < 4.5) errors.push(`palettes.csv row ${row._line} (${row.palette}): ${fg} on ${base} contrast ${ratio.toFixed(2)}:1 < 4.5:1`);
|
|
173
|
-
}
|
|
174
|
-
for (const col of GRAPHIC_VS_BACKGROUND) {
|
|
175
|
-
if (!valid[col] || !valid.background) continue;
|
|
176
|
-
const ratio = contrastRatio(valid[col], valid.background);
|
|
177
|
-
if (ratio < 3) errors.push(`palettes.csv row ${row._line} (${row.palette}): ${col} vs background contrast ${ratio.toFixed(2)}:1 < 3:1`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// styles.csv: theme enum + the payload rule — all four payload cells non-empty on operator-active rows, the checklist carrying 3-7 ;-separated items.
|
|
183
|
-
function checkStyles(csv, errors) {
|
|
184
|
-
for (const row of csv.rows) {
|
|
185
|
-
if (!VALID_STYLE_THEMES.has(row.theme))
|
|
186
|
-
errors.push(`styles.csv row ${row._line} (${row.movement}): invalid theme "${row.theme}" (want light|dark|both)`);
|
|
187
|
-
if (row.tier !== "operator-active") continue;
|
|
188
|
-
for (const col of STYLE_PAYLOAD_COLUMNS)
|
|
189
|
-
if (row[col].trim() === "")
|
|
190
|
-
errors.push(`styles.csv row ${row._line} (${row.movement}): operator-active row has empty ${col} payload`);
|
|
191
|
-
const items = row.checklist.split(";").map((s) => s.trim()).filter((s) => s !== "");
|
|
192
|
-
if (row.checklist.trim() !== "" && (items.length < 3 || items.length > 7))
|
|
193
|
-
errors.push(`styles.csv row ${row._line} (${row.movement}): checklist has ${items.length} items (want 3-7)`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// motion.csv: budget enum, archetypes from the closed six, duration/easing cells referencing only legal temporal token names.
|
|
198
|
-
function checkMotion(csv, errors) {
|
|
199
|
-
for (const row of csv.rows) {
|
|
200
|
-
if (!VALID_BUDGETS.has(row.budget))
|
|
201
|
-
errors.push(`motion.csv row ${row._line} (${row.moment}): invalid budget "${row.budget}" (want baseline|brand-forward)`);
|
|
202
|
-
|
|
203
|
-
const archetypes = row.archetypes.split(";").map((s) => s.trim()).filter((s) => s !== "");
|
|
204
|
-
if (archetypes.length === 0)
|
|
205
|
-
errors.push(`motion.csv row ${row._line} (${row.moment}): archetypes cell is empty`);
|
|
206
|
-
for (const a of archetypes)
|
|
207
|
-
if (!VALID_ARCHETYPES.has(a))
|
|
208
|
-
errors.push(`motion.csv row ${row._line} (${row.moment}): unknown archetype "${a}"`);
|
|
209
|
-
|
|
210
|
-
const durations = row.duration_token.match(/duration-[a-z]+|stagger-unit/g) ?? [];
|
|
211
|
-
if (durations.length === 0)
|
|
212
|
-
errors.push(`motion.csv row ${row._line} (${row.moment}): duration_token names no duration token`);
|
|
213
|
-
for (const tok of durations)
|
|
214
|
-
if (!MOTION_DURATION_TOKENS.has(tok))
|
|
215
|
-
errors.push(`motion.csv row ${row._line} (${row.moment}): duration token "${tok}" not in the schema vocabulary (${[...MOTION_DURATION_TOKENS].join(",")})`);
|
|
216
|
-
|
|
217
|
-
const easings = row.easing_token.match(/ease-[a-z]+/g) ?? [];
|
|
218
|
-
if (easings.length === 0)
|
|
219
|
-
errors.push(`motion.csv row ${row._line} (${row.moment}): easing_token names no easing token`);
|
|
220
|
-
for (const tok of easings)
|
|
221
|
-
if (!MOTION_EASING_TOKENS.has(tok))
|
|
222
|
-
errors.push(`motion.csv row ${row._line} (${row.moment}): easing token "${tok}" not in the schema vocabulary (${[...MOTION_EASING_TOKENS].join(",")})`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// charts.csv: operator-active only, --chart-N discipline in series_tokens, N in 1..5 everywhere, no raw hex.
|
|
227
|
-
function checkCharts(csv, errors) {
|
|
228
|
-
for (const row of csv.rows) {
|
|
229
|
-
if (row.tier !== "operator-active")
|
|
230
|
-
errors.push(`charts.csv row ${row._line} (${row.data_shape}): tier must be operator-active (got "${row.tier}")`);
|
|
231
|
-
if (!/--chart-\d/.test(row.series_tokens))
|
|
232
|
-
errors.push(`charts.csv row ${row._line} (${row.data_shape}): series_tokens mentions no --chart-N token`);
|
|
233
|
-
if (RAW_HEX_ANYWHERE.test(row.series_tokens))
|
|
234
|
-
errors.push(`charts.csv row ${row._line} (${row.data_shape}): series_tokens contains raw hex "${RAW_HEX_ANYWHERE.exec(row.series_tokens)[0]}"`);
|
|
235
|
-
for (const cell of row._cells) {
|
|
236
|
-
for (const m of cell.matchAll(/--chart-(\d+)/g)) {
|
|
237
|
-
const n = parseInt(m[1], 10);
|
|
238
|
-
if (n < 1 || n > 5) errors.push(`charts.csv row ${row._line} (${row.data_shape}): --chart-${m[1]} out of range 1..5`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// icons.csv rows against the lucide-names.json registry (kebab -> Pascal).
|
|
245
|
-
function checkIcons(csv, dir, errors) {
|
|
246
|
-
const text = readSafe(join(dir, "lucide-names.json"));
|
|
247
|
-
if (text === null) return; // presence already reported
|
|
248
|
-
let reg;
|
|
249
|
-
try { reg = JSON.parse(text); } catch { errors.push("lucide-names.json does not parse as JSON"); return; }
|
|
250
|
-
for (const key of ["lucide_react_version", "fetched", "source", "icons"])
|
|
251
|
-
if (!(key in reg)) errors.push(`lucide-names.json: missing key "${key}"`);
|
|
252
|
-
if (typeof reg.icons !== "object" || reg.icons === null) { errors.push("lucide-names.json: \"icons\" is not an object"); return; }
|
|
253
|
-
if (!csv) return;
|
|
254
|
-
for (const row of csv.rows) {
|
|
255
|
-
if (!(row.lucide_name in reg.icons))
|
|
256
|
-
errors.push(`icons.csv row ${row._line} (${row.intent}): lucide_name "${row.lucide_name}" not in lucide-names.json`);
|
|
257
|
-
else if (row.import_name !== reg.icons[row.lucide_name])
|
|
258
|
-
errors.push(`icons.csv row ${row._line} (${row.intent}): import_name "${row.import_name}" != registry "${reg.icons[row.lucide_name]}"`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// typography.csv rows against the fonts-known.json registry (family -> known weights), the contrast vocabulary, and the paste-ready payload columns: css-import parses as @fontsource statements whose package set is exactly the row's kebab-cased families, tailwind-config names all three families, display-size-hint non-empty.
|
|
263
|
-
function checkTypography(csv, dir, errors) {
|
|
264
|
-
const text = readSafe(join(dir, "fonts-known.json"));
|
|
265
|
-
if (text === null) return; // presence already reported
|
|
266
|
-
let reg;
|
|
267
|
-
try { reg = JSON.parse(text); } catch { errors.push("fonts-known.json does not parse as JSON"); return; }
|
|
268
|
-
for (const key of ["fetched", "source", "families"])
|
|
269
|
-
if (!(key in reg)) errors.push(`fonts-known.json: missing key "${key}"`);
|
|
270
|
-
if (typeof reg.families !== "object" || reg.families === null) { errors.push("fonts-known.json: \"families\" is not an object"); return; }
|
|
271
|
-
if (!csv) return;
|
|
272
|
-
for (const row of csv.rows) {
|
|
273
|
-
for (const col of ["heading_family", "body_family", "mono_family"])
|
|
274
|
-
if (!(row[col] in reg.families))
|
|
275
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): ${col} "${row[col]}" not in fonts-known.json`);
|
|
276
|
-
for (const [weightsCol, familyCol] of [["heading_weights", "heading_family"], ["body_weights", "body_family"]]) {
|
|
277
|
-
const known = reg.families[row[familyCol]];
|
|
278
|
-
if (!Array.isArray(known)) continue; // family membership error already reported
|
|
279
|
-
for (const tok of row[weightsCol].split(";").map((t) => t.trim()).filter((t) => t !== "")) {
|
|
280
|
-
if (!/^\d+$/.test(tok)) errors.push(`typography.csv row ${row._line} (${row.pairing}): ${weightsCol} entry "${tok}" is not an integer`);
|
|
281
|
-
else if (!known.includes(parseInt(tok, 10)))
|
|
282
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): weight ${tok} not a known weight of ${row[familyCol]} (known: ${known.join(",")})`);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
if (!VALID_TYPE_CONTRASTS.has(row.contrast))
|
|
286
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): invalid contrast "${row.contrast}"`);
|
|
287
|
-
|
|
288
|
-
// The font-binding contract at the data root: every import statement parses, and the imported package set is exactly the row's kebab-cased family set.
|
|
289
|
-
const families = [row.heading_family, row.body_family, row.mono_family];
|
|
290
|
-
const kebabs = new Set(families.map((f) => f.toLowerCase().replace(/ /g, "-")));
|
|
291
|
-
const pkgs = new Set();
|
|
292
|
-
const fragments = row["css-import"].split(";").map((s) => s.trim()).filter((s) => s !== "");
|
|
293
|
-
if (fragments.length === 0)
|
|
294
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): css-import is empty`);
|
|
295
|
-
for (const frag of fragments) {
|
|
296
|
-
const m = FONTSOURCE_IMPORT.exec(frag);
|
|
297
|
-
if (!m) errors.push(`typography.csv row ${row._line} (${row.pairing}): css-import fragment "${frag}" is not an @import '@fontsource/<pkg>/....css' statement`);
|
|
298
|
-
else pkgs.add(m[1]);
|
|
299
|
-
}
|
|
300
|
-
for (const pkg of pkgs)
|
|
301
|
-
if (!kebabs.has(pkg))
|
|
302
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): css-import package "${pkg}" matches none of the row's families`);
|
|
303
|
-
for (const kebab of kebabs)
|
|
304
|
-
if (fragments.length > 0 && !pkgs.has(kebab))
|
|
305
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): family "${kebab}" has no css-import statement`);
|
|
306
|
-
|
|
307
|
-
for (const fam of new Set(families))
|
|
308
|
-
if (!row["tailwind-config"].includes(fam))
|
|
309
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): tailwind-config does not name family "${fam}"`);
|
|
310
|
-
if (row["display-size-hint"].trim() === "")
|
|
311
|
-
errors.push(`typography.csv row ${row._line} (${row.pairing}): display-size-hint is empty`);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// styles.csv / product-router.csv density vocabulary + product-router key_surfaces vocabulary.
|
|
316
|
-
function checkDensityAndSurfaces(file, csv, errors) {
|
|
317
|
-
const densityCol = file === "styles.csv" ? "density_affinity" : "density_tier";
|
|
318
|
-
const nameCol = HEADERS[file][1];
|
|
319
|
-
for (const row of csv.rows) {
|
|
320
|
-
if (!VALID_DENSITIES.has(row[densityCol]))
|
|
321
|
-
errors.push(`${file} row ${row._line} (${row[nameCol]}): invalid ${densityCol} "${row[densityCol]}" (want spacious|standard|dense)`);
|
|
322
|
-
if (file === "product-router.csv" && row.tier === "operator-active") {
|
|
323
|
-
for (const surface of row.key_surfaces.split(",").map((s) => s.trim()).filter((s) => s !== ""))
|
|
324
|
-
if (!VALID_ARCHETYPES.has(surface))
|
|
325
|
-
errors.push(`product-router.csv row ${row._line} (${row.product_type}): unknown key_surface "${surface}"`);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// landing.csv: dormant-marketing on every row.
|
|
331
|
-
function checkLanding(csv, errors) {
|
|
332
|
-
for (const row of csv.rows)
|
|
333
|
-
if (row.tier !== "dormant-marketing")
|
|
334
|
-
errors.push(`landing.csv row ${row._line} (${row.pattern}): tier must be dormant-marketing (got "${row.tier}")`);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// accent-presets.md: the accent table carries exactly 12 scales, each with 7 oklch step cells; returns the parsed scale count for manifest parity (null when unparseable).
|
|
338
|
-
function checkAccentPresets(dir, errors) {
|
|
339
|
-
const text = readSafe(join(dir, "accent-presets.md"));
|
|
340
|
-
if (text === null) return null; // presence already reported
|
|
341
|
-
const lines = text.split("\n");
|
|
342
|
-
const headerIdx = lines.findIndex((l) => /^\|\s*Accent\s*\|/.test(l));
|
|
343
|
-
if (headerIdx === -1) { errors.push("accent-presets.md: accent table header row (| Accent | ...) not found"); return null; }
|
|
344
|
-
const stepLabels = lines[headerIdx].split("|").map((c) => c.trim()).filter((c) => /^\d+$/.test(c));
|
|
345
|
-
if (stepLabels.length !== ACCENT_PRESET_STEPS)
|
|
346
|
-
errors.push(`accent-presets.md: accent table declares ${stepLabels.length} step columns (want ${ACCENT_PRESET_STEPS})`);
|
|
347
|
-
let scales = 0;
|
|
348
|
-
for (let i = headerIdx + 2; i < lines.length && lines[i].startsWith("|"); i++) {
|
|
349
|
-
const cells = lines[i].split("|").slice(1, -1).map((c) => c.trim());
|
|
350
|
-
const name = cells[0] ?? "";
|
|
351
|
-
scales++;
|
|
352
|
-
const steps = cells.filter((c) => /`oklch\([^)]*\)`/.test(c));
|
|
353
|
-
if (steps.length !== ACCENT_PRESET_STEPS)
|
|
354
|
-
errors.push(`accent-presets.md: scale ${name} carries ${steps.length} oklch steps (want ${ACCENT_PRESET_STEPS})`);
|
|
355
|
-
}
|
|
356
|
-
if (scales !== ACCENT_PRESET_SCALES)
|
|
357
|
-
errors.push(`accent-presets.md: accent table carries ${scales} scales (want ${ACCENT_PRESET_SCALES})`);
|
|
358
|
-
return scales;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// manifest.json: datasets entry per CSV with the required keys and rows.total + tier counts matching the actual CSV, plus an accent-presets.md entry whose scales match the parsed table.
|
|
362
|
-
function checkManifest(dir, csvs, accentScales, errors) {
|
|
363
|
-
const text = readSafe(join(dir, "manifest.json"));
|
|
364
|
-
if (text === null) return; // presence already reported
|
|
365
|
-
let manifest;
|
|
366
|
-
try { manifest = JSON.parse(text); } catch { errors.push("manifest.json does not parse as JSON"); return; }
|
|
367
|
-
if (typeof manifest.datasets !== "object" || manifest.datasets === null) { errors.push("manifest.json: missing \"datasets\" object"); return; }
|
|
368
|
-
for (const file of CSV_FILES) {
|
|
369
|
-
const entry = manifest.datasets[file] ?? manifest.datasets[file.replace(/\.csv$/, "")];
|
|
370
|
-
if (entry === undefined) { errors.push(`manifest.json: datasets has no entry for ${file}`); continue; }
|
|
371
|
-
for (const key of ["seed", "research", "quality_bar", "rows", "refresh_trigger"])
|
|
372
|
-
if (!(key in entry)) errors.push(`manifest.json: datasets entry for ${file} missing key "${key}"`);
|
|
373
|
-
if (!entry.rows || typeof entry.rows !== "object") { errors.push(`manifest.json: datasets entry for ${file} has no rows object`); continue; }
|
|
374
|
-
if (!csvs[file]) continue;
|
|
375
|
-
const actual = {
|
|
376
|
-
total: csvs[file].rows.length,
|
|
377
|
-
operator_active: csvs[file].rows.filter((r) => r.tier === "operator-active").length,
|
|
378
|
-
dormant_marketing: csvs[file].rows.filter((r) => r.tier === "dormant-marketing").length,
|
|
379
|
-
};
|
|
380
|
-
for (const key of ["total", "operator_active", "dormant_marketing"]) {
|
|
381
|
-
if (typeof entry.rows[key] !== "number") errors.push(`manifest.json: datasets entry for ${file} has no numeric rows.${key}`);
|
|
382
|
-
else if (entry.rows[key] !== actual[key])
|
|
383
|
-
errors.push(`manifest.json: ${file} rows.${key}=${entry.rows[key]} but the CSV has ${actual[key]}`);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
const accent = manifest.datasets["accent-presets.md"];
|
|
387
|
-
if (accent === undefined) errors.push("manifest.json: datasets has no entry for accent-presets.md");
|
|
388
|
-
else if (typeof accent.scales !== "number") errors.push("manifest.json: accent-presets.md entry has no numeric scales");
|
|
389
|
-
else if (accentScales !== null && accent.scales !== accentScales)
|
|
390
|
-
errors.push(`manifest.json: accent-presets.md scales=${accent.scales} but the table carries ${accentScales}`);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Validate every combination dataset in `dir`; returns a self-integrity-shaped result.
|
|
394
|
-
export function validateCombinations(dir) {
|
|
395
|
-
const name = "combination-data validation";
|
|
396
|
-
if (!existsSync(dir))
|
|
397
|
-
return { name, pass: false, detail: "combinations dir missing", errors: [`${dir} does not exist`] };
|
|
398
|
-
|
|
399
|
-
const errors = [];
|
|
400
|
-
for (const file of REQUIRED_FILES)
|
|
401
|
-
if (!existsSync(join(dir, file))) errors.push(`missing ${file}`);
|
|
402
|
-
|
|
403
|
-
const csvs = {};
|
|
404
|
-
for (const file of CSV_FILES) {
|
|
405
|
-
const csv = loadCsv(dir, file, errors);
|
|
406
|
-
csvs[file] = csv;
|
|
407
|
-
if (csv) checkGeneric(file, csv, errors);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (csvs["palettes.csv"]) checkPalettes(csvs["palettes.csv"], errors);
|
|
411
|
-
if (csvs["styles.csv"]) checkStyles(csvs["styles.csv"], errors);
|
|
412
|
-
if (csvs["motion.csv"]) checkMotion(csvs["motion.csv"], errors);
|
|
413
|
-
if (csvs["charts.csv"]) checkCharts(csvs["charts.csv"], errors);
|
|
414
|
-
checkIcons(csvs["icons.csv"], dir, errors);
|
|
415
|
-
checkTypography(csvs["typography.csv"], dir, errors);
|
|
416
|
-
if (csvs["styles.csv"]) checkDensityAndSurfaces("styles.csv", csvs["styles.csv"], errors);
|
|
417
|
-
if (csvs["product-router.csv"]) checkDensityAndSurfaces("product-router.csv", csvs["product-router.csv"], errors);
|
|
418
|
-
if (csvs["landing.csv"]) checkLanding(csvs["landing.csv"], errors);
|
|
419
|
-
const accentScales = checkAccentPresets(dir, errors);
|
|
420
|
-
checkManifest(dir, csvs, accentScales, errors);
|
|
421
|
-
|
|
422
|
-
const totalRows = CSV_FILES.reduce((n, f) => n + (csvs[f] ? csvs[f].rows.length : 0), 0);
|
|
423
|
-
return { name, pass: errors.length === 0, detail: errors.length ? `${errors.length} issue(s)` : `${CSV_FILES.length} datasets, ${totalRows} rows validated`, errors };
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// CLI entry: node validate-combinations.mjs [--dir <path>]
|
|
427
|
-
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
428
|
-
const args = process.argv.slice(2);
|
|
429
|
-
let dir = DEFAULT_DIR;
|
|
430
|
-
for (let i = 0; i < args.length; i++) {
|
|
431
|
-
if (args[i] === "--dir") dir = resolve(args[++i] || "");
|
|
432
|
-
else if (args[i].startsWith("--dir=")) dir = resolve(args[i].slice("--dir=".length));
|
|
433
|
-
}
|
|
434
|
-
const r = validateCombinations(dir);
|
|
435
|
-
if (r.pass) { console.log(`PASS ${r.name} — ${r.detail}`); process.exit(0); }
|
|
436
|
-
console.error(`FAIL ${r.name} — ${r.detail}`);
|
|
437
|
-
for (const e of r.errors) console.error(` - ${e}`);
|
|
438
|
-
process.exit(1);
|
|
439
|
-
}
|