Repository navigation
feat(front): Resumen de evolución como pantalla principal - #9
Conversation
Trend (átomo) dibuja la línea de una serie, un punto por ejecución: escala del mínimo al máximo de la propia serie (enseña la forma de la evolución, no el valor, que ya dice el número de al lado), la referencia punteada del primer valor, trazo discontinuo para datos que no son de verdad, y cada punto con su detalle al pasar el ratón. SegmentedControl (radios nativos), ChangePill (la flecha dice hacia dónde se movió el valor y el color si eso es bueno o malo) y MetricTrend (valor actual, cambio desde el principio y línea) son moléculas construidas con ellas. Iconos arrowUp y minus, y tono warn en Badge. changeTone y shortDate pasan a lib/ui: el tono de un cambio lo comparten la comparativa de ejecuciones y el resumen, y ya no vive duplicado en ComparePanel.helper.
LevelCard enseña cómo va un nivel de una tarea: cada métrica con su valor actual, cuánto ha cambiado desde la primera ejecución y su línea, y un veredicto arriba (Mejora, Empeora, Mixto, Sin cambios) que resume cuántas mejoran y cuántas empeoran. TaskSection agrupa una tarjeta por nivel: se compara un nivel consigo mismo a lo largo del tiempo, nunca un nivel con otro (el criterio de éxito es distinto). Las cuatro métricas salen de v_run_summary: tasa de éxito, tiempo de ciclo, margen de estabilidad y error de colocación. Una métrica que la tarea no mide (la estabilidad de la inducción) no aparece, en vez de salir como «—». El cambio es la resta del último valor y el primero (dos números que ya calculó la vista); el color dice si eso es bueno o malo según la métrica (un tiempo o un error que bajan mejoran). Las clases de datos NO se mezclan en una serie: medidos, oracle y sembrados van aparte, con etiqueta y trazo discontinuo los que no son de verdad, y un aviso si la serie mezcla rangos de semillas distintos («se compara suerte, no código»).
La curva se limitaba a las ejecuciones comparables con la más reciente. Con datos reales (pruebas de humo, cada una con semillas distintas) una sola ejecución suelta se quedaba con la serie y dejaba fuera a todas las demás: la tarjeta salía con una ejecución y «sin comparación». Ahora la referencia es el rango de semillas con MÁS ejecuciones (a igualdad, el de la más reciente), que es el protocolo real de un benchmark. Sigue usando comparability(), la misma regla que bloquea la comparativa: lo que no la cumple no entra en la curva, y la tarjeta dice cuántas se dejan fuera («+3 con otras semillas»).
/ pasa a ser el Resumen y Live se mueve a /live. La barra tiene tres pestañas (Resumen, Live, Ejecuciones); routes.ts reúne las rutas y activeTab distingue las tres. El resumen enseña cómo ha evolucionado cada tarea, nivel por nivel, con las métricas de v_run_summary (tasa de éxito, tiempo de ciclo, margen de estabilidad y error de colocación). Para leerlo de un vistazo: - «De un vistazo»: un chip por tarea y nivel con su veredicto (Mejora, Mixto, Empeora, Sin comparación); pulsarlo lleva a su tarjeta. VerdictChip llama a scrollIntoView y no usa el #hash: la que scrollea es un contenedor de la pantalla, no el documento. - Cada tarjeta: valor actual, cambio desde la primera ejecución (la flecha dice hacia dónde se movió el valor y el color si eso es bueno o malo) y la línea de evolución. - Un selector de clase de datos: medidos, oracle y sembrados NO se mezclan nunca en una serie; por defecto lo medido y, si no hay, lo primero que haya, con su aviso. - Una leyenda de lectura, en una línea. No hace falta ningún endpoint nuevo. Reutiliza useRuns con la misma clave que Ejecuciones. La pantalla tiene altura fija y scrollea por dentro (la página no scrollea nunca); sus hijos no se encogen, porque las tarjetas llevan overflow: hidden y en una columna flex se aplastarían. Con tres pestañas la barra desbordaba 27 px a 390 px: se aprietan en móvil. AGENTS.md: rutas y estado al día.
Con pocos niveles la tarjeta ocupaba la mitad de la pantalla y el resto quedaba en blanco, y «De un vistazo» era un bloque enorme con un solo chip. - Dos columnas que reparten todo el ancho: a la izquierda, una tarjeta por nivel a todo el ancho; a la derecha, fija al bajar, «De un vistazo» (una lista vertical de niveles con su veredicto y cuántas métricas mejoran o empeoran) y «Cómo leerlo». En pantalla mediana pasa arriba, con la lista y la guía lado a lado; en móvil, apiladas. - Las métricas de una tarjeta son mosaicos que se reparten el ancho de LA TARJETA (container query): 1 columna estrecha, 2×2 mediana y todas en fila si es muy ancha. Antes se repartían 3+1 y quedaba un mosaico huérfano. - Cada mosaico apila valor, cambio y línea, y el gráfico es más ancho (viewBox 400×64) para que no se vea letterboxed en un mosaico grande. - VerdictChip pasa a VerdictRow y OverviewStrip a OverviewPanel: ya no son ni chip ni franja. Comprobado en Chrome con datos reales y con un mock de varias tareas y niveles, en los dos temas, y con iframes de 1850, 1440, 1100, 1000, 800, 390 y 360 px: la página no scrollea ni desborda en ninguno y la lista lateral se queda fija al bajar.
…ard-evolucion Trae de dev el alzado a escala del palé con la captura real junto al esquema (PR #6) y la tarea nueva paletizado-guionizado (vocabulario: schema.py, el CHECK de SQL y TASK_TEXT). Un conflicto, en components/molecules/index.ts: los dos lados solo añadían exportaciones (las del resumen y SnapshotView); se conservan todas. El resumen agrupa por tarea y ordena las que no conoce detrás de paletizado e inducción, así que la tarea nueva sale como sección propia sin tocar nada. Comprobado con npm run lint, tsc y next build sobre el árbol fusionado.
Las tarjetas se veían apretadas. Se abren los huecos del resumen: 32 px entre las tarjetas de nivel (antes 20), 52 entre tareas (36), 28 entre la lista «De un vistazo» y la guía de lectura (20), 40 entre la columna principal y la lateral (28) y 36 entre bloques de la pantalla (28). Dentro de una tarjeta de nivel, 20 px entre mosaicos y 28 de relleno lateral (16 y 24). En pantalla estrecha los huecos se reducen (32 entre columnas, 36 entre tareas). Comprobado en el servidor de desarrollo con un mock de varias tareas y niveles y con iframes de 1850, 1440, 1000, 800, 390 y 360 px: la página no scrollea ni desborda en ninguno.
…vía datos Antes 8 s sin eventos se leían como «Conexión perdida», aunque el canal de Realtime estuviera sano y solo callara quien produce (un paso lento, o un episodio que se quedó en running porque el proceso se cayó). Ahora: - conexión perdida: falla la consulta o el canal avisa de CHANNEL_ERROR, TIMED_OUT o CLOSED. - sin datos: canal sano y nada recibido en 30 s; aviso propio, sin «Reintentar». La marca de última señal pertenece a su episodio: al pasar al siguiente de una ejecución ya no arrastra el silencio del anterior.
…coincide con ellos Con transparencia, el último paquete se veía más oscuro que la muestra de la leyenda en cuanto había otro debajo (capa 2 sobre capa 1). Ahora se mezclan con --surface: un paquete tapa al de debajo y su color no depende de lo que haya detrás.
- «Ver captura» / «Ver esquema» (un enlace subrayado) pasa a un control segmentado Esquema | Captura en la cabecera, siempre con las dos opciones a la vista. SegmentedControl gana la variante compact. La leyenda solo se enseña con el esquema. - La captura va en un marco que ocupa todo el panel, con las esquinas y el borde del resto de la interfaz, sin recortar la foto, y con una etiqueta «captura del simulador». - La cabecera de un panel pasa a línea siguiente si leyenda y control no caben (390 px).
… sin franjas Antes iba en un marco del ancho del panel con la foto centrada y franjas vacías a los lados. Ahora la propia foto tiene esquinas y borde, y se dimensiona con las medidas del hueco (container query) para ocupar lo máximo posible sin recortar ni deformar. Además se quita el aro de foco duplicado del control segmentado.
Un cambio a peor salía con la flecha hacia arriba cuando el número subía (un tiempo de ciclo de +0.6 s), contradiciendo la propia leyenda («mejora» ↑, «empeora» ↓). Ahora la flecha sigue al veredicto: arriba si mejora, abajo si empeora; el signo del texto dice si el valor subió o bajó.
Nuevo InfoTip (icono «i» con explicación al pasar el ratón, con el foco del teclado o al tocar; se cierra con Escape o al tocar fuera). Se pinta en un portal y con posición fija, para que las tarjetas (overflow y container queries) no la recorten, y se empuja hacia dentro si está en un borde. Explica: las cuatro métricas (qué mide y si más o menos es mejor), las clases de datos, las semillas, la etiqueta oracle/sembrados, «con otras semillas», el veredicto y «De un vistazo». Sustituye los title nativos de esas etiquetas.
…ta de nivel Se mantienen los de las métricas, el selector de clases de datos y «De un vistazo».
…ismo commit Las barras de causas de fallo usaban el nombre de la ejecución (el commit) como key de React, y dos ejecuciones del mismo commit lo comparten: «Encountered two children with the same key». Ahora la key es el id de la ejecución.
…d-evolucion Conflicto en Live con el arreglo de dev del aviso de conexión perdida. Se conserva el de dev (descartar episodios abiertos más de 15 min) y se mantiene la separación de esta rama entre conexión perdida y episodio sin datos, con el umbral holgado de dev como justificación. AGENTS.md conserva las dos líneas de estado.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
VerdictRow cancela siempre el comportamiento del enlace al hacer click, bloqueando acciones estándar (Ctrl/Cmd‑click/rueda) y contradiciendo su propia intención documentada.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Este PR convierte Resumen de evolución en la pantalla principal (/) para visualizar, por tarea y nivel, la evolución de métricas de v_run_summary, y mueve Live a /live. Además, refuerza la lectura de cambios (mejora/empeora), añade componentes reutilizables (tendencias, ayudas y selectores) y corrige detalles de Live y de la comparativa.
Changes:
- Nueva pantalla Resumen/Evolución como landing (
/) con selector de clase de datos y panel lateral “De un vistazo”. - Live distingue conexión perdida vs episodio sin datos (silencio con canal sano) y evita arrastrar marcas de señal entre episodios.
- Mejora de UX en detalle/comparativa: selector “Esquema | Captura”, capturas como tarjeta, rellenos opacos en el palé y fix de keys duplicadas en comparativa.
| File | Description |
|---|---|
| frontend/styles/colors.css | Ajusta fills del palé a mezcla opaca con --surface para consistencia con la leyenda. |
| frontend/lib/ui.ts | Añade helpers de UI (changeTone, capitalize, shortDate). |
| frontend/lib/routes.ts | Centraliza nuevas rutas: dashboard: "/" y live: "/live". |
| frontend/components/screens/LiveScreen/useLiveScreen.ts | Integra nueva lógica de “staleness” y canal error desde Realtime. |
| frontend/components/screens/LiveScreen/LiveScreen.tsx | Muestra banners diferenciando “silent” vs “lost”. |
| frontend/components/screens/LiveScreen/LiveScreen.helper.ts | Modela Staleness y la detección de “lost/silent”. |
| frontend/components/screens/DashboardScreen/useDashboardScreen.ts | Nuevo hook para estado del Resumen, kind y secciones. |
| frontend/components/screens/DashboardScreen/index.ts | Barrel export de DashboardScreen. |
| frontend/components/screens/DashboardScreen/DashboardScreen.tsx | Nueva pantalla principal “Evolución” con rail lateral y ayudas. |
| frontend/components/screens/DashboardScreen/DashboardScreen.module.css | Layout sticky + scroll interno para Resumen. |
| frontend/components/screens/DashboardScreen/DashboardScreen.helper.ts | Construye secciones por tarea/nivel y gestión de kinds. |
| frontend/components/screens/AppHeader/AppHeader.helper.ts | Ajusta pestaña activa con rutas dashboard/live/runs. |
| frontend/components/organisms/TopBar/TopBar.tsx | Añade pestaña “Resumen” y usa routes.*. |
| frontend/components/organisms/TopBar/TopBar.module.css | Ajustes responsive para 3 pestañas. |
| frontend/components/organisms/TaskSection/TaskSection.tsx | Nueva sección por tarea con anchors por nivel. |
| frontend/components/organisms/TaskSection/TaskSection.module.css | Estilos y scroll-margin-top para navegación desde el rail. |
| frontend/components/organisms/TaskSection/index.ts | Barrel export de TaskSection. |
| frontend/components/organisms/OverviewPanel/OverviewPanel.tsx | Nuevo panel lateral “De un vistazo” con navegación a anchors. |
| frontend/components/organisms/OverviewPanel/OverviewPanel.module.css | Estilos del panel lateral. |
| frontend/components/organisms/OverviewPanel/OverviewPanel.helper.ts | Scroll interno a anchors via scrollIntoView. |
| frontend/components/organisms/OverviewPanel/index.ts | Barrel export de OverviewPanel. |
| frontend/components/organisms/LevelCard/LevelCard.tsx | Nueva tarjeta por nivel con métricas + tendencias. |
| frontend/components/organisms/LevelCard/LevelCard.module.css | Grid responsive por container queries para mosaicos de métricas. |
| frontend/components/organisms/LevelCard/LevelCard.helper.ts | Construye series/curvas, comparabilidad, veredictos y tooltips. |
| frontend/components/organisms/LevelCard/index.ts | Barrel export de LevelCard y helpers. |
| frontend/components/organisms/index.ts | Reexporta nuevos organismos (LevelCard/TaskSection/OverviewPanel). |
| frontend/components/organisms/EpisodeDashboard/EpisodeDashboard.tsx | Sustituye botón por selector “Esquema |
| frontend/components/organisms/CompareResult/CompareResult.tsx | Evita keys duplicadas usando key estable en barras. |
| frontend/components/organisms/ComparePanel/ComparePanel.helper.ts | Reutiliza changeTone y añade key: run.id a barras. |
| frontend/components/molecules/VerdictRow/VerdictRow.tsx | Nueva fila enlazable con veredicto y detalle. |
| frontend/components/molecules/VerdictRow/VerdictRow.module.css | Estilos de la fila de veredicto. |
| frontend/components/molecules/VerdictRow/index.ts | Barrel export de VerdictRow. |
| frontend/components/molecules/SnapshotView/SnapshotView.tsx | Convierte captura en “tarjeta” con ratio controlado. |
| frontend/components/molecules/SnapshotView/SnapshotView.module.css | Layout con container queries de tamaño y tag superpuesto. |
| frontend/components/molecules/SegmentedControl/SegmentedControl.tsx | Nuevo control segmentado accesible basado en radios. |
| frontend/components/molecules/SegmentedControl/SegmentedControl.module.css | Estilos del segmented (normal y compacto). |
| frontend/components/molecules/SegmentedControl/index.ts | Barrel export de SegmentedControl. |
| frontend/components/molecules/PanelHeader/PanelHeader.module.css | Ajusta padding y wrap para headers con controles. |
| frontend/components/molecules/NavTab/NavTab.module.css | Ajustes responsive para tabs en móvil. |
| frontend/components/molecules/MetricTrend/MetricTrend.tsx | Nuevo mosaico de métrica: valor, cambio y curva. |
| frontend/components/molecules/MetricTrend/MetricTrend.module.css | Estilos del mosaico de métrica. |
| frontend/components/molecules/MetricTrend/index.ts | Barrel export de MetricTrend. |
| frontend/components/molecules/InfoTip/InfoTip.tsx | Nuevo tooltip con portal + posición fija y seguimiento al scroll. |
| frontend/components/molecules/InfoTip/InfoTip.module.css | Estilos del tooltip y trigger. |
| frontend/components/molecules/InfoTip/InfoTip.helper.ts | Cálculo de posición del tooltip en viewport. |
| frontend/components/molecules/InfoTip/index.ts | Barrel export de InfoTip. |
| frontend/components/molecules/index.ts | Reexporta nuevos molecules (SegmentedControl, MetricTrend, InfoTip, etc.). |
| frontend/components/molecules/ChangePill/index.ts | Barrel export de ChangePill. |
| frontend/components/molecules/ChangePill/ChangePill.tsx | Badge con flecha/dirección y tono para “mejora/empeora/igual”. |
| frontend/components/molecules/ChangeLegend/index.ts | Barrel export de ChangeLegend. |
| frontend/components/molecules/ChangeLegend/ChangeLegend.tsx | Leyenda “Cómo leerlo” para interpretar flechas/signos. |
| frontend/components/molecules/ChangeLegend/ChangeLegend.module.css | Estilos de la leyenda. |
| frontend/components/atoms/Trend/Trend.tsx | Nuevo SVG de tendencia con baseline y dots. |
| frontend/components/atoms/Trend/Trend.module.css | Estilos del SVG de tendencia (ok/bad/muted, dashed, dots). |
| frontend/components/atoms/Trend/Trend.helper.ts | Genera geometría/path/dots para series con nulls. |
| frontend/components/atoms/Trend/index.ts | Barrel export de Trend. |
| frontend/components/atoms/index.ts | Reexporta Trend y TrendPoint. |
| frontend/components/atoms/Icon/Icon.tsx | Añade iconos arrowUp, minus, info y expande IconName. |
| frontend/components/atoms/Badge/Badge.tsx | Añade tono warn. |
| frontend/components/atoms/Badge/Badge.module.css | Estilos para warn (color + fill). |
| frontend/app/page.tsx | / pasa a renderizar DashboardScreen. |
| frontend/app/live/page.tsx | Nueva ruta /live para LiveScreen. |
| frontend/api/hooks/useEpisodeRealtime.ts | Señal por episodio + channelError para distinguir “lost” vs “silent”. |
| AGENTS.md | Actualiza estructura/rutas y documenta el nuevo Resumen. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| <a href={href} className={styles.row} | ||
| onClick={onSelect && ((e) => { e.preventDefault(); onSelect(); })}> |

Qué es
Una pantalla nueva, Resumen, que pasa a ser la principal (
/): enseña de un vistazo cómo ha ido evolucionando cada tarea, nivel por nivel. Live pasa a/live. Es la evidencia del criterio «iteración y mejora medida» del eje B.Qué incluye
Resumen (
/)v_run_summary(tasa de éxito, tiempo de ciclo, margen de estabilidad, error de colocación): dónde está ahora, cuánto ha cambiado desde la primera ejecución y su curva.comparability(): la curva usa solo ejecuciones comparables (en un nivel, las del rango de semillas más usado). Las demás se dejan fuera y se cuentan («+10 con otras semillas»).InfoTip) en las métricas, en el selector de clases y en «De un vistazo».↓ +0.6 sen rojo.Live
Detalle del episodio y comparativa
Otros: nueva tarea
paletizado-guionizadoen el vocabulario, yAGENTS.mdcon las rutas y el estado nuevos.devdevtrajo su propio arreglo del aviso de Live (2d04f2f: descartar episodiosrunningde más de 15 min y subir el umbral a 20 s). Se conservan sus dos cambios de fondo (el descarte de huérfanos y la justificación del umbral) y se mantiene la separación de esta rama entre conexión perdida y sin datos, con un umbral de 30 s. Conviene que quien hizo aquel arreglo mireLiveScreen.helper.ts.Verificado
npm run lint(eslint, colores, contraste),tscynpm run buildpasan tras la fusión condev.No verificado / a tener en cuenta