Manual Operativo
de Hypenosys
Centro de operaciones, documentación técnica y guía de mantenimiento. Arquitectura real basada en auditoría del repositorio hypenosys/hypenosys.github.io.
Resumen Ejecutivo
Qué es y para qué sirve HypenosysHypenosys es un estudio indie que desarrolla videojuegos con Unreal Engine 5. Su web (hypenosys.github.io) funciona simultáneamente como sitio público, centro de mando operativo y plataforma de documentación interna, todo sobre GitHub Pages sin servidor tradicional.
🎮 Estudio
Equipo de 5 personas: Axel, Alex, Javi, Dídac, Mitxel. Roles: programación, arte, diseño, animación, sistemas.
⬡ Dashboard
Kanban operativo con Kanban, pipeline, métricas QA, milestones, Hall of Fame y perfiles del equipo.
🔗 Persistencia
Sin base de datos. Los datos viven en _data/*.json en el repo. Las escrituras usan GitHub Contents API con tokens PAT.
🤖 Jules
Agente IA de Google para automatizar tareas en ramas del repositorio. Integrado desde el Jules Panel.
📦 Despliegue
GitHub Pages + Jekyll. Sin backend, sin CI custom. El build ocurre automáticamente en cada push a la rama principal.
🔒 Acceso
Autenticación via GitHub OAuth + lista blanca de usuarios. El dashboard requiere autenticación y pertenencia al equipo.
Todo el estado operativo (tareas, configuraciones, perfiles) se persiste como JSON en el repositorio Git. No hay base de datos externa. El historial de Git es el historial de datos.
Mapa General del Sistema
Arquitectura de alto nivelFlujo de datos
Flujo de autenticación
Toda la lógica corre en el navegador del usuario. El PAT se almacena en localStorage. Ver sección 08 para riesgos y sección 19 para recomendaciones de seguridad.
Componentes principales
| Componente | Tecnología | Responsabilidad | Estado |
|---|---|---|---|
| Sitio público | Jekyll + Liquid | Páginas, layouts, includes, SEO | actual |
| Dashboard Ops | HTML + JS vanilla | Kanban, métricas, perfiles, pipeline | actual |
| Jules Panel | HTML + JS vanilla | Control de agente IA Jules | actual |
| Centro de Docs | Jekyll + Markdown | GDD, Art Bible, Technical Docs | actual |
| GitHub Contents API | REST API v3 | Lectura/escritura de _data JSON | actual |
| Auth Layer | GitHub OAuth / PAT | Control acceso equipo | actual |
| Cloudflare Worker | — | Proxy seguro para tokens | recomendado |
Estructura del Repositorio
Archivos y carpetas reales observadosEstructura inferida de las páginas públicas desplegadas y patrones Jekyll estándar. Rutas marcadas con pendiente requieren confirmación manual.
| Ruta | Tipo | Responsabilidad | Riesgo |
|---|---|---|---|
index.html / index.md | Página | Home público del estudio | Bajo |
dashboard.html | Página | Centro de operaciones Kanban | Alto |
jules-panel/ | Directorio | Panel de control del agente Jules | Medio |
documentacion/ | Directorio | Centro de documentación | Bajo |
guia-comandos/ | Directorio | Guía técnica de comandos y flujos | Bajo |
tech-stack/ | Directorio | Stack tecnológico del proyecto | Bajo |
CHANGELOG.html | Página | Historial de cambios del proyecto | Bajo |
guia-ue5-svn/ | Directorio | Guía de supervivencia UE5 + SVN | Bajo |
estructura_del_proyecto/ | Directorio | Documento de estructura general | Bajo |
plan_de_arranque/ | Directorio | Plan de arranque del proyecto | Bajo |
_data/ | Jekyll data | JSON: tareas, team, configuración | Alto |
_layouts/ | Jekyll | Plantillas base de páginas | Medio |
_includes/ | Jekyll | Nav, footer, componentes reutilizables | Medio |
assets/css/ | Estilos | CSS global del sitio | Medio |
assets/javascript/ | Scripts | Módulos JS del dashboard y paneles | Alto |
_config.yml | Jekyll config | Configuración base del sitio Jekyll | Alto |
Gemfile | Ruby | Dependencias Jekyll | Bajo |
manual.html | Página | Este manual operativo | Bajo |
Páginas Principales
Inventario de rutas y propósitos| Página | Ruta | Propósito | Auth |
|---|---|---|---|
| Home | / | Presentación del estudio, equipo, project hub | No |
| Dashboard | /dashboard.html | Kanban, métricas, pipeline, perfiles | Sí |
| Documentación | /documentacion/ | GDD, Art Bible, Technical Docs, explorador | No |
| Jules Panel | /jules-panel/ | Control agente IA Jules | Sí |
| Comandos | /guia-comandos/ | Guía técnica de flujos y comandos | No |
| Tech Stack | /tech-stack/ | Stack tecnológico del proyecto | No |
| Changelog | /CHANGELOG.html | Historial de versiones y cambios | No |
| Estructura | /estructura_del_proyecto/ | Documentación de estructura | No |
| Plan Arranque | /plan_de_arranque/ | Plan de inicio del proyecto | No |
| Guía UE5+SVN | /guia-ue5-svn/ | Guía de supervivencia UE5 + SVN | No |
| Manual | /manual.html | Este documento | No |
Dashboard Operacional
Neural Operations CenterEl dashboard es el núcleo de gestión del estudio. Requiere autenticación GitHub y pertenencia a la lista blanca del equipo. Funciona como un project management tool completo sobre el navegador.
📊 Métricas rápidas
- Tareas Activas — contador live de tareas en curso
- QA Velocity Ratio — ratio global Fixed/Found de bugs
- Progreso Milestone — % completado del milestone activo (M1/M2/M3)
⬡ Jules Neural Ops
- Monitor de sesiones activas del agente Jules
- Sincronización con Jules Panel
- Vista de sesiones recientes
📈 Production Pipeline
- Milestone Burndown — gráfico de tareas restantes por fecha
- Dependency Graph — grafo de dependencias entre tareas
- Velocity Tracker — creadas vs completadas por semana
📊 Studio Group Stats
- Bug Rate — % bugs sobre total completado
- Blocker Health — tareas bloqueadas activas
- Burnout Index — stress del equipo en milestone
- Velocity Trend — completadas semanales
- Tag Heatmap — distribución de tags
🏆 Hall of Fame
- Vista Current: contribuciones en milestone activo
- Vista Archive: contribuciones históricas
⚰️ Cementerio de Tareas
- Tareas archivadas y fuera de producción
- Histórico de decisiones descartadas
Perfiles del Equipo
Modal con stats por miembro: tareas completadas, SP velocity, dependency impact, collaboration score, weekly SP velocity chart, contribución por tipo, rama distribution, tag intelligence badges. observado en frontend
Modales operativos
Crear tarea, editar tarea, mover a QA (asignar tester/resolutor), asignar equipo, ampliar imagen (lightbox), editar perfil, configuración del sistema, configuración API de IA.
Sistema Kanban
Gestión de tareas operativasColumnas / Estados
Estados internos de tarea
| Estado | Significado |
|---|---|
Pending | Creada, aún no priorizada |
ToDo | Priorizada, en backlog activo |
Working | En progreso activo |
KO | Fallida o bloqueada críticamente |
Fixed | Implementada, pendiente QA |
In Review | En revisión de código o diseño |
OK | Aprobada por QA |
Closed | Completada y cerrada |
Obsolete | Descartada / archivada |
Campos de tarea
| Campo | Tipo | Descripción |
|---|---|---|
id | String/Number | Identificador único. Atención: posibles conflictos ID numérico vs string |
title | String | Título descriptivo de la tarea |
description | Markdown | Descripción detallada |
type | Enum | Feature / Bug / Chore / Research |
priority | Enum | Critical / Major / Medium / Minor / Cosmetic / ToDo |
milestone | Enum | M1 / M2 / M3 |
branch | Enum | Programación (PRO) / Arte (ART) / Diseño (DIS) |
repo | String | Repositorio asignado o "Sin asignar" |
stage | Enum | Tema principal: Concepto, Pre-producción, Tools, Arte, Programación, QA, Build, Post-launch |
resolvedBy | String | Lead resolutor (Axel/Alex/Dídac/Javi/Mitxel) |
detectedBy | String | Tester que detectó el issue |
team | String[] | Equipo asignado |
email | String | Email del responsable principal |
startDate | Date | Fecha de inicio |
dueDate | Date | Fecha de vencimiento |
estimatedHours | Number | Horas estimadas |
storyPoints | Number | Story points para velocity tracking |
status | Enum | Estado actual (ver tabla de estados) |
completion | Float 0-1 | Porcentaje de completitud |
tags | String[] | Etiquetas libres para categorización |
acceptanceCriteria | Markdown | Criterios para marcar como OK |
externalLinks | Array | URLs de referencia externas |
subtasks | Array | Subtareas anidadas |
blockedBy | ID[] | IDs de tareas que bloquean a esta |
blocks | ID[] | IDs de tareas que esta tarea bloquea |
comments | Array | Comentarios del equipo |
images | Array | Imágenes adjuntas (base64 o referencia) |
changeHistory | Array | Historial de cambios de la tarea |
published | Boolean | Si la tarea es pública |
Existe un bug conocido donde los IDs de tarea pueden ser number en algunos contextos y string en otros. Las comparaciones con === fallan. Usar siempre String(id) al comparar. Ver sección 18 Troubleshooting.
Gestión de Imágenes
Adjuntos visuales en tareasLas tareas del Kanban admiten imágenes adjuntas. observado en frontend: existe un lightbox para ampliar imágenes y controles de eliminación dentro del modal de tarea.
📎 Añadir imagen
Input de archivo, drag & drop o paste de portapapeles. Se convierte a base64 y se almacena en el objeto de tarea.
🔍 Lightbox
Clic en miniatura abre vista ampliada. Navegación entre imágenes de la misma tarea. Z-index debe ser superior al modal de tarea.
Las imágenes en base64 pueden inflar el archivo JSON a varios MB, ralentizar la lectura via GitHub API, superar el límite de 100MB por archivo y degradar el rendimiento del dashboard. Límite recomendado: 500KB por imagen, máx. 5 imágenes por tarea.
Recomendaciones futuras
- Comprimir imágenes con Canvas API antes de guardar
- Generar thumbnails para la vista de lista
- Usar GitHub Releases como CDN de assets
- Carpeta
/assets/uploads/con commits directos - Lazy loading con
loading="lazy"e IntersectionObserver - Almacenamiento externo (Cloudinary, S3) a largo plazo
Autenticación y Sesión
Control de acceso y persistencia de credenciales🔐 Flujo de acceso
- Usuario pulsa "Identificarse con GitHub"
- GitHub OAuth / PAT manual introducido
- Sistema verifica username contra lista blanca
- Acceso concedido → sesión activa
- Acceso denegado → pantalla "Acceso Denegado"
💾 Almacenamiento
localStorage— PAT GitHub, "Remember me", configuración del sistemasessionStorage— sesión activa si no se marca "Remember me"localStorage— Jules API Key (por miembro)localStorage— configuración proveedor IA (modelo, API key, endpoint)
Configuraciones almacenadas
| Dato | Storage | Persistencia | Riesgo |
|---|---|---|---|
| GitHub PAT | localStorage | Permanente si remember me | Alto |
| Repositorio objetivo | localStorage | Permanente | Bajo |
| Jules API Key | localStorage | Permanente por miembro | Alto |
| Proveedor IA + modelo | localStorage | Permanente | Medio |
| AI API Key | localStorage | Permanente | Alto |
| Sesión activa | sessionStorage | Solo pestaña activa | Bajo |
localStorage es accesible por cualquier script de la página y vulnerable a XSS. Un PAT con scope repo permite leer y escribir todos los repositorios privados del usuario. Migrar a Cloudflare Worker que actúe como proxy, eliminando el token del cliente.
Proveedores IA soportados observado
Anthropic (Claude), OpenAI (ChatGPT), Google Gemini, Mistral AI, OpenRouter, Ollama (local), Custom OpenAI-compatible.
Integración GitHub API
Lectura, escritura y control de conflictosEl sistema usa la GitHub Contents API (GET/PUT /repos/{owner}/{repo}/contents/{path}) para leer y escribir los archivos JSON de _data/ directamente desde el navegador.
Flujo de escritura
La GitHub Contents API requiere el sha del archivo actual para actualizarlo. Sin SHA correcto la API devuelve 409 Conflict. El SHA debe obtenerse en el mismo GET previo a la escritura, no cachearse.
Troubleshooting de API
| Error | Causa probable | Solución |
|---|---|---|
401 Unauthorized | Token inválido o expirado | Regenerar PAT en GitHub Settings → Developer settings |
403 Forbidden | Token sin scope repo o repo privado | Crear PAT con scope repo |
404 Not Found | Ruta incorrecta o rama equivocada | Verificar ruta exacta del archivo y nombre de rama |
409 Conflict | SHA desactualizado (write concurrente) | Re-GET antes de PUT; nunca cachear SHA |
| JSON inválido | Guardado parcial o carácter especial | Validar con JSON.parse() antes de PUT; usar try/catch |
| Rate limit | Demasiadas peticiones (5000/h autenticado) | Cachear lecturas; agrupar escrituras |
Buenas prácticas
- Siempre hacer GET → modificar → PUT en una sola operación secuencial
- Validar el JSON con
JSON.parse()antes de codificar a base64 para el PUT - Implementar lock optimista: si SHA en servidor ≠ SHA local, pedir confirmación al usuario
- Loguear cada commit en consola con ruta y SHA para facilitar debugging
- Nunca almacenar el SHA entre sesiones; siempre refrescarlo
Jules Panel
Control del agente IA autónomoJules es el agente IA de Google (jules.google.com) que ejecuta tareas de programación de forma autónoma en ramas de repositorios GitHub. El Jules Panel de Hypenosys es una interfaz personalizada para controlarlo.
⚙️ Configuración de sesión
- Selección de repositorio (con alias visual)
- Selección o creación de rama
- Título de sesión (opcional)
- Toggle: requerir aprobación humana
- Toggle: auto-crear PR al finalizar
📋 Vistas disponibles
- Sesiones — listado con filtros y estado
- PRs — pull requests del repositorio
- Issues — issues activos
- Branches — ramas del repositorio
- Actions — GitHub Actions workflows
- Notificaciones — alertas del repositorio
El Jules Panel permite configurar un nombre visual (alias) diferente al nombre real del repositorio en GitHub. El nombre real se usa en las llamadas API; el alias solo es visual. Configurado via localStorage.
Dependencias
- Jules API Key — obtenida en jules.google.com/settings, guardada en
localStorage - GitHub PAT — necesario para acceso a repositorios privados
- Configuración de repositorio objetivo
Centro de Documentación
/documentacion/Sección pública de documentación del proyecto. observado en frontend: contiene explorador de docs, sincronización con repositorio y múltiples categorías.
📖 GDD & Lore
Game Design Document y documentación de universo narrativo.
⚙️ Technical Docs
Arquitectura, flujos técnicos, APIs, configuración.
🎨 Art Bible
Guía visual, paletas, referencias de estilo, assets.
Botones de acción observados: Editar en GitHub (abre el archivo en github.com/edit), Abrir en Obsidian (usa el protocolo obsidian:// para abrir el vault local).
La sincronización exacta con el repositorio y los estados de error específicos requieren revisión del código fuente de /documentacion/.
Archivos _data JSON
Capa de persistencia Data-as-CodeLos nombres y estructura exacta de los JSON requieren acceso directo al repositorio. Lo siguiente se infiere de la UI observada.
| Archivo probable | Propósito inferido | Quién escribe | Estado |
|---|---|---|---|
_data/tasks.json (o similar) | Tareas del Kanban con todos los campos | Dashboard (crear/editar/archivar) | pendiente |
assets/data/team.json (o similar) | Perfiles del equipo, bio, skills, colores | Modal de edición de perfil | pendiente |
_data/config.json (o similar) | Configuración del sistema, milestones activos | Modal de configuración | pendiente |
Ejemplo mínimo de tarea
{
"id": "task-001",
"title": "Implementar sistema de movimiento",
"type": "Feature",
"priority": "Major",
"status": "Working",
"milestone": "M1",
"branch": "PRO",
"storyPoints": 5,
"completion": 0.4,
"tags": ["gameplay", "ue5"],
"assignedTo": "Alex",
"createdAt": "2025-01-15T10:00:00Z"
}
Un error de red durante un PUT puede dejar el JSON parcialmente escrito. Siempre: (1) validar con JSON.parse() antes de PUT, (2) mantener backup del JSON previo, (3) implementar retry con exponential backoff.
Módulos JavaScript
Arquitectura frontend| Módulo probable | Responsabilidad | Riesgos clave |
|---|---|---|
dashboard.js | Orquestador principal del dashboard: init, render Kanban, métricas, modales | God object, acoplamiento alto |
github-api.js | Capa de abstracción sobre GitHub Contents API: GET, PUT, manejo SHA | Race conditions en escrituras concurrentes |
auth.js | Login GitHub, lista blanca, sessionStorage/localStorage | PAT expuesto en memoria |
jules.js | Integración Jules API: lanzar sesión, listar sesiones, PRs, Issues | API key en localStorage |
kanban.js | Render de columnas, drag, creación/edición de tareas, QA flow | Comparación IDs num vs string |
images.js (o lightbox) | Upload, base64, preview, lightbox, z-index modal | JSON inflation, z-index conflicto |
team.js (o profiles) | Render perfiles equipo, modal stats, velocity charts | Datos hardcoded vs _data |
repo-aliases.js | Mapeo nombre real ↔ alias visual de repositorios Jules | Desincronización alias/nombre real |
Los nombres exactos de archivos JS requieren auditoría de assets/javascript/. Los nombres anteriores son inferidos de la funcionalidad observada.
Flujos Internos
Step-by-step de operaciones críticas14.1 Crear una tarea
14.2 Editar una tarea
Click "Edit" en tarjeta → modal precargado con datos actuales → modificar campos → "Guardar Tarea" → GET SHA → encontrar tarea por ID en array → reemplazar objeto → PUT → re-render. Bug frecuente: si el ID en memoria es number y en JSON es string, el find() falla silenciosamente. Solución: tasks.find(t => String(t.id) === String(targetId)).
14.3 Mover a QA
Botón "QA" en tarjeta → modal de QA Verification → seleccionar Tester (Detectado por) y Resolutor (Resuelto por) → Confirmar → cambia estado a Fixed o In Review → actualiza métricas QA Velocity Ratio → persiste en JSON.
14.4 Archivar tarea (Cementerio)
Acción archivar → estado cambia a Obsolete → tarea sale del Kanban activo → aparece en "Cementerio de Tareas" → persiste en JSON. Las métricas de milestone la excluyen pero las del historial la incluyen.
14.5 Añadir imagen a tarea
Input file / drag-drop / paste → FileReader.readAsDataURL() → preview en modal → validar tamaño (recomendado < 500KB) → añadir a array images del objeto tarea → al guardar, se persiste como base64 en JSON → en la tarjeta se muestra miniatura → click miniatura abre lightbox → lightbox z-index debe superar al modal de tarea.
El lightbox debe tener z-index mayor que el modal de tarea. Si el lightbox queda "detrás", aumentar su z-index en CSS a un valor superior al modal.
14.6 Lanzar sesión de Jules
Jules Panel → seleccionar repositorio (nombre real, no alias) → seleccionar/crear rama → escribir prompt o vincular tarea → configurar opciones (aprobación, auto-PR) → Lanzar → Jules API recibe la petición → respuesta con session ID → panel muestra la sesión en lista → polling periódico para actualizar estado.
14.7 Login persistente
Pantalla de login → introducir GitHub token (PAT) → llamada a GitHub API /user para verificar → extraer login username → comparar con lista blanca hardcodeada en JS → si match: guardar en localStorage (o sessionStorage si no hay "Remember me") → redirigir al dashboard → en cada carga del dashboard verificar que el token almacenado sigue siendo válido.
Instalación Local
Levantar el sitio en desarrolloPrerrequisitos
- Ruby ≥ 3.0 (
ruby -vpara verificar) - Bundler (
gem install bundler) - Git
Setup
git clone https://github.com/hypenosys/hypenosys.github.io.git
cd hypenosys.github.io
bundle install
bundle exec jekyll serve --livereload
Acceder en http://localhost:4000. LiveReload recarga automáticamente al guardar cambios.
El Dashboard y Jules Panel requieren un PAT de GitHub válido y conexión a internet. La autenticación GitHub OAuth puede no funcionar con localhost. Las escrituras a _data/ van contra el repositorio real en GitHub, no en local.
Alternativa sin Jekyll (solo páginas estáticas)
# Con Python 3
python -m http.server 8080
# Con Node.js (npx serve)
npx serve . -p 8080
Las páginas Liquid/Jekyll no renderizarán. Solo funcionan las páginas HTML puras (dashboard.html, pages/jules-panel.html, manual.html).
Despliegue en GitHub Pages
Publicación automática⚙️ Configuración
- Repositorio:
hypenosys/hypenosys.github.io - Tipo: repositorio de organización (
*.github.io) - Rama de despliegue:
main(omaster) - Build: Jekyll automático en cada push
- URL:
https://hypenosys.github.io/
✅ Checklist post-deploy
- Verificar que
manual.htmlcarga en la URL pública - Comprobar que la navegación superior enlaza correctamente
- Verificar que no hay rutas absolutas (
/vs relativas) - Comprobar assets (CSS, JS) con devtools Network
- Test responsive en móvil
- Verificar que el CHANGELOG se actualizó
Los cambios en GitHub Pages pueden tardar entre 30 segundos y 5 minutos en publicarse. Si el sitio no actualiza, forzar recarga con Ctrl+Shift+R para evitar caché del navegador.
Mantenimiento
Checklists operativas➕ Añadir página nueva
- Crear archivo
.htmlo directorio conindex.md - Añadir front matter Jekyll si usa layout
- Enlazar en navegación principal
- Añadir referencia en Home si es relevante
- Actualizar CHANGELOG
- Verificar despliegue post-push
📦 Añadir campo a tarea
- Añadir campo al modal de creación (HTML)
- Añadir campo al modal de edición (HTML)
- Actualizar el objeto JS de tarea
- Actualizar render de tarjeta Kanban si procede
- Actualizar schema de validación si existe
- Documentar en sección 06 de este manual
- Migrar tareas existentes en JSON (añadir campo con valor default)
🎨 Actualizar estilos
- Identificar el CSS correcto (
assets/css/) - Modificar variables CSS
:rootsi es cambio global - Revisar responsive a 320px, 768px, 1280px
- Verificar contraste de colores (WCAG AA mínimo)
- Comprobar que no rompe el dashboard ni Jules
🔒 Revisión de seguridad
- Verificar que no hay tokens en código fuente
- Revisar que inputs de tarea se sanitizan antes de renderizar
- Comprobar que la lista blanca de usuarios es correcta
- Verificar scopes del PAT usado
- Revisar que no hay
console.logcon datos sensibles
Troubleshooting
Diagnóstico y resolución de problemas🔴 No carga el dashboard / pantalla en blanco
Causa: Error JS en la inicialización. Abrir DevTools → Console y buscar el error. Causas frecuentes: módulo JS no encontrado (ruta rota), JSON malformado en _data/, token expirado que bloquea el init.
Solución: Revisar Network tab para confirmar que todos los .js cargan con 200. Verificar que _data/*.json es JSON válido con JSON.parse() en consola.
🔴 No guarda cambios (silencioso)
Causa: 401 (token inválido), 409 (SHA desactualizado), o error de red silenciado. Archivo: github-api.js o equivalente.
Solución: Añadir console.error al catch de las promesas de escritura. Verificar respuesta de la API en Network tab. Regenerar PAT si es 401.
🔴 Token inválido / Remember me no persiste
Causa: El PAT fue regenerado o revocado en GitHub. O el dominio del sitio cambió y localStorage se limpió. Storage: localStorage.
Solución: Abrir DevTools → Application → Local Storage → eliminar la entrada del token → volver a hacer login con PAT nuevo.
🔴 Edit Task no responde / modal no precarga datos
Causa más frecuente: El ID en el DOM (atributo data-id) es string pero en el array de tareas en memoria es number. tasks.find(t => t.id === clickedId) devuelve undefined.
Solución: tasks.find(t => String(t.id) === String(clickedId)). Normalizar todos los IDs a string en el momento de cargar el JSON.
🔴 Ghost clicks en móvil (doble disparo)
Causa: El evento touchend dispara también un click sintético 300ms después. Los listeners duplicados ejecutan la acción dos veces.
Solución: Usar solo addEventListener('click', ...) y añadir touch-action: manipulation en CSS a los elementos interactivos. O usar { passive: false } + preventDefault() en touch.
🔴 Lightbox queda detrás del modal
Causa: Z-index del lightbox menor al del modal de tarea.
Solución: Asegurar que el lightbox tiene z-index superior al modal. Valores recomendados: Modal tarea: 1000, Lightbox: 2000, Navegación: 500.
🔴 JSON corrupto en _data
Causa: Escritura interrumpida (pérdida de red durante PUT) o error al serializar.
Solución: Ir a github.com/hypenosys/hypenosys.github.io/commits/main → encontrar el último commit válido → ver el JSON en ese commit → restaurar manualmente via GitHub web editor o CLI: git revert <commit-sha>.
Prevención: Validar con JSON.stringify(data) + JSON.parse() antes de cualquier PUT.
🔴 Jules no lanza sesión / no aparecen repositorios
Causas: Jules API Key inválida, PAT sin permisos sobre el repo, o alias de repositorio configurado incorrectamente (se envía el alias en lugar del nombre real).
Solución: Verificar que la Jules API Key está en localStorage del miembro. Confirmar que el nombre real del repositorio (no el alias) se usa en las llamadas API. Revisar consola para errores de CORS o 401.
🔴 Assets no cargan en GitHub Pages
Causa: Rutas absolutas (/assets/...) que funcionan en local pero en un subpath de GitHub Pages fallan. O archivos con nombres en mayúsculas/minúsculas distintos al import.
Solución: Usar rutas relativas o el filtro Jekyll {{ "/assets/css/main.css" | relative_url }}. En _config.yml, configurar baseurl correctamente (vacío para repositorios *.github.io).
Seguridad
Riesgos actuales y mitigaciones| Riesgo | Severidad | Estado | Mitigación |
|---|---|---|---|
PAT GitHub en localStorage | Crítico | actual | Migrar a Cloudflare Worker como proxy |
Jules API Key en localStorage | Alto | actual | Proxy server-side o variables de entorno |
AI API Key en localStorage | Alto | actual | Proxy server-side |
| XSS via contenido de tareas | Alto | pendiente auditar | Sanitizar con DOMPurify antes de innerHTML |
| Control de acceso solo en UI | Medio | actual | Aceptable para equipo interno; no exponer datos sensibles |
PAT scope excesivo (repo) | Medio | actual | Usar Fine-Grained PAT con permisos mínimos por repositorio |
| JSON corrupto por escritura concurrente | Medio | actual | Lock optimista + SHA check + validación pre-PUT |
| Base64 images inflando JSON | Medio | actual | Límite de tamaño + migración a storage externo |
Crear un Worker que reciba peticiones del frontend con un token de sesión temporal, ejecute las llamadas a GitHub API con el PAT almacenado como variable de entorno del Worker, y retorne solo los datos necesarios. El PAT nunca llega al navegador del usuario.
GitHub PAT mínimo recomendado (Fine-Grained)
- Repositorio: solo
hypenosys/hypenosys.github.io - Permisos:
Contents: Read & Write,Metadata: Read - Expiración: 90 días máximo, con renovación programada
Roadmap Técnico
Mejoras priorizadas por impacto| Mejora | Impacto | Esfuerzo | Prioridad |
|---|---|---|---|
| Cloudflare Worker como proxy de tokens | Crítico | Medio | 🔥 P0 |
| DOMPurify para sanitizar inputs de tareas | Alto | Bajo | 🔥 P0 |
Validación JSON.parse() pre-PUT obligatoria | Alto | Bajo | 🔥 P0 |
| Normalización de IDs a String al cargar JSON | Medio | Bajo | P1 |
| Fine-Grained PAT con scopes mínimos | Medio | Bajo | P1 |
| Compresión de imágenes pre-base64 (Canvas API) | Medio | Medio | P1 |
| Schema JSON para tareas (validación de campos) | Calidad | Medio | P2 |
Sistema de migraciones para _data | Mantenibilidad | Alto | P2 |
| Tests con Playwright (E2E dashboard) | Calidad | Alto | P3 |
| Historial de cambios por tarea individual | UX | Medio | P3 |
| Exportación Markdown/PDF del manual | UX | Bajo | P3 |
| Roles y permisos diferenciados (lead vs viewer) | Seguridad | Alto | P3 |
Glosario Técnico
Diccionario de términos del sistema- Data-as-Code
- Patrón arquitectónico donde los datos de aplicación se almacenan como archivos en un repositorio Git. El historial Git actúa como base de datos con versionado, auditoría y rollback gratuitos. Hypenosys usa
_data/*.jsoncomprometidos en GitHub. - GitHub Contents API
- Endpoint REST de GitHub (
GET/PUT /repos/{owner}/{repo}/contents/{path}) que permite leer y escribir archivos en un repositorio directamente desde el navegador, incluyendo la creación de commits automáticos. Docs: docs.github.com/en/rest/repos/contents - SHA (Secure Hash Algorithm)
- En el contexto de GitHub API, el hash del objeto Git que representa el estado actual de un archivo. Necesario para actualizar archivos (PUT). Si el SHA enviado no coincide con el del servidor, la API devuelve
409 Conflictpara prevenir sobrescrituras accidentales. - PAT (Personal Access Token)
- Token de autenticación de GitHub que actúa como contraseña de API. Reemplaza a las contraseñas básicas. Puede ser Classic (acceso amplio por scopes) o Fine-Grained (permisos por repositorio específico). Docs: docs.github.com/en/authentication
- OAuth
- Protocolo de autorización estándar (OAuth 2.0) que permite a aplicaciones de terceros acceder a recursos en nombre del usuario sin compartir credenciales. GitHub OAuth Apps permiten el flujo de autorización web con redirect_uri.
- Jules
- Agente de IA de Google (jules.google.com) que ejecuta tareas de programación de forma autónoma en ramas de repositorios GitHub: lee código, implementa cambios, crea commits y opcionalmente abre PRs.
- Kanban
- Metodología de gestión visual de trabajo originada en Toyota. Las tareas se representan como tarjetas que fluyen entre columnas que representan estados del proceso (Backlog → En Progreso → QA → Completado).
- QA Velocity Ratio
- Métrica operativa de Hypenosys. Ratio entre bugs marcados como Fixed y bugs encontrados (Found) en el mismo período. Un ratio cercano a 1.0 indica que se resuelven los bugs tan rápido como se encuentran.
- Story Points
- Unidad relativa de estimación de esfuerzo en metodologías ágiles. No representan horas sino complejidad relativa. Se usan para calcular la velocidad del equipo (SP completados por sprint/semana).
- Milestone
- Hito o punto de entrega en la planificación del proyecto. En Hypenosys: M1, M2, M3. Agrupa tareas por objetivo de release y permite calcular el burndown chart.
- Lightbox
- Patrón UI donde al hacer clic en una imagen se muestra ampliada en una capa superpuesta (overlay) oscureciendo el fondo. Común en galerías y sistemas de adjuntos.
- localStorage
- API del navegador (
window.localStorage) para almacenar pares clave-valor de forma persistente entre sesiones. Los datos persisten aunque se cierre el navegador. Accesible por cualquier JS del mismo origen. Límite ~5MB. - sessionStorage
- Igual que localStorage pero los datos se eliminan al cerrar la pestaña o el navegador. Más seguro para datos de sesión temporal.
- GitHub Pages
- Servicio de hosting estático gratuito de GitHub. Publica automáticamente el contenido del repositorio (con o sin Jekyll) en
https://{user}.github.io/{repo}. Sin servidor, sin base de datos, sin costes de hosting. - Jekyll
- Generador de sitios estáticos (jekyllrb.com) escrito en Ruby, integrado nativamente con GitHub Pages. Procesa Markdown, Liquid templates, front matter YAML y la carpeta
_data/para generar HTML estático.
Anexos
Checklists, tablas de referencia y ejemplosChecklist de Release
- Todas las tareas del milestone en estado Closed u OK
- QA Velocity Ratio ≥ 0.95 en el milestone
- No hay Blocker Health activos (tareas bloqueadas)
- CHANGELOG.html actualizado con número de versión y fecha
- Build de Jekyll pasa sin errores (revisar Actions en GitHub)
- Assets verificados en GitHub Pages (Network 200)
- Responsive verificado en mobile/tablet/desktop
- No hay tokens ni API keys en el código fuente
- Backup de
_data/*.jsonconfirmado (commit previo limpio)
Checklist de Auditoría
- Revisar
localStorageen DevTools: ningún valor sensible sin cifrar - Buscar
console.logcon tokens o datos de usuario en JS - Verificar que el PAT tiene solo los scopes necesarios
- Comprobar que inputs de tareas pasan por sanitización
- Revisar que el lightbox tiene z-index correcto
- Validar que todos los JSON en
_data/son válidos (JSON.parse()) - Verificar que no hay rutas absolutas hardcodeadas que rompan en subpaths
- Comprobar que IDs de tareas se comparan siempre como String
Tabla de Archivos Críticos
| Archivo | Si se rompe | Backup |
|---|---|---|
_data/tasks.json | Kanban vacío, pérdida de todas las tareas | Git history |
_config.yml | Build Jekyll falla, sitio no se publica | Git history |
dashboard.html | Dashboard inaccesible | Git history |
assets/javascript/ | Funcionalidades del dashboard rotas | Git history |
_layouts/default.html | Todas las páginas Jekyll sin layout | Git history |
Ejemplo de Commit Seguro (GitHub API)
// 1. Obtener estado actual con SHA
const getRes = await fetch(
`https://api.github.com/repos/hypenosys/hypenosys.github.io/contents/_data/tasks.json`,
{ headers: { Authorization: `token ${pat}`, Accept: 'application/vnd.github.v3+json' } }
);
const current = await getRes.json();
const sha = current.sha;
// 2. Modificar datos
const tasks = JSON.parse(atob(current.content.replace(/\n/g, '')));
tasks.push(newTask);
// 3. Validar antes de guardar
const jsonStr = JSON.stringify(tasks, null, 2);
JSON.parse(jsonStr); // Lanza excepción si inválido
// 4. PUT con SHA
const putRes = await fetch(
`https://api.github.com/repos/hypenosys/hypenosys.github.io/contents/_data/tasks.json`,
{
method: 'PUT',
headers: { Authorization: `token ${pat}`, Accept: 'application/vnd.github.v3+json' },
body: JSON.stringify({
message: `ops: add task "${newTask.title}"`,
content: btoa(unescape(encodeURIComponent(jsonStr))),
sha: sha
})
}
);
Tabla de Riesgos
| Riesgo | Probabilidad | Impacto | Acción |
|---|---|---|---|
| PAT comprometido via XSS | Media | Crítico | Migrar a proxy server-side |
| JSON corrupto por fallo de red | Media | Alto | Validar pre-PUT + retry |
| Conflicto SHA escritura concurrente | Baja | Medio | Lock optimista + merge manual |
| JSON inflado por base64 images | Alta | Medio | Límite de tamaño + compresión |
| Build Jekyll falla en GitHub Pages | Baja | Alto | Verificar en Actions post-push |
Manual Operativo de Hypenosys · v1.0 · Generado 2026 · hypenosys/hypenosys.github.io