L03.1: Persistencia y restauración del contexto de listados (V05)

This commit is contained in:
David Arranz 2026-10-22 12:44:14 +02:00
parent 79dfa59936
commit c2f753e781
6 changed files with 436 additions and 5 deletions

View File

@ -769,3 +769,21 @@ El informe documenta suites, capturas abiertas, geometría y fallos técnicos pr
**V12 queda resuelto en ese alcance; V05 y V07 siguen pendientes y V08 conserva su cierre
L02.2.** No se generaliza la adopción a todos los listados ni se implementan L03/L04.
## Seguimiento L03.1 — Contexto de listados V05
El 22 de octubre de 2026 se implementa y verifica [L03.1](contexto-listados-l03-1.md).
**V05 queda resuelto en Activos, Recursos y Personas para los escenarios comprobados**;
el resto de consumidores permanece pendiente. Se conservan las reproducciones históricas
de esta auditoría como línea base, incluida la pérdida vertical original.
URL conserva búsqueda, filtros, sort y página; las preferencias mantienen sus claves y el
retorno usa la entrada de History para scroll/foco, sin caché adicional. Playwright MCP
verifica siete resoluciones, Atrás/Adelante/Volver, recarga, URL compartida/inválida, registros
retirados mediante GET, errores/refetch y paneles. En móvil recupera X=40/Y=150 y el enlace
de origen; en 1920 el scroll vertical válido es cero. La reproducción previa de esta entrega
sí confirma sort/página/foco/horizontal perdidos, pero no reproduce la pérdida vertical de L01.
Se verifican 14 métodos nuevos distintos y 33 regresiones con datos sintéticos abundantes.
El informe diferencia ejecuciones finales correctas, intentos intermedios, capturas y deuda
técnica global. **V07 sigue pendiente; V08 y V12 mantienen su alcance anterior.** No se
declara restauración universal ni se implementan L03.2/L04.

View File

@ -540,3 +540,21 @@ Se utiliza una instalación propia desechable con quince compañías y una princ
técnicos reales, diferenciando errores de red provocados y fallos globales preexistentes.
**V05/V07 continúan pendientes; V08 mantiene su estado L02.2.** No se declara WCAG completo,
rendimiento de producción ni adopción universal del nuevo contrato.
## Seguimiento L03.1 — Contexto de listados V05
El 22 de octubre de 2026 se completa [L03.1](contexto-listados-l03-1.md) para Activos, Recursos
y Personas. **V05 se resuelve únicamente en estos pilotos y escenarios verificados**. Los
resultados históricos anteriores se conservan; otros consumidores quedan inventariados para L04.
La validación autenticada con Playwright MCP compara filas, filtros, Nombre descendente,
página 3, scroll X/Y y foco antes/después en siete resoluciones de 320×640 a 1920×1080.
Comprueba Atrás, Adelante, Volver, recarga con carga retenida, URL directa/compartida,
normalización, preferencias, desaparición simulada y 503 posterior. Recursos conserva su
selección contextual y modalidad; Personas recupera contactos y página 2 con foco de origen.
Se utiliza una única instalación desechable con quince compañías y una principal con
50 personas, 51 activos y 66 recursos. Se verifican 14 métodos nuevos distintos y 33
regresiones, se abren capturas representativas y se registran las ejecuciones y límites
reales en el informe. No se declara cumplimiento WCAG completo ni rendimiento de producción.
**V07 continúa pendiente; V08/V12 mantienen su alcance anterior.** Los fallos globales
preexistentes de vendor, lint/check y SSR de Compañías permanecen documentados.

View File

@ -1180,6 +1180,28 @@ almacenamiento contenga una preferencia contraria. Los datos inválidos o el alm
bloqueado no impiden utilizar la tabla. Cambiar de clave carga las preferencias de la
nueva vista sin escribir sobre ellas la selección de la anterior.
### Contexto de listados — L03.1
`EntityTableNavigation` (`@repo/rdx-ui/lib/entity-table-navigation`) expresa valores externos
de orden y página, callback `onChange` y disponibilidad `ready`. `EntityTable` / `useEntityTable`
lo aceptan opcionalmente; sin él mantienen estado no controlado. El hook admite inicialización
`initialSorting` / `initialPagination`; conserva modelos nativos de TanStack Table, tamaño y
visibilidad. No contiene endpoints, query keys ni nombres de parámetros de negocio.
`useListLocation` y `useListReturn` (`@metis/core/client`) enlazan, respectivamente, parámetros
declarados por el consumidor y restauración DOM por scope/entrada de History. El segundo
recibe rootRef y señales de disponibilidad/verificación, sin cargar registros. Se espera
layout/fuentes, se limita scroll y se conserva el foco ya aplicado durante refetch. Una
respuesta fallida permite volver a búsqueda, sin confirmar la existencia de una fila en caché.
`BrowserLink navigation="return"` conserva semántica de anchor y reutiliza historial solo
si su entrada anterior coincide con el destino. Los modos de navegación ordinarios siguen
intactos. `data-entity-id` identifica filas para orientación, sin almacenar referencias DOM.
Activos, Recursos y Personas son pilotos; la navegación contextual de Recursos mantiene
su instancia y sus políticas de Base UI. Los pickers y sublistados no adoptan automáticamente
la consulta de su padre. Contratos URL, preferencias, API y evidencias en [L03.1](contexto-listados-l03-1.md).
### Estados de consulta de listados — L02.3
`EntityListState` y `entityListState` (`@repo/rdx-ui/lib/entity-list-state`) separan

View File

@ -0,0 +1,344 @@
# L03.1 — Persistencia y restauración del contexto de listados (V05)
Fecha: 22 de octubre de 2026. Pilotos: Activos, Recursos y Personas.
Referencias: [L01](auditoria-listados-l01.md), [L02.2](configuracion-columnas-l02-2.md),
[L02.3](estados-listados-l02-3.md), [rutas](rutas-del-cliente.md) y
[listados](patron-de-listados-de-entidades.md).
## 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; `AssetList` recibe 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:** `usePeopleListContext` encapsula 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](playwright-mcp.md),
mantener vivo su preparador y utilizar el archivo privado de sesión que este proporciona:
```bash
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](../.artifacts/playwright/l03-1/indice-evidencias.md).
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.tsx` y `components/entity-table-content.tsx`: contrato opcional,
callbacks nativos de tabla e identidad estable de filas; `entity-list.test.tsx` lo 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.tsx` y 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.

View File

@ -113,6 +113,24 @@ Al reintentar, enfocar la búsqueda persistente para evitar que la desaparición
deje el foco en el documento. Las denegaciones explícitas siguen el contrato de permisos
del módulo y no se representan como vacío ni como una copia consultable sin autorización.
## Contexto navegable y retorno — L03.1
Activos, Recursos y Personas habilitan `EntityTable.navigation` explícitamente. Búsqueda,
filtros, plantilla, sort/direction y página pertenecen a la URL; tamaño y columnas conservan
sus preferencias existentes. `useListLocation` de core enlaza parámetros elegidos por el
consumidor; rdx-ui valida columnas ordenables, dirección y página, sin conocer rutas o DTO.
La normalización espera datos/metadatos válidos; una carga provisional no fuerza página 1.
Los cambios de consulta usan replace; fichas/secciones usan push. `useListReturn` conserva
coordenadas y origen de foco por entrada de History, fuera de la caché de datos. Restaurar
solo con geometría disponible, limitar scroll y enfocar el mismo registro verificado o la
búsqueda; nunca seleccionar otra fila arbitraria. Respetar interacción en curso y finalFocus
de Base UI. Un refetch no reinicia el contexto ni vuelve a aplicar el foco inicial.
La adopción no es automática: pickers, sublistados y otros consumidores continúan locales
salvo contrato propio. No heredar la página del listado padre en tablas de una ficha.
Véase el [informe L03.1](contexto-listados-l03-1.md) para alcance y consumidores pendientes.
## Inventario de listados y ubicaciones actuales
La aplicación navega por espacios y secciones mediante rutas URL independientes, según [Rutas del cliente](rutas-del-cliente.md). Los componentes siguientes se componen desde CompaniesWorkspace, CompanyDetail o IdentityWorkspace; altas, ediciones e historiales conservan sus modales.

View File

@ -95,16 +95,21 @@ La consulta conserva los filtros que ya están conectados a la URL:
- Dashboard y directorio: `companyId`; directorio también `activeOnly` y `q`.
- Activos: `q` y `template` (UUID de plantilla), tanto en el listado como en la ficha seleccionada.
- Recursos: `q` y `template` (UUID de plantilla), tanto en el listado como en la ficha seleccionada.
- Personas: `q` y `contactsOnly`, también al abrir su detalle.
- Los pilotos Activos, Recursos y Personas añaden `sort`, `direction` y `page`, según
[L03.1](contexto-listados-l03-1.md). Son parámetros de las rutas existentes, no destinos nuevos.
- Perfiles y procesos: `q`, con `archived` o `closed`, respectivamente.
- Catálogos de activos y recursos: `q` y `tab`; `relations` solo en activos.
- Cuentas y accesos: `q` y `kind`, respetando los tipos disponibles en cada ámbito.
Los booleanos se serializan como `true` o `false`. Los UUID, las pestañas y los
parámetros repetidos o desconocidos se rechazan antes de seleccionar una pantalla. La búsqueda admite
Los booleanos conservan su contrato de ruta; Solo contactos se representa con `true` o ausencia.
Identidades de ruta, pestañas y parámetros ajenos desconocidos se rechazan antes de seleccionar
una pantalla. Los pilotos validan sort/direction/page y plantilla contra sus metadatos y
normalizan valores inválidos o repetidos sin perder parámetros de otras operaciones. La búsqueda admite
hasta 200 caracteres. Un `companyId` de consulta no puede contradecir la compañía
del pathname. Esta validación del navegador no sustituye la autorización del servidor.
Cambiar filtros reemplaza la entrada actual del historial; abrir otra sección o
Cambiar filtros, sort o página en los pilotos reemplaza la entrada actual del historial; abrir otra sección o
ficha añade una entrada. Atrás y Adelante recuperan los destinos y filtros. Cerrar
una ficha vuelve al destino interno anterior; si se abrió directamente, sustituye
la entrada por su listado o por el contexto del perfil correspondiente.
@ -114,7 +119,10 @@ siguen siendo diálogos locales. No se añaden rutas `/new`, `/edit` ni parámet
`view` sin una necesidad concreta. El alta rápida que cambia de sección lleva una
señal temporal en `history.state`, consumida cuando monta la pantalla que abre el
formulario, sin introducirla en la URL compartida. Los restantes filtros locales,
la paginación, ordenación y preferencias de columnas mantienen su estado existente.
la paginación y ordenación mantienen su estado existente fuera de los pilotos. Tamaño y
columnas conservan sus claves de preferencias. Los pilotos guardan exclusivamente scroll y
origen de foco en `history.state.metisListReturn`, aislados por compañía/listado y filtros.
No se guarda una segunda copia de datos remotos ni se persiste ese contexto en localStorage.
## Implementación y futuros destinos
@ -155,7 +163,10 @@ Tickets, intervenciones y plantillas de intervención no tienen rutas activas. S
`/companies/:companyId/assets/:assetId` muestra una página de detalle con las pestañas
Información e Historial. El listado conserva la selección local para su panel lateral;
los enlaces de nombre y las acciones de ver abren la página. Los parámetros `q` y
`template` se conservan en los enlaces del listado y de retorno a este.
`template`, `sort`, `direction` y `page` se conservan en los enlaces y el retorno.
`BrowserLink navigation="return"` vuelve a la entrada anterior si coincide con el listado;
sin historial apropiado reemplaza la entrada por ese destino. Clic modificado y nueva pestaña
siguen utilizando un enlace real. La ausencia del activo conserva el acceso a Volver.
## Presentaciones responsive de la navegación