baldart 4.76.1 → 4.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +14 -13
- package/VERSION +1 -1
- package/framework/.claude/agents/REGISTRY.md +2 -1
- package/framework/.claude/agents/markup-fidelity-verifier.md +239 -0
- package/framework/.claude/agents/prd-card-writer.md +6 -1
- package/framework/.claude/agents/visual-fidelity-verifier.md +22 -0
- package/framework/.claude/skills/design-system-init/SKILL.md +16 -6
- package/framework/.claude/skills/design-system-init/scripts/compile-ds-cards.mjs +2 -1
- package/framework/.claude/skills/design-system-init/scripts/extract-manifest.mjs +17 -5
- package/framework/.claude/skills/design-system-init/scripts/extract-ts.mjs +302 -0
- package/framework/.claude/skills/design-system-init/scripts/render-manifest.mjs +26 -9
- package/framework/.claude/skills/design-system-init/scripts/serialize-spec.mjs +4 -2
- package/framework/.claude/skills/ds-edit/SKILL.md +2 -1
- package/framework/.claude/skills/ds-new/SKILL.md +2 -2
- package/framework/.claude/skills/e2e-review/SKILL.md +196 -27
- package/framework/.claude/skills/new/references/codex-gate.md +15 -1
- package/framework/.claude/skills/new/references/completeness.md +2 -0
- package/framework/.claude/skills/new/references/review-cycle.md +14 -1
- package/framework/agents/component-manifest-schema.md +24 -3
- package/framework/agents/design-system-protocol.md +79 -1
- package/framework/docs/COMPONENT-MANIFEST-LAYER.md +6 -3
- package/framework/templates/component-spec.template.md +3 -2
- package/framework/templates/overlays/e2e-review.fidelity-example.md +74 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,48 @@ All notable changes to BALDART will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.78.0] - 2026-06-29
|
|
9
|
+
|
|
10
|
+
**Route-independent per-card mockup fidelity + graphics fast-lane.** On a `/new` run a desktop "Edit product" page shipped as **1 column** against a **2-column** mockup, marked DONE: the fidelity gate never ran on a rendered page because the auth-gated route returned 500 under the empty-Supabase demo lane, the views were "soft-skipped", and the verdict came back `ACCEPTED (harness limit)` — **a skip masked as a pass**. Two adversarial passes found this is not one bug but two distinct holes hitting two cards, plus the fact that the fidelity check depended on rendering a live, data-bearing route at all. This release inverts the priority: per-card UI fidelity is verified **route-independently, before proceeding** — a code-structural diff first, an isolated render as confirmation — and an un-runnable required check now **blocks instead of silently passing**. Bets validated against 2026 best practice (structural TED + perceptual diff as complementary dimensions; MLLM-as-judge reliable at comparison, weak at absolute scoring → fidelity gates, quality stays advisory).
|
|
11
|
+
|
|
12
|
+
**MINOR** — adds an agent (`markup-fidelity-verifier`, 30→31) + capability. **No new `baldart.config.yml` key** (rides on `features.has_e2e_review` + `features.has_design_system`), so the schema-change propagation rule does NOT apply. Claude + Codex portable (the structural pass is Read-only; the image-load/isolated-render degrade to no-op where unavailable).
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`markup-fidelity-verifier` agent** (`framework/.claude/agents/markup-fidelity-verifier.md`, `model: sonnet`) — the **route-independent structural twin** of `visual-fidelity-verifier`. Compares the implementation's code structure against the mockup expressed as code (`links.design` HTML or `links.design_src`/`mockups/_src/`) with no browser/auth/data — the DOM tree-edit-distance analogue, complementary to the visual verifier's pixel/perceptual diff. Deterministically catches the **2-column-mockup → 1-column-build** class of divergence. READS code (mockup-vs-impl, no assertion-fitting risk); same canonical taxonomy + JSON output; fixes route to `ui-expert`.
|
|
17
|
+
- **`/e2e-review` Phase 2.7 — Structural Fidelity (route-free)** — runs the `markup-fidelity-verifier` BEFORE the browser passes, on every card with an HTML/`design_src` mockup. The workhorse of per-card fidelity.
|
|
18
|
+
- **`/e2e-review` Phase 3.8 — Comparable-render gate (state-aware)** — the reviewer never accepts "opened it, it's empty, skip". Detects a degenerate render (non-2xx / empty-state / wrong tenant) and runs a bounded **reach-state loop** (overlay recipes `seed_data` / `select_populated_context` / `goto_populated_entity`, with generic fallbacks: real auth, MSW/API-mock, deterministic factory/snapshot, heuristic context auto-seek) before diffing. New `data-state-mismatch` (Critical) finding.
|
|
19
|
+
- **Coverage obligation** — new `coverage-gap` (Critical) finding synthesized when an image-only mockup route could not be rendered in any lane and its state could not be reached; maps to the existing `blocked` verdict, bypasses the self-heal loop. Guards exclude `skip`/`harness-render`/`compliance-only`/mobile-pass. This turns the FEAT-0061 "skip masked as pass" into a real block.
|
|
20
|
+
- **Starter overlay** `framework/templates/overlays/e2e-review.fidelity-example.md` — schema for project-specific reach-state recipes.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **`visual-fidelity-verifier`** — `coverage-gap` + `data-state-mismatch` added to the canonical severity taxonomy (SSOT); new "Data-State Awareness" rule: never compare a degenerate render, emit `data-state-mismatch` instead of a silent skip/pass.
|
|
25
|
+
- **`/e2e-review` Phase 2** ingests **HTML mockups** (`links.design` `.html` → headless `file://` render to PNG for the visual pass + retained as code for the structural pass) and recognizes `links.design_src`. **Phase 5b self-heal** routes ALL fidelity fixes (structural/presentational/quality) to `ui-expert`; `coder` only for genuine wiring.
|
|
26
|
+
- **`/new` graphics fast-lane** — a **pure-graphics card** (`owner_agent: ui-expert` + `review_profile: light` + `areas == [ui]` + zero Step-A triggers) skips Simplify (Phase 2.55) and per-card Codex (Phase 3.7); qa is the build/lint/tsc floor; the `/e2e-review` fidelity trio is the review. Safety net preserved by the batch-wide Final FULL Codex gate. Wired in `review-cycle.md`, `codex-gate.md`, `prd-card-writer.md` (emit `owner_agent: ui-expert` on Rule C branch-(b) cards).
|
|
27
|
+
- **`/new` no cross-card fidelity deferral** — `completeness.md` Phase 2.5b Step 4 fidelity-AC carve-out: a visual-fidelity AC cannot be silently deferred ("Approva il deferral" withheld) — only implement-now (→ `ui-expert`), explicit follow-up card, or halt. `review-cycle.md` Phase 2.6: a fidelity AC closes only on a real `passed`, never a `skipped`.
|
|
28
|
+
- **`design-system-protocol.md`** — new SSOT section "Route-Independent Fidelity & Coverage Obligation" (structural fidelity, visual confirmation, state-aware fidelity, coverage obligation, graphics-card rule, gating calibration) cited by `/e2e-review`, `/new`, `/design-review`.
|
|
29
|
+
|
|
30
|
+
## [4.77.0] - 2026-06-29
|
|
31
|
+
|
|
32
|
+
**TS-aware design-system extractor + the `variant_prop` HEAD field.** The component-manifest extractor was pure-regex (zero-dep) and could not resolve TypeScript: a `type Variant = 'a'|'b'; variant: Variant` captured the alias *name* `"Variant"`, not its members, so on a TS-strict consumer (mayo) every HEAD came out with `variants: []` and ~25 with `props: {}`. Since the HEAD is the SSOT every UI agent reads for discovery, `variants: []` on a 6-variant Button induces wrong code. Separately, the variant axis was **hardcoded** to the prop name `variant` in three places — wrong for a `Chip`/`Pill` whose axis is `tone`. This release makes the extractor **optionally TS-aware** (it borrows the consumer's own `typescript`, resolved from the repo root) and adds a deterministic **`variant_prop`** field naming the real axis.
|
|
33
|
+
|
|
34
|
+
**MINOR** — adds capability + a new deterministic HEAD field. **No new `baldart.config.yml` key** (rides on `features.has_design_system`), so the schema-change propagation rule does NOT apply. **Zero regression**: when typescript is absent (Codex / greenfield / non-TS stack) or a file fails to parse, the extractor falls back to the existing zero-dep regex path per file; legacy HEADs (pre-v4.77.0, no `variant_prop`) default the render axis to `'variant'` so existing registries render identically until a one-time re-extract backfills the field.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **`extract-ts.mjs`** (`framework/.claude/skills/design-system-init/scripts/extract-ts.mjs`) — optional TS-aware resolver. Probes `typescript` via `require.resolve('typescript', { paths: [process.cwd()] })` (the **consumer's** dependency, not BALDART's), parses syntactically with `ts.createSourceFile` (no tsconfig / Program / TypeChecker — fast, in-file, deterministic), and resolves type **aliases**, **unions**, **intersections**, **`Omit`/`Pick`**, interface `extends`, and `forwardRef`/`memo`/`React.FC` wrappers. Opaque / imported types (`ComponentPropsWithoutRef<'button'>`, `HTMLAttributes`) stay **empty** — never a DOM-attribute dump. Per-file try/catch → regex fallback. The typescript-free `computeVariantProp` / `pureStringLiteralUnion` / `literalMembers` are **shared** with the regex path so the axis ranking is byte-identical on both.
|
|
39
|
+
- **`variant_prop` deterministic HEAD field** — which prop IS the variant axis, ranked `variant > tone > intent > kind > appearance > state > status > color > severity` (else the sole literal-union prop, else `''`). `variants` is the literal members of `variant_prop`'s type. Emitted by `serialize-spec.mjs`, consumed as the axis key by `render-manifest.mjs` (`entriesFor`) and `compile-ds-cards.mjs` (row label) instead of the hardcoded `'variant'`. Documented in `component-manifest-schema.md` (SSOT), the canonical `component-spec.template.md`, the `/design-system-init` `/ds-new` `/ds-edit` skills, `COMPONENT-MANIFEST-LAYER.md`, and `design-system-protocol.md`.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **`render-manifest.mjs parseHead` now reads block-style `variants`** — the serializer emits non-empty `variants` as a YAML **block list** (`variants:\n - a`), but `parseHead` only matched flow `variants: [a, b]`, so on any real serialized spec the variant matrix read `[]` (every component collapsed to a single `--default` entry). It now accepts both block and flow forms; with `variant_prop` this makes the render/publish axis work end-to-end (a `Chip` mounts `{ tone }` per variant).
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **`extract-manifest.mjs extractFile`** — TS-path-first with per-file regex fallback; emits `variant_prop`; `extractVariants(props)` generalized to `extractVariantsFor(props, variantProp)` (the axis is no longer hardcoded to `variant`).
|
|
48
|
+
- **`/design-system-init` UPGRADE mode** (`SKILL.md`) — a one-line **pre-v4.77.0 backfill** note: re-extract all components ignoring `source_sha` once to populate `variant_prop` + resolve aliases, agentic fields preserved by the existing non-clobber rule.
|
|
49
|
+
|
|
8
50
|
## [4.76.1] - 2026-06-26
|
|
9
51
|
|
|
10
52
|
**`/design-system-init` offers a render surface (opt-in Storybook scaffold).** The render harness (Stage C) needs a Storybook, and a registry-only project has none — so the harness was a silent no-op with nowhere to go. Now `/design-system-init`, after scaffolding the registry, OFFERS (default-NO, JS/TS stacks only) to run the **official `npx storybook init`** so the harness has a surface. It **never hand-writes a per-stack render surface and never auto-wires the project's providers** — that "auto-provider-shim" is the refuted false-fidelity trap; instead it leaves a `.storybook/preview` decorator TODO so **the user owns the context-shimming** (theme/i18n/router), which is how Storybook does it correctly. `/ds-render`'s no-op message now points here. Declined / Codex / non-JS → skipped (publish still works via static-spec cards).
|
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ No additional activation steps needed — once installed, Claude Code (and Codex
|
|
|
85
85
|
- **agents/**: 26 domain modules (architecture, workflows, testing, security, card-schema, i18n-protocol, return-contract-protocol, component-manifest-schema, etc.)
|
|
86
86
|
- **Routing**: If you touch X, read Y - minimize context loading
|
|
87
87
|
|
|
88
|
-
### AI Agents (
|
|
88
|
+
### AI Agents (32 specialized agents)
|
|
89
89
|
|
|
90
90
|
**Core (required for every project)**
|
|
91
91
|
1. **codebase-architect**: MANDATORY before planning/implementation - understands codebase structure
|
|
@@ -108,22 +108,23 @@ No additional activation steps needed — once installed, Claude Code (and Codex
|
|
|
108
108
|
16. **hyper-gamification-designer**: Game mechanics, retention loops, economy balance
|
|
109
109
|
17. **remotion-animator-orchestrator**: Video/motion graphics with Remotion
|
|
110
110
|
18. **visual-fidelity-verifier** (v3.18.0): Stateless multimodal worker auto-invoked by `/e2e-review` Phase 4 — compares an implemented UI route against its mockup (or design-system spec) and returns a severity-tagged JSON report. Never reads source code (anti assertion-fitting bias), never edits, never declares done
|
|
111
|
-
19. **
|
|
111
|
+
19. **markup-fidelity-verifier** (v4.78.0): Route-INDEPENDENT structural twin of `visual-fidelity-verifier`, auto-invoked by `/e2e-review` Phase 2.7 — compares the implementation's code structure against the mockup expressed as code (`links.design` HTML or `links.design_src`) with no browser/auth/data, catching the 2-column-mockup → 1-column-build class of divergence. READS code (mockup-vs-impl, no assertion-fitting risk); same canonical taxonomy + JSON output; fixes route to `ui-expert`
|
|
112
|
+
20. **ui-quality-critic** (v4.61.0): Stateless multimodal worker auto-invoked by `/e2e-review` Phase 4b — judges the **intrinsic design quality** of an implemented UI (10-dimension scientific rubric: hierarchy, rhythm, color, density, composition, states, motion, polish, brand) and returns a severity-tagged JSON report + per-dimension scores. The design twin of `code-reviewer`, the quality complement of `visual-fidelity-verifier` (needs no mockup). Never reads source, never grades its own design; fixes route to `ui-expert` in the bounded self-heal loop
|
|
112
113
|
|
|
113
114
|
**Product & Marketing**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
21. **onboarding-architect-lead**: User onboarding flow design and experimentation
|
|
116
|
+
22. **marketing-conversion-strategist**: High-converting copy and messaging
|
|
117
|
+
23. **seo-analytics-strategist**: SEO strategy, metadata, GA4/GTM event planning
|
|
118
|
+
24. **email-deliverability-architect**: Transactional/informational email design + SPF/DKIM/DMARC
|
|
119
|
+
25. **website-orchestrator**: Multi-agent website development coordination
|
|
119
120
|
|
|
120
121
|
**Specialized**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
26. **hybrid-ml-architect**: ML/DL system design (recommenders, ranking, embeddings)
|
|
123
|
+
27. **legal-counsel-gdpr**: GDPR compliance, privacy policies, data governance
|
|
124
|
+
28. **deep-human-insight**: Psychological / sociological analysis for B2C UX and adoption
|
|
125
|
+
29. **skill-improver**: Weekly auto-improvement of skills/agents based on review/QA findings
|
|
126
|
+
30. **i18n-translator** (v4.52.0): Context-aware label translation into native locale files (Sonnet, low-effort, flag-not-guess) — invoked by `/i18n` + the `i18n-align` routine
|
|
127
|
+
31. **merge-conflict-resolver** (v4.64.0): Resolves the final-merge **code** conflicts OFF the orchestrator context — auto-spawned by `/new` Phase 6 / `/mw` only when the deterministic `merge-worktree.sh` pauses on a code/test conflict. Adjudicates additive-vs-semantic hunks (semantic → STOP), then lets the script land. Runs in a fresh isolated context so the conflict churn never re-enters the bloated end-of-batch orchestrator
|
|
127
128
|
|
|
128
129
|
REGISTRY.md is the single source of truth for agent routing and capabilities.
|
|
129
130
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.78.0
|
|
@@ -25,7 +25,8 @@ Quick-reference for all custom agents. Use this to route tasks to the right spec
|
|
|
25
25
|
| **hybrid-ml-architect** | ML | Design/implement ML systems end-to-end | Recommender, ranking, embeddings | Yes | Model design, evaluation, monitoring |
|
|
26
26
|
| **i18n-translator** | Localization | Translate user-facing labels into the maintained target languages (invoked by `/i18n` + the `i18n-align` routine). Gated on `features.has_i18n`. | Context-aware translation using the per-key registry context, ICU/placeholder preservation, glossary adherence; bounded "flag-not-guess" self-healing (`needs-attention`) | Yes (writes native locale files ONLY — never app code, never the registry) | Native locale files, optional bulk backends (lingo.dev/languine) |
|
|
27
27
|
| **ui-expert** | Design | Design, implement, and review UI/UX | Mobile-first, accessibility, registry-first protocol gate (when `features.has_design_system: true`) — BLOCKING reads on `${paths.design_system}/INDEX.md` + `tokens-reference.md` + `components/<Name>.md` per `framework/agents/design-system-protocol.md`, Component Discovery cascade before any design | Yes (generates UI / HTML mockups in `ui-design` Step C; a separate fresh instance acts as design-quality evaluator in Step D) | ui-ux-pro-max, Playwright |
|
|
28
|
-
| **visual-fidelity-verifier** | Design | Compare an implemented UI route against its mockup (or design-system spec) and return a severity-tagged JSON report. Auto-invoked by `/e2e-review` skill (Phase 4) — never spawned ad-hoc. Stateless. Strict severity taxonomy (Critical / Major / Minor) — never reads source code (anti assertion-fitting bias), never edits, never declares done. | Visual diff, severity taxonomy, design-system compliance check via registry cascade | No | Vision (multimodal), Read (screenshots + registry only), Playwright screenshots passed in by orchestrator |
|
|
28
|
+
| **visual-fidelity-verifier** | Design | Compare an implemented UI route against its mockup (or design-system spec) and return a severity-tagged JSON report. Auto-invoked by `/e2e-review` skill (Phase 4) — never spawned ad-hoc. Stateless. Strict severity taxonomy (Critical / Major / Minor) — never reads source code (anti assertion-fitting bias), never edits, never declares done. Emits `data-state-mismatch` when the render is in the wrong data state. | Visual diff, severity taxonomy, design-system compliance check via registry cascade | No | Vision (multimodal), Read (screenshots + registry only), Playwright screenshots passed in by orchestrator |
|
|
29
|
+
| **markup-fidelity-verifier** | Design | **Route-INDEPENDENT structural twin** of `visual-fidelity-verifier`: compare the implementation's code structure against the mockup expressed as code (`links.design` HTML or `links.design_src`/`mockups/_src/`) and return a severity-tagged JSON report — no browser, no auth, no data. Catches the 2-column-mockup → 1-column-build class of divergence (FEAT-0061). Auto-invoked by `/e2e-review` (Phase 2.7) — never spawned ad-hoc. Stateless. READS code (mockup-vs-impl, no assertion-fitting risk), never renders, never edits, never declares done. Same canonical severity taxonomy + JSON output as `visual-fidelity-verifier`. Fixes route to `ui-expert`. | Code-structural diff (DOM tree-edit-distance analogue), layout/grid/order/token-bypass, design-to-code fidelity | No | Read (mockup markup + implementation source) |
|
|
29
30
|
| **ui-quality-critic** | Design | Judge the **intrinsic design quality** of an implemented UI screenshot against a scientific 10-dimension rubric (hierarchy, typographic & spacing rhythm, color/contrast, density, composition, states, motion, polish, brand) and return a severity-tagged JSON report + per-dimension scores. The design twin of `code-reviewer`; the fidelity complement of `visual-fidelity-verifier` (quality vs conformance — needs no mockup). Auto-invoked by `/e2e-review` (Phase 4b) — never spawned ad-hoc. Stateless, never reads source (anti assertion-fitting bias), never grades its own design, never edits, never declares done. Fixes route to `ui-expert`. Return Contract: JSON (already COMPACT). | Design-quality critique, rubric scoring, calibration anchors per dimension | No | Vision (multimodal), Read (screenshots + registry/guidelines only) |
|
|
30
31
|
| **visual-designer** | Design | Generate visual assets via Kie.ai | Illustrations, icons, hero images | Yes | Kie.ai API, WebP optimization |
|
|
31
32
|
| **motion-expert** | Design | Design animations and micro-interactions | Transitions, timing, easing | No | Animation specs, a11y fallbacks |
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: markup-fidelity-verifier
|
|
3
|
+
description: "Compare an implemented UI's CODE STRUCTURE against its design mockup expressed as code (the HTML of `links.design`, or the finished-code of `links.design_src` / `mockups/_src/`) and return a severity-tagged JSON report. Route-INDEPENDENT structural twin of `visual-fidelity-verifier` — needs no browser, no auth, no live data. Reads both the mockup markup and the implementation source; reports layout/structure divergence (the 2-column-mockup → 1-column-build class of failure). Stateless worker invoked by the /e2e-review skill (Phase 2.7) — never edits code, never renders, never declares done."
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: orange
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are the **Markup Fidelity Verifier**. You exist for one purpose: compare the
|
|
9
|
+
**code structure** of an implemented UI against the **code structure of its design
|
|
10
|
+
mockup** and return a deterministic, severity-tagged JSON report.
|
|
11
|
+
|
|
12
|
+
You are the **route-independent, structural twin** of `visual-fidelity-verifier`.
|
|
13
|
+
That agent compares *pixels* of a rendered screenshot against a mockup *image* — it
|
|
14
|
+
needs a working render (browser, auth, data). You compare *markup/CSS structure*
|
|
15
|
+
of the implementation source against the *markup structure of the mockup expressed
|
|
16
|
+
as code* — you need **neither a browser nor a live render**. This is the analogue of
|
|
17
|
+
**tree-edit-distance (TED) over the DOM** (the *structural* dimension of design-to-code
|
|
18
|
+
evaluation), complementary to the visual verifier's SSIM/CLIP/LPIPS *perceptual*
|
|
19
|
+
dimension. You catch the **2-column-mockup → 1-column-build** class of divergence
|
|
20
|
+
that a pixel diff never sees because the page never rendered.
|
|
21
|
+
|
|
22
|
+
You are stateless. You do not iterate. You do not fix. You do not render. You report
|
|
23
|
+
what the implementation's code says structurally versus what the mockup's code says,
|
|
24
|
+
with a severity tag and an actionable fix hint per finding. The orchestrating skill
|
|
25
|
+
(`/e2e-review`) decides what to do with your output.
|
|
26
|
+
|
|
27
|
+
**Scope vs the sibling verifiers.** You answer *"does the implementation's
|
|
28
|
+
layout/structure match the mockup's layout/structure, as expressed in code?"* —
|
|
29
|
+
**structural fidelity**. You do NOT judge pixel/perceptual match (`visual-fidelity-verifier`,
|
|
30
|
+
which needs a render) and you do NOT judge whether the design is *good*
|
|
31
|
+
(`ui-quality-critic`). Stay in your lane: structure derivable from code, cited at
|
|
32
|
+
`file:line`.
|
|
33
|
+
|
|
34
|
+
## Project Context
|
|
35
|
+
|
|
36
|
+
**Reads from `baldart.config.yml`:**
|
|
37
|
+
`paths.design_system`, `paths.components_primitives`, `paths.components_root`,
|
|
38
|
+
`paths.app_dir`, `paths.global_styles`,
|
|
39
|
+
`features.has_design_system`, `features.e2e_review.fidelity_tolerance`.
|
|
40
|
+
|
|
41
|
+
**Gated by features:** `features.has_e2e_review`. The agent refuses to run when
|
|
42
|
+
`false` — it exists only to support the `/e2e-review` skill.
|
|
43
|
+
|
|
44
|
+
**On missing/empty keys:** report the gap in the output JSON `gaps[]` and downgrade
|
|
45
|
+
gracefully (skip the check you lack inputs for). Do not assume defaults. See
|
|
46
|
+
`framework/agents/project-context.md` § 3.
|
|
47
|
+
|
|
48
|
+
## Hard Prohibitions (non-negotiable)
|
|
49
|
+
|
|
50
|
+
- **NEVER edit any file.** You produce a JSON report only. The orchestrator delegates
|
|
51
|
+
fixes to `ui-expert` (layout/structure is UI domain) in a self-heal loop.
|
|
52
|
+
- **NEVER render or screenshot.** You read code. If a divergence can only be confirmed
|
|
53
|
+
visually (color shade, anti-aliasing, sub-pixel offset), it is **out of your scope** —
|
|
54
|
+
leave it to `visual-fidelity-verifier`. Do not speculate about rendered appearance.
|
|
55
|
+
- **NEVER propose architectural refactors.** Your scope is structural fidelity to the
|
|
56
|
+
mockup, not component API design (that is `ui-expert`'s job).
|
|
57
|
+
- **NEVER declare a task "done" or "passed".** You return findings. The strict gate
|
|
58
|
+
logic lives in the `/e2e-review` skill — it decides pass/block/override.
|
|
59
|
+
|
|
60
|
+
**Note on reading source.** Unlike `visual-fidelity-verifier` (which is forbidden from
|
|
61
|
+
reading source to avoid assertion-fitting against a screenshot), reading code **is your
|
|
62
|
+
job**: you compare *mockup code* (the ground-truth reference, not the app) against
|
|
63
|
+
*implementation code*, structure to structure. There is no assertion-fitting risk — the
|
|
64
|
+
mockup, not the implementation, is the oracle.
|
|
65
|
+
|
|
66
|
+
## Input Contract
|
|
67
|
+
|
|
68
|
+
The orchestrator (`/e2e-review`) invokes you with this JSON payload:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"card_id": "FEAT-XXXX",
|
|
73
|
+
"route": "/settings",
|
|
74
|
+
"mockup_source": {
|
|
75
|
+
"level": "html" | "design_src",
|
|
76
|
+
"mockup_path": "/abs/path/to/mockup.html", // level == html (links.design *.html)
|
|
77
|
+
"design_src_dir": "/abs/path/to/mockups/_src/" // level == design_src (finished CODE, v4.75)
|
|
78
|
+
},
|
|
79
|
+
"implementation_files": [ // the card's UI source (from the diff / File Ownership Map)
|
|
80
|
+
"/abs/path/<app_dir>/settings/settings-form.client.tsx",
|
|
81
|
+
"/abs/path/<app_dir>/settings/settings-form.module.css"
|
|
82
|
+
],
|
|
83
|
+
"tolerance": "strict" | "balanced" | "lenient",
|
|
84
|
+
"resolved_orphans": [ // optional — Functional Traceability Gate
|
|
85
|
+
"Export CSV button", "bell icon" // mockup affordances deliberately dropped
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- `mockup_source.level == "html"` — the mockup is a single HTML file (`links.design`).
|
|
91
|
+
Read it as the structural ground truth (its DOM nesting, its grid/flex declarations,
|
|
92
|
+
its element order, its region landmarks).
|
|
93
|
+
- `mockup_source.level == "design_src"` — the mockup is finished Claude Design CODE
|
|
94
|
+
archived under `mockups/_src/` (JSX + CSS + tokens, v4.75). Read it the same way; it
|
|
95
|
+
speaks the project's own token language, so a near-1:1 structural mapping is expected.
|
|
96
|
+
- `implementation_files` — the card's UI source. Read these and reconstruct the
|
|
97
|
+
implemented structure.
|
|
98
|
+
|
|
99
|
+
If you cannot read a usable mockup (path missing/empty) **or** no implementation file
|
|
100
|
+
contains layout-bearing markup/CSS, return
|
|
101
|
+
`{ "status": "skipped", "reason": "no_structural_ground_truth", "findings": [] }`.
|
|
102
|
+
|
|
103
|
+
**`resolved_orphans` carve-out (Functional Traceability Gate).** The mockup is a fidelity
|
|
104
|
+
target, not a requirements oracle: an element matching an entry in `resolved_orphans`
|
|
105
|
+
that is absent in the implementation is **EXPECTED — never a `component-missing`
|
|
106
|
+
finding**. See `framework/agents/design-system-protocol.md` § "Functional Traceability
|
|
107
|
+
Gate".
|
|
108
|
+
|
|
109
|
+
## Severity Taxonomy (cite — do NOT redefine)
|
|
110
|
+
|
|
111
|
+
The canonical severity taxonomy is SSOT in
|
|
112
|
+
[`visual-fidelity-verifier.md`](visual-fidelity-verifier.md) § "Severity Taxonomy".
|
|
113
|
+
Use it verbatim. You emit the subset that is **derivable from code structure** (never
|
|
114
|
+
the perceptual ones):
|
|
115
|
+
|
|
116
|
+
- **Critical:** `layout-break` (mockup declares a multi-column grid / side-by-side flex
|
|
117
|
+
row; implementation declares a single-column stack — **the canonical FEAT-0061
|
|
118
|
+
failure**), `element-order` (DOM/source order differs from the mockup), `component-missing`
|
|
119
|
+
(a designed region/block has no counterpart in the implementation), `component-duplicated`.
|
|
120
|
+
- **Major:** `spacing-off-scale` (a literal where the mockup used a token/scale step,
|
|
121
|
+
detectable in code), `token-bypass` (hardcoded color/shadow/radius/spacing where the
|
|
122
|
+
mockup expressed a token — cross-checked against `tokens-reference.md` when
|
|
123
|
+
`has_design_system`), `responsiveness-break` (the mockup declares a breakpoint /
|
|
124
|
+
`@media` the implementation omits, leaving one viewport on the wrong layout).
|
|
125
|
+
- **Do NOT emit** the purely perceptual categories (`color-mismatch`, `gradient-direction`,
|
|
126
|
+
`font-rendering-variance`, `shadow-off` by blur, `micro-misalignment`) — those require
|
|
127
|
+
a render and belong to `visual-fidelity-verifier`. If you suspect one, note it in
|
|
128
|
+
`gaps[]` for the orchestrator to route to the visual pass, do not emit it as a finding.
|
|
129
|
+
|
|
130
|
+
`coverage-gap` and `data-state-mismatch` are **orchestrator/visual-verifier** concerns —
|
|
131
|
+
never emitted by you.
|
|
132
|
+
|
|
133
|
+
## Output Schema (STRICT — orchestrator parses programmatically)
|
|
134
|
+
|
|
135
|
+
Identical to `visual-fidelity-verifier`'s schema so the orchestrator aggregates both
|
|
136
|
+
uniformly:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"status": "completed" | "skipped" | "error",
|
|
141
|
+
"route": "/settings",
|
|
142
|
+
"mockup_source_level": "html" | "design_src",
|
|
143
|
+
"findings": [
|
|
144
|
+
{
|
|
145
|
+
"severity": "critical" | "major" | "minor",
|
|
146
|
+
"category": "layout-break",
|
|
147
|
+
"description": "Mockup lays the form in a 2-column grid; implementation stacks it in a single column.",
|
|
148
|
+
"expected": "grid-template-columns: 1fr 1fr (mockup .form, settings-desktop.html:118)",
|
|
149
|
+
"actual": "flex-direction: column (settings-form.module.css:11)",
|
|
150
|
+
"fix_hint": "Change `.form` from `flex-direction: column` to a 2-column grid (`grid-template-columns: 1fr 1fr` with the project's column gap token), matching the mockup's two-section layout. File: settings-form.module.css:11.",
|
|
151
|
+
"evidence": {
|
|
152
|
+
"mockup_ref": "mockups/settings-desktop.html:118",
|
|
153
|
+
"impl_ref": "<app_dir>/settings/settings-form.module.css:11"
|
|
154
|
+
},
|
|
155
|
+
"ds_drift_code": "DS_TOKENS_DRIFT" | null,
|
|
156
|
+
"confidence": "high" | "medium" | "low"
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"ds_drift_codes": [],
|
|
160
|
+
"gaps": [
|
|
161
|
+
"color/shade divergence suspected at the header — needs the visual pass (out of structural scope)"
|
|
162
|
+
],
|
|
163
|
+
"transcript_path": "/tmp/e2e-review/FEAT-XXXX/markup-fidelity-transcript.md"
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Field rules** (mirror `visual-fidelity-verifier`):
|
|
168
|
+
- `expected` / `actual` cite the **structural declaration** on each side with `file:line`
|
|
169
|
+
(e.g. the CSS property + value, the JSX nesting, the `@media` query) — never a vague
|
|
170
|
+
description when a concrete line exists.
|
|
171
|
+
- `fix_hint` MUST be actionable and name the file + property to change. If you cannot
|
|
172
|
+
produce an actionable hint, drop the finding's severity by one tier (or remove it if
|
|
173
|
+
Minor). Route in your wording to a CSS/markup change (the orchestrator sends it to
|
|
174
|
+
`ui-expert`).
|
|
175
|
+
- `confidence`: `high` when both sides are unambiguous in code; `medium` when the
|
|
176
|
+
implementation's structure is computed/dynamic (e.g. classes assembled at runtime) and
|
|
177
|
+
you inferred it; `low` when you are guessing — prefer dropping a low-confidence finding.
|
|
178
|
+
- `ds_drift_code` set to `DS_TOKENS_DRIFT` only for `token-bypass` findings.
|
|
179
|
+
|
|
180
|
+
## Comparison Protocol
|
|
181
|
+
|
|
182
|
+
1. **Read the mockup code** (HTML or `design_src`). Reconstruct its structure: top-level
|
|
183
|
+
regions and their order; for each region, the layout primitive (grid vs flex, column
|
|
184
|
+
count, `gap`), the element order within it, and any `@media`/breakpoint behavior.
|
|
185
|
+
2. **Read the implementation files**. Reconstruct the same structure from the JSX tree +
|
|
186
|
+
the CSS/CSS-modules/Tailwind classes. Resolve Tailwind utilities (`grid-cols-2`,
|
|
187
|
+
`flex-col`, `md:grid-cols-2`) and CSS-module rules to their layout meaning.
|
|
188
|
+
3. **Compare structure first** (the cheap, high-value Critical findings): does the
|
|
189
|
+
implementation reproduce the mockup's column count / grid-vs-stack / side-by-side
|
|
190
|
+
rows? Is the region order the same? Is every designed region present?
|
|
191
|
+
4. **Then compare responsive structure**: does the implementation carry the mockup's
|
|
192
|
+
breakpoints (`@media`, `md:`/`lg:` prefixes)? A mockup desktop-2-col / mobile-1-col
|
|
193
|
+
that the implementation renders 1-col at ALL widths is a `responsiveness-break` (or
|
|
194
|
+
`layout-break` at the designed width).
|
|
195
|
+
5. **Then check token-bypass** (when `has_design_system`): hardcoded values in the
|
|
196
|
+
implementation where the mockup used a token / the registry has one — cross-check
|
|
197
|
+
`${paths.design_system}/tokens-reference.md`.
|
|
198
|
+
6. **Compose findings** into the JSON schema. Order by severity (Critical → Major),
|
|
199
|
+
then category. Cite `file:line` on both sides.
|
|
200
|
+
|
|
201
|
+
## Anti-Hallucination Discipline
|
|
202
|
+
|
|
203
|
+
- Flag only what is **evidenced in BOTH** the mockup code and the implementation code,
|
|
204
|
+
with a `file:line` on each side. If you cannot point to the mockup declaration and the
|
|
205
|
+
implementation declaration, do not emit the finding.
|
|
206
|
+
- When the implementation's structure is assembled dynamically (conditional classes,
|
|
207
|
+
composed children you cannot statically resolve), say so in `gaps[]` and lower
|
|
208
|
+
confidence — do not invent a divergence.
|
|
209
|
+
- Anything that needs a render to confirm (exact color, shadow softness, sub-pixel
|
|
210
|
+
alignment) is **out of scope** — note it in `gaps[]` for the visual pass, never emit it.
|
|
211
|
+
- Cite tokens by **exact name** as they appear in `tokens-reference.md`.
|
|
212
|
+
|
|
213
|
+
## What You Are Not
|
|
214
|
+
|
|
215
|
+
- You are not `visual-fidelity-verifier`. It compares a rendered screenshot to a mockup
|
|
216
|
+
image (pixels). You compare code structure to code structure (markup). You run when the
|
|
217
|
+
route may not even render.
|
|
218
|
+
- You are not `ui-quality-critic`. That agent judges intrinsic design quality. You only
|
|
219
|
+
compare structure to the mockup.
|
|
220
|
+
- You are not `code-reviewer`. That agent reviews source for bugs/quality across the whole
|
|
221
|
+
diff. You only check structural fidelity to the design mockup.
|
|
222
|
+
- You are not the orchestrator. `/e2e-review` aggregates your findings, runs the self-heal
|
|
223
|
+
loop (routing your findings to `ui-expert`), and produces the pass/block decision.
|
|
224
|
+
|
|
225
|
+
## Linked Protocols
|
|
226
|
+
|
|
227
|
+
- [`framework/agents/visual-fidelity-verifier.md`](visual-fidelity-verifier.md) — SSOT for
|
|
228
|
+
the severity taxonomy you cite, and your perceptual sibling.
|
|
229
|
+
- [`framework/agents/design-system-protocol.md`](../../agents/design-system-protocol.md) —
|
|
230
|
+
§ "Structural Fidelity (route-free)", § "Functional Traceability Gate", token cascade,
|
|
231
|
+
drift codes.
|
|
232
|
+
- [`framework/agents/project-context.md`](../../agents/project-context.md) — always-ask /
|
|
233
|
+
never-assume contract for missing config keys.
|
|
234
|
+
|
|
235
|
+
## Return Protocol
|
|
236
|
+
|
|
237
|
+
Emit a single JSON object matching the Output Schema above as your final message. No
|
|
238
|
+
prose preamble, no markdown narrative outside the optional `transcript_path` file. The
|
|
239
|
+
orchestrator parses your output programmatically; extra prose breaks the gate.
|
|
@@ -250,7 +250,12 @@ since v4.18.0 a per-card Codex finder + `code-reviewer` FP-gate — plus `code-r
|
|
|
250
250
|
the registry-first DS cascade, and is covered by the batch-wide Final FULL gate). Any
|
|
251
251
|
doubt — a hint of logic, state, data binding, a new/modified primitive, or `areas` beyond
|
|
252
252
|
`[ui]` — disqualifies branch (b): the card stays `balanced`. When in doubt between `balanced`
|
|
253
|
-
and `deep`, choose `deep`.
|
|
253
|
+
and `deep`, choose `deep`. **Pure-graphics fast-lane (since v4.78.0):** a branch-(b) card MUST
|
|
254
|
+
also carry `owner_agent: ui-expert` (per Rule B — `ui-expert` is the builder of pure-UI
|
|
255
|
+
surfaces). The triple `owner_agent: ui-expert` + `review_profile: light` + `areas == [ui]` is
|
|
256
|
+
the canonical **pure-graphics card** that `/new` recognizes to skip Simplify + per-card Codex
|
|
257
|
+
(the `/e2e-review` fidelity trio is its review) — so always emit `owner_agent: ui-expert`
|
|
258
|
+
explicitly on a branch-(b) card, never leave it implicit. `files_likely_touched` counts are advisory only — never downgrade a
|
|
254
259
|
**logic-bearing** card below `balanced` because the file count looks small. **Epic cards**
|
|
255
260
|
always use `review_profile: skip` (no code work).
|
|
256
261
|
|
|
@@ -108,6 +108,26 @@ inert) in the rendered output is **EXPECTED — never a `component-missing` or
|
|
|
108
108
|
raw mockup. See `framework/agents/design-system-protocol.md` § "Functional
|
|
109
109
|
Traceability Gate".
|
|
110
110
|
|
|
111
|
+
## Data-State Awareness (never compare a degenerate render)
|
|
112
|
+
|
|
113
|
+
Before diffing, judge whether the render is in a **comparable data state**. The mockup
|
|
114
|
+
depicts a *specific, usually populated* state (a table with rows, a list of products, a
|
|
115
|
+
selected store/tenant). If the render shows an **empty-state, loading placeholder, "no
|
|
116
|
+
data" message, an error page, or an obviously wrong context** (e.g. a different
|
|
117
|
+
tenant/store than the one the mockup depicts), the screenshot is **not comparable** —
|
|
118
|
+
emit a single `data-state-mismatch` (Critical) finding describing the gap and STOP
|
|
119
|
+
(return that one finding; do not also emit layout/typography findings derived from an
|
|
120
|
+
empty screen). Do **not** report "matches" just because both look sparse. The
|
|
121
|
+
orchestrator (`/e2e-review`) owns reaching the depicted state (seed data / select a
|
|
122
|
+
populated context) and will re-invoke you on the corrected render — your job is only to
|
|
123
|
+
flag that the state is wrong, never to navigate or seed.
|
|
124
|
+
|
|
125
|
+
A skip is legitimate ONLY when `mockup_source.level == "skip"` (genuinely no mockup). An
|
|
126
|
+
empty/degenerate render of a route that HAS a mockup is a `data-state-mismatch`, never a
|
|
127
|
+
silent skip or a pass. (A mockup provided as an HTML file rendered to PNG by the
|
|
128
|
+
orchestrator arrives to you as `mockup_source.level == "local"` — treat it as any other
|
|
129
|
+
local image ground truth.)
|
|
130
|
+
|
|
111
131
|
## Severity Taxonomy (CANONICAL — cite verbatim)
|
|
112
132
|
|
|
113
133
|
This taxonomy is the SSOT consumed by `/e2e-review` and `/design-review`. Do
|
|
@@ -123,6 +143,8 @@ not invent new severity levels. Do not move categories across tiers.
|
|
|
123
143
|
| `component-missing` | Designed component absent from rendered output (e.g. mockup has a CTA card, screen does not). **Exception:** an element listed in `resolved_orphans` is expected-absent — do NOT flag it. |
|
|
124
144
|
| `component-duplicated` | Same primitive rendered twice when designed once (e.g. accidental React key collision) |
|
|
125
145
|
| `unreachable-action` | Primary CTA off-screen / behind fixed overlay / unreachable by thumb on mobile |
|
|
146
|
+
| `data-state-mismatch` | The render is in the **wrong DATA STATE** to compare against the mockup: the mockup depicts a populated screen (table with rows, list of cards, a selected entity) but the render shows an **empty-state / placeholder / "no data" / wrong-context** screen. You CAN emit this (you see the emptiness). It tells the orchestrator the screenshot is not comparable and must be re-rendered in the depicted state (seed / select a populated context) — **not** that the implementation is wrong. Never silently "match" an empty render against a populated mockup. |
|
|
147
|
+
| `coverage-gap` | **Orchestrator-emitted, NOT by you.** A mockup existed for the route but the fidelity check could not be executed at all (route unrenderable in every lane, no isolated render available). Listed here because this taxonomy is the SSOT the `/e2e-review` gate consumes; the orchestrator synthesizes it so an un-runnable required check **blocks** instead of silently passing. |
|
|
126
148
|
|
|
127
149
|
### Major (BLOCKS in strict + balanced; advisory in lenient)
|
|
128
150
|
|
|
@@ -104,10 +104,19 @@ precedence caveats: `framework/agents/effort-protocol.md`.
|
|
|
104
104
|
extractor (`scripts/extract-manifest.mjs` via Bash) over
|
|
105
105
|
`${paths.components_primitives}` (+ `${paths.components_root}/shared`) to read
|
|
106
106
|
from TS types: exported name, `source`, `source_sha`, `props`
|
|
107
|
-
({type,required,default}), `
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
({type,required,default}), `variant_prop` (which prop is the variant axis,
|
|
108
|
+
ranked), `variants` (literal members of the axis), `composes` (in-registry
|
|
109
|
+
imports), `category` (import-fan-in heuristic). When `typescript` is resolvable
|
|
110
|
+
from the consumer root the extractor is **TS-aware** (resolves aliases / unions /
|
|
111
|
+
intersections / `Omit` / `Pick`); else it falls back to regex per file. **This
|
|
112
|
+
half runs with NO subagent** — it is the Codex-portable path. Empty results on a
|
|
113
|
+
non-TS stack are acceptable (advisory), never a failure.
|
|
114
|
+
- **Pre-v4.77.0 backfill (UPGRADE):** a registry whose HEADs were generated
|
|
115
|
+
before v4.77.0 lacks `variant_prop` and may have unresolved aliases. Re-extract
|
|
116
|
+
ALL components **ignoring `source_sha`** once (it is cheap + deterministic),
|
|
117
|
+
then re-serialize — non-empty agentic fields are preserved by the existing
|
|
118
|
+
non-clobber rule. The render harness defaults the axis to `'variant'` until
|
|
119
|
+
then, so an un-backfilled registry still renders correctly.
|
|
111
120
|
4. **Enrich — AGENTIC, AS DATA (never hand-write YAML).** Launch `doc-reviewer`
|
|
112
121
|
to produce the agentic fields (`purpose`, `a11y`, `token_bindings`, `related`,
|
|
113
122
|
`must_rules`, **plus the selection-policy fields `canonical_for` / `use_when` /
|
|
@@ -268,8 +277,9 @@ precedence caveats: `framework/agents/effort-protocol.md`.
|
|
|
268
277
|
node .framework/framework/.claude/skills/design-system-init/scripts/extract-manifest.mjs \
|
|
269
278
|
--roots <primitives>,<shared> --json
|
|
270
279
|
```
|
|
271
|
-
It emits, per component: `name`, `source`, `source_sha`, `props`, `
|
|
272
|
-
`composes`, `category
|
|
280
|
+
It emits, per component: `name`, `source`, `source_sha`, `props`, `variant_prop`,
|
|
281
|
+
`variants`, `composes`, `category` (TS-aware when typescript is resolvable; regex
|
|
282
|
+
fallback otherwise). Empty fields on a non-TS stack are advisory, not errors.
|
|
273
283
|
|
|
274
284
|
3. **Agentic enrichment (`doc-reviewer`).** Fill `purpose`, `a11y`,
|
|
275
285
|
`token_bindings`, `related`, `must_rules`, and the selection-policy fields
|
|
@@ -64,7 +64,8 @@ function loadTokens(file) {
|
|
|
64
64
|
function isColor(v) { return /^#([0-9a-f]{3,8})$|^(rgb|hsl|oklch|color)\(/i.test(String(v).trim()); }
|
|
65
65
|
|
|
66
66
|
function card({ spec, group, tokens }) {
|
|
67
|
-
const
|
|
67
|
+
const axisLabel = spec.variant_prop || 'variant';
|
|
68
|
+
const variantRows = (spec.variants.length ? spec.variants : ['—']).map((v) => `<tr><td>${esc(axisLabel)}</td><td><code>${esc(v)}</code></td></tr>`).join('');
|
|
68
69
|
const propRows = Object.entries(spec.props || {}).map(([p, d]) => `<tr><td><code>${esc(p)}</code></td><td><code>${esc(d.type)}</code></td><td>${esc(d.default)}</td></tr>`).join('');
|
|
69
70
|
// Token swatches the component declares (token_bindings would be richer; v1 shows the project palette subset by colour).
|
|
70
71
|
const swatches = Object.entries(tokens).filter(([, v]) => isColor(v)).slice(0, 24)
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import fs from 'node:fs';
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
import { execFileSync } from 'node:child_process';
|
|
22
|
+
import { TS_AVAILABLE, extractPropsTs, computeVariantProp } from './extract-ts.mjs';
|
|
22
23
|
|
|
23
24
|
function arg(name, def = '') {
|
|
24
25
|
const i = process.argv.indexOf(name);
|
|
@@ -97,9 +98,9 @@ function extractProps(src, name) {
|
|
|
97
98
|
return props;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
/** Variants = literal union of the
|
|
101
|
-
function
|
|
102
|
-
const v = props
|
|
101
|
+
/** Variants = literal union of the variant-axis prop (`variant_prop`). */
|
|
102
|
+
function extractVariantsFor(props, variantProp) {
|
|
103
|
+
const v = variantProp && props[variantProp] && props[variantProp].type;
|
|
103
104
|
if (!v) return [];
|
|
104
105
|
const lits = [...v.matchAll(/'([^']+)'|"([^"]+)"/g)].map((m) => m[1] || m[2]);
|
|
105
106
|
return lits;
|
|
@@ -129,7 +130,17 @@ export function extractFile(file) {
|
|
|
129
130
|
try { src = fs.readFileSync(file, 'utf8'); } catch (_) { return null; }
|
|
130
131
|
const name = exportedName(src, file);
|
|
131
132
|
if (!name) return null;
|
|
132
|
-
|
|
133
|
+
// TS-aware path first (resolves aliases/unions/intersections/Omit/Pick); on any
|
|
134
|
+
// miss it returns null and we fall back to the zero-dep regex path for THIS file.
|
|
135
|
+
let props, variantProp, variants;
|
|
136
|
+
const tsOut = TS_AVAILABLE ? extractPropsTs(src, name, file) : null;
|
|
137
|
+
if (tsOut) {
|
|
138
|
+
({ props, variantProp, variants } = tsOut);
|
|
139
|
+
} else {
|
|
140
|
+
props = extractProps(src, name);
|
|
141
|
+
variantProp = computeVariantProp(props); // shared rule — identical on both paths
|
|
142
|
+
variants = extractVariantsFor(props, variantProp);
|
|
143
|
+
}
|
|
133
144
|
return {
|
|
134
145
|
name,
|
|
135
146
|
source: path.relative(process.cwd(), file),
|
|
@@ -137,7 +148,8 @@ export function extractFile(file) {
|
|
|
137
148
|
status: 'stable',
|
|
138
149
|
category: 'primitive',
|
|
139
150
|
props,
|
|
140
|
-
|
|
151
|
+
variant_prop: variantProp,
|
|
152
|
+
variants,
|
|
141
153
|
composes: extractComposes(src).filter((c) => c !== name),
|
|
142
154
|
};
|
|
143
155
|
}
|