# PLAN — Coherencia del catálogo de componentes (fase 1: debilidades) > **Kickoff para sesión nueva**: *"Lee docs/process/PLAN-component-coherence.md > y continúa la tanda que toque."* Decisión de usuario (2026-07-02): primero > resolver las debilidades medidas del sistema; la **web de información y de > componentes** (ficha canónica + demos v3 + docs site) es la FASE SIGUIENTE — > por eso las reglas D-* (demos) quedan explícitamente FUERA de esta fase. ## Diagnóstico (auditoría 2026-07-02) `npm run component:audit`: **130 componentes → 75 PASS · 50 NEEDS-WORK · 5 BROKEN** (`cascade` · `gradient-builder` · `menu-dial` · `motion` · `qr-code`). Por dimensión: | Dimensión | Estado medido | |---|---| | Ejes transversales | Paleta-31 (`paletteScaleDecls`) en **7** recipes; size-bundle (`--size-*`) en **1** (pilot toggle); `data-variant` en 55 CSS | | Estados (recipe) | `:focus-visible` falta en **11** (error) · `data-invalid` en 10 · `data-disabled` en 6 · `data-readonly` en 15 (warn) | | Tokens | **17** recipes con tipografía literal (R-2.7) | | Sema | 82 morfos con eventos → solo **41** declaran `expression:` (13 pack · 13 family-default · 1 delegated parseados); `persistence:` en 13; `a11ySemantic` en 9 | | Motion | 15/15 recipes sin `@keyframes` locales ✓; firma de evento (CSS `data-event`) en **10** componentes; prop `motion` en 18 | | A11y ancla | **33** interactivos sin URL APG (A-1.4, warn) | | Docs por componente | **28** sin README (E-2.3); F-1.x (Baseline/Comparativa/Decisiones/Gaps) faltan en ~12–18 más | | Demos (FUERA de fase) | 4 tab-union viejo · 7 sin snippet · 8 sin trace · 12 ▶ play sin wire · 7 chip-parity | ## Reglas de trabajo - Sin agentes/workflows; responder en castellano, código/docs en inglés. - NUNCA tocar `words/`, `palabras/`, `chronos/`, `media-player` (foráneo WIP). - Una tanda = cambios + verificación (`component:audit --only` por componente tocado + `npm run check` scope) + commit. `git reset -q` antes de add. - Sweeps: UN componente primero, verificar, luego el resto (no-cascade rule). - Cada fix sistémico lleva su guard (regla nueva en audit o generador). ## Tandas | Tanda | Contenido | Estado | |---|---|---| | **C0** | **Auditoría de conteos** (hallazgo usuario: "31 escalas" era 33 — UI+docs): guard I1 extendido a palette=33 · roles=9 · slots=12 (extracción sin comentarios); harness PALETTE_GROUPS +fuchsia+steel con assert de completitud; 14 menciones corregidas. **Pendiente**: misma medicina para steps=12 · planos depth=5 · sizes · weights · estilos tipográficos · packs | **HECHA (núcleo) 2026-07-02** | | **C1** | **BROKEN mecánicos → 0 BROKEN**: válvula `R-x.y exception:` en audit (R-1.1/R-1.2/R-1.5/E-2.2 + filas checklist); cascade/motion/gradient-builder `texts.label`+langs; menu-dial default export; READMEs contrato (cascade·motion·menu-dial·qr-code: Baseline/Comparativa/Decisiones/Gaps/Passive+excepciones). cascade·motion·menu-dial → PASS; gradient-builder·qr-code → NEEDS-WORK (resto = D-* fase demos + C8) | **HECHA 2026-07-02** | | **C2** | **Estados error-severity**. **(a) R-1.5 HECHA 2026-07-02**: el audit ahora reconoce las 4 evidencias sancionadas (`:focus-visible` · `:focus-within` · `:has(:focus)` · `[data-focused]` canónico) — añadir rings por recipe habría sido el anti-patrón "double border" (archetypes.css:164 ya da ring universal por arquetipo); 6 excepciones citadas (css-field/number-field → spin-field compartido; date-picker/dialog → foundation+content-exclusion; metrics/timeline → sin parte enfocable); clipboard+announce esperan su README (C8). **(b) R-1.2/1.3/1.4 HECHAS 2026-07-03**: válvula cableada también en R-1.3/R-1.4; fixes REALES verificados en navegador (switch/toggle/rating-group invalid=threat-element como checkbox·pin-input; toggle+pin-input readonly; spin-field readonly NUEVO en la capa compartida; ntp disabled); excepciones citadas (spin-field para css/number-field; dialog provider virtual; color-picker→color-field; date-range-field overlay de attrs; time-picker→time-field). ntp conserva R-1.3/R-1.4 warn para su pasada C8. media-player NO tocado (foráneo) | **HECHA** | | **C3** | **R-2.7 tipografía literal HECHA 2026-07-03** — 16 recipes: 8 font-weights → `var(--font-weight-*)` (verificado resolve=500 en navegador); ~13 proporcionales/identidad anotados `/* literal: */` con razón (avatar ∝ diámetro; `calc(1em − (md−sm))` = label one-step-down con delta token-derived; `1em` identidad de contexto; captions ×0.85; optical −1px) | **HECHA** | | **C4** | **A-1.4 APG HECHA 2026-07-03** — 20 morfos con patrón real (pickers/command/search→combobox · date-field→spinbutton espejo de time-field · rating→radio · nav-menu→disclosure · splitter→windowsplitter · tag-group/tags-input/virtual-grid→grid · file-upload/clipboard→button · announce→alert · form→landmark · image-adjustments→slider · table→table); válvula A-1.4 + notas en 5 READMEs (cropper·pagination·stepper·timeline·image-picker — sin patrón APG real). Restan 4 esperados: card/drag-drop/virtual-list (nota aterriza con su README en C8) + media-player (foráneo) | **HECHA** | | **C5** | **Sema `expression:` HECHA 2026-07-03** — los 30 morfos eventful sin declaración TODOS tienen pack en `sema/components/` → `expression: 'pack'` (clasificación sin ambigüedad; D.4). `morfo:vocabulary` limpio (solo warn naming de media-player, foráneo). `persistence`/`a11ySemantic` adicionales NO añadidos de oficio — la tabla D.9 es referencia y los 6 consumidores reales ya declaran; ampliar solo con caso real | **HECHA** | | **C6** | **Paleta universal HECHA 2026-07-03** — `universalPaletteDecls` en el generador (`normalizeRecipeTokens`): `palette-{slot}` = vocabulario reservado → cascade de PALETTE_SCALES completo automático (declaraciones de autor ganan; solo se añaden escalas ausentes). Spreads manuales de button retirados (base.ts); button regenera byte-idéntico; **toggle se universalizó solo** (+330 líneas generadas — ya exponía tokens palette-*). Lint: valores de escala en `data-color` = eidos-only sancionado (la enum del morfo sigue siendo los roles). Guard: test en recipe-css-contract fija la ÚLTIMA escala de la const. Verificado en navegador: toggle color=teal/steel resuelven | **HECHA** | | **C7** | **Size-bundle sweep HECHA 2026-07-03** — 366 refs de coordenada cruda (`--control-height-{k}` · `--font-size-{k}` · `--icon-size-{k}`) → coordenada del bundle (`--size-{k}-{coord}`) en base.ts; 34 recipes consumen el bundle (era 1, pilot toggle), 0 crudos restantes. El bundle = alias puro → CSS generado idéntico en VALOR computado (verificado en navegador: cadena `--accordion-trigger-min-height-md` = `--size-md-control-height` = `--control-height-md` = `calc(36px*1*1)`). Guard: test en recipe-css-contract prohíbe la coordenada cruda en recipes. `base`/`xxxl` sin bundle → siguen en el primitivo tipográfico | **HECHA** | | **C8** | **READMEs** — 28 nuevos + F-1.x incompletos (~18). Baseline/Comparativa (≥3 refs reales)/Decisiones/Gaps; passive → justificación. El más pesado: requiere investigación por componente | pendiente | | **C9** | **Motion eventful gap** — decidir qué interactivos deben expresar firma de evento (hoy 10) y cablearla; criterio: alta frecuencia = sobrio (doctrina D.5) | pendiente | | **C10** | **Tiers de madurez** — `stable · preview · experimental` en índice de componentes + criterio (PASS+README = stable) | pendiente | ## Tandas S — hallazgos del ejercicio Knob (STUMBLES.md, 2026-07-03) El usuario encargó a otro agente construir un Knob SOLO desde los docs; su STUMBLES.md mide la construibilidad — dimensión que las auditorías de conformidad no cubrían. Ya corregido en el acto (2026-07-03): #2 drift `kind` (la enum real es public|private|virtual; morfo.md y checklist mentían distinto + guard I2 fantasma `'internal'`; fila A-2.1 alineada con lo que el script chequea — doctrina Toggle), #6 forma del catálogo langs (snippet en morfo.md), #8 paquete mínimo (lista de archivos en component-audit §0). | Tanda | Contenido | Estado | |---|---|---| | **S1** | `npm run docs:vocabularies` — apéndice GENERADO desde código en el docs-book (archetypes con one-liner, families+holds, haptic kinds, commonLangs, escalas, sizes, variants). Generado = sin objeción de drift (stumble #1) | pendiente | | **S2** | Doctrina "Continuous components" en architecture/sema.md — trigger() vs emitEvent en gestos, retención de turno con sequence:'post', throttling de commit-set en key-repeat; documentar la resolución REAL de Slider (stumble #4) | pendiente | | **S3** | Condición `part-absent` en morfo (aria-label solo sin Label) — schema + compiler + runtime (stumble #5) | pendiente | | **S4** | `Gesture.rotate` (centro + sweep + wrap + detents + velocidad angular) — consumidores: Knob, AngleSlider, hue ring, esfera de reloj; hoy FLAGGED GAP en el provider del Knob (stumble #3) | pendiente | | **S5** | Contrato de CSS custom properties soma→eidos (`--x-progress`/`--x-angle`) — extensión del morfo, pasa 2-de-3 (soma escribe, eidos consume); hoy solo prosa en soma-architecture §9 (stumble #7) | pendiente | | **S6** | Fricciones menores (#9): state() vs $state (cuándo cada uno) · role en Provider-con-DOM · Without<>/PrimitiveDivAttributes definidos · ownership de pointermove/up en gesture layers — notas en soma docs | pendiente | FASE SIGUIENTE (no aquí): ficha canónica generada por componente, demos v3 (D-*), web de docs. Ver conversación 2026-07-02. ## Hallazgo (fuera de fase, registrado 2026-07-03) La suite eidos tiene **15 fallos preexistentes** en 6 archivos (verificado contra HEAD sin C6): create-icon (1) · component-api-contract (3) · recipe-css-contract (3: "declares every public component variable" · "raw colors" · "orphaned") · lint (2: morfo-file coverage · dialog drift-detector) · active-eidos-config (4) · active-eidos (2). Necesita su propia tanda de triage — NO se tocó aquí salvo el título stale "31 scales"→33 (el cuerpo ya afirmaba 33; su fallo es otra causa). ## Verificación de cierre de fase `npm run component:audit` → 0 BROKEN, NEEDS-WORK solo por reglas D-* (demos, fase siguiente); paleta y size-bundle universales con guard activo.