54 historias en formato XP ("Como <actor>, quiero <acción> para <beneficio>"), consolidadas desde el kanban del proyecto (48 aceptadas de 54). Agrupadas por el rol del actor; cada una conserva su Definition of Done y la iteración de origen — ver en el tablero →.
Como visitante (10)
Como visitante, quiero ver el portfolio público con proyectos reales para evaluar el trabajo de Mike
- ✓ BaseLayout, Navbar, Footer y la página principal con hero, proyectos y proceso quedan operativos.
- ✓ ProjectCard consume datos reales de GitHub vía API de repos.
- ✓ Tailwind CSS v4 configurado con paleta y tipografía del sitio.
Como visitante, quiero ver el estado en vivo de los servicios (uptime, incidentes, latencia)
- ✓ /status muestra uptime global de 30 días e incidentes activos.
- ✓ Animación EKG con latencia p95 en tiempo real por monitor (13 commits de status).
- ✓ API de latencia expone último estado y hora de chequeo por monitor.
Como visitante, quiero que el sitio sea indexable y compartible (SEO técnico completo)
- ✓ JSON-LD y breadcrumbs estructurados en páginas de proyecto.
- ✓ Feed RSS y notificación IndexNow a buscadores en cada publicación (8 commits de IndexNow).
- ✓ Web app manifest y apple-touch-icon para instalación como PWA.
Como visitante, quiero una vitrina pública de herramientas y notas técnicas del stack
- ✓ Secciones /tools y /notes publicadas con 5 artículos técnicos iniciales.
- ✓ OG images generadas dinámicamente para cada nota/proyecto.
Como visitante, quiero escanear un QR y ver cómo un tablero reconoce mi dispositivo en vivo sin cookies
- ✓ Landing con consentimiento crea sala + QR; el tablero (/board) refleja los dispositivos por polling corto (Vercel no soporta WebSocket).
- ✓ Al reentrar en incógnito o borrar cookies, el contador de "revisitas" sube: mismo dispositivo re-identificado (validado por smoke test de API).
- · Prueba end-to-end en navegador real del ciclo crear → escanear → revisita (canvas/WebGL/audio solo producen valores en un browser).
Como visitante, quiero entender qué me delata: recolector propio contrastado con una librería y una capa de comportamiento
- ✓ Recolector propio: canvas, WebGL, AudioContext, fuentes, pantalla, zona horaria, CPU/memoria, idiomas, touch y UA.
- ✓ Segunda opinión con FingerprintJS (open source): su visitorId se muestra junto al hash propio en el tablero.
- ✓ Entropía dinámica: cada señal bloqueada/vacía suma 0 bits; se declara como estimación educativa (EFF Panopticlick / AmIUnique), no medición poblacional.
- ✓ Capa de comportamiento: cadencia de tecleo, velocidad de mouse y giroscopio (con permiso iOS 13+).
Como visitante, quiero explorar el portal de clientes de la demo con datos frescos, no con lo que dejó otro visitante
- ✓ seedPortalDemo siembra clientes, facturas y client_users ficticios en la base demo, reusando scripts/seed-demo.mjs.
- ✓ Cron de Vercel resiembra el portal demo a las 4am todos los días (vercel.json).
- ✓ Endpoint público de demo añadido a las rutas accesibles sin sesión; pagos bloqueados también en el portal demo (middleware).
Como visitante, quiero que se me avise claramente cuándo la demo del portal está disponible
- ✓ Banner de demo en PortalLayout y mensajes de disponibilidad en login y /tools.
- ✓ Flag demoAvailable/demoUnavailable propaga el estado real de la base demo a la UI.
- ✓ Tests de creación y validación del token de sesión demo del portal.
Como visitante, quiero analizar cualquier dominio público (SEO, performance, accesibilidad, TLS) desde el sitio
- ✓ Endpoint público de análisis de dominio con rate limiting propio.
- ✓ SSRF protegido: resolución de hostname y rechazo de IPs privadas/reservadas antes de solicitar el sitio.
- ✓ Fetch del HTML memoizado y reutilizado entre las pruebas de SEO, performance y accesibilidad para no golpear el sitio objetivo varias veces.
- ✓ Integración con Google PageSpeed Insights (Lighthouse) para las métricas de performance.
Como visitante, quiero entender qué hace la herramienta y confiar en que no expone mi sitio a riesgos
- ✓ Sección "site analyzer" en /lab con enlace desde /tools y modal "How it works" explicando el proceso.
- ✓ Nota técnica documentando cómo se analiza cualquier dominio sin poner en riesgo el propio servidor.
- ✓ Normalización de emisor/sujeto del certificado TLS para mostrarlo de forma legible.
Como administrador (26)
Como administrador, quiero autenticarme y que solo yo pueda entrar a /admin
- ✓ Middleware de Astro protege todas las rutas /admin y /api/admin.
- ✓ Auth.js con proveedor GitHub y allowlist de logins permitidos.
- ✓ Base de datos Drizzle + libSQL con el esquema inicial de clients/projects/messages/finances.
Como administrador, quiero un dashboard con KPIs y gestión de proyectos, clientes y finanzas
- ✓ Dashboard admin con KPIs de proyectos, mensajes, clientes y finanzas.
- ✓ Sidebar de navegación agrupada y FinanceTable/MessagesList funcionales.
- ✓ Formulario de contacto público con validación y persistencia de mensajes.
Como administrador, quiero backups automáticos de la base de datos para no perder información
- ✓ API de backups sube snapshots a Vercel Blob mediante cron programado.
- ✓ Página /admin/backup lista y permite crear backups manualmente.
Como administrador, quiero iniciar sesión con GitHub en vez de usuario/contraseña
- ✓ Proveedor Credentials reemplazado por GitHub Provider en Auth.js.
Como administrador, quiero un dashboard, sidebar y páginas de certificaciones con mejor UX
- ✓ Dashboard, Sidebar, AdminLayout y contacto rediseñados con mejor jerarquía visual.
- ✓ CertCard y la página de certificaciones muestran estado (vigente/expirada) correctamente.
Como administrador, quiero monitorear la salud de mis servicios en producción
- ✓ Esquema monitors/monitor_checks/monitor_incidents con checks HTTP periódicos.
- ✓ Página /admin/monitors con estado, latencia e incidentes por monitor.
- ✓ 13 commits dedicados a monitoreo durante la iteración.
Como administrador, quiero que las variables de entorno de mis proyectos estén cifradas
- ✓ Utilidades de cifrado/descifrado (crypto.ts) para project_env_vars.
- ✓ Revelado de secretos on-demand vía fetch, sin exponerlos en el HTML inicial.
Como administrador, quiero gestionar dominios y ver su estado DNS/SSL
- ✓ Página /admin/domains con verificación de dominios (8 commits del periodo).
Como administrador, quiero un pipeline de seguimiento comercial (leads → propuesta → cierre) y briefings de cliente
- ✓ Página /admin/seguimiento con tablero de interacciones por proyecto.
- ✓ Módulo de briefings con formulario e ítems asociados.
Como administrador, quiero presentar slides a clientes con control remoto
- ✓ presentations/presentation_slides con reveal.js.
- ✓ Vistas /admin/slides/[id]/present y /control sincronizadas (8 commits).
Como administrador, quiero una demo read-only del panel admin para mostrar a reclutadores
- ✓ Entregada en Fase 10 — ver PF-LD-02.
Como administrador, quiero un sensor que observe y clasifique requests hostiles sin bloquear tráfico legítimo
- ✓ classify.ts clasifica requests por firmas conocidas de ataque.
- ✓ sensor.ts observa cada request de forma síncrona y no bloqueante (fire-and-forget).
- ✓ security_events y security_rollups agregan eventos para el panel.
Como administrador, quiero bloquear IPs maliciosas y aplicar rate limiting que sobreviva a un redeploy
- ✓ blocklist.ts responde 403 seco a IPs bloqueadas, con lectura cacheada 30s.
- ✓ rate_limit_buckets: ratelimit-durable.ts reemplaza el rate limiting en memoria (9 commits).
- ✓ Todo el bloque de enforcement es fail-open: un fallo nunca tumba el sitio.
- ✓ Tests de blocklist y clasificación de seguridad en tests/.
Como administrador, quiero anomalías de seguridad agregadas en un panel para revisión periódica
- ✓ security_anomalies almacena desviaciones detectadas sobre los rollups.
- · Panel /admin/security con vista consolidada de anomalías y acciones de respuesta.
Como administrador, quiero registrar y gestionar mis llaves de seguridad desde el panel
- ✓ Tabla webauthnCredentials (credentialID, publicKey, counter, transports, nickname) con índice por login.
- ✓ /admin/passkeys lista, añade (con nickname) y revoca llaves; excludeCredentials evita re-registrar la misma llave física.
- ✓ API /api/admin/webauthn/{registration,credentials} protegida por el gate de sesión+allowlist existente.
Como administrador, quiero entrar a /admin tocando mi llave, sin pasar por GitHub
- ✓ Flujo usernameless (sin allowCredentials): el navegador ofrece las llaves discoverable del rpID y el login se descubre por el credentialID devuelto.
- ✓ Proof HMAC de 30s (signPasskeyProof/verifyPasskeyProof) conecta la ceremonia FIDO2 con el provider "passkey" de Auth.js, sin repetir la criptografía ahí.
- ✓ rpID/origin derivados del Host de cada request (prod, previews de Vercel y dev en cualquier puerto), no hardcodeados.
Como administrador, quiero que el selector de llaves del navegador distinga cada YubiKey por su nombre
- · Detectado en uso real (2026-07-12): con 2+ llaves registradas, el diálogo nativo "Choose a passkey" de Chrome muestra "mikerb95 / USB security key" repetido para cada una, en vez del nickname (YubiKey 1, YubiKey 2) — porque userDisplayName se fija en generateRegistrationOptions con el login, no con un nombre por llave, y queda grabado en el hardware al registrar.
- · Fix: pasar un userDisplayName distinto por llave (ej. incluir el nickname) en buildRegistrationOptions, y re-registrar las llaves existentes para que tomen el nuevo nombre — el cambio no aplica retroactivo a credenciales ya grabadas.
Como administrador, quiero una demo read-only del panel admin para mostrar a reclutadores sin exponer datos reales
- ✓ /demo firma un token de sesión de solo lectura y lo valida en middleware sin tocar Auth.js.
- ✓ Middleware bloquea cualquier método no-GET en modo demo y marca el flag demo en locals.
- ✓ scripts/seed-demo.mjs siembra clientes, proyectos, finanzas, monitores y CI runs ficticios en una base aislada.
- ✓ Tests de verificación de token demo y restricción de métodos.
Como administrador, quiero que la presentación privada del deck no sea accesible desde la demo pública
- ✓ Middleware distingue isPrivate de isAdmin para aplicar headers de seguridad correctos.
- ✓ Acceso al deck de presentación restringido a sesión de administrador real, excluyendo la sesión demo.
Como administrador, quiero cobrarle a un cliente por WhatsApp con un enlace de pago corto, sin que necesite cuenta
- ✓ Página /cobrar genera un cobro con código corto (alfabeto/crypto propios) y arma el mensaje de WhatsApp.
- ✓ /pagar simula el pago del cobro validando titularidad de la factura, reutilizando la máquina de estados de payments.
- ✓ Webhook de conciliación liquida o reversa el pago y actualiza la factura asociada.
- ✓ Cron diario (sweep) marca facturas vencidas y notifica al cliente automáticamente.
- ✓ Tests de cálculo de facturas, formato de dinero en COP y expiración de cobros.
Como administrador, quiero activar clientes en el portal, invitar usuarios y gestionar sus facturas e hitos desde /admin
- ✓ /admin/portal activa clientes, invita usuarios y lista su estado (invited/active/disabled).
- ✓ /admin/portal/facturas y /admin/portal/hitos con CRUD completo de invoices y milestones.
- ✓ /admin/portal/mensajes lista y responde hilos de conversación por cliente, con notificación al responder.
- ✓ Enlaces añadidos al sidebar de administración.
Como administrador, quiero que cada push escanee vulnerabilidades de dependencias y del propio código
- ✓ .github/workflows/security.yml corre npm audit y CodeQL en cada push/PR.
- ✓ scripts/npm-audit-scan.mjs normaliza el reporte de npm audit para ingesta.
- ✓ security_findings agrega hallazgos por herramienta con severidad, estado y auto-resolución cuando el hallazgo desaparece.
Como administrador, quiero saber si las páginas públicas cumplen accesibilidad básica (axe-core)
- ✓ scripts/a11y-scan.mjs recorre páginas públicas con Playwright + @axe-core/playwright.
- ✓ .github/workflows/a11y.yml ejecuta el escaneo en CI y sube resultados al ingest.
- ✓ Umbrales de contraste de color ajustados tras los primeros hallazgos reales.
Como administrador, quiero saber si mis tests realmente detectan bugs (mutation testing), no solo si pasan
- ✓ stryker.config.json configura mutation testing sobre los módulos críticos, con reporte HTML y concurrencia ajustada.
- ✓ .github/workflows corre Stryker y scripts/mutation-scan reporta el score al panel LAB (mutationScore en ci_runs).
- ✓ Pipeline y /lab muestran el mutation score cuando hay un resultado reciente disponible.
Como administrador, quiero pruebas de contrato que fallen si un endpoint cambia su forma de respuesta sin querer
- ✓ Esquemas zod (health, checkout, status/latencia, SLO) definen la forma esperada de cada respuesta.
- ✓ tests/contracts.test.ts valida los endpoints clave contra esos esquemas.
- ✓ Roadmap actualizado: etapas 1–6 marcadas como completadas.
Como administrador, quiero ver el portal exactamente como lo ve un cliente, para dar soporte sin pedirle su clave
- ✓ Botón "Ver como cliente" en la lista de clientes con usuarios de portal activos.
- ✓ API de impersonación crea una portal_session marcada con impersonatedBy, visible en PortalLayout con aviso y logout que restaura la sesión admin.
- ✓ Modo impersonado restringido a solo lectura: bloquea pagos y cualquier acción de escritura; queda registrado en auditoría (impersonate.start/end).
Como stakeholder académico (4)
Como stakeholder académico, quiero documentación de requerimientos funcionales y user stories
- ✓ Documento de requerimientos funcionales para gestión de proyectos y clientes.
- ✓ User stories de visitantes públicos y funcionalidades de administrador.
Como stakeholder académico, quiero navegar la documentación del proyecto sin necesitar sesión de administrador
- ✓ Páginas de casos de uso, diagramas, historias de usuario, requisitos y kanban migradas de /admin/docs a /docs.
- ✓ DocsNav como componente de navegación propio, reemplazando AdminLayout en todas las vistas de documentación.
- ✓ Redirect catch-all de /admin/docs a /docs para no romper enlaces existentes.
Como stakeholder académico, quiero ver cada requisito con su verificación, notas y requisitos relacionados
- ✓ Interfaz Requisito ampliada con campos de verificación, notas técnicas e ítems relacionados.
- ✓ Modal de detalle por ítem en RequisitosView con atributos de datos para accesibilidad.
- ✓ Diagramas Mermaid (casos de uso, secuencia, clases, componentes) con tema propio y mejor contraste.
Como stakeholder académico, quiero un deck de presentación que resuma el proyecto para la sustentación
- ✓ /docs/presentacion.astro consolida arquitectura, fases y decisiones clave del proyecto.
- ✓ Enlace al deck desde DocsNav y desde la documentación principal.
Como visitante técnico (4)
Como visitante técnico, quiero un panel público con métricas de ingeniería reales (Web Vitals, CI, disponibilidad)
- ✓ /engineering muestra Core Web Vitals p75 de usuarios reales (RUM) con p50/p95, distribución de rating, tendencia 7d y peor ruta por métrica.
- ✓ Cards de pipeline CI (tests, cobertura, duración) y disponibilidad agregada, alimentadas de ci_runs y monitor_checks.
- ✓ CardPopover en cada métrica explica el origen del dato y el umbral contra el que se evalúa; página server-rendered con datos de producción.
Como visitante técnico, quiero comprobar que las métricas de /engineering están vivas y no hardcodeadas
- ✓ Endpoint /api/engineering/live devuelve marcas de tiempo frescas (última muestra RUM, último sondeo, último run CI) más el reloj del servidor.
- ✓ Las cards consultan la prueba de vida al abrirse e indican en vivo la frescura; solo expone metadatos, nunca URLs internas ni configuración.
- · Merge del PR #2 a main y despliegue a producción de la verificación en vivo.
Como visitante técnico, quiero ver un laboratorio con los experimentos de CI/CD y su resultado más reciente
- ✓ /lab lista experimentos con conteo de runs y fecha del último, alimentado por ci_runs.
- ✓ Enlace "Laboratorio" en footer y navbar, y desde el caso de estudio de tools.
- ✓ Ruta /lab añadida a STATIC_PATHS del sitemap.
Como visitante técnico, quiero ver en /lab si hay hallazgos abiertos de seguridad o accesibilidad
- ✓ /admin/lab/security lista hallazgos con filtro por estado y acción de marcar resuelto (API GET/PATCH).
- ✓ /lab muestra card de seguridad y accesibilidad condicionada a que exista ingesta reciente, con conteo agregado por estado.
- ✓ Tests de procesamiento de hallazgos y transición de estados.
Como responsable del sitio (1)
Como responsable del sitio, quiero que la demo sea ética y segura: efímera, consentida y con defensas anti-abuso
- ✓ Salas efímeras: TTL de 2h purgadas por el cron (sweepFpRooms), sin PII persistente; consentimiento explícito antes de recolectar.
- ✓ Rate limiting durable por endpoint (beat reescopado por dispositivo para eventos con muchos móviles tras una NAT).
- ✓ entropyBits acotado a 0–64 en servidor y valores escapados en el DOM (el UA es controlable).
- ✓ Cierre pedagógico: por qué el incógnito no protege y cómo defenderse (Tor, resistFingerprinting).
Como cliente (5)
Como cliente, quiero iniciar sesión en mi propio portal con invitación previa y recuperar mi contraseña si la olvido
- ✓ client_users, client_invitations y portal_sessions con hashing scrypt y sesiones opacas revocables (cookie portal_session, distinta de admin).
- ✓ APIs de login, logout, aceptar invitación y reset de contraseña, con rate limiting y logging al micro-SIEM.
- ✓ PortalAuthLayout para las pantallas de login, invitación y reset; requirePortalSession() como única puerta de entrada al tenant.
- ✓ Tests de hashing de contraseña, clasificación de rutas del portal y aislamiento entre tenants.
Como cliente, quiero ver el estado de mis proyectos, salud del servicio y mis facturas en un panel propio
- ✓ Página de overview del proyecto con HealthCard (salud de monitores) y MilestoneTimeline (hitos con estado).
- ✓ Módulo invoices/invoice_items con CRUD completo, numeración correlativa y estados (draft/sent/paid/overdue/void).
- ✓ Centro de notificaciones in-app e hilos de mensajería (portal_threads/portal_messages) con conteo de no leídos.
Como cliente, quiero gestionar mi cuenta, mi equipo, mis documentos y ver mi historial de pagos
- ✓ /portal/cuenta con perfil, cambio de contraseña y gestión de sesiones activas.
- ✓ Gestión de equipo (invitar/roles) reutilizando client_invitations desde el propio cliente.
- ✓ /portal/documentos sube y descarga archivos vía stream autenticado (se descartó URL firmada por seguridad) con auditoría.
- ✓ /mis-pagos resuelve el historial por teléfono con enlace firmado y rate limiting.
Como cliente, quiero pagar un cobro real por WhatsApp y recibir una notificación cuando se apruebe
- ✓ Checkout API en /api/c/[code]/checkout.ts integra Wompi para el cobro real, además del mock de pruebas.
- ✓ notifyCobroPaid envía alerta push al admin cuando un cobro se marca pagado.
- ✓ Rate limiting propio para las rutas de enlace de cobro (isCobroLinkPath).
Como cliente, quiero descargar mi factura en PDF para mi contabilidad
- ✓ /api/portal/facturas/[id]/pdf genera el PDF de la factura al vuelo con pdf-lib.
- ✓ Enlace de descarga de PDF añadido a cada factura en el portal del cliente.
Como visitante de la demo (1)
Como visitante de la demo, quiero explorar el portal de clientes sin poder pagar ni modificar nada real
- ✓ Rutas de pago bloqueadas explícitamente en modo demo, incluso con sesión de solo lectura válida.
- ✓ Enlace al portal/demo del panel P&L añadido al caso de estudio de tools y al login.
- ✓ README documenta el módulo de portal y cobros junto a demo y lab.
Como equipo de QA (2)
Como equipo de QA, quiero un arnés de pruebas end-to-end contra bases de datos desechables
- ✓ Playwright configurado con fixtures propios y script para crear/sembrar bases de datos desechables por corrida.
- ✓ e2e/demo.spec.ts valida acceso y aislamiento de datos de la demo; suite de páginas públicas y headers de seguridad.
- ✓ webServer de Playwright siembra la base antes de levantar el servidor de pruebas.
Como equipo de QA, quiero que las pruebas E2E corran automáticamente en cada push, no solo en mi máquina
- ✓ Job de Playwright añadido a .github/workflows/ci.yml, corriendo contra base de datos desechable sembrada.
- ✓ Roadmap actualizado marcando el E2E de Playwright como completado, con siguientes pasos definidos.
Como desarrollador (1)
Como desarrollador, quiero un único punto de acceso a variables de entorno que funcione igual en Astro, scripts y tests
- ✓ serverEnv() en src/lib/env.ts unifica el acceso a import.meta.env/process.env; checkout, cobros y mis-pagos migrados.
- ✓ .env.local y variantes locales (.bak incluido) excluidas de git para evitar fugas de credenciales de Wompi/Turso.
- ✓ Tests E2E de checkout (con idempotencia) y de formulario de contacto (validación y rate limiting) con IPs únicas por caso.