Manual de Depuración Frontend de Pryne
Síntoma→triage para las demos vanilla-JS de Pryne: páginas HTML de un solo archivo controladas por un módulo window.Roll (design-demos/js/roll-*.js), un shader WebGL con textura de grano y fallback 2D, un bucle de renderizado rAF, Lenis para scroll suave (vendorizado), y GSAP cargado asíncrono desde CDN como mejora progresiva.
ANTECEDENTES REQUERIDOS: superpowers:systematic-debugging posee la disciplina genérica de causa raíz (sin correcciones sin investigación). Síguela; este skill solo proporciona la tabla de síntomas y trampas específicas del proyecto.
Herramientas: para saber cómo controlar el navegador, capturar consolas, emular movimiento reducido y tomar trazas, usa pryne-browser-lab. Este skill asume que ya puedes ejecutar esos comandos.
Lee primero el comentario de cabecera
Cada demo v4 (v4-a a v4-d en design-demos/) lleva su historial de auditoría como un comentario HTML al principio del archivo — qué se encontró roto, qué se arregló y por qué. Verificado en los cuatro archivos a fecha de 2026-07-22. Antes de rediagnosticar algo, ejecuta head -30 de la demo que estás depurando. Varios "errores" que encontrarás están documentados como regresiones ya corregidas; si uno vuelve, estás buscando qué lo revirtió, no una causa nueva.
Conoce también la estructura de compilación (a fecha de 2026-07-22): solo v4-d-hybrid.html usa los módulos compartidos roll-*.js. v4-a/b/c son compilaciones comparativas reducidas con su propio JS inline — las comprobaciones específicas de Roll de esta tabla no se aplican a ellas, pero las mismas clases de fallo sí.
Cero errores de consola es el estado de reposo
El estándar es cero errores de consola (la rúbrica CRITIQUE en design-demos/DECISIONS.md lo exige). v3 fue verificado con cero según el registro de estado del proyecto; las variantes v4 no están certificadas hasta la campaña de dirección (pryne-design-direction-decision). Cualquier error de consola es un defecto que debe ser triado, nunca ruido ambiente que ignorar.
Tabla de síntoma → triage
| Síntoma | Causa probable | Comprobación discriminatoria |
|---|---|---|
| La animación/placa de desarrollo se ejecuta (o termina) antes de que su sección esté visible en el scroll | Regresión en el control de visibilidad. El comentario de cabecera de v4-d registra esto como la corrección de auditoría de mayor prioridad: las placas de características "se ejecutaban incondicionalmente desde la carga de la página y terminaban de resolverse antes de que alguien hiciera scroll hasta ellas". El desarrollo debe estar controlado por la clase .in del panel. | Experimento 1 a continuación |
| La animación de revelado recorta mal, salta o nunca se activa | clip-path aplicado al elemento que observa el IntersectionObserver, en lugar de a un contenedor interno. La corrección del concepto-C (llevada a v4-d, según su comentario de cabecera): el revelado con clip-path vive en un contenedor interno, nunca en el elemento observado. | Inspecciona el elemento: ¿el nodo observado y el nodo con clip-path son el mismo nodo? |
| El cursor del SO desaparece, ningún cursor personalizado lo reemplaza | No debería repetirse — corregido 2026-07-22 (encontrado por una auditoría en frío, corregido el mismo día): el manejador por evento (roll-cursor.js:17-20) y su lugar de llamada pointermove (roll-core.js:26-29) ahora ambos capturan y restauran (null Roll.cursorUpdate, eliminan .custom-cursor), además de la captura original en tiempo de configuración; el z-index de #cur/#ring subió de 50 a 70 por encima de #flash (60) para que el pulso del clímax no pueda pintar sobre el cursor. Si el síntoma reaparece, uno de esos guardas ha regresionado o un nuevo código oculta el cursor fuera de este contrato. | document.body.classList.contains('custom-cursor') vs Roll.cursorUpdate — true/null juntos es el estado roto; si ambos parecen intactos, comprueba getComputedStyle(cur).opacity y si desaparece solo en el destello del clímax |
| WebGL muestra fantasmagorías/rastros, o los colores se mezclan incorrectamente con la página | Falta de limpieza por fotograma, o discrepancia de alfa pre-multiplicado. roll-shader.js llama gl.clear(gl.COLOR_BUFFER_BIT) cada fotograma y crea el contexto con { alpha: true, premultipliedAlpha: false } (ambas correcciones de auditoría, según el comentario de cabecera de v4-d). | Compara roll-shader.js con git: git diff HEAD -- design-demos/js/roll-shader.js |
| El canvas del shader está en blanco, sin textura de grano | (a) Movimiento reducido — GL nunca se configura intencionalmente (useGL = !Roll.reduced && setupGL()); (b) Contexto WebGL perdido — el manejador webglcontextlost establece la opacidad del canvas a 0 y falla en silencio; (c) Se adquirió un contexto 2D antes de que WebGL tuviera su oportunidad (un contexto por canvas, siempre — documentado al inicio de roll-shader.js). | Roll.reduced primero. Para pérdida de contexto, comprueba el efecto observable del manejador: getComputedStyle(document.getElementById('tone')).opacity === '0''. Precaución: Roll.shader.useGL es una instantánea en tiempo de configuración (roll-shader.js:120-121) — el manejador webglcontextlost cambia solo la variable de cierre (roll-shader.js:130-132), por lo que Roll.shader.useGLpermanecetrue` después de la pérdida de contexto; no lo uses para descartar la pérdida |
| La coreografía del scroll está muerta pero la página se renderiza y el scroll funciona bien | Orden de carga de scripts o rotura del espacio de nombres window.Roll. El orden de los scripts es fundamental — la cadena y los comandos de verificación viven en pryne-frontend-architecture. Cada módulo es un IIFE que comparte estado solo a través de window.Roll; un error lanzado en un módulo temprano deja sin recursos a todo lo que viene después. | Experimento 2 a continuación; la consola mostrará Roll.X is undefined nombrando el módulo que se quedó sin recursos |
| Todo el movimiento muerto para un usuario; funciona bien en tu máquina | prefers-reduced-motion está activo para ellos. Esto es comportamiento correcto, no un error: roll-core.js establece Roll.reduced a partir de la media query, y cada módulo se controla con ella (portada eliminada, placas se renderizan completamente desarrolladas al instante, Lenis omitido, shader apagado). No lo "arregles". | Experimento 3 a continuación |
| Sensación de scroll inconsistente entre trackpad/rueda/táctil | Variación del delta de scroll bruto — la razón documentada para vendorizar Lenis (ver design-demos/DECISIONS.md). Lenis envuelve el scroll nativo, por lo que window.scrollY sigue siendo válido; es solo una mejora de sensación, nula bajo movimiento reducido. | typeof Lenis (¿cargado?), luego comprueba si la demo lo construye en su módulo de scroll |
| Lo básico funciona pero faltan los efectos hover/kinéticos/del cursor | GSAP desde CDN falló o fue bloqueado. GSAP se carga async desde jsdelivr (con fijación SRI) y cambia Roll.enhanced = true a través de Roll.upgradeMotion() solo cuando llega. La mejora progresiva es por diseño: la coreografía básica debe seguir funcionando sin ella. Si lo básico también está roto, esta no es tu causa. | Experimento 4 a continuación |
| Audio silenciado | Silenciado es el estado predeterminado: roll-audio.js comienza silenciado, persiste la elección en localStorage['ch001_muted'], y solo inicia el zumbido ambiente desde el clic en #mute-toggle (un gesto del usuario, que satisface la política de reproducción automática). Toda la síntesis está envuelta en try/catch y no tiene assets. Comprueba el estado del interruptor SND antes de sospechar del código. | Roll.audio.muted, y localStorage.getItem('ch001_muted') |
| El HUD fijo se superpone/oculta el pie de página | Regresión de la corrección de desvanecimiento del pie de página: un IntersectionObserver en footer alterna .faded en el medidor de ritmo y el botón de silencio (roll-scroll.js). | Desplázate hasta el pie de página; comprueba si .faded aparece en #beatmeter |
Experimentos discriminantes
Ejecuta estos en la consola de la página o mediante evaluate_script (ver pryne-browser-lab). Cada uno responde una pregunta antes de que toques el código.
1 — ¿El desarrollo de la placa está correctamente controlado por la visibilidad?
({ workVisible: document.getElementById('work').classList.contains('in'),
plate1: { d: Roll.featurePlate.d, done: !!Roll.featurePlate.done },
plate2: { d: Roll.featurePlate2.d, done: !!Roll.featurePlate2.done } })
En una carga nueva, antes de desplazarte a la sección de trabajo: workVisible: false y ambos valores d cerca de 0. Si d está subiendo (o done: true) mientras workVisible es false, el control ha regresionado.
2 — ¿La columna vertebral Roll está intacta?
({ core: !!window.Roll, reduced: Roll?.reduced, enhanced: Roll?.enhanced,
shader: !!Roll?.shader, audio: !!Roll?.audio, plates: !!Roll?.plates,
cursorUpdate: Roll?.cursorUpdate === null ? 'null' : typeof Roll?.cursorUpdate })
Nota sobre cursorUpdate: se inicializa como null (roll-core.js:23) y también es null antes de que GSAP cargue y en dispositivos que no son pointer: fine — null solo no prueba nada. "Falló de forma segura" se indica solo cuando document.body.classList.contains('custom-cursor') es true y cursorUpdate es null (ver la fila del cursor en la tabla de triage).
Cualquier false/undefined nombra el módulo que no terminó — comprueba la consola para el error que lanzó, y verifica el orden de los scripts en el HTML contra el orden crítico anterior.
3 — ¿Está "todo el movimiento muerto" simplemente por movimiento reducido?
({ mediaQuery: matchMedia('(prefers-reduced-motion: reduce)').matches, roll: Roll.reduced })
Ambos true → funcionando según lo diseñado; reproduce el informe emulando movimiento reducido (pryne-browser-lab). mediaQuery: true pero roll: false (o viceversa) → un módulo está leyendo la fuente de verdad incorrecta.
4 — ¿Se cargó la capa de mejora GSAP?
({ gsapLoaded: !!window.gsap, enhanced: Roll.enhanced, reduced: Roll.reduced })
gsapLoaded: false → CDN bloqueado/falló (verifica el panel de red para la solicitud a jsdelivr; una discrepancia SRI también llega aquí). gsapLoaded: true, enhanced: false, reduced: false → upgradeMotion() no se ejecutó — comprueba el hook onload de la etiqueta script.
Trampas que cuestan tiempo real
| Trampa | La historia (una línea) | La valla |
|---|---|---|
| "Verificado funcionando" ≠ dirección correcta | v3 NIGHT DRIVE estaba completamente construido, cero errores de consola, verificado — luego fue reemplazado porque el concepto unificador era incorrecto (relato completo: pryne-failure-archaeology). | No gastes esfuerzo de pulido/depuración defendiendo una compilación cuyo concepto no ha sido confirmado como la dirección actual. Verifica primero DECISIONS.md y el estado del vault. |
| Rediagnosticar una corrección documentada | Cada comentario de cabecera de v4 es un registro de cambios de defectos ya encontrados y ya corregidos. | Lee el comentario de cabecera antes de abrir las herramientas de desarrollo. Si una corrección listada parece estar rota, busca la reversión, no el síntoma. |
| "Arreglar" el movimiento reducido | Todo el movimiento apagado es el comportamiento diseñado para usuarios de prefers-reduced-motion, controlado en un solo lugar (Roll.reduced). | Ejecuta el experimento 3 antes de tratar un informe de falta de movimiento como un defecto. |
| Adquirir un contexto 2D "para probar algo" | Un canvas solo puede tener un tipo de contexto; tomar 2D primero hace que WebGL sea imposible en ese canvas para siempre (documentado en roll-shader.js). | Nunca llames a getContext('2d') en #tone en experimentos de consola. |
Cuándo NO usar este skill
- Disciplina de depuración genérica (hipótesis, cambios mínimos, causa raíz antes de corregir) → superpowers:systematic-debugging.
- Cómo controlar el navegador/medir (anatomía de herramientas MCP, trazas, Lighthouse, capturas de pantalla) → pryne-browser-lab.
- Decidir si un comportamiento viola un invariante de diseño (control de movimiento reducido, personalidad a nivel de susurro, vendorización) → pryne-frontend-architecture.
- La historia completa de por qué algo fue construido/revertido → pryne-failure-archaeology.
- Otras pilas (React/compilaciones con frameworks, proyectos que no son de Pryne): la tabla anterior es específica de la familia de demos
window.Roll; solo la disciplina genérica se transfiere.
Procedencia y mantenimiento
Ejecuta scripts/verify.sh (en el directorio de este skill) para verificar todo lo siguiente de una vez.
Hechos actualizados a fecha de 2026-07-22, verificados contra el repositorio ~/Documents/Coding Folder/Portfolio (test) en el commit 457868a. Vuelve a verificar antes de confiar:
- Lista y tamaños de módulos:
wc -l design-demos/js/*.js - Orden de carga de scripts:
grep -n "<script" design-demos/v4-d-hybrid.html - Qué demos usan los módulos compartidos:
grep -l "roll-core.js" design-demos/*.html - La convención de comentario de cabecera sigue vigente:
for f in design-demos/v4-*.html; do echo "== $f"; head -5 "$f"; done - Control de movimiento reducido:
grep -n "prefers-reduced-motion\|Roll.reduced" design-demos/js/*.js - Control de visibilidad de placas:
grep -n "classList.contains('in')" design-demos/js/roll-scroll.js - Lenis vendorizado y cableado:
ls design-demos/js/vendor/ && grep -n "new Lenis" design-demos/js/roll-scroll.js - Hook de mejora GSAP:
grep -n "upgradeMotion" design-demos/js/roll-core.js design-demos/v4-d-hybrid.html - Estado de silencio de audio y clave de almacenamiento:
grep -n "ch001_muted" design-demos/js/roll-audio.js - Barra de cero errores de consola:
grep -n "console errors" design-demos/DECISIONS.md