Repository navigation
Conversation
Lo entrega el equipo de front y sirve de especificacion para endurecer el backend. Se commitea sin tocar, para que los cambios que lo invaliden se lean en el diff y no se confundan con lo que ya venia escrito.
El SDK sigue con cero dependencias de ejecucion: pytest y ruff van en el extra [dev], que solo instalan CI y quien desarrolla. La simulacion importa theker_telemetry sin arrastrar nada nuevo. select explicito (E, F, I, UP) a proposito: el conjunto por defecto de ruff cambia entre versiones y CI se pondria rojo por algo que nadie ha tocado. line-length alto para que E501 solo cace lineas desbocadas y no obligue a romper la tabla alineada de CAMPAIGN, donde la alineacion es lo que deja leer la curva de mejora de un vistazo. Los arreglos automaticos son los que pedia UP: datetime.UTC (ya exigimos Python 3.11) y Iterable/Sequence desde collections.abc.
70 tests, ninguno toca la red. Cubren lo que hasta ahora solo se habia probado a mano, empezando por la promesa dura de AGENTS.md 4: el disco manda, un fallo de red avisa una vez y el episodio sigue. Sin eso, un benchmark de cincuenta episodios se cae por el wifi de la sala y no queda ni el jsonl. Tambien queda cubierto el formato viejo de episodes.jsonl (sin n_misrouted) y la clasificacion de claves del backfill, que es lo que permite cargar los 665 episodios historicos sin perder las metricas de la rama de ordenacion. Dos supuestos mios eran falsos y mando el codigo, no al reves: el apoyo mide 1180x380, asi que el borde que manda es el de Y y descentrar en X no cambia el margen; y pallet_states guarda una fila por paquete INTENTADO, de modo que el intento que derrumba el monton tambien deja traza.
AGENTS.md 5 dice que anadir una causa obliga a tocar tres sitios: FAILURES, el CHECK de episodes.failure y FAILURE_TEXT. Nada lo comprobaba. Saltarse uno falla tarde y mal: el episodio se escribe en disco, la subida entera se cae por el CHECK y nadie se entera hasta ver la interfaz vacia; o la UI acaba ensenando el identificador crudo, que es justo lo prohibido. La asimetria entre lo almacenable (9, con el historico place_inaccurate) y lo producible (8) es deliberada y queda fijada como subconjunto, no como igualdad, para que no la 'arregle' nadie por error. Sin base de datos: se leen los ficheros como texto. Comprobado por mutacion que falla de verdad al quitar una traduccion.
episode() sube el episodio YA terminado, con todas sus filas hijas de golpe. Para un benchmark esta bien, pero entonces la pantalla Live no tiene nada a lo que suscribirse: nunca existe una fila con status running ni llega un UPDATE de episodes, que son dos de las tres senales que escucha (API.md 4). El jurado veria el pale ya hecho, aparecido de golpe, en vez de montandose capa a capa. Es la pantalla que justifica la plataforma entera. Ahora begin() abre el episodio en curso, event()/pallet_state()/placement() van soltando filas segun ocurren, y end() escribe el jsonl y cierra con un PATCH, que es ademas el UPDATE al que Live esta suscrita y la primera vez que alguien rellena episodes.ended_at. episode() se queda igual: backfill, sembrado y la simulacion de hoy siguen funcionando sin tocar una linea. Todo pasa por _try, asi que un Supabase caido sigue degradando a solo-disco. Pendiente en el repo de la simulacion: el productor tiene que llamar begin() y end() o esto queda construido y sin usar.
fetchLatestEpisode ordena por started_at sobre la vista, y la vista no proyectaba esa columna: PostgREST devuelve 400 42703, load() en page.tsx no tiene catch y Live cae a 'todavia no hay episodios'. Solo se salvaba si habia un episodio running, que es justo lo que no hay antes de arrancar la demo. Lo detecto el equipo de front y lo esquivo con una consulta en dos pasos (API.md 2.2); esto lo arregla de raiz, asi que ese rodeo ya puede colapsarse a una sola consulta. De paso, indice parcial sobre episodes(started_at) where status='running': es la otra consulta que Live hace en cada recarga.
_try solo capturaba RuntimeError, que es lo que lanza el cliente a proposito. Pero habia dos caminos por los que una respuesta rara se llevaba por delante el benchmark entero, justo lo que este modulo existe para evitar: - Un 200 con cuerpo que no es JSON (el HTML de un proxy, por ejemplo) revienta en json.loads con un ValueError. - Una fila devuelta sin 'id' reventaba al leerla con un KeyError. Y esta ocurria FUERA de _try, ya de vuelta de la red, asi que ampliar el except no bastaba: la lectura pasa ahora por _id_of, donde confluyen los tres sitios que sacaban rows[0]['id']. Un episodio cuya fila no se sabe identificar deja de subir sus hijas pero no apaga el remoto: el siguiente episodio puede ir bien, y el jsonl esta escrito de todas formas.
rng.seed(hash((sha, seed))) no era determinista: CPython aleatoriza el hash
de las cadenas por proceso desde la 3.3, asi que cada ejecucion del sembrado
producia un historico distinto mientras el comentario de encima prometia que
relanzarlo daba el mismo. Un historico que cambia solo no sirve para ensenar
una curva de mejora, que es exactamente para lo que existe.
Medido con PYTHONHASHSEED 0/1/2 sobre ('8c5cbf4', 37):
antes 1069116207, 3997445009, 1533800031
ahora 3273588617, 3273588617, 3273588617
El test lo comprueba lanzando tres procesos de verdad: dentro de uno solo el
hash es estable y el fallo no se ve.
Va tambien Supabase.delete, para que wipe() deje de construir el DELETE a
mano con client._headers y urlopen: asi respeta el timeout de la clase y un
borrado fallido cuenta lo que dijo PostgREST en vez de un traceback pelado.
19 tests que levantan el esquema de verdad en postgres:16 y comprueban lo que ningun parser de SQL puede: que los .sql aplican, que aplican dos veces y en cualquier orden, y que las vistas calculan lo que dicen con filas dentro (1, 2 y 90 segundos dan mediana 2, no media 31). El assert que importa es otro y conviene no confundirlo: comparar columnas contra tipos TS NO caza el bug de Live, porque Episode tampoco declaraba started_at y los dos lados coincidian. Lo que lo caza es recorrer los .from() del front y exigir que cada columna de un .order()/.eq() exista en su relacion. Comprobado por mutacion: al quitar e.started_at de la vista, falla diciendo 'columnas que no existen: v_episode_summary.started_at'. Los tipos se comparan como subconjunto, no igualdad: mean_score esta en la vista y no en Run a proposito (API.md 2.1). Lo que rompe la UI es una columna que falta, que llega como undefined y se pinta mal en silencio. Trabaja en un esquema propio y solo contra localhost: apuntar DATABASE_URL a un Supabase de verdad no puede llevarse por delante los datos. Sin DATABASE_URL se salta entero.
Dos jobs. El backend levanta postgres:16 como servicio, porque el Postgres no es decoracion: el ultimo bug de esquema que se colo (round() sobre double precision) solo aparece EJECUTANDO el SQL, y el de started_at solo comparando las consultas del front contra las columnas de verdad. El montaje del esquema lo hace el propio test, asi que en local y en CI se corre exactamente lo mismo. El front hace lint y build. build y no tsc --noEmit porque Next 16 genera durante el build los tipos de PageProps<'/runs/[id]'>, que es justo el gate que van a necesitar las rutas pendientes. Van dos arreglos de lint en app/page.tsx sin los que el CI nacia en rojo, y los dos eran reales aunque menores: - useRef(Date.now()) lee el reloj durante el render, que es impuro: al re-renderizar daba otro valor y el latido medía contra un instante que nunca ocurrio. Ahora arranca a null y no marca 'sin datos nuevos' hasta que ha llegado algo de verdad. - La carga inicial silencia react-hooks/set-state-in-effect con su porque: sincronizar con Supabase al montar es lo que se quiere, y load tiene dependencias vacias, asi que no hay cascada de renders.
API.md describia dos cosas que este trabajo ha dejado de ser ciertas: - El aviso de que v_episode_summary no tiene started_at. Ya la tiene, y el rodeo en dos consultas de getLatestEpisode puede colapsarse a una. - El parrafo de que el SDK no produce episodios running ni UPDATEs. Ahora si: se documenta el ciclo begin/event/end y se anade el PATCH /episodes a la tabla de escritura. Referencias muertas al repo de la simulacion, que mandaban a editar ficheros que aqui no existen: AGENTS.md decia que el CHECK de episodes.failure estaba en 002_design.sql cuando esta en 001, y 001 citaba scripts/backfill.py y src/metrics.py. Quien siguiera esas instrucciones para anadir una causa de fallo editaba el fichero equivocado. AGENTS.md 3 documenta como lanzar los tests, y 9 avisa de lo unico que queda pendiente fuera de este repo: que la simulacion llame a begin()/end().
Las tres suscripciones de Realtime van filtradas al episodio que se esta enseñando, y no hay polling. La unica forma de que Live volviera a preguntar cual es el ultimo era recibir un UPDATE del episodio que YA tenia en pantalla. Consecuencia en la demo: abres Live, te enseña el ultimo terminado, arrancas la simulacion y Live no se entera. El episodio nuevo nace running pero nadie escucha INSERT sobre episodes, asi que la pantalla se queda en el anterior hasta recargar a mano. Con el ciclo begin()/end() recien añadido esto pasaba de ser teorico a ser lo que iba a ocurrir delante del jurado. Se añade una suscripcion a INSERT sobre episodes, sin filtro, que solo recarga si la fila nueva viene con status running: un backfill inserta cientos de episodios ya terminados y no hay por que recargar con cada uno. API.md documenta ademas que Live elige un EPISODIO y no una ejecucion, y que started_at es la hora de insercion para lo backfilleado y la de arranque real solo para lo que produce begin().
Medido contra Supabase real: a mitad de un episodio en vivo, la pantalla enseñaba 'Colocados 0 ud de 10' con tres paquetes ya en el pale, y el tiempo de ciclo en blanco. La causa es que episodes.n_placed vale 0 mientras el episodio corre: lo deja asi begin() y solo lo actualiza end(). Los otros dos KPIs (margen y utilizacion) no lo notaban porque leen de pallet_states, que si va llegando. Se cuenta de las colocaciones ya recibidas, que estan en el cliente por Realtime, en vez de añadir un PATCH por paquete. Tras el arreglo, el mismo instante enseña 'Colocados 3 ud de 10' y 'Ciclo 2.20 s/paquete'. Dos de los cuatro KPIs clavados en cero justo mientras el pale se monta es exactamente cuando el jurado esta mirando.
ejemplo_contrato.py deja UNA entrada que recorre el contrato entero de API.md: las cinco tablas, los seis tipos de evento con las claves exactas que lee EventFeed, cuatro causas de fallo con stack_collapse dominante, y los casos limite que el front tiene que saber pintar (0 colocados, cycle_time_s nulo, overhang positivo, planned_pose distinta de actual_pose). Sirve de fixture para las rutas pendientes sin depender de la simulacion. Va marcada synthetic=true, y no es negociable: son numeros inventados y la regla del proyecto es que nunca se confundan con medidos ni entren en una comparacion. humo_live.py monta un pale de verdad con pausas, usando begin()/event()/ end(). Es lo que hay que lanzar antes de una demo para saber si Live esta viva; con el salieron los dos fallos de la pantalla que se arreglaron en af7ec99 y 46fde4c. Va tambien runs/ al .gitignore, y se sacan del control de versiones dos episodes.jsonl que se me colaron en 46fde4c con un git add -A. Son datos que produce RunLog en la maquina que lo ejecuta, no codigo.
Los cinco puntos que pide la simulacion, por gravedad.
1. RunLog acepta config y lo escribe en runs.config, y las dos vistas lo
exponen. El pale es una maqueta 1:5.7 (la pinza del Panda abre 80 mm, un
europeo es inagarrable) y sin el tamano real la pantalla lo dibuja a
1200x800: todas las cotas salen 5.7 veces mal. Pallet.tsx deja de tener
PALLET_X a fuego y recibe el tamano; palletSize() en ui.ts lo busca en
config, luego en metrics (donde iba mientras tanto) y cae al europeo.
2. Imagenes: tabla snapshots en 003, bucket publico, RunLog.snapshot() sobre
_push y Supabase.upload_png(). Los dos avisos, atendidos: test_contrato
aplica ahora sorted(glob('*.sql')) en vez de 001 y 002 a mano, y el bucket
va guardado con to_regclass, comprobado contra un postgres:16 pelado.
3. stability_margin y support_polygon se mueven a theker_telemetry/pallet.py.
Estaban duplicadas porque seed/ no es importable; ahora hay UNA
implementacion y el sembrado la reexporta. Es el indicador que define toda
la interfaz: dos versiones acabarian discrepando.
4. Se fusiona main, que traia 30cfc2b (_aligned). Sin el, seed y backfill
estaban rotos en esta rama: PostgREST rechaza un lote cuyas filas no
tienen las mismas claves.
5. end() manda solo lo que cambia al cerrar; seed, task, level y n_objects se
fijaron en begin(). API.md deja de citar frontend/api/clients y getRuns,
que nunca existieron, y documenta config, snapshots y el PATCH.
116 tests en verde con Postgres.
Con DATABASE_URL puesto y sin el binario salian veinte FileNotFoundError identicos. Es la dependencia del job de backend en CI que no he podido verificar hasta que corra alli, asi que conviene que falle diciendo que hacer.
Su reescritura manda en todo lo estructural: atoms/organisms/screens, frontend/api/ con TanStack Query y lib/pallet.ts. Se resuelven a su favor los dos conflictos (app/page.tsx y el borrado de components/Pallet.tsx). Pero su front traia los tres mismos huecos de Live que ya estaban arreglados en esta rama, asi que se reaplican sobre su estructura: - lib/pallet.ts tenia PALLET_X = 1.2 a fuego. Era la peticion numero 1 de la simulacion: el pale es una maqueta 1:5.7 y dibujarlo a 1200x800 deja todas las cotas 5.7 veces mal. Ahora buildTopView/buildSideView reciben el tamano y LiveScreen se lo pasa desde config.pallet_size_m. - buildKpis leia episode.n_placed, que vale 0 mientras el episodio corre: los KPIs de Colocados y Ciclo se quedaban clavados en cero justo mientras el pale se monta. Se cuentan de las colocaciones ya recibidas. - El tercero, detectar un episodio nuevo, ya lo habian resuelto ellos por polling (LATEST_POLL_MS), asi que no se toca. API.md estaba duplicado: el suyo en docs/ y el mio en la raiz. Se unifican en docs/API.md, que gana la seccion 2.8 de totales que solo tenia el suyo. 116 tests en verde, y el test de contrato ve ahora sus 126 ficheros .ts y valida 16 consultas, incluida v_episode_summary.started_at.
Feature/backend hardening
No hay servidor propio que arrancar, asi que 'todo' es: credenciales en su sitio, dependencias puestas, esquema al dia y el front escuchando. Lo que comprueba no es ceremonia. El esquema se pega a mano en el editor de Supabase, asi que es normal que el repo vaya por delante de la base, y una columna que el front consulta y la vista no proyecta devuelve 400 y deja la pantalla vacia. En vez de 'hay conexion', se piden las cuatro columnas que de verdad se han ido rompiendo (started_at, los dos config, snapshots.url) y si falta alguna dice que .sql hay que pegar. Tambien vigila que node_modules no vaya por detras de package-lock, que es lo que pasa cada vez que alguien fusiona una rama con una dependencia nueva. En su primera ejecucion ya sirvio para algo: frontend/.env.local estaba vacio, o sea que el front nunca habia podido hablar con Supabase. Solo se habia rellenado el .env de la raiz.
Habia dos ficheros de credenciales con los mismos valores, y una copia se desincroniza sola: paso hoy, con el .env de la raiz relleno y el del front vacio, asi que la interfaz llevaba todo el rato sin poder hablar con Supabase y nadie se habia dado cuenta. Next solo carga ficheros de env de su propia carpeta, asi que next.config.ts lee el de arriba y traduce las dos claves publicas. Por LISTA BLANCA, que es lo importante del fichero: el .env tambien lleva la service_role, que se salta RLS, y todo lo que lleva NEXT_PUBLIC_ acaba en el JavaScript que descarga cualquiera. Se nombran una a una las dos claves publicas y no se recorre el fichero entero jamas. Ademas hay un guard que tumba el build si una variable publica lleva un nombre que huela a secreto. Comprobado sobre el bundle ya construido: la anon esta en .next/static (es su funcion) y la service_role no aparece.
Dos constantes absolutas mas del mismo tipo que PALLET_X: PALLET_DECK_M (0.144 m) y el suelo de zMax (0.30 m) suponian un europeo. Con la maqueta de 210 mm el pale se dibujaba 1.8 veces mas alto que TODA la carga, y el monton quedaba en el 18% del alzado: por eso parecia que no llegaban dimensiones. Ahora las dos se derivan del ancho del pale. Medido con los datos reales (carga 0.0797 m, pale 0.21 m): maqueta antes: carga 18.0% del alzado, pale 32.4% maqueta ahora: carga 63.8% del alzado, pale 20.2% europeo antes y ahora: 71.3% / 10.8% (sin cambios) Y las capturas del simulador, que ya se estaban subiendo y no las leia nadie: cliente getSnapshots, hook useSnapshots y un conmutador en cada panel para pasar del esquema a la foto. Arranca siempre en el esquema, que es el que lleva cotas y poligono de soporte; la foto esta para comprobar que el dibujo no miente, que hasta ahora no habia forma de saberlo. Va en EpisodeDashboard, asi que lo ganan Live y el detalle de la ejecucion. Se coge la captura de mayor after_seq: la del final del episodio.
# Conflicts: # frontend/api/hooks/index.ts # frontend/components/molecules/index.ts # frontend/components/screens/LiveScreen/useLiveScreen.ts
Alzado a escala del palé y captura real junto al esquema
Los tres sitios que exige AGENTS.md 5: TASKS en schema.py, el CHECK de SQL y TASK_TEXT en ui.ts. El test de vocabulario los compara, y de hecho fallo dos veces antes de cuadrar, que es justo para lo que esta. El CHECK de task sale del CREATE TABLE. Estaba dentro, y ahi no se puede ampliar: 'create table if not exists' no toca una tabla que ya existe, asi que el vocabulario solo se habria podido cambiar migrando a mano. Ahora se recrea en cada pasada, igual que el de failure, y ampliarlo es repegar 001. Los dos parsers del test tenian \w+, que no incluye el guion, y en TypeScript una clave con guion va entre comillas. Ninguno de los dos casos existia hasta ahora porque todos los valores eran palabras sueltas en ingles. Nota: el valor rompe la convencion de AGENTS.md 8 (nombres de codigo en ingles, textos en castellano); los otros dos son induction y palletizing. Se hace asi porque se ha pedido explicitamente.
Dos causas distintas dando el mismo banner, diagnosticadas con los datos de las ejecuciones reales. 1. Un episodio que llama a begin() y nunca a end() se queda en running para siempre, y getRunningEpisode le daba prioridad absoluta: Live enseñaba un episodio muerto con el aviso encima y no salia de ahi. Habia uno abierto 7.4 min con 0/10 colocados. Ahora se descartan los que llevan mas de 15 min abiertos, un orden de magnitud por encima de los 25-75 s que duran los medidos. Comprobado contra Supabase: un huerfano de 40 min queda fuera y el episodio vivo sigue pasando. 2. El umbral de 8 s era corto. El ritmo de subida es muy irregular: el mismo episodio de 204 s simulados tarda entre 26 y 71 s de reloj, o sea entre x2.9 y x7.9 con el mismo motion_speed declarado. Sube a 20 s. Y el mensaje mentia. useEpisodeRealtime ya devolvia 'subscribed' y no lo usaba nadie, asi que se decia 'conexion perdida' con el canal perfectamente suscrito: eso manda a mirar el wifi cuando el problema esta en el productor, que es el minuto que no sobra en una demo. Con el canal vivo ahora dice 'sin datos nuevos'.
…#7) * docs: README, licencia MIT y guía de contribución El repo va a ser público y no tenía README en la raíz: el único era el andamio de create-next-app bajo frontend/. Todo el conocimiento estaba en AGENTS.md, docs/API.md y el briefing, los tres en castellano. - README.md en inglés: qué es, requisitos, instalación derivada de dev.sh (las tres SQL y los tres guiones de seed), desarrollo, uso del SDK, dependencias con la auditoría de licencias, y la nota de que SUPABASE_SERVICE_KEY se salta RLS. Diagrama de arquitectura en Mermaid, que GitHub pinta sin herramientas. - LICENSE MIT, 2026 STACKSPECT. La auditoría (npx license-checker sobre el árbol instalado, pip-licenses sobre un venv limpio de backend[dev]) no encuentra nada incompatible: 32 paquetes en el conjunto que se publica, todos permisivos salvo el libvips precompilado de sharp (LGPL-3.0-or-later), que es una dependencia OPCIONAL de next, se carga dinámicamente y no se empaqueta ni se vendoriza aquí, y caniuse-lite (CC-BY-4.0), que son datos de build. - CONTRIBUTING.md con el flujo real: dev como rama de integración, los commits convencionales con asunto en castellano tal como se practican, dev.sh, las comprobaciones y lo que exige CI. Incluye las reglas duras de AGENTS.md que más caro salen en revisión: nada de Tailwind, colores solo desde styles/colors.css, y que `npm run lint` son tres comprobadores. - Versiones clavadas: package.json cierra los rangos ^ a lo que el lockfile ya resolvía y declara engines.node 22 (el de CI); pyproject clava pytest y ruff en el extra dev. Las dependencias de ejecución del SDK siguen vacías a propósito. `npm install --package-lock-only` no cambia ni un `resolved`. - .gitignore: .claude/settings.local.json solo estaba ignorado por el .git/info/exclude de este clon, que no viaja con el repo. - .gitmessage listaba los ámbitos del repo de simulación, no los de este. Verificado: npm ci + lint + build en verde; ruff y 116 tests en verde con DATABASE_URL contra un postgres:16 desechable; el árbol queda limpio tras un build completo; todos los enlaces del README resuelven. Faltan las capturas de pantalla: hay un hueco marcado en la sección Screenshots hasta que haya credenciales de Supabase con datos. * docs: AGENTS.md al dia con las rutas de detalle y los nuevos documentos Decia que /runs/[id] y /runs/[id]/[seed] estaban pendientes y llevan hechas desde el commit de la pantalla de detalle; el build las lista. Faltaban ademas 003_snapshots.sql, que dev.sh comprueba, y los tres documentos en ingles que entran en este PR. AGENTS.md pide corregir lo desactualizado en el mismo PR (preambulo), y sin esto el README y AGENTS.md se contradicen sobre que rutas existen. * docs: humo_live no marca synthetic, y el README lo decia mal palletizing.py y ejemplo_contrato.py ponen synthetic = true explicitamente; humo_live.py va por RunLog, que no toca la columna, y el default de 002_design.sql es false. Su run se lee como MEDIDO. El README decia que los tres marcaban synthetic, que es justo la confusion que el proyecto entero existe para evitar. * docs: capturas de las dos pantallas en el README Cuatro imagenes, 1440x1000, de la instancia corriendo contra el proyecto real: Ejecuciones y el detalle de un episodio, cada una en tema claro y oscuro. Van como <picture> para que sigan el tema de quien mira, igual que la cabecera de marca. En la captura de Ejecuciones hay dos ejecuciones seleccionadas a proposito: el panel sale con «No comparable» y los motivos. Un panel vacio no ensena nada, y bloquear la comparacion es justo lo que distingue a esta plataforma. El episodio es medido, con oracle, y el pie de la seccion lo dice: esos numeros sustituyen la percepcion por poses reales y no se pueden leer como los de un pipeline completo. * docs: optimizar los PNG del README, 726 KB -> 224 KB (-69 %) Mismos pixeles: 1440x1000 las capturas, sin recortar y sin reescalar. GitHub las pinta a ~890 px, asi que 1440 es lo que las mantiene nitidas en pantallas de alta densidad. runs-light 173,518 -> 50,559 -70.9 % runs-dark 173,367 -> 49,838 -71.3 % episode-light 175,181 -> 50,185 -71.4 % episode-dark 173,950 -> 48,824 -71.9 % mark-light 39,762 -> 24,772 -37.7 % wordmark-light 7,390 -> 5,038 -31.8 % Las cuatro capturas: oxipng al maximo y despues paleta de 256 por corte de medianas, sin difuminado (en una interfaz plana el difuminado emborrona el texto pequeno y encima engorda el fichero). Los dos de marca van SOLO sin perdida: oxipng ya los habia dejado en modo paleta con 256 y 166 colores, asi que cuantizar no tenia nada que rascar y solo podia estropear el borde. La primera tanda fue con maxima cobertura, que salia mas pequena (~44 KB), pero al mirarla de cerca viraba los grises neutros a rosa: el fondo de pagina pasaba de (245,245,243) a (249,233,242). Se nota en areas planas grandes. El corte de medianas conserva los neutros con una desviacion de 0 a 1. Comprobado a 3x sobre el texto pequeno y los acentos de color en los cuatro ficheros y en los dos temas: numeros en verde, insignia Oracle con su trama, "No comparable", el feed de eventos y las cotas del pale salen igual. Error maximo por canal 20/255 y ningun pixel por encima de 16. --------- Co-authored-by: manuamest <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pone
mainal dia condev, que es la rama de integracion del proyecto.Avance limpio:
maines ancestro directo dedev, 31 commits por detras y cero por delante, sin divergencia.Lleva todo el trabajo acumulado en
dev, no solo la documentacion: el esquema de Supabase y el SDK de telemetria, las tres pantallas del front, el flujo en vivo, la comparativa de ejecuciones y el CI, mas la pasada reciente de licencia MIT, guia de contribucion, dependencias clavadas y README con capturas reales.