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 :
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 :
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 :
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 :
- norm.yaml contient
DIP: DIPdansstate_mapping(fait dans cette tache) - L'enum TypeORM
DocumentStatuscontientDIP(fait par agent-entity, moduledip-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. |