diff --git a/docs/superpowers/specs/2026-08-22-bat-doctor-design.md b/docs/superpowers/specs/2026-08-22-bat-doctor-design.md new file mode 100644 index 0000000..2e198cf --- /dev/null +++ b/docs/superpowers/specs/2026-08-22-bat-doctor-design.md @@ -0,0 +1,131 @@ +# Diseño: arranque automático e inteligente para no-técnicos (doctor.ps1) + +Fecha: 2026-08-22 +Estado: aprobado en conversación + +## Problema + +Los `.bat` actuales son wrappers finos de dos `.ps1` robustos, pero asumen un +entorno ya preparado. Para una persona no técnica: + +- Si falta Python (o `python` es el stub falso de la Microsoft Store), el + error parpadea y la ventana desaparece sin que se entienda nada. +- Si faltan dependencias (`pip install -e ".[web]"` nunca se corrió), uvicorn + muere con un traceback ilegible. +- Los errores terminan con la ventana cerrándose: imposible leer qué pasó. + +## Objetivo + +Doble clic en `start-server.bat` = la aplicación abre en el navegador, +siempre. El script detecta y repara el entorno por sí mismo. Un solo icono +para el usuario; `stop-server.bat` queda como acompañante. + +## Enfoque elegido (B) + +Separación de responsabilidades: **entorno** vs **ciclo de vida**. + +``` +start-server.bat ──> scripts/doctor.ps1 (NUEVO) + ├─ ¿server ya vivo? ── sí --> abrir navegador, fin + ├─ resolver Python (py -3 > python > python3) + │ └─ ninguno válido --> winget install + │ └─ sin winget --> abrir python.org + ERROR + ├─ import-test de deps + │ └─ falla --> pip install -e ".[web]" + ├─ ffmpeg presente? -- no --> aviso suave NO bloqueante + └─ ok --> scripts/start-server.ps1 -PythonExe $py + └─ puertos, healthz, browser (SIN CAMBIOS de lógica) +``` + +`start-server.ps1` conserva toda su lógica verificada (sondas TCP rápidas, +limpieza de entradas obsoletas, barrido 8000-8100, apertura de navegador). +Único cambio permitido: parámetro opcional `-PythonExe` (default `python`) +usado en la línea de comando de uvicorn. + +### Por qué `-PythonExe` es necesario + +Tras una instalación fresca vía winget, el PATH de la sesión actual **no** +incluye el Python nuevo. El doctor resuelve la ruta concreta +(`%LocalAppData%\Programs\Python\Python312\python.exe`) y se la pasa al +launcher; sin esto, el primer arranque post-instalación fallaría. + +## Flujo detallado del doctor + +1. **Server vivo primero** (antes de cualquier chequeo de entorno): sondea el + puerto registrado en `.run/server.info`; si responde, abre el navegador y + termina con exit 0. Evita instalar dependencias solo para decir "ya está + corriendo". +2. **Resolver Python**, candidatos en orden: `py -3`, `python`, `python3`. + Cada candidato es válido solo si: + - ejecuta `--version` y la salida matchea `Python 3.` con versión ≥ 3.10; + - su ruta **no** contiene `WindowsApps` (stub de la Store que no ejecuta nada). +3. **Instalar Python si falta**: `winget install --id Python.Python.3.12 + --silent --accept-package-agreements --accept-source-agreements`. Después + de instalar, sondear rutas conocidas (`%LocalAppData%\Programs\Python\*`, + `C:\Program Files\Python312\`) antes de rendirse. Sin winget disponible: + abrir `https://www.python.org/downloads/` en el navegador + instrucción en + lenguaje simple + exit ≠ 0. +4. **Import-test de dependencias**: + `& $py -c "import fastapi, uvicorn, jinja2, sse_starlette, yt_dlp"`. + **Nunca** importa `yt_scraper.webapp.app` aquí: ese import abre la DB real + del proyecto vía `config.yaml`, corre migraciones y `reconcile_markdown` + (gotcha documentado en CLAUDE.md). Si falla el test: + `-m pip install -e ".[web]"` mostrando progreso ("instalando por primera + vez, puede tardar un par de minutos"). Si falta pip: `-m ensurepip`. +5. **ffmpeg**: `Get-Command ffmpeg`; ausente → aviso amarillo no bloqueante + ("la descarga de audio no estará disponible; todo lo demás funciona"). +6. Delegar: `& start-server.ps1 -PythonExe $py` + passthrough de args. +7. Propagar exit code. + +## Reglas de ventana (.bat) + +| Resultado | Ventana | +|---|---| +| Exit 0 | mensaje verde + cuenta regresiva ~5 s → cierra sola | +| Exit ≠ 0 | **queda abierta** (`pause`) con causa legible + últimas líneas de log | + +Hoy los errores parpadean y desaparecen: es la regla más importante del +cambio. Aplica a ambos `.bat`. + +## Mensajes + +Español plano, sin jerga dirigida al usuario final ("servidor", "conexión"; +no "puerto", "PID", "uvicorn", "PATH"): + +- `[ok] Servidor listo en http://localhost:8000 — abriendo tu navegador...` +- `[ok] El servidor ya estaba corriendo — abriendo...` +- `[info] Instalando Python automáticamente (~25 MB)…` +- `[info] Instalando las piezas que faltan por primera vez (1-2 min)…` +- Error: `Algo falló al iniciar el servidor. Esto fue lo último que hizo:` + +El pulido de copys aplica también a los mensajes visibles de +`start-server.ps1` y `stop-server.ps1` (solo texto; cero cambios de lógica). + +## Errores cubiertos + +| Caso | Detección | Acción | +|---|---|---| +| Stub falso Microsoft Store | `--version` falla o ruta `*WindowsApps*` | Siguiente candidato | +| Sin Python válido | 3 candidatos fallan | winget silencioso; sin winget → python.org + ERROR | +| PATH fresco post-instalación | winget OK | Sondeo de rutas conocidas | +| pip ausente | `-m pip --version` falla | `ensurepip` | +| Deps rotas/faltantes | import-test falla | `pip install -e ".[web]"` | +| ffmpeg ausente | `Get-Command` vacío | Aviso amarillo no bloqueante | +| Error de arranque | exit ≠ 0 del launcher | Ventana queda abierta con log | + +## Fuera de alcance + +- No se toca lógica de puertos, healthz, browser ni cancelación del launcher. +- No hay `setup.bat` separado (el doctor es el setup). +- No se instalan `[dev]` ni `[analysis]`: solo core + `[web]`, lo necesario + para correr la aplicación. +- Sin tests automatizados nuevos de PowerShell (el repo no tiene infra para + eso); verificación manual guiada por `-CheckOnly`. + +## Verificación + +- `doctor.ps1 -CheckOnly`: reporta qué haría sin cambiar nada (cada rama de + decisión observable sin efectos). +- Escenarios manuales: server ya vivo → solo navegador; server apagado → + arranque completo; stop limpio tras arranque; ventana persistente ante + error simulado (p. ej. PATH sin Python en un subproceso controlado).