📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-08-14 17:16:04 +08:00
parent ad5eeaaf5b
commit 14e7209e78
283 changed files with 201717 additions and 13469 deletions
@@ -0,0 +1,36 @@
{
"kind": "webfonts#webfontList",
"items": [
{
"family": "Zeta Serif",
"variants": ["700italic", "regular", "700"],
"subsets": ["latin-ext", "latin"],
"version": "v2",
"lastModified": "2025-06-02",
"files": {
"regular": "https://fonts.gstatic.com/zeta-regular.ttf",
"700": "https://fonts.gstatic.com/zeta-700.ttf",
"700italic": "https://fonts.gstatic.com/zeta-700-italic.ttf"
},
"category": "serif",
"kind": "webfonts#webfont"
},
{
"family": "Alpha Sans",
"variants": ["regular", "italic", "500"],
"subsets": ["vietnamese", "latin", "latin"],
"version": "v4",
"lastModified": "2025-01-03",
"files": {
"regular": "https://fonts.gstatic.com/alpha-regular.ttf",
"italic": "https://fonts.gstatic.com/alpha-italic.ttf",
"500": "https://fonts.gstatic.com/alpha-500.ttf"
},
"category": "sans-serif",
"kind": "webfonts#webfont",
"axes": [
{"tag": "wght", "start": 100, "end": 900}
]
}
]
}
@@ -0,0 +1,33 @@
{
"axisRegistry": [],
"familyMetadataList": [
{
"family": "Zeta Serif", "displayName": null, "category": "Serif", "stroke": "Serif",
"classifications": ["Display"], "size": 1000, "subsets": ["menu", "latin-ext", "latin"],
"fonts": {
"400": {"thickness": 5, "slant": 1, "width": 7, "lineHeight": 1.2},
"700": {"thickness": 7, "slant": 1, "width": 7, "lineHeight": 1.2},
"700i": {"thickness": 7, "slant": 4, "width": 7, "lineHeight": 1.2}
}, "axes": [], "designers": ["Zeta Studio"],
"lastModified": "2025-06-02", "dateAdded": "2018-04-09", "popularity": 90,
"trending": 11, "defaultSort": 9, "androidFragment": null, "isNoto": false,
"colorCapabilities": [], "primaryScript": "", "primaryLanguage": "", "isOpenSource": true,
"isBrandFont": false, "languages": []
},
{
"family": "Alpha Sans", "displayName": null, "category": "Sans Serif", "stroke": "Sans Serif",
"classifications": ["Geometric"], "size": 2000, "subsets": ["menu", "vietnamese", "latin"],
"fonts": {
"400": {"thickness": 5, "slant": 1, "width": 7, "lineHeight": 1.2},
"400i": {"thickness": 5, "slant": 4, "width": 7, "lineHeight": 1.2},
"500": {"thickness": 6, "slant": 1, "width": 7, "lineHeight": 1.2}
},
"axes": [{"tag": "wght", "min": 100.0, "max": 900.0, "defaultValue": 400.0}],
"designers": ["Ada Type", "Binh Fonts"], "lastModified": "2025-01-03",
"dateAdded": "2020-02-20", "popularity": 42, "trending": 7, "defaultSort": 4,
"androidFragment": null, "isNoto": false, "colorCapabilities": [], "primaryScript": "",
"primaryLanguage": "", "isOpenSource": true, "isBrandFont": false, "languages": []
}
],
"promotedScript": []
}
@@ -0,0 +1,3 @@
Family,Category,Stroke,Classifications,Keywords,Styles,Variable Axes,Subsets,Designers,Popularity Rank,Trending Rank,Is Noto,Date Added,Last Modified,Google Fonts URL
Alpha Sans,Sans Serif,Sans Serif,Geometric,reviewed clean keywords,400,,,Old Designer,42,7,No,2020-02-20,2024-01-01,https://fonts.google.com/specimen/Alpha+Sans
Zeta Serif,Serif,Serif,Display,reviewed editorial keywords,400,,,Old Studio,90,11,No,2018-04-09,2024-01-01,https://fonts.google.com/specimen/Zeta+Serif
1 Family Category Stroke Classifications Keywords Styles Variable Axes Subsets Designers Popularity Rank Trending Rank Is Noto Date Added Last Modified Google Fonts URL
2 Alpha Sans Sans Serif Sans Serif Geometric reviewed clean keywords 400 Old Designer 42 7 No 2020-02-20 2024-01-01 https://fonts.google.com/specimen/Alpha+Sans
3 Zeta Serif Serif Serif Display reviewed editorial keywords 400 Old Studio 90 11 No 2018-04-09 2024-01-01 https://fonts.google.com/specimen/Zeta+Serif
@@ -0,0 +1,17 @@
{
"families": [
{
"name": "Zeta Serif",
"designer": "Zeta Studio",
"license": "APACHE2",
"date_added": "2018-04-09"
},
{
"name": "Alpha Sans",
"designer": ["Ada Type", "Binh Fonts"],
"license": "OFL",
"date_added": "2020-02-20"
}
],
"excludedFamilies": []
}
@@ -0,0 +1,7 @@
{
"families": {
"Alpha Sans": {
"Keywords": "approved override keywords"
}
}
}
@@ -0,0 +1,4 @@
No,Category,Icon Name,Keywords,Library,Import Code,Usage,Best For,Style,Semantic Role,Allowed Contexts
1,Navigation,arrow-left,back,Phosphor,import { ArrowLeft } from '@phosphor-icons/react',Example,Back,Outline,interactive,interactive
2,Nature,acorn,nut,Phosphor,import { Acorn } from '@phosphor-icons/react',Example,Acorn,Outline,meaningful,meaningful
3,Guideline,example,guidance,Heroicons,import { BeakerIcon } from '@heroicons/react/24/outline',Example,Example,Outline,guideline,meaningful
1 No Category Icon Name Keywords Library Import Code Usage Best For Style Semantic Role Allowed Contexts
2 1 Navigation arrow-left back Phosphor import { ArrowLeft } from '@phosphor-icons/react' Example Back Outline interactive interactive
3 2 Nature acorn nut Phosphor import { Acorn } from '@phosphor-icons/react' Example Acorn Outline meaningful meaningful
4 3 Guideline example guidance Heroicons import { BeakerIcon } from '@heroicons/react/24/outline' Example Example Outline guideline meaningful
@@ -0,0 +1,23 @@
[
{
"name": "acorn",
"pascal_name": "Acorn",
"codepoint": 62002,
"categories": ["animals", "nature"],
"figma_category": "weather & nature",
"tags": ["savings", "food"],
"published_in": 1.2,
"updated_in": 2.0
},
{
"name": "arrow-left",
"pascal_name": "ArrowLeft",
"alias": {"name": "back-arrow", "pascal_name": "BackArrow"},
"codepoint": 62000,
"categories": ["arrows", "navigation", "arrows"],
"figma_category": "arrows",
"tags": ["previous", "back"],
"published_in": 1.0,
"updated_in": 2.1
}
]
@@ -0,0 +1,13 @@
{
"name": "@phosphor-icons/core",
"version": "2.1.1",
"license": "MIT",
"exports": {
"./thin/*.svg": "./assets/thin/*.svg",
"./light/*.svg": "./assets/light/*.svg",
"./regular/*.svg": "./assets/regular/*.svg",
"./bold/*.svg": "./assets/bold/*.svg",
"./fill/*.svg": "./assets/fill/*.svg",
"./duotone/*.svg": "./assets/duotone/*.svg"
}
}
@@ -0,0 +1,4 @@
{
"client": ["Acorn", "ArrowLeft", "IconContext"],
"ssr": ["Acorn", "ArrowLeft"]
}
@@ -0,0 +1,5 @@
{
"name": "@phosphor-icons/react",
"version": "2.1.10",
"license": "MIT"
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,111 @@
{
"schemaVersion": 1,
"fixtureRevision": "97eb2a2",
"gradeSemantics": {
"2": "Directly answers the query and is an expected best result.",
"1": "Useful and acceptable, but less specific than a grade-2 result.",
"0": "Not relevant. Every returned row not listed in judgments is implicitly grade 0."
},
"globalNegativeApplicability": {
"tag": "hard-negative",
"domains": ["style", "color", "chart", "landing", "product", "ux", "typography", "icons", "gsap", "react", "web", "google-fonts"],
"stacks": ["react", "nextjs", "vue", "svelte", "astro", "swiftui", "react-native", "flutter", "nuxtjs", "nuxt-ui", "html-tailwind", "shadcn", "jetpack-compose", "threejs", "angular", "laravel", "javafx", "wpf", "winui", "avalonia", "uno", "uwp"],
"interpretation": "Run every hard-negative case against all registered domains and stacks. Any returned row is an implicit grade-0 false positive; aggregate reporting may cap this cross-product separately."
},
"cases": [
{"id":"domain-style-glassmorphism","split":"calibration","mode":"domain","query":"frosted glass transparent blurred layered UI","domain":"style","judgments":[{"identity":{"Style Category":"Glassmorphism"},"grade":2}],"tags":["exact-intent","domain-positive"],"notes":"Canonical visual treatment, expressed through its descriptive attributes."},
{"id":"domain-style-accessible-paraphrase","split":"held_out","mode":"domain","query":"inclusive high contrast interface with keyboard nav and screen readers","domain":"style","judgments":[{"identity":{"Style Category":"Accessible & Ethical"},"grade":2},{"identity":{"Style Category":"Inclusive Design"},"grade":1}],"tags":["paraphrase","domain-positive"],"notes":"A style request, not an individual UX rule."},
{"id":"domain-color-spa","split":"calibration","mode":"domain","query":"beauty spa wellness soft pink lavender palette","domain":"color","judgments":[{"identity":{"Product Type":"Beauty/Spa/Wellness Service"},"grade":2}],"tags":["product-palette","domain-positive"],"notes":"Uses the public Product Type as the stable palette identity."},
{"id":"domain-color-cyber-typo","split":"held_out","mode":"domain","query":"cybersecurty threat dashboard matrix green dark pallete","domain":"color","judgments":[{"identity":{"Product Type":"Cybersecurity Platform"},"grade":2}],"tags":["typo","domain-positive"],"notes":"Deliberately misspells cybersecurity and palette."},
{"id":"domain-chart-time-series","split":"calibration","mode":"domain","query":"trend over time growth timeline line chart","domain":"chart","judgments":[{"identity":{"Data Type":"Trend Over Time"},"grade":2},{"identity":{"Data Type":"Time-Series Forecast"},"grade":1}],"tags":["exact-intent","domain-positive"],"notes":"Forecasting is acceptable only as a secondary interpretation."},
{"id":"domain-chart-correlation-paraphrase","split":"held_out","mode":"domain","query":"show whether two measures move together using dots and bubbles","domain":"chart","judgments":[{"identity":{"Data Type":"Correlation / Distribution"},"grade":2}],"tags":["paraphrase","domain-positive"],"notes":"Avoids the canonical words scatter and correlation."},
{"id":"domain-landing-pricing","split":"calibration","mode":"domain","query":"pricing plans tiers comparison landing CTA","domain":"landing","judgments":[{"identity":{"Pattern Name":"Pricing Page + CTA"},"grade":2},{"identity":{"Pattern Name":"Pricing-Focused Landing"},"grade":2},{"identity":{"Pattern Name":"Comparison Table + CTA"},"grade":1}],"tags":["multi-acceptable","domain-positive"],"notes":"Both dedicated pricing patterns are equally valid."},
{"id":"domain-landing-enterprise-typo","split":"held_out","mode":"domain","query":"enterprise credibilty trust authority conversion page","domain":"landing","judgments":[{"identity":{"Pattern Name":"Trust & Authority + Conversion"},"grade":2},{"identity":{"Pattern Name":"Enterprise Gateway"},"grade":1}],"tags":["typo","domain-positive"],"notes":"Misspells credibility while preserving the enterprise intent."},
{"id":"domain-product-spa","split":"calibration","mode":"domain","query":"salon massage skincare booking and wellness service","domain":"product","judgments":[{"identity":{"Product Type":"Beauty/Spa/Wellness Service"},"grade":2},{"identity":{"Product Type":"Booking & Appointment App"},"grade":1}],"tags":["multi-acceptable","domain-positive"],"notes":"The industry is primary; booking is a supporting capability."},
{"id":"domain-product-security-paraphrase","split":"held_out","mode":"domain","query":"platform for monitoring digital threats and protecting systems","domain":"product","judgments":[{"identity":{"Product Type":"Cybersecurity Platform"},"grade":2}],"tags":["paraphrase","domain-positive"],"notes":"Avoids the exact keyword cyber."},
{"id":"domain-ux-keyboard-focus","split":"calibration","mode":"domain","query":"visible focus and complete keyboard navigation for web users","domain":"ux","judgments":[{"identity":{"Category":"Accessibility","Issue":"Keyboard Navigation","Platform":"Web"},"grade":2},{"identity":{"Category":"Interaction","Issue":"Focus States","Platform":"All"},"grade":2}],"tags":["multi-acceptable","domain-positive"],"notes":"Both navigation coverage and visible focus are explicitly requested."},
{"id":"domain-ux-motion-paraphrase","split":"held_out","mode":"domain","query":"stop animations making people sick and honor their motion preference","domain":"ux","judgments":[{"identity":{"Category":"Animation","Issue":"Reduced Motion","Platform":"All"},"grade":2},{"identity":{"Category":"Animation","Issue":"Excessive Motion","Platform":"All"},"grade":1}],"tags":["paraphrase","accessibility","domain-positive"],"notes":"Preference support is stronger than generic excessive-motion advice."},
{"id":"domain-typography-luxury","split":"calibration","mode":"domain","query":"elegant luxury serif heading with clean readable body font","domain":"typography","judgments":[{"identity":{"Font Pairing Name":"Classic Elegant"},"grade":2},{"identity":{"Font Pairing Name":"Luxury Serif"},"grade":2},{"identity":{"Font Pairing Name":"Luxury Minimalist"},"grade":1}],"tags":["multi-acceptable","domain-positive"],"notes":"Several curated pairs directly satisfy this intentionally broad mood."},
{"id":"domain-typography-accessible-typo","split":"held_out","mode":"domain","query":"dyslexia frendly hyperlegible inclusive type pairing","domain":"typography","judgments":[{"identity":{"Font Pairing Name":"Accessibility First"},"grade":2},{"identity":{"Font Pairing Name":"Academic/Research"},"grade":1}],"tags":["typo","accessibility","domain-positive"],"notes":"Misspells friendly; the all-Atkinson pairing is the direct answer."},
{"id":"domain-icons-search","split":"calibration","mode":"domain","query":"find lookup search icon for a query field","domain":"icons","judgments":[{"identity":{"Category":"Action","Icon Name":"magnifying-glass","Library":"Phosphor"},"grade":2},{"identity":{"Category":"Action","Icon Name":"funnel","Library":"Phosphor"},"grade":1}],"tags":["exact-intent","domain-positive"],"notes":"Filter is useful but not equivalent to search."},
{"id":"domain-icons-warning-typo","split":"held_out","mode":"domain","query":"warnng caution danger status symbol","domain":"icons","judgments":[{"identity":{"Category":"Status","Icon Name":"warning","Library":"Phosphor"},"grade":2},{"identity":{"Category":"Status","Icon Name":"warning-circle","Library":"Phosphor"},"grade":1}],"tags":["typo","domain-positive"],"notes":"Misspells warning and allows the circled variant as secondary."},
{"id":"domain-gsap-scroll-reveal","split":"calibration","mode":"domain","query":"GSAP reveal elements when they enter the viewport on scroll","domain":"gsap","judgments":[{"identity":{"Category":"Scroll Reveal","Intensity Tier":"Subtle","Trigger":"scroll (viewport enter)"},"grade":2},{"identity":{"Category":"Scroll Reveal","Intensity Tier":"Standard","Trigger":"scroll (viewport enter)"},"grade":2},{"identity":{"Category":"Scroll Reveal","Intensity Tier":"Complex","Trigger":"scroll (continuous scrub)"},"grade":1}],"tags":["multi-acceptable","domain-positive"],"notes":"Viewport-entry variants are direct; continuous scrub is related but stronger."},
{"id":"domain-gsap-skeleton-typo","split":"held_out","mode":"domain","query":"skeletn shimmer loader while async content waits","domain":"gsap","judgments":[{"identity":{"Category":"Loading / Skeleton","Intensity Tier":"Subtle","Trigger":"on mount / async wait"},"grade":2},{"identity":{"Category":"Loading / Skeleton","Intensity Tier":"Standard","Trigger":"on mount / async wait"},"grade":1}],"tags":["typo","domain-positive"],"notes":"Misspells skeleton; subtle shimmer is the most exact row."},
{"id":"domain-react-parallel-promises","split":"calibration","mode":"domain","query":"React Promise.all parallel concurrent requests instead of waterfall","domain":"react","judgments":[{"identity":{"Category":"Async Waterfall","Issue":"Promise.all Parallel","Platform":"React/Next.js"},"grade":2},{"identity":{"Category":"Async Waterfall","Issue":"Dependency Parallelization","Platform":"React/Next.js"},"grade":1}],"tags":["exact-intent","domain-positive"],"notes":"The first row exactly names Promise.all."},
{"id":"domain-react-dynamic-import-paraphrase","split":"held_out","mode":"domain","query":"load a heavy JavaScript chunk only when the component is needed","domain":"react","judgments":[{"identity":{"Category":"Bundle Size","Issue":"Dynamic Imports","Platform":"React/Next.js"},"grade":2},{"identity":{"Category":"Bundle Size","Issue":"Conditional Loading","Platform":"React/Next.js"},"grade":1}],"tags":["paraphrase","domain-positive"],"notes":"Avoids the canonical term lazy in the main clause."},
{"id":"domain-web-icon-label","split":"calibration","mode":"domain","query":"accessible name for an icon-only mobile button","domain":"web","judgments":[{"identity":{"Category":"Accessibility","Issue":"Icon Button Labels","Platform":"iOS/Android/React Native"},"grade":2}],"tags":["accessibility","domain-positive"],"notes":"App-interface guidance uses the public web domain key."},
{"id":"domain-web-virtual-list-paraphrase","split":"held_out","mode":"domain","query":"keep a very long mobile list smooth without rendering every row","domain":"web","judgments":[{"identity":{"Category":"Performance","Issue":"Virtualize Long Lists","Platform":"iOS/Android/React Native"},"grade":2}],"tags":["paraphrase","domain-positive"],"notes":"Describes virtualization without naming FlatList."},
{"id":"domain-google-fonts-inter","split":"calibration","mode":"domain","query":"Inter variable sans serif Google font family","domain":"google-fonts","judgments":[{"identity":{"Family":"Inter"},"grade":2},{"identity":{"Family":"Inter Tight"},"grade":1}],"tags":["exact-entity","domain-positive"],"notes":"Exact family is preferred over its related Tight family."},
{"id":"domain-google-fonts-atkinson-paraphrase","split":"held_out","mode":"domain","query":"hyperlegible accessible sans typeface for easier reading","domain":"google-fonts","judgments":[{"identity":{"Family":"Atkinson Hyperlegible"},"grade":2},{"identity":{"Family":"Atkinson Hyperlegible Next"},"grade":1}],"tags":["paraphrase","accessibility","domain-positive"],"notes":"The established family is locked; its newer related family is acceptable."},
{"id":"stack-angular-standalone","split":"calibration","mode":"stack","query":"standalone Angular components for a new project","stack":"angular","judgments":[{"identity":{"Category":"Components","Guideline":"Use standalone components"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Modern Angular component architecture."},
{"id":"stack-angular-signals-paraphrase","split":"held_out","mode":"stack","query":"reactive local state with Angular signal primitives","stack":"angular","judgments":[{"identity":{"Category":"Components","Guideline":"Use signals for state"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Held out wording for signals."},
{"id":"stack-astro-islands","split":"calibration","mode":"stack","query":"Astro islands architecture interactive components","stack":"astro","judgments":[{"identity":{"Category":"Architecture","Guideline":"Use Islands Architecture"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical Astro architecture."},
{"id":"stack-astro-zero-js-paraphrase","split":"held_out","mode":"stack","query":"ship no browser JavaScript unless interaction requires it","stack":"astro","judgments":[{"identity":{"Category":"Architecture","Guideline":"Default to zero JS"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes the zero-JS default."},
{"id":"stack-avalonia-namespace","split":"calibration","mode":"stack","query":"Avalonia XAML namespace declaration","stack":"avalonia","judgments":[{"identity":{"Category":"XAML","Guideline":"Use Avalonia XAML namespace"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Exact Avalonia XAML setup concern."},
{"id":"stack-avalonia-compiled-binding","split":"held_out","mode":"stack","query":"type checked compiled binding with x DataType","stack":"avalonia","judgments":[{"identity":{"Category":"XAML","Guideline":"Use compiled bindings with x:DataType"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Punctuation-free paraphrase of x:DataType."},
{"id":"stack-flutter-stateless","split":"calibration","mode":"stack","query":"prefer StatelessWidget when Flutter UI has no mutable state","stack":"flutter","judgments":[{"identity":{"Category":"Widgets","Guideline":"Use StatelessWidget when possible"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Direct widget choice."},
{"id":"stack-flutter-const-paraphrase","split":"held_out","mode":"stack","query":"reduce Flutter rebuild cost with immutable compile-time widgets","stack":"flutter","judgments":[{"identity":{"Category":"Widgets","Guideline":"Use const constructors"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes const constructor value without copying the title."},
{"id":"stack-html-tailwind-z-index","split":"calibration","mode":"stack","query":"Tailwind z-index utility scale for layered UI","stack":"html-tailwind","judgments":[{"identity":{"Category":"Z-Index","Guideline":"Use Tailwind z-* scale"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Specific utility-scale guidance."},
{"id":"stack-html-tailwind-hover","split":"held_out","mode":"stack","query":"smooth transition when a Tailwind element is hovered","stack":"html-tailwind","judgments":[{"identity":{"Category":"Animation","Guideline":"Hover transitions"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Natural language hover-motion request."},
{"id":"stack-javafx-application","split":"calibration","mode":"stack","query":"launch JavaFX UI from an Application subclass","stack":"javafx","judgments":[{"identity":{"Category":"Application","Guideline":"Start UI from Application subclass"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical JavaFX application lifecycle."},
{"id":"stack-javafx-threading-paraphrase","split":"held_out","mode":"stack","query":"prevent slow background work from freezing the FX application thread","stack":"javafx","judgments":[{"identity":{"Category":"Threading","Guideline":"Keep work off the FX Application Thread"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes the failure mode rather than repeating the title."},
{"id":"stack-compose-pure-ui","split":"calibration","mode":"stack","query":"pure Jetpack Compose UI composables","stack":"jetpack-compose","judgments":[{"identity":{"Category":"Composable","Guideline":"Pure UI composables"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Direct Compose architecture query."},
{"id":"stack-compose-single-source","split":"held_out","mode":"stack","query":"one authoritative owner for Compose screen state","stack":"jetpack-compose","judgments":[{"identity":{"Category":"State","Guideline":"Single source of truth"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Paraphrases state ownership."},
{"id":"stack-laravel-blade-component","split":"calibration","mode":"stack","query":"reusable Laravel Blade UI components","stack":"laravel","judgments":[{"identity":{"Category":"Blade Templates","Guideline":"Use Blade components for reusable UI"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Direct Blade reuse guidance."},
{"id":"stack-laravel-props","split":"held_out","mode":"stack","query":"declare typed inputs for a Blade component using props","stack":"laravel","judgments":[{"identity":{"Category":"Blade Templates","Guideline":"Use @props for component type-safety"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Natural wording around @props."},
{"id":"stack-nextjs-app-router","split":"calibration","mode":"stack","query":"Next.js App Router for a new project","stack":"nextjs","judgments":[{"identity":{"Category":"Routing","Guideline":"Use App Router for new projects"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Current routing architecture."},
{"id":"stack-nextjs-server-components","split":"held_out","mode":"stack","query":"render on the server by default and opt into client boundaries","stack":"nextjs","judgments":[{"identity":{"Category":"Rendering","Guideline":"Use Server Components by default"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Held-out RSC wording."},
{"id":"stack-nuxt-ui-module","split":"calibration","mode":"stack","query":"install the Nuxt UI module","stack":"nuxt-ui","judgments":[{"identity":{"Category":"Installation","Guideline":"Add Nuxt UI module"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Basic Nuxt UI setup."},
{"id":"stack-nuxt-ui-semantic-color","split":"held_out","mode":"stack","query":"style Nuxt UI components through meaning-based color props","stack":"nuxt-ui","judgments":[{"identity":{"Category":"Components","Guideline":"Use semantic color props"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Avoids copying semantic exactly except its concept."},
{"id":"stack-nuxtjs-file-routing","split":"calibration","mode":"stack","query":"Nuxt file-based page routing","stack":"nuxtjs","judgments":[{"identity":{"Category":"Routing","Guideline":"Use file-based routing"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical Nuxt routing convention."},
{"id":"stack-nuxtjs-ssr","split":"held_out","mode":"stack","query":"server render Nuxt pages unless a client-only boundary is necessary","stack":"nuxtjs","judgments":[{"identity":{"Category":"Rendering","Guideline":"Use SSR by default"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes the SSR default."},
{"id":"stack-react-native-functional","split":"calibration","mode":"stack","query":"functional React Native components","stack":"react-native","judgments":[{"identity":{"Category":"Components","Guideline":"Use functional components"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Direct component style."},
{"id":"stack-react-native-stylesheet","split":"held_out","mode":"stack","query":"define reusable native styles outside render instead of inline objects","stack":"react-native","judgments":[{"identity":{"Category":"Styling","Guideline":"Use StyleSheet.create"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes StyleSheet.create without naming it."},
{"id":"stack-react-usestate","split":"calibration","mode":"stack","query":"React useState for component local state","stack":"react","judgments":[{"identity":{"Category":"State","Guideline":"Use useState for local state"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical local state hook."},
{"id":"stack-react-effect-cleanup","split":"held_out","mode":"stack","query":"remove subscriptions and timers when a React effect unmounts","stack":"react","judgments":[{"identity":{"Category":"Effects","Guideline":"Clean up effects"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes cleanup behavior."},
{"id":"stack-shadcn-cli","split":"calibration","mode":"stack","query":"install shadcn components with the CLI","stack":"shadcn","judgments":[{"identity":{"Category":"Setup","Guideline":"Use CLI for installation"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical setup path."},
{"id":"stack-shadcn-css-vars","split":"held_out","mode":"stack","query":"theme shadcn semantic colors through custom properties","stack":"shadcn","judgments":[{"identity":{"Category":"Theming","Guideline":"Use CSS variables for colors"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Uses custom properties as a synonym for CSS variables."},
{"id":"stack-svelte-state","split":"calibration","mode":"stack","query":"Svelte 5 $state rune for reactive state","stack":"svelte","judgments":[{"identity":{"Category":"Reactivity","Guideline":"Use $state in Svelte 5"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Specific Svelte 5 primitive."},
{"id":"stack-svelte-effect","split":"held_out","mode":"stack","query":"run a Svelte 5 side effect when reactive dependencies change","stack":"svelte","judgments":[{"identity":{"Category":"Reactivity","Guideline":"Use $effect for side effects"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Behavioral wording for $effect."},
{"id":"stack-swiftui-state","split":"calibration","mode":"stack","query":"SwiftUI @State for view-local value state","stack":"swiftui","judgments":[{"identity":{"Category":"State","Guideline":"Use @State for local state"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical property wrapper choice."},
{"id":"stack-swiftui-navigation","split":"held_out","mode":"stack","query":"modern value-driven iOS navigation container replacing NavigationView","stack":"swiftui","judgments":[{"identity":{"Category":"Navigation","Guideline":"Use NavigationStack or NavigationSplitView (iOS 16+)"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes NavigationStack migration intent."},
{"id":"stack-threejs-orbitcontrols","split":"calibration","mode":"stack","query":"Three.js OrbitControls must be imported separately","stack":"threejs","judgments":[{"identity":{"Category":"Setup","Guideline":"Import OrbitControls from Three.js Addons"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Common setup failure."},
{"id":"stack-threejs-pixel-ratio","split":"held_out","mode":"stack","query":"avoid excessive GPU work on retina screens by limiting renderer DPR","stack":"threejs","judgments":[{"identity":{"Category":"Setup","Guideline":"Pixel Ratio Cap at 2"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Uses DPR and performance wording."},
{"id":"stack-uno-winui-xaml","split":"calibration","mode":"stack","query":"Uno Platform WinUI XAML API surface","stack":"uno","judgments":[{"identity":{"Category":"XAML","Guideline":"Use WinUI XAML API surface"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical cross-platform XAML surface."},
{"id":"stack-uno-package-paraphrase","split":"held_out","mode":"stack","query":"choose the modern Uno WinUI package instead of the legacy Uno UI package","stack":"uno","judgments":[{"identity":{"Category":"XAML","Guideline":"Use Uno.WinUI not Uno.UI for new projects"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Natural package-selection wording."},
{"id":"stack-uwp-xbind","split":"calibration","mode":"stack","query":"UWP compiled x:Bind data binding","stack":"uwp","judgments":[{"identity":{"Category":"XAML","Guideline":"Use x:Bind for compiled bindings"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical compiled binding guidance."},
{"id":"stack-uwp-migration","split":"held_out","mode":"stack","query":"which Windows UI framework should a brand new app choose instead of legacy UWP","stack":"uwp","judgments":[{"identity":{"Category":"Architecture","Guideline":"Prefer WinUI 3 for new projects"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Tests that legacy-stack guidance can recommend the successor."},
{"id":"stack-vue-composition","split":"calibration","mode":"stack","query":"Vue Composition API for a new project","stack":"vue","judgments":[{"identity":{"Category":"Composition","Guideline":"Use Composition API for new projects"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical Vue architecture."},
{"id":"stack-vue-pinia","split":"held_out","mode":"stack","query":"central shared Vue application state store","stack":"vue","judgments":[{"identity":{"Category":"State","Guideline":"Use Pinia for global state"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Avoids naming Pinia in the query."},
{"id":"stack-winui-infobar","split":"calibration","mode":"stack","query":"WinUI InfoBar for status messages","stack":"winui","judgments":[{"identity":{"Category":"Controls","Guideline":"Use InfoBar for status messages"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Specific WinUI control selection."},
{"id":"stack-winui-dispatcherqueue","split":"held_out","mode":"stack","query":"marshal a WinUI update back onto the UI thread with the modern dispatcher","stack":"winui","judgments":[{"identity":{"Category":"Threading","Guideline":"Use DispatcherQueue not Dispatcher"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Describes DispatcherQueue without copying its name."},
{"id":"stack-wpf-property-change","split":"calibration","mode":"stack","query":"WPF INotifyPropertyChanged data binding updates","stack":"wpf","judgments":[{"identity":{"Category":"Data Binding","Guideline":"Implement INotifyPropertyChanged"},"grade":2}],"tags":["stack-positive","exact-intent"],"notes":"Canonical MVVM notification contract."},
{"id":"stack-wpf-virtualization","split":"held_out","mode":"stack","query":"keep a huge WPF list responsive by only creating visible item containers","stack":"wpf","judgments":[{"identity":{"Category":"Performance","Guideline":"Use VirtualizingStackPanel for large lists"},"grade":2}],"tags":["stack-positive","paraphrase"],"notes":"Behavioral description of list virtualization."},
{"id":"auto-route-accessibility","split":"calibration","mode":"auto","query":"WCAG keyboard accessibility and visible focus","expectedRoute":"ux","judgments":[{"identity":{"Category":"Accessibility","Issue":"Keyboard Navigation","Platform":"Web"},"grade":2},{"identity":{"Category":"Interaction","Issue":"Focus States","Platform":"All"},"grade":1}],"tags":["auto-router","routing-positive"],"notes":"Specific accessibility intent should outrank generic style terms."},
{"id":"auto-route-color","split":"calibration","mode":"auto","query":"hex color palette accent foreground destructive tokens","expectedRoute":"color","judgments":[{"identity":{"Product Type":"Design System/Component Library"},"grade":2}],"tags":["auto-router","routing-positive"],"notes":"Color vocabulary should route to palettes."},
{"id":"auto-route-chart","split":"calibration","mode":"auto","query":"scatter chart for correlation distribution","expectedRoute":"chart","judgments":[{"identity":{"Data Type":"Correlation / Distribution"},"grade":2}],"tags":["auto-router","routing-positive"],"notes":"Explicit chart type and analytic intent."},
{"id":"auto-route-landing","split":"held_out","mode":"auto","query":"hero testimonials CTA conversion landing page","expectedRoute":"landing","judgments":[{"identity":{"Pattern Name":"Hero + Testimonials + CTA"},"grade":2},{"identity":{"Pattern Name":"Hero + Features + CTA"},"grade":1}],"tags":["auto-router","routing-positive","held-out-route"],"notes":"Multiple landing-specific terms should dominate product routing."},
{"id":"auto-route-fonts","split":"held_out","mode":"auto","query":"JetBrains Mono Google font family variable styles","expectedRoute":"google-fonts","judgments":[{"identity":{"Family":"JetBrains Mono"},"grade":2}],"tags":["auto-router","routing-positive","held-out-route"],"notes":"Entity lookup in the large Google Fonts catalog."},
{"id":"auto-route-icons","split":"calibration","mode":"auto","query":"Phosphor warning icon glyph for danger status","expectedRoute":"icons","judgments":[{"identity":{"Category":"Status","Icon Name":"warning","Library":"Phosphor"},"grade":2},{"identity":{"Category":"Status","Icon Name":"warning-circle","Library":"Phosphor"},"grade":1}],"tags":["auto-router","routing-positive"],"notes":"Explicit icon/glyph vocabulary."},
{"id":"auto-route-gsap","split":"held_out","mode":"auto","query":"GSAP ScrollTrigger stagger reveal animation","expectedRoute":"gsap","judgments":[{"identity":{"Category":"Scroll Reveal","Intensity Tier":"Standard","Trigger":"scroll (viewport enter)"},"grade":2},{"identity":{"Category":"Stagger List","Intensity Tier":"Standard","Trigger":"load or scroll"},"grade":1}],"tags":["auto-router","routing-positive","held-out-route"],"notes":"GSAP vocabulary is unambiguous even with generic animation."},
{"id":"auto-route-react","split":"calibration","mode":"auto","query":"React Suspense waterfall bundle rerender optimization","expectedRoute":"react","judgments":[{"identity":{"Category":"Async Waterfall","Issue":"Suspense Boundaries","Platform":"React/Next.js"},"grade":2}],"tags":["auto-router","routing-positive"],"notes":"React performance terms should not route to generic UX."},
{"id":"negative-gibberish-alpha","split":"calibration","mode":"auto","query":"zzqqxx plmokn qvtrz","judgments":[],"tags":["hard-negative","gibberish","abstention"],"notes":"No catalog row is relevant; suggestions and returned rows are false positives for measurement."},
{"id":"negative-gibberish-numeric","split":"held_out","mode":"auto","query":"7391 qzxv 0044 nmnq","judgments":[],"tags":["hard-negative","gibberish","abstention"],"notes":"Held-out alphanumeric noise."},
{"id":"negative-geography-fact","split":"calibration","mode":"auto","query":"capital of Mongolia population census","judgments":[],"tags":["hard-negative","out-of-scope","abstention"],"notes":"A factual geography question, not a design request."},
{"id":"negative-math-proof","split":"held_out","mode":"auto","query":"prove there are infinitely many prime numbers","judgments":[],"tags":["hard-negative","out-of-scope","abstention"],"notes":"A mathematics request with no relevant catalog guidance."},
{"id":"negative-cooking","split":"calibration","mode":"auto","query":"sourdough starter feeding schedule at room temperature","judgments":[],"tags":["hard-negative","out-of-scope","abstention"],"notes":"A cooking instruction request; product-category overlap must not count as relevance."},
{"id":"negative-biology","split":"held_out","mode":"auto","query":"photosynthesis equation for freshwater algae","judgments":[],"tags":["hard-negative","out-of-scope","abstention"],"notes":"A science question with no UI intent."},
{"id":"design-system-spa","split":"calibration","mode":"design-system","query":"beauty spa wellness booking landing page","judgments":[{"identity":{"Product Type":"Beauty/Spa/Wellness Service"},"grade":2}],"coherence":{"productCategory":"Beauty/Spa/Wellness Service","styleNames":["Soft UI Evolution","Neumorphism","Glassmorphism"],"patternNames":["Hero-Centric + Social Proof","Hero-Centric Design","Hero + Testimonials + CTA"],"colorMode":"light"},"tags":["design-system","coherence","readme-example"],"notes":"Industry, calming style, social proof pattern, and light palette should agree."},
{"id":"design-system-cybersecurity-dark","split":"calibration","mode":"design-system","query":"cybersecurity threat monitoring platform dark mode","judgments":[{"identity":{"Product Type":"Cybersecurity Platform"},"grade":2}],"coherence":{"productCategory":"Cybersecurity Platform","styleNames":["Cyberpunk UI","Dark Mode (OLED)","HUD / Sci-Fi FUI"],"patternNames":["Trust & Authority + Real-Time","Trust & Authority + Conversion","Real-Time / Operations Landing","Enterprise Gateway"],"colorMode":"dark"},"tags":["design-system","coherence","dark-mode"],"notes":"Security, real-time operations, technical style, and dark palette form one coherent system."},
{"id":"design-system-saas","split":"calibration","mode":"design-system","query":"SaaS dashboard for a B2B cloud product","judgments":[{"identity":{"Product Type":"SaaS (General)"},"grade":2},{"identity":{"Product Type":"B2B Service"},"grade":1}],"coherence":{"productCategory":"SaaS (General)","styleNames":["Glassmorphism","Flat Design","Minimalism & Swiss Style","Soft UI Evolution"],"patternNames":["Hero + Features + CTA","Feature-Rich Showcase"],"colorMode":"light"},"tags":["design-system","coherence","readme-example"],"notes":"Locks the README's broad SaaS example to a maintainable B2B system."},
{"id":"design-system-healthcare","split":"calibration","mode":"design-system","query":"accessible healthcare analytics dashboard for patients","judgments":[{"identity":{"Product Type":"Healthcare App"},"grade":2},{"identity":{"Product Type":"Patient Portal / Health Records"},"grade":1}],"coherence":{"productCategory":"Healthcare App","styleNames":["Accessible & Ethical","Inclusive Design","Neumorphism","Soft UI Evolution"],"patternNames":["Social Proof-Focused","Hero + Testimonials + CTA","Trust & Authority + Conversion"],"colorMode":"light"},"tags":["design-system","coherence","accessibility","readme-example"],"notes":"Accessibility and patient trust are stronger constraints than decorative dashboard styling."},
{"id":"design-system-portfolio-dark","split":"held_out","mode":"design-system","query":"creative portfolio website with dark mode and scroll storytelling","judgments":[{"identity":{"Product Type":"Portfolio/Personal"},"grade":2}],"coherence":{"productCategory":"Portfolio/Personal","styleNames":["Motion-Driven","Brutalism","Dark Mode (OLED)","Minimalism & Swiss Style","Interactive Cursor Design"],"patternNames":["Storytelling-Driven","Scroll-Triggered Storytelling","Portfolio Grid","Horizontal Scroll Journey"],"colorMode":"dark","colorProductTypes":["Portfolio/Personal"]},"tags":["design-system","coherence","dark-mode","readme-example"],"notes":"Explicit creative intent activates the curated Brutalism rule; the derived dark surface must retain the Portfolio/Personal palette identity."},
{"id":"design-system-fintech-dark","split":"held_out","mode":"design-system","query":"fintech banking app with a secure dark dashboard","judgments":[{"identity":{"Product Type":"Fintech/Crypto"},"grade":2},{"identity":{"Product Type":"Banking/Traditional Finance"},"grade":1}],"coherence":{"productCategory":"Fintech/Crypto","styleNames":["Dark Mode (OLED)","Glassmorphism","Accessible & Ethical","Minimalism & Swiss Style"],"patternNames":["Trust & Authority","Trust & Authority + Conversion","Enterprise Gateway"],"colorMode":"dark"},"tags":["design-system","coherence","dark-mode","readme-example"],"notes":"Explicit dark mode must not conflict with the palette or anti-pattern advice."},
{"id":"design-system-spa-paraphrase","split":"held_out","mode":"design-system","query":"calming salon for massages facials and appointment reservations","judgments":[{"identity":{"Product Type":"Beauty/Spa/Wellness Service"},"grade":2},{"identity":{"Product Type":"Booking & Appointment App"},"grade":1}],"coherence":{"productCategory":"Beauty/Spa/Wellness Service","styleNames":["Soft UI Evolution","Neumorphism","Organic Biophilic","Nature Distilled"],"patternNames":["Hero-Centric + Social Proof","Hero-Centric Design","Hero + Testimonials + CTA"],"colorMode":"light"},"tags":["design-system","coherence","paraphrase"],"notes":"Held-out industry paraphrase without the exact words beauty, spa, or wellness."},
{"id":"design-system-cyber-typo","split":"held_out","mode":"design-system","query":"cybersecurty operatons center with live threat alerts and OLED UI","judgments":[{"identity":{"Product Type":"Cybersecurity Platform"},"grade":2}],"coherence":{"productCategory":"Cybersecurity Platform","styleNames":["Cyberpunk UI","Dark Mode (OLED)","HUD / Sci-Fi FUI"],"patternNames":["Trust & Authority + Real-Time","Real-Time / Operations Landing","Trust & Authority + Conversion"],"colorMode":"dark"},"tags":["design-system","coherence","typo","dark-mode"],"notes":"Typos plus an OLED constraint test end-to-end recovery and agreement."}
]
}
@@ -0,0 +1,100 @@
{
"schemaVersion": 1,
"status": "provisional-baseline-regression-gate",
"baselineRevision": "97eb2a2",
"runtimeFingerprint": "0d096d52499c2dd84ef80b2e45a1793bc4ed7c9be8fe5948b08151790959b376",
"oracleFingerprint": "02e7226428c64a995ec24ae104ce4d186f5f91bf0f062556e90ef9b0fcb6d67a",
"approvingMaintainer": "repository maintainer approved Phase 1 testing checkpoint; second judgment review remains required before final-target promotion",
"units": "All metrics are ratios in [0,1]. Precision treats missing ranks as non-relevant. Negative abstention is measured across the hard-negative by domain/stack cross-product.",
"splitPolicy": {
"calibration": "May be inspected while tuning retrieval.",
"held_out": "Must not be inspected to tune ranking weights; used for final confirmation.",
"varianceAndConfidence": "Corpus is deterministic and has no sampling variance. Report exact ratios and sample counts; do not claim population confidence intervals from this curated set."
},
"tolerancePolicy": "A 1e-12 numeric tolerance permits floating-point representation only. Current known failures remain visible in relevance-baseline.json; the gate prevents regressions and does not claim final quality targets are met.",
"metrics": {
"routingAccuracy": {"floor": 0.875, "tolerance": 1e-12},
"precisionAt1": {"floor": 0.7631578947368421, "tolerance": 1e-12},
"precisionAt3": {"floor": 0.3815789473684211, "tolerance": 1e-12},
"mrrAt3": {"floor": 0.8355263157894737, "tolerance": 1e-12},
"ndcgAt3": {"floor": 0.8366350964266653, "tolerance": 1e-12},
"negativeAbstention": {"floor": 0.9117647058823529, "tolerance": 1e-12},
"typoRecoveryAt3": {"floor": 1.0, "tolerance": 1e-12},
"designSystemCoherence": {"floor": 0.71875, "tolerance": 1e-12}
},
"sampleMinimums": {
"cases": 90,
"retrieval": 76,
"routing": 8,
"negativeChecks": 204,
"typo": 5,
"designSystem": 8,
"domain:style": 2,
"domain:color": 2,
"domain:chart": 2,
"domain:landing": 2,
"domain:product": 2,
"domain:ux": 2,
"domain:typography": 2,
"domain:icons": 2,
"domain:gsap": 2,
"domain:react": 2,
"domain:web": 2,
"domain:google-fonts": 2
},
"splits": {
"calibration": {
"metrics": {
"routingAccuracy": {"floor": 0.8, "tolerance": 1e-12},
"precisionAt1": {"floor": 0.8974358974358975, "tolerance": 1e-12},
"precisionAt3": {"floor": 0.40170940170940167, "tolerance": 1e-12},
"mrrAt3": {"floor": 0.9230769230769231, "tolerance": 1e-12},
"ndcgAt3": {"floor": 0.9103072109232644, "tolerance": 1e-12},
"negativeAbstention": {"floor": 0.9411764705882353, "tolerance": 1e-12},
"typoRecoveryAt3": {"floor": 0.0, "tolerance": 1e-12},
"designSystemCoherence": {"floor": 0.6875, "tolerance": 1e-12}
},
"sampleMinimums": {"cases": 46, "retrieval": 39, "routing": 5, "negativeChecks": 102, "typo": 0, "designSystem": 4}
},
"held_out": {
"metrics": {
"routingAccuracy": {"floor": 1.0, "tolerance": 1e-12},
"precisionAt1": {"floor": 0.6216216216216216, "tolerance": 1e-12},
"precisionAt3": {"floor": 0.36036036036036034, "tolerance": 1e-12},
"mrrAt3": {"floor": 0.7432432432432432, "tolerance": 1e-12},
"ndcgAt3": {"floor": 0.7589807054707906, "tolerance": 1e-12},
"negativeAbstention": {"floor": 0.8823529411764706, "tolerance": 1e-12},
"typoRecoveryAt3": {"floor": 1.0, "tolerance": 1e-12},
"designSystemCoherence": {"floor": 0.75, "tolerance": 1e-12}
},
"sampleMinimums": {"cases": 44, "retrieval": 37, "routing": 3, "negativeChecks": 102, "typo": 5, "designSystem": 4}
}
},
"lockedCases": {
"domain-style-glassmorphism": {"withinTop": 1, "minimumGrade": 2},
"domain-color-spa": {"withinTop": 1, "minimumGrade": 2},
"domain-chart-time-series": {"withinTop": 1, "minimumGrade": 2},
"domain-landing-pricing": {"withinTop": 1, "minimumGrade": 2},
"domain-product-spa": {"withinTop": 1, "minimumGrade": 2},
"domain-ux-keyboard-focus": {"withinTop": 1, "minimumGrade": 2},
"domain-typography-luxury": {"withinTop": 1, "minimumGrade": 2},
"domain-icons-search": {"withinTop": 1, "minimumGrade": 2},
"domain-gsap-scroll-reveal": {"withinTop": 1, "minimumGrade": 2},
"domain-react-parallel-promises": {"withinTop": 1, "minimumGrade": 2},
"domain-web-icon-label": {"withinTop": 1, "minimumGrade": 2},
"domain-google-fonts-inter": {"withinTop": 1, "minimumGrade": 2},
"stack-swiftui-navigation": {"withinTop": 1, "minimumGrade": 2},
"stack-threejs-orbitcontrols": {"withinTop": 1, "minimumGrade": 2},
"stack-uwp-migration": {"withinTop": 1, "minimumGrade": 2},
"stack-winui-dispatcherqueue": {"withinTop": 1, "minimumGrade": 2}
},
"proposedFinalTargets": {
"routingAccuracy": 0.93,
"precisionAt1": 0.8,
"mrrAt3": 0.88,
"ndcgAt3": 0.92,
"negativeAbstention": 0.95,
"typoRecoveryAt3": 0.85,
"designSystemCoherence": 0.9
}
}
@@ -0,0 +1,354 @@
#!/usr/bin/env python3
"""Offline contract tests for deterministic upstream catalog refreshes."""
import csv
import json
import os
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
REPO = next(
parent for parent in Path(__file__).resolve().parents
if all((parent / "scripts" / script).is_file() for script in (
"refresh-google-fonts.py", "refresh-icon-catalog.py",
))
)
FIXTURES = Path(__file__).parent / "fixtures" / "catalogs"
FONT_SCRIPT = REPO / "scripts" / "refresh-google-fonts.py"
ICON_SCRIPT = REPO / "scripts" / "refresh-icon-catalog.py"
class CatalogRefreshTest(unittest.TestCase):
def run_command(self, *args, env=None):
return subprocess.run(
[sys.executable, *map(str, args)],
cwd=REPO,
env=env,
capture_output=True,
text=True,
check=False,
)
def font_args(self, directory, api=None, metadata=None, approve=True, existing=None, overrides=None):
args = [
FONT_SCRIPT,
"--api-input", api or FIXTURES / "google-api.json",
"--metadata-input", metadata or FIXTURES / "google-metadata.json",
"--existing-csv", existing or FIXTURES / "google-existing.csv",
"--overrides", overrides or FIXTURES / "google-overrides.json",
"--output-csv", directory / "google-fonts.csv",
"--license-output", directory / "google-font-licenses.json",
"--verified-at", "2026-08-13",
"--metadata-revision", "fixture-catalogs-v1",
"--expected-count", "2",
]
if approve:
args.append("--approve-changes")
return args
def icon_args(self, directory, source=None, curated=None, package=None, react_exports=None):
return [
ICON_SCRIPT,
"--input", source or FIXTURES / "phosphor-core.json",
"--package-json", package or FIXTURES / "phosphor-package.json",
"--react-package-json", FIXTURES / "phosphor-react-package.json",
"--react-exports-input", react_exports or FIXTURES / "phosphor-react-exports.json",
"--curated-csv", curated or FIXTURES / "icons-curated.csv",
"--output", directory / "phosphor-icons-upstream.json",
"--verified-at", "2026-08-13",
"--expected-count", "2",
]
def test_live_font_refresh_requires_environment_key(self):
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
args = self.font_args(directory)
args[1:3] = ["--live"]
env = dict(os.environ)
env.pop("GOOGLE_FONTS_API_KEY", None)
result = self.run_command(*args, env=env)
self.assertEqual(2, result.returncode)
self.assertIn("GOOGLE_FONTS_API_KEY is required for --live", result.stderr)
self.assertIn("use --api-input for offline CI", result.stderr)
def test_font_refresh_is_deterministic_and_preserves_reviewed_fields(self):
with tempfile.TemporaryDirectory() as first, tempfile.TemporaryDirectory() as second:
first_path, second_path = Path(first), Path(second)
self.assertEqual(0, self.run_command(*self.font_args(first_path)).returncode)
self.assertEqual(0, self.run_command(*self.font_args(second_path)).returncode)
self.assertEqual(
(first_path / "google-fonts.csv").read_bytes(),
(second_path / "google-fonts.csv").read_bytes(),
)
self.assertEqual(
(first_path / "google-font-licenses.json").read_bytes(),
(second_path / "google-font-licenses.json").read_bytes(),
)
with (first_path / "google-fonts.csv").open(encoding="utf-8", newline="") as handle:
rows = list(csv.DictReader(handle))
csv_bytes = (first_path / "google-fonts.csv").read_bytes()
license_bytes = (first_path / "google-font-licenses.json").read_bytes()
licenses = json.loads(license_bytes)
self.assertEqual(["Alpha Sans", "Zeta Serif"], [row["Family"] for row in rows])
self.assertEqual("Sans Serif", rows[0]["Stroke"])
self.assertEqual("approved override keywords", rows[0]["Keywords"])
self.assertEqual("400 | 400i | 500", rows[0]["Styles"])
self.assertEqual("wght: 100..900", rows[0]["Variable Axes"])
self.assertEqual(["OFL", "APACHE2"], [item["license"] for item in licenses["families"]])
self.assertEqual(["Alpha Sans", "Zeta Serif"], [item["name"] for item in licenses["families"]])
self.assertEqual("fixture-catalogs-v1", licenses["source"]["revision"])
self.assertTrue(all(item["status"] == "active" for item in licenses["families"]))
self.assertTrue(all(item["verifiedAt"] == "2026-08-13" for item in licenses["families"]))
with tempfile.TemporaryDirectory() as raw:
reused = Path(raw)
metadata_path = reused / "metadata.json"
metadata_path.write_bytes(license_bytes)
result = self.run_command(*self.font_args(
reused, metadata=metadata_path
))
self.assertEqual(0, result.returncode, result.stderr)
self.assertEqual(
csv_bytes, (reused / "google-fonts.csv").read_bytes(),
)
def test_font_refresh_rejects_schema_size_dates_and_licenses(self):
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
api = json.loads((FIXTURES / "google-api.json").read_text())
metadata = json.loads((FIXTURES / "google-metadata.json").read_text())
cases = []
wrong_schema = dict(api)
wrong_schema["kind"] = "unexpected"
cases.append((wrong_schema, metadata, "webfonts#webfontList"))
cases.append(({**api, "items": api["items"][:1]}, metadata, "expected 2 items"))
bad_url = json.loads(json.dumps(api))
bad_url["items"][0]["files"]["regular"] = "https://example.com/font.ttf"
cases.append((bad_url, metadata, "https://fonts.gstatic.com"))
bad_date = json.loads(json.dumps(api))
bad_date["items"][0]["lastModified"] = "1970-01-01"
cases.append((bad_date, metadata, "suspicious date"))
bad_license = json.loads(json.dumps(metadata))
bad_license["families"][0]["license"] = "UNKNOWN"
cases.append((api, bad_license, "invalid or missing official license"))
for index, (api_value, metadata_value, error) in enumerate(cases):
api_path, metadata_path = directory / f"api-{index}.json", directory / f"metadata-{index}.json"
api_path.write_text(json.dumps(api_value))
metadata_path.write_text(json.dumps(metadata_value))
result = self.run_command(*self.font_args(directory, api_path, metadata_path))
with self.subTest(error=error):
self.assertEqual(2, result.returncode)
self.assertIn(error, result.stderr)
def test_font_refresh_fails_closed_on_concurrency_or_interrupted_pair(self):
for sentinel, error in (
(".google-font-refresh.lock", "another refresh is already running"),
(".google-font-refresh.incomplete.json", "incomplete prior refresh"),
):
with self.subTest(sentinel=sentinel), tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
(directory / sentinel).write_text("occupied\n", encoding="utf-8")
result = self.run_command(*self.font_args(directory))
self.assertEqual(2, result.returncode)
self.assertIn(error, result.stderr)
self.assertFalse((directory / "google-fonts.csv").exists())
self.assertFalse((directory / "google-font-licenses.json").exists())
def test_catalog_cross_check_uses_explicit_schema_without_font_file_urls(self):
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
args = self.font_args(directory)
args[1:3] = ["--catalog-input", FIXTURES / "google-catalog.json"]
result = self.run_command(*args)
with (directory / "google-fonts.csv").open(encoding="utf-8", newline="") as handle:
rows = list(csv.DictReader(handle))
self.assertEqual(0, result.returncode, result.stderr)
self.assertEqual(["Alpha Sans", "Zeta Serif"], [row["Family"] for row in rows])
self.assertEqual("Geometric", rows[0]["Classifications"])
self.assertEqual("42", rows[0]["Popularity Rank"])
self.assertEqual("latin | vietnamese", rows[0]["Subsets"])
def test_official_metadata_checkout_is_strict_and_reusable(self):
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
metadata_root = directory / "google-fonts"
family_dir = metadata_root / "ofl" / "alphasans"
family_dir.mkdir(parents=True)
(family_dir / "METADATA.pb").write_text(
'name: "Alpha Sans"\n'
'designer: "Alpha Designer"\n'
'license: "OFL"\n'
'date_added: "2024-01-02"\n',
encoding="utf-8",
)
args = self.font_args(directory)
metadata_index = args.index("--metadata-input")
args[metadata_index:metadata_index + 2] = ["--metadata-root", metadata_root]
result = self.run_command(*args)
self.assertEqual(2, result.returncode)
self.assertIn("fewer than 90%", result.stderr)
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
metadata_root = directory / "google-fonts"
for slug, family, license_name in (
("alphasans", "Alpha Sans", "OFL"),
("zetaserif", "Zeta Serif", "APACHE2"),
):
family_dir = metadata_root / "ofl" / slug
family_dir.mkdir(parents=True)
(family_dir / "METADATA.pb").write_text(
f'name: "{family}"\n'
f'designer: "{family} Designer"\n'
f'license: "{license_name}"\n'
'date_added: "2024-01-02"\n',
encoding="utf-8",
)
args = self.font_args(directory)
metadata_index = args.index("--metadata-input")
args[metadata_index:metadata_index + 2] = ["--metadata-root", metadata_root]
result = self.run_command(*args)
licenses = json.loads((directory / "google-font-licenses.json").read_text())
self.assertEqual(0, result.returncode, result.stderr)
self.assertEqual(["Alpha Sans", "Zeta Serif"], [item["name"] for item in licenses["families"]])
def test_catalog_rejects_bool_rank_duplicate_axis_and_unreviewed_addition(self):
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
catalog = json.loads((FIXTURES / "google-catalog.json").read_text())
invalid_rank = json.loads(json.dumps(catalog))
invalid_rank["familyMetadataList"][0]["popularity"] = True
rank_path = directory / "rank.json"
rank_path.write_text(json.dumps(invalid_rank))
rank_args = self.font_args(directory)
rank_args[1:3] = ["--catalog-input", rank_path]
rank_result = self.run_command(*rank_args)
invalid_axis = json.loads(json.dumps(catalog))
axis = invalid_axis["familyMetadataList"][1]["axes"][0]
invalid_axis["familyMetadataList"][1]["axes"].append(dict(axis))
axis_path = directory / "axis.json"
axis_path.write_text(json.dumps(invalid_axis))
axis_args = self.font_args(directory)
axis_args[1:3] = ["--catalog-input", axis_path]
axis_result = self.run_command(*axis_args)
existing = directory / "existing.csv"
lines = (FIXTURES / "google-existing.csv").read_text().splitlines()
existing.write_text("\n".join(lines[:2]) + "\n")
approval_result = self.run_command(*self.font_args(directory, approve=False, existing=existing))
bad_overrides = directory / "overrides.json"
bad_overrides.write_text('{"families":{"Unknown Font":{"Keywords":"bad"}}}')
override_result = self.run_command(*self.font_args(directory, overrides=bad_overrides))
self.assertIn("invalid popularity", rank_result.stderr)
self.assertIn("duplicate axis tags", axis_result.stderr)
self.assertIn("family-set changes require --approve-changes", approval_result.stderr)
self.assertIn("Zeta Serif", approval_result.stdout)
self.assertFalse((directory / "google-fonts.csv").exists())
self.assertIn("overrides target unknown families", override_result.stderr)
def test_explicit_license_exclusion_is_reported_and_not_promoted(self):
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
metadata = json.loads((FIXTURES / "google-metadata.json").read_text())
metadata["families"] = metadata["families"][:1]
metadata["excludedFamilies"] = [{
"name": "Alpha Sans", "reason": "No matching official METADATA.pb",
"source": "https://github.com/google/fonts",
}]
metadata_path = directory / "metadata.json"
metadata_path.write_text(json.dumps(metadata))
result = self.run_command(*self.font_args(directory, metadata=metadata_path))
report = json.loads(result.stdout)
licenses = json.loads((directory / "google-font-licenses.json").read_text())
with (directory / "google-fonts.csv").open(encoding="utf-8", newline="") as handle:
rows = list(csv.DictReader(handle))
self.assertEqual(0, result.returncode, result.stderr)
self.assertEqual(["Zeta Serif"], [row["Family"] for row in rows])
self.assertEqual("needs-review", report["excludedFamilies"][0]["status"])
self.assertEqual("needs-review", licenses["excludedFamilies"][0]["status"])
def test_exclusion_sources_match_offline_validator_policy(self):
metadata = json.loads((FIXTURES / "google-metadata.json").read_text())
allowed = (
"https://fonts.google.com/specimen/Alpha+Sans",
"https://github.com/google/fonts",
"https://github.com/google/fonts/tree/main/ofl/alphasans",
)
rejected = (
"https://example.com/google/fonts",
"https://github.com/other/fonts",
"https://fonts.google.com:444/specimen/Alpha+Sans",
)
for index, source in enumerate((*allowed, *rejected)):
with self.subTest(source=source), tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
candidate = json.loads(json.dumps(metadata))
candidate["families"] = candidate["families"][1:]
candidate["excludedFamilies"] = [{
"name": "Zeta Serif",
"reason": "No matching official METADATA.pb",
"source": source,
}]
metadata_path = directory / f"metadata-{index}.json"
metadata_path.write_text(json.dumps(candidate))
result = self.run_command(*self.font_args(directory, metadata=metadata_path))
self.assertEqual(source in allowed, result.returncode == 0, result.stderr)
def test_icon_manifest_normalizes_and_records_all_import_forms(self):
with tempfile.TemporaryDirectory() as first, tempfile.TemporaryDirectory() as second:
first_path, second_path = Path(first), Path(second)
self.assertEqual(0, self.run_command(*self.icon_args(first_path)).returncode)
self.assertEqual(0, self.run_command(*self.icon_args(second_path)).returncode)
output = first_path / "phosphor-icons-upstream.json"
self.assertEqual(output.read_bytes(), (second_path / output.name).read_bytes())
manifest = json.loads(output.read_text())
self.assertEqual("2.1.1", manifest["source"]["version"])
self.assertEqual(("active", "2026-08-13"), (manifest["status"], manifest["verifiedAt"]))
self.assertEqual(["thin", "light", "regular", "bold", "fill", "duotone"], manifest["weights"])
self.assertEqual(["acorn", "arrow-left"], [icon["name"] for icon in manifest["icons"]])
arrow = manifest["icons"][1]
self.assertEqual(["arrows", "navigation"], arrow["categories"])
self.assertIn('from "@phosphor-icons/react"', arrow["clientImport"])
self.assertIn('from "@phosphor-icons/react/ssr"', arrow["ssrImport"])
self.assertEqual(2, manifest["curatedValidatedCount"])
def test_icon_refresh_rejects_invalid_schema_size_and_curated_import(self):
with tempfile.TemporaryDirectory() as raw:
directory = Path(raw)
icons = json.loads((FIXTURES / "phosphor-core.json").read_text())
invalid = json.loads(json.dumps(icons))
del invalid[0]["pascal_name"]
source = directory / "invalid.json"
source.write_text(json.dumps(invalid))
schema_result = self.run_command(*self.icon_args(directory, source))
size_args = self.icon_args(directory)
size_args[-1] = "3"
size_result = self.run_command(*size_args)
curated = (FIXTURES / "icons-curated.csv").read_text().replace("{ Acorn }", "{ Horse }")
curated_path = directory / "icons.csv"
curated_path.write_text(curated)
import_result = self.run_command(*self.icon_args(directory, curated=curated_path))
package_path = directory / "package.json"
package_path.write_text('{"name":"@phosphor-icons/core","version":"2.2.0"}')
version_result = self.run_command(*self.icon_args(directory, package=package_path))
alias_collision = json.loads(json.dumps(icons))
alias_collision[1]["alias"] = {"name": "acorn", "pascal_name": "BackArrow"}
alias_path = directory / "alias.json"
alias_path.write_text(json.dumps(alias_collision))
alias_result = self.run_command(*self.icon_args(directory, source=alias_path))
exports = json.loads((FIXTURES / "phosphor-react-exports.json").read_text())
exports["ssr"].remove("Acorn")
exports_path = directory / "exports.json"
exports_path.write_text(json.dumps(exports))
exports_result = self.run_command(*self.icon_args(directory, react_exports=exports_path))
self.assertIn("invalid official IconEntry schema", schema_result.stderr)
self.assertIn("expected 3 icons", size_result.stderr)
self.assertIn("import component does not match", import_result.stderr)
self.assertIn("version must be 2.1.1", version_result.stderr)
self.assertIn("alias collides", alias_result.stderr)
self.assertIn("React exports missing", exports_result.stderr)
if __name__ == "__main__":
unittest.main()
@@ -11,16 +11,21 @@ or directly:
python scripts/tests/test_core.py
"""
import os
import json
import subprocess
import sys
import tempfile
import unittest
from unittest.mock import patch
from pathlib import Path
SCRIPTS_DIR = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(SCRIPTS_DIR))
import core
from core import BM25, detect_domain, search, search_stack, CSV_CONFIG, AVAILABLE_STACKS
from design_system import generate_design_system, persist_design_system, DesignSystemGenerator
from design_system import DesignSystemGenerator, generate_design_system
class TestTokenizer(unittest.TestCase):
@@ -42,11 +47,80 @@ class TestTokenizer(unittest.TestCase):
self.assertEqual(bm25.tokenize("e-commerce store"), bm25.tokenize("ecommerce store"))
self.assertEqual(bm25.tokenize("dark-mode toggle"), bm25.tokenize("dark toggle"))
def test_boundary_safe_nav_normalization_preserves_existing_words(self):
bm25 = BM25()
tokens = bm25.tokenize("nav navigation navbar")
self.assertIn("navigation", tokens)
self.assertIn("navbar", tokens)
self.assertNotIn("navigationigation", tokens)
self.assertNotIn("navigationbar", tokens)
def test_punctuation_and_uk_variants_normalize_to_canonical_tokens(self):
bm25 = BM25()
tokens = bm25.tokenize("colour, organisation; behaviour customisation")
for expected in ("color", "organization", "behavior", "customization"):
self.assertIn(expected, tokens)
class TestBm25CoreBehavior(unittest.TestCase):
def test_empty_documents_produce_no_scores_or_vocab(self):
bm25 = BM25()
bm25.fit([])
self.assertEqual(bm25.score("anything"), [])
self.assertEqual(bm25.vocabulary(), [])
def test_bm25_cache_rebuilds_after_file_mtime_changes(self):
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "search.csv"
path.write_text("Name,Keywords\nAlpha,alpha token\n", encoding="utf-8")
results_a, bm25_a = core._search_csv(path, ["Name", "Keywords"], ["Name"], "alpha", 1)
self.assertEqual(results_a[0]["Name"], "Alpha")
path.write_text("Name,Keywords\nBeta,beta token\n", encoding="utf-8")
stat = path.stat()
os.utime(path, ns=(stat.st_atime_ns + 1_000_000_000, stat.st_mtime_ns + 1_000_000_000))
results_b, bm25_b = core._search_csv(path, ["Name", "Keywords"], ["Name"], "beta", 1)
self.assertEqual(results_b[0]["Name"], "Beta")
self.assertIsNot(bm25_a, bm25_b)
def test_search_uses_one_verified_rows_and_index_snapshot(self):
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "search.csv"
path.write_text("Name,Keywords\nAlpha,alpha token\n", encoding="utf-8")
original_get_bm25 = core._get_bm25
replaced = False
def replace_after_read(filepath, search_cols, data, signature=None,
cache_variant=""):
nonlocal replaced
if not replaced:
path.write_text("Name,Keywords\nBeta,beta token\n", encoding="utf-8")
replaced = True
return original_get_bm25(
filepath, search_cols, data, signature, cache_variant)
with patch.object(core, "_get_bm25", side_effect=replace_after_read):
results, _, _ = core._search_csv_detailed(
path, ["Name", "Keywords"], ["Name"], "alpha", 1)
self.assertEqual(results[0]["Name"], "Alpha")
results, _, _ = core._search_csv_detailed(
path, ["Name", "Keywords"], ["Name"], "beta", 1)
self.assertEqual(results[0]["Name"], "Beta")
class TestSearchDomains(unittest.TestCase):
"""Known query -> expected top-domain sanity checks (not exact-row pinning,
since data can grow; these assert the engine still finds *something*
relevant for each domain's core vocabulary)."""
def test_read_failure_is_not_reported_as_a_search_result(self):
failure = UnicodeDecodeError("utf-8", b"\xff", 0, 1, "invalid")
with patch("core._load_csv_snapshot", side_effect=failure):
domain = search("palette", domain="color", max_results=1)
stack = search_stack("component", "react", max_results=1)
for result in (domain, stack):
self.assertEqual(0, result["count"])
self.assertEqual([], result["results"])
self.assertRegex(result["error"], r"^Unable to read search data:")
self.assertNotIn("invalid", result["error"])
def test_ui_is_searchable_in_style_domain(self):
result = search("ui minimalism", domain="style", max_results=1)
@@ -62,12 +136,62 @@ class TestSearchDomains(unittest.TestCase):
self.assertIn("suggestions", result)
self.assertNotIn("error", result)
def test_hard_negative_query_abstains_across_registered_domains_and_stacks(self):
query = "sourdough starter crumb fermentation"
for domain in CSV_CONFIG:
with self.subTest(kind="domain", name=domain):
self.assertEqual(search(query, domain=domain, max_results=1)["count"], 0)
for stack in AVAILABLE_STACKS:
with self.subTest(kind="stack", name=stack):
self.assertEqual(search_stack(query, stack, max_results=1)["count"], 0)
def test_typo_suggestions_are_deterministic_and_retryable(self):
first = search("testimonal", domain="landing", max_results=3)
second = search("testimonal", domain="landing", max_results=3)
self.assertEqual(first["count"], 0)
self.assertEqual(first.get("suggestions"), second.get("suggestions"))
self.assertTrue(first["suggestions"], "typo path should return at least one deterministic suggestion")
retry = search(first["suggestions"][0], domain="landing", max_results=3)
self.assertGreater(retry["count"], 0)
def test_suggestions_never_repeat_the_input_or_offer_a_dead_first_retry(self):
pricing = search("pricing", domain="landing", max_results=3)
self.assertNotIn("pricing", pricing.get("suggestions", []))
minimal = search("minimal", domain="style", max_results=3)
self.assertEqual(1, minimal["count"])
self.assertEqual(
"minimalism-and-swiss-style", minimal["results"][0]["Style ID"]
)
def test_unknown_programmatic_domain_keeps_legacy_style_fallback(self):
result = search("minimalism", domain="unknown", max_results=1)
self.assertEqual(result["domain"], "unknown")
self.assertEqual(result["file"], CSV_CONFIG["style"]["file"])
self.assertGreater(result["count"], 0)
def test_unsupported_icon_library_abstains_instead_of_returning_other_library(self):
result = search("lucide icon", diagnostics=True)
self.assertEqual(result["domain"], "icons")
self.assertEqual(result["count"], 0)
self.assertEqual(result["diagnostics"]["reason"], "unsupported-library")
def test_every_configured_domain_file_exists_and_is_searchable(self):
for domain, config in CSV_CONFIG.items():
with self.subTest(domain=domain):
result = search("design", domain=domain, max_results=1)
self.assertNotIn("error", result, f"domain '{domain}' failed: {result.get('error')}")
def test_chart_output_keeps_legacy_grade_during_risk_migration(self):
result = search("time series chart", domain="chart", max_results=1)
self.assertEqual(result["count"], 1)
self.assertEqual(
result["results"][0]["Accessibility Grade"],
"deprecated: use Accessibility Risk",
)
self.assertIn("Accessibility Risk", result["results"][0])
def test_every_stack_file_exists_and_is_searchable(self):
for stack in AVAILABLE_STACKS:
with self.subTest(stack=stack):
@@ -83,16 +207,67 @@ class TestDomainDetection(unittest.TestCase):
self.assertEqual(detect_domain("accessibility contrast wcag"), "ux")
def test_ambiguous_query_returns_runner_up(self):
domain, runner_up = detect_domain("font pairing elegant crypto", return_scores=True)
domain, _ = detect_domain("font pairing elegant crypto", return_scores=True)
self.assertIsNotNone(domain)
# runner_up may be None if the winning domain has no close second --
# this just verifies the call shape works without raising.
def test_empty_query_falls_back_to_style(self):
self.assertEqual(detect_domain("...!!!???"), "style")
def test_router_prioritizes_color_intent_over_generic_product_terms(self):
self.assertEqual(detect_domain("semantic color tokens palette"), "color")
def test_router_prioritizes_icons_when_icon_library_and_icon_intent_present(self):
self.assertEqual(detect_domain("lucide search icon outline"), "icons")
def test_router_prioritizes_typography_for_font_pairing_queries(self):
self.assertEqual(detect_domain("font pairing elegant serif body font"), "typography")
def test_router_prioritizes_chart_queries_over_generic_product_keywords(self):
self.assertEqual(detect_domain("time series chart forecast"), "chart")
def test_hash_only_routes_color_for_a_valid_hex_literal(self):
self.assertNotEqual(detect_domain("C# WPF desktop app"), "color")
self.assertEqual(detect_domain("use #ff00aa as the accent"), "color")
def test_product_router_keeps_high_signal_service_aliases(self):
self.assertEqual(detect_domain("beauty spa"), "product")
self.assertEqual(detect_domain("salon booking"), "product")
def test_native_drag_intent_beats_generic_react_token(self):
self.assertEqual(detect_domain("drag reorder react native"), "web")
def test_every_router_term_is_searchable_or_has_a_corpus_rewrite(self):
for domain, keywords in core._domain_keywords().items():
config = CSV_CONFIG[domain]
path = core.DATA_DIR / config["file"]
index = core._get_bm25(path, config["search_cols"], core._load_csv(path))
vocabulary = set(index.vocabulary())
for keyword in keywords:
with self.subTest(domain=domain, keyword=keyword):
searchable = bool(set(index.tokenize(keyword)) & vocabulary)
explicitly_routing_only = keyword in core._DOMAIN_QUERY_REWRITES.get(domain, {})
self.assertTrue(searchable or explicitly_routing_only)
class TestPersistence(unittest.TestCase):
def test_concurrent_non_force_persist_has_one_writer(self):
with tempfile.TemporaryDirectory() as tmp:
search_script = SCRIPTS_DIR / "search.py"
processes = [subprocess.Popen(
[sys.executable, str(search_script), f"saas dashboard {index}",
"--design-system", "--persist", "--project-name", "Race Probe",
"--output-dir", tmp, "--json"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
) for index in range(8)]
statuses = []
for process in processes:
stdout, stderr = process.communicate(timeout=30)
self.assertEqual(process.returncode, 0, stderr)
statuses.append(json.loads(stdout)["persistence"]["status"])
self.assertEqual(statuses.count("success"), 1)
self.assertEqual(statuses.count("skipped_exists"), 7)
def test_persist_then_skip_then_force(self):
with tempfile.TemporaryDirectory() as tmp:
result = generate_design_system("saas dashboard", "Test Project", persist=True, output_dir=tmp)
@@ -106,6 +281,24 @@ class TestPersistence(unittest.TestCase):
self.assertEqual(result2["persistence"]["status"], "skipped_exists")
self.assertEqual(master.read_text(encoding="utf-8"), original_content)
# A new page override may be added without rewriting the existing Master.
page_result = generate_design_system(
"checkout form", "Test Project", persist=True, page="Checkout", output_dir=tmp
)
self.assertEqual(page_result["persistence"]["status"], "success")
self.assertEqual(master.read_text(encoding="utf-8"), original_content)
page_file = Path(tmp) / "design-system" / "test-project" / "pages" / "checkout.md"
self.assertEqual(page_result["persistence"]["created_files"], [str(page_file)])
self.assertTrue(page_file.exists())
# Existing page overrides are protected by the same default no-overwrite rule.
page_content = page_file.read_text(encoding="utf-8")
page_result2 = generate_design_system(
"different checkout", "Test Project", persist=True, page="Checkout", output_dir=tmp
)
self.assertEqual(page_result2["persistence"]["status"], "skipped_exists")
self.assertEqual(page_file.read_text(encoding="utf-8"), page_content)
# With force=True it must overwrite.
result3 = generate_design_system("ecommerce luxury", "Test Project", persist=True, output_dir=tmp, force=True)
self.assertEqual(result3["persistence"]["status"], "success")
@@ -130,5 +323,21 @@ class TestReasoningMatch(unittest.TestCase):
self.assertIsInstance(rule, dict)
class TestDiagnosticsContracts(unittest.TestCase):
def test_diagnostics_opt_in_is_additive_for_domain_search(self):
baseline = search("minimalism", domain="style", max_results=1)
diagnosed = search("minimalism", domain="style", max_results=1, diagnostics=True)
self.assertEqual(set(baseline.keys()), set(diagnosed.keys()) - {"diagnostics"})
self.assertIn("diagnostics", diagnosed)
self.assertIn("top_score", diagnosed["diagnostics"])
self.assertIn("query_rewrites", diagnosed["diagnostics"])
def test_diagnostics_opt_in_is_additive_for_stack_search(self):
baseline = search_stack("performance", "react", max_results=1)
diagnosed = search_stack("performance", "react", max_results=1, diagnostics=True)
self.assertEqual(set(baseline.keys()), set(diagnosed.keys()) - {"diagnostics"})
self.assertIn("diagnostics", diagnosed)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,198 @@
#!/usr/bin/env python3
"""Semantic quality contracts for the core UI/UX datasets."""
import csv
import sys
import unittest
from pathlib import Path
SCRIPTS_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = SCRIPTS_DIR.parent / "data"
sys.path.insert(0, str(SCRIPTS_DIR))
from core import search # noqa: E402
from validate_data import ( # noqa: E402
CHART_NON_COLOR_GUIDANCE,
CHART_RISKS,
CHART_TEXT_FALLBACK,
COLOR_CONTRAST_PAIRS,
CSS_IMPORT,
ICON_CONTEXTS,
ICON_ROLES,
ICON_USAGE_REQUIREMENTS,
WCAG_GRADE,
_check_chart_contract,
_check_icon_contract,
_check_typography_contract,
_configured_font_names,
_font_families,
_font_names,
contrast_ratio,
)
def read_rows(name):
with (DATA_DIR / name).open(encoding="utf-8", newline="") as handle:
return list(csv.DictReader(handle))
class TestSemanticColors(unittest.TestCase):
def test_declared_text_and_ui_pairs_meet_role_thresholds(self):
for row in read_rows("colors.csv"):
for foreground, (background, role, minimum) in COLOR_CONTRAST_PAIRS.items():
with self.subTest(
product=row["Product Type"], pair=foreground, role=role):
self.assertGreaterEqual(
contrast_ratio(row[foreground], row[background]), minimum)
def test_destructive_tokens_are_not_success_green(self):
for row in read_rows("colors.csv"):
value = row["Destructive"].lstrip("#")
red, green, blue = (int(value[index:index + 2], 16)
for index in (0, 2, 4))
with self.subTest(product=row["Product Type"]):
self.assertFalse(green > red * 1.1 and green > blue * 1.1)
class TestAccessibilityGuidance(unittest.TestCase):
def test_wcag_22_topics_have_explicit_rows_and_are_retrievable(self):
rows = read_rows("ux-guidelines.csv")
issues = {row["Issue"] for row in rows}
expected_platforms = {
"Focus Not Obscured (Minimum)": "Web",
"Focus Not Obscured (Enhanced)": "Web",
"Focus Appearance": "Web",
"Dragging Movements": "All",
"Target Size (Minimum)": "Web",
"Consistent Help": "All",
"Redundant Entry": "All",
"Accessible Authentication (Minimum)": "All",
"Auto-Rotating Content Controls": "All",
}
self.assertTrue(expected_platforms.keys() <= issues)
for issue, expected_platform in expected_platforms.items():
with self.subTest(issue=issue):
row = next(row for row in rows if row["Issue"] == issue)
self.assertEqual(row["Platform"], expected_platform)
self.assertIn(row["Severity"], {"Medium", "High", "Critical"})
self.assertNotEqual(row["Do"], row["Description"])
result = search(issue, domain="ux", max_results=3)
self.assertTrue(any(row.get("Issue") == issue for row in result["results"]))
def test_native_and_web_target_sizes_remain_distinct(self):
native = next(row for row in read_rows("app-interface.csv")
if row["Issue"] == "Touch Target Size")
web = next(row for row in read_rows("ux-guidelines.csv")
if row["Issue"] == "Target Size (Minimum)")
native_text = " ".join(native.values())
web_text = " ".join(web.values())
self.assertIn("44pt", native_text)
self.assertIn("48dp", native_text)
self.assertIn("24 CSS px", web_text)
def test_motion_recipes_offer_reduced_motion_or_user_control(self):
for row in read_rows("motion.csv"):
text = " ".join(row.values()).casefold()
with self.subTest(row=row["No"], category=row["Category"]):
self.assertTrue(
"reduced-motion" in text or "user-controlled" in text,
"every motion recipe needs an explicit opt-out",
)
class TestChartsTypographyAndIcons(unittest.TestCase):
def test_mutated_accessibility_and_import_contracts_fail(self):
mutations = []
chart = dict(read_rows("charts.csv")[0])
chart["A11y Fallback"] = "A visible table is available."
mutations.append((_check_chart_contract, chart, "keyboard"))
typography = dict(read_rows("typography.csv")[0])
typography["CSS Import"] = (
"@import url('https://fonts.googleapis.com/css2?family=Comic+Sans');"
)
mutations.append((_check_typography_contract, typography, "differ"))
missing_weight = dict(read_rows("typography.csv")[0])
missing_weight["Notes"] += " Recommended weight 900."
mutations.append((_check_typography_contract, missing_weight, "weights"))
icon = dict(read_rows("icons.csv")[0])
icon["Import Code"] = "import { IconName } from '@phosphor-icons/react'"
mutations.append((_check_icon_contract, icon, "import"))
for checker, row, expected in mutations:
with self.subTest(checker=checker.__name__):
problems = []
checker([row], problems)
self.assertTrue(any(expected in problem for problem in problems))
def test_chart_risk_is_not_a_conformance_grade(self):
for row in read_rows("charts.csv"):
with self.subTest(data_type=row["Data Type"]):
self.assertIn(row["Accessibility Risk"], CHART_RISKS)
self.assertEqual(
row["Accessibility Grade"],
"deprecated: use Accessibility Risk",
)
text = " ".join((row["Accessibility Notes"], row["A11y Fallback"]))
self.assertIsNone(WCAG_GRADE.search(text))
self.assertIsNotNone(CHART_TEXT_FALLBACK.search(text.casefold()))
self.assertIsNotNone(
CHART_NON_COLOR_GUIDANCE.search(text.casefold())
)
self.assertIn("keyboard", text.casefold())
def test_named_fonts_match_google_import_css_import_and_tailwind_config(self):
for row in read_rows("typography.csv"):
url_families = _font_families(row["Google Fonts URL"])
families = _font_names(url_families)
configured = _configured_font_names(row["Tailwind Config"])
named = {row["Heading Font"], row["Body Font"]}
with self.subTest(pairing=row["Font Pairing Name"]):
self.assertTrue(named <= families)
self.assertTrue(named <= configured)
match = CSS_IMPORT.fullmatch(row["CSS Import"])
self.assertIsNotNone(match)
self.assertEqual(
sorted(url_families),
sorted(_font_families(match.group(2))),
)
def test_icon_semantics_are_explicit_and_imports_are_concrete(self):
for row in read_rows("icons.csv"):
with self.subTest(icon=row["Icon Name"]):
self.assertIn(row["Semantic Role"], ICON_ROLES)
self.assertEqual(set(row["Allowed Contexts"].split("|")), ICON_CONTEXTS)
self.assertNotRegex(row["Usage"], r"[\u3400-\u9fff]")
self.assertNotIn("IconName", row["Import Code"])
for requirement in ICON_USAGE_REQUIREMENTS:
self.assertRegex(row["Usage"].casefold(), requirement)
def test_natural_accessibility_queries_are_retrievable(self):
cases = {
"chart": ("keyboard accessible chart", "keyboard"),
"landing": ("accessible drag interaction", "keyboard controls"),
"icons": ("decorative icon aria hidden", "aria-hidden"),
"gsap": ("stop animation offscreen", "visibility"),
"ux": ("error summary validation", "error summary"),
}
for domain, (query, expected) in cases.items():
with self.subTest(domain=domain, query=query):
result = search(query, domain=domain, max_results=3)
self.assertGreater(result["count"], 0)
self.assertIn(
expected.casefold(),
" ".join(str(value) for value in result["results"][0].values()).casefold(),
)
class TestCurrentReactGuidance(unittest.TestCase):
def test_effect_event_is_scoped_and_community_use_latest_is_removed(self):
rows = read_rows("react-performance.csv")
effect_event = next(row for row in rows if row["Issue"] == "Effect Events")
text = " ".join(effect_event.values()).casefold()
self.assertIn("inside effects", text)
self.assertIn("dependencies", text)
self.assertFalse(any("uselatest" in " ".join(row.values()).casefold()
for row in rows))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,421 @@
#!/usr/bin/env python3
"""Cross-file semantic contracts for curated design data."""
import copy
import csv
import json
import re
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
SCRIPTS_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = SCRIPTS_DIR.parent / "data"
sys.path.insert(0, str(SCRIPTS_DIR))
from core import AVAILABLE_STACKS, STACK_CONFIG # noqa: E402
from design_system import DesignSystemGenerator # noqa: E402
from reasoning_contract import apply_decision_rules, parse_decision_rules # noqa: E402
import validate_data # noqa: E402
from validate_data import _check_reasoning_contract # noqa: E402
def read_rows(name):
with (DATA_DIR / name).open(encoding="utf-8", newline="") as handle:
return list(csv.DictReader(handle))
def split_values(value, delimiter):
return [part.strip() for part in value.split(delimiter) if part.strip()]
def style_identities(row):
return [
row["Style ID"], row["Style Category"],
*split_values(row["Aliases"], "|"),
]
class TestStyleIdentityContract(unittest.TestCase):
def setUp(self):
self.styles = read_rows("styles.csv")
def test_ids_aliases_status_and_parents_are_unambiguous(self):
ids = {row["Style ID"] for row in self.styles}
self.assertEqual(len(ids), len(self.styles))
aliases = {}
for row in self.styles:
style_id = row["Style ID"]
self.assertRegex(style_id, r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
self.assertIn(row["Status"], {"active", "supplemental", "deprecated"})
parent = row["Parent Style ID"]
if parent:
self.assertIn(parent, ids)
self.assertNotEqual(parent, style_id)
if row["Status"] == "supplemental":
self.assertTrue(parent)
if row["Status"] == "deprecated":
has_redirect = bool(row["Replacement Domain"] and row["Replacement ID"])
self.assertNotEqual(bool(parent), has_redirect)
for alias in split_values(row["Aliases"], "|"):
self.assertNotIn(alias.casefold(), aliases)
aliases[alias.casefold()] = style_id
def test_every_product_and_reasoning_style_reference_resolves(self):
lookup = {}
for row in self.styles:
lookup.update(
(identity.casefold(), row["Style ID"])
for identity in style_identities(row)
)
references = []
for row in read_rows("products.csv"):
references.extend(split_values(row["Primary Style Recommendation"], "+"))
references.extend(split_values(row["Secondary Styles"], ","))
for row in read_rows("ui-reasoning.csv"):
references.extend(split_values(row["Style_Priority"], "+"))
unresolved = sorted({
reference for reference in references
if reference.casefold() not in lookup
})
self.assertEqual([], unresolved)
class TestReasoningContract(unittest.TestCase):
def test_known_product_sets_match_exactly(self):
product_rows = read_rows("products.csv")
color_rows = read_rows("colors.csv")
reasoning_rows = read_rows("ui-reasoning.csv")
self.assertEqual([192, 192, 192], [
len(product_rows), len(color_rows), len(reasoning_rows)])
products = {row["Product Type"] for row in product_rows}
colors = {row["Product Type"] for row in color_rows}
reasoning = {row["UI_Category"] for row in reasoning_rows}
self.assertEqual(products, colors)
self.assertEqual(products, reasoning)
self.assertEqual(len(products), 192)
def test_decision_rules_use_closed_array_grammar(self):
for row in read_rows("ui-reasoning.csv"):
with self.subTest(category=row["UI_Category"]):
parsed = parse_decision_rules(row["Decision_Rules"])
self.assertTrue(all(isinstance(actions, list) for actions in parsed.values()))
def test_duplicate_unknown_keys_and_unknown_actions_fail_closed(self):
invalid = (
'{"must_have":["constraint:first"],"must_have":["constraint:second"]}',
'{"if_not_supported":["constraint:test"]}',
'{"must_have":["execute:arbitrary"]}',
'{"must_have":[["constraint:nested"]]}',
'{"must_have":[{"constraint":"nested"}]}',
)
for raw in invalid:
with self.subTest(raw=raw), self.assertRaises(ValueError):
parse_decision_rules(raw)
def test_must_have_and_explicit_signals_are_applied_and_reported(self):
rules = parse_decision_rules(
'{"must_have":["constraint:keyboard-navigation"],'
'"if_mobile":["constraint:optimize-touch-targets"]}')
desktop = apply_decision_rules(rules, "accessible government portal")
mobile = apply_decision_rules(rules, "accessible mobile government portal")
self.assertEqual(desktop["constraints"], ["keyboard-navigation"])
self.assertEqual(
mobile["constraints"], ["keyboard-navigation", "optimize-touch-targets"])
self.assertEqual(
[item["condition"] for item in mobile["activated"]],
["must_have", "if_mobile"],
)
def test_generator_matches_reasoning_exactly_and_defaults_only_for_unknown(self):
generator = DesignSystemGenerator()
categories = [row["Product Type"] for row in read_rows("products.csv")]
for category in categories:
with self.subTest(category=category):
self.assertEqual(generator._find_reasoning_rule(category)["UI_Category"], category)
self.assertEqual(generator._find_reasoning_rule("Government"), {})
self.assertTrue(generator._apply_reasoning("External Unknown", "unknown")["is_default"])
def test_reasoning_patterns_reference_landing_identities(self):
patterns = set()
for row in read_rows("landing.csv"):
patterns.add(row["Pattern Name"])
patterns.update(alias for alias in row["Aliases"].split("|") if alias)
reasoning = read_rows("ui-reasoning.csv")
self.assertEqual(192, len(reasoning))
for row in reasoning:
with self.subTest(category=row["UI_Category"]):
self.assertIn(row["Recommended_Pattern"], patterns)
def test_every_known_product_generates_a_traceable_landing_pattern(self):
generator = DesignSystemGenerator()
patterns = {row["Pattern Name"] for row in read_rows("landing.csv")}
for category in (row["Product Type"] for row in read_rows("products.csv")):
with self.subTest(category=category):
result = generator.generate(category)
self.assertIn(result["source_identities"]["landing"], patterns)
def test_representative_new_products_generate_traceable_sources(self):
generator = DesignSystemGenerator()
styles = {row["Style ID"] for row in read_rows("styles.csv")}
colors = {row["Product Type"] for row in read_rows("colors.csv")}
typography = {row["Font Pairing Name"] for row in read_rows("typography.csv")}
patterns = {row["Pattern Name"] for row in read_rows("landing.csv")}
cases = {
"government grant portal accessible trustworthy": "Grant / Funding Portal",
"API developer portal documentation": "API Developer Portal",
"academic journal scholarly publishing accessible": "Academic Journal / Scholarly Publishing",
"patient portal mobile secure": "Patient Portal / Health Records",
"status page outage monitoring": "Status Page / Incident Management",
}
for query, category in cases.items():
with self.subTest(query=query):
result = generator.generate(query)
sources = result["source_identities"]
self.assertEqual(category, result["category"])
self.assertFalse(result["reasoning_default"])
self.assertEqual(category, sources["product"])
self.assertEqual(category, sources["reasoning"])
self.assertIn(sources["style"], styles)
self.assertIn(sources["color"], colors)
self.assertIn(sources["typography"], typography)
self.assertIn(sources["landing"], patterns)
def test_constraints_reach_domain_queries(self):
generator = DesignSystemGenerator()
calls = []
def capture(query, domain, max_results):
calls.append((domain, query))
return {"domain": domain, "count": 0, "results": []}
reasoning = {
"pattern": "Unmapped Pattern",
"color_mood": "Trustworthy",
"typography_mood": "Readable",
"constraints": ["keyboard-navigation", "touch-targets"],
}
with patch("design_system.search", side_effect=capture):
generator._multi_domain_search(
"public portal", "Government Portal", reasoning, ["Minimalism"])
queried = {domain: query for domain, query in calls}
for domain in ("style", "color", "typography", "landing"):
with self.subTest(domain=domain):
self.assertIn("keyboard navigation", queried[domain])
def test_canonical_style_priority_is_not_limited_to_bm25_top_three(self):
generator = DesignSystemGenerator()
unrelated = [
generator._resolve_style("Kinetic Brutalism (Mobile)"),
generator._resolve_style("Glassmorphism"),
]
selected = generator._select_best_match(unrelated, ["Brutalism"])
self.assertEqual("brutalism", selected["Style ID"])
def test_duplicate_semantic_reasoning_labels_fail_validation(self):
product = {"Product Type": "Duplicate"}
color = {"Product Type": "Duplicate"}
reasoning = {
"UI_Category": "Duplicate", "Decision_Rules": "{}", "Confidence": ""
}
problems = []
_check_reasoning_contract(
[product, dict(product)], [color, dict(color)],
[reasoning, dict(reasoning)], set(), set(), problems,
)
self.assertTrue(any("duplicate" in problem.lower() for problem in problems))
def test_every_exact_product_label_resolves_to_itself(self):
generator = DesignSystemGenerator()
for row in read_rows("products.csv"):
category = row["Product Type"]
with self.subTest(category=category):
result = generator.generate(category)
reasoning = generator._apply_reasoning(category, category)
expected = [
generator._resolve_style(priority).get("Style ID")
for priority in reasoning["style_priority"]
]
expected = [style_id for style_id in expected if style_id]
self.assertEqual(category, result["category"])
self.assertFalse(result["reasoning_default"])
self.assertTrue(expected)
self.assertEqual(expected[0], result["style"]["id"])
def test_style_aliases_have_one_exact_owner(self):
generator = DesignSystemGenerator()
self.assertEqual(
generator._resolve_style("Minimalism")["Style ID"],
"minimalism-and-swiss-style",
)
self.assertEqual(generator._resolve_style("Clean Science"), {})
self.assertEqual(
generator._resolve_style("Holographic/HUD")["Style ID"],
"hud-sci-fi-fui",
)
class TestLandingAndStackContract(unittest.TestCase):
def test_landing_sections_use_one_delimiter(self):
for row in read_rows("landing.csv"):
with self.subTest(pattern=row["Pattern Name"]):
sections = row["Section Order"].split(" > ")
self.assertGreaterEqual(len(sections), 2)
self.assertTrue(all(section.strip() for section in sections))
self.assertFalse(any(re.match(r"^\d+\.\s", section) for section in sections))
def test_stack_schema_is_additive_and_uniform(self):
for stack in AVAILABLE_STACKS:
path = DATA_DIR / STACK_CONFIG[stack]["file"]
with path.open(encoding="utf-8", newline="") as handle:
reader = csv.DictReader(handle)
self.assertTrue({"Applies To", "Status", "Verified At"} <= set(reader.fieldnames or []))
for row in reader:
self.assertIn(row["Status"], {"active", "supplemental", "deprecated", "unverified"})
def test_provenance_sidecar_has_stable_shape(self):
payload = json.loads((DATA_DIR / "data-provenance.json").read_text(encoding="utf-8"))
self.assertEqual(payload["schemaVersion"], 1)
self.assertIsInstance(payload["records"], list)
for record in payload["records"]:
self.assertTrue({"entityKind", "entityId", "sourceFile", "status", "verifiedAt", "sources"} <= set(record))
self.assertIsInstance(record["sources"], list)
source_types = {source.get("type") for source in record["sources"]}
if source_types <= {"derived"}:
self.assertEqual("needs-review", record["sla"])
def test_provenance_rejects_bad_shapes_enums_and_hosts_without_crashing(self):
canonical = json.loads(
(DATA_DIR / "data-provenance.json").read_text(encoding="utf-8")
)
cases = [[], None, "invalid"]
malformed_record = copy.deepcopy(canonical)
malformed_record["records"].append(None)
cases.append(malformed_record)
malformed_source = copy.deepcopy(canonical)
malformed_source["records"][0]["sources"] = [None]
cases.append(malformed_source)
unapproved_source = copy.deepcopy(canonical)
official = next(
record for record in unapproved_source["records"]
if any(source.get("type") == "official" for source in record["sources"])
)
official["sources"] = [
{"type": "official", "ref": "https://evil.example/fake"}
]
cases.append(unapproved_source)
invalid_enums = copy.deepcopy(canonical)
invalid_enums["records"][0].update(
status="invented", sla="whenever", confidence=float("nan")
)
cases.append(invalid_enums)
reasoning, styles = read_rows("ui-reasoning.csv"), read_rows("styles.csv")
for index, payload in enumerate(cases):
with self.subTest(case=index), tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / "data-provenance.json").write_text(
json.dumps(payload), encoding="utf-8"
)
problems = []
with patch.object(validate_data, "DATA_DIR", root):
validate_data._check_provenance(reasoning, styles, problems)
self.assertTrue(problems)
def test_dataset_provenance_scope_binds_real_rows_and_fields(self):
problems = []
valid = validate_data._valid_dataset_source_key(
"colors.csv", {"Scope": "No 1-192; Notes field"},
("dataset-contract", "valid"), problems,
)
self.assertTrue(valid)
self.assertEqual([], problems)
stack_problems = []
self.assertTrue(validate_data._valid_dataset_source_key(
"stacks/html-tailwind.csv", {"Scope": "No 57-59; Guideline field"},
("dataset-contract", "valid-stack"), stack_problems,
))
self.assertEqual([], stack_problems)
for source_file, source_key in (
("unknown.csv", {"Scope": "No 1; Notes field"}),
("colors.csv", {"Scope": "No 999; Notes field"}),
("colors.csv", {"Scope": "No 1; Invented Field"}),
):
with self.subTest(source_file=source_file, source_key=source_key):
problems = []
self.assertFalse(validate_data._valid_dataset_source_key(
source_file, source_key, ("dataset-contract", "bad"), problems
))
self.assertTrue(problems)
class TestGeneratedCatalogContract(unittest.TestCase):
def load_json(self, name):
return json.loads((DATA_DIR / name).read_text(encoding="utf-8"))
def test_canonical_catalogs_and_provenance_are_release_ready(self):
problems = validate_data.validate()
self.assertEqual([], [problem for problem in problems if "catalog" in problem])
def test_font_license_and_typography_drift_fail_closed(self):
fonts = read_rows("google-fonts.csv")
licenses = self.load_json("google-font-licenses.json")
licenses["families"][0]["license"] = "UNKNOWN"
problems = []
validate_data._check_font_catalog(
fonts, licenses, read_rows("typography.csv"), problems
)
self.assertTrue(any("invalid active family" in problem for problem in problems))
missing_font = copy.deepcopy(read_rows("typography.csv"))
missing_font[0]["Google Fonts URL"] = (
"https://fonts.googleapis.com/css2?family=Invented+Sans:wght@400"
)
problems = []
validate_data._check_font_catalog(
fonts, self.load_json("google-font-licenses.json"), missing_font, problems
)
self.assertTrue(any("absent from approved catalog" in problem for problem in problems))
def test_font_source_revision_and_exclusion_policy_fail_closed(self):
fonts = read_rows("google-fonts.csv")
typography = read_rows("typography.csv")
licenses = self.load_json("google-font-licenses.json")
licenses["excludedFamilies"][0]["source"] = "https://github.com/google/fonts"
problems = []
validate_data._check_font_catalog(fonts, licenses, typography, problems)
self.assertFalse(any("invalid exclusion" in problem for problem in problems))
licenses["excludedFamilies"][0]["source"] = "https://github.com/other/fonts"
licenses["source"]["revision"] = "main"
problems = []
validate_data._check_font_catalog(fonts, licenses, typography, problems)
self.assertTrue(any("invalid exclusion" in problem for problem in problems))
self.assertTrue(any("invalid source revision" in problem for problem in problems))
def test_curated_icon_and_summary_drift_fail_closed(self):
manifest = self.load_json("phosphor-icons-upstream.json")
manifest["icons"][0]["clientImport"] = (
'import { Wrong } from "@phosphor-icons/react"'
)
problems = []
validate_data._check_phosphor_catalog(read_rows("icons.csv"), manifest, problems)
self.assertTrue(any("invalid identity or imports" in problem for problem in problems))
summary = self.load_json("catalog-summary.json")
summary["counts"]["googleFonts"] -= 1
problems = []
validate_data._check_catalog_summary(
summary,
self.load_json("google-font-licenses.json"),
self.load_json("phosphor-icons-upstream.json"),
problems,
)
self.assertTrue(any("stale count for googleFonts" in problem for problem in problems))
if __name__ == "__main__":
unittest.main(verbosity=2)
@@ -25,6 +25,7 @@ sys.path.insert(0, str(SCRIPTS_DIR))
from design_system import ( # noqa: E402
_filter_anti_patterns_for_mode,
_contrast_ratio,
_palette_is_dark,
_query_wants_dark,
_relative_luminance,
@@ -106,6 +107,15 @@ class TestPaletteSelection(unittest.TestCase):
def test_empty_results(self):
self.assertEqual(_select_palette_for_mode([], "dark"), {})
def test_category_identity_wins_over_unrelated_dark_palette(self):
chosen = _select_palette_for_mode(
[LIGHT_PALETTE, DARK_PALETTE], "dark", "SaaS")
self.assertEqual("SaaS", chosen["Product Type"])
self.assertEqual("derived-dark", chosen["_mode_derivation"])
self.assertTrue(_palette_is_dark(chosen))
self.assertGreaterEqual(
_contrast_ratio(chosen["Ring"], chosen["Background"]), 3.0)
class TestAntiPatternGating(unittest.TestCase):
def test_dark_clause_dropped_others_kept(self):
@@ -138,6 +148,26 @@ class TestEndToEndCoherence(unittest.TestCase):
"dark-mode query returned a light background: {}".format(background),
)
def test_generator_exports_every_semantic_foreground_pair(self):
colors = DesignSystemGenerator().generate("SaaS dashboard")["colors"]
pairs = (
("on_primary", "primary"),
("on_secondary", "secondary"),
("on_accent", "accent"),
("foreground", "background"),
("card_foreground", "card"),
("muted_foreground", "muted"),
("on_destructive", "destructive"),
)
for foreground, background in pairs:
with self.subTest(pair=foreground):
self.assertTrue(colors[foreground])
self.assertTrue(colors[background])
self.assertGreaterEqual(
_contrast_ratio(colors[foreground], colors[background]), 4.5
)
self.assertEqual(colors["on_cta"], colors["on_accent"])
def test_dark_query_foreground_is_lighter_than_background(self):
ds = DesignSystemGenerator().generate(self.QUERY)
background = _relative_luminance(ds["colors"]["background"])
@@ -0,0 +1,176 @@
#!/usr/bin/env python3
"""Freshness and migration contracts for native, desktop, and 3D stacks."""
import csv
import sys
import unittest
from pathlib import Path
from urllib.parse import urlsplit
SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
from core import (DATA_DIR, STACK_CONFIG, STACK_CURRENT_APPLICABILITY,
search_stack) # noqa: E402
from validate_data import STACK_OFFICIAL_HOSTS # noqa: E402
STACKS = {
"react-native", "flutter", "swiftui", "jetpack-compose", "avalonia",
"uwp", "winui", "wpf", "uno", "javafx", "threejs", "laravel",
}
def _rows(stack):
path = DATA_DIR / STACK_CONFIG[stack]["file"]
with path.open(encoding="utf-8", newline="") as handle:
return list(csv.DictReader(handle))
class TestNativeDesktopStackFreshness(unittest.TestCase):
def test_rows_have_final_freshness_metadata(self):
for stack in STACKS:
for row in _rows(stack):
with self.subTest(stack=stack, row=row["No"]):
expected = "deprecated" if stack == "uwp" else "active"
self.assertEqual(row["Status"], expected)
self.assertTrue(row["Applies To"].startswith(
STACK_CURRENT_APPLICABILITY[stack]))
self.assertRegex(row["Verified At"], r"^\d{4}-\d{2}-\d{2}$")
self.assertEqual("legacy" in row["Applies To"], stack == "uwp")
def test_high_impact_rows_use_official_sources(self):
for stack in STACKS:
for row in _rows(stack):
if row["Severity"] not in {"Critical", "High"}:
continue
with self.subTest(stack=stack, row=row["No"]):
parsed = urlsplit(row["Docs URL"])
self.assertEqual(parsed.scheme, "https")
self.assertIn(parsed.hostname, STACK_OFFICIAL_HOSTS[stack])
def test_current_mobile_contracts(self):
cases = {
("react-native", "Hermes bundled default engine"): "hermes",
("flutter", "predictive back result callback"): "onpopinvokedwithresult",
("flutter", "accessible nonlinear text scaling"): "textscaler",
("swiftui", "multicolumn navigation sidebar detail"): "navigationsplitview",
}
for (stack, query), expected in cases.items():
with self.subTest(stack=stack):
result = search_stack(query, stack, max_results=1)
self.assertEqual(result["count"], 1)
recommended = " ".join(
result["results"][0][field]
for field in ("Guideline", "Do", "Code Good")
).casefold()
self.assertIn(expected, recommended)
def test_windows_current_and_maintenance_lanes_are_visible(self):
current = search_stack("new Windows desktop app Windows App SDK", "winui")
legacy = search_stack("UWP maintenance x:Bind migration", "uwp")
self.assertGreater(current["count"], 0)
self.assertGreater(legacy["count"], 0)
self.assertEqual({row["Status"] for row in current["results"]}, {"active"})
self.assertEqual({row["Status"] for row in legacy["results"]}, {"deprecated"})
self.assertTrue(any("winui" in " ".join(row.values()).casefold()
for row in legacy["results"]))
successor = search_stack(
"which Windows UI framework should a brand new app choose instead of legacy UWP",
"uwp", max_results=1,
)
self.assertEqual("Prefer WinUI 3 for new projects", successor["results"][0]["Guideline"])
def test_old_version_without_curated_rows_abstains(self):
cases = {
"react-native": "React Native 0.75 Hermes",
"flutter": "Flutter SDK 3.22 back navigation",
"swiftui": "iOS 15 SwiftUI navigation",
"avalonia": "Avalonia UI v11 storage picker",
"winui": "WinUI SDK 2 desktop app",
"javafx": "JavaFX SDK 21 table view",
"threejs": "Three.js r128 OrbitControls",
"laravel": "Laravel 12 validation",
}
for stack, query in cases.items():
with self.subTest(stack=stack):
self.assertEqual(search_stack(query, stack)["count"], 0)
def test_migration_intent_returns_current_replacements(self):
cases = {
"flutter": "replace deprecated WillPopScope with current Flutter API",
"react-native": "upgrade legacy Hermes configuration",
"threejs": "replace deprecated outputEncoding with current color space",
}
for stack, query in cases.items():
with self.subTest(stack=stack):
result = search_stack(query, stack)
self.assertGreater(result["count"], 0)
self.assertEqual({row["Status"] for row in result["results"]}, {"active"})
versioned_cases = {
"react-native": "upgrade React Native 0.75 to React Native 0.86 Hermes",
"flutter": "upgrade Flutter 3.22 to Flutter 3.44 predictive back",
"threejs": "upgrade Three.js r128 to r185 outputColorSpace",
}
for stack, query in versioned_cases.items():
with self.subTest(stack=stack, query=query):
result = search_stack(query, stack)
self.assertGreater(result["count"], 0)
self.assertEqual({row["Status"] for row in result["results"]}, {"active"})
def test_standalone_current_and_deprecated_identifiers_resolve_replacement(self):
cases = {
("flutter", "onPopInvokedWithResult"): "PopScope",
("flutter", "TextScaler"): "large fonts",
("threejs", "outputColorSpace"): "Color Space",
("threejs", "outputEncoding"): "Color Space",
}
for (stack, query), guideline in cases.items():
with self.subTest(stack=stack, query=query):
result = search_stack(query, stack, max_results=1)
self.assertEqual(result["count"], 1)
self.assertIn(guideline.casefold(), result["results"][0]["Guideline"].casefold())
def test_current_threejs_uses_supported_module_and_color_apis(self):
cases = {
"Three.js current OrbitControls addon import": "three/addons/",
"Three.js current renderer color space": "outputcolorspace",
}
for query, expected in cases.items():
with self.subTest(query=query):
result = search_stack(query, "threejs", max_results=1)
self.assertEqual(result["count"], 1)
recommended = " ".join(
result["results"][0][field]
for field in ("Guideline", "Do", "Code Good")
).casefold()
self.assertIn(expected, recommended)
def test_deprecated_symbols_are_not_recommended_by_current_rows(self):
forbidden = {
"react-native": ("hermes_enabled",),
"flutter": ("willpopscope", "onpopinvoked:", "textscalefactor"),
"swiftui": ("navigationview", "presentationmode"),
"winui": ("dispatcher.runasync", "system.windows"),
"uno": ("system.windows", 'requestedtheme="default"'),
"javafx": ("fxpermission", "javadoc/21"),
"threejs": (
"r128", "three.orbitcontrols", "outputencoding",
"three.srgbencoding", "examples/js/controls",
),
}
for stack, tokens in forbidden.items():
for row in _rows(stack):
if row["Status"] != "active":
continue
recommended = " ".join(
row[field] for field in ("Guideline", "Do", "Code Good")
).casefold()
for token in tokens:
with self.subTest(stack=stack, row=row["No"], token=token):
self.assertNotIn(token, recommended)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,182 @@
#!/usr/bin/env python3
"""Unit tests for metric math and relevance fixture validation."""
import importlib.util
import tempfile
import unittest
from pathlib import Path
ROOT = next(parent for parent in Path(__file__).resolve().parents
if (parent / "scripts/evaluate-relevance.py").exists())
MODULE_PATH = ROOT / "scripts/evaluate-relevance.py"
SPEC = importlib.util.spec_from_file_location("evaluate_relevance", MODULE_PATH)
evaluator = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(evaluator)
class TestMetricMath(unittest.TestCase):
def test_precision_counts_missing_ranks_as_non_relevant(self):
self.assertEqual(evaluator.precision_at_k([2], 1), 1.0)
self.assertAlmostEqual(evaluator.precision_at_k([2], 3), 1 / 3)
self.assertEqual(evaluator.precision_at_k([], 3), 0.0)
self.assertEqual(evaluator.precision_at_k([2], 0), 0.0)
def test_reciprocal_rank_stops_at_k(self):
self.assertEqual(evaluator.reciprocal_rank([0, 2, 0]), 0.5)
self.assertEqual(evaluator.reciprocal_rank([0, 0, 0, 2]), 0.0)
self.assertEqual(evaluator.reciprocal_rank([]), 0.0)
def test_ndcg_uses_graded_gain_and_handles_empty_ideal(self):
self.assertEqual(evaluator.ndcg_at_k([2, 1], [2, 1]), 1.0)
self.assertLess(evaluator.ndcg_at_k([1, 2], [2, 1]), 1.0)
self.assertEqual(evaluator.ndcg_at_k([], [], 3), 0.0)
def test_result_grades_match_identity_subsets(self):
results = [
{"Category": "State", "Guideline": "Use useState", "Severity": "Medium"},
{"Category": "State", "Guideline": "Use useReducer", "Severity": "Medium"},
]
judgments = [
{"identity": {"Guideline": "Use useReducer"}, "grade": 2},
{"identity": {"Category": "State"}, "grade": 1},
]
self.assertEqual(evaluator.grades_for_results(results, judgments), [1, 2])
class TestFixtureValidation(unittest.TestCase):
@staticmethod
def valid_fixture():
case = {
"id": "domain-style-minimal",
"split": "calibration",
"mode": "domain",
"domain": "style",
"query": "minimal grid",
"judgments": [{"identity": {"Style Category": "Minimalism"}, "grade": 2}],
}
return {
"schemaVersion": 1,
"globalNegativeApplicability": {"domains": ["style"], "stacks": []},
"cases": [dict(case, id=f"case-{index}") for index in range(60)],
}
def test_valid_schema(self):
self.assertEqual(evaluator.validate_fixture(self.valid_fixture(), {"style": {}}, []), [])
def test_rejects_bad_count_duplicate_id_and_grade(self):
fixture = self.valid_fixture()
fixture["cases"] = fixture["cases"][:2]
fixture["cases"][1]["id"] = fixture["cases"][0]["id"]
fixture["cases"][0]["judgments"][0]["grade"] = 3
errors = "\n".join(evaluator.validate_fixture(fixture, {"style": {}}, []))
self.assertIn("60-100", errors)
self.assertIn("duplicate case id", errors)
self.assertIn("grade 1 or 2", errors)
class TestThresholdGate(unittest.TestCase):
def test_runtime_fingerprint_binds_reasoning_contract(self):
original = evaluator.ROOT, evaluator.RUNTIME_DIR, evaluator.DATA_DIR
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
runtime = root / "src/ui-ux-pro-max/scripts"
data = root / "src/ui-ux-pro-max/data"
runtime.mkdir(parents=True)
data.mkdir(parents=True)
for name in ("core.py", "design_system.py", "reasoning_contract.py"):
(runtime / name).write_text(name, encoding="utf-8")
(data / "styles.csv").write_text("No,Style\n1,Test\n", encoding="utf-8")
evaluator.ROOT, evaluator.RUNTIME_DIR, evaluator.DATA_DIR = root, runtime, data
try:
before = evaluator.runtime_fingerprint()
(runtime / "reasoning_contract.py").write_text("changed", encoding="utf-8")
self.assertNotEqual(before, evaluator.runtime_fingerprint())
finally:
evaluator.ROOT, evaluator.RUNTIME_DIR, evaluator.DATA_DIR = original
def test_oracle_fingerprint_hashes_the_selected_cases_file(self):
canonical = evaluator.FIXTURE_DIR / "relevance-cases.json"
with tempfile.TemporaryDirectory() as tmp:
selected = Path(tmp) / "cases.json"
selected.write_bytes(canonical.read_bytes())
self.assertEqual(
evaluator.oracle_fingerprint(selected), evaluator.oracle_fingerprint(canonical))
selected.write_bytes(canonical.read_bytes() + b" ")
self.assertNotEqual(
evaluator.oracle_fingerprint(selected), evaluator.oracle_fingerprint(canonical))
def test_metric_sample_and_locked_case_failures_are_actionable(self):
report = {
"metrics": {"precisionAt1": 0.5},
"samples": {"retrieval": 1},
"cases": [{"id": "locked", "grades": [0], "actual": [{"Style Category": "Wrong"}]}],
}
manifest = {
"metrics": {"precisionAt1": {"floor": 0.8, "tolerance": 0.01}},
"sampleMinimums": {"retrieval": 2},
"lockedCases": {"locked": {"withinTop": 1, "minimumGrade": 2}},
}
manifest["splits"] = {"calibration": {"metrics": {}, "sampleMinimums": {}},
"held_out": {"metrics": {}, "sampleMinimums": {}}}
report["splits"] = {"calibration": {"metrics": {}, "samples": {}},
"held_out": {"metrics": {}, "samples": {}}}
failures = evaluator.check_thresholds(report, manifest)
self.assertEqual(len(failures), 3)
self.assertTrue(any("Wrong" in failure for failure in failures))
def test_manifest_rejects_missing_contract_sections(self):
errors = evaluator.validate_manifest({}, "fingerprint")
self.assertTrue(any("missing sections" in error for error in errors))
self.assertTrue(any("missing metrics" in error for error in errors))
def test_manifest_rejects_non_finite_and_invalid_sample_values(self):
manifest = {
"schemaVersion": 1,
"status": "approved",
"approvingMaintainer": "maintainer",
"units": "ratios",
"splitPolicy": {},
"runtimeFingerprint": "fingerprint",
"oracleFingerprint": "oracle",
"baselineRevision": "97eb2a2",
"metrics": {name: {"floor": float("nan")} for name in evaluator.REQUIRED_METRICS},
"sampleMinimums": {"cases": True},
"lockedCases": {"case": {}},
"splits": {
split: {
"metrics": {name: {"floor": 0.0} for name in evaluator.REQUIRED_METRICS},
"sampleMinimums": {"cases": 1},
} for split in ("calibration", "held_out")
},
}
errors = evaluator.validate_manifest(manifest, "fingerprint", "oracle")
self.assertTrue(any("finite" in error for error in errors))
self.assertTrue(any("non-negative integer" in error for error in errors))
def test_manifest_binds_oracle_and_validates_baseline_revision(self):
manifest = {
"schemaVersion": 1,
"status": "approved",
"approvingMaintainer": "maintainer",
"units": "ratios",
"splitPolicy": {},
"runtimeFingerprint": "runtime",
"oracleFingerprint": "wrong",
"baselineRevision": "not-a-revision",
"metrics": {name: {"floor": 0.0} for name in evaluator.REQUIRED_METRICS},
"sampleMinimums": {"cases": 1},
"lockedCases": {"case": {}},
"splits": {
split: {
"metrics": {name: {"floor": 0.0} for name in evaluator.REQUIRED_METRICS},
"sampleMinimums": {"cases": 1},
} for split in ("calibration", "held_out")
},
}
errors = evaluator.validate_manifest(manifest, "runtime", "expected")
self.assertTrue(any("oracleFingerprint" in error for error in errors))
self.assertTrue(any("baselineRevision" in error for error in errors))
if __name__ == "__main__":
unittest.main(verbosity=2)
@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""Regression tests for the public style taxonomy and search contract."""
import csv
import json
import statistics
import sys
import unittest
from pathlib import Path
SCRIPTS_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = SCRIPTS_DIR.parent / "data"
sys.path.insert(0, str(SCRIPTS_DIR))
from core import search # noqa: E402
from design_system import _style_is_dark_primary # noqa: E402
def read_rows(name):
with (DATA_DIR / name).open(encoding="utf-8", newline="") as handle:
return list(csv.DictReader(handle))
class TestStyleTaxonomy(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.styles = read_rows("styles.csv")
cls.by_id = {row["Style ID"]: row for row in cls.styles}
def test_curated_state_distribution_is_explicit(self):
counts = {
status: sum(row["Status"] == status for row in self.styles)
for status in ("active", "supplemental", "deprecated")
}
self.assertEqual(
{"active": 50, "supplemental": 29, "deprecated": 9}, counts
)
def test_every_style_name_and_alias_has_a_deterministic_destination(self):
for row in self.styles:
queries = [row["Style Category"]]
queries.extend(alias for alias in row["Aliases"].split("|") if alias)
for query in queries:
with self.subTest(style=row["Style ID"], query=query):
result = search(query, max_results=1)
if (row["Status"] == "deprecated"
and row["Replacement Domain"] == "landing"):
self.assertEqual(0, result["count"])
self.assertEqual(
{
"domain": row["Replacement Domain"],
"id": row["Replacement ID"],
},
result.get("redirect"),
)
else:
expected_id = (
row["Replacement ID"]
if row["Status"] == "deprecated"
else row["Style ID"]
)
self.assertEqual(expected_id, result["results"][0]["Style ID"])
def test_deprecated_rows_never_appear_in_generic_results(self):
for query in ("modern interface", "marketing page", "trust design"):
with self.subTest(query=query):
result = search(query, domain="style", max_results=20)
self.assertLessEqual(
{row["Status"] for row in result["results"]}, {"active"}
)
def test_style_arbitration_does_not_steal_product_intent(self):
result = search("design a financial dashboard for my bank", max_results=1)
self.assertEqual("product", result["domain"])
def test_family_variants_and_mobile_intent_remain_distinct(self):
expected_parents = {
"gradient-mesh-aurora-evolved": "aurora-ui",
"swiss-modernism-2-0": "minimalism-and-swiss-style",
"neumorphism-mobile": "neumorphism",
"claymorphism-mobile": "claymorphism",
"spectrum-2": "spectrum-design-system",
}
for style_id, parent_id in expected_parents.items():
with self.subTest(style=style_id):
row = self.by_id[style_id]
self.assertEqual(parent_id, row["Parent Style ID"])
self.assertIn(row["Status"], {"supplemental", "deprecated"})
self.assertEqual("style", self.by_id["bento-grids"]["Replacement Domain"])
self.assertEqual(
"bento-box-grid", self.by_id["bento-grids"]["Replacement ID"]
)
self.assertEqual(
"neumorphism-mobile",
search("Neumorphism (Mobile)", "style", 1)["results"][0]["Style ID"],
)
self.assertEqual(
"claymorphism-mobile",
search("Claymorphism (Mobile)", "style", 1)["results"][0]["Style ID"],
)
self.assertEqual(
"material-you-md3-mobile",
search("M3 Expressive", "style", 1)["results"][0]["Style ID"],
)
self.assertEqual(
"neumorphism-mobile",
search("mobile neumorphism UI", "style", 1)["results"][0]["Style ID"],
)
self.assertEqual(
"claymorphism-mobile",
search("mobile app with claymorphism", "style", 1)["results"][0]["Style ID"],
)
self.assertEqual(
"spectrum-2",
search("design system for Spectrum 2", "style", 1)["results"][0]["Style ID"],
)
def test_claim_fields_use_controlled_non_guarantee_language(self):
allowed_performance = {"cost:low", "cost:moderate", "cost:high"}
allowed_accessibility = {"risk:low", "risk:conditional", "risk:high"}
allowed_mode = {"supported", "conditional", "not-recommended"}
for row in self.styles:
with self.subTest(style=row["Style ID"]):
self.assertIn(row["Performance"].split("|", 1)[0], allowed_performance)
self.assertIn(row["Accessibility"].split("|", 1)[0], allowed_accessibility)
self.assertIn(row["Light Mode ✓"], allowed_mode)
self.assertIn(row["Dark Mode ✓"], allowed_mode)
self.assertIn(row["Preferred Mode"], {"auto", "light", "dark"})
claim_text = " ".join(row.values())
self.assertNotRegex(claim_text, r"(?i)\bWCAG\s+A{2,3}\+?\b")
self.assertNotRegex(
claim_text,
r"(?i)\bWCAG\b.{0,40}\b(?:compliant|compliance)\b",
)
self.assertNotRegex(row["Framework Compatibility"], r"\d+/10")
self.assertTrue(_style_is_dark_primary(self.by_id["dark-mode-oled"]))
self.assertFalse(
_style_is_dark_primary(self.by_id["minimalism-and-swiss-style"])
)
def test_searchable_prompt_lengths_are_balanced(self):
lengths_by_type = {}
for row in self.styles:
length = len(row["AI Prompt Keywords"].split())
self.assertLessEqual(length, 40, row["Style ID"])
lengths_by_type.setdefault(row["Type"], []).append(length)
general_median = statistics.median(lengths_by_type["General"])
mobile_median = statistics.median(lengths_by_type["Mobile"])
self.assertLessEqual(mobile_median, general_median * 1.6)
def test_new_rows_have_first_party_provenance(self):
payload = json.loads(
(DATA_DIR / "data-provenance.json").read_text(encoding="utf-8")
)
records = {
record["entityId"]: record
for record in payload["records"]
if record["entityKind"] == "style"
}
new_rows = [row for row in self.styles if int(row["No"]) > 85]
self.assertGreaterEqual(len(new_rows), 4)
for row in new_rows:
with self.subTest(style=row["Style ID"]):
record = records[row["Style ID"]]
self.assertTrue(record["sources"])
self.assertTrue(
any(source["type"] == "official" for source in record["sources"])
)
if __name__ == "__main__":
unittest.main(verbosity=2)
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""Canonical regression contracts for resilient UI text layouts."""
import csv
import sys
import unittest
from pathlib import Path
SCRIPTS_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = SCRIPTS_DIR.parent / "data"
sys.path.insert(0, str(SCRIPTS_DIR))
from core import search, search_stack # noqa: E402
def read_rows(relative_path):
with (DATA_DIR / relative_path).open(encoding="utf-8", newline="") as handle:
return list(csv.DictReader(handle))
UX_PHRASES = {
"Heading Line Balance": ("progressive visual heuristic", "natural-wrap fallback"),
"Long Token Wrapping": ("overflow-wrap anywhere", "text children shrink"),
"Text Reflow and Spacing": ("narrow widths zoom", "content-driven height"),
"Essential Text Truncation": ("complete access", "visible full-detail path"),
"Compact Label Semantics": ("badges communicate state", "meaning and ownership"),
"Chip Collection Reflow": ("filter chips", "operable +n disclosure"),
"Compact Label Overflow": ("stay whole on one line", "keyboard pointer and touch"),
"Compact Control Semantics": ("native role", "pressed or selected state"),
"Contextual Live Badge Updates": ("meaningful contextual status", "atomic status"),
"Cancellable State Transitions": ("interrupt an in-flight transition", "final semantic state"),
}
TAILWIND_PHRASES = {
"Balanced heading wrapping": ("text-balance", "natural wrapping fallback"),
"Long token resilience": ("wrap-anywhere", "min-w-0"),
"Compact label layout": ("flex flex-wrap gap-2", "whitespace-nowrap", "shrink-0"),
}
class TestTextLayoutRetrieval(unittest.TestCase):
def test_locked_queries_return_the_canonical_identity_first(self):
cases = (
("orphan heading line balance", "Heading Line Balance"),
("long url token breaks layout", "Long Token Wrapping"),
("badge chip label wraps to second line", "Compact Label Overflow"),
("filter chip collection reflow hidden values", "Chip Collection Reflow"),
("live badge count screen reader", "Contextual Live Badge Updates"),
("rapid chip animation interrupted", "Cancellable State Transitions"),
)
for query, expected in cases:
with self.subTest(query=query):
result = search(query, domain="ux", max_results=3, diagnostics=True)
actual = [row.get("Issue") for row in result["results"]]
self.assertTrue(actual, result.get("diagnostics"))
self.assertEqual(expected, actual[0], f"ranking={actual!r}")
def test_tailwind_query_returns_compact_label_layout_first(self):
result = search_stack(
"chip badge overflow nowrap", "html-tailwind",
max_results=3, diagnostics=True,
)
actual = [row.get("Guideline") for row in result["results"]]
self.assertTrue(actual, result.get("diagnostics"))
self.assertEqual("Compact label layout", actual[0], f"ranking={actual!r}")
class TestTextLayoutDataContracts(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.ux = read_rows("ux-guidelines.csv")
cls.tailwind = read_rows("stacks/html-tailwind.csv")
def test_new_ux_rows_are_unique_sequential_and_keep_critical_guidance(self):
matches = [row for row in self.ux if row["Issue"] in UX_PHRASES]
self.assertEqual(len(UX_PHRASES), len(matches))
self.assertEqual(len(matches), len({row["Issue"] for row in matches}))
self.assertEqual(list(range(110, 120)), [int(row["No"]) for row in matches])
for row in matches:
with self.subTest(issue=row["Issue"]):
text = " ".join(row.values()).casefold()
for phrase in UX_PHRASES[row["Issue"]]:
self.assertIn(phrase.casefold(), text)
self.assertIn(row["Severity"], {"Medium", "High", "Critical"})
def test_new_tailwind_rows_are_unique_current_and_keep_required_utilities(self):
matches = [row for row in self.tailwind if row["Guideline"] in TAILWIND_PHRASES]
self.assertEqual(len(TAILWIND_PHRASES), len(matches))
self.assertEqual(len(matches), len({row["Guideline"] for row in matches}))
self.assertEqual([57, 58, 59], [int(row["No"]) for row in matches])
for row in matches:
with self.subTest(guideline=row["Guideline"]):
text = " ".join(row.values()).casefold()
for phrase in TAILWIND_PHRASES[row["Guideline"]]:
self.assertIn(phrase.casefold(), text)
self.assertEqual("active", row["Status"])
self.assertEqual("html-tailwind 4.3", row["Applies To"])
self.assertEqual("2026-08-13", row["Verified At"])
def test_refined_rows_are_context_sensitive_not_universal_recipes(self):
expected = {
"8": ("depends on distance complexity platform", "shared motion tokens"),
"14": ("match how an element changes speed", "linear for constant-rate progress"),
"19": ("badges validation text", "stable content-driven container"),
"78": ("avoid flashing for near-instant work", "platform and component guidance"),
}
forbidden = ("use 150-300ms", "operations > 300ms", "linear motion feels robotic")
by_number = {row["No"]: row for row in self.ux}
for number, phrases in expected.items():
with self.subTest(row=number):
row = by_number[number]
guidance = " ".join((row["Description"], row["Do"])).casefold()
for phrase in phrases:
self.assertIn(phrase, guidance)
for claim in forbidden:
self.assertNotIn(claim, guidance)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,177 @@
#!/usr/bin/env python3
"""Freshness and generation-isolation contracts for web stack guidance."""
import csv
import sys
import unittest
from pathlib import Path
from urllib.parse import urlsplit
SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
from core import DATA_DIR, STACK_CONFIG, WEB_STACKS, search_stack # noqa: E402
from validate_data import STACK_OFFICIAL_HOSTS # noqa: E402
CURRENT_APPLICABILITY = {
"react": "react 19.2.x",
"nextjs": "nextjs 16.2",
"vue": "vue 3.5.x",
"svelte": "svelte 5",
"astro": "astro 7.1.6",
"angular": "angular 22.x",
"html-tailwind": "html-tailwind 4.3",
"shadcn": "shadcn cli 4",
"nuxtjs": "nuxtjs 4.5",
"nuxt-ui": "nuxt-ui 4.10",
}
def _rows(stack):
path = DATA_DIR / STACK_CONFIG[stack]["file"]
with path.open(encoding="utf-8", newline="") as handle:
return list(csv.DictReader(handle))
class TestWebStackFreshness(unittest.TestCase):
def test_web_rows_have_explicit_freshness_metadata(self):
for stack in WEB_STACKS:
for row in _rows(stack):
with self.subTest(stack=stack, row=row["No"]):
self.assertIn(row["Status"], {"active", "deprecated"})
self.assertTrue(row["Applies To"].startswith(stack))
self.assertRegex(row["Verified At"], r"^\d{4}-\d{2}-\d{2}$")
self.assertEqual(
"legacy" in row["Applies To"].casefold(),
row["Status"] == "deprecated",
)
def test_high_impact_rows_use_official_sources(self):
for stack in WEB_STACKS:
for row in _rows(stack):
if row["Severity"] not in {"Critical", "High"}:
continue
with self.subTest(stack=stack, row=row["No"]):
parsed = urlsplit(row["Docs URL"])
self.assertEqual(parsed.scheme, "https")
self.assertIn(parsed.hostname, STACK_OFFICIAL_HOSTS[stack])
def test_active_rows_use_the_verified_current_applicability(self):
for stack, applicability in CURRENT_APPLICABILITY.items():
for row in _rows(stack):
if row["Status"] != "active":
continue
with self.subTest(stack=stack, row=row["No"]):
self.assertTrue(row["Applies To"].startswith(applicability))
def test_svelte_current_and_legacy_queries_do_not_mix_generations(self):
current = search_stack("Svelte state props and events", "svelte")
legacy = search_stack("Svelte 4 legacy props and events", "svelte")
self.assertGreater(current["count"], 0)
self.assertGreater(legacy["count"], 0)
self.assertEqual({row["Status"] for row in current["results"]}, {"active"})
self.assertEqual({row["Status"] for row in legacy["results"]}, {"deprecated"})
self.assertTrue(all("legacy" in row["Applies To"] for row in legacy["results"]))
def test_explicit_old_major_uses_only_curated_legacy_rows(self):
cases = {
"nextjs": "Next.js 15 middleware auth matcher",
"html-tailwind": "Tailwind 3 JIT content configuration",
"nuxtjs": "Nuxt 3 app config runtime config migration",
}
for stack, query in cases.items():
with self.subTest(stack=stack):
result = search_stack(query, stack)
self.assertGreater(result["count"], 0)
self.assertEqual(
{row["Status"] for row in result["results"]}, {"deprecated"}
)
def test_current_major_migration_query_stays_on_current_guidance(self):
result = search_stack("Next.js 16 migration to proxy", "nextjs")
self.assertGreater(result["count"], 0)
self.assertEqual({row["Status"] for row in result["results"]}, {"active"})
def test_shadcn_named_base_excludes_incompatible_composition_apis(self):
result = search_stack("shadcn Base UI asChild composition", "shadcn")
self.assertGreater(result["count"], 0)
self.assertEqual(result["results"][0]["Guideline"],
"Use render for Base UI composition")
self.assertTrue(all("base=radix" not in row["Applies To"]
for row in result["results"]))
def test_common_old_major_syntaxes_select_legacy_guidance(self):
cases = {
"svelte": "svelte@4 props events",
"nextjs": "Next.js (v15) middleware",
"html-tailwind": "tailwindcss@3 JIT",
"nuxtjs": "nuxt@3 app config",
}
for stack, query in cases.items():
with self.subTest(stack=stack, query=query):
result = search_stack(query, stack)
self.assertGreater(result["count"], 0)
self.assertEqual(
{row["Status"] for row in result["results"]}, {"deprecated"}
)
def test_old_major_without_curated_legacy_guidance_abstains(self):
result = search_stack("Astro 5 content collections", "astro")
self.assertEqual(result["count"], 0)
self.assertEqual(result["results"], [])
def test_current_high_drift_queries_return_current_contracts(self):
cases = {
("nextjs", "Next.js 16 request interception proxy"): "proxy",
("html-tailwind", "Tailwind 4 CSS-first source detection"): "source",
("react", "React Effect Event latest values inside an Effect"): "effect event",
}
for (stack, query), expected in cases.items():
with self.subTest(stack=stack):
result = search_stack(query, stack, max_results=1)
self.assertEqual(result["count"], 1)
row = result["results"][0]
self.assertEqual(row["Status"], "active")
self.assertIn(expected, row["Guideline"].casefold())
def test_stack_without_curated_legacy_rows_keeps_nonlegacy_fallback(self):
result = search_stack(
"which Windows UI framework should a new app choose instead of legacy UWP",
"uwp",
)
self.assertGreater(result["count"], 0)
def test_stale_apis_are_not_recommended_by_active_rows(self):
forbidden = {
"svelte": ("$: ", "export let ", "on:click", "createeventdispatcher"),
"nextjs": ("middleware.ts", "function middleware", "skipmiddleware"),
"html-tailwind": (
"content: [", "mode: 'jit'", "@tailwindcss/aspect-ratio",
"tailwindcss-container-queries",
),
"nuxt-ui": ("#cell-status", "sortable: true", "v-model:content"),
"astro": (
"output: 'hybrid'", "viewtransitions", "@astrojs/tailwind",
"astro add prefetch",
),
}
for stack, tokens in forbidden.items():
for row in _rows(stack):
if row["Status"] != "active":
continue
recommended = " ".join(
row[field] for field in ("Guideline", "Do", "Code Good")
).casefold()
for token in tokens:
with self.subTest(stack=stack, row=row["No"], token=token):
self.assertNotIn(token, recommended)
self.assertNotIn(
"fetch(url {", _rows("nextjs")[12]["Code Good"].casefold()
)
self.assertNotIn("catch(e) {}", _rows("react")[39]["Code Good"].casefold())
if __name__ == "__main__":
unittest.main()