# 09 — AUDITORÍA DE MEMORIA CIENTÍFICA Y RECUPERACIÓN (ORC-27)

## 1. Integridad y Persistencia en `ScientificMemory` (`app/core/scientific_memory.py`)

### 1.1. Entidades Persistidas y Trazabilidad Criptográfica
- **Proyectos e Hipótesis (`research_projects`, `hypotheses`):** Registro del origen de la hipótesis (`HEURISTIC`, `OBSERVED`, `USER_QUESTION_FORMULATED`) y estado de validación.
- **Experimentos y Corridas (`experiments`, `experiment_runs`):** Configuración JSON, semilla aleatoria (`random_seed`), tiempo de pared (`execution_time_seconds`), tiempo de CPU (`cpu_time_seconds`), pico de memoria (`peak_memory_mb`) y métricas detalladas.
- **Artefactos Científicos (`scientific_artifacts`):** Pesos de modelos (`.pt`, `.joblib`) y reportes con cálculo real de hash `SHA-256` por bloques de 64 KB (`ScientificMemory.calculate_file_hash`) y tamaño en bytes verificado en disco.
- **Evidencia Científica (`scientific_evidence`):** Huella `sha256_fingerprint`, nivel de confianza calibrado (`VERY_HIGH`, `HIGH`, `MODERATE_SYNTHETIC`, `LOW`, `UNCERTAIN`) y limitaciones explícitas.

### 1.2. Respaldo SQLite Seguro bajo Concurrencia (`create_memory_backup`)
- **Hallazgo ORC-27:** La implementación original utilizaba `shutil.copy2(source_db, backup_file)`. En bases SQLite configuradas con `PRAGMA journal_mode=WAL`, copiar únicamente el archivo `.db` a nivel de sistema de archivos mientras existen conexiones abiertas puede omitir transacciones alojadas en el archivo `-wal` o producir páginas inconsistentes.
- **Solución Implementada:**
  - Se reemplazó `shutil.copy2` por la API nativa de respaldo en línea de SQLite (`sqlite3.Connection.backup(dst_conn)`), la cual adquiere los bloqueos compartidos apropiados y copia todas las páginas consolidadas de manera atómica.
  - Inmediatamente después de completar el respaldo, se ejecuta `PRAGMA integrity_check` sobre la copia generada y se verifica que retorne `'ok'`.
- **Protección de la Base Operativa:** Durante toda la misión ORC-27, la base canónica `C:\xampp\htdocs\centro de investigacion\storage\db\research_center.db` (`SHA-256: E4A5A8DAE3B9BF9C24DF50ABC243CB4969F4CF4B29CC98F1013091CF01F9CDE9`) no fue abierta ni modificada; todas las pruebas de respaldo se ejecutaron sobre la base temporal aislada en `working_copy`.

---

## 2. Reconciliación de Fallos vs. Reanudación Real de Experimentos (`app/core/coordinator.py`)

### 2.1. Distinción Semántica y Técnica (`recover_interrupted_tasks`)
El método `ScientificCoordinator.recover_interrupted_tasks()` distingue ahora de forma explícita y auditable dos situaciones ante tareas huérfanas encontradas en estado `RUNNING` tras una caída o apagado:

| Caso | Condición Técnica | Estado Final | `recovery_action` | `resumed` | Explicación |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **1. Reconciliación de Tarea Interrumpida (Comportamiento por defecto)** | La tarea `RUNNING` no posee un bloque `checkpoint` verificable en su payload. | `FAILED` | `RECONCILED_AS_FAILED` | `False` | **No simula que la tarea se reanudó.** Se clasifica limpiamente como `FAILED` con mensaje `CRASH_RECOVERY (RECONCILED_NOT_RESUMED)` y registro en `AuditLog`. |
| **2. Re-encolado desde Checkpoint Reproducible** | La tarea `RUNNING` incluye `payload["checkpoint"]` con `"resumable": True`, `"state"` no nulo y `"checkpoint_sha256"` que coincide exactamente con el hash SHA-256 del estado serializado. | `PENDING` | `CHECKPOINT_REQUEUED_FOR_RESUMPTION` | `True` | Verifica criptográficamente el estado guardado antes de re-encolar la tarea como `PENDING` para su reanudación reproducible. |

### 2.2. Evidencia de Verificación
- **Escenario E (`run_scenarios.py`):** Verifica que una tarea huérfana sin checkpoint se reconcilia como `FAILED` con diagnóstico `CRASH_RECOVERY`.
- **Prueba Unitaria `test_4h_recovery_distinguishes_reconciliation_from_checkpoint_resumption` (`tests/test_mission27_scientific_audit.py`):** Verifica simultáneamente que una tarea sin checkpoint recibe `recovery_action="RECONCILED_AS_FAILED"` (`resumed=False`) y una tarea con checkpoint SHA-256 válido recibe `recovery_action="CHECKPOINT_REQUEUED_FOR_RESUMPTION"` (`resumed=True`, `checkpoint_verified=True`).

