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/audit-uix-2026-09-19/findings-register.md

104 KiB

title type audience authority status
UIX — registro de hallazgos estáticos notes human + agent process — evidencia de auditoría; no cambia contratos ni estado del ledger reviewed

UIX — registro de hallazgos estáticos

Fecha: 2026-09-19. Este registro desarrolla el plan y las fichas de módulos. Distingue evidencia estática de una reproducción. El ledger vigente conserva el estado oficial de cada fila.

Cola de correcciones tras la auditoría

Este registro es la cola de trabajo para después de cerrar la auditoría, no una autorización para mezclar correcciones funcionales con las extracciones neutras. Las filas de la tabla siguiente cubren todos los defectos de comportamiento confirmados en la tabla de hallazgos de esta auditoría a fecha 2026-09-26. Q12, Q13, Q17, Q20–Q23 y Q74 se añaden como discrepancias de API o metadatos confirmadas por lectura, aún sin reproducción de su efecto en consumidores de metadatos ni decisión de compatibilidad. Q14–Q16, Q18–Q19, Q24–Q31 y Q33 se reprodujeron en consumidores Eidos montados en Chromium. Q34 se reprodujo con el proveedor Sticky y geometría real en Chromium. Q35–Q36 se reprodujeron con el wrapper SkipLink montado en Chromium. Q37 se confirmó en el DOM de Breadcrumb y en la regla WAI-ARIA de conflictos del rol presentacional. Q38 se reprodujo al cambiar reactivamente el href de AnchorNav.Link. Q39 se reprodujo con pageSize=0 en Pagination y count>0. Q40 se reprodujo con una colisión entre el sufijo de un ID duplicado de NavTree y otro ID declarado; Svelte abortó el render por claves iguales. Q41 se reprodujo al pulsar ArrowDown desde el control de una rama abierta de TreeView: el foco no avanzó a la hoja. Q42 ya constaba medido en el README Eidos de TreeView: el hover de una rama abierta tiñe el subárbol por la ubicación del arquetipo en el contenedor. Q43 se reprodujo al seleccionar una fila hija de TreeGrid: el clic también selecciona la fila padre y esta prevalece. Q44 recoge las dos anomalías de hover de TreeGrid medidas previamente en su README Eidos. Q45 se reprodujo al pulsar un botón de acción dentro de GridList: la acción se ejecuta, pero el clic burbujea a la fila y reemplaza la selección múltiple. Q46 recoge el doble velo de hover medido previamente para GridList. L-027, la selección de fila sin acento, ya estaba confirmado y diferido en el ledger. Q47 se reprodujo al pulsar una acción dentro de una fila Table: también se seleccionó la fila. Q48 y Q49 recogen mediciones previas de hover con hoverable=false y pinning en RTL. Q50 es una discrepancia de tipos de la API genérica; Q51 recoge el hover sin respuesta del SortButton ya descrito en el README Eidos. Q52 y Q53 se reprodujeron en VirtualGrid Eidos en Chromium: scroll horizontal RTL con rango vacío y salto a columna que conserva offset 0. Q54 se reprodujo en su provider: claves únicas por eje generan claves de celda duplicadas; el efecto sobre el keyed each queda por reproducir. Q55 y Q56 se reprodujeron en ScrollArea Eidos en Chromium: un mínimo CSS de thumb mayor que el usado por Soma desborda el track al final; un style como objeto se descarta y deja la raíz sin altura. Q57–Q59 se reprodujeron en Splitter Eidos en Chromium: falta enlace del separador al panel, la orientación ARIA sigue el layout en lugar del separador físico y expandir restaura defaultSize en lugar del tamaño anterior. Q60–Q62 se reprodujeron en Knob Eidos: value omitido fuera del rango declarado, step cero que publica NaN y pivote tomado del root con Label. Q62 usa geometría real y eventos sintéticos, no captura nativa de puntero. También se reprodujo el foco ausente al pulsar Label, ya registrado como L-110; no se crea otro ID ni se cambia su estado oficial. El estado oficial de los ID L-* sigue en el ledger; esta cola no crea un segundo estado ni da por reparado un defecto porque el gate general pase.

Lote posterior ID Condición para abordarlo y prueba de cierre
Anuncios y attach Q01-a, Q01-b, Q01-c Fijar ownership del canal y de los temporizadores. Incorporar pruebas permanentes de timeout: 0, attach → dispose → attach y cancelación, que fallen antes y pasen después; comprobar servicios prestados intactos.
Arranque de ActiveUix Q05 Separar rollback tras constructor fallido de la dirección inicial en targetWindow. Fijar ambos casos con pruebas cliente; comprobar listeners y preferencias en documento global e iframe.
Sustitución de raíz Eidos L-126 Caracterizar dos raíces sucesivas sobre el mismo DOM; al destruir la antigua, la nueva debe conservar atributos y estilos. Comprobar SSR y aplicación DOM.
Foco de overlays L-138 Medir document.activeElement al cerrar Dialog y Drawer por Escape y botón, con trigger vivo y desmontado, en Chromium, Firefox y WebKit.
Valor visible de Select L-140 Fijar etiqueta inicial con portal cerrado, también con children de Select.Value; comprobar selección y registro de etiquetas tras abrir/cerrar.
Acento de GridList seleccionado L-027 Fijar la apariencia de una fila seleccionada y del checkbox en varios temas y estados; decidir firma visual antes de mover el selector de acento.
Referencias enlazadas L-152 Clasificar wrappers afectados y añadir pruebas cliente de bind:ref por familia; reparar sólo los casos demostrados.
Firma háptica L-153 Decidir el contrato para un override parcial sin firma base (rechazar, silenciar o completar); fijar la decisión con prueba del resolver y canal antes de cambiar el runtime.
Idiomas L-141, L-144 Reproducir fallback con ar y tipado de claves de catálogo de aplicación; completar traducciones y decidir la frontera de tipos en lotes separados.
Props de Badge Q12 Caracterizar as, visibleFrom y una prop de layout en un consumidor real; decidir si retirar esas props del tipo o implementar su semántica sin romper compatibilidad.
Props de carga Eidos Q13 Caracterizar as, visibleFrom, width y height en Spinner/Skeleton, incluida la geometría computada de Skeleton; decidir el contrato público y la compatibilidad antes de modificar el runtime.
Partes de Avatar Q14 Fijar en tests cliente la demora de Avatar.Fallback y el callback de Avatar.Image dentro del root; comparar con el callback y la demora del root antes de corregir cada prop.
Herencia de Image.Img Q15 Fijar src y alt heredados en composición explícita y atributos propios prioritarios; verificar carga, fallback y SSR al corregir la herencia.
Error abreviado de Image Q16 Fijar el fallback visual tras error cuando sólo se pasa fallback; conservar la distinción de errorFallback, slot Error explícito y fallbackSrc.
Elemento de Code Q17 Comprobar consumidores de defaultElement y fijar la expectativa code frente a span; alinear los metadatos sin alterar la salida DOM predeterminada.
Estilo de Highlight Q18 Probar Highlight importado solo y junto con Mark, con colores por defecto y personalizados; cargar la receta de marca sin duplicar reglas CSS.
Consulta de Highlight Q19 Fijar en cliente las consultas '', ' ', listas mixtas y términos con espacios significativos; decidir la normalización antes de cambiar el matcher.
Elemento de Mark Q20 Comprobar consumidores de defaultElement y alinear mark frente a span sin variar el tag renderizado por defecto.
Elemento de Kbd Q21 Comprobar consumidores de defaultElement y alinear kbd frente a span sin variar el tag renderizado por defecto.
Elemento de Heading Q22 Comprobar consumidores de defaultElement y alinear el <h2> inicial frente a span sin variar el render ni la relación level/style.
Elemento de Display Q23 Comprobar consumidores de defaultElement y alinear <h1> frente a span sin variar el tag renderizado por defecto.
Marcadores de Prose Q24 Añadir prueba cliente permanente de marcadores ul/ol normales e ignorados, incluido tema; aplicar la exclusión a ::marker sin alterar las listas normales.
Foco de Link sin href Q25 Caracterizar usos reales de Link sin destino y fijar con prueba cliente el foco y rol deseados antes de decidir un cambio de DOM.
Props de Link deshabilitado Q26 Fijar en prueba cliente qué atributos de ancla deben omitirse del <span> y comprobar que las props comunes siguen pasando.
Estilo de Label Q27 Añadir prueba cliente permanente que combine style del consumidor con size/weight/color; preservar la asociación for y la precedencia prevista al fusionar estilos.
Posición de Box en RTL Q28 Fijar en prueba cliente left y right dentro de contenedores LTR/RTL; decidir si el contrato es físico o lógico y conservar los insets explícitos al corregir la receta.
alignContent de Stack Q29 Caracterizar el uso público de alignContent sin wrap; decidir si se omite del tipo Stack o se expone wrap, con prueba cliente de geometría y compatibilidad.
grow vertical de Group Q30 Fijar anchos de hijos para Group grow en fila y columna; decidir si grow vertical debe igualar ancho, limitarse a fila o cambiar su descripción, comprobando ButtonGroup vertical.
Props de Box en Container Q31 Fijar precedencia de size/align frente a width, maxWidth y márgenes heredados; decidir si se admiten overrides o se estrecha el tipo, y comprobar los Blocks consumidores.
Elemento de Section Q32 Comprobar consumidores de defaultElement y alinear el metadato div con el root section predeterminado, sin alterar el DOM ni la capacidad as de SiteFooter.
Props de Box en Section Q33 Fijar la precedencia de display y width heredados frente a los valores fijos del wrapper; decidir el contrato con pruebas cliente sobre Section y los Blocks, incluido SiteFooter.
Estado de Sticky al terminar sección Q34 Fijar en Chromium, Firefox y WebKit la entrada y salida por ambos extremos del contenedor, con edge=top/bottom, offset y scroll-host explícito. Corregir la observación sin lecturas síncronas de layout y comprobar el atributo real.
onclick de SkipLink Q35 Decidir si componer el callback con la navegación o retirar esa prop de la API; fijar foco, cancelación y llamada del consumidor en prueba cliente antes de cambiar el wrapper.
Limpieza de foco de SkipLink Q36 Fijar destinos visibles, ocultos, desconectados y con tabindex previo; limpiar sólo el atributo que puso el componente, incluso cuando focus() no lo consigue.
Ellipsis decorativo de Breadcrumb Q37 Fijar el árbol de accesibilidad del elipsis estático e interactivo en Chromium, Firefox y WebKit; decidir si el estático es decorativo o nombrado y retirar el conflicto sin cambiar el botón.
Destino reactivo de AnchorNav Q38 Fijar cambio de href con dos destinos reales, registro/observación y atributos del enlace; decidir si el prop es estable por contrato o re-registrarlo sin fugas ni pérdida de duplicados.
Rango vacío de Pagination Q39 Fijar pageSize=0 y valores negativos con count>0; decidir validación, normalización o rango vacío, protegiendo cálculo de páginas, slice y controles.
Claves de NavTree Q40 Reproducir sufijo generado frente a ID declarado, además de duplicados ordinarios; reservar claves únicas de todo el árbol y verificar ruta activa, expansión y logger sin cambiar IDs válidos.
Teclado y rol de ramas TreeView Q41 Alinear la lista de candidatos y el tab stop de rama; fijar ArrowDown/Up, flechas horizontales, Home/End y typeahead en un árbol anidado real, más el foco accesible.
Velo hover de ramas TreeView Q42 Medir fila y contenedor en abierto/anidado, desplazar el arquetipo a la fila solo con caracterización visual y de eventos, y proteger el resultado en claro/oscuro y RTL.
Selección anidada de TreeGrid Q43 Fijar selección de hija, padre y hermanas con clic, teclado y expansión; aislar el evento de fila sin perder selección múltiple ni controles interactivos.
Hover de filas TreeGrid Q44 Medir bandas y filas normales en claro/oscuro, varias órdenes de carga CSS y motores; resolver la precedencia visual en un lote separado.
Botones internos de GridList Q45 Fijar clic en acción, enlace y checkbox dentro de fila con selección simple y múltiple; conservar ejecución y foco de cada control sin selección accidental.
Hover doble de GridList Q46 Medir velo de fila y celda en claro/oscuro y estados selected/highlighted; decidir un único portador del hover en lote visual separado.
Botones internos de Table Q47 Fijar clic en acción, enlace y RowDetailTrigger con selección simple/múltiple; conservar la acción sin seleccionar una fila por burbujeo.
hoverable de Table Q48 Medir filas en hover con la prop activada y desactivada, y decidir si el arquetipo o la receta es dueño del velo.
Pinning de Table en RTL Q49 Fijar desplazamiento horizontal y offset de columnas fijadas en LTR/RTL en Chromium, Firefox y WebKit; decidir coordenadas lógicas sin alterar el enum por inferencia.
Tipo genérico de Table Q50 Reproducir un consumidor TypeScript sin casts con createTable<T>; decidir parametrización pública y compatibilidad de los tipos de partes.
Hover de SortButton Q51 Medir estados normal, hover, foco y orden asc/desc; decidir precedencia entre receta y arquetipo en lote visual.
Rango horizontal RTL de VirtualGrid Q52 Normalizar el offset lógico por dirección; cubrir scroll negativo, ambos extremos y resize en Chromium, Firefox y WebKit antes de corregir.
Salto RTL de VirtualGrid Q53 Fijar scrollToCell con start/center/end/auto por ambos ejes y verificar offset nativo negativo y celda visible; conservar el evento de navegación.
Claves compuestas de VirtualGrid Q54 Reproducir colisiones con delimitadores y tipos string/number en un consumidor keyed; definir clave inequívoca sin romper estabilidad ni identidad de las celdas.
Geometría del thumb de ScrollArea Q55 Fijar tamaño pintado y recorrido con mínimos CSS, tracks cortos, padding, ambos ejes y RTL; decidir qué capa posee la geometría antes de corregir drag y offset.
Splitter: enlace y eje ARIA Q57, Q58 Fijar ID del panel primario, orientación física en ambos ejes y composición de tres paneles; verificar ARIA y teclado en tres motores antes de corregir Morfo.
Restauración de Splitter Q59 Caracterizar tamaño previo, defaultSize, min/max del vecino, collapse por teclado/doble clic y escrituras externas; decidir contrato sin romper bindings ni callbacks.
Knob: valor inicial Q60 Caracterizar value omitido, min distinto de cero y valores controlados fuera de rango; decidir seed sin añadir callbacks ni cambiar la semántica de reset.
Knob: step no positivo Q61 Fijar step cero, negativo y no finito, rejillas decimales y límites; decidir rechazo o fallback antes de impedir la publicación de NaN.
Knob: pivote del dial Q62 Medir Control y root con Label, ValueText y ValueField; reproducir gesto nativo y captura en tres motores antes de elegir el nodo del pivote.
Knob: foco de Label L-110 Resolver el Control real mediante ActiveDom y verificar foco al pulsar Label, disabled, readonly y destrucción; mantener el estado oficial del ledger.
Gesture: cancel antes del buffer Q63 Caracterizar down/cancel/move/unmount y callbacks; fijar limpieza de listeners antes y después del umbral sin cambiar offsets o capture por inferencia.
Gesture: segundo puntero Q64 Probar dos pointerdown antes del buffer y el gesto siguiente; conservar el primer pointerId y garantizar una sola sesión/listener en eventos sintéticos y multi-touch nativo.
Gesture: snaps secuenciales Q65 Fijar snap asentado, inicio de gesto, vecinos, wrap, velocidad y entradas externas; decidir estado de referencia para drag y rotate sin cambiar AxialDrag.
AxialDrag: cleanup de cancel Q66 Fijar down/cancel/down y desmontaje antes/después del buffer; garantizar una sesión y una cleanup sin perder la posición asentada.
AxialDrag: identidad del puntero Q67 Caracterizar move/up/cancel de otros IDs y captura; verificar multi-touch nativo sin alterar signos, límites, snaps ni callbacks.
AxialDrag: progreso controlado Q68 Fijar offsetPx/value01 con snap externo inicial y actualizado, antes/durante/después del drag; medir tamaño y progreso en Drawer.
ZoomPan: mínimo e inicio Q69 Adjudicar escala inicial y reset con minScale distinto de 1; comprobar isZoomed, límites y Cropper montado antes de cambiar el contrato.
Cropper: nombre del zoom Q70 Nombrar el thumb enfocable mediante el contrato Slider y verificar idiomas y nombre accesible sin cambiar valor o teclado.
Cropper: límites de zoom reactivos Q71 Adjudicar cambios de minScale/maxScale tras mount; sincronizar controles y motor conservando clamp, anclas y reset.
Cropper: disabled con teclado Q72 Fijar Tab/Enter en reset y teclado del Slider con disabled; conservar exportación programática y reactivación.
Cropper: identidad del puntero Q73 Fijar ID de sesión para move/up/cancel y captura en selección, resize y pan; comprobar multi-touch nativo antes de corregir.
Cropper: justificación APG Q74 Adjudicar el texto apg que afirma keyboard nudge frente al gap diferido; alinear metadatos sin implementar teclado dentro del refactor neutral.
Style del root ScrollArea Q56 Fijar estilos string/objeto, precedencia con size/radius y autosize; conservar dimensiones y tokens del consumidor sin alterar la geometría Soma.

Lotes posteriores de ImagePicker e ImageAdjustments:

Lote posterior ID Condición y prueba de cierre
Payload y objeto URL Q75 Adjudicar cuándo emitir select/remove/replace; hacer corresponder file y URL sin cambiar ownership, revocación ni orden de callbacks por inferencia.
Notificación de ajustes y bindings Q76 Fijar contrato de onChange para teclado, fit y cambios externos; impedir emisiones duplicadas conservando bindings y preview.
Nombre de ajustes Q77 Nombrar cada thumb en ImageAdjustments mediante el contrato Slider y traducciones; probar nombres, varios ajustes y teclado.
Destino perceptual al seleccionar Q78 Caracterizar selección y desmontaje de FileUpload; conservar señal y handler sin retrasar arbitrariamente la transición ni introducir un fallback ajeno al contrato.

Al aparecer otro defecto durante la auditoría se añade aquí y en la tabla de evidencia de abajo con un ID existente del ledger o un Q* nuevo, reproducción o límite de la evidencia, consumidor afectado y prueba de cierre. Al terminar las fases 6 y 7 se concilian los ID con el ledger, se ordenan por impacto y se ejecutan lotes independientes con prueba antes/después y gate completo.

L-130 es una medición de CSS/primer pintado, no un defecto adjudicado. Q02 requiere decidir si existe una promesa de integración prefs → Sema. Q11 no se reprodujo en el <SText> real y permanece como señal condicionada. Ninguno entra en la cola de correcciones confirmadas por inferencia.

Defectos y riesgos de comportamiento

ID Área Evidencia de código Estado de auditoría Siguiente prueba, sin cambio de contrato
Q01-a ActiveUix announce announce() sólo reemplaza el timer si timeout > 0; timeout: 0 conserva el timer anterior Reproducido en Vitest client (Chromium) Corregir en un lote de defectos con prueba permanente
Q01-b ActiveUix attach La closure de AnnounceChannel captura la primera instancia; el canal prestado sobrevive a dispose() Reproducido en Vitest client (Chromium) Definir ownership del canal antes de corregirlo
Q01-c ActiveUix attach announce() programa el borrado en app.timers, pero uix.dispose() deja pendiente ese temporizador prestado Reproducido en Vitest client (Chromium) Definir cancelación por instancia junto con Q01-b
Q05 ActiveUix boot createActiveUix() registra listeners antes de validar Sema y lee la dirección del documento global aunque ActiveDom apunte a otro targetWindow Ambos casos reproducidos en Vitest client (Chromium) Garantizar rollback en error y alinear la lectura inicial de preferencias con el destino configurado
L-126 Eidos lifecycle dispose() borra IDs de estilo y atributos sin marca de la instancia que escribió después Reproducido en Vitest server Corregir ownership de DOM y estilos en un lote de defectos
L-130 Eidos CSS apps/base importa index.css, que incluye generated/base.css, y crea ActiveEidos con applyDom:true, que escribe foundation runtime Medido en el ledger; la alternativa afecta primer paint Comparar SSR, hidratación, CSS y first paint antes de variar el entrypoint
L-138 Dialog/Drawer Dialog construye FocusScope antes de HideOthers. El ledger reproduce el retorno a body mientras el trigger sigue inert; los tests no leen activeElement Reproducido en el ledger; estático compatible Browser test de Escape/Cerrar en Chromium, Firefox y WebKit con trigger vivo y desmontado
L-027 GridList selección La regla visual para [data-selected] se retiró porque Soma escribe data-state='selected'; una fila seleccionada conserva el velo neutro y el checkbox sí recibe acento. Medido en next-features.md §13 y diferido en el ledger Decidir firma visual y fijar píxeles en varios estados antes de cambiar el selector.
L-140 Select getDisplayText() devuelve el value si no hay etiqueta registrada. Los items registran su etiqueta al montarse; con el portal cerrado no hay registro inicial Reproducido sin children en Vitest client (Chromium) Decidir cómo suministrar la etiqueta inicial; probar también children en Value
L-152 Eidos wrappers Censo previo: wrappers con ref = $bindable reenvían ...rest a Soma sin siempre enlazar bind:ref Confirmado por el ledger; no se repite el censo como prueba funcional Clasificación por familia y prueba cliente de cada contrato bind:ref afectado
L-153 Sema resolver SemaSignatureOverride permite Partial<HapticSignature>; sin slice base applyOverride() clona ese parcial. Con channels:['haptic'] y sin kind, HapticChannel desreferencia un perfil inexistente antes de la defensa S-31 Reproducido en Vitest server Decidir y fijar rechazo, silencio o completado antes de corregir el comportamiento
L-141 Langs catálogo El ledger confirma componentes sin traducción ar; con ese locale aparece texto de otro idioma Confirmado por el ledger; catálogo estático en boot Arrancar con ar y comprobar las claves de componente afectadas, antes de completar traducciones
L-144 Langs de aplicación Una ruta inválida de catálogo de app compila y sólo se detecta en desarrollo; uixLangs no integra ese tipo Confirmado por el ledger; requiere decisión de API Compilar una clave válida e inválida del catálogo app y decidir la frontera de tipo pública
Q11 Eidos canvas-text FontFace tardía: WebKit pintó 5 líneas a 124 px y layout() contó 4 tras invalidar; Chromium y Firefox coincidieron Harness parcial intermitente; <SText> real pasa en tres motores Aislar sólo si reaparece en otro consumidor; no modificar el runtime
Q12 Badge API BadgeProps hereda BoxProps, pero badge.svelte renderiza un <span> y reenvía as, visibleFrom y props de layout mediante ...rest sin resolverlas Discrepancia confirmada por lectura; salida DOM aún no caracterizada Probar las props en cliente y decidir contrato de compatibilidad después de la auditoría
Q13 Spinner/Skeleton API Ambos tipos heredan BoxProps y afirman componer Box; los wrappers renderizan <div> y reenvían as, visibilidad y medidas mediante ...rest. Skeleton anuncia width/height en su ejemplo, mientras su CSS fija inline-size:100% y block-size:var(--skeleton-size) Discrepancia confirmada por lectura; geometría DOM aún no medida Medir atributos y tamaño en cliente; decidir compatibilidad de props al cerrar la auditoría
Q14 Avatar parts Avatar.Fallback desestructura delayMs sin usarlo; Avatar.Image sólo conecta onImageStatusChange al provider cuando no hay root padre Reproducido en Vitest client (Chromium) Probar las dos props dentro de un Avatar antes de corregir su cableado
Q15 Image.Img ImageImgProps promete heredar src y alt del root, pero la part fija defaults null y '' en su <img> sin leer los props del padre Reproducido en Vitest client (Chromium) Probar herencia y precedencia de valores explícitos; cubrir carga y SSR
Q16 Image error El README promete usar Fallback si no hay Error, pero la ruta abreviada oculta Fallback en error y monta un slot Error sin contenido si falta errorFallback Reproducido en Vitest client (Chromium) Probar error con fallback solo, errorFallback, slot explícito y fallbackSrc
Q17 Code Morfo codeMorfo declara defaultElement: 'span'; el wrapper Eidos, su tipo y README establecen code por defecto Discrepancia estática confirmada; no se midió efecto en consumidores Inspeccionar consumidores de metadatos y alinear el elemento declarado sin cambiar el render
Q18 Highlight CSS highlight.svelte importa highlight.css, pero la receta [data-mark] sólo está en mark.css, que importa Mark Reproducido en Vitest client (Chromium): amarillo nativo, sin padding Probar importación aislada y conjunta, tema y color CSS libre antes de cargar la receta
Q19 Highlight query El filtro usa t.length > 0; una consulta de sólo espacios genera coincidencias aunque el README prometía ignorarla Reproducido en Vitest client (Chromium) Decidir normalización de espacios y fijar casos de lista y término significativo
Q20 Mark Morfo markMorfo declara defaultElement: 'span'; el wrapper Eidos, su tipo y README establecen mark por defecto Discrepancia estática confirmada; no se midió efecto en consumidores Inspeccionar consumidores de metadatos y alinear el elemento declarado sin cambiar el render
Q21 Kbd Morfo kbdMorfo declara defaultElement: 'span'; el wrapper Eidos, su tipo y README establecen kbd por defecto Discrepancia estática confirmada; no se midió efecto en consumidores Inspeccionar consumidores de metadatos y alinear el elemento declarado sin cambiar el render
Q22 Heading Morfo headingMorfo declara defaultElement: 'span'; el wrapper Eidos, su tipo y README establecen <h2> por defecto Discrepancia estática confirmada; la sonda montó <h2> en Chromium Inspeccionar consumidores de metadatos y alinear el elemento declarado sin cambiar el render
Q23 Display Morfo displayMorfo declara defaultElement: 'span'; el wrapper Eidos, su tipo y README establecen <h1> por defecto Discrepancia estática confirmada; no se midió efecto en consumidores Inspeccionar consumidores de metadatos y alinear el elemento declarado sin cambiar el render
Q24 Prose CSS Las reglas ul > li::marker y ol > li::marker omiten el filtro data-prose-ignore; una sonda con color de marcador distinto mostró que también colorean las listas ignoradas Reproducido en Vitest client (Chromium), 1/1; sonda retirada Fijar listas normales e ignoradas en prueba permanente antes de ajustar los selectores
Q25 Link sin href El tipo afirmaba que el <a> sin destino seguía siendo enfocable; al montar Link sin href, focus() no lo convirtió en document.activeElement, mientras que el enlace con destino sí Reproducido en Vitest client (Chromium), 1/1; sonda retirada Caracterizar consumidores y decidir el contrato de foco y rol sin cambiar navegación por inferencia
Q26 Link deshabilitado El wrapper afirma descartar atributos de ancla en el <span> deshabilitado, pero target="_blank" y rel="nofollow" pasados por el consumidor permanecen en ese elemento Reproducido en Vitest client (Chromium), 1/1; sonda retirada Probar filtrado y reenvío de props comunes antes de cambiar la rama deshabilitada
Q27 Label style LabelProps hereda style, pero label.svelte escribe style={resolvedStyle} después de ...restProps; una sonda con padding-inline-start: 7px y size="lg" conservó el tamaño, pero perdió el padding Reproducido en Vitest client (Chromium), 1/1; sonda retirada Probar composición de estilo externo y ejes internos antes de cambiar el wrapper
Q28 Box en RTL box.css aplica --box-left/right tanto a propiedades físicas como a inset-inline-start/end; en RTL, las declaraciones lógicas posteriores cambian de lado left={10} y right={10} Reproducido en Vitest client (Chromium); sonda retirada Decidir contrato físico o lógico y probar ambos ejes antes de cambiar la receta
Q29 Stack alignContent StackProps hereda alignContent de Flex pero omite wrap; el wrapper deja flex-wrap: nowrap, de modo que el prop se acepta y se escribe aunque no mueve los hijos en el uso tipado normal Reproducido en Vitest client (Chromium), 1/1; sonda retirada Decidir si reducir el tipo o abrir wrap, con prueba de efecto y compatibilidad
Q30 Group grow GroupProps.grow promete igualar anchos, pero la receta usa flex: 1 1 0 en cualquier dirección; con direction="column", dos hijos de 30/50 px conservaron anchos distintos, frente a la fila que los igualó Reproducido en Vitest client (Chromium), 2/2; sonda retirada Decidir contrato vertical y proteger también ButtonGroup vertical antes de corregir
Q31 Container props ContainerProps hereda width, maxWidth y márgenes de Box, pero el wrapper los sobrescribe tras ...restProps; size="sm" conservó 480 px pese a maxWidth={320} y width="50%", y align ganó a marginLeft/marginX Reproducido en Vitest client (Chromium), 1/1; sonda retirada Decidir precedencia o estrechar la API; probar size, align y Blocks antes de corregir
Q32 Section Morfo sectionMorfo declara defaultElement: 'div', pero Eidos monta <section> por defecto y as="footer" monta <footer> Discrepancia estática y DOM confirmados; efecto en lectores de Morfo pendiente Alinear metadato tras comprobar sus consumidores, sin cambiar el DOM ni as
Q33 Section props SectionProps hereda display y width de Box, pero el wrapper escribe display="block" y width="100%" después de ...restProps; la sonda conservó esos valores con display="flex" y width="50%" explícitos Reproducido en Vitest client (Chromium), 1/1; sonda retirada Decidir precedencia o estrechar API; cubrir los Blocks consumidores antes de corregir
Q34 Sticky data-stuck El único centinela queda fuera del scroll-host al salir por arriba; StickyProvider conserva stuck=true aun cuando el límite inferior de la sección obliga al box a dejar de estar pegado y salir de la vista. Reproducido en Vitest client (Chromium), 1/1; sonda retirada Caracterizar ambos bordes y scroll-hosts; añadir observación del fin de sección y regresión sin cambiar API.
Q35 SkipLink onclick SkipLinkProps hereda onclick de HTMLAnchorAttributes, pero el <a> escribe onclick={jump} después de ...restProps; el callback del consumidor no se invoca. Reproducido en Vitest client (Chromium), 1/1; sonda retirada Decidir composición o estrechar el tipo; proteger foco, fragmento y callback antes de corregir.
Q36 SkipLink tabindex Cuando el destino tiene display:none, jump() le añade tabindex="-1", pero focus() falla y no se dispara el blur que lo quitaría. Reproducido en Vitest client (Chromium), 1/1; sonda retirada Fijar limpieza tras foco fallido y conservación de un tabindex previo antes de corregir.
Q37 Breadcrumb Ellipsis El Ellipsis no interactivo monta <span role="presentation" aria-label="…">; WAI-ARIA indica que el atributo global aria-label hace ignorar el rol presentacional. DOM confirmado en Vitest client (Chromium), 1/1; efecto en lectores pendiente Decidir si es decorativo o nombrado; medir árbol accesible en tres motores antes de cambiar el Morfo.
Q38 AnchorNav href El wrapper actualiza el href del <a> reactivamente, pero AnchorNavLinkProvider.targetId se calcula sólo en el constructor y conserva el registro anterior. Reproducido en Vitest client (Chromium), 1/1; sonda retirada Decidir estabilidad de la prop o re-registro; probar observación y ref-count de destinos duplicados.
Q39 Pagination pageSize Con count=95, page=5 y pageSize=0, totalPages=0 y pages=[], pero pageRange={start:1,end:0} contradice el contrato de rango vacío {0,0}. Reproducido en Vitest client (Chromium): sonda fallida y retirada Decidir contrato de entrada y fijar 0, negativos y transición desde tamaño válido antes de corregir.
Q40 NavTree keys Los IDs a, a#root/2, a generan las claves a, a#root/2, a#root/2; el sufijo de recuperación choca con un ID declarado y el keyed each aborta. Reproducido en Vitest client (Chromium): each_key_duplicate; sonda retirada Reservar claves únicas en el árbol completo; probar estado y avisos con colisiones sucesivas.
Q41 TreeView teclado getVisibleItems() enumera [role="treeitem"], pero el tab stop de rama está en BranchControl sin ese rol; ArrowDown calcula índice -1 y enfoca el contenedor no enfocable. Reproducido en Vitest client (Chromium): aserción de foco fallida y retirada Caracterizar las teclas y el árbol accesible; alinear candidato y foco sin cambiar selección por inferencia.
Q42 TreeView hover El arquetipo item está en el Branch que contiene control e hijos; su regla transversal de hover pinta el contenedor completo cuando está abierto. Medido en el README Eidos (2026-08-21); estructura y CSS ratificados en esta auditoría Medir estados y tema; mover el arquetipo solo en un lote visual separado con canario de píxeles.
Q43 TreeGrid selección RowChildren anida las filas en el DOM; TreeGridRowProvider.onclick llama a handleRowClick sin detener la propagación. Un clic en index selecciona después al padre src. Reproducido en Vitest client (Chromium): se esperaba index, se obtuvo src; sonda retirada Caracterizar clic y teclado de filas anidadas antes de aislar el evento.
Q44 TreeGrid hover La banda striped tiene mayor especificidad que el hover; fuera de ella, el hover de la receta empata con el velo del arquetipo item y depende del orden de carga. Medido en README Eidos (2026-08-20): seis cargas, cuatro sin velo y dos con velo Medir píxeles en cada estado y fijar precedencia visual con regresión.
Q45 GridList acción GridListRowProvider.onclick procesa el clic de un botón hijo como clic de fila. En selección múltiple, pulsar Open dentro de alpha reemplazó beta,alpha por alpha. Reproducido en Vitest client (Chromium): aserción fallida y retirada Caracterizar controles internos antes de aislar sus eventos.
Q46 GridList hover Fila y celda declaran el arquetipo item; el fondo de hover de la receta y el velo del arquetipo se apilan bajo el puntero. Medido en README Eidos (2026-08-21) y next-features.md §12 Medir capas de píxeles y resolver portadores del hover sin cambiar el checkbox.
Q47 Table acción TableRowProvider.onclick alterna la selección sin comprobar el origen del evento; pulsar Open dentro de Ada también seleccionó Ada. Reproducido en Vitest client (Chromium): aserción fallida y retirada Caracterizar controles internos y selección por teclado antes de aislar clics.
Q48 Table hoverable La fila usa el arquetipo item; su velo de hover sigue activo aunque el root omita data-hoverable. Medido en README Eidos (2026-08-20), antes de tokenización Medir el efecto visual y decidir qué capa manda en un lote separado.
Q49 Table pinning RTL buildColumnStyle escribe offsets físicos left/right; con dir=rtl el borde de una columna fijada se desplazó de 350 a 444 px al hacer scroll horizontal. Medido en README Eidos (2026-08-17) a 375 px Reproducir geometría y semántica start/end en los tres motores antes de cambiar el engine.
Q50 Table tipos TableProps.table es TableInstance<unknown>; createTable<T> produce TableInstance<T> invariante, por lo que un consumidor tipado necesita cast. Discrepancia estática confirmada en tipos y README; compilación de consumidor pendiente Fijar caso TypeScript real y decidir genéricos en Table y partes sin romper inferencia.
Q51 Table SortButton El velo de hover del SortTrigger pierde ante background: transparent de la receta, de modo que no hay respuesta visual al hover. Documentado en README Eidos; medición visual pendiente en esta auditoría Medir píxeles en hover/foco/sort antes de cambiar la precedencia.
Q52 VirtualGrid rango RTL El viewport guarda el.scrollLeft negativo; rangeFor lo usa como offset positivo. Con dir=rtl y scrollLeft=-800, una cuadrícula de 1.000 × 200 celdas quedó sin celdas en el DOM. Reproducido en Vitest client (Chromium) y captura inspeccionada; sonda retirada Fijar coordenadas lógicas por dirección y cubrir rango, overscan, extremos y resize en tres motores.
Q53 VirtualGrid salto RTL scrollToCell(20,10,{rowAlign:'start',columnAlign:'start'}) manda left=800 en un scroller RTL; el navegador conserva scrollLeft=0, aunque la fila avanza a 480 px. Reproducido en Vitest client (Chromium): offset esperado -800, obtenido 0; sonda retirada Cubrir align por eje, celda visible y evento discreto antes de convertir offset lógico a nativo.
Q54 VirtualGrid claves ${rowKey}|${columnKey} no es inequívoco: filas a|b,a y columnas c,b|c generan dos claves a|b|c, pese a que cada eje tiene claves únicas. Reproducido en provider con Chromium: 3 claves únicas para 4 celdas; render pendiente Fijar consumidor con keyed each y conservar estabilidad de claves con delimitadores y tipos mixtos.
Q55 ScrollArea thumb El mínimo CSS de 64px pinta un thumb de 64px; Soma calcula 22,4px sobre un track de 112px. Al final, el borde inferior excede el track en 41,59375px. Reproducido en Chromium con DOM real y captura inspeccionada; sonda retirada Fijar geometría efectiva de tamaño, offset y drag por eje sin introducir lecturas de layout por cada scroll.
Q56 ScrollArea style El root conserva style sólo si es string, aunque el tipo acepta objeto. Un objeto con block-size 120px e inline-size 200px deja altura 0px. Reproducido en Chromium: altura esperada 120, obtenida 0; sonda retirada Caracterizar precedencia de estilos de consumidor y props visuales antes de fusionar ambos formatos.
Q57 Splitter enlace ARIA El separador no emite aria-controls aunque el panel primario tiene ID. Reproducido en Chromium: atributo null; sonda retirada Caracterizar identificación de paneles y enlace ARIA sin romper composición ni IDs.
Q58 Splitter eje ARIA En layout horizontal, el separador físico vertical anuncia aria-orientation horizontal. Reproducido en Chromium con DOM y captura inspeccionada; sonda retirada Separar orientación del layout, teclado y geometría ARIA para ambos ejes.
Q59 Splitter restauración Tras escribir 40/35/25 y colapsar con Enter a 0/75/25, expandir da 25/50/25 (defaultSize), no 40/35/25. Reproducido en Chromium con teclado nativo; sonda retirada Fijar historial de tamaño, límites vecinos y cambios externos antes de alterar expansión.
Q60 Knob valor inicial Con min 20, max 100 y value omitido, aria-valuenow es 0 pese al default min del tipo; ambos wrappers inicializan a cero. Reproducido en Chromium: valor esperado 20, obtenido 0; sonda retirada Caracterizar defaults y escrituras controladas antes de ajustar el seed.
Q61 Knob step cero Con value 20 y step 0, ArrowRight produce NaN y lo publica como aria-valuenow. Reproducido con teclado nativo en Chromium; sonda retirada Decidir validación o fallback de step con rangos y callbacks preservados.
Q62 Knob pivote Gesture.rotate usa el root que incluye Label. Al mover al punto derecho del dial sm, la posición fue 75 en lugar de 85. Reproducido con DOM real, eventos sintéticos y captura inspeccionada; sonda retirada Caracterizar composición y gesto nativo antes de mover el pivote a Control.
L-110 Knob foco de Label El onclick de Label sigue vacío. Pulsar su span deja el foco en body en lugar del Control. Reproducido con Playwright en Chromium; ya diferido en ledger Enlazar foco mediante ActiveDom sin alterar disabled ni readonly.
Q63 Gesture cancel cancel() retorna si isDragging es false aunque pointerdown ya enlazó listeners. Tras down/cancel/move 20px, onDrag se ejecutó una vez. Reproducido en jsdom con documento aislado y captura mock; sonda retirada Fijar cancelación durante el buffer y desmontaje con propiedad de listeners explícita.
Q64 Gesture puntero Dos pointerdown antes del buffer sustituyen pointerId y enlazan listeners dos veces; move del segundo dispara dos callbacks. Tras up, el siguiente gesto también dispara dos. Ambas consecuencias reproducidas en jsdom; sondas retiradas Fijar punteros y limpieza en gestos consecutivos; reproducir multi-touch nativo.
Q65 Gesture sequential drag y rotate borran activeSnapPoint en pointerdown, anulando la restricción de vecinos. Con velocidad cero, drag salta de 100 a 300 y rotate de 90 a 270 con wrap false. Reproducido en ambos resolvers con jsdom y reloj fijo; sondas retiradas Decidir snap de referencia; cubrir continuidad, wrap y proyección. Sin consumidor actual de este modo.
Q66 AxialDrag cancel Tras down/cancel/down/move 20px, onDrag se ejecuta dos veces. cancel limpia flags y captura, pero no document listeners. Reproducido en jsdom, documento aislado y captura mock; sonda retirada Fijar cleanup durante el buffer, drag activo y desmontaje sin perder posición asentada.
Q67 AxialDrag puntero No se guarda el pointerId del down. Move de ID2 arrastra la sesión de ID1; up de ID2 emite onDragEnd y la cierra. Ambos casos reproducidos en jsdom; sondas retiradas Fijar identidad y release/cancel de otros IDs; probar multi-touch nativo antes de corregir.
Q68 AxialDrag value01 Con referenceSize 400, snaps [0.5,1] y activeSnap 0.5, offsetPx devuelve 200 pero value01 devuelve 0 al leer sólo _offsetPx. Reproducido en jsdom mediante getters públicos; sonda retirada Caracterizar snaps controlados y progreso; medir su efecto en Drawer antes de corregir.
Q69 ZoomPan mínimo Con minScale 2/maxScale 5, scale empieza en 1 y tras zoomTo(3)/reset vuelve a 1. Contradice el mínimo declarado, aunque reset documenta explícitamente 1. Dos sondas jsdom fallan; 19 tests existentes pasan; sondas retiradas Adjudicar seed/reset y medir Cropper con mínimos personalizados antes de corregir.
Q70 Cropper zoom ARIA El wrapper da aria-label Zoom al root de Slider, pero el thumb con role slider carece de nombre accesible. Reproducido con matcher de nombre accesible en Chromium; sonda retirada Fijar nombre del thumb y traducciones; contrastar APG Slider.
Q71 Cropper límites Cambiar maxScale de 5 a 2 tras mount deja aria-valuemax en 5; ZoomPan conserva la configuración capturada por el constructor. Reproducido con prop reactiva y clic nativo en Chromium; sonda retirada Adjudicar límites actualizados; medir clamp, controles y estados actuales.
Q72 Cropper disabled A escala 1.2, activar disabled y llegar al reset por Tab/Enter devuelve escala 1. Los controles no reciben disabled y resetZoom no lo comprueba. Reproducido con teclado nativo en Chromium; sonda retirada Fijar bloqueo de reset y Slider; preservar exportación programática.
Q73 Cropper puntero Down ID1 en Selection y move ID2 de 32px desplazan crop.x de 0.25 a 0.35; la sesión no conserva pointerId. Chromium montado, eventos sintéticos y captura mock; sonda retirada Cubrir selección, handle y pan; reproducir multi-touch nativo.
Q74 Cropper metadatos apg afirma keyboard nudge desde C5, pero Selection/Handle no tienen handler de teclado y README lo difiere. El campo de metadatos se conserva. Discrepancia confirmada por lectura; efecto en consumidores de metadatos sin reproducir Adjudicar y alinear la justificación; no implementar teclado por inferencia.

ImagePicker e ImageAdjustments: Q75–Q78

ID Área Hallazgo Evidencia Cierre pendiente
Q75 ImagePicker payload La selección emite file nuevo con url null; remove emite file null con la URL anterior. El efecto de URL se ejecuta después del callback. Dos sondas Chromium con PNG y URLs reales fallan. Fijar correspondencia y timing de callbacks, reemplazo y cleanup.
Q76 ImagePicker onChange ArrowUp cambia brillo y preview, pero conserva una sola emisión de selección. Cambiar fit externamente a contain tampoco emite. Dos sondas con teclado/clic nativos fallan. Adjudicar y cubrir el contrato que promete full value whenever anything changes.
Q77 ImageAdjustments ARIA Los ocho thumbs tienen nombre accesible vacío; aria-label del root compuesto no nombra el thumb. Ocho sondas Chromium del panel real reciben vacío; brillo embebido ya estaba reproducido. Cubrir nombres de todos los thumbs, traducciones y teclado.
Q78 ImagePicker / FileUpload La transición empty→ready desmonta FileUpload antes de emitir commit-set-add; el runtime informa destino provider ausente y omite señal perceptual. Sonda captura el diagnóstico del logger real tras upload nativo; selección y preview funcionan. Conservar el destino semántico y el handler durante selección/desmontaje.

Las seis sondas fallaron mientras pasaban 21 tests de caracterización en tres archivos. Se montaron ActiveUix, Soma y ActiveEidos reales; el PNG se creó por canvas y se seleccionó mediante la API Playwright de input files. No se automatiza el diálogo de archivos del sistema operativo. Los spies de console observan la ejecución original del logger, sin sustituirlo. Se retiraron las sondas al terminar, sin corregir estos comportamientos.

Q75: tras cargar el PNG, el img tiene blob URL pero la última emisión todavía tiene url null. Después de pulsar Remove, file es null y url mantiene el blob previo, que el efecto revoca al actualizarse. Q76: tras selección hay una llamada onChange; ArrowUp en brillo pone brightness=1 y el filtro brightness(1.01), pero sigue habiendo una llamada. Un botón que cambia el binding fit a contain reproduce la misma ausencia. Q77: el matcher de nombre del thumb de brillo recibe vacío. Es un uso distinto de la misma pauta que Q70, en ImageAdjustments, no un defecto nuevo adjudicado al motor Slider por inferencia. La auditoría propia del panel amplía la reproducción a brightness, contrast, saturation, temperature, hue, blur, grayscale y sepia: ocho sondas fallan mientras pasan 22 tests de caracterización en dos archivos. Se montan servicios UIX reales y las sondas se retiran al terminar. Q77 conserva su ID y sigue abierto. Q78: 100ms después de la selección, console recibe el mensaje del runtime file-upload.commit-set-add found no DOM element for target part provider. El flujo sigue en ready y el handler corre; se pierde la señal, no la carga de imagen. La reproducción no mide sonido físico ni vibración.

Después de sustituir la conexión repetida del wrapper por bindProps, las cuatro sondas de Q75/Q76 vuelven a fallar con los mismos resultados mientras pasan los cuatro canarios Eidos. El helper no modifica esos comportamientos; las sondas de contraste se retiraron al terminar.

Reproducción de Q70–Q73 y L-152 en Cropper

Cinco sondas montaron Cropper Eidos en Chromium con ActiveUix, Soma y ActiveEidos reales, imagen PNG local de 200×100 y viewport 320×160. Pasaron 19 tests existentes mientras fallaron las cinco expectativas. Se retiraron las sondas; no se modificaron estos comportamientos.

Q70: el matcher de nombre accesible del thumb con role slider recibió nombre vacío en vez de Zoom; aria-label sólo aparece en el root compuesto. La referencia de cierre es APG Slider y el contrato thumbLabels de Soma. Q71: un botón cambia maxScale de 5 a 2 después del montaje; aria-valuemax del thumb sigue siendo 5. El constructor de Cropper copió los límites a ZoomPan una vez; el test no adjudica aún el mínimo ni clamps de transición.

Q72: pulsar + lleva la escala a 1.2. Activar disabled, partir de un botón previo al root y usar Tab llega a los controles, incluido reset. Enter en reset lleva aria-valuenow a 1 en vez de conservar 1.2. El puntero bloqueado por CSS no impide la activación mediante teclado; resetZoom omite el guard. Q73: down ID1 (100,100) y move ID2 (132,100) sobre Selection cambian crop.x de 0.25 a 0.35. Se termina con pointercancel ID1. Sólo la captura DOM está mockeada, no servicios UIX. No se acredita multi-touch nativo ni se da por reproducida la identidad de Handle/Viewport por similitud textual.

L-152: la sonda experimental con bind:ref quedó null, pero el gate posterior detectó que ref no es bindable en el wrapper Eidos. Ese enlace se retiró del harness: no es evidencia de fallo usando una API válida. Se conserva el ID existente como limitación de reenvío de bindables, confirmada por tipos, sin crear un duplicado ni cambiar su estado oficial en el ledger.

Q74: la justificación apg del Morfo afirma keyboard nudge; no hay binding ni handler de teclado en Selection o Handle, y el README lo difiere. Es una discrepancia de metadatos por lectura, no otro fallo de interacción reproducido. Se preserva el campo hasta adjudicar su corrección.

Reproducción de Q69

Crear ZoomPan con minScale 2 y maxScale 5 produce scale 1. En una segunda sonda, fijar viewport 200×100, ejecutar zoomTo(3) y reset también produce scale 1. Ambas expectativas de scale >= minScale fallan, mientras pasan 19 tests existentes de Cropper, incluidos cinco de la capa. Las sondas se retiraron y el comportamiento se conserva.

El mínimo declarado y la vuelta explícita a escala 1 en reset tienen contratos contradictorios; decidir semilla y reset es parte del lote posauditoría. No se infiere todavía un fallo visual de Cropper con minScale personalizado ni se cambia el mínimo efectivo de sus controles.

Reproducción de Q66–Q68

Cuatro sondas usaron AxialDrag con ActiveDom real, documento jsdom aislado por caso y captura mock. Q66: down de puntero 1 con buffer 5px, cancel(), otro down y move a 20px ejecutaron onDrag dos veces; el primer juego de listeners sigue enlazado y comparte el estado activo del siguiente gesto. Q67: down de ID1 y move de ID2 a 20px dispararon onDrag una vez; otra sonda inició drag con ID1 y up de ID2 disparó onDragEnd una vez, finalizando la sesión equivocada. El estado sólo guarda el ID capturado tras movimiento, no la identidad de seguimiento del down.

Q68: sin iniciar gesto, referencia 400, snaps [0.5,1] y activeSnap externo 0.5 produjeron offsetPx 200 y value01 0 en vez de 0.5. El primero lee snap externo; el segundo usa _offsetPx local. Drawer consume ambos, pero estas sondas de capa no acreditan su efecto visual en el componente.

Fallaron cuatro sondas y pasaron ocho pruebas existentes de AxialDrag y Drawer. Se retiraron las sondas; los defectos se conservan. Un canario separado verifica arrastre de ratón y captura nativos en Chromium para el camino normal. No reproduce todavía los casos multi-touch de Q67.

Reproducción de Q63–Q65

Cinco sondas ejecutaron Gesture con un documento jsdom aislado por caso, ActiveDom real y captura de puntero mock. Q63: down de puntero 1 con buffer 5px, cancel() y move a 20px ejecutaron onDrag una vez; cancel no cerró la sesión pendiente. Q64: down de 1 y down de 2 antes del buffer, seguido de move de 2 a 20px, ejecutaron onDrag dos veces. Tras up de 2, down de 3 y move a 20px volvieron a ejecutar dos callbacks; quedó un juego de listeners anterior. ActiveDom.dispose no limpia estos listeners por el gesto.

Q65 se midió con performance.now fijo en cero, eliminando velocidad y proyección. Drag right de ancho 400px, snaps [100,200,300] y sequential true asentó 100; el siguiente down/move/up a 300 asentó 300 en lugar del vecino de 200px. Rotate con centro (200,200), snaps [0,90,180,270], sequential true y wrap false asentó 90; el siguiente gesto al punto izquierdo asentó 270 en lugar de 180. Ambos pointerdown borran activeSnapPoint antes del resolver. Los consumidores actuales de Gesture no configuran snaps secuenciales; Drawer con snaps usa AxialDrag y no se adjudica a ese camino el defecto.

Fallaron las cinco sondas y pasaron los 65 tests existentes. Se retiraron las sondas y se conservaron estos comportamientos. La evidencia no valida captura ni multi-touch nativos; las pruebas de cierre deben añadirlos.

Reproducción de Q60–Q62 y L-110

Cuatro sondas temporales montaron Knob Eidos en Chromium. Con min 20, max 100 y value omitido, aria-valuenow fue 0, frente al default min del tipo (Q60). Con value 20 y step 0, ArrowRight publicó aria-valuenow NaN (Q61). Para Q62, el dial de talla sm tenía Label compuesto. Pointerdown cerca del centro superior del dial y pointermove al punto situado dos píxeles antes de su borde derecho, a la altura de su centro, produjeron 75 en lugar de 85 para min 0, max 100, step 5 y sweep -135..135. El pivote de Gesture.rotate se mide sobre el root completo, incluido Label: su centro vertical fue 57,25 px. Los eventos de esta sonda fueron sintéticos, con cajas DOM reales; no se validó captura de puntero. Se inspeccionó la captura del indicador tras el gesto. La cuarta sonda pulsó Label con Playwright y obtuvo document.body como elemento enfocado, en lugar de Control (L-110). Fallaron las cuatro mientras pasaron trece pruebas válidas. Se retiraron las sondas; esos comportamientos se preservan.

Reproducción de Q57–Q59

Tres sondas temporales montaron Splitter Eidos con paneles 25/50/25 y dos separadores. El primero no emitió aria-controls, pese a que el panel tenía un ID real (Q57). En layout horizontal, su caja es vertical y aria-orientation emitió horizontal (Q58); la captura se inspeccionó. Para Q59, una escritura externa fijó 40/35/25. Enter colapsó a 0/75/25 y un segundo Enter produjo 25/50/25, en lugar de 40/35/25. Las tres sondas fallaron mientras las seis pruebas normales pasaron. Se retiraron las sondas; no se cambió runtime. Son discrepancias frente al patrón APG, que advierte que la revisión de su ejemplo aún está pendiente; no se presentan como resultados de una prueba con lector de pantalla.

Reproducción de Q55–Q56

La primera sonda montó ScrollArea Eidos de 200 × 120 px con contenido 600 × 600 px y ambas barras. Tras medir overflow, aplicó --scroll-area-thumb-min-size:64px y desplazó el viewport a scrollTop=480. El track vertical midió 112 px; el thumb mantuvo height inline de 22,4 px, pero su caja pintada midió 64 px y excedió el final de la barra en 41,59375 px. La captura mostró el recorte del thumb en el borde de la raíz. La segunda sonda pasó las mismas dimensiones como objeto style: la raíz midió 0 px de alto. Fallaron ambas aserciones y pasaron las otras cinco pruebas. Las sondas se retiraron; canario permanente y suite del provider volvieron a pasar 5/5. No se alteró pintura, fusión de estilos ni cálculo.

Reproducción de Q52–Q54

Dos sondas temporales montaron el mismo harness Eidos en Chromium. Tras la medición inicial, desplazar el viewport RTL a scrollLeft=-800 produjo 0 celdas; la captura confirmó el viewport en blanco. En un montaje nuevo, scrollToCell(20,10,{rowAlign:'start',columnAlign:'start'}) llevó scrollTop a 480 px, pero scrollLeft permaneció en 0. La tercera sonda creó un provider de 2 × 2 con claves únicas por eje y obtuvo a|b|c, a|b|b|c, a|c, a|b|c; falló la aserción de cuatro claves únicas. No se montó un consumidor con esas claves: su posible aborto de render queda por reproducir. Las tres sondas se retiraron, el runtime permanece intacto y el canario permanente conserva la cobertura válida de LTR y del anclaje inicial RTL.

Reproducción de Q47 y evidencia de Q48–Q51

El canario temporal montó Table Eidos con selección múltiple, dejó Ada sin seleccionar y pulsó el botón Open de su celda. La acción se ejecutó y aria-selected de Ada cambió a true. La aserción fallida se retiró; la prueba permanente comprueba render, sort, selección, detalle y la acción por separado. El README Eidos conserva mediciones anteriores para Q48 y Q49, describe la discrepancia de tipos Q50 y señala la falta de feedback visual Q51. No se cambió ningún handler, tipo público ni estilo.

Reproducción de Q45 y evidencia de Q46 y L-027

El canario temporal montó GridList Eidos con selección múltiple, marcó beta y alpha, y pulsó el botón Open dentro de alpha. El contador de acciones llegó a 1, pero el valor enlazado cambió de beta,alpha a alpha. La aserción fallida se retiró; el canario permanente verifica la acción y la selección por fila y checkbox por separado. El README Eidos conserva la medición anterior de doble hover, y el ledger mantiene L-027 diferido. No se modificó el handler ni la receta visual.

Reproducción de Q43 y evidencia de Q44

El canario temporal montó TreeGrid Eidos con src expandido e index dentro de RowChildren. Tras pulsar la fila index, el valor enlazado terminó en src, no en index: el handler del padre procesa el mismo clic después del hijo. La aserción fallida se retiró del test permanente, que cubre expansión, estructura y selección de una fila hermana. El README Eidos conserva las mediciones anteriores de hover para Q44; esta pasada no repitió la captura de píxeles. No se cambió ningún handler ni estilo.

Reproducción de Q41 y evidencia de Q42

Un canario temporal montó TreeView Eidos con una rama src abierta y una hoja index. Después de enfocar BranchControl, ArrowDown dejó document.activeElement en ese control, en vez de moverlo a la hoja. La aserción fallida se retiró; la prueba permanente de expansión, selección y tamaño pasó junto con los cuatro tests Soma (5/5). El mismo DOM coloca data-archetype="item" en el Branch que envuelve la fila y sus hijos; el README Eidos conserva la medición visual previa del velo extendido. No se modificó la navegación ni la receta.

Reproducción de Q40

Una sonda temporal montó NavTree Eidos real con tres nodos cuyos IDs eran a, a#root/2 y a. El provider avisó del duplicado a, pero la clave derivada del tercer nodo ya pertenecía al segundo; Svelte lanzó each_key_duplicate para los índices 1 y 2. Se retiró la sonda y la prueba permanente de navegación pasó 1/1. No se cambió el algoritmo.

Reproducción de Q39

Una sonda temporal en el test del provider asignó pageSize=0 con 95 elementos. Las aserciones de totalPages=0 y pages=[] pasaron; la de pageRange={start:0,end:0} falló al recibir {start:1,end:0}. Se retiró la sonda y la suite normal de Soma y Eidos pasó 4/4. No se ha decidido si el cero debe rechazarse, normalizarse o producir un rango vacío.

Reproducción de Q38

Una sonda temporal creó el provider y un Link con href='#intro', cambió la fuente reactiva a '#usage' y dejó que Svelte actualizara. El targetId y activeId siguieron en intro, aunque el wrapper lee el href actual al construir las props del <a>. Pasaron la sonda y los dos tests existentes de IO (3/3); se retiró la sonda. La divergencia entre atributo y destino observado requiere caracterizar el consumidor completo antes de elegir si se prohíbe el cambio o se re-registra.

Reproducción de Q37

La prueba permanente de Breadcrumb montó el Ellipsis decorativo real en Chromium y confirmó role="presentation" junto a aria-label, mientras el botón interactivo conservó type="button" y aria-haspopup="menu". Pasaron 4/4 tests de Eidos/Soma. La guía WAI-ARIA sobre ocultar semántica establece que los navegadores ignoran role="presentation" cuando el elemento tiene una propiedad ARIA global como aria-label. Queda por medir el árbol accesible real y decidir cuál de las dos semánticas pretende UIX; no se cambió el Morfo ni el render.

Reproducción de Q35 y Q36

Dos sondas temporales montaron el SkipLink real en Chromium. Al hacer clic con un onclick de consumidor, el foco pasó al <main> pero el callback registró cero llamadas (Q35). Un segundo enlace apuntó a un <main> con display:none: el foco no llegó a ese nodo y el tabindex="-1" que el wrapper acababa de añadir siguió presente (Q36). Pasaron ambas sondas y la prueba normal de foco (3/3); después se retiraron las sondas. La prueba permanente cubre el camino visible, su geometría enfocada en Chromium y la retirada del tabindex después del blur. No se cambió el handler.

Reproducción de Q34

Una sonda temporal en el test cliente real de Sticky montó un scroll-host de 200 px, una sección de 280 px con centinela y box sticky, y contenido posterior. Con offset=12, al desplazar 100 px el box estaba pegado y result.stuck era true. Tras desplazar 400 px, el límite inferior de la sección y el box estaban fuera de la vista (getBoundingClientRect().bottom < 0 para ambos), pero result.stuck seguía en true después de esperar al observador. Pasaron la sonda y el test existente (2/2); se retiró la sonda. La referencia de Chrome usa un segundo centinela para detectar la salida de la sección. No se modificó el runtime ni se infirió la solución para edge=bottom sin prueba.

Reproducción de Q32 y Q33

La sonda temporal montó Section con size="sm", containerSize="sm", decor="glow", display="flex" y width="50%" en Chromium, junto a un Section as="footer". El primero produjo un <section> con Container, Background.Pattern y Section.Header interiores, padding superior de 32 px, display: block y --box-width: 100%; el segundo produjo <footer>. Pasó 1/1 antes y después de corregir sólo documentación, y se retiró. No se alteró el runtime ni se infirió cómo usan defaultElement sus consumidores.

Reproducción de Q31

Una sonda temporal cargó eidos/index.css y montó Container en un padre de 1000 px. Con size="sm", maxWidth={320} y width="50%", la medida computada permaneció en 480 px; marginLeft={24} y marginX={24} tampoco desplazaron los márgenes que fijó align. La misma sonda confirmó el root as="section", el padding por token y su override con paddingX={0}, size="full" sin límite de ancho y container-type: inline-size. Pasó 1/1 y se retiró. La decisión de compatibilidad queda para el lote de defectos posterior a la auditoría.

Reproducción de Q30

Una sonda temporal cargó eidos/index.css y montó dos Group grow de 200 px con hijos Box de 30 y 50 px. En fila, ambos hijos acabaron con el mismo ancho; en columna conservaron anchos diferentes. La misma sonda comprobó el wrap por defecto, attached en ambos ejes e inline-flex; pasó 2/2 y se retiró. El tipo permite direction="column", y su JSDoc describe grow como igualación de ancho. La corrección exige decidir si esa promesa se mantiene para columna o se acota a fila; ButtonGroup vertical también compone Group y debe cubrirse en el lote posauditoría.

Reproducción de Q29

Una sonda temporal montó <Stack width="200px" align="start" alignContent="end"> con un hijo Box de 20 px. Chromium computó flex-wrap: nowrap y align-content: end, pero el hijo permaneció en el inicio del eje transversal; la prueba pasó 1/1 y se retiró. La propiedad CSS no tiene efecto en contenedores flex de una sola línea, según MDN. Esto confirma una prop pública inerte en el uso normal de Stack; no se ha decidido retirar la prop heredada ni exponer wrap, porque ambos cambian el contrato.

Reproducción de Q28

Una sonda temporal de Vitest client montó cuatro Box absolutos con width={20} y left={10} o right={10} dentro de contenedores de 200 px en LTR y RTL, cargando eidos/index.css. En LTR, ambos quedaron a 10 px del borde físico solicitado. En RTL, ambos quedaron a 170 px de ese borde. Las dos aserciones RTL fallaron, y la sonda se retiró sin modificar la receta. Antes de corregir, hay que fijar si esos nombres garantizan lados físicos o si la API quiere propiedades lógicas con otros nombres; también probar inset, insetX y insetY para evitar regresiones.

Reproducción de Q18–Q19

Una sonda temporal de Vitest client montó Highlight sin importar Mark, con el CSS base de Eidos. <Highlight text="A B" query=" " /> produjo un <mark>. Otra instancia con query="world" mostró fondo nativo amarillo rgb(255, 255, 0) y padding-inline-start: 0px. La primera aserción de la sonda esperaba fondo transparente y falló; se corrigió para fijar la salida observada, pasó 1/1 y se retiró. No se modificó el runtime.

Reproducción de Q14–Q16

Una sonda temporal de Vitest client montó un Avatar con Avatar.Image (src explícito) y Avatar.Fallback delayMs={10000}. Durante loading, el fallback ya estaba en el DOM. Al despachar load en el <img>, el callback onImageStatusChange del root recibió loaded y el de Avatar.Image no recibió nada. El root no tenía demora propia: esto aísla la prop de la part.

En el mismo harness, <Image src="/audit-image.png" alt="Audit"><Image.Img /></Image> montó un <img> sin atributo src y con alt="". El provider conservaba data-status="loading"; la part no había heredado los atributos nativos que su tipo y README prometen. Un tercer caso usó la ruta abreviada con fallback="Loading image": tras despachar error, el fallback desapareció y [data-image-error] quedó sin texto. La sonda pasó 1/1 afirmando esas salidas observadas y se retiró; no se modificó el runtime. La primera versión de la sonda esperaba que Image.Img no se montara, hipótesis refutada: el nodo sí existe, pero carece de src.

Reproducción de Q11

La prueba permanente de paridad geométrica pasó 12/12 casos: cuatro textos a dos anchos en Chromium, Firefox y WebKit. El harness temporal de carga tardía usó FontFace con la Lora local, midió primero con fallback y luego añadió la fuente. En WebKit, un ancho de 124 px dio 4 líneas antes y 5 tras cargarla; después de invalidar la familia, el motor siguió contando 4 en varias ejecuciones. Esperar un frame y 150 ms no bastó; otra ejecución con una medición canvas adicional antes de la carga pasó. El harness no montó SText ni reprodujo el evento loadingdone, por lo que la causa sigue abierta. Se retiró la prueba temporal para no dejar el gate rojo y no se cambió el runtime.

La prueba posterior monta <SText> con @font-face CSS, escucha el evento loadingdone de su familia y lee bind:lineCount. A 124 px, Lora cambió el DOM de 4 a 6 líneas y el binding acabó en 6 en Chromium, Firefox y WebKit (3/3). Así, la discrepancia del harness parcial no se reproduce en el flujo real del componente. La prueba queda como regresión; Q11 no autoriza un cambio del algoritmo ni del hook sin un fallo reproducible en un consumidor real.

Reproducción de Q01-a y Q01-b

Dos pruebas temporales en Vitest client (Chromium) reprodujeron los riesgos de ActiveUix. Para Q01-a, announce('old', 'polite', 25) seguido de announce('new', 'polite', 0) dejó la región vacía al avanzar 25 ms: el timer del primer anuncio borró el segundo. Para Q01-b, se hizo attach a un mismo ActiveApp, se destruyó la primera instancia y se hizo otro attach. Al invocar el canal announce del motor compartido, el spy de la primera instancia recibió el mensaje y el de la segunda no recibió ninguno.

Las pruebas temporales se retiraron para no dejar la suite roja. No se cambió el runtime: corregir cualquiera de los dos casos altera el comportamiento de anuncios y pertenece a un lote de defectos distinto de las extracciones neutrales autorizadas.

Reproducción de Q01-c y cobertura de L-155 (2026-09-23)

Una prueba temporal de Vitest client (Chromium) adjuntó ActiveUix a un ActiveApp, llamó a announce('First root', 'polite', 10_000) y comprobó que app.timers contenía uix:announce:polite. Tras uix.dispose(), la aserción de que la clave hubiera desaparecido falló: el temporizador seguía pendiente. La prueba temporal se retiró y no se cambió el runtime. Este resultado fija la deuda de limpieza de attach; no demuestra que el callback borre un nodo de otra instancia, ya que conserva la referencia al nodo anterior.

La cobertura permanente de L-155 ahora comprueba attach con anuncio por defecto en el sink compartido, el opt-out de defineUixServices y el opt-in del motor Sema desnudo. La suite dirigida pasó de 101/101 a 104/104 pruebas.

Reproducción de Q05

Una prueba temporal de Vitest client (Chromium) creó ActiveUix con prefs.storage: false, dom: false y una ruta inválida en events.overrides.runtime. Se sustituyó matchMedia para contar los listeners del watcher de preferencias. El arranque lanzó como se esperaba, pero había registrado dos listeners change y no retiró ninguno (1 prueba fallida, 32 omitidas por filtro). Tras retirar la prueba, la suite ActiveUix pasó 32/32. Esto confirma la falta de rollback en ese camino de error.

Otra prueba temporal creó un iframe con <html dir="rtl"> mientras el documento global tenía dir="ltr"; pasó frame.contentWindow como dom.targetWindow y desactivó la proyección. La preferencia direction inicial fue ltr en vez de rtl (1 prueba fallida, 32 omitidas por filtro). La suite ActiveUix volvió a pasar 32/32 al retirar esta prueba. No se ha comprobado todavía una diferencia de matchMedia ni de idiomas entre ventanas; la divergencia de dirección sí demuestra que el bootstrap lee el documento ajeno al destino configurado.

Reproducción de L-126

En este checkout, dos pruebas temporales de Vitest server crearon dos raíces createActiveEidos con el mismo ActiveDom, document.head y los mismos IDs de estilo. Después de construir la segunda raíz, first.dispose() eliminó #uix-eidos-static y dejó data-theme sin valor, aunque second seguía vivo. Ambas pruebas fallaron por separado. Se retiraron del árbol para conservar la suite verde: la corrección cambiaría la semántica de ownership del runtime y requiere un lote de defecto autorizado expresamente. La suite Eidos existente volvió a ejecutarse después de retirar las pruebas temporales.

Reproducción de L-153

Una prueba temporal pasó al resolver una señal emerge con channels: ['haptic'] y overrides: { haptic: { intensity: 0.5 } }. resolveSignature() devolvió la firma parcial { intensity: 0.5 }. Al entregarla a HapticChannel.handle(), la promesa rechazó con TypeError: Cannot read properties of undefined (reading 'pattern') en kindToPattern(), antes de llegar a la defensa de valores numéricos S-31. El resultado fue 1 test fallido y 30 pasados en resolver.test.ts. Se retiró la prueba temporal y las dos suites dirigidas del resolver y el canal háptico volvieron a pasar (43/43). No se ha elegido aún si una firma incompleta debe rechazarse, silenciarse o completarse; esa decisión cambia el contrato y queda separada de las extracciones neutrales.

Reproducción de L-140

Un harness temporal montó el Select real con value={['a']}, un item value="a" label="Alpha" dentro del Content cerrado y un Select.Value sin children. Antes de abrir el portal, [data-select-value] mostró a en vez de Alpha (1 prueba fallida en Vitest client, Chromium). Se retiró el harness y la suite existente del provider pasó 13/13. La variante con children personalizados en Select.Value no se ha reproducido todavía; no se ha cambiado el comportamiento de selección ni el registro de etiquetas.

Herramientas y mantenimiento

ID Área Evidencia de código Disposición propuesta
Q03 Gate de tipos check-gate.ts pedía salida humana mientras parseaba marcadores machine Corregido: solicita salida machine; check:gate pasó dos veces con el ledger existente
Q04 ActiveUix Attach distingue motores prestados y fallbacks propios; la cabecera y los tests ya reflejan ambos casos No extraer una vista común sin ahorro probado: podría volver eager los servicios de ActiveApp
L-157–L-159 Guards de texto Varias defensas dependen de cómo se parte una línea, se cita una cadena o se declara un tipo Migrar sólo invariantes que lo requieran a AST, con corpus contado y mutaciones negativas
Comentarios largos ActiveUix/Sema La historia de decisiones ocupa una parte notable de coordinadores críticos y mezcla invariantes activos con episodios pasados Mover historia a documentos de proceso cuando se toque una zona; conservar junto al código sólo contrato y razón vigente

Candidatos que no son defectos

Esta tabla conserva las condiciones de entrada de la auditoría. Los pilotos de repetidor, dimensiones de pickers y serialización de longitudes ya se ejecutaron en astra; sus resultados y pruebas figuran en el plan. Las demás filas siguen pendientes de una comparación específica antes de extraer código.

Área Repetición observada Condición de entrada
CssField/NumberField SpinPressRepeater + factory son 65 líneas idénticas por provider CssField no importa sus trigger providers; faltan pulsación sostenida, cancelación, salida y disabled
Tres pickers Eidos Resolución visual, dimensiones y composición Popover/PickerShell idénticas Faltan pruebas directas de wrappers Eidos y de SSR/estilo compuesto
Longitudes en Content Eidos Cinco overlays y cuatro pickers comparten bloques exactos de serializeLength y dimensionVar, con dos políticas de cero formatLayoutLength no es equivalente; hace falta una matriz de caracterización antes de una ayuda pura
Longitudes de overlays Eidos Las 30 variables llamaban serializeLength antes de que dimensionVar la volviera a llamar Resuelto en astra: se retiró la primera llamada; matriz CSS, contratos Eidos y tipos pasaron
ContextMenu/DropdownMenu Morfo 216 líneas normalizadas coinciden; difieren Trigger, teclado, retorno de foco y dirección No hay comparación compilada de esas diferencias; un factory puede ocultarlas o romper tipos literales
Dialog/AlertDialog Eidos Parte del content visual se solapa Focus, role, descripción y acciones siguen siendo contratos separados
Calendar/RangeCalendar Ya comparten CalendarNav y gridWalk Límites de vista, título y selección de rango tienen contratos distintos; no procede más extracción
ActiveUix standalone/attach Adquisición y lectura de servicios repetidas Los modos tienen ownership, preboot y pereza diferentes
Blocks compuestos Sólo coincide el ensamblado de namespace de article-grid y feature-split Es superficie de API tipada, no lógica; un constructor genérico ocultaría exports y ahorraría poco
VirtualList/VirtualGrid Eidos Root y CSS de cromo casi idénticos al sustituir identidad y tokens; rangos Soma diferentes Q52–Q53 bloquean paridad RTL; hooks/forwards nuevos requieren ahorro probado y tests de ambas familias.

La regla es deliberada: un defecto se corrige en su propio lote con una prueba que lo reproduzca; una duplicación sólo se extrae cuando puede demostrar que conserva el resultado actual. Ninguno de estos apuntes autoriza cambios de comportamiento.

T01 — presupuesto del canario ZoomPan (infraestructura de pruebas)

Fecha: 2026-09-26. El gate termina con 504 archivos/5583 tests aprobados y un archivo/test fallido. El último click en Reset del canario ZoomPan recibe Timeout 107ms exceeded tras los pasos de ancla/pan y dos capturas, dentro de un presupuesto de 30s. No falló una aserción numérica ni se adjudica una regresión del motor por ese diagnóstico.

La recogida de capturas pasa a ser opt-in mediante VITE_UIX_AUDIT_SCREENSHOTS=1. La prueba normal mantiene todos sus clicks nativos y aserciones con 30s; no se usa force ni un timeout mayor. Cierre: dirigido con capturas, inspección de ambas imágenes y gate completo sin capturas de este canario. Validación final: 12/12 tests dirigidos con capturas, ambas inspeccionadas; gate completo 505/505 archivos y 5584/5584 tests. T01 queda cerrado. Es deuda de pruebas separada de la cola funcional; no permite dar por cerrado Q69 ni otro defecto del sistema.

T02 — escala de las capturas de auditoría (infraestructura de pruebas)

Fecha: 2026-09-27. El panel mide 392px en su DOM y body/section tienen zoom=1, pero el iframe de Vitest mide 156.88px frente a sus 414px CSS. Las capturas guardadas tenían 158px de ancho. La sonda que exige proporción 1 falla; no demuestra que la receta reduzca el componente.

Con sólo browser.ui=false el iframe pasa a 332.68px: la sonda sigue fallando mientras pasan 18 canarios funcionales. El contexto Playwright exterior de 1280×1400 mantiene la escala 1: la configuración temporal pasa 19/19 tests en cinco archivos. La configuración mantenida scripts/audit-visual.config.ts repite 19/19, reutilizando plugins, aliases, descubrimiento y browsers normales. Se inspeccionan ocho capturas nuevas de ZoomPan, Cropper, ImagePicker y ImageAdjustments. Se retira la sonda y los artefactos de fallo se conservan en tmp.

El gate previo de ImageAdjustments pasa 506 archivos/5594 tests. El gate con la herramienta mantenida repite 506/506 archivos y 5594/5594 tests: tipos de src/scripts sin errores, 893 documentos sin incidencias y smoke apps/base 27/27. T02 queda cerrado como infraestructura visual, separada de la cola funcional; no cierra Q69–Q78.

Powered by TurnKey Linux.