Aller au contenu

ADR-0001 — Co-localisation des schémas probatoires et atomicité du handoff

  • Statut : Accepté (2026-06-07)
  • Story : PD-342 (PD-335-C), umbrella PD-335
  • Décideurs : PO ProbatioVault, gouvernance IA
  • Portée : ProbatioVault-backend — chaîne probatoire (handoff schedule PD-340, propagation correlation_id PD-341)

Contexte

La matérialisation durable du handoff probatoire (transition TRANSFERRED → PROOF_PENDING_ANCHOR, persistance du lien anchor_proof_link, écriture du correlation_id) repose sur une transaction ACID unique couvrant plusieurs schémas PostgreSQL : vault_secure (table document_transfers), vault_blockchain (table anchor_proof_link) et vault_merkle (table merkle_trees, surface durable du passage).

Cette atomicité n'existe que parce que les 16 schémas vault_* sont aujourd'hui co-localisés sur une seule base PostgreSQL desservie par un unique DataSource TypeORM. La garantie est donc réelle mais implicite : aucun artefact ne la documentait comme invariant, aucun mécanisme ne la vérifiait. Une évolution d'infrastructure future (relocalisation d'un schéma probatoire vers une base physique distincte, par exemple pour des raisons de charge, de souveraineté ou de cycle de vie) romprait silencieusement l'atomicité : une transaction ne couvrirait plus qu'un sous-ensemble des schémas, violant les invariants probatoires (no-confirm-before-commit, absence de lien orphelin, absence d'écriture partielle) sans aucun changement de code détectable — ni à la compilation, ni au démarrage, ni en test.

La feuille de route d'infrastructure ne prévoit aucune séparation physique des schémas probatoires à ce jour. Le risque est donc latent, non imminent, mais non protégé.

Décision

  1. Invariant explicite : l'atomicité ACID du handoff probatoire est garantie si et seulement si les schémas probatoires requis (vault_secure, vault_blockchain, vault_merkle) sont servis par la même connexion transactionnelle (même empreinte physique {host, port, database}).

  2. Garde fail-closed au démarrage : un garde s'exécute au bootstrap de l'application, avant la mise en service du flux probatoire. Il lit l'empreinte physique de la connexion réelle de chaque entité probatoire (options runtime du DataSource, jamais une configuration dupliquée) et vérifie la co-localisation. En cas de violation, l'application refuse de démarrer (aucun mode dégradé, aucun avertissement seul). Référence d'implémentation : ProbativeSchemaColocationGuard (src/modules/document-transfer/runtime/probative-schema-colocation.guard.ts) et sa logique pure déterministe probative-schema-colocation.evaluator.ts.

  3. Preuve de régression : un test d'intégration (PostgreSQL réel) prouve qu'une défaillance injectée pendant le handoff ne laisse aucune écriture partielle sur les trois surfaces durables probatoires. Référence : extension de test/integration/document-transfer/schedule-anchor-proof.integration.spec.ts.

  4. Pas d'implémentation cross-base maintenant (YAGNI) : tant qu'aucune séparation physique n'est planifiée, aucune mécanique de cohérence cross-base n'est implémentée. Le présent ADR en fige le design cible (ci-dessous) pour activation future.

Conséquences

Effet Description
Positif Le couplage « co-localisation ⇒ atomicité » devient explicite, gardé et tracé. Toute rupture future échoue bruyamment (refus de démarrage) plutôt que silencieusement.
Positif Aucune dette d'implémentation spéculative ; l'effort cross-base est différé jusqu'à un besoin réel.
Contrainte Toute évolution d'infrastructure touchant la topologie des schémas vault_* doit consulter le présent ADR et respecter le garde.
Maintenance La liste des schémas probatoires requis est figée dans le code ; l'ajout d'un futur schéma probatoire impose la mise à jour conjointe du garde et de cet ADR.

Design cible — séparation physique future (non implémenté)

Le jour où un schéma probatoire serait déplacé vers une base physique distincte, l'atomicité ACID mono-transaction ne sera plus disponible. La cohérence devra alors être obtenue par un patron transactional-outbox + relais post-commit + reconciliation, déjà éprouvé dans le module anchor pour la livraison de confirmation (intention durable anchor_confirmation_intent, budget de réessais retry_count, balayage de réconciliation). Aucune extension d'énumération ni nouvelle table ne sera nécessaire au-delà de la réutilisation de ce patron. Ce design ne sera activé que par une story dédiée déclenchée par la décision de séparation physique.

Références

  • Garde de co-localisation : ProbativeSchemaColocationGuard (boot fail-closed).
  • Preuve de régression : test d'intégration du handoff (schedule-anchor-proof.integration.spec.ts).
  • Stories liées : PD-340 (handoff atomique livré), PD-341 (correlation_id), PD-335 (umbrella).
  • Spécification : PD-342-specification.md (INV-342-01..14).