| Morfo `date-picker` + `date-range-picker`: declarar parts Footer/Clear/Cancel/Close + arquetipo `footer` añadido al vocabulario | **hecho** |
| `MonthRangePicker` / `YearRangePicker` como componentes nuevos | **descartar** — cubierto por `<DateRangePicker kind='month'>` / `kind='year'`. No vale la pena duplicar superficie cuando un prop captura la variante. |
| Promover `<DatePicker.YearView>` / `<DatePicker.MonthView>` a componentes independientes `<YearCalendar>` / `<MonthCalendar>` con demos aislados | **diferir** — actualmente acoplados al `DatePickerProvider`. Independizarlos requiere refactor del context-lookup. Mantener acoplados hasta que aparezca un caso de uso real fuera del picker. |
| Propagar el patrón modal+Footer a `time-picker`, `time-range-picker`, `color-picker`, `combobox`, `select`, `popover` cuando proceda | **implementar** — decidir por componente; muchos no necesitan footer. |
| Tests browser del flujo completo (Playwright): inline/modal, kind chip, range state machine, snapshot/cancel, click-outside, Escape | **implementar** — alta cobertura para flujos que ahora dependen de state machine. |
| Sema event al bloquear click cuando `mode='modal'` | **diferir** — útil para screen reader feedback. No bloquea. |
## Soma / Popover — bloqueo detectado durante Words
| Item | Disposición |
| --- | --- |
| Auditar `Popover` como componente propio antes de usarlo para `Words.ToolPopover` | **implementar** — el trigger emite `present` en la traza, pero el estado `open` no queda reflejado de forma fiable en DOM y `Content` puede no montarse. En la demo de `Words`, abrir el editor de link desde el `BubbleMenu` produce `TypeError: Cannot read properties of undefined (reading 'wrapperProps')` en `src/uix/soma/components/popover/popover-provider.svelte.ts`. No es una adaptación específica de `Words`; es deuda de `Soma/Popover` y debe resolverse con su propia demo, tests y audit visual. |
| Revisar `Portal` sólo si la auditoría de `Popover` lo confirma | **diferir** — no modificar `internal/Portal` como efecto lateral de `Words`; aislar primero si el fallo está en trigger/provider/presence/portal. |
| Añadir test browser o harness Svelte para click real de `Popover.Trigger` → `Content` visible | **implementar** — los tests unitarios del provider no cubren el flujo renderizado con `Trigger`, `Portal`, `Content`, `bind:open` y atributos morfo en DOM. |
| 20 tokens de color muertos (rol `tertiary` completo + `{intent}-active`× 8 + `fulfill-hover`/`loss-hover`/`loss-element`) en `themes/base.ts` + `generated/base.css` | **diferir** — no rompen nada. Validar que `tertiary` no es un hook futuro reservado antes de borrarlo. |
| Afinar valores de los tokens xs/xl extrapolados durante el batch de tamaños | **diferir** — la extrapolación proporcional (xs ≈ 75% de sm, xl ≈ 125% de lg) puede no proporcionarse bien en componentes concretos; revisar visualmente y reajustar caso por caso. |
| Range view-cell: differentiate visualmente start/end vs in-range (actualmente todos se ven solid primary) — pendiente afinar tinte de `data-in-range` para que sea más sutil que start/end | **implementar** cuando se haga el browser-walk del range-picker. |
| Audit A-3.6 falla con eventos `open`/`close` válidos de `emerge` | **diferir** — el regex pide `{verb}-X` pero muchos verbs son legítimos solos. Refinar regex para aceptar verbs canónicos. |
| Audit no verifica que el demo cierre el popover al hacer click-outside (cuando aplica) | **diferir** — verificación visual queda al browser-walk. |
| Translations check: añadir warning cuando un componente declara `texts.label` pero su catálogo no tiene `label` | **implementar** — pequeño, evita drift. |
| **Audit R-2.6** — `var(--color-X)` referenciado sin estar declarado en `generated/base.css` | **hecho** (`scripts/component-audit.ts`). Set declarado se calcula al inicio desde `generated/base.css`; cada componente CSS se chequea contra él. |
| **Audit D-7.4** — el array de chips de cada control (size, variant, color) enumera el tipo completo declarado en `types.ts` | **hecho** (`scripts/component-audit.ts`). Parsea `{PascalName}{Prop}` exacto, resuelve alias canónicos (`ControlVariant`, `SelectionVariant`, `ChipVariant`, `MarkerVariant`, `TabsVariant`, `ColorRole`+narrowings) vía `SHARED_VARIANT_VOCAB`. |
| Nuevo: audit que verifique que ninguna Part Eidos consulta `provider.opts.{boolean}Button.current` (composición pura) | **diferir** — chequeo defensivo, low value. |
Diagnóstico (2026-05-24): el usuario reporta carga lenta + Chrome marca
`[Violation] 'setTimeout' handler took 78ms`. Profiling identifica 4
cuellos de botella reales, ordenados por impacto. Como UIX está en fase
de desarrollo del framework, todos quedan como **diferir** hasta entrar
en pasada de polish; documentados aquí para no perder el rastro.
| Item | Disposición |
| --- | --- |
| **CSS layout 744 KB render-blocking**: `src/uix/eidos/index.css``@import`-ea 70+ recipes + `generated/base.css` (213 KB) — total 944 KB. Cada `/uix/*` descarga + parsea todo antes del first paint, la mayoría para componentes que la página no usa. | **diferir** — fix arquitectónico: dejar foundation global (~250 KB: `base.css` + `archetypes.css` + `events.css`) y mover cada recipe `XComponent.css` a un `import` dentro del `.svelte` correspondiente para que Vite lo code-splittee con el JS. Esto requiere migrar la convención del foundation eidos. |
| **`ActiveEidos.create({ applyDom: true })` re-genera todo en runtime**: `apply()` (`src/uix/eidos/active-eidos.svelte.ts:361-389`) corre `renderStaticCss()` + `renderThemeCss()` sincrónicamente, itera `THEME_BASE_RECIPE_TOKENS` (2.884 líneas) + color×scale×role×alpha (>1k declaraciones), inyecta 3 `<style>` tags con `textContent = css`. Es la causa más probable de la violación `setTimeout 78ms` (el `$effect` envolvente se programa vía la cola que Chrome reporta como setTimeout). Además **duplica** el CSS bundleado vía `import '@/uix/eidos/index.css'`. | **diferir + investigar trade-off** — fix conservador (1 línea): `applyDom: false` en `web/routes/uix/+layout@.svelte:302` y depender del CSS bundleado. Pero hay que confirmar primero si runtime theme switching depende de la inyección (theme editors live). Si depende, la solución es hacer `apply()` lazy: cachear el render por `themeId` y solo inyectar overrides cuando `setCssVariables()` se llame. |
| **28 sema modules + 71 lang files eagerly imported en el layout**: `web/routes/uix/+layout@.svelte:10-38` (sema) + `src/uix/langs/components/index.ts` (langs). Añade ~30 KB al chunk del layout, bloquea tree-shaking, cada `/uix/*` registra todos aunque el usuario no abra esos componentes. | **diferir** — fix arquitectónico: registración lazy por componente. Cada componente al montar registra su propio sema/langs pack en lugar de el central en boot. Requiere API nueva (`uix.events.registerPack`/`uix.langs.lazy`) + refactor del patrón de boot. |
| **Sidebar con 100+ links `data-sveltekit-preload-{code,data}="hover"`**: `web/routes/uix/+layout@.svelte:746-769`. Mover el cursor por el rail dispara fetches masivos. Las rutas docs son prerenderizadas — el `preload-data` no aporta nada. | **diferir** — fix barato (5 líneas): quitar `data-sveltekit-preload-data` de los links del rail; cambiar `data-sveltekit-preload-code="hover"` a `viewport` para que precargue al entrar en pantalla, no al hover. Mantener `hover` en topbar/brand. |
**Cuándo abordarlo**: cuando la lib entre en pasada de optimización
pre-release. El orden de ataque sugerido es el inverso del coste:
quitar `preload-data` del rail (10 minutos, ganancia inmediata) → fijar
`applyDom: false` con cache lazy (medio día, gana 50-80ms) → split CSS
por componente (1-2 días, gana el grueso del transfer) → sema/langs
## Kim audit Round 3 — deuda arquitectónica restante
Cerrados de Round 3 (en commits previos esta sesión): §2 context.ts
revert (`d4e668b2`), item 28 `defaultCalculateStrength` referencia
fantasma. Lo siguiente queda **diferir** hasta sprint de refactor.
### §3 · Duplicación de código (sin contracto que la detecte)
| Item | Disposición |
| --- | --- |
| **FloatingShellProvider factory** — el setup `FloatingProvider.create + Presence + FloatingContent.create` está copia-pegado en 15 providers (popover, dropdown-menu, combobox, select, tooltip, context-menu, 5 pickers, link-preview, menubar). Cada uno repite ~40-60 líneas. Total: ~600 líneas duplicadas (18 matches confirmados via grep). | **diferir** — refactor grande de alto valor pero alto riesgo (15 archivos críticos). Sprint propio. |
| **BaseSegmentProvider** (date-field 1674 líneas, time-field 1072 líneas). Ambos tienen `BaseNumericSegmentProvider` / `BaseTimeNumericSegmentProvider` con `updateSegment`, `handleNumberKey`, `handleBackspace`, `handleArrowUp/Down`, `onfocusout` y `SEGMENT_CONFIGS` estructuralmente idénticos. Plus `DayPeriod`, `Literal`, `TimeZone` casi byte-idénticas. ~300 líneas duplicadas. | **diferir** — extraer base abstracta genérica sobre `<TProvider, TPart>`. Las firmas divergen un poco (`formatValue` con/sin `forDisplay`, `getAnnouncement` en date no en time) — reconciliarlas requiere decisión cuidadosa. |
| **ListSelectionHelper** (combobox 1091 líneas + select). `selectItem` con modo single/multi, `allowDeselect`, `handleClose`, trigger de eventos ~80 líneas duplicadas. Combobox sólo añade `inputValue` sync. | **diferir** — abstracción de tamaño medio, valor visible. |
| **`DateFieldProvider.updateSegment`** — método monolítico, 96 líneas, 7+ ramas if. | **diferir** — partir en helpers por segment kind. |
| **24 imports directos de `../field/field-provider.svelte`** (en realidad 37 de 42 cross-component soma imports). No es violación de capa (soma→soma) sino inconsistencia: el patrón canónico es importar la clase desde el barrel del componente vía `internals.ts`. | **diferir** — cosmético, alto coste (tocar 37 archivos) sin cambio behavioral. |
| **`$effect` redundantes** en date-field, time-field, drawer, command — el audit sugiere mergear cascadas reactivas que comparten dependencias. | **diferir** — caso por caso, lectura cuidadosa requerida. |
### §6 · Greenfield guardrails on brownfield code
| Item | Disposición |
| --- | --- |
| Los tests de contrato (`contracts.test.ts`, `recipe-css-contract.test.ts`, `component-api-contract.test.ts`) fuerzan estado final prístino mientras el código sigue migrándose. Actualmente 6 fallos remanentes son todos Words/cookie-infra. Cuando aparezca un componente transicional en estado intermedio, los tests bloquean. | **diferir** — tres opciones a evaluar cuando aplique: (A) lista `EXEMPTIONS` con fechas objetivo, (B) acelerar migración, (C) split tests en "core infra" (debe pasar) vs "housekeeping" (failures conocidos trackeados). |
### §7 P4 · Items menores no abordados
| Item | Disposición |
| --- | --- |
| **Item 27 · `data-last-action` vocabulario divergente** — dialog/drawer usan `'saved'`, pickers usan `'committed'` / `'range-committed'`. CSS de salida en `dialog.css` + `drawer.css` selecciona sobre `[data-last-action='saved']`. Cambiarlo rompe esas reglas. | **diferir + decisión** — los dos nombres son semánticamente distintos (form-persist vs value-commit). Si se unifica, el side seleccionado debe documentarse como canónico y propagarse a las 4-6 morfos + 2-4 CSS afectadas. |
| **Item 29 · `SEMA_VERBS` expansion** — el audit sugiere que la lista actual de verbos canónicos no cubre toda la superficie de morfos existente. Requiere auditoría de verbos usados vs declarados. | **diferir** — investigación previa necesaria para concretar scope. |
| READMEs/architecture docs siguen citando `morfo.translations` en muchos `.md` | **diferir** — el código está migrado a `texts`, los docs lagging. Sweep separado. |