| name | html-artifact-integrity |
| description | Use al escribir HTML grande — trozos y verificación previa. |
| version | 1.0.0 |
| tags | ["html","escritura-ficheros","verificacion","integridad","aurora"] |
Integridad de artefactos HTML grandes
Cuándo usar
Al generar o entregar cualquier HTML >5 KB (informes, auditorías, dashboards, landings), y al citar cifras de repos o herramientas en documentos comparativos. Incluye referencia del estado verificado del repo Aurora: references/aurora-repo-estado.md.
Problema observado (2026-08-28)
Los writes de ficheros HTML grandes (>5 KB) con write_file pueden corromperse a mitad de stream: texto truncado, fragmentos absurdos insertados (1</ bus, < doble tbody), placeholders sin sentido. Ocurrió 2 veces en la misma sesión. El tool reporta verified: true aunque el contenido esté roto — la verificación del tool NO detecta corrupción semántica.
Flujo fiable (validado)
- Partir en trozos de ~2-3 KB máximo. Escribir el trozo 1 con
write_file al destino (sobrescribe), y los siguientes a ficheros temporales ($LOCALAPPDATA/Temp/parteN.html).
- Verificar cada trozo tras escribirlo: releer o comprobar que el contenido tiene sentido antes de continuar. Si un trozo sale corrupto, reescribir SOLO ese trozo (más pequeño si hace falta).
- Ensamblar:
cat parte2.html parte3.html >> destino.html en un solo comando de terminal.
- Verificación final programática (no a ojo) — balance de etiquetas y contenido. SIEMPRE regex con límite
[\s>]: un h.count('<p') ingenuo también cuenta <path (SVG) y <b cuenta <body/<br>, produciendo MISMATCH falsos que obligan a re-verificar (pasó 2026-09-01 con p 25≠22 que eran 22=22):
import re
h = open(fichero, encoding='utf-8').read()
for tag in ['section','div','p','span','table','tbody','thead','html','body']:
o = len(re.findall(r'<%s(?=[\s>])' % tag, h))
assert o == h.count('</%s>' % tag), (tag, o)
assert h.rstrip().endswith('</html>')
4b. Validar la sintaxis del JS antes de la captura visual (2026-09-01). Tras ensamblar, extraer el <script> y pasarlo por node --check — caza errores de sintaxis de ensamblaje (un trozo que dejó cat[...]; sin cerrar, un rint() duplicado) que el balance de etiquetas no ve y que de otro modo aparecerían como «página vacía» en la captura:
python -c "import re,sys; h=open(sys.argv[1],encoding='utf-8').read(); open('tmp.js','w').write(re.search(r'<script>(.*)</script>',h,re.S).group(1))" file.html
node --check tmp.js && echo OK
Verificar que el JS extraído sea sintácticamente válido por sí mismo; al dividir por trozos conviene que cada parte empiece/termine en frontera de bloque (p. ej. el array de datos acaba con ];) para que la unión sea limpia.
5. Verificación visual: abrir con open_preview y leer con read_preview para confirmar que el texto renderizado es coherente (detecta corrupción que el balance de etiquetas no ve).
6. Limpiar los temporales de $LOCALAPPDATA/Temp.
Pitfalls
- No confiar en el
verified: true del write: verifica hash on-disk vs lo enviado, pero si el stream se corrompió antes de llegar, "verifica" contenido corrupto.
- NO anexar HTML grande con heredoc de bash (
cat >> destino << 'EOF'): un bloque de ~30 KB con comillas/apóstrofes del texto falla con unexpected EOF while looking for matching ''' y se pierde el intento entero. Flujo correcto: escribir cada parte como fichero propio con write_file y ensamblar con cat parte1 parte2 > destino en un solo comando, luego validar balance (paso 4) y borrar las partes.
- Reescribir el fichero entero tras detectar corrupción reintroduce el mismo riesgo: mejor trozos más pequeños.
- Si
execute_code está bloqueado por consentimiento, el plan B es trozos + cat (validado). No insistir con execute_code.
- La corrupción puede insertar texto que parece HTML válido: balance de etiquetas + lectura del preview es el mínimo aceptable antes de entregar.
Verificación de datos antes de citarlos
Cuando el artefacto compara herramientas/repos, verificar cada cifra con el sistema vivo (API GitHub, ficheros locales, DB), nunca con memoria o con lo que diga un README desactualizado. Ejemplos reales (2026-08-28): el README de MasterMind decía "143 skills" y la cuenta real en ChromaDB era 311; el skill de Aurora referenciaba un CHEATSHEET.md eliminado del repo (404 verificado por API de GitHub). Para porcentajes (p. ej. barras de lenguajes), calcularlos desde gh api repos/O/R/languages con un script — no estimarlos a ojo.
Rebuild estático cuando "no se ve nada" (2026-08-28, galería Aurora)
Detalle completo del caso: references/js-rendered-blank-page.md.
Síntoma: el usuario reporta página en blanco o casi vacía pese a HTTP 200 y CSS íntegro (el diff live-vs-repo era solo CRLF). Causa raíz típica: el contenido se pinta por JS (innerHTML de tokens leídos del CSSOM, KPIs, gráficos) — cualquier fallo del script o del CDN deja la página vacía, y los parches incrementales NO lo arreglan. Remedio: rehacer el HTML con contenido 100% estático visible sin JS, y dejar el JS solo como mejora progresiva (scrollspy, reveals, switchers). Reglas del rebuild:
- Nunca
opacity:0 condicionado a que el JS añada una clase (is-visible): si el JS muere, el contenido desaparece. Gatear reveals solo cuando un script haya añadido antes una clase js al root.
- Mostrar solo API verificada: extraer las clases reales de cada CSS con regex sobre los ficheros (catálogo
classes.json por pack) antes de escribir ejemplos — nada de clases recordadas de memoria.
- Ensamblar desde
gallery-parts/parteN.html con cat parte1..4 > destino (mismo flujo de trozos de arriba), y reescribir el JS final para que maneje los controles REALES del HTML (botones data-skin, selects) — los anchors huérfanos del sidebar (api-*) se cazan con el chequeo href="#x" → ids inexistentes.
Verificación visual de páginas con scroll-reveals (2026-09-01, nantest web)
Una landing narrativa con elementos .rv{opacity:0} revelados por IntersectionObserver miente al capturarla headless: Chrome pinta antes de que el observer dispare, y vision_analyze reporta «página vacía / contraste bajísimo / contenido no cargado» cuando en un navegador real todo está bien. Dos reglas para no perseguir fantasmas:
- Modo aplanado de prueba. Añadir al JS:
if (location.hash==='#test') document.body.classList.add('aplanado') con CSS body.aplanado .rv{opacity:1!important;transform:none!important;transition:none!important} (y lo mismo para animaciones de entrada tipo @keyframes: animation:none!important). Capturar SIEMPRE la URL con #test para auditoría visual. Sin esto, cada franja parece rota y se "arregla" contraste que no estaba roto.
- Altura real antes de recortar. No adivinar
--window-size=...,7600: el fondo fijo rellena el hueco y el análisis ve "sección final vacía" (en realidad fuera del documento). Sondear la altura con un script sonda que haga document.title='ALTO:'+document.documentElement.scrollHeight + --dump-dom + grep -o "ALTO:[0-9]*", y luego recapturar con --window-size ≈ esa altura (o recortar el PNG a scrollHeight con PIL). Ojo: la altura cambia con el viewport — medir con el mismo ancho y una altura generosa.
- Fallback de revelado para usuarios reales:
setTimeout(()=>document.querySelectorAll('.rv:not(.on)').forEach(el=>el.classList.add('on')),4000) — si el observer falla o el contenido ya está visible sin scroll, nada queda oculto.
- Firmas y HUD en
position:absolute dentro de secciones muy altas quedan fuera de franjas de captura: verificar el cierre midiendo la última .act (offsetTop+offsetHeight vía title sonda), no asumiendo que el final de página = final del screenshot.
Verificación headless con Chrome en Windows (alternativa a browser_exec)
Cuando el navegador de automatización no esté disponible (pide permiso de depuración remota en Chrome), verificar por terminal:
"C:/Program Files/Google/Chrome/Application/chrome.exe" --headless=new --disable-gpu \
--dump-dom --virtual-time-budget=12000 "file:///C:/ruta/absoluta/archivo.html" > dump.html
- SIEMPRE
file:/// con ruta absoluta en barras — pasar la ruta cruda (gallery.html) hace que Chrome la trate como dominio → DNS error y el dump es la página de error.
- Aserciones por grep sobre el dump: nº de secciones/anclas presentes, ausencia de mojibake,
<canvas> creados por Three.js, 0 clases gate.
- WebGL/Three.js: añadir
--enable-unsafe-swiftshader --use-gl=angle. Errores de consola: --enable-logging=stderr y filtrar uncaught|error (excluir ruido GPU/Fontconfig).
- Screenshot: ruta absoluta con barras SIEMPRE (
--screenshot=C:/Users/.../out.png --window-size=1440,2400). Con ruta relativa (y sobre todo combinada con --dump-dom y redirecciones), Chrome la resuelve contra su propio cwd y falla Failed to write file: Acceso denegado (0x5) — verificar el PNG con ls después.
- Piezas canvas/generativas (plexus, partículas, animación JS pura): el grep del dump NO dice nada del render. Flujo validado (2026-09-01, nantest):
--virtual-time-budget≥8000 + --screenshot absoluto → vision_analyze pidiendo defectos concretos (nodos huérfanos, zonas vacías, contraste de líneas/halos) → corregir → re-capturar → re-verificar. Esperar al menos una ronda de crítica visual; es normal que la primera versión se vea "apagada o a medio dibujar". Truco de densidad: enlaces k-nearest (cada nodo enlaza con sus K más cercanos, K≈3) eliminan los puntos huérfanos que el umbral de proximidad deja sueltos; halos con createRadialGradient en modo lighter para que los nodos brillen.
- Deploy GitHub Pages desde repo nuevo:
gh repo create O/R --public → push main → gh api repos/O/R/pages -X POST -f "source[branch]=main" -f "source[path]=/" → poll curl -s -o /dev/null -w '%{http_code}' URL cada ~20s (404 durante building es normal; ~40s hasta 200, confirmar tamaño con curl -s URL | wc -c). Repos ya publicados: gh api repos/O/R/pages/builds/latest --jq '{status,commit}' + byte-exactitud vs wc -c fichero. La caché de Pages puede tardar ~1 min extra tras built.
- Pitfalls de repo recién creado con
git init (verificado 2026-09-06, bolo-palma): (a) la rama por defecto de git init es master, así que git push -u origin main falla con src refspec main does not match any — hacer git branch -m master main ANTES de pushear. (b) Si el repo remoto NO existe, git push da Repository not found; crear con gh repo create O/R --public --source . --remote origin --push. (c) gh repo create puede devolver X Unable to add remote "origin" (el remote ya estaba) pero NO garantiza que el push se completara: verificar SIEMPRE con git ls-remote origin — si sale vacío, re-ejecutar git push -u origin main; solo cuando refs/heads/main aparece hay que activar Pages. Activar Pages vía API con -f "source[branch]=main" -f "source[path]=/"; si el repo ya tiene Pages activado la API devuelve 409 GitHub Pages is already enabled (no es error, solo consultar pages/builds/latest).
Pitfall: vision_analyze puede ALUCINAR el texto literal que "ve" (verificado 2026-09-06, bolo-palma)
vision_analyze es fiable para layout/composición/paleta (alineación, desbordes, colores, "hay una tabla") pero NO para citar texto específico. En esta sesión afirmó que la página contenía las cadenas «TIPO, BOLO, BEBÉ», «la bolera se construye a lo ancho», «Hace 2 días» y «Escuelas de bolos (cabecera)» — ninguna existe en el HTML real. Si corriges contenido basándote en esos claims, rompes texto correcto y persigues fantasmas.
Reglas:
- El source es la fuente de verdad, no la visión. Para validar texto/estructura real, leer el archivo con
execute_code (regex sobre el HTML) o el DOM del navegador. Ej.: contar <h3> de cards, extraer filas de <tbody>, listar fechas del blog — eso dice qué existe de verdad.
- La visión sí confirma propiedades no-textuales con fiabilidad: paleta monocroma (0 elementos naranja/ámbar), ausencia de desbordes, secciones alineadas. Usarla para eso.
- Cuando la visión cita un texto concreto que no escribiste, NO lo tomes como "corrección" — marca el claim como no verificado y contrasta contra el source antes de editar.
- Detectar colores cálidos (naranja/ámbar) en el source con regex sobre los hex, no con la visión:
re.findall(r'#(?:e0a800|8a6d00|ff6|f60|...)', html).
Piezas creativas «flipalo»: espectáculo primero, robustez como red de seguridad (2026-09-01, nantest)
Lección de dirección de la sesión completa: el usuario pidió "algo totalmente loco, filosófico, visual". El agente falló POR EXCESO de prudencia: primero pieza de una sola pantalla («quería más web, con historia»), luego web editorial sobrecargada de técnica («has seguido demasiado las normas, más filosófico y menos técnico»), luego un poema calmado con JS de enjambre («está roto»), y por último CSS puro estático («no es lo que buscábamos»). Lo que sí validó: vórtice canvas hiperactivo + palabras estáticas flotando encima + <noscript> de emergencia. Reglas:
- El espectáculo es el producto. Canvas generativo denso (anillos girando, estelas con
rgba de arrastre, modos lighter, reacción al puntero, explosiones al click) + texto estático mínimo por encima (mix-blend-mode:screen + glow). La robustez NO debe quitar el show: se añade como capa de seguridad, nunca sustituyendo la pieza.
- Red de seguridad en 3 capas (invisible cuando todo funciona): texto HTML/CSS siempre visible sobre el canvas;
<noscript> con un gradiente de fondo; contadores/reveales nunca ocultan contenido de base.
- Pedir confirmación creativa con 2-3 opciones de giro antes de iterar más — iterar a ciegas sobre "esto no es lo que queríamos" quema 4 versiones.
Verificación de páginas LONG-SCROLL (multi-sección 100vh) (2026-09-01, descenso nantest)
Una página de N secciones de 100vh (≈7000px+) no se puede verificar con una captura de página completa: Chrome headless con --window-size=...,6900 renderiza el canvas fixed y la primera viewport de contenido DOM, y el resto sale vacío aunque la página esté perfecta. Lo que NO funcionó: scrollTo(0,y) inyectado en load + --virtual-time-budget (captura antes del scroll), ni fragmentos #id en file://. Método validado, en orden de preferencia:
- Verificación estructural por DOM (barata y suficiente para contenido):
--dump-dom y contar palabras/.rv/.flot por <section> con regex en Python. Confirma que las 8 secciones tienen su texto completo, que nada se perdió en el ensamblaje.
- Modo captura por sección para auditoría visual puntual: CSS
html.capmode .sec{display:none} + html.capmode .sec.activa{display:flex}, y en el JS if (location.hash==='#test'){ const n=+(new URLSearchParams(location.search).get('sec')||0); document.querySelectorAll('.sec').forEach((s,i)=>s.classList.toggle('activa',i===n)); }. Capturar ...?sec=4 una sección por screenshot (100vh normal). El screenshot puede seguir congelando animaciones — juzgar composición estática.
- Si el canvas es fixed con estela, en modo test dibujar un solo frame (
if(test) cuadro(); else requestAnimationFrame(cuadro)) para que la captura no pille estados intermedios del fade.
No iterar el diseño visual por franjas de una captura larga: las conclusiones serán falsas (vacío = fuera del documento, no sección rota). Verificar el cierre (última sección + firma) capturando la última sección en modo ?sec=N-1.
Pitfall: captura headless congela animaciones CSS — estados iniciales opacity:0 parecen "roto" (2026-09-01)
Chrome headless con --virtual-time-budget congela las CSS animations en su primer frame: N capturas con distintos budgets producen PNGs byte-idénticos (señal de congelación). Consecuencia: cualquier elemento cuya visibilidad arranque en opacity:0 dentro de un keyframe («aparece») se captura invisible y vision_analyze reporta «página vacía / falta contenido» aunque en un navegador real funcione — y el agente acaba «arreglando» contraste que no estaba roto o degradando la pieza a texto estático soso.
- Regla de diseño a prueba de captura: la visibilidad base NUNCA depende del estado inicial de una animación. Escribir
opacity:.9 en la clase y que el keyframe solo module (@keyframes respira{0%,100%{opacity:.9}50%{opacity:.4}}), en vez de opacity:0 + keyframe de entrada.
- Detección: si dos screenshots con distinto
--virtual-time-budget pesan exactamente igual, las animaciones están congeladas — juzgar composición estática, no presencia.
prefers-reduced-motion y @media print deben fijar opacity visible (animation:none + opacity explícita), mismo motivo.
Validar un pack CSS de design system por código (sin navegador) (2026-09-07)
Cuando no se puede abrir el navegador (permiso de depuración remota no autorizado)
y hay que verificar un HTML/CSS de Aurora, hacerlo por código con
python scripts/validate-aurora-css.py <archivo.html> (o desde el repo, con el
dir_repo por defecto). Comprueba balance de llaves, existencia de clases nz-*
(caza typos/clases inventadas que el audit-aurora.py no ve) y existencia de
tokens var(--nz-*). Detalle y pitfall del regex BEM en
references/validar-pack-css-por-codigo.md.
Pitfall: patch difuso puede borrar el bloque equivocado
En HTML grandes, un patch cuyo old_string coincide ambiguamente puede aplicar el reemplazo en otra zona y eliminar contenido hermano (pasó al insertar una tabla duplicando un cierre </section>). SIEMPRE leer el diff devuelto línea a línea y releer la zona tocada tras cada patch; si borró de más, restaurar el bloque en el siguiente paso antes de continuar. Con secuencias repetidas (</table>\n</div>\n</section>), incluir contexto único (un <h4> o comentario) en old_string para anclar el sitio.