You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/process/continue-audit-fixes-2026-0...

28 KiB

Continue — Auditoría de componentes + fase de fixes (P0–P7)

⛔ PROGRAMA COMPLETO (2026-07-11) — este handoff queda CERRADO como histórico. Matriz 134/0/0 · contracts 37/37. Cierre formal: docs/audit/components/_cierre.md. La continuación viva es continue-cleanroom-fixes-2026-07.md (los P1 de la auditoría clean-room). No arrancar sesiones desde aquí.

Handoff. Fecha: 2026-07-08 · actualizado 2026-07-10 (S4 lote 1 ✅). Rama alpha-0.1-sec-dom.

S4 COMPLETO (2026-07-10) → el audit-fix P0–P7 está CERRADO. E-2.3 = 0 en el catálogo (29 dossiers eidos + README soma field-langs; máquina 81→102 PASS; baseline 57). Los NEEDS-WORK restantes son flags no-README documentados en los ## Gaps de cada dossier (S9, demos D-*, censos R-1.x). Pendiente de decisión del usuario: (a) los one-liners recopilados abajo; (b) ¿"lote 6" opcional? — 13 READMEs ANTIGUOS con F-1.x en warn (color-swatch, float-panel, format-date/number, gradient-*·metrics [son S9], onion-menu, radio-cards, relative-time, scroll-frames, timeline, trans) que existen pero no tienen forma dossier; NO estaban en el plan S4 (solo cubría los ausentes). Después: S9 en sesiones dedicadas + reconciliación touch-rows↔§37. P5 y P6 están CERRADOS: P5 = S1·S5·depth·touch·S6·S8 ✅ (S6 53b6f629..d1670caa; S8 83e13be6). P6 = C6 3b02fd11 · C7 444e05eb · B.11 0f0feb4f ✅. Pende tu decisión touch-rows↔§37. Follow-ups no bloqueantes abajo.

Documento de continuación para retomar la fase de fixes de la re-auditoría de componentes. La fuente de verdad de los veredictos es docs/audit/components/_veredictos.md (bloques N1–N10 · S1–S11 · C1–C8 + fixes obvios). Los censos de apoyo: _naming.md y _system.md. Las 91 fichas pilotadas viven en docs/audit/components/{kebab}.md.

Estado por paquete

Paquete Estado
P0 doctrina (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline) ✅
P1 guardas (vocab-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos lint classHooks) ✅
P2 contratos morfo (role=application×4, aria-selected fuera del Day, drp translationRef, field data-state, pin-input commit-set, media-player renames) ✅
P3 packs sema (13 packs nuevos + 12 morfos family-default→'pack') ✅
P4 naming/API N1–N10 ✅ COMPLETO
P5 sistema/eidos ✅ CERRADO — S1 ✅ · S5 ✅ · depth ✅ · touch-rows ✅ · S6 ✅ (composición + poda + de-alias) · S8 ✅ (--calendar-range-* API pública 83e13be6) · (touch↔§37 pende decisión)
P6 C7 textarea-measure + D13 · C6 weekInfo · B.11 splitter ✅ CERRADO — C6 3b02fd11 · C7 444e05eb · B.11 0f0feb4f
P7 S9 iniciativas · S4 READMEs · S7 papeleo · errores de tipo baseline ✅ S4 COMPLETO 2026-07-10 (baseline ✅ · S7 ✅ · S4 29+1 dossiers ✅, E-2.3=0, máquina 102 PASS) — S9 queda como sesiones dedicadas POST-audit-fix

✅ Reconciliación touch-rows ↔ §37 — CERRADA (decisión usuario 2026-07-10)

Opción (a) con nota: el ::before hit-slop queda CONFIRMADO como mecanismo para markers desnudos (linaje Material mdc-touch-target / Android touch-delegates / iOS hit-test insets). El argumento WCAG-solape del §37 original se resuelve con (1) requisito de spacing en pilas táctiles densas (pitch ≥ --touch-target; AA garantizado en el peor caso por la excepción Spacing) y (2) la fila-label de Field sigue viva como mejora ADITIVA — la fila será el target AAA real cuando exista (2.5.5 Equivalent) y el pseudo queda de red. Redactado en changelog.md §37 (reconciliación) + reference.md §37 (standing "confirmed 2026-07-10").

P4 — hecho este bloque (N1–N10)

  • N1 selectionMode: select, combobox, toggle-group, calendar (unión discriminada; data-type como atributo se conserva — contrato eidos; solo prop
    • opts key → selectionMode). date-picker interno migrado; range-calendar N/A. file-upload conserva multiple (espejo nativo).
  • N2 onValueCommit (norma única callback terminal): pin-input onComplete→, search/password onSubmit→, date/time/color-field añaden el callback disparado en commit-set (solo al completar, no al limpiar), textarea onSubmit→ (hueco de censo cazado). 5 sitios internos de picker/rango pasan undefined.
  • N3 carousel value→index/onIndexChange. N5 deselectable (calendar migra preventDeselect con inversión de polaridad). N6 tooltip openDelay/closeDelay, groupSkipDelay. N7 combobox allowCustomValue. N8 poda defaultValue (editable/select/grid-list; knob conserva). N4/N10 = veredictos que ratifican el statu quo (sin código).
  • N9 razón tipada + onInvalid: tags-input migra validate-boolean→mensaje + onValueInvalid→onInvalid(reason, value, msg?) con 'duplicate'|'custom'. Verificado que date/time/ranges YA cumplían (razones tipadas via DateOnInvalid/TimeOnInvalid = 'min'|'max'|'custom'). css-field nivelado (decisión del usuario): onInvalid(reason, value, msg?) con 'parse'|'custom', reenviando el message que ya computaba. (file-upload N/A — FileRejectionReason[] ya tipado; words excluido.)

P5 — hecho este bloque

  • S1 codemod 22 class-hooks → [data-{c}-{part}] (plantilla + CSS): cropper 8 (+ estado .is-zoomed→data-zoomed), button 6 (+ :is() simplificado), image-picker 6, toggle 1, image-adjustments 1. eidos-lint: class-hooks 0 / invalid 0 en los 5.
  • S5 outline: eliminado el remanente box-shadow (transición muerta) de field.css; el resto del catálogo ya canonizado (comentarios post-migración).
  • depth pass: data-depth="overlay" en tooltip-content, toast-item, float-panel-content. No-destructivo (probado: la regla propia de cada overlay gana la cascada por orden de fuente; el plano solo aporta identidad frost/Cristal
    • forced-colors). banner fuera (decisión del usuario — es strip inline).
  • touch-rows: token --touch-target: 44px (:root en archetypes.css). Botones tokenizados. checkbox/switch: ::before transparente max(100%, var(--touch-target)) gated a coarse → crece el ÁREA, no el visual (modelo React Aria / Material mdc-touch-target; suelo único 44 AAA). radio-group row tokenizado.

Decisiones de método relevantes (para no repetir errores)

  • depth: la disyuntiva "migrar-al-plano vs quitar-shadow" partía de una premisa falsa (el "doble shadow" — box-shadow NO es aditivo). El fix correcto era el simple: solo añadir data-depth; la regla propia gana. Verificado en vivo.
  • touch-rows: NO anclar en "44px" sin ver que (a) área ≠ visual son ejes distintos, y (b) el visual depende de size/fuente. Se comparó con 5 frameworks (React Aria, Radix, Material/MUI, Ark, Base/Bootstrap/shadcn): solo React Aria y Material desacoplan área/visual; nadie hace split AA/AAA por talla (el análogo MUI se considera bug). → suelo único 44 AAA sobre el ÁREA.

P5 — S6 Field composition (HECHO 2026-07-08→09, pusheado)

Doctrina del usuario: todos los fields deben SER fields (componen field.css), y ningún field concreto declara tokens que aliasen los de Field. Commits 5eb92132..d1670caa (12). Detalle en memoria: compose-base-field-not-replicate, no-token-aliases, recipe-orphan-test-prune.

  • 16 fields componen field.css — cuatro cardinalidades descubiertas y documentadas:
    1. multi-field (date/time-range-field): montan un <DateField>/<TimeField> real por endpoint (soma), tamaño/variant/color via visual context.
    2. single-field picker (date/time-picker): el root ES el field (data-field + data-{c}-field), chrome de field.css.
    3. single-control text (search/mask/number/css): colapsan fieldRole('provider')
      • fieldRole('control') en la única caja; field.css [data-field-control] fija flex-direction: row (evita que la columna del provider apile los afijos).
    4. field con contenido debajo (password: strength meter): estructura Field canónica completa con una parte <Control> real + el meter como hermano del stack.
  • Poda de recipe-tokens (a820e5f3 + d1670caa, 199 tokens): mover el chrome a field.css dejó los --{c}-field-height/padding/border/bg/font/... huérfanos (emitidos sin consumidor). El guard recipe-css-contract.test.ts "orphaned" los caza (167 en la 1ª pasada + 32 tras de-aliasar los pickers). mask-field quedó sin tokens propios → entrada de recipe eliminada.
  • De-alias de pickers (d1670caa): date-range/time/time-range-picker aliasaban --date-field-*/--time-field-* para su parte "field-shaped" → retargeteados a los tokens públicos de Field (--field-control-*/--field-*/--field-segment-height) o al primitivo donde Field no tiene equivalente (wrap column-gap = segment gap). Value-preserving (mismo primitivo tras un hop), verificado con revisión adversarial (workflow, 4 agentes) + navegador en los 4 pickers.

Lecciones de método (S6) — no repetir

  • El baseline svelte-check NO corre vitest. Tras cualquier refactor de recipe/CSS, corre además npx vitest run src/uix/eidos/recipe-css-contract.test.ts (25 tests: huérfanos, phantom, "todo var resuelve", scope-contract). La regresión de 167 huérfanos fue invisible al baseline durante toda la composición S6.
  • --field-segment-height solo existe bajo [data-field][data-size]. Un elemento compuesto SIN [data-size] cae al default [data-field] = md, fijado. Antes de cambiar un calc(--control-height - space-2) por --field-segment-height, verifica en el DOM (xs/sm/lg) que el elemento consumidor lleva [data-size]. La revisión adversarial cazó exactamente esto en time-range-picker.

P5 — CERRADO ✅ (S8 hecho 2026-07-09, 83e13be6)

  • S8 calendar-surface: 8 tokens públicos --calendar-range-{start,end}-* en el recipe calendar (API de la capa); range-calendar los consume por el nombre público; drp deja de tunear los --_calendar-range-* privados. Value-preserving (25/25, baseline 59, eidos-lint 0, navegador idéntico en 4 colores + drp popover). Asimetría deliberada documentada: start desde :root (tema-overrideable), end atado al accent en el host. Detalle en memoria recipe-orphan-test-prune / compose-base-field-not-replicate (mismo patrón anti-alias que el de-alias de pickers).

P6 — CERRADO ✅ (2026-07-09→10)

  • C6 (3b02fd11): getWeekInfo(locale) público en $libs/days (convención Intl 1=lun..7=dom; firstDay espeja el getWeekStart interno; weekend de Intl con fallback sáb/dom documentado, caché por locale). Calendar + range-calendar derivan weekend del locale (antes: hardcoded sáb/dom con 'en-US' — mal para ar-SA). Verificado en vivo (ar-SA → vie/sáb). Hallazgo extra arreglado: getSimpleWeekNumber mezclaba marcos (weekStartsOn absoluto + locale-relativo) → semanas 'simple' desplazadas −1 en es-ES; ancla en marco en-US fijo + test de regresión.
  • C7 (444e05eb): textarea measureAutosize difiere la lectura vía dom.measure del contexto (antes: reflow forzado POR TECLA). Censo D13: violaciones arregladas en scroll-area (getters del thumb leían clientHeight en $derived → reflow por tick; ahora track medido por RO en $state), virtual-list (×2 viewports), virtual-grid, carousel (seed diferido + fallback vivo en render eliminado). Conformes sin cambio: float-panel, cropper, splitter (lecturas event-time antes de write — D13 es regla de timing). Suites actualizadas al contrato diferido.
  • B.11 (0f0feb4f): par bindable del splitter — sizes (bindable, % por orden de registro: seed + sync + escritura externa clampada) + onResize→onSizesChange (norma X+onXChange; dos nombres = alias). onResizeEnd se queda (momento commit). Verificado en vivo en ambas direcciones.
  • Gotcha de entorno registrado (memoria hidden-pane-suspends-raf): el Browser pane oculto (visibilityState: hidden) suspende rAF + entrega de RO → sondas de medición "muertas", screenshots en timeout. Chequear la vitalidad de rAF ANTES de diagnosticar; verificar vía suite de navegador de vitest en su lugar. La sonda reflowDetector quedó pendiente por esto (opcional, no bloqueante).

P7 — estado (2026-07-10)

  • Baseline ✅ (c0ae3de1): editable + grid-list arreglados (tipos-solo, cast sobre renderProps) → protocolo ahora 57. Quedan 9 src/ + ~48 web/ fuera de mandato (inventario arriba en el protocolo).
  • S7 papeleo ✅ (fe844c62): doctrina §5 + clasificador + F-1.5 ya existían (P0/P1); añadidos los 2 artefactos que faltaban — ## Passive justification en color-swatch (F-1.5 verificado verde en la máquina) + README de capa de spin-field. radio-cards = interactive para la máquina (F-1.5 N/A; su cita canónica es el header del morfo, verificada).
  • S4 READMEs ◐ — decisión del usuario (2026-07-10): dossier completo, por lotes de ~6/sesión, máquina en verde por lote. Forma real (hallazgo): F-1.x lee el README de eidos como dossier de embarque (## Baseline / ## Comparativa ≥3 refs / ## Decisiones / ## Gaps con disposiciones) + puerta del consumidor (plantilla date-field); el grandfather.json existe para que estos dossiers se escriban en P7. Gran parte = trasplante ficha→README (las 91 fichas ya tienen Baseline/Comparativa). Lotes propuestos (28 + field-langs):
    1. ✅ forms (2026-07-10, 7b82aa79): button · password-field · time-range-field · textarea · knob (READMEs eidos) + soma field-langs (contrato headless). Máquina: 81→85 PASS; los 5 E-2.3 limpios; F-1.x verde en los 6; baseline 57. Método fijado: exceptions R-1.x exception: SOLO con evidencia leída (pf R-1.3/R-1.4 → field.css; trf R-1.5/R-1.3/R-1.4 → endpoints TimeField reales, espejo drf) + gaps de ficha aún vivos trasladados a ## Gaps con disposición. button sigue NEEDS-WORK solo por D-1.5 (regla de DEMO, fuera del alcance). One-liners detectados y NO ejecutados (decisión pendiente): apg de trf (spinbutton) y apg none-con-razón de field-langs (su A-1.4 exception en README ya no se honra tras C5); README soma de button drifted (pending/pressed vs types reales) + docblocks F-2/F-3 de la ficha.
    2. ✅ pickers/nav (2026-07-10): range-calendar · natural-time-picker · menubar · navigation-menu · command · listbox. Máquina 85→90 PASS; F-1.x verde en los 6; baseline 57. Verificado pre-escritura el estado post-fixes (los 6 con hallazgos de ficha YA resueltos por P2/P3/S3b/S8: packs de range-calendar y listbox, aria-selected, role application, apg de ntp). Hallazgo nuevo con evidencia (README de range-calendar, Gaps): las marcas holiday/event siguen MUERTAS en range-calendar — sus 2 tokens (--calendar-day-holiday-shadow, --calendar-event-shadow) se emiten bajo [data-calendar] en generated/base.css y no resuelven bajo [data-range-calendar] (el resto del préstamo va en :root y sí). ntp sigue NEEDS-WORK por su paquete S9 "ntp integral" (A-3.1 del Picker genérico sin eventos · R-2.1 los 9 cielos sin anotar · README soma + tests) — todo trasladado a su ## Gaps con disposición; one-liner expression: 'delegated' en ntp pendiente de decisión (diría la verdad y silencia A-3.1). R-1.3 readonly de range-calendar/listbox = gap real (no pinta en ningún sitio) → censo, SIN exception.
    3. ✅ data (2026-07-10): table · tree-view · tree-grid · grid-list · feed · virtual-list · virtual-grid. Máquina 90→95 PASS; F-1.x verde en los 7; baseline 57. Los 7 tenían soma README ✓ y suite ✓; varios hallazgos de ficha ya resueltos post-audit y documentados en los dossiers (table pack S3b + sort reclasificado a commit.set; apg de virtual-list en forma C5 "none — rationale"; D13 de virtual-* vía dom.measure, C7; error de tipos de grid-list en baseline c0ae3de1). virtual-list/virtual-grid quedan NEEDS-WORK SOLO por D-1.5 (regla de DEMO, fuera del alcance — mismo caso que button). Hallazgo nuevo (Gaps de tree-grid): asimetría de naming entre gemelos — tree-grid usa value+expanded, tree-view usa selectedValue+expandedValue (N4 solo ratificó el segundo; ambos multi-eje) → decisión de espejo pendiente. R-1.3 readonly de tree-grid/grid-list = gap real → censo, SIN exception.
    4. ✅ typography/display (2026-07-10): s-text · s-text-virtual-list · badge · card · image · skeleton · spinner. Máquina 95→99 PASS; F-1.x (+ F-1.5 en los 4 pasivos: s-text, image, skeleton, spinner) verde en los 7; baseline 57. Quedan NEEDS-WORK con F-1.x verde y el resto en Gaps: badge (R-4.3 hover del Remove con color-mix hand-rolled → censo state-layer; R-4.3 NO honra exceptions de README), s-text-virtual-list (R-1.5 = ARTEFACTO del archetype: 'item' en Row — display de 0 eventos scope: ['eidos']; one-liner de morfo pendiente que lo vuelca a passive, el README ya trae la Passive justification preparada) y spinner (D-7.4, demo). apg de badge/card/virtual-list ya estaban en forma C5 "none — rationale" ✓.
    5. ✅ util (2026-07-10): link-preview · announce · clipboard + drag-drop (el nº29 del censo E-2.3, omitido del plan de 5 lotes e incorporado al cierre — el "28" del plan era 29 en el censo real). Máquina 99→102 PASS. Exceptions con evidencia: announce R-1.5 (cero partes interactivas — eventos programáticos de live region) y clipboard R-1.5 (el Trigger compone <Button>; focus en button.css). Estados post-fixes verificados: clipboard y drag-drop con pack S3b ✓, link-preview con expression: 'delegated' ✓, apg C5 en drag-drop ✓ (link-preview aún sin apg → one-liner en su Gaps). Criterio de éxito por lote: npx tsx scripts/component-audit.ts → los F-1.x del lote pasan (el informe cae en tmp/component-audit.md).
  • S9 iniciativas ◐ — sesiones dedicadas POST-S4. gradient-* ✅ CERRADA (2026-07-10, 45ae3691): la memoria "trabajo sin commitear" era stale (todo estaba en b3d0e290/f5eb8acb); la sesión graduó ambos de ACTIVE_DEV_TRACK (única violación: barrel soma; contracts quedan con los mismos 13 fallos AJENOS preexistentes — knob/menu-dial/metrics/onion-menu), declaró apg ×2, subió ambos READMEs a dossier y arregló el falso negativo D-1.2 del builder. ntp integral ✅ CERRADA (2026-07-10, dd249eac): el ítem estrella (eventos del Picker genérico) YA estaba pagado en S3b (la ficha era stale — verificado: morfo+pack+triggers+suite); lo que la sesión añadió: R-2.1 aprende la válvula literal: (espejo R-4.x) + los 9 colores del cielo anotados (no tokenizados, F-2) + README soma (contrato del coordinador de dominio + cadena delegada) + suite del provider 12/12. metrics+suite ✅ CERRADA (2026-07-10, 8e3a67bd) — S9 COMPLETO: metrics reclasificado como puente interno (SOMA_INTERNAL_PRIMITIVES, con picker-shell — su soma es una función sin Provider POR DISEÑO; contracts 13→12 fallos, cero metrics) + import self-alias arreglado + apg C5 + dossier (goodTrend como diferencial) + suite 7/7 (helper resolveDeltaIntent extraído puro + el puente signal con intent del prop source y contrato de live region). Censo contracts ✅ (2026-07-10, cb554ae2) — 12→3 fallos: knob (barrel + README soma) · menu-dial/onion-menu = puentes internos (SOMA_INTERNAL_PRIMITIVES; el guard de scope resta el set de AMBOS lados — un puente puede declarar 'soma' con verdad sin ser namespace público) · 6 barrels desacoplados del filename del provider (patrón internals.ts del picker: calendar/cropper/field-langs/gradient-/ image-adjustments) · listSourceFiles salta el ACTIVE_DEV_TRACK (words/chronos fuera de los guards de ficheros) · data-archetype = attr conocido del compilador + los 4 triggers field-overlay (color-picker/drp/ time-picker/trp) alineados al precedente date-picker/ntp (el contrato ya coincide con el DOM; cero cambio runtime) · marker data-float-panel-no-drag declarado en el morfo. Cola runtime-discipline ✅ CERRADA (2026-07-11, bdd17f04 + field 33f346ed) — contracts.test 37/37 VERDE por primera vez: field adopta commit-submit al Enter (doctrina de familia, verificado en vivo: stamp commit/neutral/active + traza de la demo) · float-panel migra TODO a dom.writeProperty/removeProperty (síncrono — el hot-path pegado al cursor conserva su timing, verificado en vivo píxel-perfecto puntero Y teclado) + uix.timers.schedule para el readout + los modos kb ahora suenan por los MISMOS handle-events que el puntero (pick al entrar, drop al salir/cancelar) · A-3.7 afinado a acciones DISTINTAS (Set — la semántica de su propio comentario; 0 fallos de catálogo) · media-provider = PUERTO Svelte-agnóstico por diseño documentado → excepción del guard de listeners (la válvula que timers ya tenía para announce). Matriz: 120 PASS / 14 NEEDS-WORK / 0 BROKEN. Censo R-1.x ✅ CERRADO (2026-07-11, ee9786bc): 9 componentes — badge con fix REAL (hover del Remove al velo --state-hover §38, verificado en demo) + 8 exceptions con evidencia leída (alert-dialog/fab/split-button/ qr-code componen <Button>; date-picker superpone data-field y su trigger es DateField.Input compuesto; drf = layout-only espejo de trf; mask-field IS-A Field ×4 reglas; pin-input YA pinta el ring en [data-active] con tokens canónicos — vehículo que la heurística no reconoce). Matriz: 129 PASS / 5 NEEDS-WORK / 0 BROKEN — los 5 = el lote 6 final (color-swatch · onion-menu · radio-cards · scroll-frames · timeline, cero errores, solo dossiers antiguos). Lote 6 ✅ CERRADO (2026-07-11, 7f2bcc28) → PROGRAMA COMPLETO: la matriz entera en VERDE — 134 PASS / 0 NEEDS-WORK / 0 BROKEN (+ timeline gana apg C5 y sus dos 1em anotados literal:; onion-menu renombra "## Decisions"→"## Decisiones"; scroll-frames gana su Passive justification). contracts 37/37 · vocab 0 · baseline 57. La cola del programa de auditoría está VACÍA. Siguiente = los P1 de la auditoría clean-room del usuario (docs/audit-new-10-july/audit-active-uix.md) — los dos que amplían la máquina primero (CSS secundarios sin escaneo R- · card-group sin morfo, invisible a la matriz) y el resto de frentes (literales en archetypes.css, live-region dual, paleta 33 2/17…). Demos FUERA del fix (decisión usuario 2026-07-10, e53bf0e8): habrá un frame NUEVO para exponer las demos de todo el sistema → las reglas D-* son verdict-NEUTRAL en la máquina (datos visibles, matriz sin pintar; se reescriben contra el frame nuevo). Matriz resultante: 118 PASS / 16 NEEDS-WORK / 0 BROKEN, y los 16 = tres familias reales: (1) censo R-1.x focus/disabled ×9 (alert-dialog/date-picker/drf/fab/ mask-field/pin-input/qr-code/split-button + badge R-4.3 state-layer); (2) dossiers antiguos ×6 = lote 6 reducido (color-swatch/onion-menu/ radio-cards/scroll-frames/timeline/float-panel); (3) contrato ×2: field A-3.1 + float-panel A-3.7 — convergen con el hallazgo "field/float-panel infra-declarados" de la clean-room nueva. Otros: class-hooks preexistentes color-picker(2)/time-picker(8)/trp(12). Audit-fix cerrado con S4; S9 cerrado; la auditoría viva ahora es la clean-room del usuario en docs/audit-new-10-july/.

Lo que queda — S4 CERRADO; siguiente = decisiones + S9

Follow-ups no bloqueantes (registrados)

  • task_d66356de — TimeRangePicker no propaga data-size a sus endpoints (caja fijada a md en todo tamaño; verificado en DOM 2026-07-08). DateRangePicker SÍ lo hace (via el visual context de date-range-field). Espejar ese patrón; luego el embedded-trigger de time-range-picker podrá volver a --field-segment-height (hoy usa el privado --_time-field-height a propósito, por este hueco).
  • Task #31 (memoria) — migrar los date/time pickers al PickerProvider<TValue> genérico (deferValue). POST audit-fix, no ahora.

Protocolo de verificación (invariante en toda la fase)

  • Baseline de tipos: npm run check → errores fuera de alpha|words|palabras|chronos debe seguir en 57 (era 59; P7 arregló los 2 mandados: editable + grid-list — tipos-solo, cast sobre renderProps). (Comando: npm run check 2>&1 | grep " ERROR " | grep -vaiE "alpha|words|palabras|chronos" | wc -l.) Quedan 9 src/ fuera de mandato (file-upload ×4 aria-label, navigation-menu sideOffset, arts/connection ×4) + ~48 en web/ (demos) — candidatos, no mandato.
  • Vocab guard: npx tsx scripts/morfo-vocabulary-check.ts → exit 0 (el único WARN preexistente es cropper.commit-crop, descubribilidad, no mandato).
  • eidos-lint: npx tsx scripts/eidos-lint.ts <componente> → class-hooks 0 / invalid 0.
  • Suites por componente en verde antes de pasar al siguiente (no-cascada: UN componente, verificar, luego el siguiente).
  • UI: check en navegador con los preview_* (no screenshot — flaky; usar preview_eval/preview_inspect). Limitación conocida: no se puede forzar pointer: coarse en el preview headless (el resize a móvil no cambia el tipo de puntero) — verificar coarse por CSSOM + gating en fine.

Exclusiones DURAS (no tocar / no commitear)

  • words/**, palabras/**, chronos — el rename N1 los rompió; decisión del usuario: dejarlos rotos (excluidos del baseline y del commit).
  • web/routes/alpha/** — sitio de docs TERMINADO.
  • Nada a git sin petición explícita; al commitear, paths explícitos excluyendo lo de arriba + .claude/ (settings locales).

Powered by TurnKey Linux.