Saltar a contenido

ADR-055: Reserva propia del expediente, condición del literal d, versión pública y fecha del documento

Estado: Aceptado Fecha: 2026-10-29 Autores: Giampiero (mantenedor principal); épica «deuda del lote D», puntos 2, 4, 5 y 6 Relacionados: ADR-039 (catálogo de causales), ADR-046 (vencimiento de la reserva)


Contexto

Ley 1712/2014: la reserva se motiva (art. 28), dura como máximo 15 años desde la generación de la información (art. 22), el literal d del art. 19 se reserva «mientras no se haga efectiva la medida de aseguramiento o se formule pliego de cargos», y si solo una parte está cubierta se entrega el resto en una versión pública (art. 21).

Decisiones

6. fecha_documento y generado_at (migración document 058)

En una Entrada el documento lo generó el remitente antes de llegar: contar la reserva desde el recibo la alargaba. radicados.fecha_documento solo existe en Entradas; generado_at = COALESCE(fecha_documento a 00:00 UTC, registered_at) es una columna generada, única fuente del tope de 15 años, del vencimiento y del rango del índice del art. 20. POST /documents la exige para doc_type='E'; el correo la toma de Date: (ausente, ilegible o futura → NULL); la ventanilla pública no la declara; el import INT-05 la trae si es válida. Una carta de hace 16 años no admite reserva del art. 19 (reserva_agotada). Corregirla (PATCH /{id}/conservacion) en un radicado reservado exige además PERM_RECLASIFICAR ≥ 3 (403): retrasar la generación acorta la reserva, es en efecto desclasificar. El CHECK del tope revienta como 422 reserva_excede_tope.

5. Condición del literal d (document 059)

clasificacion_condicion ∈ {medida_aseguramiento, pliego_cargos}, bicondicional con la causal (CHECK) y obligatoria en radicar, reclasificar y desclasificar (el catálogo compartido marca requiere_condicion). El plazo sigue siendo el tope; la condición, si se cumple antes, termina la reserva por un acto tipado POST /documents/{id}/reserva/condicion-cumplida (PERM_RECLASIFICAR ≥ 3): {condicion, acto_referencia, fecha_acto, motivo}, 2→1 directo (ADR-039: bajar de 2 a 1 no pide segunda persona), asiento document.reserva_condicion_cumplida con el estado anterior completo, mismo evento de reclasificación que el vencimiento (knowledge re-aplica el nivel), y retira la versión pública. Sin fila: 404 neutro si no la ve, 409 condicion_no_aplica si la ve. El índice del art. 20 dice «hasta la medida/el pliego; máximo hasta {vence}».

2. Reserva propia del expediente (archive 081)

El expediente tiene la misma reserva que el radicado: causal, plazo, fundamento, norma, excepción (y, por coherencia, condición) y clasificado_at, con el mismo catálogo y las mismas reglas. Por eso el catálogo se trasladó a shared/orpycamcp_common/causales_reserva.py (cuatro sitios que el guardián compara: módulo, CHECK de document 050, CHECK de archive 081 y enum MCP). ExpedienteCreate exige la reserva motivada con nivel ≥ 2 (422 por cada combinación inválida). Vence 2→1 por la ley, sin actor y canal system, con un job propio (reserva_vencimiento, advisory lock de dos claves, cursor id >, tenants active y suspended); el UPDATE repite la condición, así que la lectura de candidatas no decide. El nivel 3 no vence. No hay evento: knowledge no deriva el acceso del nivel del expediente; el guardián test_el_nivel_del_expediente_no_cambia_en_silencio admite exactamente este UPDATE (hacia 1, desde 2). Lista de trabajo GET /expedientes/reservas-por-vencer (clearance en el WHERE de filas y total). Los expedientes reservados se suman al índice público del art. 20, agrupados por su serie y content-free.

Residuo: reclasificación manual de un expediente (subir exige propagar a knowledge con GREATEST; bajar de 3 exige el circuito de segunda persona).

4. Versión pública (document 060)

radicado_version_publica: un PDF tachado que elabora una persona (PERM_RECLASIFICAR ≥ 3) y aprueba otra (≥ 4), con rvp_sod por CHECK. El sistema no garantiza que el tachado sea irreversible (capa de texto recuperable): lo garantizan las dos personas y el aviso fijo de la interfaz. Una versión vigente por radicado (índice único parcial); contenido inmutable; retirada es terminal. La huella y el tipo los resuelve storage (el PDF lo subió quien lo elabora y nadie más lo usa; idéntico a un anexo → 422). Lectura: GET con clearance devuelve la vigente y su historial; sin clearance solo la proyección pública de una versión aprobada (asunto público, partes reservadas, huella), con 404 idéntico si no hay; el contenido se sirve mediado y deja traza de lectura. Si el radicado deja de ser reservado con excepción parcial, la versión se retira en la misma transacción del acto (causa: excepcion_cambiada o reserva_terminada); un constraint trigger diferido hace fallar el COMMIT si un camino lo olvida.

Residuo: publicación de oficio de la versión pública (chokepoint propio en OAI/CMIS) y su aparición en la búsqueda.

Hallazgo cerrado: el índice electrónico y los radicados de nivel superior

El índice aplicaba el nivel del expediente a todos sus ítems: uno de nivel 1 exponía número, tipología y huella de radicados de nivel superior (vincular no compara niveles, M2). Remedio: el recorte va en la fuente —document-service, dueño del nivel, devuelve solo los radicados con nivel_seguridad <= clearance en su WHERE—. El JSON del índice se sirve filtrado (y su recuento); el XML firmado, que no puede servirse recortado, responde el mismo 404 que un índice inexistente a quien no alcanza todos sus ítems; verify no enumera lo que el lector no alcanza; si document-service no responde, 503 (nunca la lista completa). No hay <listaControlAcceso> por documento (perfil v5): sigue siendo residuo.

Consecuencias

Migraciones document 058-060, archive 081. Sin backfill: lo que no cumple un CHECK nuevo deja la restricción sin validar y se recrea con scripts/reset-entorno.sh.

Ronda de corrección (2026-10-29)

  • Índice electrónico: una política en todos los caminos. Para un lector que no alcanza todos los ítems, la vista vigente, la lista de versiones y la reconstrucción declaran el TOTAL (num_documentos) y añaden vista: "parcial", omitidos_por_reserva (solo los ítems vigentes, nunca negativo), num_visibles y la base «Ley 1712/2014 arts. 18 y 19». xml_sha256 y firma_id son del índice COMPLETO. El XML firmado no se recorta: 409 indice_incluye_informacion_reservada (antes 404). Ese código es un oráculo de EXISTENCIA sin contenido —dice que hay algo que el lector no puede ver, no qué— y se acepta porque 404 mentía sobre un índice que existe y la omisión ya es visible en la vista parcial. verify recalcula documentos_ok/valido solo sobre lo visible.
  • Canal MCP. consultar_version_publica entrega por MCP únicamente la proyección de la versión APROBADA; una versión elaborada o retirada lleva asunto y partes reservadas que nadie aprobó entregar, y la barrera de residencia no la reconocería como contenido. La proyección pública añade causal_codigo, fundamento_norma y vence_at (no el fundamento libre) y cambiar la causal retira la versión (excepcion_cambiada).
  • Date: del correo solo se toma si el correo está autenticado (cabecera escrita por el remitente: acorta o alarga una reserva), con cota de 30 años; en otro caso la declara quien radica en el formulario de la bandeja.
  • Corregir fecha_documento a una fecha más antigua recorta, en la misma transacción, el plazo a 180 - meses(generación→clasificación); si no queda nada, la reserva terminó y el radicado baja 2→1 como en el barrido (versión pública retirada, asiento y evento). El asiento registra plazo antes/después.
  • Expediente. La generación es LEAST(opened_at, fecha_extrema_inicial) (columna generada generado_at) en el CHECK, en exp_reserva_vence_at, en el índice del art. 20 y en la validación al crear.
  • Literal d del expediente tiene salida: POST /expedientes/{id}/reserva/condicion-cumplida (gemelo del radicado, PERM_RECLASIFICAR ≥3, clearance en el WHERE, asiento atómico) y su tool MCP.