PD-272 — Plan d'implementation¶
1. Synthese¶
Story simple : 1 migration PostgreSQL ajoutant un trigger BEFORE UPDATE OR DELETE sur vault_secure.legal_composite_proof pour garantir l'immutabilite post-persistance.
Pattern identique a legal_access_event (PD-81) et merkle_trees (PD-237) deja presents dans le codebase.
2. Contraintes techniques¶
Dependances inter-PD¶
| Story | Statut | Nature |
|---|---|---|
| PD-81 | DONE | Table legal_composite_proof creee dans 1740400000000-CreateLegalPreTables.ts |
| PD-237 | DONE | Pattern trigger merkle_trees_immutable() — reference |
Framework de test¶
- Runner : Jest (coherent avec le reste du backend)
- Tests d'integration : avec PostgreSQL reel (migration appliquee)
- Variables CI :
DATABASE_URLrequise
Compatibilite ESM/CJS¶
- Aucune dependance ESM-only
3. Go/No-Go¶
| Hypothese | Verification | Resultat |
|---|---|---|
Table legal_composite_proof existe | Migration 1740400000000 | OK — table creee dans schema vault_secure |
Schema vault_secure existe | Migration 1740400000000 | OK — CREATE SCHEMA IF NOT EXISTS |
| Pattern trigger existant | 1737000300000-CreateMerkleAppendOnlyTriggers.ts | OK — pattern repliquable |
4. Taches¶
TASK-1 : Migration PostgreSQL¶
Fichier : src/database/migrations/1740900000000-PD272-CreateLegalCompositeProofImmutableTrigger.ts
Contenu up() : 1. Creer la fonction vault_secure.legal_composite_proof_immutable() qui RAISE EXCEPTION 2. Creer le trigger trg_legal_composite_proof_immutable BEFORE UPDATE OR DELETE
Contenu down() : 1. DROP TRIGGER IF EXISTS 2. DROP FUNCTION IF EXISTS
Pattern suivi : Identique a vault_secure.legal_access_event_immutable() (PD-81, lignes 190-206 de 1740400000000)
Messages d'erreur contractuels : - UPDATE : legal_composite_proofs: modification interdite — preuve immutable - DELETE : legal_composite_proofs: suppression interdite — preuve immutable
TASK-2 : Test d'integration¶
Fichier : src/database/migrations/__tests__/PD272-legal-composite-proof-immutable.integration-spec.ts
Tests : - TC-NOM-01 : INSERT autorise - TC-ERR-01 : UPDATE bloque avec message exact - TC-ERR-02 : DELETE bloque avec message exact - TC-NEG-03 : UPDATE no-op (SET proof_version = proof_version) bloque aussi
TASK-3 : Verification Prolog¶
Apres migration, relancer extract-facts.py pour generer le fact :
table_trigger(legal_composite_proof, trg_legal_composite_proof_immutable, 'BEFORE UPDATE OR DELETE').
Puis verifier que check_proof_immutable_trigger passe a OK.
5. Code contracts¶
contracts:
PD-272-migration:
type: migration
file: src/database/migrations/1740900000000-PD272-CreateLegalCompositeProofImmutableTrigger.ts
invariants:
- INV-272-01: "Toute ligne est immutable apres insertion"
- INV-272-02: "UPDATE rejete avec message contractuel"
- INV-272-03: "DELETE rejete avec message contractuel"
- INV-272-04: "INSERT reste autorise"
- INV-272-05: "Immutabilite inconditionnelle"
patterns:
- "BEFORE UPDATE OR DELETE trigger"
- "RAISE EXCEPTION avec message metier"
- "RAISE EXCEPTION messages conformes spec"
forbidden:
- "IF/WHEN condition dans le trigger (immutabilite inconditionnelle)"
- "pg_notify (pas necessaire pour ce cas)"
reference: "1740400000000-CreateLegalPreTables.ts:190-206"
6. Mapping INV/CA vs taches/tests¶
| Invariant/CA | Tache couvrant | Test couvrant |
|---|---|---|
| INV-272-01 | TASK-1 | TC-NOM-01 + TC-ERR-01 + TC-ERR-02 |
| INV-272-02 | TASK-1 | TC-ERR-01 |
| INV-272-03 | TASK-1 | TC-ERR-02 |
| INV-272-04 | TASK-1 | TC-NOM-01 |
| INV-272-05 | TASK-1 | TC-NEG-03 |
| INV-272-08 | TASK-3 | TC-NOM-02 (Prolog) |
| CA-272-01 | TASK-1 | TC-NOM-03 (metadata) |
| CA-272-02 | TASK-1 | TC-ERR-01 |
| CA-272-03 | TASK-1 | TC-ERR-02 |
| CA-272-04 | TASK-1 | TC-NOM-01 |
| CA-272-05 | TASK-3 | TC-NOM-02 |
| CA-272-06 | TASK-2 | TC-NR-01 |
7. Estimation¶
| Tache | Volume | Complexite |
|---|---|---|
| TASK-1 (migration) | ~40 lignes | Faible |
| TASK-2 (tests) | ~80 lignes | Faible |
| TASK-3 (Prolog) | ~5 min | Trivial |
| Total | ~120 lignes | Faible |