{
  "$schema": "./schemas/divergences-decisions.schema.json",
  "version": 1,
  "decisions": [
    {
      "id": "icon-variable-resolved-to-hex",
      "category": "figma-pipeline-limitation",
      "component": "Icon",
      "components": null,
      "subject": "Figma SVG export Variable resolved-to-hex",
      "figmaSide": "Figma image API resolves Color Variable bindings to literal hex in exported SVG; current single-color non-brand icon path fill literal is #dbdbdb.",
      "codeSide": "figma-sync/export-icons.mjs transformSvgCurrentColor converts #dbdbdb path fills to currentColor; consumer components provide color via cascade.",
      "status": null,
      "reason": "Figma export drops Variable binding semantics; code restores the intended currentColor behavior without treating it as design drift.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "pnpm audit:icon-fill-currentcolor → 0 findings",
      "notes": "Full rationale remains in divergences.md."
    },
    {
      "id": "datetime-select-feature-topology",
      "category": "component-level-translation",
      "component": "DateTime",
      "components": ["DateTime", "Select"],
      "subject": "DateTime ↔ Select.feature=date|time",
      "figmaSide": "Figma Select component contains date/time feature variants.",
      "codeSide": "Code splits date/time behavior into DateTime docs/topology over SelectBoxLine + SelectBoxFilled; no independent DateTime component file currently exists.",
      "status": null,
      "reason": "Website docs and Vue usage organize by user feature while Figma keeps one Select component.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -rnE \"SelectBoxLine|SelectBoxFilled|DateTime\" playground/docs/pages/DateTimePage.vue src/canonical",
      "notes": "DateTime should audit against Select.feature=date|time subset."
    },
    {
      "id": "input-dual-family-single-component",
      "category": "component-level-translation",
      "component": "Input",
      "components": null,
      "subject": "Input ↔ input box/filled + input box/line",
      "figmaSide": "Figma uses line and filled families for visual style.",
      "codeSide": "Code exposes a single Input component with variant/appearance style switching.",
      "status": null,
      "reason": "Developers should import one Input component instead of separate InputFilled/InputLine components.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Input audit should compare against the union of filled and line Figma families."
    },
    {
      "id": "select-dual-family-single-component",
      "category": "component-level-translation",
      "component": "Select",
      "components": null,
      "subject": "Select ↔ select box/filled + select box/line",
      "figmaSide": "Figma uses line and filled families for Select, with date/time feature variants excluded to DateTime.",
      "codeSide": "Code exposes a single Select component.",
      "status": null,
      "reason": "Developers should import one Select component; date/time variants are documented under DateTime.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Select audit should compare against filled + line and exclude feature=date|time."
    },
    {
      "id": "breadcrumb-item-container-topology",
      "category": "component-level-translation",
      "component": "Breadcrumb",
      "components": ["Breadcrumb", "BreadcrumbItem"],
      "subject": "Breadcrumb ↔ Breadcrumb/Item container aggregation",
      "figmaSide": "Figma publishes Breadcrumb/Item but no container.",
      "codeSide": "Code has Breadcrumb container for layout and BreadcrumbItem for the Figma item.",
      "status": null,
      "reason": "Container is a code-side composition layer around the item-level Figma source.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Breadcrumb container is allowed as code-side extra; BreadcrumbItem audits against Figma."
    },
    {
      "id": "steps-item-container-topology",
      "category": "component-level-translation",
      "component": "Steps",
      "components": ["Steps", "StepItem"],
      "subject": "Steps ↔ Step/Item container aggregation",
      "figmaSide": "Figma publishes Step/Item.",
      "codeSide": "Code has Steps container plus StepItem corresponding to Figma item.",
      "status": null,
      "reason": "Container + item is the runtime topology for item-level Figma source.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Same pattern as Breadcrumb."
    },
    {
      "id": "button-eight-sets-single-component",
      "category": "component-level-translation",
      "component": "Button",
      "components": null,
      "subject": "Button ↔ 8 Button/{theme} {size} component sets",
      "figmaSide": "Figma splits Button into 8 sets by theme and size.",
      "codeSide": "Code exposes a single Button component with props covering theme, size, and type dimensions.",
      "status": null,
      "reason": "Developers should not import eight different Button components.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Button audit should compare against the property union of 8 sets."
    },
    {
      "id": "icon-registry-asset-aggregation",
      "category": "component-level-translation",
      "component": "Icon",
      "components": null,
      "subject": "Icon ↔ icon/* asset aggregation",
      "figmaSide": "Figma publishes many icon/{category}/{name} component sets.",
      "codeSide": "Icon.vue is a runtime registry entry that loads icons by name.",
      "status": null,
      "reason": "Icon is an asset registry aggregation, not a standard single component-to-set mapping.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "Check icon-aliases.ts values resolve to existing Figma icons.",
      "notes": "Icon uses icon-aliases.ts completeness checks instead of standard props/variants audit."
    },
    {
      "id": "logo-icon-assets-aggregation",
      "category": "component-level-translation",
      "component": "Logo",
      "components": null,
      "subject": "Logo ↔ icon/logo/TVU + icon/logo/ts",
      "figmaSide": "Figma maintains logo assets in icon/logo sets.",
      "codeSide": "Logo switches by type prop (tvu | ts).",
      "status": null,
      "reason": "Logo is a runtime component over multiple logo asset sources.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Logo audit should compare against the union of icon/logo/TVU and icon/logo/ts."
    },
    {
      "id": "alert-figma-source-unconfirmed",
      "category": "component-level-translation",
      "component": "Alert",
      "components": null,
      "subject": "Alert Figma source unconfirmed (RESOLVED — component deleted)",
      "figmaSide": "Deterministically confirmed the Figma published library has NO Alert component (only Notification + Popup Box).",
      "codeSide": "Alert.vue was a fabricated code-only component (option c). DELETED via CANONICAL-030 (owner ack 2026-06-04): src/components/Alert/ removed, no export, no canonical wrapper.",
      "status": "resolved",
      "reason": "Source confirmed nonexistent; per hard-rule #3 a fabricated component is a bug → deleted rather than kept.",
      "resolvedAt": "2026-06-04",
      "resolutionRef": "CANONICAL-030",
      "phase": null,
      "verifyHint": null,
      "notes": "Historical designer options retained in divergences.md for provenance."
    },
    {
      "id": "badge-canonical-legacy-dual-source",
      "category": "code-side-dual-source",
      "component": "Badge",
      "components": null,
      "subject": "Badge canonical/legacy dual source",
      "figmaSide": "Figma Badge axes align to canonical Badge.",
      "codeSide": "src/canonical/Badge.vue is npm SoT; src/components/Badge/Badge.vue is legacy for playground/local use.",
      "status": null,
      "reason": "Canonical rewrite cannot wrap legacy because legacy API is too far from Figma axes.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -rnE \"export .*Badge|from './canonical/Badge'|LEGACY\" src/index.ts src/components/Badge/Badge.vue",
      "notes": "AI tools must use canonical Badge for Figma URL → Vue code generation."
    },
    {
      "id": "badge-neutral-filled-bg-layer3-to-layer4",
      "category": "resolved",
      "component": "Badge",
      "components": null,
      "subject": "Badge Neutral filled background: bg-layer3 → bg-layer4 (Figma catch-up)",
      "figmaSide": "Pre-2026-05-27: Badge type=circle|rectangle color=neutral tag=filled background bound to Color Type/Background/Layer_3 (Dark #262626 / Light #f0f0f0). 2026-05-27 Figma update: rebound to Color Type/Background/Layer_4 (Dark #353535 / Light #dbdbdb), verified by user in consumer product design.",
      "codeSide": "src/canonical/Badge.vue line 24: Neutral.fillBg = var(--bg-layer4) since 2026-05-08 pre-emptive migration (commented in source: '2026-05-08 Badge fig: Black→Neutral rename + filled-bg layer_4 + theme-aware text'). Code led Figma by ~3 weeks.",
      "status": "resolved",
      "reason": "Drift originated from code-side pre-emptive token semantic upgrade (layer_3 → layer_4) ahead of Figma; closed when Figma side caught up on 2026-05-27 sync.",
      "resolvedAt": "2026-05-27",
      "resolutionRef": "commit 31911264 (Figma sync) + commit 9e3ca1ae checkpoint (code-side initial migration baseline)",
      "phase": null,
      "verifyHint": "grep -nE \"Neutral.*--bg-layer4\" src/canonical/Badge.vue && grep -nE \"^  --bg-layer4:\" src/tokens/variables.css",
      "notes": "Drift was never formally logged at the time of code-side migration (2026-05-08); recorded retroactively at close for paper trail. No code change required at resolution — Figma sync alone closed the loop."
    },
    {
      "id": "figma-dark-theme-design-state",
      "category": "accepted-divergence",
      "component": null,
      "components": ["Input", "CheckBox", "Radio", "Switch", "Select", "Tooltip"],
      "subject": "Figma dark theme property",
      "figmaSide": "Variant property dark theme on/off.",
      "codeSide": "Runtime theme is provided by global ThemeProvider/CSS variables, not per-component prop.",
      "status": "accepted-documented-divergence",
      "reason": "Design-time theme preview switch only.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Exceptions: Notification.theme and Message.theme are single-instance runtime switches and are mapped as code props."
    },
    {
      "id": "figma-enable-design-state",
      "category": "accepted-divergence",
      "component": null,
      "components": ["Input", "CheckBox", "Radio", "Switch", "Select"],
      "subject": "Figma enable property",
      "figmaSide": "Variant property enable on/off or yes/no.",
      "codeSide": "Runtime disabled state is represented by code disabled prop.",
      "status": "accepted-documented-divergence",
      "reason": "Design-time preview switch only.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": null
    },
    {
      "id": "figma-status-design-state-values",
      "category": "accepted-divergence",
      "component": null,
      "components": ["Input", "CheckBox", "Radio", "Select"],
      "subject": "Figma status design-state values",
      "figmaSide": "status includes default/normal/hover/click/error/Filled visual preview values.",
      "codeSide": "Most states are handled by CSS pseudo-classes and runtime error/disabled props; selected runtime exceptions are documented elsewhere.",
      "status": "accepted-documented-divergence-with-exceptions",
      "reason": "Most status values are design-time visual previews rather than runtime props.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Runtime exceptions include Switch.status=live and Select.status=multi select."
    },
    {
      "id": "figma-ux-design-state-values",
      "category": "accepted-divergence",
      "component": null,
      "components": ["Input", "Select"],
      "subject": "Figma UX design-state values",
      "figmaSide": "UX includes click/default/hover/error visual preview values.",
      "codeSide": "Most UX states are not mapped; runtime exceptions are documented separately.",
      "status": "accepted-documented-divergence-with-exceptions",
      "reason": "Figma UX values are design-time visual previews.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Runtime exception: Select.UX=editable."
    },
    {
      "id": "figma-show-icon-option-design-state",
      "category": "accepted-divergence",
      "component": null,
      "components": ["CheckBox", "Radio"],
      "subject": "Figma Show Icon / Show Option properties",
      "figmaSide": "Boolean design properties controlling icon/option visibility.",
      "codeSide": "Runtime icon/text visibility is handled by Vue slots.",
      "status": "accepted-documented-divergence",
      "reason": "Design-time decorative switches only.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": null
    },
    {
      "id": "figma-content-slot-vue-slot",
      "category": "accepted-divergence",
      "component": null,
      "components": ["Input", "FormItem", "Notification", "Message"],
      "subject": "Figma Content SLOT",
      "figmaSide": "Content is a Figma SLOT property.",
      "codeSide": "Vue default slot carries equivalent runtime content composition.",
      "status": "accepted-documented-divergence",
      "reason": "Figma SLOT is not equal to Vue slot API.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "FormItem.Label and Tooltip.Content are separate dual-form decisions."
    },
    {
      "id": "figma-icon-slot-vue-slot",
      "category": "accepted-divergence",
      "component": null,
      "components": ["CheckBox", "Radio"],
      "subject": "Figma Icon SLOT",
      "figmaSide": "Icon is a Figma SLOT property.",
      "codeSide": "Vue slots handle runtime composition.",
      "status": "accepted-documented-divergence",
      "reason": "Same Content SLOT decision.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": null
    },
    {
      "id": "progress-pseudo-theme-accepted-divergence",
      "category": "accepted-divergence",
      "component": "Progress",
      "components": null,
      "subject": "Figma Progress.theme pseudo theme",
      "figmaSide": "Progress dark/light variants have identical fills in Figma.",
      "codeSide": "Code removed self-created .progress--light visual difference; theme prop remains vestigial to avoid breaking callers.",
      "status": "accepted-documented-divergence",
      "reason": "Align code with Figma source and avoid invented visual differences.",
      "resolvedAt": null,
      "resolutionRef": "T1b 2026-04-29",
      "phase": "Phase 6.4 deprecation evaluation pending",
      "verifyHint": "grep -rnE \"progress--light|progress--\\\\$\\{theme\\}\" src/components/Progress/Progress.vue",
      "notes": "Runtime + canonical API still expose theme, but it has no visual difference."
    },
    {
      "id": "formitem-label-dual-form",
      "category": "resolved",
      "component": "FormItem",
      "components": null,
      "subject": "FormItem.label ↔ Figma Label SLOT",
      "figmaSide": "Label is a Figma SLOT.",
      "codeSide": "Code supports string prop or named #label slot; named slot wins when both exist. Default slot reserved for content/control area.",
      "status": "resolved",
      "reason": "Simple text should use prop; complex content should use slot.",
      "resolvedAt": "2026-05-14",
      "resolutionRef": "v0.4.0",
      "phase": "Phase 6.6",
      "verifyHint": "grep -n 'slot name=\"label\"' src/components/FormItem/FormItem.vue",
      "notes": "Phase 6.6a (commit 529bb366, 2026-05-14): FormItem named #label slot shipped. Default slot kept as content/control area (Element Plus convention)."
    },
    {
      "id": "tooltip-content-dual-form",
      "category": "resolved",
      "component": "Tooltip",
      "components": null,
      "subject": "Tooltip.content ↔ Figma Content SLOT",
      "figmaSide": "Content is a Figma SLOT.",
      "codeSide": "Code supports string prop or named #content slot; named slot wins when both exist. Default slot reserved for trigger element.",
      "status": "resolved",
      "reason": "Matches Element Plus-style dual form.",
      "resolvedAt": "2026-05-14",
      "resolutionRef": "v0.4.0",
      "phase": "Phase 6.6",
      "verifyHint": "grep -n 'slot name=\"content\"' src/components/Tooltip/Tooltip.vue",
      "notes": "Phase 6.6b (commit 0db2bfd0, 2026-05-14): Tooltip named #content slot shipped. Default slot kept as trigger element (Element Plus convention). Companion to formitem-label-dual-form."
    },
    {
      "id": "badge-api-split",
      "category": "resolved",
      "component": "Badge",
      "components": null,
      "subject": "Badge API type split",
      "figmaSide": "Figma Type axis means shape: Circle/Rectangle.",
      "codeSide": "Canonical Badge.vue splits into color (semantic palette: Neutral|Blue|Green|Orange|Red) + tag (Filled|Line) + type (Circle|Rectangle, aligned to Figma Type axis name). Legacy Badge.vue retains type=color for playground/App.vue + src/legacy/index.ts + atomicAssets demo only (not exported to npm per badge-canonical-legacy-dual-source decision).",
      "status": "resolved",
      "reason": "Same prop name carried different semantics across Figma and code.",
      "resolvedAt": "2026-05-14",
      "resolutionRef": "v0.4.0",
      "phase": "Phase 6.7",
      "verifyHint": "grep -nE \"^type Type = 'Circle' \\| 'Rectangle'\" src/canonical/Badge.vue",
      "notes": "Phase 6.7 ack (commit 5546ae0b, 2026-05-14, B path): canonical Badge has had this API since checkpoint 9e3ca1ae. SoT drift discovered in pickup 2026-05-14 (pickup mistakenly described canonical.type as Black/Blue/Green color enum); reconciled by clarifying divergences.md + decision JSON narrative. Earlier codeSide 'split into color + shape' was semantic intent — 'shape' is the semantic, not the prop name; canonical keeps 'type' to align with Figma Type axis name per translation-minimization principle. No code change needed."
    },
    {
      "id": "button-canonical-api-migration",
      "category": "migration-plan",
      "component": "Button",
      "components": null,
      "subject": "Button dual API canonical migration",
      "figmaSide": "Figma Button dimensions include theme across 8 sets.",
      "codeSide": "Code currently has old variant/size/disabled/loading API and canonical* API; decision is to complete migration to canonical contract.",
      "status": "resolved",
      "reason": "Canonical API should become the primary Figma-aligned contract.",
      "resolvedAt": "2026-05-18",
      "resolutionRef": "136ddc4b",
      "phase": "Phase 6.8",
      "verifyHint": null,
      "notes": "Phase 6.8 (2026-05-18): canonicalTheme axis added to ButtonBridge.vue + Button.vue; legacy props (variant/size/disabled/loading) @deprecated since 0.5.0, removed in v0.6.0."
    },
    {
      "id": "promptmessage-interact-design-state",
      "category": "accepted-divergence",
      "component": "Message",
      "components": null,
      "subject": "Message.interact",
      "figmaSide": "interact property is a design-time preview switch.",
      "codeSide": "Runtime interaction is handled by CSS :hover/:active and event handlers.",
      "status": "accepted-documented-divergence",
      "reason": "Design-time preview switch only.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": null
    },
    {
      "id": "notification-success-deletion",
      "category": "resolved",
      "component": "Notification",
      "components": null,
      "subject": "Notification.success status",
      "figmaSide": "No success status exists in the published Notification component set.",
      "codeSide": "Historical runtime-only success status was invented in code.",
      "status": "resolved",
      "reason": "Code invented a status outside the Figma family.",
      "resolvedAt": "2026-04-28",
      "resolutionRef": "Phase 6.3",
      "phase": null,
      "verifyHint": "grep -rnE \"['\\\"]success['\\\"]|notif--success\" src/components/Notification src/canonical/Notification.vue",
      "notes": "Code no longer has success runtime status; dead CSS .notif--success was removed."
    },
    {
      "id": "notification-type-deletion",
      "category": "resolved",
      "component": "Notification",
      "components": null,
      "subject": "Notification.type legacy axis",
      "figmaSide": "No legacy type axis exists in the published Notification component set.",
      "codeSide": "Historical legacy compatibility axis existed in code.",
      "status": "resolved",
      "reason": "Code invented a second semantic axis outside the Figma family.",
      "resolvedAt": "2026-04-28",
      "resolutionRef": "Phase 6.3",
      "phase": null,
      "verifyHint": "grep -rnE \"type\" src/components/Notification src/canonical/Notification.vue",
      "notes": "Legacy type axis no longer exists in runtime or canonical Notification implementation."
    },
    {
      "id": "chart-color-palette-2026-05-13",
      "category": "accepted-divergence",
      "component": "Chart",
      "components": null,
      "subject": "12 chart-color-* tokens (BRIDGE-MOCKUP-005)",
      "figmaSide": "Figma library has 4 base hue Variables (brand/blue/orange/red) but no chart-specific categorical palette of 12 hues.",
      "codeSide": "src/tokens/variables.css ships --chart-color-1..12 (1-4 alias to brand/red/blue/orange; 5-12 inline hex following HSL rotation rules; see chart-palette-preview.html for the rule).",
      "status": "accepted-documented-divergence",
      "reason": "CANONICAL-011 Chart implementation needs 12-color categorical palette; Figma library would have shipped same hues post-design-review. User approved code-first 2026-05-19.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -nE 'chart-color' src/tokens/variables.css | wc -l → 24 (12 dark + 12 light)",
      "notes": "When designer adds Color Type/Chart/1..12 Variables to Figma, sync pipeline will produce var() references; code tokens 5-12 should switch from inline hex to var(--figma-chart-N). 0 API change, 0 consumer impact."
    },
    {
      "id": "msg-bg-tinted-dark-set-2026-05-19",
      "category": "accepted-divergence",
      "component": null,
      "components": ["Notification", "Message", "Pill"],
      "subject": "--msg-bg-red / --msg-bg-brand / --msg-bg-blue / --msg-bg-grey (BRIDGE-MOCKUP-001)",
      "figmaSide": "Library has UX/Red/Msg-bg / UX/Blue/Msg-bg / UX/Orange/Msg-bg but all are light-pink/light-blue (双 mode 同色), so dark theme tinted bg has no native token expression; mockup workaround uses hardcoded RGB at 0.18 opacity.",
      "codeSide": "src/tokens/variables.css ships 4 theme-aware tokens --msg-bg-red / --msg-bg-brand / --msg-bg-blue / --msg-bg-grey, with dark/light values pre-computed as 18% tint over respective theme bg.",
      "status": "accepted-documented-divergence",
      "reason": "Mockup work needed dark-mode soft bg; user approved code-first 2026-05-19 to unblock Pill component (BRIDGE-MOCKUP-003) and downstream consumer mockups.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -nE 'msg-bg-(red|brand|blue|grey)' src/tokens/variables.css → 8 entries (4 in :root dark + 4 in [data-theme=light])",
      "notes": "When designer adds UX/Red/Msg-bg-dark etc. Variables to Figma, the dark hex values should match these (#3b1c1a / #19311e / #1a2b3c / #353535)."
    },
    {
      "id": "pill-component-family-2026-05-19",
      "category": "accepted-divergence",
      "component": null,
      "components": ["PillStatus", "PillCounter"],
      "subject": "Pill/Status + Pill/Counter component family (BRIDGE-MOCKUP-003)",
      "figmaSide": "PillStatus: now published as `Pill Status` (componentSet 5672:140617, 8 variants Status×Active) — divergence half closed. PillCounter: still no published `Pill/Counter`; remains file-local masters in product mockup files (e.g. MicroApps DtZcMkhNy6qh6jbQQnhreQ `MicroApp Counter Pill`).",
      "codeSide": "src/canonical/PillStatus.vue + PillCounter.vue ship as canonical exports via src/index.ts. Internal src/components/Pill/Pill.vue base (geometry 1:1 with Figma Pill Status: h28 / pad 4-12-4-10 / gap 8 since CANONICAL-028 2026-06-02). PillStatus variants: On-air/Preview/Analyzing/Inactive × Active(True/False); colors = hue@18% bg + solid hue border/dot/text via color-mix over --red/--brand/--blue (NOT --msg-bg-* tokens). PillCounter variants: success/warning/info/neutral (code-first, no Figma source).",
      "status": "accepted-documented-divergence-with-exceptions",
      "reason": "MicroApps mockup work needed dark-mode status pill; user approved code-first 2026-05-19. PillStatus has since been reconciled 1:1 to the published Figma `Pill Status` (CANONICAL-028) and verifier-covered (render-verification 8/8 pass 2026-06-02) → that half is resolved. PillCounter remains code-first pending Figma promotion.",
      "resolvedAt": null,
      "resolutionRef": "CANONICAL-028 (PillStatus half: published + synced + hue/geometry aligned + verifier-covered)",
      "phase": null,
      "verifyHint": "grep -lrE 'PillStatus|PillCounter' src/canonical src/index.ts",
      "notes": "PillStatus is now Figma-backed + machine-verified (render-verification 8/8). PillCounter is the only open half: when a `Pill/Counter` is promoted into the library, its wrapper's data-figma-component='Pill/Counter' will match. No docs site page yet."
    },
    {
      "id": "input-html-leaf-no-flex-children-2026-05-21",
      "category": "accepted-divergence",
      "component": "Input",
      "components": ["Input", "InputBoxFilled", "InputBoxLine"],
      "subject": "Figma input box root gap ↔ HTML <input> leaf element (cannot host flex children)",
      "figmaSide": "Figma input box/filled + input box/line root is layoutMode=HORIZONTAL container with 2 subnodes (text + optional icon/feature region) and itemSpacing (gap) = 8px. All size variants bind gap=8 (evidence: figma-data/normalized/components-tokenized/input_box_{filled,line}__*.json).",
      "codeSide": "Code .tvu-input (rootTargetSelector per render-verification-manifest) is the HTML <input> element directly. <input> is an HTML void element — cannot have child elements, therefore cannot host flex auto-layout children, therefore CSS gap has no effect. The .tvu-input-wrap parent <div> wraps {<input>, optional <span class=tvu-input-count>} which is a code-side count container, not the semantic equivalent of Figma's input box flex root.",
      "status": "accepted-documented-divergence",
      "reason": "Technical impossibility — same family as figma-content-slot-vue-slot (API form technical unreachable). HTML <input> spec prohibits child elements. Restructuring Input to wrap <input> in a flex container with a separate text/icon slot would be a larger architectural change and would alter the public API. Current Input.vue uses native <input> for accessibility + form-data parity, which is the right call.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -n 'rootGapMeaningful' tests/visual-verify/lib/drift-compare-core.ts",
      "notes": "Reflects 2026-05-21 path A P9 reroute decision. Pickup §3 had originally proposed adding gap to .tvu-input-wrap but discovery showed semantic mismatch (wrap is count container, not Figma input box equivalent). Logged as legal divergence per FIGMA_AS_SOURCE_OF_TRUTH §合法边界 (API form technical unreachable family). verifyHint asserts the verifier honors rootGapMeaningful skip path for HTML void rootTargetSelector (P11 commit threaded target.rootTargetSelector into rootGapMeaningful computation)."
    },
    {
      "id": "dropdown-borderhex-zerowidth-currentcolor-noise-2026-05-22",
      "category": "accepted-divergence",
      "component": "DropDownListSelect",
      "components": ["DropDownListSelect"],
      "subject": "Verifier rootBorderHex false positive when border-width=0 (CSS computed border-color = currentColor)",
      "figmaSide": "Drop down List/Item has no stroke; manifest expected.rootBorderHex === null across all 83 affected variants (Radio/Multi/Operation × theme × state matrix).",
      "codeSide": "src/canonical/DropDownListSelect.vue .dropdown-item CSS declares no border-* properties. Browser default border-width: 0, but per CSS spec computed border-color falls back to currentColor. getComputedStyle(...).borderColor therefore returns the current text color (default text / disabled text / brand active), tracking text color across states.",
      "status": "accepted-documented-divergence",
      "reason": "Not visual drift — borderWidth=0 means no border renders, user sees nothing. CSS spec gap: computed border-color is non-null even when border-width is 0. Same family as input-html-leaf-no-flex-children-2026-05-21 (verifier-side schema gap, not code/Figma divergence).",
      "resolvedAt": "2026-05-22",
      "resolutionRef": "commit 5e6e690e (tests/visual-verify/manifest-verifier.spec.ts line 235): conditional pushSkipped with double-gate `numberFromPx(actual.borderWidth) === 0 && expected.rootBorderHex === null`. Mirrors rootGap pushSkipped precedent (line 227-231).",
      "phase": "P10a",
      "verifyHint": "grep -n 'expected.rootBorderHex === null' tests/visual-verify/lib/drift-compare-core.ts",
      "notes": "Impact: entry FAIL 501 → 473 (-28, remaining dropdown entries still FAIL due to P10b hover-bg / P10c type-aware-color); field A_TRUE_DRIFT_CANDIDATE 1021 → 963 (-58); field B_RESIDUAL_SCHEMA_GAP 94 → 65 (-29). Refs: docs/internal/_prompts/p10a-verifier-borderhex-zerowidth-skip.prompt.md, docs/internal/_plans/p10-dropdown-color-findings-2026-05-22.md. verifyHint asserts the verifier code has the double-gate skip condition for rootBorderHex."
    },
    {
      "id": "dropdown-type-selected-alias-radio-2026-05-25",
      "category": "component-level-translation",
      "component": "DropDownListSelect",
      "components": ["DropDownListSelect"],
      "subject": "Figma Type=Selected ↔ code Type='Radio' alias (Selected 不存在于 code ListType union)",
      "figmaSide": "Figma 'Drop down List/Item' 有第 4 个 Type 变体值 'Selected' (8 entries, 来源 page '— — Input/Select box', Select component popup 选中态显示). 视觉规格与 Radio Checked=Yes 完全一致: text=#2fb54e (--brand), no fill, no stroke, pH=12 pR=8 gap=8. 多 1 个 'Is Parent' axis (cascade 父节点指示) 但无独立视觉效果.",
      "codeSide": "src/canonical/DropDownListSelect.vue ListType union = 'Radio' | 'Multi' | 'Operation List' | 'Sort By' (无 'Selected'). scripts/generate-render-verification-manifest.mjs dropdownTypeForAxes(): if (axes.Type === 'Selected') return 'Radio' — manifest 生成器已 alias Selected → Radio 给 verifier deterministic render.",
      "status": "accepted-documented-divergence",
      "reason": "Selected 视觉 = Radio (实证扫全 8 Selected variants vs 同 Checked Radio variants 0 像素差异). 给 code ListType 加第 5 个 union 值是纯噪音, API 表面积膨胀无视觉收益. Selected 是 Figma 在 Select popup 上下文给同样视觉打的不同 variant 标签, 通过 manifest 生成器 alias 解决符合硬规则 #6 canonical SoT 单源消费.",
      "resolvedAt": "2026-05-25",
      "resolutionRef": "scripts/generate-render-verification-manifest.mjs dropdownTypeForAxes (alias 早已 in-place); P10bc fix in src/canonical/DropDownListSelect.vue codifies the checked-axis brand-text behavior so Selected entries render identical to Radio Checked=Yes per Figma 真源.",
      "phase": "P10c",
      "verifyHint": "grep -n \"axes.Type === 'Selected'\" scripts/generate-render-verification-manifest.mjs",
      "notes": "Figma 'Is Parent' axis 在 Selected 类型中 8 entries 分布: 6 Is Parent=Yes (含 Checked=Yes + Disabled/Hover 组合) + 2 Is Parent=No Checked=No. Is Parent=Yes 与 Is Parent=No 实测视觉无差异 (pH/pR/gap/bg/stroke 全相同), 仅 Checked 轴影响 text color. verifyHint asserts the manifest generator's Selected→Radio alias exists. Refs: docs/internal/_plans/p10-dropdown-color-findings-2026-05-22.md, docs/internal/_prompts/p10bc-dropdown-checked-active-axis-fix.prompt.md."
    },
    {
      "id": "input-select-disabled-text-figma-single-variant-outliers-2026-05-25",
      "category": "resolved",
      "component": "Input",
      "components": ["Input", "InputBoxFilled", "InputBoxLine", "Select", "SelectBoxFilled", "SelectBoxLine"],
      "subject": "Disabled state text color: ~2 Figma single-variant outliers cannot be unified by selector rule",
      "figmaSide": "After plan owner 2026-05-25 full scan of 88 enable=off variants + P13b render-verification, 86/88 follow a consistent token-binding pattern: status=default/empty → --text-disabled (placeholder pseudo); status=normal/Filled + M/L → --text-tips; status=Filled or input-line+normal at size=XL → --text-disabled; status=multi-select → --text-tips; status=normal+filled at size=XL → --text-tips. The remaining 2 variants break the pattern: (a) 'select box/line, status=default, light theme, size=L, feature=time' expects --text-tips (#595959 light) instead of --text-disabled; (b) 'input box/line, status=normal, light theme, size=XL, feature=text count' expects --text-body (#141414 light) instead of --text-disabled.",
      "codeSide": "src/components/Input/Input.vue and src/canonical/SelectBoxBase.vue implement the dominant 86/88 pattern via :placeholder-shown sub-state + .tvu-input--xl override + .tvu-input--xl.tvu-input--filled:disabled:not(:placeholder-shown) narrow override (filled XL disabled retains --text-tips) + .select-trigger:disabled root color (multi-select fallback). The 2 outliers would require single-variant CSS exceptions that pollute the canonical rule.",
      "status": "resolved",
      "reason": "Single-variant Figma drift outliers contradicting the dominant token-binding pattern across 86/88 disabled variants. Adding CSS exceptions for 1-2 entries would obscure the canonical disabled-text rule defended by P13a + P13b CSS layering and increase maintenance burden disproportionate to fidelity gain. Likely Figma-side oversight; flagged for designer review at next library audit.",
      "resolvedAt": "2026-06-04",
      "resolutionRef": "Resolved by owner's Figma disabled-color republish driving CANONICAL-032 (commit 2f4a34d0) + EXTRACT-009 (commit 929a7712): the 2 single-variant outliers were unified to the dominant token in Figma exactly as this entry predicted, so the 3 narrow disabled-text overrides were removed — .tvu-input:disabled now binds --text-disabled for all states. Global render-verification drift A = 0.",
      "phase": "P13b",
      "verifyHint": "grep -nE 'color: var\\(--text-disabled\\)' src/components/Input/Input.vue",
      "notes": "P13a+P13b code layering: .tvu-input:disabled → --text-tips (filled state). .tvu-input:disabled:placeholder-shown → --text-disabled (placeholder pseudo state). .tvu-input--xl:disabled → --text-disabled (textarea disabled, all states). .tvu-input--xl.tvu-input--filled:disabled:not(:placeholder-shown) → --text-tips (input box/filled XL with value, Figma exception). .select-trigger:disabled root → --text-tips (multi-select fallback target). .select-trigger:disabled .select-value{--placeholder} → --text-tips / --text-disabled (single-select). RESOLVED 2026-06-04 (CANONICAL-032 / EXTRACT-009): owner unified the 2 outliers in Figma, so the narrow overrides above were removed and .tvu-input:disabled now binds --text-disabled uniformly; verifyHint now asserts that unified binding (Input.vue:123)."
    },
    {
      "id": "message-size-m-fixed-width-design-state-2026-06-29",
      "category": "accepted-divergence",
      "component": "Message",
      "components": null,
      "subject": "Message size=M frame FIXED 320 (bound to 'Module Width/M' variable) ↔ code responsive max-width",
      "figmaSide": "Message size=M component frame (all 4 statuses) is layoutSizingHorizontal=FIXED at width 320, with the width bound to the 'Module Width/M' FLOAT variable (VariableID:2599:51273, =320). Owner re-published 2026-06-29 to this clean form, dropping an earlier contradictory FIXED-320 + maxWidth-360 (+ briefly minWidth-320) combo. The fixed-width frame is the design-canvas representation of the M size. size=L is unaffected (HUG + maxWidth 480).",
      "codeSide": "src/components/Message/Message.vue .message--M uses max-width: var(--prompt-message-m-max-width) (320px) — responsive: hugs content and shrinks below 320 in narrow containers.",
      "status": "accepted-documented-divergence",
      "reason": "Design-state (fixed-width design frame) vs runtime (responsive max-width), same family as promptmessage-interact-design-state + the figma-*-design-state entries. Runtime max-width is the correct app behavior: a hard 320px Message overflows narrow containers — the documented 248px dropdown content area cannot fit 320 (the historical BM-1047 'Notification/Message 最窄 320 > 248 下拉区不兼容' incompatibility). Both sides resolve to the same 320 value via a width token and coincide at the render-verification 320px harness viewport (render-verification PASS, drift-gate A_TRUE_DRIFT=0), so this is below the deterministic gate's resolution. Owner decision 2026-06-29: keep code responsive.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -nE 'max-width: var\\(--prompt-message-m-max-width\\)' src/components/Message/Message.vue",
      "notes": "Figma width variable = 'Module Width/M' (VariableID:2599:51273, FLOAT, =320). Code token = --prompt-message-m-max-width (320px in src/tokens/variables.css). The extract pipeline records only the resolved width (320) + sizing mode (FIXED) — it does NOT capture node dimension→variable bindings or min/max constraints, so the owner's 2026-06-29 republish (token-bound width, removed min/max) produced no figma-data delta (message raw/tokenized + render-verification-manifest unchanged, still rootWidthMode=FIXED width=320). Possible future enhancement: capture dimension variable bindings + min/max in extract. size=L stays max-width (HUG + 480), which is correctly modeled."
    },
    {
      "id": "logo-menulist-usermenu-code-first-2026-07-02",
      "category": "accepted-divergence",
      "component": null,
      "components": ["Logo", "MenuList", "UserMenu"],
      "subject": "Logo / MenuList / UserMenu dual-framework components (META-02)",
      "figmaSide": "Figma has NO standalone `Logo`, `MenuList`, or `UserMenu` component-set. The equivalents exist only as internal layers of the published `Top bar` component (`Menu List` + `Menu` items; the account area PM avatar / `Account Name`) and as brand icon-registry assets (`icon/logo/TVU`, `icon/logo/ts`). No independent component nodes to 1:1 reconcile against.",
      "codeSide": "src/canonical/{Logo,MenuList,UserMenu}.vue ship into the dual-framework library (WC defineCustomElement + React wrapper via components.config.ts), NOT via src/index.ts (published Vue package untouched). Logo promotes the existing base components/Logo/Logo.vue (renders registry brand SVGs). MenuList extracts the topbar menu pattern: orientation horizontal (brand underline) / vertical (brand color + trailing action/selected checkmark). UserMenu composes a solid-color initials/image avatar into a dropdown panel (account header name+role+email, custom actions as a MenuList body, inline language toggle, sign-out). All tokenized (no hardcoded hex); icons from the registry.",
      "status": "accepted-documented-divergence",
      "reason": "META-02: CLAUDE_DESIGN_RULES §2-3 (Logo is a registry asset not hand-drawn / after-login TopBar rightContent must be UserMenu / Menu groups use MenuList / bilingual = in-UserMenu language toggle) require these as consumable components in the claude.ai/design dual-framework library, but Figma models them only inside TopBar. Owner approved code-first 2026-07-02 (independent scheduling ack). Faithful to Figma where a source exists (Logo assets, TopBar menu pattern); the UserMenu dropdown shell is code-first per the design-rule spec.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -lE \"tvu-logo|tvu-menu-list|tvu-user-menu\" src/web-components/components.config.ts && ls src/canonical/Logo.vue src/canonical/MenuList.vue src/canonical/UserMenu.vue",
      "notes": "Not in render-verification manifest (no Figma nodes) → render-gate-UNVERIFIED, same posture as PillCounter/Breadcrumb; parity guarded by audit:binding-config-parity + smoke tests + the shared generator pipeline. If these are ever promoted to standalone Figma component-sets, reconcile 1:1 and close this divergence."
    },
    {
      "id": "breadcrumb-slash-separator-code-first-2026-07-07",
      "category": "accepted-divergence",
      "component": "BreadcrumbItem",
      "components": ["BreadcrumbItem"],
      "subject": "BreadcrumbItem separatorType='slash' — code-only separator style",
      "figmaSide": "Figma `Breadcrumb/Item` ships ONLY the arrow separator (icon/Arrow/right, node 5004:1478). There is NO slash-separator variant in the published component-set.",
      "codeSide": "src/components/Breadcrumb/BreadcrumbItem.vue (+ canonical passthrough + components.config.ts + generated React wrapper) add separatorType?: 'arrow' | 'slash' (default 'arrow', fully backward-compatible). 'slash' renders a theme-aware '/' divider reusing the .breadcrumb-item__separator box + colour (per-state colouring identical to the arrow). The arrow path is byte-for-byte unchanged.",
      "status": "accepted-documented-divergence",
      "reason": "Owner approved code-first 2026-07-07 (INFRA-F46 live walkthrough): teams want a '/' breadcrumb separator style even though Figma models only the arrow. Additive + opt-in; default preserves the Figma-faithful arrow so existing usage has zero drift.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -n separatorType src/components/Breadcrumb/BreadcrumbItem.vue src/canonical/BreadcrumbItem.vue src/web-components/components.config.ts; grep -n slash playground/docs/pages/BreadcrumbPage.vue react-pilot/src/demos/Breadcrumb.tsx",
      "notes": "Default 'arrow' keeps every existing BreadcrumbItem Figma-faithful; only explicit separatorType='slash' opts into the code-only style. If Figma ever adds a slash variant, reconcile 1:1 and close."
    }
  ]
}
