Manual Técnico v1.0 · Hypenosys Indie Studio

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.

Jekyll GitHub Pages GitHub API Jules AI Kanban Data-as-Code Unreal Engine 5
01

Resumen Ejecutivo

Qué es y para qué sirve Hypenosys

Hypenosys 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.

Data-as-Code

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.

02

Mapa General del Sistema

Arquitectura de alto nivel

Flujo de datos

Usuario GitHub Pages Frontend Jekyll Dashboard / Docs / Jules GitHub API _data/*.json Commit repo

Flujo de autenticación

Frontend público GitHub OAuth / PAT Lista blanca equipo Operaciones restringidas Escritura JSON / API
Sin capa de servidor

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

ComponenteTecnologíaResponsabilidadEstado
Sitio públicoJekyll + LiquidPáginas, layouts, includes, SEOactual
Dashboard OpsHTML + JS vanillaKanban, métricas, perfiles, pipelineactual
Jules PanelHTML + JS vanillaControl de agente IA Julesactual
Centro de DocsJekyll + MarkdownGDD, Art Bible, Technical Docsactual
GitHub Contents APIREST API v3Lectura/escritura de _data JSONactual
Auth LayerGitHub OAuth / PATControl acceso equipoactual
Cloudflare WorkerProxy seguro para tokensrecomendado
03

Estructura del Repositorio

Archivos y carpetas reales observados
🔬
Dev Note

Estructura inferida de las páginas públicas desplegadas y patrones Jekyll estándar. Rutas marcadas con pendiente requieren confirmación manual.

RutaTipoResponsabilidadRiesgo
index.html / index.mdPáginaHome público del estudioBajo
dashboard.htmlPáginaCentro de operaciones KanbanAlto
jules-panel/DirectorioPanel de control del agente JulesMedio
documentacion/DirectorioCentro de documentaciónBajo
guia-comandos/DirectorioGuía técnica de comandos y flujosBajo
tech-stack/DirectorioStack tecnológico del proyectoBajo
CHANGELOG.htmlPáginaHistorial de cambios del proyectoBajo
guia-ue5-svn/DirectorioGuía de supervivencia UE5 + SVNBajo
estructura_del_proyecto/DirectorioDocumento de estructura generalBajo
plan_de_arranque/DirectorioPlan de arranque del proyectoBajo
_data/Jekyll dataJSON: tareas, team, configuraciónAlto
_layouts/JekyllPlantillas base de páginasMedio
_includes/JekyllNav, footer, componentes reutilizablesMedio
assets/css/EstilosCSS global del sitioMedio
assets/javascript/ScriptsMódulos JS del dashboard y panelesAlto
_config.ymlJekyll configConfiguración base del sitio JekyllAlto
GemfileRubyDependencias JekyllBajo
manual.htmlPáginaEste manual operativoBajo
04

Páginas Principales

Inventario de rutas y propósitos
PáginaRutaPropósitoAuth
Home/Presentación del estudio, equipo, project hubNo
Dashboard/dashboard.htmlKanban, métricas, pipeline, perfiles
Documentación/documentacion/GDD, Art Bible, Technical Docs, exploradorNo
Jules Panel/jules-panel/Control agente IA Jules
Comandos/guia-comandos/Guía técnica de flujos y comandosNo
Tech Stack/tech-stack/Stack tecnológico del proyectoNo
Changelog/CHANGELOG.htmlHistorial de versiones y cambiosNo
Estructura/estructura_del_proyecto/Documentación de estructuraNo
Plan Arranque/plan_de_arranque/Plan de inicio del proyectoNo
Guía UE5+SVN/guia-ue5-svn/Guía de supervivencia UE5 + SVNNo
Manual/manual.htmlEste documentoNo
05

Dashboard Operacional

Neural Operations Center

El 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.

06

Sistema Kanban

Gestión de tareas operativas

Columnas / Estados

Backlog/ToDo En Progreso QA Verification Completado

Estados internos de tarea

EstadoSignificado
PendingCreada, aún no priorizada
ToDoPriorizada, en backlog activo
WorkingEn progreso activo
KOFallida o bloqueada críticamente
FixedImplementada, pendiente QA
In ReviewEn revisión de código o diseño
OKAprobada por QA
ClosedCompletada y cerrada
ObsoleteDescartada / archivada

Campos de tarea

CampoTipoDescripción
idString/NumberIdentificador único. Atención: posibles conflictos ID numérico vs string
titleStringTítulo descriptivo de la tarea
descriptionMarkdownDescripción detallada
typeEnumFeature / Bug / Chore / Research
priorityEnumCritical / Major / Medium / Minor / Cosmetic / ToDo
milestoneEnumM1 / M2 / M3
branchEnumProgramación (PRO) / Arte (ART) / Diseño (DIS)
repoStringRepositorio asignado o "Sin asignar"
stageEnumTema principal: Concepto, Pre-producción, Tools, Arte, Programación, QA, Build, Post-launch
resolvedByStringLead resolutor (Axel/Alex/Dídac/Javi/Mitxel)
detectedByStringTester que detectó el issue
teamString[]Equipo asignado
emailStringEmail del responsable principal
startDateDateFecha de inicio
dueDateDateFecha de vencimiento
estimatedHoursNumberHoras estimadas
storyPointsNumberStory points para velocity tracking
statusEnumEstado actual (ver tabla de estados)
completionFloat 0-1Porcentaje de completitud
tagsString[]Etiquetas libres para categorización
acceptanceCriteriaMarkdownCriterios para marcar como OK
externalLinksArrayURLs de referencia externas
subtasksArraySubtareas anidadas
blockedByID[]IDs de tareas que bloquean a esta
blocksID[]IDs de tareas que esta tarea bloquea
commentsArrayComentarios del equipo
imagesArrayImágenes adjuntas (base64 o referencia)
changeHistoryArrayHistorial de cambios de la tarea
publishedBooleanSi la tarea es pública
ID Numérico vs String

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.

07

Gestión de Imágenes

Adjuntos visuales en tareas

Las 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.

Riesgo: Base64 en JSON

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
08

Autenticación y Sesión

Control de acceso y persistencia de credenciales

🔐 Flujo de acceso

  1. Usuario pulsa "Identificarse con GitHub"
  2. GitHub OAuth / PAT manual introducido
  3. Sistema verifica username contra lista blanca
  4. Acceso concedido → sesión activa
  5. Acceso denegado → pantalla "Acceso Denegado"

💾 Almacenamiento

  • localStorage — PAT GitHub, "Remember me", configuración del sistema
  • sessionStorage — 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

DatoStoragePersistenciaRiesgo
GitHub PATlocalStoragePermanente si remember meAlto
Repositorio objetivolocalStoragePermanenteBajo
Jules API KeylocalStoragePermanente por miembroAlto
Proveedor IA + modelolocalStoragePermanenteMedio
AI API KeylocalStoragePermanenteAlto
Sesión activasessionStorageSolo pestaña activaBajo
Riesgo crítico: Tokens en localStorage

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.

09

Integración GitHub API

Lectura, escritura y control de conflictos

El 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

GET archivo actual Obtener SHA Modificar datos PUT con SHA Commit automático
SHA obligatorio en 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

ErrorCausa probableSolución
401 UnauthorizedToken inválido o expiradoRegenerar PAT en GitHub Settings → Developer settings
403 ForbiddenToken sin scope repo o repo privadoCrear PAT con scope repo
404 Not FoundRuta incorrecta o rama equivocadaVerificar ruta exacta del archivo y nombre de rama
409 ConflictSHA desactualizado (write concurrente)Re-GET antes de PUT; nunca cachear SHA
JSON inválidoGuardado parcial o carácter especialValidar con JSON.parse() antes de PUT; usar try/catch
Rate limitDemasiadas 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
10

Jules Panel

Control del agente IA autónomo

Jules 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
Alias de 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
11

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).

🔬
Pendiente de auditar

La sincronización exacta con el repositorio y los estados de error específicos requieren revisión del código fuente de /documentacion/.

12

Archivos _data JSON

Capa de persistencia Data-as-Code
Estructura pendiente de auditoría completa

Los nombres y estructura exacta de los JSON requieren acceso directo al repositorio. Lo siguiente se infiere de la UI observada.

Archivo probablePropósito inferidoQuién escribeEstado
_data/tasks.json (o similar)Tareas del Kanban con todos los camposDashboard (crear/editar/archivar)pendiente
assets/data/team.json (o similar)Perfiles del equipo, bio, skills, coloresModal de edición de perfilpendiente
_data/config.json (o similar)Configuración del sistema, milestones activosModal de configuraciónpendiente

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"
}
Riesgo: Corrupción de JSON

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.

13

Módulos JavaScript

Arquitectura frontend
Módulo probableResponsabilidadRiesgos clave
dashboard.jsOrquestador principal del dashboard: init, render Kanban, métricas, modalesGod object, acoplamiento alto
github-api.jsCapa de abstracción sobre GitHub Contents API: GET, PUT, manejo SHARace conditions en escrituras concurrentes
auth.jsLogin GitHub, lista blanca, sessionStorage/localStoragePAT expuesto en memoria
jules.jsIntegración Jules API: lanzar sesión, listar sesiones, PRs, IssuesAPI key en localStorage
kanban.jsRender de columnas, drag, creación/edición de tareas, QA flowComparación IDs num vs string
images.js (o lightbox)Upload, base64, preview, lightbox, z-index modalJSON inflation, z-index conflicto
team.js (o profiles)Render perfiles equipo, modal stats, velocity chartsDatos hardcoded vs _data
repo-aliases.jsMapeo nombre real ↔ alias visual de repositorios JulesDesincronización alias/nombre real
🔬
Dev Note

Los nombres exactos de archivos JS requieren auditoría de assets/javascript/. Los nombres anteriores son inferidos de la funcionalidad observada.

14

Flujos Internos

Step-by-step de operaciones críticas
14.1 Crear una tarea
1. Click "Nueva Tarea"→ abre modal con pestañas Info/Equipo & Fechas/Extra
2. Rellenar campos→ validación frontend (título obligatorio mínimo)
3. Click "Guardar Tarea"→ genera ID único, objeto JS en memoria
4. GET _data/tasks.json→ obtiene SHA actual
5. Push tarea al array→ serializar a base64
6. PUT con SHA→ commit automático al repo
7. Re-render Kanban→ nueva tarjeta aparece en columna correcta
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.

Z-index

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.

15

Instalación Local

Levantar el sitio en desarrollo

Prerrequisitos

  • Ruby ≥ 3.0 (ruby -v para 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.

Funciones que no operan en local

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).

16

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 (o master)
  • Build: Jekyll automático en cada push
  • URL: https://hypenosys.github.io/

✅ Checklist post-deploy

  • Verificar que manual.html carga 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ó
Tiempo de propagación

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.

17

Mantenimiento

Checklists operativas

➕ Añadir página nueva

  • Crear archivo .html o directorio con index.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 :root si 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.log con datos sensibles
18

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).

19

Seguridad

Riesgos actuales y mitigaciones
RiesgoSeveridadEstadoMitigación
PAT GitHub en localStorageCríticoactualMigrar a Cloudflare Worker como proxy
Jules API Key en localStorageAltoactualProxy server-side o variables de entorno
AI API Key en localStorageAltoactualProxy server-side
XSS via contenido de tareasAltopendiente auditarSanitizar con DOMPurify antes de innerHTML
Control de acceso solo en UIMedioactualAceptable para equipo interno; no exponer datos sensibles
PAT scope excesivo (repo)MedioactualUsar Fine-Grained PAT con permisos mínimos por repositorio
JSON corrupto por escritura concurrenteMedioactualLock optimista + SHA check + validación pre-PUT
Base64 images inflando JSONMedioactualLímite de tamaño + migración a storage externo
Arquitectura Cloudflare Worker (recomendada)

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
20

Roadmap Técnico

Mejoras priorizadas por impacto
MejoraImpactoEsfuerzoPrioridad
Cloudflare Worker como proxy de tokensCríticoMedio🔥 P0
DOMPurify para sanitizar inputs de tareasAltoBajo🔥 P0
Validación JSON.parse() pre-PUT obligatoriaAltoBajo🔥 P0
Normalización de IDs a String al cargar JSONMedioBajoP1
Fine-Grained PAT con scopes mínimosMedioBajoP1
Compresión de imágenes pre-base64 (Canvas API)MedioMedioP1
Schema JSON para tareas (validación de campos)CalidadMedioP2
Sistema de migraciones para _dataMantenibilidadAltoP2
Tests con Playwright (E2E dashboard)CalidadAltoP3
Historial de cambios por tarea individualUXMedioP3
Exportación Markdown/PDF del manualUXBajoP3
Roles y permisos diferenciados (lead vs viewer)SeguridadAltoP3
21

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/*.json comprometidos 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 Conflict para 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.
22

Anexos

Checklists, tablas de referencia y ejemplos

Checklist 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/*.json confirmado (commit previo limpio)

Checklist de Auditoría

  • Revisar localStorage en DevTools: ningún valor sensible sin cifrar
  • Buscar console.log con 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

ArchivoSi se rompeBackup
_data/tasks.jsonKanban vacío, pérdida de todas las tareasGit history
_config.ymlBuild Jekyll falla, sitio no se publicaGit history
dashboard.htmlDashboard inaccesibleGit history
assets/javascript/Funcionalidades del dashboard rotasGit history
_layouts/default.htmlTodas las páginas Jekyll sin layoutGit 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

RiesgoProbabilidadImpactoAcción
PAT comprometido via XSSMediaCríticoMigrar a proxy server-side
JSON corrupto por fallo de redMediaAltoValidar pre-PUT + retry
Conflicto SHA escritura concurrenteBajaMedioLock optimista + merge manual
JSON inflado por base64 imagesAltaMedioLímite de tamaño + compresión
Build Jekyll falla en GitHub PagesBajaAltoVerificar en Actions post-push

Manual Operativo de Hypenosys · v1.0 · Generado 2026 · hypenosys/hypenosys.github.io