diff --git a/.claude/commands/autopilot.md b/.claude/commands/autopilot.md index 2064f3fc..6b76aef1 100644 --- a/.claude/commands/autopilot.md +++ b/.claude/commands/autopilot.md @@ -1,11 +1,11 @@ --- name: autopilot -description: Modo full-auto. Toma issue o flow con DoD definido, valida readiness, ejecuta hasta cerrarlo sin interaccion humana. Auto-selecciona opciones Recomendadas. Spawnea subagentes. No pregunta. Para. +description: Modo full-auto self-Q&A. Toma issue o flow con DoD definido, valida readiness, ejecuta hasta cerrarlo sin interaccion humana. Ante cada decision se autoformula la pregunta, se autoresponde con razonamiento explicito, y avanza. Spawnea subagentes. Para. --- -# /autopilot — Modo autonomo end-to-end +# /autopilot — Modo autonomo end-to-end con self-Q&A -Ejecuta un issue o flow **hasta cierre** sin intervencion humana. Auto-selecciona la opcion **Recomendada** en cualquier decision, spawnea subagentes en paralelo, y persiste estado en `task_runs`. **No usa `AskUserQuestion`**: si una decision no tiene Recomendado claro, ABORTA con razon antes de improvisar. +Ejecuta un issue o flow **hasta cierre** sin intervencion humana. Ante cada decision, Claude **se formula la pregunta a si mismo y se la responde** (self-Q&A) con razonamiento trazable, en vez de abortar. Auto-prefiere la opcion **Recomendada** cuando exista; cuando no, decide en base a evidencia del codigo, registry, y reglas. Cada Q&A queda persistido en `task_runs.events_json[]` para auditoria. Spawnea subagentes en paralelo y persiste estado en `task_runs`. Diferencia con comandos relacionados: @@ -86,8 +86,8 @@ Si `FALTA` esta presente: opcionalmente autopilot ofrece spawnear `fn-constructo Durante toda la ejecucion de `/autopilot`: -1. **NO invocar `AskUserQuestion`** bajo ningun caso. Si surge una decision sin Recomendado obvio, ABORT con `status=needs_human` + razon. -2. **Auto-pick "Recommended"** en cualquier flag con opciones (mocks vs prod, default branch, etc.) — usar primer item etiquetado como recomendado en el archivo / convencion del proyecto. +1. **NO invocar `AskUserQuestion` al humano**. En su lugar, **self-Q&A loop**: cuando surja una decision, Claude la formaliza como `Question -> Options -> Reasoning -> Choice` y persiste el bloque en `task_runs.events_json[]`. Ver seccion "Self-Q&A loop" mas abajo. Solo ABORTA con `status=needs_human` si la decision toca: (a) destructivo sin rollback (`--force`, `git reset --hard`, `DROP TABLE`), (b) credenciales/secrets, (c) paths protegidos, (d) contradice DoD explicito del issue. En esos casos, NUNCA self-answer — escala al humano. +2. **Auto-pick "Recommended"** en cualquier flag con opciones (mocks vs prod, default branch, etc.) — usar primer item etiquetado como recomendado en el archivo / convencion del proyecto. Si no hay marcado, self-Q&A con justificacion. 3. **Acciones destructivas prohibidas sin flag explicito**: `git reset --hard`, `git push --force`, `rm -rf` fuera de `/tmp/`, `DROP TABLE`, `--no-verify`, `--force`. Si una accion las requiere -> ABORT. 4. **Hooks NO se saltan**. Si pre-commit falla, fix raiz; si excede scope, ABORT (NO `--no-verify`). 5. **Paths protegidos** de `dev/autonomous_protected_paths.json` se respetan exactamente. @@ -110,7 +110,50 @@ Durante toda la ejecucion de `/autopilot`: 8. **Timeout** default 60 min. Override con `--max-minutes`. 9. **Idempotencia**: re-lanzar `/autopilot ` sobre el mismo target reanuda desde el ultimo `task_run` exitoso (lookup por `issue_id` o `flow_id` en `task_runs`). 10. **No self-modification**: NUNCA tocar `.claude/agents/`, `.claude/commands/`, `.claude/rules/`, `.claude/scripts/`, `.claude/CLAUDE.md`. -11. **Trazabilidad**: cada decision se persiste en `task_runs.events_json[]` con `{ts, agent, action, evidence, diff_summary, auto_choice}`. +11. **Trazabilidad**: cada decision se persiste en `task_runs.events_json[]` con `{ts, agent, action, evidence, diff_summary, auto_choice, self_qa?}`. + +--- + +## Self-Q&A loop (corazon del modo) + +Cuando aparece una decision sin Recomendado explicito, **NO abortar y NO preguntar al humano**. Claude: + +1. **Formula la pregunta** en una frase. Una sola pregunta por bloque, especifica, contestable. +2. **Lista opciones** (2-4). Misma forma que `AskUserQuestion` interno: `label + description`. Si solo hay una opcion viable, indicalo (`Options: [A] only viable`). +3. **Razona en 1-3 lineas** apoyandote en: registry (`mcp__registry__fn_search`), reglas (`.claude/rules/`), tests previos, archivos del repo, convenciones del proyecto. +4. **Elige** y marca `confidence: high|med|low`. Si `low` y la accion no es trivialmente reversible -> ABORT con `status=needs_human` y adjunta el bloque Q&A. +5. **Persiste** en `events_json[]` con shape: + ```json + { + "ts": "...", + "agent": "autopilot", + "action": "self_qa", + "self_qa": { + "question": "Crear el flag enabled=false o ya enabled=true?", + "options": [ + {"label": "enabled=false", "rationale": "TBD doctrina (feature_flags.md): merge codigo terminado pero NO expuesto"}, + {"label": "enabled=true", "rationale": "feature ya tiene tests verde y DoD 100%"} + ], + "choice": "enabled=false", + "confidence": "high", + "reasoning": "feature_flags.md regla: 'cuando se activa: cambiar enabled:true y rellenar enabled_at'. Activar va en commit posterior." + } + } + ``` +6. **Avanza** sin esperar. + +**Tope de self-Q&A**: `--max-self-answers` (default 20). Si se excede -> ABORT `status=overdeliberating` con dump de todas las Q&A. Una iteracion del bucle que necesita >5 Q&A es señal de DoD vago — abortar. + +**Cuando NO usar self-Q&A (ABORT en vez de auto-responder)**: + +| Caso | Razon | +|---|---| +| Destructivo sin rollback (`git reset --hard`, `rm -rf` fuera `/tmp/`, `DROP TABLE`, `--no-verify`, `--force`) | Coste de error infinito | +| Credenciales/tokens/secrets | Riesgo de exfiltracion | +| Paths protegidos (`dev/autonomous_protected_paths.json`) | Regla dura del orquestador | +| Contradiccion explicita con DoD del issue | DoD es contrato | +| Decision arquitectonica multi-app (renombrar tabla compartida, romper API publica) | Blast radius > 1 artefacto | +| `confidence: low` + accion no reversible | Self-Q&A no garantiza acierto sin oraculo | --- @@ -171,6 +214,7 @@ iterations: 3 / 10 duration: 18 min / 60 dod_checks: 5/5 pass proposals: 2 creadas, 1 auto-aplicada +self_qa: 7 (6 high / 1 med / 0 low) agents_spawned: fn-constructor x2, fn-recopilador x1 commits: 4 (3 feat + 1 refactor) branch: master (registry-only, push directo) @@ -226,6 +270,7 @@ estimated_iter: 3-5 | `--dry-run` | off | Plan + dispatch simulado, no aplica cambios | | `--allow-construct-missing` | off | Si flow tiene `FALTA: crear `, spawn fn-constructor antes | | `--auto-apply-proposals` | `safe` | Pasado a fn-orquestador en Path A | +| `--max-self-answers N` | 20 | Tope de bloques Self-Q&A por run. Excedido -> ABORT `overdeliberating` | --- @@ -240,6 +285,8 @@ estimated_iter: 3-5 | `aborted_protected_path` | Cambio en path protegido | Humano revisa intent | | `iterations_exhausted` | Excedido `--max-iterations` | Humano evalua si vale subir tope | | `sandbox_breach` | Diff fuera del worktree | ABORT critico, audit | +| `overdeliberating` | Excedido `--max-self-answers` | DoD probablemente vago — humano refina criterios | +| `low_confidence_abort` | Self-Q&A devolvio `confidence: low` en accion no reversible | Humano valida la decision concreta | --- @@ -250,7 +297,9 @@ estimated_iter: 3-5 | `/autopilot` sin pre-check DoD | Trabajar sin criterio de exito = bucle infinito | | Auto-relleno de DoD inventada | Criterios falsos -> falso "done" | | Merge a master sin tests verde | Master no deployable | -| `AskUserQuestion` desde dentro | Rompe el contrato autonomo | +| `AskUserQuestion` al humano | Rompe el contrato autonomo — usa self-Q&A loop | +| Self-Q&A sin razonamiento explicito | Decision opaca, no auditable | +| Self-Q&A con `confidence: high` en accion destructiva sin oraculo | Confianza injustificada — escalar | | Salto de hooks (`--no-verify`) | Encubre bugs reales | | Tocar mas issues que el target | Scope creep silencioso | | Borrar archivos sin backup en events_json | Pierde auditoria | diff --git a/dev/proposals_e2e_checks_0121/altsnap_jitter_test.yaml b/dev/proposals_e2e_checks_0121/altsnap_jitter_test.yaml new file mode 100644 index 00000000..751ed3de --- /dev/null +++ b/dev/proposals_e2e_checks_0121/altsnap_jitter_test.yaml @@ -0,0 +1,134 @@ +# Propuesta e2e_checks para apps/altsnap_jitter_test +# Generado por fn-recopilador modo design-e2e +# Fecha: 2026-05-19 +# Issue: 0121a wave 3 +# +# Diagnostico: +# app_id: altsnap_jitter_test +# lang: cpp +# framework: imgui (fn::run_app) +# domain: tools +# entry_point: main.cpp +# version: 0.1.0 +# tags: [imgui, test, regression, headless] +# +# Estructura del directorio: +# CMakeLists.txt — usa add_imgui_app(altsnap_jitter_test main.cpp); sin +# funciones del registry enlazadas (test puro de framework) +# main.cpp — harness 6 fases; #ifdef _WIN32 para p2-p6 +# appicon.ico — presente +# .git/ — sub-repo propio +# +# Toolchain: mingw-w64 (cross-compile Windows desde WSL) +# Runner Windows: bash/functions/infra/e2e_run_cpp_windows.sh +# source + e2e_run_cpp_windows => build + deploy Desktop + run .exe +# +# Fases del binario: +# p1.sync (cross-platform) — glfwSetWindowPos 60 frames, aserta max_sync=0px +# p2.altsnap (Windows-only) — WM_ENTERSIZEMOVE + burst SWP + WM_EXITSIZEMOVE +# sobre HWND principal; aserta renders_during=0 +# p3.secondary(Windows-only) — mismo bracket sobre HWND de viewport flotante +# p4.minimize (Windows-only) — iconify+restore; aserta alive(during)=1 + +# renders_iconified>0 +# p5.alt_rmb (Windows-only) — SendMessageW WM_RBUTTONDOWN+Alt; aserta +# alt_rmb_resize_count delta=1 +# p6.alt_lmb (Windows-only) — SendMessageW WM_LBUTTONDOWN+Alt; aserta +# alt_lmb_move_count delta=1 +# +# Exit del binario: 0 = ALL phases PASS (o SKIP en Linux), !=0 = alguna FAIL +# +# Tests/: NO tiene directorio de tests separado +# operations.db: NO usa (pure framework test harness, sin entities/relations) +# ops_audit: OMITIDO +# tag 'service': NO — no expone HTTP +# smoke con health: OMITIDO +# uses_functions: [] — no linkea funciones del registry +# drift uses_functions: OMITIDO +# +# Patron de checks: +# - WSL/Linux: build (cmake Linux) + binary_exists + run bajo xvfb (p1 solamente) +# - Windows: e2e_run_cpp_windows (build mingw + deploy + run nativo, p1-p6) +# - Los checks Windows se marcan severity: warning porque requieren WSL2 con +# interop habilitado, mingw-w64 instalado y /mnt/c/Users/lucas/Desktop +# accesible. En CI Linux puro estos checks se skipean de forma natural +# (cmd.exe / taskkill.exe no existen). +# - El check linux_run es severity: critical porque p1 (sync) es cross-platform +# y debe pasar siempre en WSL. + +app_id: altsnap_jitter_test + +e2e_checks: + + # Configura el build de Linux si el directorio de build no existe. + # Requerido antes de build_linux la primera vez (cmake configure-only, rapido). + # Se salta si cpp/build/linux/build.ninja ya existe (cmake --build no necesita + # reconfiguracion). + - id: cmake_configure_linux + cmd: > + test -f /home/lucas/fn_registry/cpp/build/linux/build.ninja || + cmake -S /home/lucas/fn_registry/cpp + -B /home/lucas/fn_registry/cpp/build/linux + -DFN_BUILD_TESTS=OFF + -DCMAKE_BUILD_TYPE=RelWithDebInfo + timeout_s: 60 + severity: critical + + # Compila el target para Linux (xvfb). Valida que main.cpp compila sin errores + # y el linkado con fn_framework (app_base.cpp, GLFW, OpenGL) tiene exito. + # Este es el check de compilacion base que corre en cualquier entorno. + - id: build_linux + cmd: "cmake --build /home/lucas/fn_registry/cpp/build/linux --target altsnap_jitter_test -j4" + timeout_s: 300 + severity: critical + + # Verifica que el binario Linux existe tras el build. + - id: binary_exists_linux + cmd: "test -f /home/lucas/fn_registry/cpp/build/linux/apps/altsnap_jitter_test/altsnap_jitter_test" + timeout_s: 5 + severity: critical + + # Ejecuta el harness bajo xvfb (p1.sync solamente; p2-p6 se autoskipean en Linux). + # Valida que la capa de sincronizacion GLFW pos callback no tiene regresiones. + # Usa llvmpipe (soft-render) para no depender de GPU real en CI. + # Exit 0 = p1 PASS. Cualquier otro exit = bad_sync > 0 o crash. + - id: linux_run + cmd: > + xvfb-run -a -s "-screen 0 1280x800x24" + env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=llvmpipe + /home/lucas/fn_registry/cpp/build/linux/apps/altsnap_jitter_test/altsnap_jitter_test + timeout_s: 60 + severity: critical + + # Verifica que el .ico esta presente. + # add_imgui_app genera altsnap_jitter_test_appicon.rc; si el .ico falta el build + # mingw pasa pero el .exe Windows queda sin icono embebido. + - id: icon_exists + cmd: "test -f /home/lucas/fn_registry/apps/altsnap_jitter_test/appicon.ico" + timeout_s: 5 + severity: warning + + # Cross-compila para Windows via mingw-w64, despliega al Desktop de Windows + # (matando instancia previa con taskkill.exe), y lanza el .exe nativamente + # desde WSL con WSL interop. Corre las 6 fases completas (p1-p6). + # Exit 0 = p1 PASS + p2 PASS (renders_during=0) + p3 PASS + p4 PASS + + # p5 PASS (alt_rmb delta=1) + p6 PASS (alt_lmb delta=1). + # Exit != 0 = alguna fase FAIL o crash del harness. + # + # severity: warning porque requiere: + # - WSL2 con interop habilitado (cmd.exe / taskkill.exe en PATH) + # - mingw-w64 instalado (sudo apt install mingw-w64) + # - /mnt/c/Users/lucas/Desktop accesible + # - cpp/build/windows pre-configurado (build_cpp_windows.sh) + # En CI Linux puro este check no puede correr. En la maquina de desarrollo + # (home-wsl) es el check mas valioso: detecta regresiones del WndProc subclass + # real bajo Win32 que xvfb no puede cubrir. + - id: windows_run + cmd: > + FN_REGISTRY_ROOT=/home/lucas/fn_registry + bash -c ' + source /home/lucas/fn_registry/bash/functions/infra/e2e_run_cpp_windows.sh + e2e_run_cpp_windows altsnap_jitter_test + ' + timeout_s: 300 + severity: warning + expect_stdout_contains: "overall=PASS" diff --git a/dev/proposals_e2e_checks_0121/app_hub_launcher.yaml b/dev/proposals_e2e_checks_0121/app_hub_launcher.yaml new file mode 100644 index 00000000..06d74e6f --- /dev/null +++ b/dev/proposals_e2e_checks_0121/app_hub_launcher.yaml @@ -0,0 +1,84 @@ +# e2e_checks proposal — app_hub_launcher +# app_id: app_hub_launcher +# lang: cpp +# stack: imgui hub — lista y lanza apps C++ Windows desde Desktop/apps/ +# date: 2026-05-19 +# issue: 0121a wave 3 +# author: fn-recopilador (design-e2e) +# +# Diagnostico: +# lang=cpp, framework=imgui, entry_point=main.cpp +# NO tiene tests/ propios, NO tiene operations.db +# Binario en: cpp/build/windows/apps/app_hub_launcher/app_hub_launcher.exe +# Target CMake: app_hub_launcher (registrado via add_subdirectory en cpp/CMakeLists.txt:497) +# Runtime deps en local_files/: +# - local_files/hub_manifest.tsv (regenerable con refresh_app_hub) +# - local_files/icons/*.png (regenerable con refresh_app_hub) +# Sin --self-test implementado en el binario (no aplica el check de smoke headless) +# Sin health endpoint HTTP (app GUI pura, no service) +# NO tag 'service' -> sin smoke check de puerto +# NO operations.db -> sin ops_audit +# +# Justificacion de cada check: +# build -> gate primario: garantiza que el target compila sin errores +# binary_exists -> verifica que el .exe existe en la ruta de deploy Windows esperada +# appicon_exists -> appicon.ico en la fuente es requerido por add_imgui_app para +# embeber el recurso RC en el .exe; si falta el icono no compila en WIN32 +# manifest_present -> hub_manifest.tsv en local_files/ es la fuente de descripcion + +# accent de cada tarjeta; su ausencia degrada el hub (cards grises sin +# descripcion) pero NO crashea; severity: warning porque es regenerable +# icons_cache_present -> local_files/icons/ con al menos 1 PNG indica que refresh_app_hub +# se ha corrido; su ausencia es operacional, no de build; warning + +e2e_checks: + # Check 1: compilacion del target CMake + # Por que: gate esencial — si el target no compila, nada mas tiene sentido. + # Corre sobre el build de Windows via mingw toolchain (cross-compile desde WSL). + - id: build + cmd: "cd /home/lucas/fn_registry/cpp && cmake --build build/windows --target app_hub_launcher -j" + timeout_s: 300 + severity: critical + + # Check 2: binario Windows presente en la ruta de deploy + # Por que: el pipeline redeploy_cpp_app_windows copia el .exe al Desktop. + # Este check verifica que el binario existe y es un archivo regular. + # Si el build cross no produjo el .exe o el cp falló, este check lo detecta. + - id: binary_exists + cmd: "test -f /home/lucas/fn_registry/cpp/build/windows/apps/app_hub_launcher/app_hub_launcher.exe" + timeout_s: 5 + severity: critical + + # Check 3: appicon.ico presente en el directorio fuente + # Por que: add_imgui_app auto-genera el .rc solo si appicon.ico existe en CMAKE_CURRENT_SOURCE_DIR. + # Si se borra el ico, el .exe no tiene icono embebido (no falla el build, pero rompe el + # contrato visual de la suite). El .ico es artefacto versionado en el sub-repo. + - id: appicon_exists + cmd: "test -f /home/lucas/fn_registry/apps/app_hub_launcher/appicon.ico" + timeout_s: 5 + severity: critical + + # Check 4: hub_manifest.tsv presente en la ruta de deploy Windows + # Por que: sin manifest, el hub arranca pero todas las tarjetas muestran nombre snake_case + # y sin descripcion ni accent correcto. Degradacion funcional significativa pero no crash. + # Regenerable con: ./fn run refresh_app_hub --no-restart + # Path esperado tras deploy: Desktop/apps/app_hub_launcher/local_files/hub_manifest.tsv + - id: manifest_present + cmd: "test -f /mnt/c/Users/lucas/Desktop/apps/app_hub_launcher/local_files/hub_manifest.tsv" + timeout_s: 5 + severity: warning + + # Check 5: cache de iconos PNG presente (al menos 1 archivo) + # Por que: sin iconos PNG en local_files/icons/, las tarjetas del hub no muestran imagen + # (degradacion visual). El directorio se crea/puebla con refresh_app_hub. + # Regenerable con: ./fn run refresh_app_hub --no-restart + # Se verifica que el directorio existe y tiene al menos 1 .png (no validamos cada app). + - id: icons_cache_present + cmd: "test -d /mnt/c/Users/lucas/Desktop/apps/app_hub_launcher/local_files/icons && ls /mnt/c/Users/lucas/Desktop/apps/app_hub_launcher/local_files/icons/*.png >/dev/null 2>&1" + timeout_s: 5 + severity: warning + +# Acciones sugeridas si los checks de warning fallan: +# manifest_present / icons_cache_present: +# ./fn run refresh_app_hub +# o si el hub está cerrado: +# ./fn run refresh_app_hub --no-restart diff --git a/dev/proposals_e2e_checks_0121/element_matrix_chat.yaml b/dev/proposals_e2e_checks_0121/element_matrix_chat.yaml new file mode 100644 index 00000000..410f70a4 --- /dev/null +++ b/dev/proposals_e2e_checks_0121/element_matrix_chat.yaml @@ -0,0 +1,184 @@ +# e2e_checks proposal — element_matrix_chat +# app_id: element_matrix_chat +# lang: bash +# framework: docker-compose +# stack: 10+ contenedores Docker (Synapse, Element Web, MAS, LiveKit, PostgreSQL x2, +# Synapse Admin, Nginx, LiveKit JWT, element-call-web) +# date: 2026-05-19 +# issue: 0121a wave 3 +# +# NOTAS DE DISENO: +# - La app es infraestructura Docker Compose pura: no hay build de codigo, +# no hay binarios locales que compilar, no hay tests unitarios. +# - Los checks validan que los archivos de config esten bien formados, +# que docker/docker-compose esten disponibles, y que el stack levante +# correctamente (smoke via health de la API Matrix). +# - NINGUN check usa credenciales reales. El smoke usa MATRIX_SERVER_NAME=localhost +# y valida solo que la API responde con JSON valido (no autenticacion). +# - El health check de Synapse (/_matrix/client/versions) es publico y no +# requiere credenciales — responde 200 con lista de versiones soportadas. +# - El check de LiveKit usa el endpoint /healthz que tambien es publico. +# - Los checks marcados severity: warning son los que dependen de red externa +# o de que el VPS este vivo — no bloquean merge en local. +# - No existe operations.db en esta app (infra Docker pura, no usa bucle reactivo). +# Por tanto no se incluye ops_audit. +# +# JUSTIFICACION POR CHECK: +# | check | razon | +# |--------------------------|------------------------------------------------------------------------| +# | env_example_syntax | Valida que .env.example es parseable bash sin errores de sintaxis | +# | docker_compose_validate | docker-compose config --quiet detecta YAML/interpolacion rota | +# | docker_compose_livekit_v | Igual para el compose secundario de LiveKit | +# | config_templates_present | Asegura que configs/ tiene los templates obligatorios para setup.sh | +# | setup_sh_syntax | bash -n detecta errores de sintaxis en scripts sin ejecutarlos | +# | create_user_sh_syntax | Igual para el script de creacion de usuarios | +# | element_config_json | Valida que element-config.json es JSON valido (python3 -m json.tool) | +# | smoke_synapse_health | GET /_matrix/client/versions en VPS. severity: warning (red externa) | +# | smoke_element_web | GET http://VPS:8081 devuelve 200. severity: warning (red externa) | +# | smoke_mas_health | GET http://VPS:8083/health devuelve 200. severity: warning | +# | smoke_synapse_admin | GET http://VPS:8082 devuelve 200. severity: warning | + +e2e_checks: + # ----------------------------------------------------------------------- + # CHECKS LOCALES (critical — sin red externa, sin credenciales) + # ----------------------------------------------------------------------- + + - id: env_example_syntax + # por que: .env.example define todas las variables del stack. + # Un error de sintaxis bash impide hacer `source .env` en setup.sh. + cmd: "bash -n projects/element_agents/apps/element_matrix_chat/.env.example || (bash --posix -c 'set -a; source projects/element_agents/apps/element_matrix_chat/.env.example' 2>&1 | grep -v 'command not found' && true)" + timeout_s: 5 + severity: warning + + - id: docker_compose_validate + # por que: docker-compose config --quiet detecta YAML malformado, + # imagenes invalidas, y referencias a variables sin definir antes + # de intentar levantar el stack. + # Requiere docker-compose instalado localmente. + cmd: > + cd projects/element_agents/apps/element_matrix_chat && + cp -n .env.example .env.e2e_tmp 2>/dev/null; + env $(grep -v '^#' .env.example | xargs) docker-compose -f docker-compose.yml config --quiet + timeout_s: 20 + severity: critical + + - id: docker_compose_livekit_validate + # por que: el compose de LiveKit es independiente pero obligatorio para + # videollamadas. Validarlo por separado asegura que no rompe al hacer + # `docker-compose -f docker-compose.livekit.yml up -d`. + cmd: > + cd projects/element_agents/apps/element_matrix_chat && + env $(grep -v '^#' .env.example | xargs) docker-compose -f docker-compose.livekit.yml config --quiet + timeout_s: 20 + severity: critical + + - id: config_templates_present + # por que: setup.sh requiere configs/nginx/well-known.conf, + # configs/well-known/, y configs/livekit/livekit.example.yaml. + # Si alguno falta, setup.sh falla silenciosamente o con un error opaco. + cmd: > + test -f projects/element_agents/apps/element_matrix_chat/configs/nginx/well-known.conf && + test -d projects/element_agents/apps/element_matrix_chat/configs/well-known && + test -f projects/element_agents/apps/element_matrix_chat/configs/homeserver.yaml.template && + echo "all config templates present" + expect_stdout_contains: "all config templates present" + timeout_s: 5 + severity: critical + + - id: setup_sh_syntax + # por que: bash -n detecta errores de sintaxis en el script principal + # de deploy sin ejecutarlo (sin efectos secundarios). + cmd: "bash -n projects/element_agents/apps/element_matrix_chat/scripts/setup.sh" + timeout_s: 5 + severity: critical + + - id: create_user_sh_syntax + # por que: create-user.sh es el script operativo para crear usuarios Matrix. + # Un error de sintaxis lo romperia silenciosamente en produccion. + cmd: "bash -n projects/element_agents/apps/element_matrix_chat/scripts/create-user.sh" + timeout_s: 5 + severity: critical + + - id: backup_sh_syntax + # por que: backup.sh gestiona backups de PostgreSQL. Un error de sintaxis + # podria dejar la app sin backup en produccion. + cmd: "bash -n projects/element_agents/apps/element_matrix_chat/scripts/backup.sh" + timeout_s: 5 + severity: warning + + - id: element_config_json + # por que: element-config.json configura el cliente web de Element. + # JSON invalido hace que Element Web no arranque (pantalla en blanco). + cmd: "python3 -m json.tool projects/element_agents/apps/element_matrix_chat/element-config.json > /dev/null" + timeout_s: 5 + severity: critical + + - id: element_config_template_json + # por que: el template en configs/ se usa en setup para generar la + # configuracion final. Debe ser JSON valido tambien. + cmd: "python3 -m json.tool projects/element_agents/apps/element_matrix_chat/configs/element-config.json.template > /dev/null 2>&1 || echo 'template_not_json_skip'" + timeout_s: 5 + severity: warning + + # ----------------------------------------------------------------------- + # CHECKS REMOTOS (warning — dependen del VPS organic-machine.com) + # Estos checks validan el estado del stack en produccion. + # severity: warning porque red externa puede fallar por razones ajenas al codigo. + # Se ejecutan en fn-analizador con MATRIX_HOST env var si esta configurado. + # ----------------------------------------------------------------------- + + - id: smoke_synapse_health + # por que: /_matrix/client/versions es el endpoint publico de Synapse + # que confirma que el homeserver esta vivo y responde JSON valido. + # No requiere autenticacion. Fallo aqui = stack caido en produccion. + cmd: > + MATRIX_HOST=${MATRIX_E2E_HOST:-matrix-af2f3d.organic-machine.com} && + curl -sf --max-time 10 + "https://${MATRIX_HOST}/_matrix/client/versions" + | python3 -m json.tool > /dev/null + timeout_s: 15 + severity: warning + + - id: smoke_element_web + # por que: Element Web en :8081 (o via proxy) devuelve el SPA HTML. + # Fallo = usuarios no pueden acceder al cliente. + # Usa el proxy Coolify que expone HTTPS en 443. + cmd: > + MATRIX_HOST=${MATRIX_E2E_HOST:-matrix-af2f3d.organic-machine.com} && + curl -sf --max-time 10 -o /dev/null -w "%{http_code}" + "https://${MATRIX_HOST}/" + | grep -E "^(200|301|302)$" + timeout_s: 15 + severity: warning + + - id: smoke_mas_health + # por que: MAS (Matrix Authentication Service) debe estar vivo para + # que el login OIDC funcione. /health es su endpoint publico. + cmd: > + MATRIX_HOST=${MATRIX_E2E_HOST:-matrix-af2f3d.organic-machine.com} && + curl -sf --max-time 10 + "https://${MATRIX_HOST}/_mas/health" + | python3 -c "import sys,json; d=json.load(sys.stdin); sys.exit(0 if d.get('status')=='ok' else 1)" + timeout_s: 15 + severity: warning + + - id: smoke_synapse_admin_accessible + # por que: Synapse Admin en :8082 debe estar accesible para operaciones + # de administracion. Solo verificamos que el proxy responde, no login. + cmd: > + MATRIX_HOST=${MATRIX_E2E_HOST:-matrix-af2f3d.organic-machine.com} && + curl -sf --max-time 10 -o /dev/null -w "%{http_code}" + "https://${MATRIX_HOST}/synapse-admin/" + | grep -E "^(200|301|302)$" + timeout_s: 15 + severity: warning + + - id: smoke_livekit_health + # por que: LiveKit expone /healthz en :7882. Si cae, Element Call + # no puede establecer conexiones RTC (videollamadas rotas). + cmd: > + curl -sf --max-time 10 + "https://matrix-rtc-320bd4.organic-machine.com/healthz" + | grep -i "ok\|healthy\|alive" || true + timeout_s: 15 + severity: warning diff --git a/dev/proposals_e2e_checks_0121/footprint_geo_stack.yaml b/dev/proposals_e2e_checks_0121/footprint_geo_stack.yaml new file mode 100644 index 00000000..adf28070 --- /dev/null +++ b/dev/proposals_e2e_checks_0121/footprint_geo_stack.yaml @@ -0,0 +1,189 @@ +# e2e_checks proposal — footprint_geo_stack +# app_id: footprint_geo_stack +# lang: bash +# stack: docker-compose (PostGIS 16-3.4 + Martin tile server + Valhalla routing) +# geo_deps: PostGIS/GEOS/PROJ (via postgis image), Martin MVT (maplibre), Valhalla (nilsnolde) +# date: 2026-05-19 +# issue: 0121a wave 3 +# +# NOTAS DE DISENO: +# +# 1. Esta app NO tiene codigo fuente propio (solo docker-compose.yml). No hay build step. +# Los checks validan que el compose file es valido y que el stack arranca y responde. +# +# 2. VALHALLA_DATA_DIR es obligatoria — apunta a datos locales de tiles de Espana. +# Sin el .env o la variable, el compose falla antes de arrancar. +# Los checks de smoke estan marcados severity: warning porque dependen de: +# - Docker running +# - Datos de tiles presentes en VALHALLA_DATA_DIR +# - ~60-90s de boot de Valhalla (carga tiles en memoria) +# +# 3. NO se usan tile servers externos ni APIs reales — todos los health checks +# van contra localhost con datos locales (sin red externa). +# +# 4. Puertos e2e = mismos que produccion (no hay conflicto tipico porque aurgi-pc +# es el unico pc_target y el stack se levanta/para explicitamente). +# Si en el futuro se corre en CI, escalar a puertos efimeros via env overrides. +# +# 5. El check compose_config es critico (valida syntax YAML + referencias de env). +# El resto son warning porque dependen de runtime externo (Docker daemon + datos). + +e2e_checks_suggested: + # --- CHECK 1: compose config valido --- + # Valida que docker-compose.yml no tiene errores de sintaxis y que todas las + # variables de entorno obligatorias estan definidas. Exit 0 sin arrancar nada. + # Requiere un .env con VALHALLA_DATA_DIR apuntando a una ruta existente (puede ser /tmp). + - id: compose_config + cmd: > + cd apps/footprint_geo_stack && + VALHALLA_DATA_DIR=/tmp/valhalla_e2e_stub docker compose config --quiet + timeout_s: 10 + severity: critical + # por que: unico check sin dependencia de Docker running ni datos reales. + # Detecta errores de yaml, variables no definidas, servicios mal referenciados. + + # --- CHECK 2: compose pull (dry-run de imagenes) --- + # Verifica que las imagenes declaradas existen en los registros publicos. + # No arranca contenedores. Util para detectar tags inexistentes o cambios de imagen. + # severity: warning — requiere acceso a ghcr.io y Docker Hub. + - id: compose_pull + cmd: > + cd apps/footprint_geo_stack && + VALHALLA_DATA_DIR=/tmp/valhalla_e2e_stub docker compose pull --dry-run 2>&1 | + grep -v "^#" | head -20 + timeout_s: 60 + severity: warning + expect_exit: 0 + # por que: detecta si las imagenes han sido eliminadas o el tag :latest cambio. + # No es gate critico porque depende de red externa (registries). + + # --- CHECK 3: postgis health --- + # Levanta solo el contenedor PostGIS y verifica pg_isready. + # Usa un proyecto e2e separado para no interferir con la instancia productiva. + # severity: warning — requiere Docker daemon activo. + - id: postgis_health + cmd: > + cd apps/footprint_geo_stack && + VALHALLA_DATA_DIR=/tmp/valhalla_e2e_stub + docker compose -p footprint_e2e up -d postgis && + sleep 8 && + docker exec better_maps_postgis_footprint_e2e + pg_isready -U geoserver -d gis 2>/dev/null || + docker exec $(docker ps --filter "name=footprint_e2e" --filter "ancestor=postgis/postgis:16-3.4" -q | head -1) + pg_isready -U geoserver -d gis + timeout_s: 45 + severity: warning + # por que: PostGIS es la dependencia base de Martin. Si no arranca o pg_isready + # falla, todos los checks de tiles/queries fallan en cascada. + # cleanup: ver check compose_down al final. + + # --- CHECK 4: martin health endpoint --- + # Con PostGIS sano, levanta Martin y verifica /health. + # Martin ya tiene el healthcheck nativo declarado en docker-compose.yml. + # severity: warning — depende de PostGIS y Docker. + - id: martin_health + cmd: > + cd apps/footprint_geo_stack && + VALHALLA_DATA_DIR=/tmp/valhalla_e2e_stub + docker compose -p footprint_e2e up -d martin && + sleep 5 && + curl -sf --retry 5 --retry-delay 3 http://127.0.0.1:3000/health + health: "http://127.0.0.1:3000/health" + timeout_s: 60 + severity: warning + expect_stdout_contains: "" + # por que: Martin es el tile server principal (MVT). Si /health responde con 200, + # la conexion con PostGIS y la capa de tiles esta operativa. + + # --- CHECK 5: martin catalog (tablas PostGIS expuestas) --- + # Verifica que Martin descubre tablas en PostGIS y expone el catalogo. + # Respuesta esperada: JSON con lista de sources (puede ser [] si no hay tablas aun). + # severity: warning — respuesta depende de datos cargados en PostGIS. + - id: martin_catalog + cmd: > + curl -sf http://127.0.0.1:3000/catalog | + python3 -c "import sys, json; d=json.load(sys.stdin); print(f'sources: {len(d.get(\"tiles\", d) if isinstance(d, dict) else [])}')" + timeout_s: 15 + severity: warning + # por que: diagnostica que Martin puede leer el schema de PostGIS. + # No requiere datos cargados — un catalogo vacio es valido. + + # --- CHECK 6: valhalla status --- + # Verifica que Valhalla arranca y responde al endpoint /status. + # CRITICO: Valhalla necesita VALHALLA_DATA_DIR con tiles reales para responder. + # El check usa /tmp/valhalla_e2e_stub como stub — Valhalla arrancara pero + # puede tardar o fallar si no hay tiles. Por eso severity: warning. + # Para un gate real, VALHALLA_DATA_DIR debe apuntar a los datos reales. + - id: valhalla_status + cmd: > + cd apps/footprint_geo_stack && + VALHALLA_DATA_DIR=/tmp/valhalla_e2e_stub + docker compose -p footprint_e2e up -d valhalla && + sleep 20 && + curl -sf --retry 3 --retry-delay 5 http://127.0.0.1:8002/status + timeout_s: 90 + severity: warning + # por que: Valhalla carga tiles en memoria al arrancar (~30-90s segun tamanio). + # Con datos de Espana completos el boot es lento. En e2e con stub /tmp, + # el container arranca pero puede reportar "no tiles loaded" — aceptable. + + # --- CHECK 7: valhalla route (smoke con coordenadas Madrid→Barcelona) --- + # Envia una peticion de ruta real a Valhalla. + # Solo ejecutar si VALHALLA_DATA_DIR apunta a datos reales (no stub). + # severity: warning — depende de tiles de Espana cargados. + - id: valhalla_route_smoke + cmd: > + curl -sf -X POST http://127.0.0.1:8002/route + -H 'Content-Type: application/json' + -d '{"locations":[{"lat":40.4168,"lon":-3.7038},{"lat":41.3874,"lon":2.1686}],"costing":"auto","directions_options":{"language":"es-ES"}}' + | python3 -c "import sys, json; r=json.load(sys.stdin); print(f'trip_km: {r[\"trip\"][\"summary\"][\"length\"]:.1f}')" + timeout_s: 30 + severity: warning + # por que: verifica que la ruta Madrid→Barcelona resuelve con datos reales. + # Si falla (tiles stub o no cargados), no bloquea — es diagnostico. + # Con tiles reales deberia devolver ~600km. + + # --- CHECK 8: cleanup --- + # Para y elimina los contenedores e2e para no dejar estado sucio. + # Siempre severity: warning — si falla la limpieza no es gate critico. + - id: compose_down_e2e + cmd: > + cd apps/footprint_geo_stack && + VALHALLA_DATA_DIR=/tmp/valhalla_e2e_stub + docker compose -p footprint_e2e down --volumes --remove-orphans 2>&1 | tail -5 + timeout_s: 30 + severity: warning + # por que: limpia contenedores + volumen postgis-data del proyecto e2e. + # Evita acumulacion de containers parados entre runs de e2e. + +# --------------------------------------------------------------------------- +# CHECKS OMITIDOS Y POR QUE +# --------------------------------------------------------------------------- +# ops_audit (fn-recopilador): OMITIDO — la app no tiene operations.db. +# Es un stack docker-compose, no una app con ciclo reactivo propio. +# +# build step: OMITIDO — no hay codigo fuente que compilar. Entry point es +# docker-compose.yml. El unico "build" es docker compose pull. +# +# pytest / go test: OMITIDO — no hay directorio tests/, no hay codigo Python/Go. +# +# tile server externo / API externa: OMITIDO por diseno — todos los checks +# van contra localhost. Los datos de tiles (Espana) son locales. +# +# Valhalla isochrone/matrix checks: OMITIDO en este wave — smoke basico +# con /route es suficiente para el gate. Anadir en wave 4 si se estabiliza +# la carga de tiles en e2e. +# --------------------------------------------------------------------------- + +# ACTIVACION GRADUAL SUGERIDA: +# +# Wave A (critico puro, sin Docker): +# - compose_config +# +# Wave B (con Docker, sin datos): +# - compose_config + compose_pull + postgis_health + martin_health +# +# Wave C (stack completo con datos reales en VALHALLA_DATA_DIR): +# - todos los checks (incluyendo valhalla_route_smoke) +# +# Para CI en aurgi-pc: setear VALHALLA_DATA_DIR en el entorno del runner. diff --git a/dev/proposals_e2e_checks_0121/metabase_registry.yaml b/dev/proposals_e2e_checks_0121/metabase_registry.yaml new file mode 100644 index 00000000..bffd152e --- /dev/null +++ b/dev/proposals_e2e_checks_0121/metabase_registry.yaml @@ -0,0 +1,111 @@ +# e2e_checks proposal — metabase_registry +# app_id: metabase_registry +# lang: py +# stack: Python + httpx + argparse (no venv propio, usa python/.venv del repo) +# entry: apps/metabase_registry/main.py +# date: 2026-05-19 +# issue: 0121a wave 3 +# +# Notas de deteccion: +# - lang=py, framework=httpx (requirements.txt: httpx>=0.27.0) +# - Tres scripts ejecutables: main.py, create_registry_dashboard.py, +# create_apps_dashboard.py. Sin pyproject.toml, sin tests/. +# - NO es service (sin tag service, sin puerto HTTP propio). +# - operations.db presente con schema completo (11 tablas + schema_migrations). +# - Credenciales: en .env y env vars, NUNCA en checks. Todos los checks +# son mock-only (import + syntax + dry-run sin conexion a Metabase real). +# - Sin tests/ → check 'tests' OMITIDO (regla: no inventar tests inexistentes). +# +# REGLAS APLICADAS: +# - Sin credenciales reales en ningun cmd. +# - Todos los checks son idempotentes y terminan solos (sin &). +# - python/.venv del repo padre (ruta absoluta para claridad; adaptar si se +# crea venv propio en la app). +# - ops_audit incluido porque operations.db existe y tiene 2 entities + assertions. + +e2e_checks_suggested: + # --- Check 1: import --- + # Verifica que el modulo main.py es importable y sus dependencias del registry + # (python/functions/metabase/) estan disponibles sin errores de import. + # No conecta a Metabase — solo resuelve imports. + - id: import + cmd: > + cd /home/lucas/fn_registry/apps/metabase_registry && + /home/lucas/fn_registry/python/.venv/bin/python3 -c + "import sys, os; + sys.path.insert(0, os.path.join(os.getcwd(), '..', '..', 'python', 'functions')); + from metabase import MetabaseClient, metabase_create_card, metabase_create_dashboard, metabase_update_dashboard; + from metabase.client import metabase_auth; + from metabase.databases import metabase_add_database, metabase_list_databases; + print('imports OK')" + expect_stdout_contains: "imports OK" + timeout_s: 15 + severity: critical + # por que: si los imports del registry fallan, ningun script funciona. + # detecta: renombrado de funciones metabase_*, rotura de __init__.py, httpx no instalado. + + # --- Check 2: cli_help --- + # Verifica que el parser CLI de main.py esta intacto. + # argparse imprime usage sin necesitar credenciales ni conexion. + - id: cli_help + cmd: > + cd /home/lucas/fn_registry/apps/metabase_registry && + /home/lucas/fn_registry/python/.venv/bin/python3 main.py --help + expect_stdout_contains: "metabase_registry" + expect_exit: 0 + timeout_s: 10 + severity: critical + # por que: si --help falla, el CLI esta roto (argparse error o import error previo). + + # --- Check 3: syntax_check (todos los scripts) --- + # Compila los tres scripts con py_compile sin ejecutarlos. + # Detecta SyntaxError y NameError de nivel modulo antes de cualquier deploy. + - id: syntax_check + cmd: > + cd /home/lucas/fn_registry/apps/metabase_registry && + /home/lucas/fn_registry/python/.venv/bin/python3 -m py_compile + main.py create_registry_dashboard.py create_apps_dashboard.py + create_script_navegador_dashboard.py && + echo "syntax OK" + expect_stdout_contains: "syntax OK" + timeout_s: 10 + severity: critical + # por que: los 4 scripts no tienen tests unitarios; py_compile es el gate + # mas ligero antes de un run real. Detecta typos, variables no definidas + # a nivel modulo, imports ciclicos. + + # --- Check 4: dry_run_parser --- + # Ejercita el parser con credenciales ficticias para verificar que + # los defaults de env vars y la logica de build_parser() funcionan. + # Falla INTENCIONALMENTE en autenticacion (no hay Metabase disponible) + # pero debe mostrar el mensaje de error de auth, no un traceback de Python. + # severity: warning porque el fallo esperado viene de red, no del codigo. + - id: dry_run_parser + cmd: > + cd /home/lucas/fn_registry/apps/metabase_registry && + METABASE_URL=http://127.0.0.1:19999 + METABASE_ADMIN_EMAIL=test@example.com + METABASE_ADMIN_PASSWORD=fake_password_for_e2e + /home/lucas/fn_registry/python/.venv/bin/python3 main.py + --url http://127.0.0.1:19999 + --admin-email test@example.com + --admin-password fake_password_for_e2e + 2>&1 | head -5 + expect_stdout_contains: "[metabase_registry]" + expect_exit: 1 + timeout_s: 15 + severity: warning + # por que: verifica que el flujo run() arranca (auth, log(), log_err()), + # no que Metabase este activo. exit 1 es esperado (sin servidor en :19999). + # Si sale exit 2 (argparse error) o traceback sin el prefijo → regresion. + + # --- Check 5: ops_audit --- + # Invoca fn-recopilador sobre operations.db de esta app. + # operations.db tiene schema completo (schema_migrations + 11 tablas), + # 2 entities y assertions declaradas. + - id: ops_audit + ref: "fn-recopilador:apps/metabase_registry" + severity: warning + # por que: operations.db existe y tiene datos vivos. El recopilador valida + # integridad referencial, snapshots al dia y assertions activas evaluadas. + # warning porque la app no depende de operations.db para su funcion principal. diff --git a/dev/proposals_e2e_checks_0121/script_navegador.yaml b/dev/proposals_e2e_checks_0121/script_navegador.yaml new file mode 100644 index 00000000..94010198 --- /dev/null +++ b/dev/proposals_e2e_checks_0121/script_navegador.yaml @@ -0,0 +1,121 @@ +# e2e_checks proposal — script_navegador +# app_id: script_navegador +# lang: go (NOTE: app.md declara lang:go, no Python — peticion usaba "Python" pero la app es Go) +# stack: Go + CDP browser functions del registry + gopkg.in/yaml.v3 + go-sqlite3 +# date: 2026-05-19 +# issue: 0121a wave 3 +# autor: fn-recopilador (design-e2e) +# +# INSTRUCCIONES DE USO: +# Copiar el bloque e2e_checks: al frontmatter de apps/script_navegador/app.md +# DESPUES de revision humana. El recopilador NO escribe directamente en app.md. +# +# ERROR CRITICO DETECTADO (independiente de e2e): +# apps/script_navegador/registry.db existe — viola la regla db_locations.md. +# registry.db SOLO debe existir en la raiz del repo fn_registry/. +# Accion: rm apps/script_navegador/registry.db +# Posible causa: fn ops init o go run cwd resolvio mal el path al inicializar +# la ops DB y creo un registry.db vacio como side effect. + +e2e_checks: + # ----------------------------------------------------------------------- + # CHECK 1: build + # Por que: verifica que el modulo Go compila con CGO (go-sqlite3) y las + # dependencias del registry (replace fn-registry => /home/lucas/fn_registry). + # Sin esto nada funciona. El binario se deja en /tmp para no contaminar el dir. + # ----------------------------------------------------------------------- + - id: build + cmd: > + cd /home/lucas/fn_registry/apps/script_navegador && + CGO_ENABLED=1 go build -tags fts5 -o /tmp/script_navegador_e2e . + timeout_s: 120 + + # ----------------------------------------------------------------------- + # CHECK 2: cli_help + # Por que: verifica que el binario arranca, parsea flags y sale limpiamente + # cuando falta --script. Sin Chrome ni conexion real. Exit 1 esperado + # (el binario imprime "error: --script es obligatorio" y sale con 1). + # ----------------------------------------------------------------------- + - id: cli_help + cmd: "/tmp/script_navegador_e2e 2>&1 || true" + expect_stdout_contains: "--script es obligatorio" + timeout_s: 10 + + # ----------------------------------------------------------------------- + # CHECK 3: syntax_yaml + # Por que: valida que los ejemplos YAML del repo son parseable por el propio + # binario (flag --script con archivo valido + forzar fallo de conexion CDP + # para que no intente Chrome real). Usa un ejemplo que no necesita red. + # Exit 1 esperado al no haber Chrome, pero DEBE fallar despues de "X pasos", + # no antes (significa que el YAML se parseo bien). + # ----------------------------------------------------------------------- + - id: syntax_yaml + cmd: > + /tmp/script_navegador_e2e + --script /home/lucas/fn_registry/apps/script_navegador/examples/busqueda_google.yaml + --port 19222 + 2>&1 || true + expect_stdout_contains: "busqueda_google" + timeout_s: 15 + + # ----------------------------------------------------------------------- + # CHECK 4: chrome_detect (severity: warning) + # Por que: detecta si chrome.exe de Windows esta disponible desde WSL. + # NO arranca Chrome ni navega. Solo verifica presencia del ejecutable. + # Marcado warning porque CI/CD sin Windows no tendra chrome.exe — no debe + # bloquear el merge, solo informar al humano. + # ----------------------------------------------------------------------- + - id: chrome_detect + cmd: > + ls "/mnt/c/Program Files/Google/Chrome/Application/chrome.exe" 2>/dev/null && + echo "chrome_found" || + (ls "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe" 2>/dev/null && + echo "chrome_found_x86") || + echo "chrome_not_found" + expect_stdout_contains: "chrome" + severity: warning + timeout_s: 5 + + # ----------------------------------------------------------------------- + # CHECK 5: ops_schema + # Por que: verifica que operations.db existe y tiene las tablas obligatorias + # del schema fn_operations (executions, entities, relations, logs). + # NO requiere Chrome. Detecta drift de migraciones. + # ----------------------------------------------------------------------- + - id: ops_schema + cmd: > + sqlite3 /home/lucas/fn_registry/apps/script_navegador/operations.db + "SELECT name FROM sqlite_master WHERE type='table' + AND name IN ('entities','relations','executions','logs','assertions','assertion_results') + ORDER BY name;" 2>/dev/null + expect_stdout_contains: "executions" + timeout_s: 10 + + # ----------------------------------------------------------------------- + # CHECK 6: ops_audit + # Por que: invoca al fn-recopilador para auditoria completa de operations.db + # (integridad referencial, snapshots, assertions activas, etc.). + # ----------------------------------------------------------------------- + - id: ops_audit + ref: "fn-recopilador:apps/script_navegador" + +# ----------------------------------------------------------------------- +# CHECKS OMITIDOS y por que: +# +# - smoke (Chrome real): +# Requiere Chrome Windows corriendo con --remote-debugging-port. +# No determinista en CI. Moverlo a suite manual o suite "slow" con tag. +# Si se quiere en e2e: chrome_detect debe pasar primero y el check +# debe lanzar chrome.exe headless (--headless=new) y cerrar al final. +# +# - wslpath_convert: +# wslpath es stdlib WSL, sin logica en el binario propia — no necesita +# check separado. Cubierto implicitamente por chrome_detect. +# +# - unit tests (go test): +# No hay archivos *_test.go en el directorio. Si se anaden en el futuro, +# agregar: +# - id: tests +# cmd: "cd /home/lucas/fn_registry/apps/script_navegador && go test -count=1 ./..." +# timeout_s: 60 +# ----------------------------------------------------------------------- diff --git a/dev/proposals_e2e_checks_0121/services_monitor.yaml b/dev/proposals_e2e_checks_0121/services_monitor.yaml new file mode 100644 index 00000000..5067cf93 --- /dev/null +++ b/dev/proposals_e2e_checks_0121/services_monitor.yaml @@ -0,0 +1,78 @@ +# Propuesta e2e_checks para apps/services_monitor +# Generado por fn-recopilador modo design-e2e +# Fecha: 2026-05-19 +# Issue: 0121a wave 3 +# +# Diagnostico: +# app_id=services_monitor, lang=cpp, framework=imgui (fn::run_app) +# domain=tools, version=0.1.0 +# modulos: main.cpp + http_client.cpp (TCP raw, no libcurl) +# deps externas: fn_module_data_table (issue 0097), ws2_32 (WIN32) +# vendor: vendor/nlohmann/json.hpp (header-only, bundled) +# HTTP client: HttpClient propio sobre sockets POSIX/WIN32 +# target API: services_api en 127.0.0.1:8485 — GET /api/services, POST /api/check +# tag 'service': NO (es frontend GUI, no daemon) +# operations.db: NO usa (ops_audit: OMITIDO) +# tests/: NO (directorio ausente, check tests: OMITIDO) +# --self-test: fn::run_app no parsea argv (piloto 0120 confirmado) +# appicon.ico: PRESENTE (phosphor=pulse, accent=#10b981) +# integration check: services_api en :8485 — dependencia externa; severity=warning +# +# Patron: C++ ImGui — checks estructurales sin lanzar GUI ni GPU. +# El check de integracion (integration_api) es warning porque services_api +# puede estar parada en el entorno de CI y no debe bloquear el gate. + +app_id: services_monitor + +e2e_checks: + # Build: compila el target services_monitor via cmake. + # Valida main.cpp + http_client.cpp + enlace con fn_module_data_table + # (data_table::render, issue 0097) y ws2_32 en WIN32. + # Detecta regresiones de API en data_table o cambios de firma en http_client.h. + - id: build + cmd: "cmake --build /home/lucas/fn_registry/cpp/build/windows --target services_monitor -j" + timeout_s: 300 + severity: critical + + # Verifica que el .exe existe tras el build. + # Detecta casos donde cmake sale con exit 0 pero el linker fallo silenciosamente + # (ha ocurrido con fn_module_data_table cuando el target es condicional via + # if(TARGET fn_module_data_table) sin hacer REQUIRED). + - id: binary_exists + cmd: "test -f /home/lucas/fn_registry/cpp/build/windows/apps/services_monitor/services_monitor.exe" + timeout_s: 5 + severity: critical + + # Verifica que el .ico esta presente junto al fuente de la app. + # add_imgui_app genera services_monitor_appicon.rc que windres embebe en el .exe; + # si el .ico falta el build pasa pero el .exe queda sin icono (phosphor=pulse + # accent=#10b981 segun app.md). Visible al copiar a Desktop/apps/. + - id: icon_exists + cmd: "test -f /home/lucas/fn_registry/apps/services_monitor/appicon.ico" + timeout_s: 5 + severity: warning + + # Integration check: si services_api esta corriendo en :8485, GET /api/services + # debe responder 200 con un JSON que contenga la clave "services". + # Severity WARNING: services_api puede no estar activa en CI. Este check sirve + # como smoke de conectividad y parseo de respuesta, no como gate de merge. + # Para activarlo en entornos donde services_api siempre corre, escalar a critical. + - id: integration_api + cmd: > + curl -sf --max-time 5 http://127.0.0.1:8485/api/services + | python3 -c "import sys,json; d=json.load(sys.stdin); assert 'services' in d, 'key services missing'" + timeout_s: 10 + severity: warning + +# Justificacion de checks incluidos / omitidos: +# +# | check | razon | +# |-----------------|-------| +# | build | valida compilacion de main.cpp + http_client.cpp + linkado data_table | +# | binary_exists | detecta linker silencioso cuando fn_module_data_table es condicional | +# | icon_exists | app.md declara phosphor+accent; .ico debe existir para windres | +# | integration_api | app es un cliente HTTP puro; sin conexion a services_api no hay datos | +# | ops_audit | OMITIDO — no usa operations.db | +# | tests | OMITIDO — sin directorio tests/ ni *_test.* detectados | +# | self_test | OMITIDO — fn::run_app no parsea argv (confirmado piloto 0120) | +# | smoke (GUI) | OMITIDO — app GUI, requiere display + GPU, no viable en CI headless | diff --git a/dev/proposals_e2e_checks_0121/tables_qa.yaml b/dev/proposals_e2e_checks_0121/tables_qa.yaml new file mode 100644 index 00000000..fbbb46e2 --- /dev/null +++ b/dev/proposals_e2e_checks_0121/tables_qa.yaml @@ -0,0 +1,149 @@ +# Propuesta e2e_checks para apps/tables_qa +# Generado por fn-recopilador modo design-e2e +# app_id: tables_qa +# Issue: 0121a wave 3 +# Fecha: 2026-05-19 +# +# Diagnostico: +# lang=cpp, framework=imgui (fn::run_app), domain=tools +# toolchain: mingw-w64 (cross-compile Windows desde WSL) — mismo toolchain que el resto del ecosistema +# modulos propios: main.cpp + qa_state + qa_panel + test_suite + perf_tests + 10 tabs (tab_*.cpp) +# dependencia clave: fn_module_data_table (target CMake opcional — linked si existe) +# Enlaza via: target_link_libraries(tables_qa PRIVATE fn_module_data_table) +# Si fn_module_data_table no existe, el build procede sin el modulo (tabs muestran stubs). +# sin frontend/ — no hay pnpm/vite +# sin tests/ ni pytest — suite propia en test_suite.cpp (run_test_suite(), 10 checks in-process) +# sin migrations/ — no usa BD propia +# sin operations.db — no usa el ciclo reactivo de operations (ops_audit: OMITIDO) +# sin tag 'service' — no expone HTTP (smoke/health: OMITIDO) +# appicon.ico: PRESENTE en apps/tables_qa/appicon.ico +# +# FLAG --self-test: +# Parseado en main.cpp (L77-84) pero MARCADO como WIP fase 2 TBD. +# Retorno actual: siempre exit 0 con mensaje "SKIPPED (fase 2 TBD)". +# El self-test headless real no esta implementado — requiere init parcial del +# framework (logger) + imgui_test_engine (fase 2 del issue 0108). +# Dado que exit 0 es garantizado por el stub, el check se incluye como WARNING: +# verifica que el binario acepta el flag sin crash y confirma el estado WIP. +# Se podra ascender a critical cuando la fase 2 este implementada. +# +# test_suite.cpp (in-process): +# run_test_suite() ejecuta 10 smoke tests sobre la API publica de data_table. +# Tests: TableInput construction, ColumnSpec enums, BadgeRule/ChipRule fields, +# State default-constructible, TableEvent enums, ColorScale config, Duration +# thresholds, Button setup, Multi-table input, ColorStop ordering. +# NO requiere display ni contexto GL — opera solo sobre estructuras C++ en memoria. +# Sin embargo, se invoca desde el QA panel en runtime (boton "Run Tests"), +# NO desde argv. Por tanto NO hay un --run-tests CLI flag que pueda usarse +# como check headless directo. El check tests_binary_link (ver abajo) verifica +# que el modulo data_table linka sin error como proxy del test suite. +# +# BUILD path: cmake --build cpp/build --target tables_qa -j +# BINARY path: cpp/build/apps/tables_qa/tables_qa (Linux/WSL) +# o cpp/build/windows/apps/tables_qa/tables_qa.exe (Windows cross) +# app.md documenta el run command como: +# ./cpp/build/apps/tables_qa/tables_qa (Linux/WSL nativo) +# No se detecta referencia a build de Windows en app.md — se usa build nativo WSL. +# +# Patron: C++ ImGui app testbed (QA visual, sin modo capture propio, sin HTTP) + +app_id: tables_qa + +e2e_checks: + # Build del target tables_qa en el directorio de build WSL nativo. + # Enlaza fn_module_data_table (data_table::render, TQL, renderers, etc.) si el + # target existe. Este check valida: (a) los 10 tabs compilan sin error, (b) la + # API publica de data_table_cpp es ABI-compatible con las cabeceras usadas en + # los tabs, (c) ninguna TU del modulo rompe ODR. + # Es el build gate de data_table v2.0+ para issue 0081 BeginTable migration. + - id: build + cmd: "cmake --build /home/lucas/fn_registry/cpp/build --target tables_qa -j" + timeout_s: 300 + severity: critical + # Nota: si fn_module_data_table aun no esta buildado, cmake lo buildara como + # dependencia transitiva antes de tables_qa (order dado por target_link_libraries). + + # Verifica que el binario existe tras el build. + # cmake --build puede retornar exit 0 en rebuilds parciales sin producir binario + # si el target ya esta up-to-date pero el archivo fue borrado manualmente. + - id: binary_exists + cmd: "test -f /home/lucas/fn_registry/cpp/build/apps/tables_qa/tables_qa" + timeout_s: 5 + severity: critical + + # Prueba el flag --self-test declarado en main.cpp. + # Estado actual (v0.1.0): stub WIP que imprime "SKIPPED (fase 2 TBD)" y sale exit 0. + # El check confirma: (a) el binario acepta el flag sin SIGSEGV ni abort, + # (b) el logger se inicializa correctamente antes del early exit, + # (c) exit 0 consistente con el contrato del stub. + # Marcado WARNING porque el test suite real no esta corriendo — cuando fase 2 + # implemente --self-test real con imgui_test_engine, ascender a critical y + # añadir expect_stdout_contains para verificar resultados concretos. + - id: self_test_stub + cmd: "/home/lucas/fn_registry/cpp/build/apps/tables_qa/tables_qa --self-test" + timeout_s: 15 + expect_exit: 0 + expect_stdout_contains: "SKIPPED" + severity: warning + # Cuando fase 2 este lista, cambiar expect_stdout_contains a "pass" o similar + # y ascender severity a critical. + + # Verifica que el .ico esta presente junto al fuente. + # add_imgui_app genera tables_qa_appicon.rc que windres incluye en el .exe + # (recurso .rsrc). Si appicon.ico falta, el build WSL pasa pero el .exe + # Windows queda sin icono embebido. Fallo aqui detecta borrado accidental + # del .ico antes de cross-compile. + - id: icon_exists + cmd: "test -f /home/lucas/fn_registry/apps/tables_qa/appicon.ico" + timeout_s: 5 + severity: warning + + # Verifica que los 10 tabs estan declarados en tabs.h como funciones render_*. + # tables_qa crece anadiendo tabs (10 en v0.1.0: basic, renderers, buttons, + # color_rules, dots, joins, tql, drill, events, compat). + # Un tab anadido en tab_*.cpp pero no declarado en tabs.h produce linker error + # solo si main.cpp lo referencia; este check detecta drift entre tabs.h y + # el conteo esperado sin necesitar build. + # Cuenta "render_" en tabs.h como proxy del numero de tabs declarados. + - id: tabs_declared + cmd: > + count=$(grep -c 'render_' /home/lucas/fn_registry/apps/tables_qa/tabs.h 2>/dev/null); + echo "render_ declarations in tabs.h: $count"; + test "$count" -ge 10 + timeout_s: 5 + severity: warning + # Nota: si se añaden tabs futuros, el umbral -ge 10 es minimo; no necesita + # actualizarse salvo que se eliminen tabs (lo que requeriria revision manual). + + # Verifica que CMakeLists.txt lista los 10 tab_*.cpp en add_imgui_app. + # Un tab_foo.cpp creado pero no añadido al CMakeLists no se compila y sus + # funciones quedan como undefined symbols (detectado en link, no en compile). + # Este check estático es mas rapido que el build completo y da feedback antes. + - id: cmakelists_tab_srcs + cmd: > + count=$(grep -c '^ tab_' /home/lucas/fn_registry/apps/tables_qa/CMakeLists.txt 2>/dev/null); + echo "tab_*.cpp in CMakeLists: $count"; + test "$count" -ge 10 + timeout_s: 5 + severity: warning + +# Justificacion por check: +# | check | razon | +# |---------------------|------------------------------------------------------------------------------------| +# | build | enlaza fn_module_data_table — gate de API data_table v2+ para issue 0081 | +# | binary_exists | confirma que cmake produjo binario (no solo exit 0) | +# | self_test_stub | verifica flag --self-test sin crash; estado WIP documentado; escala a critical | +# | icon_exists | .ico requerido para windres cross-compile; faltante no rompe build WSL | +# | tabs_declared | detecta drift entre tabs.h y numero esperado de tabs del QA testbed | +# | cmakelists_tab_srcs | detecta tab_*.cpp creado pero no registrado en add_imgui_app(...) | +# +# checks OMITIDOS y razon: +# | check | razon de omision | +# |----------------|------------------------------------------------------------------------------------| +# | ops_audit | no usa operations.db — no hay bucle reactivo en esta app | +# | smoke/health | no es service, no expone HTTP — es testbed GUI | +# | capture_mode | no implementado — "Export golden" en app.md es boton UI, no flag CLI | +# | | (tab_compat.cpp L: "TBD: requires capture API integration") | +# | run_test_suite | run_test_suite() es in-process vía botón UI, no accessible via CLI flag | +# | pytest | sin tests/ dir, sin pytest | +# | perf_test | perf_tests son in-process (run_perf_test via UI), no CLI invocable headless |