File size: 45,197 Bytes
ee54756 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | # Plan de corrección — hallazgos de la auditoría del 2026-09-23
> Documento de ejecución para el agente implementador. Fuente de los hallazgos (IDs SEC-, VER-, BUG-, …): [`AUDIT_2026-09-23.md`](AUDIT_2026-09-23.md).
> Fecha: 2026-09-23. Idioma de trabajo: español; código, prompts y comentarios de código siguen en el idioma que ya usa cada archivo.
---
## 0. Cómo usar este documento
- Plan **por fases**. Cada fase trae objetivo, hallazgos que cierra, tareas numeradas, archivos, criterios de aceptación medibles, plan B, esfuerzo y costo en US$. No se pasa a la siguiente fase sin cumplir los criterios de aceptación, salvo que la sección 7 marque que dos fases son independientes.
- **Reglas de trabajo** (valen para todas las fases):
1. **Corrección mínima en el lugar correcto.** Rige la misma regla de `PLAN_RAG.md §0` (sin sobreingeniería, sin abstracciones de una sola implementación). Un arreglo va en la función por la que pasan todos los llamadores, no en cada llamador.
2. **Todo bug empieza con un test de regresión que falla**; después viene el arreglo.
3. **Toda decisión o desvío se registra en `docs/DECISIONS.md`**, con el formato existente y numeración a partir de **#69**.
4. **Si cambia un contrato Pydantic**, se regeneran los JSON Schema con `python scripts/export_schemas.py` y se comprueba que siguen sincronizados.
5. **Los resultados ya reportados de `eval-v1` no se reescriben en su sitio.** Si un arreglo cambia una cifra publicada, se recalcula desde los datos guardados (0 llamadas LLM), se escribe en un archivo nuevo y se actualiza el informe con una nota del cambio.
6. **Ninguna llamada de pago sin aprobación explícita del usuario.** La key tiene un tope de US$2,00 y quedan unos US$0,42.
7. **Operación en esta máquina** (7,5 GB de RAM, repo en `/mnt/c`):
- Un solo proceso con modelos cargados a la vez.
- Corridas largas con `setsid nohup … < /dev/null &` y log persistente en `data/eval/results/`.
- Nunca `uv add` ni `uv sync` con una corrida en marcha. Siempre `uv sync --extra dev --extra ingest` (más `--extra demo` desde la Fase 3).
- El REPL de Lean se usa desde `~/ragsolver_cache/lean_project`.
8. **Después de cambiar código**, ejecutar `graphify update .` (lo pide `CLAUDE.md`).
9. **No hacer commit ni push** salvo que el usuario lo pida explícitamente.
10. **Cierre de cada fase:** la suite pasa (`uv run pytest -q --deselect tests/test_retriever_integration.py`, más la integración cuando aplique) y la documentación afectada queda actualizada en la misma fase.
---
## 1. Decisiones que necesita el usuario
| # | Decisión | Se necesita en | Opción recomendada |
|---|---|---|---|
| D1 | Licencia del repositorio | Fase 9 (antes del commit) | Apache-2.0, coherente con `CITATION.cff`, Mathlib, miniF2F-v2 y Qwen3 |
| D2 | Commit y push de la rama (habilita el CI) | Fase 6.4 y Fase 9 | Tras cerrar las Fases 1–3 |
| D3 | Qué hacer con `blobs/` (caché de MinerU con el texto del PDF) | Fase 2 | Ignorarlo en git; borrarlo sólo si el usuario lo confirma |
| D4 | Alcance de la ingesta informal | Fase 5 | Guard de un solo documento con metadatos correctos; multidocumento sólo si hay un segundo PDF real |
| D5 | Replan | Fase 7 | Pasar el feedback del Crítico al Planificador (0 llamadas extra); alternativa: `max_replans: 0` |
| D6 | Gasto | Fases 4.8, 7.1 y 9.4 | Smoke de 5 problemas triviales (≈ US$0,001); `eval-v1-full` (≈ US$0,30) |
| D7 | `HF_TOKEN` y nombres de los repos | Fase 9.3 | — |
| D8 | Sandbox a nivel de sistema operativo | Fase 10 | Obligatorio antes de exponer la demo fuera de localhost |
---
## 2. Estado del arte consultado y decisiones
| Tema | Qué hacen los proyectos de referencia | Decisión para RagSolver | Por qué |
|---|---|---|---|
| Validar pruebas no confiables | La guía oficial de Lean ([Validating a Lean Proof](https://lean-lang.org/doc/reference/latest/ValidatingProofs/)) define niveles: (1) el kernel acepta; (2) `#print axioms` con sólo `propext`, `Classical.choice` y `Quot.sound` (avisa de que `Lean.trustCompiler` delata evaluación nativa); (3) replay con `lean4checker`; (4) `comparator` en sandbox. Advierte que "las tácticas y el metacódigo pueden realizar acciones arbitrarias". | **Ahora, nivel 2**: lista blanca de axiomas y rechazo de comandos y metaprogramación en las tácticas. **Nivel 3/4 opcional**, sólo para las pruebas que se publiquen (Fase 10). | Cierra SEC-1 y VER-1 con pocas líneas y sin infraestructura nueva. |
| Replay del entorno | [SafeVerify](https://github.com/GasStationManager/SafeVerify): `Environment.replay` más la lista blanca de los 3 axiomas; lo usa el leaderboard de PutnamBench, y Mistral mantiene un fork para su evaluación ([LeanstralSafeVerify](https://github.com/mistralai/LeanstralSafeVerify)). Rechaza `native_decide`. | La misma lista blanca. Replay con SafeVerify sólo como verificación opcional de las pruebas publicadas. | Reutiliza una herramienta estándar en lugar de escribir un replay propio. |
| Juez de referencia | [leanprover/comparator](https://github.com/leanprover/comparator) (Lean FRO, 2026): compila en un sandbox `landrun`, exporta con `lean4export` y hace replay con el kernel oficial y con kernels independientes. | No adoptarlo por ahora. | Necesita Landlock, que **no está activo** en este kernel de WSL2 (verificado: `landlock` no aparece en `/sys/kernel/security/lsm`), y un `.olean` por problema. |
| Precedente | En los artefactos miniF2F publicados de Kimina-Prover, [15/225 soluciones dependían de `Lean.ofReduceBool`](https://github.com/MoonshotAI/Kimina-Prover-Preview/issues/11). | VER-1 se trata como un riesgo real, no teórico. | Un prover publicado cayó exactamente en este fallo. |
| Sandbox en WSL2 | bubblewrap funciona en WSL2 porque usa namespaces de usuario, y Landlock es sólo una capa adicional ([Codex linux-sandbox](https://github.com/openai/codex/blob/main/codex-rs/linux-sandbox/README.md), [lince #356](https://github.com/RisorseArtificiali/lince/issues/356)). | bubblewrap mediante un script envoltorio pasado como `lake_path` de `lean-interact` (Fase 10). | Verificado aquí: `/usr/bin/bwrap` está instalado, `max_user_namespaces = 29869`, y `LeanREPLConfig(lake_path=…)` es configurable, así que no hay que parchear la librería. |
| Reintentos de LLM | Lección propia (DECISIONS #59): los reintentos invisibles gastaron unos US$0,9. | No reintentar timeouts de lectura (pueden haberse cobrado) y contar cada intento. | Mismo tipo de fallo que LLM-1. |
| Reranker con poca RAM (2026-09-23, a pedido del usuario) | llama.cpp sirve Qwen3-Reranker de forma nativa por `/v1/rerank` (`--reranking --pooling rank --embedding`), con conversión GGUF oficial ya publicada ([`ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF`](https://huggingface.co/ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF); confirmado funcionando en el hilo de la propia model card de Qwen, [discusión #22](https://huggingface.co/Qwen/Qwen3-Reranker-0.6B/discussions/22)). | Reranker de los agentes por GPU vía Vulkan (Fase 7.7), mismo patrón que el embedder (#48); no se espera a una máquina con más RAM. | `tools/llama-vulkan/llama-server.exe` ya instalado es un binario genérico de llama.cpp (sirve cualquier GGUF); `/dev/dxg` confirma el passthrough de GPU activo en esta máquina; el reranker (~1,8 GB) cabe junto al embedder en los 16 GB de VRAM de la RX 9060 XT sin tocar los 7,3 GB de RAM de WSL. |
---
## 3. Mapa hallazgo → fase
| Hallazgo | Severidad | Fase | Acción |
|---|---|---|---|
| SEC-1 | HIGH | 1 (y 10) | Validación de tácticas y del enunciado de la demo; sandbox de sistema operativo con disparador |
| VER-1 | HIGH | 1 | Auditoría de axiomas con lista blanca |
| SEC-2 | HIGH | 2 | `.gitignore` y publicación saneada |
| ING-1 | HIGH | 5 | Guard, metadatos reales, `section_path` real |
| BUG-1 | MEDIUM | 4 | IDs de cita estables |
| BUG-2 | MEDIUM | 1 | Una sola fuente Lean para el REPL y el reporte |
| BUG-3 | MEDIUM | 5 | CLI con `argparse` |
| BUG-4 | MEDIUM | 5 | Rutas ancladas a la raíz del repo |
| LLM-1 | MEDIUM | 4 | Política de reintentos |
| DES-1 | MEDIUM | 7 | Replan con feedback (D5) |
| TRZ-1 | MEDIUM | 4 | `run_id` en `RetrievalContext` |
| MET-1 | MEDIUM | 3 (doc), 4 (pin), 7 (tabla por defecto) | Documentar, registrar la tabla servida, `mathlib_full` por defecto |
| DATA-1 | MEDIUM | 3 (doc), 7 (medición), 10 (corpus) | Limitación documentada; disparador medido |
| DEP-1 | MEDIUM | 3 | `pyproject.toml` |
| TEST-1…4, CI-1 | MEDIUM/LOW | 1, 4, 5, 6 | Tests por fase y CI en funcionamiento |
| ACC-1, BUG-5, BUG-6 | LOW | 4 | Contabilidad completa |
| MET-2 | LOW | 7 | Redefinir la métrica |
| ARQ-2 | LOW | 5 | Versiones sólo desde `versions.yaml` |
| ARQ-3 | LOW | 8 | `channels` como parámetro del constructor |
| ARQ-4 | LOW | — | **No se corrige**: una reconstrucción atómica de `mathlib_full` (1,1 GB de vectores) no cabe cómodamente en 7,5 GB. Se documenta el riesgo; la tabla se puede regenerar desde el JSONL embebido |
| ARQ-5 y §9 (Ponytail) | INFO | 8 | Limpieza |
| ARQ-1 | LOW | 10 | Con disparador |
| SEC-3 | LOW | 3 (doc), 10 | Documentar el `llama-server` en `0.0.0.0` y cerrarlo con el sandbox |
| DES-2 | INFO | 7 | Sólo documentar |
| DES-3 | INFO | — | Ya reconocido en `final_report.md §7`; sin acción |
| DOC-1…12 | — | 3 y la fase que corresponda | Ver cada fase |
| Tareas del PLAN sin hacer (§3.A de la auditoría) | — | 1.5, 5, 7.3, 10 | Test índice ↔ Lean, etiqueta de cita, taxonomía del retriever; el resto con disparador |
---
## 4. Fases
### Fase 1 — Verificador seguro y sólido
**Cierra:** SEC-1, VER-1, BUG-2, TEST-1 y el test de arranque índice ↔ Lean de `PLAN_RAG §12`. **Esfuerzo:** M. **Costo:** US$0.
**Objetivo.** Que "verificado" signifique: el kernel aceptó la prueba usando sólo `propext`, `Classical.choice` y `Quot.sound`. Que el texto generado por el LLM no pueda ejecutar comandos. Y que el `lean_code` reportado sea exactamente el código que se verificó.
**Tareas**
1. **Una sola fuente Lean por intento** (`lean/verify.py`). Una función arma, a partir del problema y las tácticas, el texto que se envía al REPL: la cabecera `open …` sin imports, `set_option maxHeartbeats N in` y el enunciado con las tácticas empalmadas. Esa misma función la usan:
- `LeanChecker.check`;
- el `lean_code` del `SolutionReport`, tanto en el grafo (`graph.py:143`) como en las líneas base (`run.py:59`);
- el backend `lake`, mientras exista.
`lean_code` pasa a ser los imports de configuración seguidos de esa fuente: un archivo autocontenido que reproduce el entorno del REPL. Sobre `Problem.imports` adicionales: comprobar que ningún conjunto de problemas actual los usa y rechazar con un error explícito los problemas que los traigan, en lugar de ignorarlos en silencio.
2. **Validación previa de las tácticas.** Una función pura que reemplaza a `_FORBIDDEN`:
- Quita antes los comentarios (`--` y `/- … -/`).
- Compara tokens completos, con límites de palabra.
- Rechaza:
- comandos que empiezan por `#` (`#eval`, `#print`, `#exit`, `#check`, …);
- metaprogramación: `run_tac`, `run_cmd`, `run_elab`, `elab`, `elab_rules`, `macro`, `macro_rules`, `syntax`, `notation`, `infix`, `infixl`, `infixr`, `prefix`, `postfix`;
- atributos y código no confiable: `attribute`, `@[`, `unsafe`, `implemented_by`, `extern`;
- cambios de entorno: `set_option` (el presupuesto de heartbeats lo fija el sistema), `import`;
- declaraciones: `theorem`, `lemma`, `def`, `abbrev`, `instance`, `example`, `axiom`, `opaque`, `structure`, `inductive`, `class`, y `namespace` / `section` / `end` al principio de una línea;
- pruebas no verificadas por el kernel: `native_decide`, `+native`, `ofReduceBool`, `trustCompiler`, `sorry`, `admit`;
- `IO.`.
- Se permite `open … in`, que es una táctica legítima.
- El rechazo devuelve un `Verdict` con un error legible, para que el Crítico pueda corregirlo en el siguiente intento.
3. **Auditoría de axiomas.**
- Cuando el veredicto sale sin errores, el sistema (no el LLM) ejecuta `#print axioms <nombre del teorema>` sobre el entorno que devolvió el REPL.
- Se acepta sólo si los axiomas son un subconjunto de {`propext`, `Classical.choice`, `Quot.sound`}. Cualquier otro (`sorryAx`, `Lean.ofReduceBool`, `Lean.trustCompiler`, un axioma nuevo) da `ok=false` con el error "non-standard axioms: …".
- El nombre del teorema se saca de `formal_statement`. Si el enunciado es un `example` sin nombre, al empalmar se declara como `theorem` con un nombre fijo; eso no debilita el enunciado.
- `Verdict` gana el campo `axioms: list[str]`: regenerar los esquemas y registrar el cambio en DECISIONS.
4. **Demo** (`demo/app.py`).
- El enunciado formal que escribe el usuario pasa por la misma validación y debe ser exactamente un `theorem` o `lemma` que termina en `:= by sorry`.
- La pestaña "Resolver" se niega a ejecutar si el servidor escucha en una dirección que no es de loopback.
5. **Consistencia índice ↔ Lean** (`PLAN_RAG §12`).
- En el precalentamiento del runner: `#check` de 20 `decl_name` elegidos al azar (semilla fija) de la tabla servida, excluyendo nombres privados y auxiliares.
- Se escribe en el log cuántos existen. Si son menos de 19/20, se muestra un aviso, sin abortar.
- Cuesta segundos, porque el REPL ya está caliente.
6. **Tests sin Lean** (`tests/test_verify.py`, archivo nuevo).
- Deben rechazarse: la inyección exacta de la auditoría (`simp` seguido de `#eval IO.println …`), `decide +native`, `exact Lean.ofReduceBool _ _ rfl`, `run_tac`.
- Deben aceptarse: un comentario que contiene la palabra "axiom"; tácticas legítimas como `nlinarith [sq_nonneg (a - b)]`, `norm_num [Nat.Prime] at h ⊢`, `decide`, `omega`, `interval_cases x <;> simp_all`.
- Parseo de la salida de `#print axioms`, con fixtures de mensajes reales capturados una vez del REPL.
- `lean_code` del reporte == texto recibido por un checker falso que registra lo que le llega.
7. **Test lento con Lean real.**
- Marcador `slow`, registrado en `pytest.ini`; se salta si no existe la caché de Lean.
- Comprueba que la inyección nunca llega al REPL y que `decide +native` sobre `2 ^ 10 = 1024` queda rechazado por la auditoría de axiomas.
- Se ejecuta una vez en local y el resultado se anota en DECISIONS.
8. **Re-verificación offline de `eval-v1`** (0 llamadas LLM, sólo el REPL: unos 5 minutos de import más unos segundos).
- Extraer las tácticas de los `lean_code` de las 27 filas `done` y verificarlas de nuevo con el verificador nuevo.
- Pasar la validación de tácticas sobre los `lean_code` finales de las 300 filas, para medir falsos positivos.
- Salida en un archivo nuevo, `data/eval/runs/eval-v1/reverify.json`; `results.jsonl` no se modifica.
9. **Documentación.**
- Nueva sección "Modelo de amenazas del verificador" en `architecture.md`: qué se valida y qué no (no hay sandbox de sistema operativo hasta la Fase 10).
- Entrada #69 en DECISIONS.
**Aceptación**
- Los tests nuevos pasan. El test lento se ejecutó una vez en local: la inyección fue rechazada y `+native` también.
- Re-verificación: **27/27 siguen aceptadas y sólo usan los 3 axiomas estándar**. Si alguna no pasa, la discrepancia queda en DECISIONS y en `final_report.md §6.3/§8` con las cifras corregidas.
- **0 falsos positivos** de la validación sobre las 27 pruebas aceptadas.
- Esquemas sincronizados y `graphify update .` ejecutado.
**Plan B**
- Si la salida de `#print axioms` no se puede leer de forma fiable del REPL, el sistema emite un `#eval` propio con `Lean.collectAxioms` sobre la constante. Es código del arnés, no del LLM, así que no contradice la tarea 2.
- Si la lista de rechazo bloquea tácticas legítimas en más del 1 % de los intentos de desarrollo, se afina la lista y se registra el cambio.
---
### Fase 2 — Licencias y datos, antes de cualquier commit
**Cierra:** SEC-2. **Esfuerzo:** S. **Costo:** US$0. **Depende de:** D3.
**Tareas**
1. **`.gitignore`.** Añadir:
- `problemas resueltos olimpiadas.pdf`;
- `blobs/`;
- `lean_project/scratch/` y `lean_project.lock`;
- `.claude/*.graphify-bak`;
- `data/eval/runs/`: las corridas se publican ya saneadas en HF (tarea 3) y contienen 354 fragmentos del PDF.
Preguntar al usuario si `Propuesta_Proyecto_Final_IA_UPTC.pdf`, que es suya, debe versionarse.
2. **Comprobación por contenido.**
- Tomar los primeros 60 caracteres del `content_md` de cada una de las 153 filas de `informal.jsonl`.
- Buscarlos en todos los archivos que se añadirían al repo: la lista de `git add --dry-run .` y los archivos de `data/eval/results/` y `data/eval/problems/`.
- Resultado esperado: **0 coincidencias**.
3. **`scripts/publish_hf.py`.**
- Cuando algún documento tenga `redistributable=false`, las carpetas de `runs` se suben desde una copia temporal saneada: `snippet` vacío en las citas del corpus informal, y nada del PDF en las trazas.
- Los originales nunca se modifican.
- Aplicar la misma comprobación de la tarea 2 sobre la copia saneada.
4. **Documentación.** DECISIONS #70 y una nota en `user_guide.md` ("Publicación").
**Aceptación**
- `git status --porcelain --ignored` muestra esos archivos como ignorados.
- La comprobación por contenido da 0 coincidencias, tanto en el repo como en la copia saneada que produce `publish_hf.py --dry-run`.
**Plan B.** Si el usuario quiere las corridas en git, se versiona una copia saneada generada por la misma función (por ejemplo `data/eval/runs_public/`).
---
### Fase 3 — Dependencias y documentación de estado
**Cierra:** DEP-1, DOC-1…3, DOC-5, DOC-7…10, DOC-12, la parte documental de MET-1 y DATA-1, y SEC-3 (documentación). **Esfuerzo:** M. **Costo:** US$0.
**Tareas**
1. **`pyproject.toml`.**
- Declarar `httpx` y `tenacity`.
- Nuevo extra `demo` con `gradio`, con cota inferior a la versión mayor instalada (6.x).
- Pasar `ranx` a `dev`.
- Quitar `pandas` y `accelerate` **sólo** si, sin ellos, cargan el embedder y el reranker (bf16 en CPU) y la suite pasa. Si falla algo, se restauran y se anota.
- `uv lock` y `uv sync --extra dev --extra ingest --extra demo`, siempre sin corridas en marcha.
2. **Documentación inmediata.** Todo esto no depende de cambios de código:
- `README.md`: `uv sync` con los extras.
- `install.md`: GPU vía Vulkan (#48), número real de tests, y tabla única con las 9 variables de entorno (`RAGSOLVER_MATHLIB_TABLE`, `RAGSOLVER_EMBED_BACKEND`, `RAGSOLVER_EMBED_URL`, `RAGSOLVER_EMBED_BF16`, `RAGSOLVER_LEAN_PROJECT`, `RAGSOLVER_DB`, `RAGSOLVER_HF_DATASET`, `GRADIO_SERVER_NAME`, `HF_TOKEN`).
- `PROGRESS.md`: reescritura. Una sección "Estado actual" al principio, sin contradicciones, y el material antiguo movido a "Historial". Se quita la tabla invalidada de la Fase 1, la sección "Sin GPU" y "Qué esperar cuando vuelvas".
- `interfaces.md`: rango real de `subgoals`.
- DECISIONS: tachar la #34 indicando que la reemplaza la #40.
- Docstring de `evaluate.py`.
- `versions.yaml`: el comentario de `provider_routing`.
3. **Limitaciones en `final_report.md` §8 (y §10 donde aplique).**
- La tabla de 3.903 filas está sembrada con las premisas gold de miniF2F, lo que favorece al RAG (MET-1).
- Hueco de cobertura: ninguna declaración `*.eq_*` ni del núcleo de Lean en el corpus (DATA-1).
- `used_in_sketch` no es fiable en los 34 reportes con IDs repetidos (BUG-1, hasta la Fase 4).
- RF-3 y RF-6 se aplican por problema, no por subobjetivo.
- La frase "we release" pasa a indicar que la publicación está preparada pero pendiente.
4. **DECISIONS #71.** Una sola tabla con los desvíos del plan que no estaban registrados (§3.A de la auditoría): recuperación informal sólo en PLAN, sin boost por `math_area`, `_fts_text` en lugar de `name_tokens`, embeddings float32 en lugar de float16, `re_retrieve` implícito, sin tabla `documents`.
5. **`scripts/run_chain.sh`.** El resumen automático se escribe en `data/eval/results/chain_summary.md` en lugar de agregarse a `PROGRESS.md` (cambio de una línea).
6. **`llama-server` en `0.0.0.0` (SEC-3).** En `install.md` y en la cabecera de `embed_full_mathlib.sh`, indicar que se prefiera ligarlo a la IP de WSL, o apagarlo al terminar.
**Aceptación**
- `uv run python -c "import httpx, tenacity, gradio"` funciona en el entorno sincronizado con los extras; la suite pasa.
- Checklist de la auditoría §11 cerrado, cada punto contrastado con el código (con `grep` o `graphify explain`).
- Nada en `README`, `install.md`, `PROGRESS.md`, `architecture.md` ni `interfaces.md` contradice el código.
---
### Fase 4 — Citas, trazas y contabilidad
**Cierra:** BUG-1, TRZ-1, LLM-1, ACC-1, BUG-5, BUG-6 y el pin de tabla de MET-1. **Esfuerzo:** M. **Costo:** US$0, más un smoke opcional (D6).
**Tareas**
1. **IDs de cita estables.**
- El estado del grafo guarda un mapa `chunk_id → citation_id`.
- Los nodos `plan` y `retrieve` sólo asignan número a los chunks nuevos (el siguiente libre de cada prefijo, `M` o `I`) y reutilizan los existentes. Los prompts muestran esos IDs.
- Test de regresión: un replan y una re-recuperación que devuelven chunks repetidos y nuevos no producen IDs duplicados, y `used_in_sketch` marca exactamente el chunk citado.
- Los datos de `eval-v1` no se recalculan: no es reconstruible porque las trazas del retriever no se pueden atribuir a una configuración. Queda como limitación (Fase 3.3).
2. **Trazas del retriever por corrida.**
- `RetrievalContext` gana `run_id`. El orquestador le pasa el identificador del par.
- El `Retriever` escribe en el archivo de ese par cuando `run_id` está presente; si no, se mantiene el comportamiento actual (CLI, demo, evaluación).
- El tratamiento de trazas interrumpidas del runner pasa a cubrir también las búsquedas.
- Para las corridas nuevas, cambiar `:` por `__` en los nombres de archivo de traza, porque `/mnt/c` es NTFS. Registrarlo en DECISIONS.
- Test: tras una corrida falsa, hay un archivo por par con los eventos de agentes y de búsquedas, y ningún archivo `<problema>:<uuid>`.
3. **Pin de tabla.** `VersionsPin` gana `mathlib_table`, con la tabla que realmente sirve el corpus `mathlib`. Regenerar esquemas.
4. **Política de reintentos** (`llm.py`).
- Número de intentos configurable, por defecto 4.
- Se reintenta ante error de conexión, timeout de conexión, 429 y 5xx. **No** se reintenta ante un timeout de lectura, porque el proveedor puede haber cobrado.
- `timeout_s` de la sección `llm` baja a 300 s: 4.000 tokens de salida al proveedor más lento medido (19 tok/s, #62) son unos 210 s.
- Cada intento fallido se registra con su código, y la respuesta lleva el número de intentos (`attempts`), que también va a la traza.
- Tests con `httpx.MockTransport`, que viene con `httpx` (sin dependencias nuevas): 503, 503, 200 da 3 intentos registrados; un timeout de lectura no se reintenta.
5. **Contabilidad sin pérdidas.**
- `_guard` captura cualquier excepción, salvo `DailyQuotaExhausted`, y la convierte en `status: error` dentro del grafo, conservando lo acumulado. Hoy `solve()` descarta el estado (`graph.py:222`).
- El evento `invalid_json` registra `llm_calls`, tokens y `cost_usd`.
- Las filas de error de las líneas base llevan el modelo.
- Test: una excepción a mitad de corrida conserva los contadores (análogo al test existente de JSON inválido).
6. **Self-consistency.** Sumar `cost_usd` y el número real de llamadas de cada muestra (BUG-5).
7. **Caché de consultas.** Las escrituras y el desalojo de `_QUERY_CACHE` van bajo un lock (BUG-6).
8. **Smoke opcional (D6).** Los 5 problemas triviales con `full`, código nuevo, LLM real y REPL. Costo ≈ US$0,001.
- Esperado: 5/5 resueltos; `axioms` informado; una traza por par con las búsquedas; `mathlib_table` en el pin.
9. **Documentación.** `architecture.md` (trazas), `reproduce.md` y `user_guide.md` (estructura de `traces/`), DECISIONS.
**Aceptación**
- Tests nuevos en verde.
- En una corrida falsa de 3 pares con replan: 0 IDs duplicados, 1 archivo de traza por par, y los contadores de llamadas, tokens y costo del reporte coinciden con la suma de los eventos de la traza.
---
### Fase 5 — Ingesta, CLI y configuración
**Cierra:** ING-1, BUG-3, BUG-4, ARQ-2 y la etiqueta de cita de `PLAN §7.5`. **Esfuerzo:** M (L si D4 = multidocumento). **Costo:** US$0, porque la caché de enriquecimiento evita llamadas.
**Tareas**
1. **Rutas ancladas a la raíz.** Una función que resuelve la raíz del repo a partir de `__file__` (válido con la instalación editable que hace `uv`). La usan todas las lecturas de `configs/`, `.env` y los valores por defecto de `data/`: `llm.py`, `agents/agents.py`, `rag/retrieve.py`, `rag/enrich.py`, `rag/ingest_*.py`, `rag/embed_corpus.py`, `orchestrator/run.py`, `logging/jsonl_writer.py`, `demo/app.py`.
- Si algún día se distribuye como wheel no editable, añadir una variable de entorno para la raíz; hoy no hace falta.
- Test: con otro directorio de trabajo, se cargan la configuración de `LLM` (con una clave falsa en el entorno) y la del `Retriever`.
2. **Versiones desde `versions.yaml`.** `index_version` y la versión del parser dejan de estar escritas en `ingest_informal.py` (4 sitios). `publish_space.py` cuenta las filas de la tabla, igual que `publish_hf.py`. El título de la demo toma la tag de `versions.yaml`.
3. **CLI.** `ragsolver ingest` delega en el `argparse` de `ingest_informal`, a través de un `main(argv)`. Test: las flags se interpretan igual en cualquier orden (`--skip-llm --pdf X` y `--pdf X --skip-llm`).
4. **Ingesta correcta (D4 = guard).**
- Si `data/corpus/informal.jsonl` existe con otro `doc_id`, la ingesta aborta con un mensaje claro, salvo que se pase `--replace`.
- Los metadatos del documento se construyen con el modelo `Document`, que así deja de estar muerto y además valida. Salen de `pymupdf` (`metadata`: title, author, creationDate) y los sobrescriben las flags `--title`, `--authors`, `--year`, `--license` y `--redistributable`.
- `reproduce.md §2` pasa las flags del PDF de ejemplo, de modo que su `document.json` quede **idéntico** al actual.
- `section_path` toma el título de sección que realmente detectó `parse_structure`, no el literal "Problemario".
5. **Etiqueta de cita** (`PLAN §7.5`).
- Cada `InformalChunk` gana `source_label` (por ejemplo "Moreno Núñez & Tellechea Armenta (2002)"), escrito en la ingesta.
- `_informal_citation_label` lo usa si existe; si no, el `doc_id`.
- Hay que reingerir (0 llamadas por la caché), reembeber (≈ 215 s en CPU) y reconstruir las tablas `informal` y `figures`.
6. **Multidocumento, sólo si D4 = multi.** Archivos por documento (`data/corpus/informal/<doc_id>.jsonl` y sus figuras), `ragsolver ingest <dir>` que recorre los PDFs, y el CLI de `index` concatenando el directorio.
7. **Tests.** Guard de otro `doc_id`, metadatos desde flags, parseo del CLI, rutas desde otro directorio. La prueba de ingesta de extremo a extremo va en la Fase 6.
8. **Documentación.** `user_guide.md §4` (qué soporta y qué no), `reproduce.md §2`, DECISIONS.
**Aceptación**
- Reingerir el PDF de ejemplo da 153 chunks con los **mismos `content_sha`**, `document.json` sin cambios y 0 llamadas LLM.
- `ragsolver search … --corpus informal --mode exemplar` muestra la etiqueta con autor y año.
- `ragsolver ingest` desde otro directorio funciona.
- Ingerir un segundo PDF sin `--replace` aborta sin tocar el corpus.
**Plan B.** Si `pymupdf` no trae metadatos útiles para un PDF, las flags son obligatorias para ese documento (error explícito si faltan).
---
### Fase 6 — Tests de las zonas sin cobertura y CI
**Cierra:** TEST-2 (resto), TEST-3, TEST-4 y CI-1. **Esfuerzo:** M. **Costo:** US$0. **Depende de:** Fases 1, 4 y 5; D2 para el CI.
**Tareas**
1. **Fixture sintético.** Sin contenido con licencia:
- un PDF de 2 páginas generado dentro del test con `pymupdf`, con una sección "Problemario" (2 problemas), otra "Soluciones" (2 soluciones) y un dibujo vectorial;
- un JSON de bloques estilo MinerU mínimo en `tests/fixtures/`.
Test: `ingest_informal.run(skip_llm=True)` de extremo a extremo, con directorios temporales. Esperado: 2 cards emparejadas, 1 figura recortada, archivos de corpus escritos.
2. **Tests con modelos falsos** (reutilizando el embedder falso y la LanceDB temporal de `test_retriever.py`):
- `_cli` de búsqueda;
- `evaluate_variants` sobre una tabla temporal;
- `embed_corpus.run` con `embed_documents` sustituido.
3. **Integración con Lean real** (marcador `slow`; no se ejecuta en CI). Los 5 problemas triviales pasan por el `Orchestrator` con un LLM falso que devuelve tácticas correctas conocidas y el `LeanChecker` real. Esperado: 5/5 `done` con axiomas estándar. Tarda unos 5 minutos, casi todos del import.
4. **CI** (tras D2).
- Se ejecuta en cada push con `-m "not slow"` y deseleccionando la integración con el índice.
- Caché de `uv` en `setup-uv`.
- Medir la duración. **Sólo si** supera 15 minutos, usar en CI el índice de ruedas de torch para CPU.
5. **Documentación.** En `install.md`, qué tests se saltan sin datos locales (15, según la auditoría) y cómo ejecutar los `slow`.
**Aceptación**
- Cobertura: `ingest_informal.py` ≥ 50 %, `cli.py` ≥ 80 %, `evaluate.py` y `embed_corpus.py` > 0 %, `lean/verify.py` ≥ 70 %, total ≥ 78 %.
- El CI termina en verde una vez en GitHub.
---
### Fase 7 — Diseño y métricas
**Cierra:** DES-1, MET-2, la taxonomía de errores del retriever (`PLAN F5 t5`), la medición de DATA-1, la tabla por defecto de MET-1, la documentación de DES-2 y el reranker para los agentes (restricción de capacidad del §8 de la auditoría). **Esfuerzo:** S–M. **Costo:** US$0, salvo la medición opcional.
> **Orden:** si se aprueba `eval-v1-full` (D6), debe ejecutarse **antes** de la tarea 7.1, con `prompt_version: "1"`, para no mezclar el cambio de prompt con el cambio de índice (ver Fase 9.4).
**Tareas**
1. **Replan con feedback (D5).**
- El prompt del Planificador recibe `{feedback}`: la razón del Crítico y la estrategia anterior.
- El grafo deja de vaciar `feedback` antes de replanificar; lo vacía después.
- `prompt_version: "2"`.
- Test con el LLM falso: el segundo prompt del planner contiene el feedback.
- `eval-v1` no se repite; sus trazas registran `prompt_version: "1"`.
- Medición opcional (con aprobación de gasto) sobre el split dev, **sin** afirmar una mejora: con n = 24, la variación entre corridas es del tamaño del efecto (informe §7).
2. **Métrica de re-recuperación (MET-2).** Se reemplaza `re_retrieval_success` por `re_retrieval_used`: la fracción de re-recuperaciones en modo `error` tras las cuales el siguiente intento usa (según `identifiers_used`) al menos una declaración que devolvió esa búsqueda, o se resuelve. Actualizar `eval_report.py` y `fill_final_report.py`. `eval-v1` sólo tiene 1 re-recuperación: se recalcula desde los datos si es trivial y, si no, se anota.
3. **Taxonomía de errores del retriever.** Se calcula desde los datos guardados, sin traza nueva y a US$0:
- "Lema necesario no recuperado": premisas gold (`premises_gold.jsonl`) que no están entre las citas consultadas del par (`results.jsonl`).
- "Nombre alucinado": identificadores de `unknown identifier` en los errores del verificador que no existen en el corpus.
- "Ejemplar irrelevante": revisión manual de 20 listas de ejemplares, o se omite con una nota si no hay revisor.
- "Figura mal descrita": no aplica a miniF2F.
- Nueva sección en `eval_report.md`.
4. **Disparador de DATA-1.** A partir de la tarea 3, contar los identificadores desconocidos, y los `missing_identifiers` del Crítico, cuyo último componente empieza por `eq_` o que existen en Lean (se comprueba con `#check` en el REPL) pero no en el corpus. Si superan el **5 %** de los fallos por lema faltante, se activa la tarea de corpus de la Fase 10.
5. **Tabla por defecto (MET-1).** Si la tabla `mathlib_full` existe y `RAGSOLVER_MATHLIB_TABLE` no está definida, el corpus `mathlib` se sirve desde `mathlib_full` (el pin de la Fase 4.3 lo registra). `run_chain.sh` y `reproduce.md` fijan `RAGSOLVER_MATHLIB_TABLE=mathlib` de forma explícita para reproducir `eval-v1`.
6. **DES-2.** Documentar en `architecture.md` y en `final_report.md` que la crítica del último intento sólo alimenta la taxonomía (40 llamadas, 3,6 %). Sin cambio de código.
7. **Reranker para los agentes por GPU (revisado 2026-09-23 — ya no depende de más RAM).**
- **Hallazgo:** el reranker no necesita más RAM del sistema, necesita correr en la GPU, igual que ya se hizo con el embedder (DECISIONS #48). `tools/llama-vulkan/llama-server.exe` es un binario genérico de llama.cpp: sirve cualquier GGUF, no sólo el embedder. llama.cpp soporta Qwen3-Reranker de forma nativa por el endpoint `/v1/rerank` (`--reranking --pooling rank --embedding`), con conversión GGUF oficial ya publicada y verificada (`ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF`). La RX 9060 XT (16 GB VRAM) tiene sitio de sobra junto al embedder (~1,8 GB); el tráfico va por Vulkan, sin tocar los 7,3 GB de RAM de WSL. Verificado en esta máquina: `/dev/dxg` presente (passthrough de GPU activo), `tools/llama-vulkan/llama-server.exe` ya instalado.
- **Tareas del lado del usuario** (Windows, fuera de este agente): descargar `Qwen3-Reranker-0.6B-Q8_0.gguf` (o el f16 si se prefiere) a `tools/llama-vulkan/models/`; arrancar un segundo `llama-server.exe` en un puerto distinto al del embedder (por ejemplo 8082) con `--reranking --pooling rank --embedding -ngl 99 --host 0.0.0.0 --port 8082`.
- **Tareas de código** (este agente, en la Fase 7):
- `rag/retrieve.py::Retriever._reranker()` gana un camino `RAGSOLVER_RERANK_BACKEND=llama` que llama a `POST {RAGSOLVER_RERANK_URL}/v1/rerank` con `{query, documents}` en vez de `CrossEncoder.predict()`, replicando el patrón de `embed.py::_llama_embed` (mismo `ThreadPoolExecutor`, mismo manejo de reintentos, `RAGSOLVER_RERANK_URL` con el mismo fallback de gateway que `_llama_url()`).
- `configs/agents.yaml: retrieval.rerank: true`.
- Nuevo script `scripts/rerank_llama.sh` (mismo formato que `embed_full_mathlib.sh`) con el comando exacto del servidor, para no depender de memoria.
- Test con un servidor HTTP falso (`httpx.MockTransport`, sin dependencias nuevas) que verifica el payload y el parseo de la respuesta.
- **Medición** (con el servidor arriba, 0 costo de LLM): repetir la Fase 5 de `PLAN_RAG.md` (evaluación sobre MathlibQR, variante `hibrido_rerank`) sobre el índice completo y comparar con los 0,45 de recall@10 ya medidos sin reranker de agentes (#56). Si la ganancia es ≥ 3 puntos, se documenta como confirmada y `retrieval.rerank: true` queda como valor por defecto también en un smoke de los 5 triviales.
**Aceptación**
- Tests del prompt y de la métrica en verde.
- Sección de taxonomía publicada en `eval_report.md`, con el cálculo reproducible con un comando.
- Resultado del disparador de DATA-1 registrado en DECISIONS.
- Con el servidor de reranking arriba: `RAGSOLVER_RERANK_BACKEND=llama ragsolver search "gcd is commutative" --corpus mathlib` devuelve resultados reordenados y el campo `scores.rerank` poblado, sin cargar el `CrossEncoder` de torch (verificable por RSS o por log).
**Plan B (tarea 7).** Si el usuario no tiene la máquina con la GPU disponible en el momento de esta fase, la tarea de código se implementa y se prueba con el servidor falso igual, y la medición y el `rerank: true` por defecto quedan pendientes de que el servidor esté arriba — no bloquean el resto de la fase.
---
### Fase 8 — Limpieza (Ponytail)
**Cierra:** la auditoría §9, ARQ-3, ARQ-5 y el código muerto de §3.D. **Esfuerzo:** S–M. **Costo:** US$0. **Depende de:** Fase 6 (red de tests) y D3.
**Tareas**
1. **Borrar código sin uso:** `render_pages`, `read_events` (sólo si ningún script lo necesita), `LLMResponse.json`, `Enricher.models` y `Enricher.model`, `QUERY_INSTRUCTION`, `VectorFigure.n_paths` y los tests que sólo prueban eso.
2. **Borrar scripts reemplazados:** `scripts/run_night.sh` y `scripts/run_eval_v1.sh` (los reemplaza `run_chain.sh`), y actualizar sus menciones en la documentación.
3. **Quitar el backend `lake`** (`verify()`, `run_lean()`, `parse_messages()`). El sandbox de la Fase 10 envuelve el REPL mediante `lake_path` y no necesita el camino por proceso. La función de fuente única de la Fase 1 sustituye a `build_file`.
4. **Deduplicar:**
- despacho de verificación: `graph.verify` y `run_baseline` llaman a una sola función de chequeo;
- un helper para construir `VersionsPin`.
5. **Estado del `Retriever` (ARQ-3):** `channels` pasa a ser parámetro del constructor. `evaluate.py` construye un `Retriever` por variante; el embedder ya está cacheado a nivel de módulo, así que es barato. La caché de tablas deja de ser `lru_cache` sobre un método y pasa a un dict por instancia.
6. **Artefactos (según D3):** `lean_project/scratch/`, `lean_project.lock` y `.claude/settings.json.graphify-bak`; `blobs/` sólo si el usuario lo confirma.
7. **`graphify update .`** y comprobar con `graphify explain` que no quedan nodos huérfanos de lo borrado.
**Aceptación**
- La suite pasa.
- Balance de líneas registrado en DECISIONS; la auditoría estimaba unas −150.
- Ningún `import` roto: `python -m compileall src scripts demo`.
---
### Fase 9 — Entregables (decisiones del usuario)
**Esfuerzo:** S. **Costo:** ≈ US$0,30 si se ejecuta `eval-v1-full`. **Depende de:** D1, D2, D6 y D7. **Obligatorio antes:** Fases 1 y 2.
**Tareas**
1. **Licencia (D1).** Archivo `LICENSE` y `CITATION.cff` con la URL real del repositorio.
2. **Commit (D2).**
- Revisar la lista de archivos antes; la comprobación de la Fase 2.2 debe dar 0.
- El commit y el push los ordena el usuario. El push dispara el CI (Fase 6.4).
3. **Hugging Face (D7).**
- `publish_hf.py --dry-run` y revisión de la copia saneada; luego publicar el dataset y el Space.
- Comprobar que el Space arranca y responde a una búsqueda, y que "Resolver" muestra el aviso de uso sólo local.
4. **`eval-v1-full` (D6).**
- Se ejecuta con `run_chain.sh` después de las Fases 1 y 4 y **antes** de la Fase 7.1, con `prompt_version: "1"` y la misma configuración de agentes que `eval-v1`.
- Los cambios de las Fases 1 y 4 no alteran los resultados, salvo lo que muestre la re-verificación (1.8): así, la única diferencia con `eval-v1` es el índice. Hay que declararlo así en el informe.
5. **Cierre.** Actualizar `final_report.md` (enlaces publicados, §6.3 con `eval-v1-full` si se ejecutó) y `PROGRESS.md`.
**Aceptación**
- Repo y dataset públicos sin texto del PDF (comprobación por contenido = 0).
- CI en verde.
- Informe con los enlaces reales.
---
### Fase 10 — Trabajo con disparador (sólo si se cumple la condición)
| Extensión | Disparador | Qué implica | Aceptación |
|---|---|---|---|
| Sandbox de sistema operativo para Lean (SEC-1 residual, SEC-3) | Antes de exponer la demo fuera de localhost, de procesar problemas o corpus de terceros, o de cambiar a un proveedor LLM no confiable | Script envoltorio pasado como `lake_path` de `lean-interact`: `bwrap` sin red (`--unshare-net`), bind de sólo lectura del proyecto Lean y del toolchain, `tmpfs` para lo temporal, sin acceso a `$HOME` ni al repo | Un `#eval IO.FS.readFile` sobre el `.env`, forzado con la validación desactivada en un test aislado, falla por permisos; el import de Mathlib tarda lo mismo (±10 %) |
| Replay de las pruebas publicadas (nivel 3) | Antes de presentar las pruebas como definitivas en HF o en el informe final | SafeVerify sobre las pruebas resueltas. `comparator` queda descartado mientras Landlock no esté activo | Todas pasan, o las discrepancias quedan documentadas |
| RF-1, RF-3 y RF-6 por subobjetivo | Si el curso exige el cumplimiento literal | Subobjetivos como lemas `have` o `lemma` verificados por separado, con reintentos por subobjetivo | Diseño propio (L) antes de implementar |
| Gold de OCR de 10 páginas | Cuando haya un anotador humano | Transcripción manual; métricas de `PLAN F2 t10` | Fórmulas ≥ 95 %, texto ≤ 3 % de distancia de edición |
| Completar el corpus (DATA-1) | Si el disparador de la Fase 7.4 supera el 5 % | Extraer las declaraciones que faltan (`eq_*` y núcleo de Lean) con el pipeline de LeanSearch v2 sin informalización (`PLAN F1`, plan B) y reindexar | El recall de premisas gold sube y se documenta |
| Multidocumento (si D4 = guard) | Un segundo documento real para ingerir | Fase 5.6 | Dos documentos conviven con citas correctas |
| Contratos transversales (ARQ-1) | El próximo cambio que toque contratos de dos paquetes | Mover `TraceEvent`, `VersionsPin`, `Citation` y `Verdict` a un único módulo de contratos | Sin dependencias `lean → agents` ni `logging ↔ rag` (verificado con Graphify) |
| Optimizaciones (auditoría §8) | Corridas en las que la recuperación supere el 10 % del tiempo de pared | Embeber las consultas de todos los subobjetivos en un lote; figuras en una sola consulta; embeddings en Arrow en lugar de JSONL | Latencia medida antes y después |
| PutnamBench | Trabajo futuro (#67) | Portar los enunciados al pin `v4.28.0-rc1` | — |
---
## 5. Esfuerzo y costo estimados
| Fase | Esfuerzo | Costo | Bloquea a |
|---|---|---|---|
| 1 Verificador | M (≈ 1 día) | US$0 | 3 (parcial), 4, 6, 9 |
| 2 Licencias | S | US$0 | 9 |
| 3 Dependencias y documentación | M | US$0 | 6 |
| 4 Citas, trazas y contabilidad | M | US$0 (+ US$0,001 opcional) | 6, 7, 9.4 |
| 5 Ingesta, CLI y configuración | M (L si hay multidocumento) | US$0 | 6 |
| 6 Tests y CI | M | US$0 | 8 |
| 7 Diseño y métricas | S–M | US$0 (+ medición opcional) | — |
| 8 Limpieza | S–M | US$0 | — |
| 9 Entregables | S | ≈ US$0,30 si hay `eval-v1-full` | — |
Total estimado del agente: **6 a 8 días de trabajo**, sin contar la Fase 10.
---
## 6. Definición de terminado (global)
1. Los 4 HIGH (SEC-1, VER-1, SEC-2, ING-1) están cerrados, con tests.
2. Cada MEDIUM está cerrado, o aplazado con su motivo registrado en DECISIONS.
3. `eval-v1` re-verificado con el verificador nuevo y el resultado documentado.
4. La documentación de estado es consistente con el código (checklist de la auditoría §11 cerrado).
5. La suite pasa (incluidos los tests nuevos); el test `slow` se ejecutó al menos una vez; el CI está en verde.
6. `graphify update .` ejecutado tras la última fase de código.
---
## 7. Orden de ejecución
1. **Fase 1** y **Fase 2** (independientes entre sí; ambas antes de cualquier commit).
2. **Fase 3** (depende de la Fase 1 sólo para la sección de amenazas; el resto puede ir en paralelo).
3. **Fase 4**. Si se aprueba, aquí va el smoke de D6.
4. **Fase 9.1–9.2**: licencia y commit, si el usuario lo decide. Habilita el CI.
5. **Fase 5**, luego **Fase 6**.
6. **Fase 9.4** (`eval-v1-full`, si se aprueba), **antes** de la Fase 7.1.
7. **Fase 7**, **Fase 8**, **Fase 9.3** (publicación en HF).
8. **Fase 10**, según los disparadores.
---
## 8. Referencias consultadas
- Lean FRO, *Validating a Lean Proof* (manual de referencia): https://lean-lang.org/doc/reference/latest/ValidatingProofs/
- leanprover/comparator (juez en sandbox para pruebas generadas por IA): https://github.com/leanprover/comparator
- GasStationManager/SafeVerify (replay más lista blanca de axiomas; leaderboard de PutnamBench): https://github.com/GasStationManager/SafeVerify · fork de Mistral: https://github.com/mistralai/LeanstralSafeVerify
- Kimina-Prover-Preview, issue #11 (15/225 soluciones de miniF2F dependían de `Lean.ofReduceBool`): https://github.com/MoonshotAI/Kimina-Prover-Preview/issues/11
- Kimina Lean Server: https://arxiv.org/abs/2504.21230
- OpenAI Codex, `linux-sandbox` (Landlock y bubblewrap): https://github.com/openai/codex/blob/main/codex-rs/linux-sandbox/README.md
- Sandbox en WSL (bubblewrap y namespaces de usuario): https://github.com/RisorseArtificiali/lince/issues/356
- Comprobaciones locales (2026-09-23): `/usr/bin/bwrap` presente; `max_user_namespaces = 29869`; Landlock ausente de `/sys/kernel/security/lsm`; `lean-interact` 0.11.5 admite `LeanREPLConfig(lake_path=…)`.
- llama.cpp, soporte de Qwen3-Reranker vía `/v1/rerank`: https://huggingface.co/ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF · https://huggingface.co/Qwen/Qwen3-Reranker-0.6B/discussions/22 · issue de calidad del endpoint (usar la conversión oficial): https://github.com/ggml-org/llama.cpp/issues/16407
- Comprobación local (2026-09-23): `/dev/dxg` presente (passthrough de GPU activo en WSL2); `tools/llama-vulkan/llama-server.exe` ya instalado (mismo binario usado para el embedder, #48).
|