El día que Scope dejó de ser un visor y empezó a ser una herramienta

Hoy cerré una sesión larga con un objetivo claro: llevar Scope de “prototipo que abre XMLs” a “herramienta que sirve para trabajar en serio”. V1.5 quedó en producción en olaveruiz.cl/tools/scope, y el cambio no es cosmético — es de propósito.

Scope nace de una necesidad concreta del trabajo formal: los PMs envían carta Gantt semanal en formato XML MSPDI (el export estándar de MS Project), y revisar el avance significaba abrir el archivo en MS Project, cruzar tareas a mano, anotar comentarios en planillas paralelas y, al final de la semana, no tener trazabilidad de qué cambió ni por qué. Scope V1 resolvía la parte de lectura. V1.5 cierra el círculo: ahora puedes ingresar comentarios, categorizar tareas, escribir notas semanales en markdown y exportar de vuelta a XML MSPDI con todo eso inyectado, listo para abrirse en MS Project sin perder nada.

Las tres pestañas nuevas

Ruta Crítica. El XML MSPDI trae un campo <Critical> por tarea. Hasta ayer lo estaba ignorando. Hoy lo lee, ordena las tareas críticas por fecha y resalta en rojo las que vienen atrasadas respecto a la baseline. Arriba van tres números: cuántas tareas críticas hay, el % real promedio y los días que quedan al fin del proyecto. Es la vista que abro primero cada lunes.

Recursos. Lista plana del pool: personas, materiales, tipo, capacidad, tarifa y costo. Nada glamoroso, pero antes esto solo se veía abriendo el XML en MS Project. Ahora es una tabla.

Notas Semanales. Editor markdown con preview en vivo, una nota por semana, template precargado (Avances / Riesgos / Próximos pasos / Notas adicionales). Las notas son persistentes entre snapshots — si subo el XML de la semana 19, la nota de la semana 18 sigue ahí. Es el cuaderno de bitácora que siempre terminaba perdido en un Word suelto.

El export que parecía imposible

La parte que más me preocupaba era el export XML. MS Project es exigente: si rompes un atributo, el archivo no abre. La estrategia que funcionó fue guardar el XML raw original en un bucket privado de Supabase Storage al subir el snapshot, y al exportar reabrirlo, inyectar los cambios y devolverlo. Los comentarios anclados a tareas van como Field Notes por tarea. La categoría va a Extended Attributes Text1/2/3, que en MS Project se ven como columnas extra. Las notas semanales acumuladas se inyectan en el Field Notes del proyecto.

El resultado: el XML exportado abre en MS Project con toda la información de Scope incrustada, sin perder una sola dependencia ni una fecha. Para snapshots viejos sin raw, hay un fallback que reconstruye un MSPDI minimal desde el JSON parseado.

Los tropiezos reales (porque esto es Build in Public, no un changelog de marketing)

1. Migration drift. La migración 002 del plan tenía la columna raw_xml_storage_path. En producción no estaba. Lo descubrí mid-deploy, cuando el insert fallaba. Tuve que escribir la migración 007 en caliente y aplicarla antes de seguir. Lección: una migración en el plan no es una migración aplicada. Antes de asumir, \d nombre_tabla siempre.

2. El bug del span inline de 0 píxeles. Las mini-barras de % Global se veían todas grises a pesar de que las clases .green/.warn/.danger estaban bien aplicadas. Revisé la cascada, los valores, los selectores. Tres iteraciones después caché el problema: los <span> son inline por default, y width y height no aplican a elementos inline. La barra existía, tenía color, pero medía literalmente cero píxeles. Fix de dos líneas: display: block en track y fill. Es el tipo de bug que toma horas no porque sea complejo, sino porque el síntoma (sin color) apunta hacia el lugar equivocado (clases CSS).

3. PAT sin permisos de PR. Intenté abrir el pull request con gh pr create y GitHub respondió Resource not accessible by personal access token. El PAT actual no tiene pull_request:write. Solución del momento: merge local a main y push directo. Funcionó porque main no tiene branch protection — algo que Linus apuntó después como deuda crítica. Pendiente: rotar el PAT con scopes correctos y activar branch protection.

4. Pills en vez de barras. Las barras de D/B/T (Diseño/Build/Test, las fases del proyecto) tenían el mismo problema conceptual que las mini-barras: cuando una fase tenía tareas todas al 0%, width: 0% hacía la barra invisible. Reemplacé las barras por pills coloreados grandes según feedback. Mejor diseño: el color va en el fondo, el borde y el texto, así que es imposible que se vea “todo gris”. Diseñar para que nunca exista un estado vacío sin información es más difícil que parece.

5. Auditoría post-launch de Linus. Health score 78/100. Crítico: branch protection inexistente, working tree inflado a 1.5 GB por .vercel/output y 21 imágenes webp de chronicles pesando más de 1 MB cada una. Hice lo urgente — limpiar branches mergeadas, reforzar .gitignore, crear CODEOWNERS. El resto (PAT, branch protection, comprimir webps) queda en cola.

El aprendizaje

Los bugs visuales pequeños son los más insidiosos. Un <span> inline sin display: block es un bug de una línea de CSS que toma tres iteraciones detectar, porque el síntoma engaña: ves “sin color” y miras las clases, no el modelo de caja. La regla que me llevo: cuando algo no se ve, antes de revisar estilos revisa que el elemento exista con dimensiones mayores a cero en el inspector. Y diseña para que el estado vacío también comunique — una barra con color tenue cuando avg=0, un pill dashed gris con ”—” cuando no hay tareas. El usuario nunca debería preguntarse si la herramienta está rota o si simplemente no hay datos.

Estado final

Scope V1.5 LIVE. 78/78 tests Vitest pasando. Cero leaks de Service Role Key en el bundle cliente. Tres commits limpios en main, branches mergeadas eliminadas, CODEOWNERS creado, gitignore reforzado. La herramienta se usa mañana en el día a día real — ahí empieza la verdadera iteración.