{
  "$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",
      "figmaSide": "No corresponding Figma component set has been found yet.",
      "codeSide": "Alert exists in code but its source is pending designer confirmation.",
      "status": null,
      "reason": "Until source is confirmed, Alert cannot enter audit scope.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": null,
      "notes": "Designer options remain in divergences.md."
    },
    {
      "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": "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 PromptMessage.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", "PromptMessage"],
      "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": "PromptMessage",
      "components": null,
      "subject": "PromptMessage.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", "PromptMessage", "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": "Library does not yet have Pill/Status nor Pill/Counter components; current usage is file-local masters in product mockup files (e.g. MicroApps DtZcMkhNy6qh6jbQQnhreQ has `MicroApp State Pill` + `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. PillStatus variants: Inactive/Preview/Live/Analyzing. PillCounter variants: success/warning/info/neutral. Uses --msg-bg-* tokens from msg-bg-tinted-dark-set-2026-05-19.",
      "status": "accepted-documented-divergence",
      "reason": "MicroApps mockup work needed dark-mode status pill; user approved code-first 2026-05-19 to unblock跨产品复用 instead of waiting for library promotion.",
      "resolvedAt": null,
      "resolutionRef": null,
      "phase": null,
      "verifyHint": "grep -lrE 'PillStatus|PillCounter' src/canonical src/index.ts",
      "notes": "When designer promotes file-local Pill components into the published library, code wrappers' data-figma-component attrs ('Pill/Status' / 'Pill/Counter') match. No docs site page yet (deferred to Figma library availability)."
    }
  ]
}
