Aller au contenu

PD-278 — Agent Developer : Module dip-tla-formal

Agent : agent-developer (Agent B) Module : dip-tla-formal Story : PD-278 — NF Z42-013 : ajout contractuel de l'etat DIP Date : 2026-03-01


1. Perimetre

Element Detail
Code contract dip-tla-formal
Invariants CA-10: DIP dans DocStates TLA+, TLC verifie DocStates ⊆ RealStates sans violation
Forbidden Modifier _AnchoredFacts_NF.tla directement ; supprimer des etats existants du modele
Fichiers autorises ProbatioVault-doc/docs/normes/nf-z42-013/formal/NF_Z42_013.tla, norm.yaml

2. Analyse du modele existant

2.1 Etat du modele TLA+ (NF_Z42_013.tla)

Le modele TLA+ contient deja l'etat DIP dans DocStates :

DocStates == {"SIP", "AIP", "DIP", "ELIMINATED"}  \* ligne 54

Les transitions DIP sont deja modelisees :

Action TLA+ Transition Ligne Gardes
CommunicateAIP(doc_id) AIP -> DIP 249-264 doc_state[doc_id] = "AIP", doc_copies[doc_id] >= MIN_COPIES, current_actor in {"PA","SA","auditor"}
ReturnDIP(doc_id) DIP -> AIP 267-273 doc_state[doc_id] = "DIP"

Les invariants TLA+ couvrent deja DIP :

Invariant TLA+ Couverture DIP Ligne
TypeInvariant doc_state[d] in DocStates (DIP inclus) 352-358
MetadataInvariant doc_state[d] in {"AIP", "DIP"} 362-365
HashInvariant doc_state[d] in {"SIP", "AIP", "DIP"} 369-372
GeoCopiesInvariant doc_state[d] = "DIP" => copies >= MIN_COPIES 381-384
ArchivalAttestationInvariant doc_state[d] in {"AIP", "DIP"} 418-423
IntegrityFreshnessInvariant via VerifyDocumentIntegrity sur {"AIP", "DIP"} 173-174

Conclusion : Le fichier NF_Z42_013.tla est complet pour PD-278. Aucune modification necessaire.

2.2 Etat du fichier _AnchoredFacts_NF.tla (genere par CI)

Le fichier genere contient :

RealStates == {"SIP", "AIP", "ELIMINATED"}   \* 3 valeurs — DIP ABSENT

Et l'ASSUME de double inclusion :

ASSUME AnchorAssume_States ==
    /\ RealStates \subseteq DocStates     \* OK : {"SIP","AIP","ELIMINATED"} dans {"SIP","AIP","DIP","ELIMINATED"}
    /\ DocStates \subseteq RealStates     \* FAIL : "DIP" absent de RealStates

Cause racine : norm.yaml ne contenait pas le mapping DIP: DIP dans state_mapping. Le script extract-facts.py genere RealStates a partir de ce mapping.

2.3 Chaine de causalite

norm.yaml (state_mapping: 3 entrees)
  --> extract-facts.py (write_tla_constants)
    --> _AnchoredFacts_NF.tla (RealStates = 3 valeurs)
      --> ASSUME DocStates \subseteq RealStates = VIOLATION (DIP absent)

3. Modification effectuee

3.1 Fichier modifie : norm.yaml

Chemin : ProbatioVault-doc/docs/normes/nf-z42-013/formal/norm.yaml

Diff :

  state_mapping:
    PENDING: SIP
    SEALED: AIP
+   DIP: DIP
    EXPIRED: ELIMINATED

Justification : L'etat DIP porte le meme nom dans le modele TLA+ et dans le code backend (DocumentStatus.DIP). Le mapping est identite (DIP -> DIP), a la difference des autres etats qui ont une traduction OAIS (PENDING->SIP, SEALED->AIP, EXPIRED->ELIMINATED).

3.2 Fichier NON modifie : NF_Z42_013.tla

Le modele TLA+ contenait deja DIP dans DocStates et toutes les transitions/invariants associes. Aucune modification necessaire.

3.3 Fichier NON modifie : _AnchoredFacts_NF.tla

Interdit par le code contract (forbidden: Modifier _AnchoredFacts_NF.tla directement). Ce fichier sera regenere automatiquement par extract-facts.py lors du prochain build CI, avec le mapping DIP: DIP ajoute dans norm.yaml.

Resultat attendu apres regeneration CI :

RealStates == {"SIP", "AIP", "DIP", "ELIMINATED"}

NormMapping == [s \in {"PENDING", "SEALED", "DIP", "EXPIRED"} |->
    CASE s = "PENDING" -> "SIP"
    []   s = "SEALED" -> "AIP"
    []   s = "DIP" -> "DIP"
    []   s = "EXPIRED" -> "ELIMINATED"
]

ASSUME AnchorAssume_States ==
    /\ RealStates \subseteq DocStates    \* OK : 4 valeurs dans 4 valeurs
    /\ DocStates \subseteq RealStates    \* OK : 4 valeurs dans 4 valeurs (egalite)

4. Verification formelle (TC-FML-01)

4.1 Pre-requis pour TLC sans violation

Pour que TLC verifie DocStates ⊆ RealStates sans violation, deux conditions doivent etre reunies :

  1. norm.yaml contient DIP: DIP dans state_mapping (fait dans cette tache)
  2. L'enum TypeORM DocumentStatus contient DIP (fait par agent-entity, module dip-entity-extension)

La condition 2 est necessaire car extract-facts.py extrait les valeurs enum depuis les entities TypeORM. Sans DIP dans l'enum, le script ne generera pas "DIP" dans RealStates meme si norm.yaml contient le mapping.

4.2 Matrice de couverture TLC

Invariant TLC DIP couvert Mecanisme
TypeInvariant Oui doc_state[d] in DocStates et DIP dans DocStates
MetadataInvariant Oui Guard explicite doc_state[d] in {"AIP", "DIP"}
HashInvariant Oui Guard explicite doc_state[d] in {"SIP", "AIP", "DIP"}
JournalChainInvariant N/A Invariant sur journal, pas sur etat
GeoCopiesInvariant Oui doc_state[d] = "DIP" => copies >= MIN_COPIES
JournalNonEmptyInvariant N/A Invariant sur journal
NoRegressionInvariant Oui Trivially TRUE (garanti structurellement)
IntegrityFreshnessInvariant Indirect VerifyDocumentIntegrity accepte {"AIP","DIP"}
EliminationAttestationInvariant N/A Concerne ELIMINATED
ArchivalAttestationInvariant Oui doc_state[d] in {"AIP", "DIP"}

4.3 Transitions verifiees par TLC

Transition Source Destination Guard(s) Attestation
CommunicateAIP AIP DIP copies >= MIN_COPIES, actor in {PA,SA,auditor} restitution
ReturnDIP DIP AIP state = DIP aucune (retour simple)

4.4 Transitions interdites (verifiees structurellement)

Transition Pourquoi interdite Mecanisme
SIP -> DIP Aucune action TLA+ n'a cette transition Absence d'action
DIP -> ELIMINATED Aucune action TLA+ n'a cette transition Absence d'action
DIP -> SIP Aucune action TLA+ n'a cette transition Absence d'action
ELIMINATED -> DIP Aucune action TLA+ n'a cette transition Absence d'action

5. Correspondance spec PD-278 / modele TLA+

5.1 Mapping terminologique

Spec PD-278 (code) Modele TLA+ (OAIS) norm.yaml mapping
PENDING SIP PENDING: SIP
SEALED AIP SEALED: AIP
DIP DIP DIP: DIP (nouveau)
EXPIRED ELIMINATED EXPIRED: ELIMINATED

Note : RESTITUTED (PD-279) n'a pas d'equivalent OAIS dans NF Z42-013. Il n'apparait pas dans DocStates du modele TLA+ et n'est pas dans norm.yaml. C'est coherent car RESTITUTED est un etat ISO 14641, pas NF Z42-013.

5.2 Invariants spec vs TLA+

Invariant spec Invariant TLA+ equivalent Couverture
INV-278-01 (DIP dans enum) DocStates contient "DIP" Directe
INV-278-02 (gardes SEALED->DIP) CommunicateAIP : copies >= MIN_COPIES, actor autorise Partielle (auth/RLS/rate-limit non modellises)
INV-278-03 (retour explicite) ReturnDIP : action explicite, pas de timer Directe
INV-278-05 (attestation) CommunicateAIP : append attestation "restitution" Directe
INV-278-06 (WORM) CommunicateAIP/ReturnDIP : UNCHANGED doc_hash Directe
INV-278-08 (EXPIRED terminal) Aucune action depuis ELIMINATED Structurelle

6. Hypotheses explicites

ID Hypothese Impact si faux
H-TLA-01 extract-facts.py genere RealStates a partir des valeurs (values()) du state_mapping de norm.yaml Si le script utilise les cles au lieu des valeurs, RealStates contiendrait "DIP" (cle) mais aussi "PENDING", "SEALED", "EXPIRED" au lieu des termes OAIS — verification du script confirmee
H-TLA-02 L'enum TypeORM DocumentStatus contiendra DIP apres le travail de l'agent-entity (module dip-entity-extension) Si absent, extract-facts.py ne detectera pas DIP dans les artefacts reels et ne l'inclura pas dans RealStates meme avec le mapping norm.yaml
H-TLA-03 Le modele TLA+ utilise AIP (terme OAIS) la ou le code utilise SEALED, et le retour DIP -> AIP correspond au retour DIP -> SEALED du code Coherent avec l'architecture de mapping existante

7. Decision architecturale

architectural_decisions:
  - decision: "Mapping identite DIP: DIP dans norm.yaml (pas de traduction OAIS)"
    rationale: "DIP est le terme OAIS standard (Dissemination Information Package), identique dans le modele TLA+ et le code backend"
    alternatives_considered:
      - "Creer un terme de mapping different (ex: COMMUNICATED)"
      - "Renommer l'etat code en DISSEMINATED"
    trade_offs:
      - "Avantage: alignement direct OAIS, aucune ambiguite terminologique"
      - "Particularite: seul mapping identite dans la table (les 3 autres ont une traduction)"

8. Matrice de couverture tests

Test ID Fichier Couverture
TC-FML-01 TLC model checker sur NF_Z42_013.tla + _AnchoredFacts_NF.tla regenere CA-10 : DocStates ⊆ RealStates sans violation
TC-INV-01 (partiel) TLC verifie DIP in DocStates INV-278-01 au niveau formel

Note : TC-FML-01 sera executable apres que : 1. norm.yaml est mis a jour (fait) 2. L'enum TypeORM DocumentStatus contient DIP (module dip-entity-extension, agent-entity) 3. CI execute extract-facts.py pour regenerer _AnchoredFacts_NF.tla 4. TLC est lance sur la configuration NF_Z42_013.cfg


9. Fichiers modifies

Fichier Action Justification
ProbatioVault-doc/docs/normes/nf-z42-013/formal/norm.yaml Ajout DIP: DIP dans state_mapping Permet a extract-facts.py de generer "DIP" dans RealStates
ProbatioVault-doc/docs/normes/nf-z42-013/formal/NF_Z42_013.tla Aucune modification Modele deja complet : DIP dans DocStates, transitions CommunicateAIP/ReturnDIP, invariants couvrant DIP
ProbatioVault-doc/docs/normes/nf-z42-013/formal/_AnchoredFacts_NF.tla Non modifie (forbidden) Regenere par CI depuis norm.yaml

10. Dependances inter-agents

Agent Dependance Direction Impact
agent-entity (dip-entity-extension) DocumentStatus.DIP dans l'enum TypeORM Ce module depend de extract-facts.py extrait les valeurs enum depuis les entities TypeORM ; sans DIP dans l'enum, RealStates sera incomplet
agent-migration (dip-migration) ALTER TYPE document_status ADD VALUE 'DIP' Ce module depend de La migration DB doit etre executee pour que l'enum PostgreSQL contienne DIP
agent-tests (dip-tests) TC-FML-01 Depend de ce module Le test formel TLC ne passera qu'apres la regeneration de _AnchoredFacts_NF.tla

11. Points de vigilance

ID Point Mitigation
V-TLA-01 RESTITUTED (PD-279) n'est pas dans DocStates TLA+ Normal : RESTITUTED est ISO 14641, pas NF Z42-013. Le modele ISO_14641.tla a son propre mapping. Pas d'ecart.
V-TLA-02 Le retour dans le modele TLA+ est DIP -> AIP (terme OAIS), pas DIP -> SEALED (terme code) Gere par NormMapping dans _AnchoredFacts_NF.tla — mapping bidirectionnel SEALED<->AIP
V-TLA-03 Ordre d'execution CI : extract-facts.py doit tourner APRES la migration et l'ajout de l'enum TS Pipeline CI existant : migration -> build -> extract-facts -> TLC. Ordre correct.