# 08_RECUPERACION_CHECKPOINT.md — Auditoría y Verificación de Recuperación por Checkpoint (Sección 7)

## 1. Diagnóstico del Cambio Previo en ORC-27

En `ORC-27`, el método `ScientificCoordinator.recover_interrupted_tasks()` reencolaba una tarea huérfana en estado `RUNNING` hacia `PENDING` cuando detectaba un bloque `checkpoint` con `checkpoint_sha256` válido, pero asignaba:
- `recovery_action = "CHECKPOINT_REQUEUED_FOR_RESUMPTION"`
- `resumed = True`

**Limitación identificada en la revisión de ORC-29 (Sección 7):**
1. **Reencolar a `PENDING` no es reanudar:** En el instante en que `recover_interrupted_tasks()` coloca una tarea nuevamente en la cola (`PENDING`), el motor científico aún no ha restaurado el estado ni continuado el cálculo. Declarar `resumed=True` en ese momento es prematuro.
2. **Protección de pertenencia e idempotencia:** Se requería comprobar explícitamente que el checkpoint perteneciera a la tarea (`task_id` y `task_type`), que declarara `idempotent=True`, y que el motor correspondiente restaurara efectivamente el estado intermedio sin reejecutar el trabajo ya completado.

---

## 2. Arquitectura Implementada en ORC-29 (`app/core/coordinator.py`)

### A. Nivel de Cola (`ScientificCoordinator.recover_interrupted_tasks()`)
Se distinguen estrictamente dos acciones al recuperar tareas `RUNNING` huérfanas tras una caída:

| Caso | Condición Verificada | Nuevo Estado | `recovery_action` | `requeued` | `resumed` |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **1. Reconciliación Post-Mortem (Por defecto)** | Sin checkpoint, o `idempotent != True`, o `checkpoint_sha256` inválido, o discrepancia de pertenencia (`task_id` / `task_type`). | `FAILED` | `RECONCILED_AS_FAILED` | `False` | `False` |
| **2. Reencolado con Checkpoint Verificado** | `resumable=True`, `idempotent=True`, `checkpoint_sha256` coincide con el hash canónico de `state`, y coinciden `task_id` / `task_type`. | `PENDING` | `REQUEUED` | `True` | `False` |

> **Regla de Honestidad:** Al salir de `recover_interrupted_tasks()`, `resumed` es **siempre `False`**, porque la tarea únicamente ha sido reconciliada (`FAILED`) o reencolada (`PENDING` / `REQUEUED`).

---

### B. Nivel de Motor Científico (`ScientificCoordinator.run_discovery_cycle()`)
Para el ciclo de descubrimiento multi-ronda (`DISCOVERY_CYCLE` en `ALGORITHM` Lab):
1. **Serialización suficiente tras la Ronda 1:**
   Al concluir la Ronda 1 (`BROAD_EXPLORATION`), el coordinador construye `round_1_checkpoint` con el estado determinista completo:
   - `round_completed = 1`, `lab_type`, `benchmark_target`, `dimension`
   - `r1_best`, `r1_best_config`, `r1_metric`, `baseline_metric`, `baseline_name`
   - `summary_1`, `decision_history_r1`, `overall_winner_r1`
   - `checkpoint_sha256` calculado sobre `json.dumps(state, sort_keys=True)`.
2. **Validación de integridad y pertenencia antes de restaurar:**
   Cuando `run_discovery_cycle(..., checkpoint=...)` recibe un checkpoint:
   - Verifica `resumable is True` e `idempotent is True`.
   - Recalcula el `SHA-256` de `state` y rechaza cualquier alteración con `ValueError("INVALID_CHECKPOINT_HASH")`.
   - Comprueba pertenencia (`task_id`, `task_type == "DISCOVERY_CYCLE"`) y coherencia de parámetros (`lab_type`, `benchmark_target`, `dimension`, `round_completed == 1`).
3. **Restauración real y continuación en Ronda 2:**
   - El motor restaura en memoria los resultados de la Ronda 1 **sin reejecutar la Ronda 1**.
   - Ejecuta directamente la Ronda 2 (`FOCALIZED_REFINEMENT`) a partir del ganador y parámetros restaurados de la Ronda 1.
   - Marca el resultado final con `resumed_from_checkpoint = True` y `restored_round = 1`.
4. **Equivalencia numérica verificada:**
   - Probado en `tests/test_mission27_scientific_audit.py::test_4h_recovery_distinguishes_reconciliation_from_checkpoint_resumption`: la ejecución interrumpida tras la Ronda 1 y restaurada desde el checkpoint produce exactamente el mismo `final_metric`, `best_candidate`, `verdict` y `decision_history` que una ejecución de 2 rondas no interrumpida.
   - Probado en `tests/test_mission29_candidate_certification.py::test_orc29_checkpoint_tamper_and_ownership_rejection`: cualquier alteración del hash `SHA-256`, discrepancia de `task_id` o `idempotent=False` bloquea el reencolado y la restauración.

