@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
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
// skills/design/scripts/select-chart.mjs - B1 chart-selection engine: eliminate, rank, refuse
|
|
2
|
+
//
|
|
3
|
+
// One graph question per call. The model authors the data-signature JSON (the judgment
|
|
4
|
+
// half); this script computes elimination, scoring, ranking, and refusal (the mechanical
|
|
5
|
+
// half) over the chart-leaf census at ../data/charts/census.csv. Deterministic: same
|
|
6
|
+
// signature in, same output out. No network, stdlib only.
|
|
7
|
+
//
|
|
8
|
+
// OUTPUT CONTRACT (docs/features/design-skill/07-evaluation-integrity.md §B1 — exact):
|
|
9
|
+
// {
|
|
10
|
+
// "admissible": ["chart-id"],
|
|
11
|
+
// "eliminated": [{ "id": "chart-id", "reasonCode": "elimination-code" }],
|
|
12
|
+
// "ranking": [{ "id": "chart-id", "score": 0, "reasonCodes": ["reason-code"] }],
|
|
13
|
+
// "recommendation": "chart-id-or-null",
|
|
14
|
+
// "refusal": "refusal-code-or-null"
|
|
15
|
+
// }
|
|
16
|
+
//
|
|
17
|
+
// INPUT SIGNATURE (fixed schema; the fuzzy question->schema derivation is the caller's job):
|
|
18
|
+
// {
|
|
19
|
+
// question: string (audit only — no rule reads it),
|
|
20
|
+
// questions: [string] — optional; >1 entries signals a compound question (refusal),
|
|
21
|
+
// structure: "flat|tree|edges|matrix|geo|interval|grid|scalar|text" (default flat;
|
|
22
|
+
// scalar is derived when dimensions are empty and a measure exists),
|
|
23
|
+
// granularity: "raw-observations|pre-aggregated" (default pre-aggregated),
|
|
24
|
+
// rows: number — dataset row count (drives the overplotting regime),
|
|
25
|
+
// measures: [{ id, kind, unit, sign: "positive|signed|fraction", integer, isId, ambiguous }],
|
|
26
|
+
// dimensions: [{ id, kind: "nominal|ordinal|temporal|geo", ordered, cardinality,
|
|
27
|
+
// spacing: "regular|irregular", ambiguous }],
|
|
28
|
+
// target: { value, source } — optional explicit target,
|
|
29
|
+
// semantics: { targetExists, partsFormWhole, orderedProgression, temporalSubject,
|
|
30
|
+
// negativeValues, wholeUnits, uncertainty, mixedUnits },
|
|
31
|
+
// task: { primary, secondary: [<=2] } — optional; derived from schema defaults
|
|
32
|
+
// when absent (explicit caller cues always win over the defaults),
|
|
33
|
+
// ambiguities: [{ field, alternatives }] — optional; non-empty signals refusal,
|
|
34
|
+
// constraints: { viewport: "mobile|tablet|desktop|wide" (default desktop),
|
|
35
|
+
// interactionAllowed, colorVisionSafe, guardOverrides }
|
|
36
|
+
// }
|
|
37
|
+
//
|
|
38
|
+
// REFUSAL CONTRACT — four codes (graph-selection research 02 §7), checked in this order:
|
|
39
|
+
// 1. "unmappable-structure" (R-UNMAPPABLE) structure fits no signature: free prose,
|
|
40
|
+
// a lone non-numeric fact, or an empty signature (no fields at all).
|
|
41
|
+
// 2. "compound-question" (R-COMPOUND) input.questions carries >1 disjoint graph
|
|
42
|
+
// questions; decomposition is the invoker's job — the engine never charts two at once.
|
|
43
|
+
// 3. "ambiguous-schema" (R-AMBIGUOUS) the schema derives two ways that land in
|
|
44
|
+
// different families (any field marked ambiguous, or input.ambiguities non-empty).
|
|
45
|
+
// 4. "no-quantitative-measure" (R-NO-MEASURE) no measure and no countable structure —
|
|
46
|
+
// a bare entity list is a table row-set, not a chart; the engine refuses rather than
|
|
47
|
+
// inventing a count.
|
|
48
|
+
//
|
|
49
|
+
// FIXED OPEN PARAMETERS (the research left these open; fixed here, per 01 §B1):
|
|
50
|
+
// * Scoring constants (research §5 M-table, rescaled to reduce clamp saturation at 100
|
|
51
|
+
// while preserving every ordering relationship: M1 dominant, |M3 match| > |M2|,
|
|
52
|
+
// over-cap ~ -M3): BASE 50, clamp [0,100];
|
|
53
|
+
// M1 native fit: family anchor +20, native +16, fallback (table family) +6
|
|
54
|
+
// M2 encoding: position/length +8, angle/area/color-only -8, else 0
|
|
55
|
+
// M3 primary-task match +12 / mismatch -8 (leaves with no native task: 0)
|
|
56
|
+
// M4 secondary-task match +4 each (<=2)
|
|
57
|
+
// M5 cardinality: comfortable +4, near cap -4, over cap -12 (+ facet escape code)
|
|
58
|
+
// M6 dataset size: rows>=10000 binned +8 / point -8; rows>=1000 hexbin +4
|
|
59
|
+
// M7 decorative/low-precision -8 (pictogram, gauge, streamgraph, radar)
|
|
60
|
+
// M8 flag synergy +6 (temporalSubject->line family; partsFormWhole->part-to-whole
|
|
61
|
+
// family; targetExists->target-native leaves; uncertainty->error-bands)
|
|
62
|
+
// Threshold constants (research §6, fixed by decision inside the evidenced bands):
|
|
63
|
+
// K1 pie slices 6 · K2 bar categories 12 · K3 line series 6 · K4 stacked totals 10
|
|
64
|
+
// K5 overplot 1000 offered / 10000 mandatory · K6 facet panels 20 · K7 grouped
|
|
65
|
+
// series 4 · K8 waffle 10x10 · K9 SPLOM vars 8 · K10 network nodes 300.
|
|
66
|
+
// Hard elimination sits at 2x the comfort cap ("cardinality-exceeded"); between
|
|
67
|
+
// comfort and 2x the leaf is demoted, never killed (T-H: membership is decided only
|
|
68
|
+
// in elimination, so only 2x-destructive overflow counts as an expressiveness kill).
|
|
69
|
+
// * Task facets (research open question 3): primary + <=2 secondary facets are accepted
|
|
70
|
+
// on explicit input.task; schema-default derivation supplies a primary only. Rationale:
|
|
71
|
+
// one question can carry compound facets ("compare regions over time"), but the engine
|
|
72
|
+
// never invents a secondary the caller didn't assert.
|
|
73
|
+
// * Ambiguity handling (research open question 4): R-AMBIGUOUS stays a refusal variant,
|
|
74
|
+
// not a third response type — two response types keep the invoker contract simple
|
|
75
|
+
// (the research's own recommendation, adopted).
|
|
76
|
+
// * Guard overrides (research open question 5): guard variants (dual axes, truncated
|
|
77
|
+
// bar axes, broken axes, 3D) are census-level exclusions — they are design variants,
|
|
78
|
+
// not chart leaves, so there is nothing in the 57-leaf universe to resurrect.
|
|
79
|
+
// constraints.guardOverrides is accepted for forward compatibility and is currently a
|
|
80
|
+
// no-op on this closed output contract; overrides remain a rendering-layer concern
|
|
81
|
+
// recorded by the caller (per the ruling: overridable by explicit flag, logged).
|
|
82
|
+
// * The research's "conditional section" (options that unlock if a flag flips) folds
|
|
83
|
+
// into `eliminated` with flag-gate reason codes ("parts-form-whole-not-asserted",
|
|
84
|
+
// "target-not-asserted", ...) because the ratified 07 §B1 contract is closed — the
|
|
85
|
+
// unlock flag is recoverable from the reason code.
|
|
86
|
+
//
|
|
87
|
+
// CENSUS FLAG SEMANTICS (requires_flags / excludes_flags, |-separated):
|
|
88
|
+
// Flags fall into three classes; requires_flags evaluates per class:
|
|
89
|
+
// ordering class {temporal, ordered-progression} — OR (any one satisfies)
|
|
90
|
+
// structure class {tree, edges, matrix, geo, interval, scalar, grid} — OR
|
|
91
|
+
// semantic class (everything else) — AND (each required)
|
|
92
|
+
// excludes_flags: any listed flag present in the derived input flags eliminates the leaf.
|
|
93
|
+
// Elimination emits one reasonCode per leaf, first match in this fixed precedence:
|
|
94
|
+
// structure-incompatible -> excluded-flag codes -> missing-required-flag codes
|
|
95
|
+
// ("ordered-x-required" for the ordering class, "structure-required" for the structure
|
|
96
|
+
// class, "<flag>-not-asserted"/"-required" for semantic flags) -> signature-mismatch
|
|
97
|
+
// -> cardinality-exceeded -> viewport-too-small.
|
|
98
|
+
|
|
99
|
+
import fs from 'node:fs';
|
|
100
|
+
import path from 'node:path';
|
|
101
|
+
import process from 'node:process';
|
|
102
|
+
import { fileURLToPath } from 'node:url';
|
|
103
|
+
|
|
104
|
+
const VIEWPORT_ORDER = ['mobile', 'tablet', 'desktop', 'wide'];
|
|
105
|
+
const ORDER_FLAGS = new Set(['temporal', 'ordered-progression']);
|
|
106
|
+
const STRUCTURE_FLAGS = new Set(['tree', 'edges', 'matrix', 'geo', 'interval', 'scalar', 'grid']);
|
|
107
|
+
const REFUSING_STRUCTURES = new Set(['text', 'prose', 'text-weights', 'none', 'unknown']);
|
|
108
|
+
|
|
109
|
+
const EXCLUDE_REASON_BY_FLAG = {
|
|
110
|
+
temporal: 'temporal-series-incompatible',
|
|
111
|
+
'negative-values': 'negative-values-incompatible',
|
|
112
|
+
'irregular-temporal': 'irregular-temporal-incompatible',
|
|
113
|
+
'mixed-units': 'mixed-units-incompatible',
|
|
114
|
+
'no-color-encoding': 'color-encoding-inaccessible',
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const REQUIRE_REASON_BY_FLAG = {
|
|
118
|
+
'parts-form-whole': 'parts-form-whole-not-asserted',
|
|
119
|
+
'target-exists': 'target-not-asserted',
|
|
120
|
+
'negative-values': 'negative-values-not-asserted',
|
|
121
|
+
'raw-observations': 'raw-observations-required',
|
|
122
|
+
'whole-units': 'whole-units-required',
|
|
123
|
+
uncertainty: 'uncertainty-not-asserted',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// EL-4: a non-flat structure admits only these leaves (plus any leaf that requires it).
|
|
127
|
+
const STRUCTURE_COMPAT = {
|
|
128
|
+
tree: ['treemap', 'sunburst', 'tree-diagram', 'table', 'table-hybrid'],
|
|
129
|
+
edges: ['sankey', 'chord', 'network-graph', 'table', 'table-hybrid'],
|
|
130
|
+
matrix: ['heatmap', 'chord', 'table', 'table-hybrid'],
|
|
131
|
+
geo: ['choropleth', 'symbol-map', 'flow-map', 'point-map', 'table', 'table-hybrid'],
|
|
132
|
+
interval: ['gantt', 'event-timeline', 'table', 'table-hybrid'],
|
|
133
|
+
scalar: ['kpi-card', 'gauge', 'progress-bar', 'bullet', 'table', 'table-hybrid'],
|
|
134
|
+
grid: ['heatmap', 'table', 'table-hybrid'],
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// M1 tiers: the settled core mappings (research 00 §C-1) anchor each family.
|
|
138
|
+
const FAMILY_ANCHORS = new Set([
|
|
139
|
+
'bar', 'line-basic', 'pie', 'histogram', 'scatter', 'ordered-bar', 'sankey',
|
|
140
|
+
'network-graph', 'choropleth', 'event-timeline', 'kpi-card', 'heatmap', 'candlestick',
|
|
141
|
+
]);
|
|
142
|
+
const FALLBACK_LEAVES = new Set(['table', 'table-hybrid']);
|
|
143
|
+
|
|
144
|
+
// M2: position/length reads most accurately; angle/area/color-only reads worst.
|
|
145
|
+
const POSITION_LEAVES = new Set([
|
|
146
|
+
'bar', 'grouped-bar', 'stacked-bar', 'diverging-bar', 'lollipop', 'dot-plot', 'dumbbell',
|
|
147
|
+
'line-basic', 'line-multi', 'line-with-target', 'slope', 'bump', 'sparkline', 'ordered-bar',
|
|
148
|
+
'pareto', 'waterfall', 'scatter', 'histogram', 'box-plot', 'strip-plot', 'bullet',
|
|
149
|
+
'progress-bar', 'gantt', 'candlestick', 'error-bands',
|
|
150
|
+
]);
|
|
151
|
+
const LOW_PRECISION_LEAVES = new Set([
|
|
152
|
+
'pie', 'waffle', 'treemap', 'sunburst', 'marimekko', 'funnel', 'streamgraph', 'bubble',
|
|
153
|
+
'radar', 'pictogram', 'gauge', 'chord', 'heatmap', 'calendar-heatmap', 'choropleth',
|
|
154
|
+
'symbol-map',
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
// M7: decorative/low-precision forms named by the research.
|
|
158
|
+
const DECORATIVE_LEAVES = new Set(['pictogram', 'gauge', 'streamgraph', 'radar']);
|
|
159
|
+
|
|
160
|
+
// M3/M4 native-task table (research §5, Amar-10 + trend/compare/overview).
|
|
161
|
+
const NATIVE_TASKS = {
|
|
162
|
+
correlate: ['scatter', 'hexbin', 'splom', 'parallel-coordinates', 'bubble'],
|
|
163
|
+
trend: ['line-basic', 'line-multi', 'line-with-target', 'area', 'sparkline'],
|
|
164
|
+
distribution: ['histogram', 'density', 'box-plot', 'violin', 'strip-plot'],
|
|
165
|
+
'retrieve-value': ['table', 'table-hybrid', 'kpi-card'],
|
|
166
|
+
filter: ['table', 'table-hybrid'],
|
|
167
|
+
extremum: ['bar', 'ordered-bar', 'pareto'],
|
|
168
|
+
sort: ['ordered-bar', 'pareto', 'bar'],
|
|
169
|
+
compare: ['bar', 'grouped-bar', 'dot-plot', 'slope'],
|
|
170
|
+
cluster: ['scatter', 'heatmap'],
|
|
171
|
+
anomalies: ['scatter', 'box-plot'],
|
|
172
|
+
range: ['dumbbell', 'box-plot', 'gantt'],
|
|
173
|
+
derive: ['table', 'waterfall'],
|
|
174
|
+
overview: ['bar', 'line-basic', 'heatmap'],
|
|
175
|
+
};
|
|
176
|
+
const NATIVE_TASKS_BY_LEAF = (() => {
|
|
177
|
+
const byLeaf = new Map();
|
|
178
|
+
for (const [task, leaves] of Object.entries(NATIVE_TASKS)) {
|
|
179
|
+
for (const leaf of leaves) {
|
|
180
|
+
if (!byLeaf.has(leaf)) byLeaf.set(leaf, new Set());
|
|
181
|
+
byLeaf.get(leaf).add(task);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return byLeaf;
|
|
185
|
+
})();
|
|
186
|
+
|
|
187
|
+
// M5 cardinality rules per leaf: comfort cap (K constants), hard kill at 2x comfort
|
|
188
|
+
// unless the census constraint is itself hard (slope's 2-3 x positions).
|
|
189
|
+
const CARDINALITY_RULES = {
|
|
190
|
+
pie: { kind: 'categories', comfort: 6 }, // K1
|
|
191
|
+
waffle: { kind: 'categories', comfort: 6 }, // mirrors K1
|
|
192
|
+
bar: { kind: 'categories', comfort: 12, demoteOnly: true }, // K2
|
|
193
|
+
'ordered-bar': { kind: 'categories', comfort: 12, demoteOnly: true },
|
|
194
|
+
lollipop: { kind: 'categories', comfort: 12, demoteOnly: true },
|
|
195
|
+
'dot-plot': { kind: 'categories', comfort: 12, demoteOnly: true },
|
|
196
|
+
'grouped-bar': { kind: 'series', comfort: 4 }, // K7
|
|
197
|
+
'stacked-bar': { kind: 'series', comfort: 10 }, // K4
|
|
198
|
+
'line-multi': { kind: 'series', comfort: 6 }, // K3
|
|
199
|
+
bump: { kind: 'series', comfort: 6 },
|
|
200
|
+
slope: { kind: 'x-positions', comfort: 3, hard: 3 },
|
|
201
|
+
radar: { kind: 'categories', comfort: 3 },
|
|
202
|
+
'network-graph': { kind: 'nodes', comfort: 150, hard: 300 }, // K10
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// M6 dataset-size regimes (K5).
|
|
206
|
+
const BINNED_LEAVES = new Set(['hexbin', 'histogram', 'density', 'box-plot', 'heatmap', 'calendar-heatmap']);
|
|
207
|
+
const POINT_LEAVES = new Set(['scatter', 'strip-plot', 'bubble']);
|
|
208
|
+
const OVERPLOT_OFFERED = 1000;
|
|
209
|
+
const OVERPLOT_MANDATORY = 10000;
|
|
210
|
+
|
|
211
|
+
// M8 synergy targets beyond the family-level ones.
|
|
212
|
+
const TARGET_NATIVE_LEAVES = new Set(['progress-bar', 'bullet', 'gauge', 'line-with-target']);
|
|
213
|
+
|
|
214
|
+
const SCORE = {
|
|
215
|
+
base: 50,
|
|
216
|
+
m1Anchor: 20,
|
|
217
|
+
m1Native: 16,
|
|
218
|
+
m1Fallback: 6,
|
|
219
|
+
m2Position: 8,
|
|
220
|
+
m2LowPrecision: -8,
|
|
221
|
+
m3Match: 12,
|
|
222
|
+
m3Mismatch: -8,
|
|
223
|
+
m4Secondary: 4,
|
|
224
|
+
m5Comfortable: 4,
|
|
225
|
+
m5NearCap: -4,
|
|
226
|
+
m5OverCap: -12,
|
|
227
|
+
m6Binned: 8,
|
|
228
|
+
m6Overplot: -8,
|
|
229
|
+
m6HexbinOffered: 4,
|
|
230
|
+
m7Decorative: -8,
|
|
231
|
+
m8Synergy: 6,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Parses one CSV line honoring double-quoted fields.
|
|
235
|
+
function parseCsvLine(line) {
|
|
236
|
+
const cells = [];
|
|
237
|
+
let cell = '';
|
|
238
|
+
let inQuotes = false;
|
|
239
|
+
for (let i = 0; i < line.length; i += 1) {
|
|
240
|
+
const ch = line[i];
|
|
241
|
+
if (inQuotes) {
|
|
242
|
+
if (ch === '"' && line[i + 1] === '"') { cell += '"'; i += 1; }
|
|
243
|
+
else if (ch === '"') inQuotes = false;
|
|
244
|
+
else cell += ch;
|
|
245
|
+
} else if (ch === '"') inQuotes = true;
|
|
246
|
+
else if (ch === ',') { cells.push(cell); cell = ''; }
|
|
247
|
+
else cell += ch;
|
|
248
|
+
}
|
|
249
|
+
cells.push(cell);
|
|
250
|
+
return cells;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function parseCensusCsv(text) {
|
|
254
|
+
const lines = text.split(/\r?\n/).filter((line) => line.trim() !== '');
|
|
255
|
+
const header = parseCsvLine(lines[0]).map((h) => h.trim());
|
|
256
|
+
const expected = ['id', 'family', 'min_measures', 'max_measures', 'min_dims', 'max_dims', 'requires_flags', 'excludes_flags', 'viewport_min', 'notes'];
|
|
257
|
+
if (header.join(',') !== expected.join(',')) {
|
|
258
|
+
throw new Error(`census header mismatch: expected "${expected.join(',')}" got "${header.join(',')}"`);
|
|
259
|
+
}
|
|
260
|
+
return lines.slice(1).map((line, index) => {
|
|
261
|
+
const cells = parseCsvLine(line);
|
|
262
|
+
const row = {};
|
|
263
|
+
header.forEach((key, i) => { row[key] = (cells[i] ?? '').trim(); });
|
|
264
|
+
return {
|
|
265
|
+
id: row.id,
|
|
266
|
+
family: row.family,
|
|
267
|
+
minMeasures: Number(row.min_measures),
|
|
268
|
+
maxMeasures: Number(row.max_measures),
|
|
269
|
+
minDims: Number(row.min_dims),
|
|
270
|
+
maxDims: Number(row.max_dims),
|
|
271
|
+
requiresFlags: row.requires_flags === '' ? [] : row.requires_flags.split('|').map((f) => f.trim()),
|
|
272
|
+
excludesFlags: row.excludes_flags === '' ? [] : row.excludes_flags.split('|').map((f) => f.trim()),
|
|
273
|
+
viewportMin: row.viewport_min === '' ? 'mobile' : row.viewport_min,
|
|
274
|
+
notes: row.notes,
|
|
275
|
+
order: index,
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function loadDefaultCensus() {
|
|
281
|
+
const censusPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'data', 'charts', 'census.csv');
|
|
282
|
+
return parseCensusCsv(fs.readFileSync(censusPath, 'utf8'));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Normalizes loose dimension-kind aliases onto the N/O/Q/T backbone.
|
|
286
|
+
function dimensionKind(dim) {
|
|
287
|
+
const kind = dim.kind || 'nominal';
|
|
288
|
+
if (kind === 'categorical') return 'nominal';
|
|
289
|
+
return kind;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function effectiveMeasures(input) {
|
|
293
|
+
return (input.measures || []).filter((m) => m.isId !== true);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Derives the auditable flag set the census rows are gated on.
|
|
297
|
+
function deriveFlags(input) {
|
|
298
|
+
const dims = input.dimensions || [];
|
|
299
|
+
const measures = effectiveMeasures(input);
|
|
300
|
+
const semantics = input.semantics || {};
|
|
301
|
+
const constraints = input.constraints || {};
|
|
302
|
+
const structure = deriveStructure(input);
|
|
303
|
+
const flags = new Set();
|
|
304
|
+
|
|
305
|
+
if (dims.some((d) => dimensionKind(d) === 'temporal')) flags.add('temporal');
|
|
306
|
+
if (semantics.orderedProgression === true || dims.some((d) => d.ordered === true && dimensionKind(d) !== 'temporal')) {
|
|
307
|
+
flags.add('ordered-progression');
|
|
308
|
+
}
|
|
309
|
+
if (semantics.partsFormWhole === true) flags.add('parts-form-whole');
|
|
310
|
+
if (semantics.targetExists === true || input.target != null) flags.add('target-exists');
|
|
311
|
+
if (semantics.negativeValues === true || measures.some((m) => m.sign === 'signed')) flags.add('negative-values');
|
|
312
|
+
if ((input.granularity || 'pre-aggregated') === 'raw-observations') flags.add('raw-observations');
|
|
313
|
+
if (semantics.wholeUnits === true
|
|
314
|
+
|| (measures.length > 0 && measures.every((m) => m.kind === 'count' || m.unit === 'count' || m.integer === true))) {
|
|
315
|
+
flags.add('whole-units');
|
|
316
|
+
}
|
|
317
|
+
if (dims.some((d) => dimensionKind(d) === 'temporal' && d.spacing === 'irregular')) flags.add('irregular-temporal');
|
|
318
|
+
if (semantics.uncertainty === true || semantics.uncertaintyExists === true) flags.add('uncertainty');
|
|
319
|
+
if (semantics.mixedUnits === true
|
|
320
|
+
|| (measures.length > 1 && new Set(measures.map((m) => m.unit ?? m.kind ?? '')).size > 1)) {
|
|
321
|
+
flags.add('mixed-units');
|
|
322
|
+
}
|
|
323
|
+
const accessibility = constraints.accessibility;
|
|
324
|
+
if (constraints.colorVisionSafe === true
|
|
325
|
+
|| accessibility === 'color-independent'
|
|
326
|
+
|| (Array.isArray(accessibility) && accessibility.includes('color-independent'))) {
|
|
327
|
+
flags.add('no-color-encoding');
|
|
328
|
+
}
|
|
329
|
+
if (STRUCTURE_FLAGS.has(structure)) flags.add(structure);
|
|
330
|
+
return { flags, structure };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function deriveStructure(input) {
|
|
334
|
+
if (input.structure) return input.structure;
|
|
335
|
+
const dims = input.dimensions || [];
|
|
336
|
+
const measures = effectiveMeasures(input);
|
|
337
|
+
if (dims.length === 0 && measures.length > 0) return 'scalar';
|
|
338
|
+
return 'flat';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Refusal check — fires instead of a ranking; order is part of the contract (header).
|
|
342
|
+
function deriveRefusal(input, structure) {
|
|
343
|
+
const dims = input.dimensions || [];
|
|
344
|
+
const measures = effectiveMeasures(input);
|
|
345
|
+
const allFields = [...(input.measures || []), ...dims];
|
|
346
|
+
if (REFUSING_STRUCTURES.has(structure)) return 'unmappable-structure';
|
|
347
|
+
if (structure === 'flat' && measures.length === 0 && dims.length === 0 && input.target == null) {
|
|
348
|
+
return 'unmappable-structure';
|
|
349
|
+
}
|
|
350
|
+
if (Array.isArray(input.questions) && input.questions.length > 1) return 'compound-question';
|
|
351
|
+
if ((Array.isArray(input.ambiguities) && input.ambiguities.length > 0)
|
|
352
|
+
|| allFields.some((f) => f.ambiguous === true)) {
|
|
353
|
+
return 'ambiguous-schema';
|
|
354
|
+
}
|
|
355
|
+
if (measures.length === 0 && structure === 'flat'
|
|
356
|
+
&& !dims.some((d) => ['temporal', 'geo'].includes(dimensionKind(d)))) {
|
|
357
|
+
return 'no-quantitative-measure';
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Schema-default task derivation (the engine's half of the two-stage derivation;
|
|
363
|
+
// explicit query-cue extraction is the caller's half and arrives as input.task).
|
|
364
|
+
function deriveTask(input, flags) {
|
|
365
|
+
if (input.task && input.task.primary) {
|
|
366
|
+
return {
|
|
367
|
+
primary: input.task.primary,
|
|
368
|
+
secondary: (input.task.secondary || []).slice(0, 2),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const dims = input.dimensions || [];
|
|
372
|
+
const measures = effectiveMeasures(input);
|
|
373
|
+
const catDims = dims.filter((d) => ['nominal', 'ordinal'].includes(dimensionKind(d)));
|
|
374
|
+
let primary;
|
|
375
|
+
if (dims.length === 0) primary = flags.has('target-exists') ? 'compare' : 'retrieve-value';
|
|
376
|
+
else if (measures.length >= 2 && flags.has('raw-observations')) primary = 'correlate';
|
|
377
|
+
else if (flags.has('temporal') && measures.length >= 1) primary = 'trend';
|
|
378
|
+
else if (measures.length === 1 && flags.has('raw-observations') && catDims.length <= 1) primary = 'distribution';
|
|
379
|
+
else if (measures.length >= 2) primary = 'correlate';
|
|
380
|
+
else if (catDims.length >= 1 && measures.length >= 1) primary = 'compare';
|
|
381
|
+
else primary = 'overview';
|
|
382
|
+
return { primary, secondary: [] };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Resolves the cardinality the leaf's M5 rule inspects.
|
|
386
|
+
function cardinalityFor(rule, input) {
|
|
387
|
+
const dims = input.dimensions || [];
|
|
388
|
+
const catDims = dims.filter((d) => ['nominal', 'ordinal'].includes(dimensionKind(d)));
|
|
389
|
+
const orderedDims = dims.filter((d) => dimensionKind(d) === 'temporal' || d.ordered === true);
|
|
390
|
+
const cards = (list) => list.map((d) => d.cardinality).filter((c) => Number.isFinite(c));
|
|
391
|
+
switch (rule.kind) {
|
|
392
|
+
case 'categories': {
|
|
393
|
+
const c = cards(catDims.length > 0 ? catDims : dims);
|
|
394
|
+
return c.length > 0 ? c[0] : undefined;
|
|
395
|
+
}
|
|
396
|
+
case 'series': {
|
|
397
|
+
const nonOrdered = dims.filter((d) => !(dimensionKind(d) === 'temporal' || d.ordered === true));
|
|
398
|
+
if (orderedDims.length > 0 && nonOrdered.length > 0) {
|
|
399
|
+
const c = cards(nonOrdered);
|
|
400
|
+
return c.length > 0 ? Math.max(...c) : undefined;
|
|
401
|
+
}
|
|
402
|
+
const c = cards(catDims);
|
|
403
|
+
return c.length >= 2 ? Math.min(...c) : c[0];
|
|
404
|
+
}
|
|
405
|
+
case 'x-positions': {
|
|
406
|
+
const c = cards(orderedDims);
|
|
407
|
+
return c.length > 0 ? c[0] : undefined;
|
|
408
|
+
}
|
|
409
|
+
case 'nodes': {
|
|
410
|
+
const c = cards(dims);
|
|
411
|
+
return c.length > 0 ? Math.max(...c) : undefined;
|
|
412
|
+
}
|
|
413
|
+
default:
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Elimination — one reasonCode per leaf, fixed precedence (see header).
|
|
419
|
+
function eliminationReason(leaf, input, flags, structure, viewport) {
|
|
420
|
+
const measures = effectiveMeasures(input);
|
|
421
|
+
const dims = input.dimensions || [];
|
|
422
|
+
|
|
423
|
+
if (structure !== 'flat' && STRUCTURE_COMPAT[structure]
|
|
424
|
+
&& !STRUCTURE_COMPAT[structure].includes(leaf.id)
|
|
425
|
+
&& !leaf.requiresFlags.includes(structure)) {
|
|
426
|
+
return 'structure-incompatible';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
for (const flag of leaf.excludesFlags) {
|
|
430
|
+
if (flags.has(flag)) return EXCLUDE_REASON_BY_FLAG[flag] || `${flag}-incompatible`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const orderingRequired = leaf.requiresFlags.filter((f) => ORDER_FLAGS.has(f));
|
|
434
|
+
if (orderingRequired.length > 0 && !orderingRequired.some((f) => flags.has(f))) {
|
|
435
|
+
return 'ordered-x-required';
|
|
436
|
+
}
|
|
437
|
+
const structureRequired = leaf.requiresFlags.filter((f) => STRUCTURE_FLAGS.has(f));
|
|
438
|
+
if (structureRequired.length > 0 && !structureRequired.some((f) => flags.has(f))) {
|
|
439
|
+
return 'structure-required';
|
|
440
|
+
}
|
|
441
|
+
for (const flag of leaf.requiresFlags) {
|
|
442
|
+
if (ORDER_FLAGS.has(flag) || STRUCTURE_FLAGS.has(flag)) continue;
|
|
443
|
+
if (!flags.has(flag)) return REQUIRE_REASON_BY_FLAG[flag] || `${flag}-not-asserted`;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (measures.length < leaf.minMeasures || measures.length > leaf.maxMeasures
|
|
447
|
+
|| dims.length < leaf.minDims || dims.length > leaf.maxDims) {
|
|
448
|
+
return 'signature-mismatch';
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const rule = CARDINALITY_RULES[leaf.id];
|
|
452
|
+
if (rule && !rule.demoteOnly) {
|
|
453
|
+
const card = cardinalityFor(rule, input);
|
|
454
|
+
const hard = rule.hard ?? rule.comfort * 2;
|
|
455
|
+
if (card !== undefined && card > hard) return 'cardinality-exceeded';
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (VIEWPORT_ORDER.indexOf(viewport) < VIEWPORT_ORDER.indexOf(leaf.viewportMin)) {
|
|
459
|
+
return 'viewport-too-small';
|
|
460
|
+
}
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Scoring — additive over the fixed M-table; every weight is a constant (header).
|
|
465
|
+
function scoreLeaf(leaf, input, flags, task) {
|
|
466
|
+
let score = SCORE.base;
|
|
467
|
+
const reasonCodes = [];
|
|
468
|
+
|
|
469
|
+
if (FAMILY_ANCHORS.has(leaf.id)) { score += SCORE.m1Anchor; reasonCodes.push('m1-native-anchor'); }
|
|
470
|
+
else if (FALLBACK_LEAVES.has(leaf.id)) { score += SCORE.m1Fallback; reasonCodes.push('m1-fallback'); }
|
|
471
|
+
else { score += SCORE.m1Native; reasonCodes.push('m1-native-fit'); }
|
|
472
|
+
|
|
473
|
+
if (POSITION_LEAVES.has(leaf.id)) { score += SCORE.m2Position; reasonCodes.push('m2-position-encoding'); }
|
|
474
|
+
else if (LOW_PRECISION_LEAVES.has(leaf.id)) { score += SCORE.m2LowPrecision; reasonCodes.push('m2-low-precision-encoding'); }
|
|
475
|
+
|
|
476
|
+
const nativeTasks = NATIVE_TASKS_BY_LEAF.get(leaf.id);
|
|
477
|
+
if (nativeTasks && task.primary) {
|
|
478
|
+
if (nativeTasks.has(task.primary)) { score += SCORE.m3Match; reasonCodes.push(`m3-task-match:${task.primary}`); }
|
|
479
|
+
else { score += SCORE.m3Mismatch; reasonCodes.push('m3-task-mismatch'); }
|
|
480
|
+
for (const secondary of task.secondary) {
|
|
481
|
+
if (nativeTasks.has(secondary)) { score += SCORE.m4Secondary; reasonCodes.push(`m4-secondary-task-match:${secondary}`); }
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const rule = CARDINALITY_RULES[leaf.id];
|
|
486
|
+
if (rule) {
|
|
487
|
+
const card = cardinalityFor(rule, input);
|
|
488
|
+
if (card !== undefined) {
|
|
489
|
+
if (card <= rule.comfort) { score += SCORE.m5Comfortable; reasonCodes.push('m5-cardinality-comfortable'); }
|
|
490
|
+
else if (card <= (rule.hard ?? rule.comfort * 2)) { score += SCORE.m5NearCap; reasonCodes.push('m5-cardinality-near-cap'); }
|
|
491
|
+
else { score += SCORE.m5OverCap; reasonCodes.push('m5-cardinality-over-cap', 'op-facet-suggested'); }
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const rows = Number.isFinite(input.rows) ? input.rows : undefined;
|
|
496
|
+
if (rows !== undefined) {
|
|
497
|
+
if (rows >= OVERPLOT_MANDATORY) {
|
|
498
|
+
if (BINNED_LEAVES.has(leaf.id)) { score += SCORE.m6Binned; reasonCodes.push('m6-binned-boost'); }
|
|
499
|
+
if (POINT_LEAVES.has(leaf.id)) { score += SCORE.m6Overplot; reasonCodes.push('m6-overplot-penalty'); }
|
|
500
|
+
} else if (rows >= OVERPLOT_OFFERED && leaf.id === 'hexbin') {
|
|
501
|
+
score += SCORE.m6HexbinOffered;
|
|
502
|
+
reasonCodes.push('m6-binned-boost');
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (DECORATIVE_LEAVES.has(leaf.id)) { score += SCORE.m7Decorative; reasonCodes.push('m7-decorative-penalty'); }
|
|
507
|
+
|
|
508
|
+
const semantics = input.semantics || {};
|
|
509
|
+
if (semantics.temporalSubject === true && leaf.family === 'line') {
|
|
510
|
+
score += SCORE.m8Synergy; reasonCodes.push('m8-flag-synergy:temporal-subject');
|
|
511
|
+
}
|
|
512
|
+
if (flags.has('parts-form-whole') && leaf.family === 'part-to-whole') {
|
|
513
|
+
score += SCORE.m8Synergy; reasonCodes.push('m8-flag-synergy:parts-form-whole');
|
|
514
|
+
}
|
|
515
|
+
if (flags.has('target-exists') && TARGET_NATIVE_LEAVES.has(leaf.id)) {
|
|
516
|
+
score += SCORE.m8Synergy; reasonCodes.push('m8-flag-synergy:target-exists');
|
|
517
|
+
}
|
|
518
|
+
if (flags.has('uncertainty') && leaf.id === 'error-bands') {
|
|
519
|
+
score += SCORE.m8Synergy; reasonCodes.push('m8-flag-synergy:uncertainty');
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return { score: Math.max(0, Math.min(100, score)), reasonCodes };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// The B1 decision function — one engine, two callers (CLI here, contract runner imports).
|
|
526
|
+
export function selectChart(input, censusRows) {
|
|
527
|
+
if (input == null || typeof input !== 'object' || Array.isArray(input)) {
|
|
528
|
+
throw new Error('selectChart: input must be a data-signature object');
|
|
529
|
+
}
|
|
530
|
+
const census = censusRows ?? loadDefaultCensus();
|
|
531
|
+
const { flags, structure } = deriveFlags(input);
|
|
532
|
+
|
|
533
|
+
const refusal = deriveRefusal(input, structure);
|
|
534
|
+
if (refusal !== null) {
|
|
535
|
+
return { admissible: [], eliminated: [], ranking: [], recommendation: null, refusal };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const viewport = (input.constraints && input.constraints.viewport) || 'desktop';
|
|
539
|
+
const task = deriveTask(input, flags);
|
|
540
|
+
|
|
541
|
+
const admissibleLeaves = [];
|
|
542
|
+
const eliminated = [];
|
|
543
|
+
for (const leaf of census) {
|
|
544
|
+
const reasonCode = eliminationReason(leaf, input, flags, structure, viewport);
|
|
545
|
+
if (reasonCode !== null) eliminated.push({ id: leaf.id, reasonCode });
|
|
546
|
+
else admissibleLeaves.push(leaf);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const ranking = admissibleLeaves
|
|
550
|
+
.map((leaf) => ({ leaf, ...scoreLeaf(leaf, input, flags, task) }))
|
|
551
|
+
.sort((a, b) => (b.score - a.score) || (a.leaf.order - b.leaf.order))
|
|
552
|
+
.map((entry) => ({ id: entry.leaf.id, score: entry.score, reasonCodes: entry.reasonCodes }));
|
|
553
|
+
|
|
554
|
+
return {
|
|
555
|
+
admissible: admissibleLeaves.map((leaf) => leaf.id),
|
|
556
|
+
eliminated,
|
|
557
|
+
ranking,
|
|
558
|
+
recommendation: ranking.length > 0 ? ranking[0].id : null,
|
|
559
|
+
refusal: null,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Reads the whole of stdin (used when no input path is given).
|
|
564
|
+
function readStdin() {
|
|
565
|
+
return fs.readFileSync(0, 'utf8');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function main() {
|
|
569
|
+
const inputPath = process.argv[2];
|
|
570
|
+
let raw;
|
|
571
|
+
try {
|
|
572
|
+
raw = inputPath ? fs.readFileSync(inputPath, 'utf8') : readStdin();
|
|
573
|
+
} catch (error) {
|
|
574
|
+
console.error(`select-chart: cannot read input: ${error.message}`);
|
|
575
|
+
process.exit(1);
|
|
576
|
+
}
|
|
577
|
+
let input;
|
|
578
|
+
try {
|
|
579
|
+
input = JSON.parse(raw);
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.error(`select-chart: input is not valid JSON: ${error.message}`);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
let output;
|
|
585
|
+
try {
|
|
586
|
+
output = selectChart(input);
|
|
587
|
+
} catch (error) {
|
|
588
|
+
console.error(`select-chart: ${error.message}`);
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
595
|
+
main();
|
|
596
|
+
}
|