24 KiB
L03.1 — Persistencia y restauración del contexto de listados (V05)
Fecha: 22 de octubre de 2026. Pilotos: Activos, Recursos y Personas. Referencias: L01, L02.2, L02.3, rutas y listados.
1. Resumen ejecutivo
Los pilotos añaden ordenación y página a su consulta navegable. Personas incorpora también
búsqueda y Solo contactos a la URL, conservando su fallback local cuando está embebida fuera
de una ruta propia. El tamaño de página y la visibilidad mantienen sus preferencias existentes.
El retorno utiliza coordenadas e identidad de foco en la entrada de history.state, sin DTO,
referencias DOM persistidas ni otra caché remota.
Activos conserva su ficha completa; Recursos conserva selección URL y panel/diálogo contextual.
No se añaden rutas, endpoints, dependencias, cambios de backend o modificaciones vendor.
Los archivos previos del usuario en docs/auditorias/ se conservan sin modificación.
2. Auditoría del estado anterior
| Consumidor inspeccionado | URL antes de L03.1 | Estado local / persistido | Desmontaje, retorno y acceso directo anteriores |
|---|---|---|---|
| Activos | q, template; ficha con esos parámetros |
Sort/página en useEntityTable; selección contextual local; tamaño global y columnas en localStorage |
Ficha completa desmonta listado. Atrás recuperaba filtros, pero sort ascendente/página 1/foco BODY; horizontal se perdía. |
| Recursos | q, template, selección en pathname |
Sort/página locales; apertura de panel y diálogo explícito temporales; tamaño persistido | Seleccionar no desmonta tabla. Cierre compacto usa historial; cierre inline oculta panel conservando selección/ruta. Recarga mantenía filtros, pero no sort/página. |
| Personas | Selección en pathname | Búsqueda y Solo contactos en componente padre; sort/página en tabla; tamaño persistido | Detalle sustituye tabla. Padre podía conservar filtros, pero se perdían al reconstruirse; sort/página se reiniciaban. |
| Puestos | Ficha en pathname | Búsqueda y editor locales; sort/página locales; tamaño persistido | Hay ficha completa; la reconstrucción pierde el estado local. Pendiente L04. |
| Perfiles | q, archived, perfil/configuración en pathname |
Sort/página locales; diálogos locales; tamaño persistido | Entorno/configuración sustituye vista. URL reproduce filtros/destino, no todo el contexto. Pendiente L04. |
| Compañías | q, activeOnly, companyId |
Tabla/controlador y selección local según consumidor; tamaño persistido | Cambiar espacio reconstruye directorio. Filtros URL sobreviven; sort/página/scroll no tienen contrato completo. |
| Catálogo de activos | q, tab |
Sort/página y editor/detalle/historial locales | Los diálogos no cambian ruta; cambiar catálogo/sección puede desmontar. URL no representa sort/página. |
| Sublistado de puestos de una persona | Ninguna consulta propia navegable | Datos de queries compartidas; controles locales del sublistado | Se desmonta con la ficha. No debe heredar la página/orden del listado de Personas. |
Los datos remotos proceden de TanStack Query: inventarios/catálogos de Activos y Recursos, compañía y referencias de puestos/perfiles. No son preferencias ni contexto de navegación. Un panel/diálogo no equivale a abandonar la página; no se cambia ese contrato para uniformar. Esta matriz combina inspección del código y L01. Los consumidores ajenos a los tres pilotos no se presentan como una nueva validación visual con listados poblados.
La nueva reproducción previa, con 15 compañías y 50 personas/51 activos/66 recursos, conserva
las medidas históricas de L01. En esta instalación hay 40 portátiles con q=AUDIT-ASSET:
| Dimensión | Antes de abrir ficha | Atrás anterior, 1440×900 | Atrás anterior, 390×844 |
|---|---|---|---|
| Orden / página | Descendente / 3 | Ascendente / 1 | Ascendente / 1 |
| Búsqueda / plantilla | AUDIT-ASSET / Portátil | Conservadas | Conservadas |
| Scroll X | 0 escritorio, 40 móvil | 0 | 0: pérdida de los 40 px |
| Scroll Y | 100 | 100 en esta muestra | 100 en esta muestra |
| Foco | Enlace AUDIT-ASSET-24 | BODY | BODY |
La muestra nueva no reproduce la pérdida vertical concreta de L01; las pruebas posteriores desplazan deliberadamente la ficha a otra coordenada para comprobar una restauración real.
3. Arquitectura del contexto
| Responsabilidad | Fuente única |
|---|---|
| Búsqueda/filtros/plantilla/sort/página | URL, únicamente en consumidores habilitados |
| Tamaño / columnas | Preferencias ya existentes |
| Scroll y origen del foco | history.state.metisListReturn, con versión y ámbito |
| Registros y referencias | TanStack Query y sus query keys existentes |
| Modalidad, selección y paneles | Contratos V01/V06 del módulo y Base UI |
@metis/core/client conserva su History API. useListLocation enlaza los nombres de
parámetros que aporta el consumidor; no conoce TanStack Table ni columnas. rdx-ui recibe
un EntityTableNavigation presentacional (values, onChange, ready), valida metadatos
y utiliza los modelos/callbacks nativos de TanStack Table. No se añade dependencia core→rdx-ui.
4. Contrato URL
| Parámetro de los pilotos | Validación / valor inicial |
|---|---|
q |
Búsqueda existente; límite de ruta 200 caracteres y semántica propia intacta. |
template |
Activos/Recursos: identidad conocida en el catálogo. Se conserva pendiente de metadatos; después se normaliza a Todo si no existe. |
contactsOnly |
Personas: true; ausente significa false. False/malformados se normalizan; no se añade a otros consumidores. |
sort |
ID existente y ordenable; admite characteristic-<UUID> estable. Acciones/columnas desconocidas vuelven al orden inicial. |
direction |
asc o desc, con columna válida. Una pareja inválida usa el orden inicial. |
page |
Entero decimal positivo, índice compartible desde 1, seguro en JavaScript; se acota al conjunto disponible. |
El orden inicial sigue las reglas existentes: declaración explícita, Nombre, primera columna ordenable o ausencia de orden. Se omiten sort/direction predeterminados y página 1. Se normalizan repeticiones de los parámetros de contexto; los parámetros ajenos no se sobrescriben. El parser del workspace sigue rechazando claves desconocidas y las rutas/identidades mal formadas.
La validación semántica de plantilla y contexto se realiza en el consumidor, una vez disponibles sus definiciones. El parser permite que esos valores lleguen a esa frontera sin convertir una página inválida o una columna retirada en una ruta de negocio nueva. Los demás contratos de validación HTTP y autorización no cambian.
Política aplicada: replace para búsqueda, filtros discretos, sort, página y normalizaciones.
Son variaciones del mismo espacio operativo; Atrás debe volver de ficha a listado, sin recorrer
cada pulsación/página de trabajo. Abrir otra sección o ficha mantiene push. La URL sigue
siendo compartible y reproducible aunque no se cree una entrada por cada ajuste.
Cambiar un filtro desde su control reinicia página; normalizar contactsOnly=false a ausencia
conserva una página solicitada válida, porque no cambia el conjunto filtrado.
5. Preferencias
Se conservan entity-page-size y asset-inventory-columns, sus formatos, límites, defaults y
tolerancia al almacenamiento bloqueado. No se trasladan a parámetros URL ni se borran IDs
antiguos. El enlace compartido utiliza el tamaño preferido del receptor y acota la página si
es necesario; no promete idéntica partición entre usuarios con tamaños distintos.
6. Contexto temporal de retorno
metisListReturn contiene versión 1, scope, URL de referencia, X/Y e identidad opcional de fila
y href. Los números se validan como finitos/no negativos y acotados; los destinos son internos.
Se conserva el resto de history.state, incluidos previous, create y propiedades ajenas.
URL y href admiten hasta 8192 caracteres serializados: una búsqueda válida de 200 caracteres
Unicode puede superar 2000 al codificarse. Este límite no amplía el contrato de búsqueda.
La captura se integra antes del push de la infraestructura existente y mediante focusin /
scrollend de los contenedores del listado. Así también se conserva el contexto antes de
recorridos nativos del historial. No se añade otro listener global independiente de popstate,
ni temporizadores de sondeo, ni almacenamiento de DTO o nodos DOM.
El ámbito incluye compañía y listado. Cambiar filtros invalida una posición incompatible; una corrección automática de sort/página no impide recuperar y limitar coordenadas del mismo conjunto filtrado. Los snapshots no se transfieren a otra compañía.
7. Evolución de useEntityTable
navigation es opcional; sin ella continúa el estado no controlado de los consumidores
existentes. Con ella, sorting y pageIndex efectivos se derivan de los valores externos y sus
callbacks escriben la consulta. El tamaño sigue siendo preferencia local persistida. También
se admiten initialSorting / initialPagination en el hook para inicialización explícita.
En modo controlado, actualizar paginación solo guarda localmente cambios de tamaño; no
mantiene una copia del índice externo.
Se conservan useReactTable, modelos de filtro/orden/paginación, selección, protección y
visibilidad de columnas. No hay otro algoritmo de ordenación ni paginación. Los controles
conservan aria-sort y aria-current. Cambiar sort reinicia la página desde su handler; cambiar
criterios la reinicia desde el controlador del consumidor. No hay dos fuentes activas de verdad.
La normalización de URL es una sincronización con una frontera externa y espera ready:
no acota página 3 a 1 por el conjunto vacío provisional del skeleton, ni elimina un campo
dinámico mientras falta el catálogo. La reducción confirmada del conjunto limita la página.
8. Consumidores piloto
- Activos: controlador conserva q/template;
AssetListrecibe navegación opcional y transmite el contrato a EntityTable. Los enlaces mantienen la consulta en la ficha completa. - Recursos: conserva q/template, selección en pathname y una sola tabla. Cierre inline sigue ocultando panel sin borrar ruta/selección; cierre compacto vuelve por History y su fallback directo conserva la consulta. No se introduce una ficha completa nueva.
- Personas:
usePeopleListContextencapsula URL y fallback embebido. Los enlaces y acciones de apertura conservan consulta; el botón Volver recupera la entrada del listado. El padre transmite frescura de compañía para no confirmar registros de una copia fallida.
9. Restauración de scroll y foco
El scroll vertical real es [data-slot=workspace-scroll]; el horizontal es el contenedor de
la tabla. Se espera disponibilidad de datos y composición relevante, fuentes listas y el
siguiente frame de layout. No se espera una cantidad fija de milisegundos.
Al volver se limita X/Y al máximo real. El foco vuelve al enlace del mismo data-entity-id;
si cambió su href por una normalización, se usa un control de esa misma fila. Si desapareció
o los datos no están verificados, se usa la búsqueda; no se selecciona otra fila arbitraria.
En navegación directa sin contexto se orienta al título, respetando interacción ya iniciada.
El enfoque usa preventScroll después de restaurar coordenadas.
BrowserLink navigation="return" reutiliza la entrada anterior solo si coincide con su
destino; sin historial apropiado, reemplaza por el listado. Conserva enlaces reales, clic
modificado, descarga y nueva pestaña. La ficha ausente conserva también acceso a Volver.
El contexto ya aplicado no se repite por un refetch. Si el usuario escribe durante la carga no se le roba el foco. Los diálogos siguen sus finalFocus; la restauración genérica no los enfoca ni abre. Recursos mantiene su instancia y Base UI conserva la orientación modal.
10. Datos y parámetros inválidos
Página inválida vuelve a 1; fuera de rango se limita a la última válida. Orden desconocido, no ordenable o dirección inválida vuelve al inicial. Una columna dinámica retirada solo deja de ser válida si tampoco existe en las instantáneas conservadas: no se elimina legado.
El vacío confirmado y los filtros sin coincidencias conservan L02.3. Error inicial y posterior no se confunden con desaparición. Un 503 posterior mantiene filas anteriores y contexto, pero no autoriza a usar esa caché como prueba para devolver el foco al registro. Personas recibe pending/ready/unverified del propietario de su consulta, sin una petición adicional.
11. Compatibilidad con Recursos y V01
Activos conserva el mínimo de contenedor 1144 px; Recursos 984 px. No cambian presupuestos, shell, rutas, intención de apertura, modalidad, observers ni reglas de resize. Un sublistado o picker no adopta la consulta del padre por usar EntityTable: debe habilitar navegación explícitamente. No se altera la selección provisional de los pickers ni V06.
12. Pruebas automatizadas
Las pruebas nuevas verifican parser/serializer, límites numéricos, IDs dinámicos, sort no ordenable, fallback, tabla controlada con filas/página efectivas, consumidor no controlado, History anterior/Adelante/retorno directo, propiedades ajenas y validación/aislamiento de scope. Las pruebas de rutas se ajustan al nuevo límite entre validación sintáctica y semántica.
scripts/list-context-ui.test.py utiliza Playwright MCP Docker/Chromium con sesión ordinaria,
login real y datos sintéticos. Cubre matriz de retorno, recarga/enlace compartido, parámetros
inválidos, resultados reducidos, Personas/contactos, Recursos contextual/directo, desaparición,
refetch retenido, carga inicial retenida al recargar, compañía distinta, Escape sin navegación,
campo dinámico retirado y errores simulados de Activos/Compañía. No escribe entidades.
Los comandos y resultados reales finales se recogen junto a las evidencias; no se equiparan las comprobaciones dirigidas correctas con las suites globales que mantienen deuda previa.
Ejecuciones y resultados
| Comprobación ejecutada | Resultado real |
|---|---|
Typecheck dirigido de @repo/rdx-ui, @metis/core, @metis/assets, @metis/resources, @metis/companies |
Los cinco correctos; Compañías repetido tras el ajuste final de canonicalización. |
pnpm --filter @repo/rdx-ui test |
11/11 archivos correctos, incluido contrato controlado/no controlado. |
pnpm --filter @metis/core test |
3/3 archivos correctos; History, aislamiento y URL Unicode incluidos. |
pnpm --filter @metis/assets test |
27/27 archivos correctos. |
pnpm --filter @metis/resources test |
11/11 archivos correctos. |
pnpm --filter @metis/companies test |
40/41 archivos correctos. Falla companies-page.test.tsx, con las dos expectativas SSR ya documentadas en L02.3: Personas (1) y mensaje de acceso de persona. Rutas del workspace pasa. |
| Biome y ESLint dirigidos a los 28 archivos TypeScript modificados/nuevos | Correctos, sin fixes ni supresiones; repetidos tras el último ajuste. |
pnpm typecheck |
Falla en vendor calendar.tsx:165, TS2375 sobre locale y exactOptionalPropertyTypes, ya documentado. No se modifica vendor. |
pnpm lint |
Falla: 15 errores, un warning y cuatro infos, como L02.3. |
pnpm check |
Falla: 167 errores, un warning y cuatro infos; L02.3 registraba 168 errores. Los archivos propios del cambio pasan la comprobación dirigida. |
pnpm test |
7/8 tareas correctas; falla únicamente la tarea de Compañías anterior. Se ejecuta contra otra MariaDB temporal metis_desk_test, aislada de la instalación del navegador. |
pnpm build |
2/2 tareas correctas; permanece el aviso de chunks superiores a 500 kB. |
| Playwright L03.1 | 14 métodos distintos comprobados: ejecución principal 12/12, revisión 4/4 (incluye el nuevo caso 13) y revisión de Personas 3/3 (incluye el nuevo caso 14). Los métodos repetidos no se cuentan dos veces. |
| Regresiones Playwright L02.1 / L02.2 / L02.3 | 8/8, 8/8 y 8/8 métodos correctos, respectivamente. |
| Otras regresiones Playwright | 9/9: navegación, capacidad de inventarios, contraste seleccionado, asistentes y políticas de foco. |
Son 47 métodos distintos de navegador verificados, contando pilotos y regresiones. No se afirma una ejecución única de los 14 métodos nuevos: se conservan los logs de cada lote. Las pruebas comprueban filas reales, aria-sort/current, filtros, coordenadas y elemento activo; no se limitan a comparar la URL.
Para repetirlos, preparar la instalación y el volumen según Playwright MCP, mantener vivo su preparador y utilizar el archivo privado de sesión que este proporciona:
METIS_PLAYWRIGHT_STORAGE_STATE=.artifacts/playwright/authenticated-<id>/session.json \
python3 scripts/list-context-ui.test.py
El script reutiliza los helpers MCP existentes, no inicia servicios y bloquea escrituras HTTP de negocio durante la inspección. Requiere acceso al Docker configurado y sesión ordinaria sintética activa; no sirve una sesión de producción. No se versionan archivos de sesión.
13. Evidencias Playwright
Directorio local ignorado: .artifacts/playwright/l03-1/, con baseline before/, capturas y
mediciones nuevas en after/, resultados URL/DOM/X/Y/foco, consola, logs e índice. Las imágenes
locales no son evidencia portable por sí solas: compartir expresamente PNG/mediciones sin
sesiones. Se abren capturas representativas antes de concluir la revisión visual.
La matriz incluye 1920×1080, 1440×900, 1366×768, 1024×768, 768×1024, 390×844 y 320×640. El recorrido inicial corregido a 1440 y 390 conserva Nombre descendente, página 3 y el enlace AUDIT-ASSET-24. Se restauran 100 px verticales y, en móvil, 40 px horizontales; la suite ampliada usa 150 px y desplaza la ficha a otra coordenada para comprobar la recuperación.
| Resolución | Sort / página al regresar | Scroll X/Y antes y después | Foco al regresar |
|---|---|---|---|
| 1920×1080 | Nombre descendente / 3 | 0 / 0: la tabla cabe en altura | Mismo enlace de registro |
| 1440×900 | Nombre descendente / 3 | 0 / 150 | Mismo enlace de registro |
| 1366×768 | Nombre descendente / 3 | 0 / 150 | Mismo enlace de registro |
| 1024×768 | Nombre descendente / 3 | 0 / 150 | Mismo enlace de registro |
| 768×1024 | Nombre descendente / 3 | 40 / 150 | Mismo enlace de registro |
| 390×844 | Nombre descendente / 3 | 40 / 150 | Mismo enlace de registro |
| 320×640 | Nombre descendente / 3 | 40 / 150 | Mismo enlace de registro |
En las siete medidas coinciden consulta, filas e identidad del foco, sin overflow horizontal global. Se alterna Atrás y «Volver a activos»; a 1440 también se comprueba Adelante y otro Atrás. No se exige scroll vertical inexistente en 1920. Personas recupera contactos, descendente, página 2, X=40/Y=27 y enlace de origen en 390×844. Recursos se verifica a 1920 inline y a 390 compacto, con misma tabla, consulta y orden/página después del cierre.
La recarga con GET retenido en 320 mantiene page=3 y el orden descendente durante skeleton:
no se fuerza una página efectiva antes de que existan filas. Al liberar los datos vuelve a
página 3, X=40/Y=150 y al enlace original. Tras un 503 posterior, Activos y Personas conservan
la tabla/contexto, pero enfocan búsqueda, porque la existencia del registro no está verificada.
Índice local: evidencias y logs.
Mediciones de referencia: after/measurements-2776681.json (12 métodos),
after/measurements-2784654.json (revisión y tamaño persistido) y
after/measurements-2797155.json (Personas y canonicalización de false).
Capturas abiertas e inspeccionadas: baseline y retorno móvil, Personas, carga retenida a
320, retorno de Personas sin frescura y cierre contextual de Recursos a 390.
Se utiliza una instalación propia desechable metis_desk_test, con 15 compañías, 50 personas,
51 activos y 66 recursos. No se precisan nuevos puestos/perfiles para estos pilotos; sus
escenarios previos se mantienen como regresión. Las retiradas de registros/campos y los 503
son interceptaciones GET explícitas, no borrados ni incidencias espontáneas del servidor.
14. Regresiones
Se ejecutan suites dirigidas y regresiones de L02.1, L02.2, V12, navegación responsive,
capacidad de inventarios, contraste seleccionado, asistentes, acciones y V06. Se conserva
la carga estable y la tabla durante refetch. Se registran por separado intentos intermedios
con errores de instrumentación/contexto Node-DOM o timeouts y las ejecuciones finales.
Se corrigieron durante la entrega el reinicio prematuro al cargar, la frescura de Personas
aportada por su consulta propietaria y la pérdida de página al canonicalizar un filtro false.
El límite de snapshots se ajustó para URL Unicode válidas y se eliminó la copia local del
índice controlado. Cada ajuste tiene comprobación dirigida o de navegador correspondiente.
Los primeros errores de instrumentación MCP (DOM fuera de page.evaluate) y timeouts de
lotes concurrentes/HMR no se ocultan ni se contabilizan como pruebas superadas; las
ejecuciones finales se realizaron con menor concurrencia y pasaron.
15. Limitaciones
V05 se delimita a los tres pilotos y escenarios ejecutados; no se declara una restauración
universal. No se acredita rendimiento de producción, lectores de pantalla, todos los motores
o todas las variantes de historial externo. scrollend se verifica en Chromium de la imagen
fijada. Los datos remotos pueden cambiar y las coordenadas se limitan a la geometría vigente.
Skills consultadas: ux-navigation principal; ux-tables, ux-search,
vercel-composition-patterns, vercel-react-best-practices, shadcn y ux-error-recovery.
Se aplican a orientación, continuidad operativa, fronteras de estado, composición y recuperación;
no se introducen tendencias de búsqueda, rediseño ni bibliotecas. Prevalecen AGENTS/DESIGN y
APIs reales. Los auditores especializados referidos por las skills no están disponibles;
no se instalan ni se afirma haberlos ejecutado.
16. Consumidores pendientes para L04
Puestos, Perfiles, Compañías, catálogos, procesos/configuraciones y sublistados deben decidir su contrato navegable antes de adoptar el mecanismo. Un selector remoto conserva su cursor y búsqueda del proveedor: no debe serializarse como paginación local para uniformar.
V07 permanece pendiente. V08 y V12 conservan su alcance ya documentado. No se implementa L04 ni se modifica la normalización de las búsquedas de negocio.
L03.2 requiere otra instrucción: no se adelantan sus funcionalidades. L04 podrá reutilizar el contrato tras analizar desmontaje, semántica de selección y consultas de cada consumidor.
17. Archivos principales y cierre
packages/rdx-ui/src/lib/entity-table-navigation.ts,hooks/use-entity-table.ts,components/entity-list.tsxycomponents/entity-table-content.tsx: contrato opcional, callbacks nativos de tabla e identidad estable de filas;entity-list.test.tsxlo verifica.modules/core/src/web/navigation/shared/:use-list-location.ts,use-list-return.ts,list-return-context.ts, History/BrowserLink y pruebas; exports públicos de navegación/core.- Activos: controlador de inventario,
inventory-page.tsx,asset-list.tsxy ficha completa. - Recursos: controlador y
resources-page.tsx, preservando panel y fallback de retorno. - Compañías:
use-people-list-context.ts,people-section.tsx, composición de detalle de compañía, exports y parser/pruebas de rutas existentes. scripts/list-context-ui.test.py: pruebas autenticadas nuevas con los helpers MCP existentes.DESIGN.md, patrón de listados, componentes, rutas, seguimientos de ambas auditorías y este informe: responsabilidades, alcance verificado y consumidores pendientes.
No se modifica packages/shadcn-ui, shell responsive, estilos, contratos HTTP, base de datos,
autenticación, dependencias ni docs/auditorias/. Se revisa el diff completo y enlaces locales.
El preparador propio termina con código 0 y confirma la retirada del entorno temporal; se
comprueba que su archivo privado de sesión ya no existe. Capturas y mediciones permanecen
ignoradas en .artifacts/playwright/l03-1/, sin incorporar credenciales al repositorio.