Aller au contenu

PD-273 — Plan d'implémentation

1. Contexte

L'audit formel PV-AUDIT-001 a identifié 3 checks Prolog en statut PARTIEL. L'objectif est de corriger ces 3 écarts pour atteindre 24/24 checks OK, sans régression.

3 Gaps identifiés

# Check Problème Solution
1 check_audit_trigger_update_delete extract-facts.py génère 'BEFORE UPDATE OR DELETE' au lieu de immutable ou prevent_update_delete Modifier extract-facts.py pour normaliser l'atome du trigger
2 check_audit_signature_sign Méthode est signAuditEntry() mais Prolog cherche sign Modifier le Prolog pour accepter signAuditEntry OU ajouter alias sign()
3 check_destruction_sequence Séquence PostgreSQL brute, pas d'entité TypeORM détectable Modifier le Prolog pour accepter service_method(destruction_audit, logDestruction)

2. Approche hybride (extract-facts.py + Prolog)

L'approche recommandée par le besoin est hybride : - Gap 1 : Modifier extract-facts.py pour normaliser les atomes de triggers - Gap 2 : Ajouter alias sign() dans AuditSignatureService (modification backend minimale) - Gap 3 : Modifier les règles Prolog pour reconnaître le pattern existant

3. Tâches

TASK-1 : Modifier extract-facts.py — normalisation atomes triggers

Fichier : scripts/formal/extract-facts.py Modification : Dans la logique de génération de table_trigger/3, normaliser les events de trigger : - 'BEFORE UPDATE OR DELETE' → atome prevent_update_delete - 'BEFORE UPDATE' → atome prevent_update - 'BEFORE DELETE' → atome prevent_delete

INV couverts : INV-273-03 CA couverts : CA-273-01

TASK-2 : Modifier Prolog check_audit_trigger_update_delete

Fichier : docs/normes/pv-audit/formal/pv_audit_compliance.pl Modification : Adapter la règle pour accepter le nouvel atome prevent_update_delete :

check_audit_trigger_update_delete :-
    table_trigger(audit_log, _, prevent_update_delete), !.

INV couverts : INV-273-03 CA couverts : CA-273-01

TASK-3 : Ajouter alias sign() dans AuditSignatureService

Fichier : src/modules/audit/services/audit-signature.service.ts Modification : Ajouter une méthode sign() qui délègue à signAuditEntry() :

async sign(entry: AuditLog): Promise<AuditLog> {
    return this.signAuditEntry(entry);
}

INV couverts : INV-273-04 CA couverts : CA-273-02

TASK-4 : Modifier Prolog check_destruction_sequence

Fichier : docs/normes/pv-audit/formal/pv_audit_compliance.pl Modification : Adapter la règle pour accepter le service existant :

check_destruction_sequence :-
    service_method(destruction_audit, logDestruction), !.
check_destruction_sequence :-
    entity_column(audit_log, destruction_seq, _), !.

INV couverts : INV-273-05 CA couverts : CA-273-03

TASK-5 : Mettre à jour test_data.pl

Fichier : docs/normes/pv-audit/formal/test_data.pl Modification : Mettre à jour les faits de test statiques pour refléter les nouveaux atomes : - table_trigger(audit_log, trg_audit_log_immutable, prevent_update_delete). - service_method(audit_signature, sign). - service_method(destruction_audit, logDestruction).

INV couverts : INV-273-01, INV-273-02 CA couverts : CA-273-04, CA-273-05

TASK-6 : Tests d'intégration Prolog

Fichier : Exécution swipl dans CI Action : Vérifier que les 24 checks passent avec les faits mis à jour :

swipl -l test_data.pl -l pv_audit_compliance.pl -g "run_audit." -t "halt."

INV couverts : INV-273-01, INV-273-02 CA couverts : CA-273-04, CA-273-05

TASK-7 : Test unitaire Jest alias sign()

Fichier : src/modules/audit/services/__tests__/audit-signature.service.spec.ts Action : Ajouter un test vérifiant que sign() délègue à signAuditEntry() :

it('sign() should delegate to signAuditEntry()', async () => {
    const spy = jest.spyOn(service, 'signAuditEntry').mockResolvedValue(mockEntry);
    const result = await service.sign(mockEntry);
    expect(spy).toHaveBeenCalledWith(mockEntry);
    expect(result).toBe(mockEntry);
});

INV couverts : INV-273-04 CA couverts : CA-273-02

4. Contraintes techniques

Dépendances inter-PD

Story Statut Nature
PD-272 DONE Trigger trg_legal_composite_proof_immutable (même pattern, autre table)
PD-250 DONE Séquence audit_destruction_seq (créée par migration PD-250)

Framework de test

  • Prolog : SWI-Prolog (swipl) — tests déterministes via run_audit.
  • Jest : Tests unitaires NestJS pour alias sign()
  • extract-facts.py : Python 3, exécution locale

Compatibilité

  • Pas de dépendance ESM
  • Pas de migration DDL (aucune modification de schéma)

5. Mapping INV/CA → Tâches/Tests

Invariant/CA Tâche couvrant Test couvrant
INV-273-01 TASK-5, TASK-6 TC-NOM-273-04 (Prolog run_audit)
INV-273-02 TASK-5, TASK-6 TC-NOM-273-05 (Prolog run_audit)
INV-273-03 TASK-1, TASK-2 TC-NOM-273-01 (check Prolog)
INV-273-04 TASK-3 TC-NOM-273-02 (check Prolog + Jest TASK-7)
INV-273-05 TASK-4 TC-NOM-273-03 (check Prolog)
INV-273-06 TASK-5, TASK-6 TC-NOM-273-06 (2 runs Prolog)
INV-273-07 N/A (par construction) TC-ERR-273-03
CA-273-01 TASK-1, TASK-2 TC-NOM-273-01
CA-273-02 TASK-3 TC-NOM-273-02, TASK-7
CA-273-03 TASK-4 TC-NOM-273-03
CA-273-04 TASK-5, TASK-6 TC-NOM-273-05
CA-273-05 TASK-6 TC-NOM-273-04
CA-273-06 TASK-6 TC-ERR-273-01/02/03/04

6. Ordre d'exécution

  1. TASK-1 (extract-facts.py) — prérequis pour la génération des bons faits
  2. TASK-2 (Prolog check trigger) — dépend de TASK-1 pour l'atome correct
  3. TASK-3 (alias sign()) — indépendant
  4. TASK-4 (Prolog check destruction) — indépendant
  5. TASK-5 (test_data.pl) — après TASK-½/4
  6. TASK-7 (Jest sign()) — après TASK-3
  7. TASK-6 (run Prolog complet) — vérification finale après toutes les modifications

7. Risques

Risque Probabilité Mitigation
L'atome normalisé casse d'autres règles Prolog Faible Vérifier les 24 checks après chaque modification
L'alias sign() crée une confusion API Faible Méthode documentée comme alias dans le code
extract-facts.py ne supporte pas le nouveau mapping Faible Tester avec --norm-pv-audit après modification