Saltar a contenido

ADR-051: Conservación de la entidad suspendida (empaquetado del AIP) y reintentos de preservación

Estado: Aceptado Fecha: 2026-10-22 Autores: Giampiero (mantenedor principal); lote D, bloque D-5 Relacionados: ADR-035 §8 (la suspensión no detiene la conservación; enmendado por este ADR), ADR-042 (el AIP se versiona; el reempaquetado tiene su propio backoff; enmendado), ADR-023 (WORM), ADR-036 (la ruta que enruta Starlette), Ley 594/2000 art. 46, Ac. AGN 003/2015


Contexto

C-6 y B10 dejaron tres deudas en la frontera entre «operar» y «conservar», verificadas en el código:

# Hecho Consecuencia
H1 reempaque_agotado solo se limpia tras un reempaquetado exitoso (mark_reempaquetado), y candidatos_reempaque lo excluye Un AIP con el reempaquetado agotado no tiene salida: ni lista, ni endpoint, ni pantalla. Su gemelo del empaquetado inicial (aip/reintentar) tampoco tenía pantalla
H2 El empaquetado del AIP estaba bloqueado en dos sitios para una entidad suspendida: run_once_aip recorría solo activas y document-service veta POST /internal/documentos/anexos (el inventario de anexos que el AIP necesita) El AIP de una entidad suspendida no existía hasta reactivarla, aunque el plazo de conservación corre (ADR-035 §8)
H3 GET /api/v1/preservacion/aip/{id} (interna, con token) no estaba en la lista de conservación de storage La reconciliación del 409 aip_ya_existe y _cabeza_en_storage del reempaquetado —que ya corre sobre suspendidas— recibían 403 y razonaban sobre una cabeza «desconocida»

Decisión

1. Reintento manual del reempaquetado, en un eje propio

POST /api/v1/expedientes/{id}/aip/reempaque/reintentar (sin body; USUA_PERM_EXPEDIENTE min_crud=3), gemelo de aip/reintentar. Un solo UPDATE con las condiciones en el WHERE: estado='empaquetado' AND reempaque_agotado y el clearance del llamante contra el nivel_seguridad vigente del expediente (RF-SEG-08). reempaque_agotado y reempaque_intentos vuelven a su valor inicial, reempaque_proximo_at = now() (así candidatos_reempaque, que ordena NULLS FIRST, lo toma en la siguiente barrida: el barrido no gana una rama nueva) y reempaque_ultimo_error se conserva. Asiento archive.aip_reempaque_reintento_manual en la misma transacción. Cualquier causa de no-aplicación (inexistente, sobre el clearance, no agotado, estado<>'empaquetado') es un 404 expediente_not_found neutro.

Alternativas descartadas. Ampliar aip/reintentar según el estado: un mismo verbo haría dos cosas distintas según un estado que el cliente no ve, y ADR-042 separa los ejes. Reinicio por tiempo en el barrido: no deja acto humano trazable.

GET /api/v1/expedientes/indices/aip-agotados?page=&size= (USUA_PERM_EXPEDIENTE) lista ambos ejes: estado='agotado' o estado='empaquetado' AND reempaque_agotado, con eje, intentos y ultimo_error. El clearance va en el WHERE de las filas y del count(*) de X-Total-Count: un total mayor que las filas visibles sería un oráculo. GET /expedientes/{id}/aip gana reempaque_agotado y reempaque_ultimo_error, tomados de la fila espejo de archive (storage no los conoce). La pantalla /admin/preservacion suma la pestaña «AIP agotados», con «Reintentar» que llama al POST del eje de la fila.

2. El empaquetado del AIP es conservación (enmienda de ADR-035 §8)

  • document-service admite POST /api/v1/internal/documentos/anexos en su lista de conservación: lectura de sistema, content-free (solo file_id/filename por radicado) y sin gate de clearance por diseño (inventario total), el mismo perfil de la excepción de /clasificados. La lista sigue siendo explícita y por fullmatch; el asiento diario por ruta no cambia.
  • run_once_aip pasa a list_tenants_conservacion (como las demás fases de conservación).
  • storage admite GET /api/v1/preservacion/aip/{id} (la cabeza; cierra H3). No admite .../aip/{id}/versiones: nadie de conservación la usa y queda vetada hasta que alguien la necesite a propósito.

Alternativas descartadas. Seguir esperando la reactivación: el AIP no existiría mientras corre el plazo. Que storage inventaríe solo: rompe «document es dueño de los anexos».

3. Lo que sigue esperando la reactivación

El sellado del índice (run_once, solo entidades activas) es operar. Un expediente cerrado justo antes de suspender, con el índice aún pendiente_firma, no tiene AIP hasta reactivar (el AIP exige índice firmado). Se declara; no se cambia.

Consecuencias

  • Una entidad suspendida obtiene su AIP y el reempaquetado ve la cabeza real; ya no hay archive.aip_agotado por un veto de la propia pila.
  • Se abren dos rutas internas más a la excepción de ADR-035 §8, ambas de lectura, con token interno y entidad suspended aprovisionada; ninguna ruta de usuario cambia.
  • Un operador con permiso de escritura sobre expedientes recupera un AIP agotado desde la pantalla, en cualquiera de los dos ejes, con rastro en audit_log.
  • Quedan fuera de este ADR la verificación contra EU DSS (bloque D-6) y el residuo de POST /api/v1/storage/internal/artefacto como escritura genérica (ADR-035 §8).

Pruebas

Unitarias (404 neutro, SQL con estado/bandera/clearance en el WHERE, asiento en la transacción, total con el mismo gate), de integración contra Postgres real (la barrida lo retoma; el total no filtra reservados), de admisión en document y storage (fullmatch, método, .../versiones vetada) y de iteración de run_once_aip sobre list_tenants_conservacion, con run_once fijado en activas.