Skip to content

ADR-057: Soporte y formato por tipo documental (Ac. AGN 001/2024, Anexo 6) y semillas de integración sin disparadores

Estado: Aceptado Fecha: 2026-10-29 Autores: Giampiero (mantenedor principal); épica «soporte y formato por tipo documental», cierra la deuda B9 del lote D Relacionados: ADR-054 (cuadro público y certificado RUSD), ADR-025 (versionado de la TRD), ADR-053 (MCP)


Contexto

El Acuerdo AGN 001/2024 obliga a registrar en las TRD los formatos electrónicos de los tipos documentales (art. 4.1.4) y el Anexo 6 pide, en el campo «Soporte o Formato», dos cosas frente a cada tipo documental: marcar si se produce en físico («Papel») y, si es electrónico, indicar la extensión en que se produce y se preservará. Es información «mínima y obligatoria» para evaluar y convalidar la TRD. El Anexo 7 repite el campo para la TVD, que no tiene columna de tipos: va por agrupación. OrpycaMCP guardaba el tipo documental (código, nombre, plantilla) pero no cómo se produce.

Por separado, el carril de integración de archive-service instalaba cuatro disparadores itest_rellena_* que completaban en silencio procedimiento, certificado RUSD, tipos documentales y reserva motivada: los tests pasaban con filas que el producto rechaza, y escondían justo lo que el lote D volvió obligatorio. Con la obligatoriedad del soporte habrían sido el quinto atajo.

Decisión

1. Qué es «soporte» y «formato»

Dos campos ortogonales que calcan las dos columnas del Anexo 6: soporte_fisico BOOLEAN y formatos_electronicos TEXT[]. Un tipo producido en papel y en electrónico se expresa sin inventar un tercer valor. La API expone además soporte ∈ {fisico, electronico, fisico_y_electronico} | null como derivado de lectura (null = no declarado). Descartado: un enum con hibrido (redundante con la lista y reintroduce el vocabulario que medio_conservacion retiró a propósito).

Relación con radicados.medio_conservacion (electronico|fisico): mismo vocabulario, distinto objeto. El segundo es el soporte del original de un radicado; los campos nuevos son los soportes admitidos para la clase. No hay FK ni CHECK entre servicios.

El formato es un catálogo cerrado de códigos (orpycamcp_common.formatos_documentales): pdfa, pdf, tif, png, jpg, gif, odt, ods, docx, xlsx, pptx, doc, xls, ppt, csv, txt, eml y xml, cada uno con extensión, MIME, PUID de PRONOM y una marca informativa apto_preservacion (art. 7.1.2 par.: formatos abiertos). Texto libre habría producido «pdf», «.PDF», «PDF/A» y «pdfa» como cuatro cosas. El catálogo vive en tres sitios (Python, la función SQL td_formatos_validos y lo que storage admite) y shared/tests/test_formatos_documentales_gemelos.py los ata. Solo se añaden códigos (quitar uno rompe el CHECK de las filas que lo usan) y un código nuevo exige que storage admita su MIME en alguna de sus dos listas: allowed_mime_types (anexos que sube una persona) o allowed_system_mime_types (artefactos que produce el sistema: el XML y el .eml del correo recibido, Ley 527/1999 art. 12) — de ahí que eml y xml entren. Fuera a propósito: zip y —Q2— audio y video, que storage no admite en ninguna lista: limitación declarada (las audiencias y grabaciones no se pueden registrar como formato de un tipo hasta que storage los admita; ver docs/es/normativa.md). Enmienda (2026-10-30): Q2 saldada por ADR-060: el catálogo pasa a 26 códigos (WAV/BWF, FLAC, MP3, Opus, MPEG-4, QuickTime, Matroska, WebM) con la migración 085; la 082 se conserva intacta. El PUID de PDF/A es None a propósito: PDF y PDF/A comparten MIME y el PUID de PRONOM de un PDF/A lo da el validador según el perfil (trd_series.pdfa_profile, migración 035), no el catálogo.

2. Dónde vive

  • TRD: en tipos_documentales (migración archive 082). Aquí la fila del tipo es el tipo-en-la-serie (UNIQUE (trd_serie_id, code)), así que una tabla aparte sería una relación 1:1 sin ganancia.
  • TVD: en trd_series (solo tipo_instrumento = 'TVD', CHECK trd_series_soporte_forma).
  • Instantánea: trd_serie_tipos_convalidados gana las dos columnas; el cuadro público lee de ella (el histórico ?vigente_en dice el soporte que regía, no el vivo). Las filas anteriores a la 082 quedan NULL = «soporte no registrado», sin inventar.

3. Obligatoriedad y congelación

  • En borrador es opcional. Aprobar exige que cada tipo activo que la versión tendrá al convalidarse (los propios más los heredables de la vigente que el re-apuntado le moverá) declare papel, formato o ambos: 409 tipo_documental_sin_soporte con la lista de códigos. Convalidar y ratificar una migrada lo comprueban de nuevo sobre los tipos ya re-apuntados, dentro de la transacción y antes de la instantánea. La TVD: 409 tvd_sin_soporte al aprobar + CHECK de respaldo.
  • El tipo es contenido de la versión (Ac. AGN 001/2024 art. 5.1.4.1 nums. 7 y 9 y su parágrafo, que exige llevar el registro documentado de los cambios): el disparador trg_tipos_documentales_congelado rechaza alta y baja fuera de borrador (una migrada tampoco admite tipos nuevos: solo la captura única del soporte vacío, patrón de procedimiento), rechaza mover el tipo de serie fuera de borrador (única excepción: el re-apuntado de la convalidación, derogada → convalidada del mismo código) y rechaza reactivar un tipo inactivo fuera de borrador —un tipo inactivo no cuenta en el gate de soporte; la BAJA, que no es contenido, sigue libre—. Lee la(s) serie(s) FOR SHARE, que choca con el UPDATE de estado de aprobar: no hay carrera entre «añado un tipo» y «el Comité aprueba». El servicio toma el mismo bloqueo dentro de la transacción del alta/baja y traduce el rechazo del motor, si llegara, al mismo 409.
  • Hueco cerrado de paso: TipoDocumentalService.create/delete no comprobaban el estado de la serie (solo update). Ahora 409 trd_version_inmutable, con el disparador de respaldo. La UI ofrece «Redeclarar en el borrador»: es la forma de cambiar el soporte de un tipo vigente (el código propio tapa al heredado).

4. Formato y política de preservación: aviso, no bloqueo

Al aprobar se calcula avisos_formato: formato_en_riesgo_segun_plan (el MIME del formato está en la política resuelta COMO el job de migración de storage: formatos_origen del plan vigente o, si no declara ninguno, FORMAT_MIGRATION_MIME_TYPES del despliegue; lo que el plan lista como formatos_destino no es riesgo, y pdfa nunca lo está por MIME) y formato_no_apto_conservacion (disposición CT o S sin ningún formato apto: «apto» es abierto y sin pérdida). Enmienda ADR-060 (C1): un tercer aviso, formato_apto_condicional, cuando el único apto es WAV o MKV, cuyo códec no se identifica: «apto según contenedor, códec sin verificar». Viajan en la respuesta y en el asiento archive.trd_version_aprobada; la UI los muestra en un aviso persistente, no en un toast. Si no se pudo leer el plan, avisos_formato_evaluados = false: «no sé», nunca «sin avisos». Un bloqueo convertiría una actualización del plan en un instrumento inaprobable (la TRD convalidada es inmutable).

5. La radicación no se bloquea (Q3)

No se valida en la radicación que el anexo cumpla el formato del tipo (el derivado de lectura se calcula contra la TRD vigente el día del radicado: instantánea de la versión convalidada que regía, o el tipo vivo de la migrada): el art. 4.1.4 obliga a planear la producción, una Entrada viene de fuera y la ventanilla no puede rechazarla por su extensión, y doc_class es solo el code (no único entre series). Solo un derivado de lectura en GET /documents/{id}: conformidad_tipo {soporte, anexos_fuera_de_formato}, null para Entradas, sin tipo o con tipo ambiguo; pdfa cuenta conforme solo con pdfa_status = 'conformant'. Deuda declarada: un gate (no un aviso) para Salidas/Internas producidas fuera de E23 cuyo anexo no case con el formato del tipo. Saldada por ADR-058: las Salidas e Internas (también por lote, E23 y al retipificar) se gatean; las Entradas siguen sin bloqueo.

6. Certificado RUSD de una versión migrada (Q1)

Una migrada no tiene acto (CHECK de la 037) y sin certificado no transfiere ni elimina lo que gobierna. Puede registrar su certificado una sola vez (fill-once por trigger, con asiento), por code + version en vez de por acto, y declara su procedencia: el acto y la fecha de convalidación que el certificado inscribe (rusd_acto_declarado, rusd_convalidacion_declarada, columnas que permiten validar). El CHECK trd_series_rusd_certificado_forma exige convalidación declarada ≤ fecha del certificado (y el servicio, ≤ hoy); como la migrada tiene fecha_convalidacion NULL, la procedencia declarada es lo que mantiene válida la fila al derogarse o relevarse por la N+1 (primera versión de la 082 exigía fecha_convalidacion en derogada y dejaba la fila sin salida: 23514 permanente). Al ratificar se exige además fecha_convalidacion ≤ fecha del certificado (422 fecha_convalidacion_posterior_al_certificado).

7. Semillas de integración sin disparadores

Retirados los cuatro itest_rellena_* y los SET itest.*. Las semillas viven en tests/integration/_semillas.py y recorren el camino real: create → tipos → aprobar → convalidar → certificado, expediente reservado validado con la misma regla que la API (validar_reserva_expediente) y escrito con las siete columnas a la vez. La única excepción honesta es migrada (sembrar_serie_migrada/marcar_migrada): no se alcanza por ningún acto, solo por el backfill de la 037. El guardián tests/test_semillas_sin_atajos.py prohíbe en el resto del carril los disparadores de relleno, SET itest.*, DISABLE TRIGGER, y los UPDATE/INSERT de estado o nivel, salvo una lista blanca con motivo (el sujeto ES el camino crudo) que no puede envejecer. Esto destapó y corrigió el rojo latente de las pruebas que transfieren con una serie migrada (decía 409 trd_sin_certificado_rusd con o sin disparadores).

Citas

ArchiveService._exigir_procedimiento y la descripción MCP del procedimiento citaban el Acuerdo AGN 004/2019, derogado; ahora «Ac. AGN 001/2024, Anexo 6, campo Procedimiento». Las menciones al 060/2001 de document-service quedan fuera de esta épica.

Consecuencias

  • Datos previos (entorno de prueba): los tipos anteriores a la 082 quedan sin soporte; una versión que los herede no se aprueba hasta redeclararlos en el borrador. Una TVD convalidada antes de la 082 no tiene soporte y no se backfillea: el CHECK de respaldo exime a derogada, así que puede derogarse o relevarse por la N+1; mientras siga convalidada sin soporte, cualquier UPDATE de esa fila (p. ej. registrar su RUSD) lo rechaza el CHECK: se redeclara en una versión nueva o se recrea el entorno de prueba (tenant_demo tiene una así). Sin backfill ni ramas de compatibilidad.
  • Cambio de contrato del portal: el CSV público lleva [soporte] en la celda de cada tipo y una columna nueva al final, soporte_formato (llena solo en filas TVD).
  • MCP: tool nueva listar_formatos_documentales; crear_tipo_documental y las tools de TVD ganan soporte_fisico y formatos_electronicos; los certificados RUSD admiten code + version (migrada). actualizar_tipo_documental no lo gana: el soporte es contenido de la TRD y se edita sobre un borrador.
  • La ratificación de una TVD migrada con soporte no se implementa: una TVD nace en borrador y nunca llega a migrada.

Ronda de corrección (2026-10-29, dos auditorías)

  • Derogatoria. El Acuerdo AGN 001/2024 (art. 10.1) deroga expresamente los Acuerdos 060 de 2001 y 004 de 2019; las citas al 004/2019 que quedaban en archive (acta de eliminación, selección M, cuadro público) pasan al equivalente vigente (arts. 4.5.3, 4.5.4 num. 6 y par. 1, 7.2.1 par. 1, 5.1.3.2). Respaldo de la congelación de tipos: art. 5.1.4.1 nums. 7 y 9 (transformar tipos físicos en electrónicos; nuevos tipos documentales) y su parágrafo (registro documentado de cambios).
  • Decidido distinto (M3). La auditoría pedía congelar activo. Se congela la reactivación (false → true) fuera de borrador, que es la vía por la que un tipo sin soporte volvería al contenido sin pasar por el gate; la baja (true → false) sigue libre: no es contenido (la instantánea no cambia) y es la forma de retirar un tipo que ya no se usa sin crear una versión nueva. Un tipo dado de baja en una versión convalidada solo vuelve redeclarándolo en el borrador.
  • Respaldo del motor con rostro (L2). Alta y baja de tipos toman FOR SHARE la serie dentro de su transacción y, si el disparador rechaza por una carrera, el servicio responde el mismo 409 trd_version_inmutable; aprobar de la TVD traduce el CHECK de respaldo a 409 tvd_sin_soporte.
  • Guardián de semillas (L1). Detecta también session_replication_role, DROP TRIGGER, UPDATE con alias y nivel_seguridad en cualquier posición del SET, con controles negativos.

Enmienda (2026-11-06, decisión del dueño): el ZIP entra como anexo

«Los zip van como anexos a los expedientes y a los documentos que se radican como tal; quien lleva la TRD es el expediente y el radicado, no el anexo». La frase «fuera a propósito: zip» queda superada. zip (application/zip, PUID x-fmt/263, apto_preservacion=False) entra al catálogo con solo_anexo=True:

  • La TRD no lo gobierna. No está en CODIGOS ni en td_formatos_validos (sin migración): declararlo en formatos_electronicos es 422 formato_solo_anexo. El selector de formatos de admin/trd y admin/tvd no lo ofrece.
  • El evaluador (único, conformidad_tipo) no juzga un anexo subido de ese formato contra los formatos del tipo, y un radicado o expediente cuyo único anexo es ZIP queda solo_soporte: del formato no se afirmó nada. El soporte se sigue juzgando; el asiento marca eximido y exencion: solo_anexo en ese anexo. Es la única exención: un .docx en un tipo que solo admite PDF sigue fuera, y el MIME es el de files (storage lo detecta por contenido: un .docx rotulado «zip» se registra como lo que es).
  • Nunca principal. Un ZIP marcado es_principal es 422 zip_no_puede_ser_principal; sin principal no llega a firma, versión pública ni envío como pieza principal.
  • Limitación declarada. Orpyca no inspecciona el contenido del ZIP ni tiene antivirus; su contenido no se indexa ni se preserva por separado (se conserva el contenedor tal cual).