PD-281 — Plan d'implémentation¶
1. Découpage en composants¶
C1 — Discriminateur enum dans extract-facts.py¶
Fichier : scripts/formal/extract-facts.py Responsabilité : Modifier la fonction _collect_zlint_entities() et la boucle de génération enum_states dans write_zlint_config() pour ne produire des checks Anchor enum que pour les colonnes status ou explicitement mappées dans _z_enum_type_mappings.
Modifications :
_collect_zlint_entities()(lignes 1498-1522) : Adapter la collecte pour stocker tous les enums d'une entité (pas seulement le dernier), en conservant le nom de colonne (field) pour chaque enum.- Boucle
enum_states(lignes 1607-1612 danswrite_zlint_config()) : Ajouter un filtre de discrimination : seules les entrées dontfield == 'status'ou dont le couple(entity_name)est présent dans_z_enum_type_mappingspour le slug courant sont incluses dansanchored_enums.
Mécanisme de discrimination (v2 — correction AMB-02, couple entité+colonne) :
# Pseudo-code du filtre — itère sur le couple (entity_name, field)
for ent_name, enum_list in enum_states.items():
# enum_list = [{field, values}, ...] (multi-enum par entité)
for enum_data in enum_list:
field = enum_data['field']
values = enum_data['values']
# DISCRIMINATION sur le couple (ent_name, field)
if field == 'status':
# Colonne status → inclusion automatique
z_type = enum_type_map.get(ent_name, f'{ent_name}_state')
anchored_enums[ent_name] = {'values': values, 'expected_z_type': z_type}
elif ent_name in enum_type_map:
# Mapping explicite dans _z_enum_type_mappings → inclusion
z_type = enum_type_map[ent_name]
anchored_enums[ent_name] = {'values': values, 'expected_z_type': z_type}
# Sinon → exclusion (pas de check Anchor enum pour ce couple)
Observable : Les _generated-z-lint.yaml régénérés ne contiennent plus anchor_batch_event, key_envelope, legal_access_event, legal_mandate, deposit (pour leurs colonnes non-status) dans anchored.enum_states.
C2 — Complétion des types Z dans les fichiers .zed¶
Fichiers :
docs/normes/rfc-3161/formal/RFC_3161.zeddocs/normes/nf-z42-013/formal/NF_Z42_013.zeddocs/normes/iso-14641/formal/ISO_14641.zeddocs/normes/pv-pre/formal/PV_PRE.zed
Responsabilité : Ajouter les déclarations de types Z manquants dans la SECTION 1 (Basic Types) de chaque fichier .zed cible, avec les valeurs exactes spécifiées.
Types à ajouter :
| Type Z | Fichier .zed | Valeurs | Emplacement |
|---|---|---|---|
TimestampBatchStatus | RFC_3161.zed | OPEN \| SEALED \| TIMESTAMPED \| FAILED | SECTION 1 — ajout explicite du type (même si BatchState existe avec les mêmes valeurs, TimestampBatchStatus est exigé par la Spec §5.3 et vérifié par TC-NOM-05) |
AnchorBatchStatus | NF_Z42_013.zed | PENDING \| BUILDING \| SUBMITTED \| PENDING_FINALITY \| FINALIZED \| FAILED | SECTION 1 |
AnchorBatchStatus | ISO_14641.zed | PENDING \| BUILDING \| SUBMITTED \| PENDING_FINALITY \| FINALIZED \| FAILED | SECTION 1 |
LegalReKeyStatus | PV_PRE.zed | ACTIVE \| REVOKED \| EXPIRED \| COMPLETED \| DESTROYED | SECTION 1 |
Observable : Chaque type Z est présent dans le fichier .zed cible avec exactement les valeurs listées, en notation Z standard (TypeName ::= VAL1 | VAL2 | ...).
C3 — Régénération et validation globale¶
Fichiers :
scripts/formal/extract-facts.py(exécution)- Tous les
_generated-z-lint.yaml(9 normes) docs/normes/AUDIT-SYNTHESIS.md
Responsabilité : Exécuter la chaîne complète extract-facts.py → verify-norm.sh sur les 9 normes, valider le résultat SUCCESS global, mettre à jour AUDIT-SYNTHESIS.md.
Observable : verify-norm.sh retourne SUCCESS sur 9/9 normes, AUDIT-SYNTHESIS.md affiche Z-lint 9/9 PASS.
C4 — Règle conditionnelle DepositStatus¶
Fichiers : NF_Z42_013.zed, ISO_14641.zed (conditionnellement)
Responsabilité : Vérifier dans les faits extraits si deposit.status existe comme colonne lifecycle. Si oui, ajouter DepositStatus dans les .zed concernés. Si non, ne rien ajouter.
Mécanisme : Rechercher entity_enum_values(deposit, status, [...]) dans les faits Prolog générés. Présence = colonne lifecycle confirmée. Absence = pas d'exigence DepositStatus.
Observable : Présence/absence de DepositStatus dans les .zed conforme à l'existence de deposit.status dans les faits extraits.
2. Flux techniques¶
Flux A — Discrimination (C1)¶
1. extract-facts.py extrait entity_enum_values depuis les entités TypeORM
2. _collect_zlint_entities() collecte TOUS les enums (entity, field, values)
3. write_zlint_config() itère sur enum_states :
3a. Si field == 'status' → inclusion (Anchor enum check généré)
3b. Si entity dans _z_enum_type_mappings pour ce slug → inclusion (mapping explicite)
3c. Sinon → exclusion (pas de check Anchor enum)
4. anchored.enum_states dans _generated-z-lint.yaml ne contient que les state machines
Flux B — Complétion (C2)¶
1. Identifier les types Z manquants (tableau §5.3 de la spec)
2. Pour chaque type Z manquant :
2a. Ouvrir le fichier .zed cible
2b. Localiser SECTION 1 (Basic Types)
2c. Ajouter la déclaration TypeName ::= VAL1 | VAL2 | ...
2d. Vérifier cohérence avec les valeurs enum du code (faits Prolog)
3. Sauvegarder sans modifier les autres sections
Flux C — Régénération et validation (C3)¶
1. Exécuter extract-facts.py pour régénérer :
- _generated-facts.pl (faits Prolog)
- _generated-z-lint.yaml (configs Z-lint) pour chaque norme
2. Pour chaque norme (9 au total) :
2a. Exécuter verify-norm.sh <norm-dir> <reports-dir>
2b. Vérifier statut de sortie = 0 (SUCCESS)
3. Vérifier AUDIT-SYNTHESIS.md : Z-lint = 9/9 PASS
4. Vérifier idempotence : relancer le flux → même résultat
Flux D — Condition DepositStatus (C4)¶
1. Exécuter extract-facts.py
2. Rechercher entity_enum_values(deposit, status, [...]) dans les faits
3. Si trouvé :
3a. Extraire les valeurs enum
3b. Ajouter DepositStatus dans NF_Z42_013.zed et ISO_14641.zed
3c. Mettre à jour _z_enum_type_mappings si nécessaire
4. Si non trouvé :
4a. Ne rien ajouter
4b. Vérifier qu'aucun check DepositStatus n'est exigé
3. Mapping invariants → mécanismes¶
| Invariant ID | Exigence | Mécanisme | Composant | Observable | Risque |
|---|---|---|---|---|---|
| INV-281-01-discrimination | Seules les colonnes status et celles mappées dans _z_enum_type_mappings sont éligibles au check Anchor enum | Filtre conditionnel dans write_zlint_config() : if data['field'] != 'status' and ent_name not in enum_type_map: continue | C1 | Absence des couples exclus dans anchored.enum_states des _generated-z-lint.yaml | Faible — logique de filtre simple et déterministe |
| INV-281-02-exclusion-classification | Les 5 colonnes de classification sont exclues du check | Conséquence directe du filtre INV-281-01 (ces colonnes ne sont pas status et n'ont pas de mapping explicite) | C1 | Aucun des 5 couples (anchor_batch_event.event_type, key_envelope.envelope_type, legal_access_event.event_type, legal_mandate.validation_status, deposit.document_category) dans enum_states | Faible — couvert par INV-01 |
| INV-281-03-completude-types-z | Chaque colonne status doit avoir un type Z dans le .zed cible | Ajout manuel des 4 types Z manquants dans les fichiers .zed (SECTION 1) | C2 | Présence textuelle de TimestampBatchStatus, AnchorBatchStatus, LegalReKeyStatus avec valeurs exactes | Faible — ajout déclaratif |
| INV-281-04-non-regression-anchor-entity | Checks Anchor entity inchangés | Aucune modification de la logique Anchor entity dans z-notation-lint.py ni dans la boucle anchored_entities de extract-facts.py | C1 | Diff avant/après sur sections anchored.entities et check_anchoring() pour les entities : identique | Moyen — vérifier que le refactoring de _collect_zlint_entities ne perturbe pas le dict entities |
| INV-281-05-idempotence | Deux exécutions identiques → même résultat | Le script extract-facts.py est déterministe (tri alphabétique des clés, même input → même output). Aucun élément aléatoire ajouté. | C1, C3 | Deux runs consécutifs → diff des _generated-z-lint.yaml = vide | Faible — le code actuel est déjà déterministe |
| INV-281-06-resultat-global | verify-norm.sh = SUCCESS, Z-lint = 9/9 PASS | Exécution complète de la chaîne de vérification après corrections C1+C2+C4 | C3 | Statut de sortie verify-norm.sh = 0 sur 9 normes, AUDIT-SYNTHESIS.md affiche 9/9 PASS | Moyen — dépend de la correction complète des 3 volets |
| INV-281-07-modele-etats-lint | Pas de spécification de transitions métier | Aucun ajout de règles de transitions dans les .zed ou le linter. Les types Z ajoutés (C2) déclarent les valeurs enum sans contraindre les transitions. | C2 | Inspection des .zed modifiés : aucun Δ-schema de transition ajouté pour les nouveaux types | Faible — périmètre limité aux déclarations de types |
| INV-281-08-responsabilite-doc-only | Changements limités à ProbatioVault-doc | Tous les fichiers modifiés sont dans ProbatioVault-doc : scripts/formal/extract-facts.py, docs/normes/*/formal/*.zed, docs/normes/*/formal/_generated-z-lint.yaml | C1, C2, C3 | git diff --stat ne montre que des chemins dans ProbatioVault-doc | Faible — périmètre naturellement doc-only |
4. Mapping critères d'acceptation → mécanismes¶
| Critère ID | Mécanisme(s) | Composant | Observable | Risque |
|---|---|---|---|---|
| CA-01 | Exécution verify-norm.sh sur 9 normes après corrections C1+C2+C4 | C3 | Statut de sortie = 0, 0 échec Z-lint | Moyen — dépend de l'intégration des 3 volets |
| CA-02 | Ajout des 4 types Z dans les .zed cibles + condition DepositStatus | C2, C4 | Lecture des fichiers .zed : types présents avec valeurs exactes | Faible — ajout déclaratif |
| CA-03 | Filtre discrimination dans write_zlint_config() | C1 | Absence des couples exclus dans anchored.enum_states des configs générées | Faible |
| CA-04 | Mise à jour de AUDIT-SYNTHESIS.md après run complet | C3 | Texte Z-lint 9/9 PASS visible dans le document | Faible — conséquence de CA-01 |
| CA-05 | Filtre discrimination (C1) élimine les faux positifs des configs | C1 | Vérification textuelle : aucun des 5 couples exclus dans enum_states | Faible |
| CA-06 | Non-modification de la logique Anchor entity dans z-notation-lint.py et de la boucle anchored_entities dans extract-facts.py | C1 | Comparaison avant/après des sections anchored.entities et des checks Anchor entity : identiques | Moyen |
| CA-07 | Déterminisme du script (tri alphabétique, pas de random, entrées identiques) | C1, C3 | Deux runs → diff des artefacts = vide | Faible |
| CA-08 | Vérification de l'existence de deposit.status dans les faits Prolog + ajout conditionnel de DepositStatus | C4 | Si entity_enum_values(deposit, status, ...) existe → DepositStatus dans les .zed; sinon → absent | Moyen — dépend de la source de vérité |
5. Mapping tests (TC-*) → mécanismes + observables¶
| Test ID | Référence spec | Mécanisme(s) | Point(s) d'observation | Niveau de test visé |
|---|---|---|---|---|
| TC-NOM-01 | INV-281-01, CA-03 | Filtre discrimination field != 'status' dans write_zlint_config() | _generated-z-lint.yaml pour pv-anchor : anchor_batch_event absent de enum_states | Intégration |
| TC-NOM-02 | INV-281-01 | Filtre discrimination field == 'status' → inclusion | _generated-z-lint.yaml : anchor_batch présent dans enum_states avec expected_z_type | Intégration |
| TC-NOM-03 | INV-281-01, CA-03 | Mapping explicite dans _z_enum_type_mappings → inclusion | _generated-z-lint.yaml : entité mappée présente avec le z_type du mapping | Intégration |
| TC-NOM-04 | INV-281-02, CA-03, CA-05 | Filtre discrimination exclut les 5 couples contractuels | Vérification textuelle des 9 _generated-z-lint.yaml : aucun des 5 couples dans enum_states | Intégration |
| TC-NOM-05 | INV-281-03, CA-02 | Présence des 4 types Z dans les .zed cibles | Lecture des fichiers : TimestampBatchStatus, AnchorBatchStatus, LegalReKeyStatus présents avec valeurs exactes | Unitaire |
| TC-NOM-06 | INV-281-03, CA-08 | Règle conditionnelle DepositStatus (branche vraie) | Si deposit.status existe : DepositStatus dans NF_Z42_013.zed et ISO_14641.zed | Intégration |
| TC-NOM-07 | INV-281-03, CA-08 | Règle conditionnelle DepositStatus (branche fausse) | Si deposit.status absent : DepositStatus non exigé | Intégration |
| TC-NOM-08 | INV-281-06, CA-01, CA-04 | Exécution complète verify-norm.sh sur 9 normes | Statut SUCCESS, 0 échec Z-lint, AUDIT-SYNTHESIS.md = 9/9 PASS | E2E |
| TC-NOM-09 | INV-281-05, CA-07 | Deux exécutions consécutives de extract-facts.py + verify-norm.sh | diff des _generated-z-lint.yaml et des verdicts = vide | E2E |
| TC-NOM-10 | INV-281-04, CA-06 | Non-modification de la logique Anchor entity | diff avant/après des sections anchored.entities = vide | Intégration |
| TC-NOM-11 | INV-281-07 | Inspection des .zed modifiés : aucune règle de transition métier | Aucun Δ-schema de transition ajouté pour les nouveaux types | Unitaire |
| TC-NOM-12 | INV-281-08 | git diff --stat limité à ProbatioVault-doc | Aucune modification hors projet doc | Unitaire |
| TC-ERR-01 | ERR-281-01 | Validation des formats §5.1 dans le linter | Message FAIL lint avec champ invalide identifié | Intégration |
| TC-ERR-02 | ERR-281-02, INV-281-02 | Détection de faux positif post-correction | Non-conformité majeure si check Anchor enum pour colonne non-status non mappée | Intégration |
| TC-ERR-03 | ERR-281-03, INV-281-03 | Type Z absent du .zed cible | verify-norm.sh échoue avec motif "type Z manquant" | E2E |
| TC-ERR-04 | ERR-281-04, CA-01 | verify-norm.sh ≠ SUCCESS | Story non acceptée | E2E |
| TC-ERR-05 | ERR-281-05, CA-04 | AUDIT-SYNTHESIS.md ≠ 9/9 PASS | Story non acceptée | E2E |
| TC-ERR-06 | ERR-281-06, INV-281-04 | Régression Anchor entity détectée | Story non acceptée, régression bloquante | Intégration |
| TC-ERR-07 | ERR-281-07, INV-281-05 | Non-idempotence entre deux runs | Story non acceptée, défaut pipeline | E2E |
| TC-NR-01 | INV-281-04 | Baseline Anchor entity avant/après | Diff strictement identique | Intégration |
| TC-NR-02 | CA-06 | Contrôles non ciblés inchangés | Aucun changement des règles hors scope | Intégration |
| TC-NR-03 | INV-281-08 | Périmètre doc-only | git diff --stat limité à ProbatioVault-doc | Unitaire |
| TC-NR-04 | INV-281-05 | Idempotence | Run #1 == Run #2 | E2E |
| TC-NEG-01 à TC-NEG-10 | ERR-281-01, §5.1 | Validation des formats (regex, longueur, casse) | Messages FAIL lint explicites | Unitaire |
6. Gestion des erreurs¶
| Erreur | Condition | Traitement | Code/Statut | Observable |
|---|---|---|---|---|
| ERR-281-01 | Donnée non conforme aux formats §5.1 | Le linter Z (z-notation-lint.py) produit un FAIL avec cause explicite traçant le champ invalide | FAIL lint | Message d'erreur dans le rapport JUnit XML |
| ERR-281-02 | Faux positif (Anchor enum check pour colonne non-status non mappée) | Détecté lors de l'inspection des _generated-z-lint.yaml post-régénération | Non-conformité majeure | Couple fautif visible dans anchored.enum_states |
| ERR-281-03 | Type Z requis absent du .zed cible | z-notation-lint.py produit un FAIL "Z type {z_type} not found in spec" | FAIL lint | Échec dans le rapport JUnit |
| ERR-281-04 | verify-norm.sh ≠ SUCCESS | Script retourne exit code 1 (FAILURES > 0) | Exit code 1 | Sortie console + rapport |
| ERR-281-05 | AUDIT-SYNTHESIS.md ≠ 9/9 PASS | Divergence d'observable audit | Non-conformité | Texte dans le document |
| ERR-281-06 | Régression Anchor entity | Détectée par comparaison diff avant/après | Bloquante | Différence dans sections anchored.entities |
| ERR-281-07 | Non-idempotence | Détectée par double exécution + diff | Non-idempotence pipeline | Diff non vide entre run #1 et #2 |
7. Impacts sécurité¶
Aucun impact sécurité identifié.
- PD-281 modifie uniquement des scripts d'analyse statique et des spécifications formelles (
*.zed) - Aucune donnée utilisateur traitée
- Aucune modification de code backend (entités, services, controllers)
- Aucun secret, token ou credential manipulé
- Les scripts modifiés (
extract-facts.py) ne sont exécutés qu'en CI/CD (pipeline de vérification formelle), pas en production
8. Hypothèses techniques¶
| ID | Hypothèse | Impact si faux |
|---|---|---|
| H-281-01 | Le pipeline cible couvre exactement 9 normes : rfc-3161, rfc-5054, nf-z42-013, iso-14641, pv-anchor, pv-audit, pv-envelope, pv-pre, pv-proof (v2 — vérifié sur le filesystem : docs/normes/*/formal/ contient ces 9 dossiers. La Spec §5.1 liste etsi-119-xxx, eidas, rgpd qui n'existent pas — divergence AMB-01 résolue, la Spec doit être mise à jour). | Les CAs numériques (9/9 PASS) deviennent invalides. Ajuster le nombre cible. |
| H-281-02 | La structure actuelle de _collect_zlint_entities() stocke un seul enum par entité (le dernier trouvé). La modification pour filtrer par field doit conserver ce comportement mono-enum ou le rendre multi-enum. | Si multi-enum nécessaire (entité avec status ET event_type), adapter le dict pour une liste d'enums par entité. Le filtre doit alors itérer sur chaque enum de l'entité. |
| H-281-03 | Les fichiers .zed référencés (RFC_3161.zed, NF_Z42_013.zed, ISO_14641.zed, PV_PRE.zed) sont les sources de vérité de leurs normes respectives | Risque de corriger le mauvais artefact si un autre fichier .zed est la référence |
| H-281-04 | Le check Anchor entity et le check Anchor enum sont orthogonaux dans z-notation-lint.py (check_anchoring traite les deux sections indépendamment) | Si couplage caché, la modification du filtre enum pourrait affecter les checks entity |
| H-281-05 | deposit n'a qu'une seule colonne enum (document_category). L'existence de deposit.status est vérifiable dans les faits Prolog extraits. | Si deposit a un status, il faut ajouter DepositStatus (CA-08). Si la source de vérité est inaccessible, CA-08 n'est pas testable. |
| H-281-06 | RFC_3161.zed contient déjà BatchState ::= OPEN \| SEALED \| TIMESTAMPED \| FAILED (constaté). (v2 — correction ECT-01) : TimestampBatchStatus est ajouté explicitement dans le .zed conformément à la Spec §5.3. Le mapping _z_enum_type_mappings référence {'timestamp_batch': 'TimestampBatchStatus'}. BatchState coexiste comme type interne. | Si les deux types avec mêmes valeurs causent un conflit dans le linter → renommer ou supprimer BatchState (risque faible, le linter ne valide que les types référencés par expected_z_type). |
9. Points de vigilance (risques, dette, pièges)¶
9.1 Multi-enum par entité¶
Risque : _collect_zlint_entities() stocke actuellement enum_states[entity_name] = {field, values}, écrasant les enums précédents de la même entité. Si une entité a à la fois status et event_type, seul le dernier extrait sera conservé.
Mitigation : Adapter la structure pour stocker une liste d'enums par entité : enum_states[entity_name] = [{field, values}, ...]. Le filtre itère ensuite sur chaque enum et n'inclut que ceux avec field == 'status' ou mapping explicite.
9.2 TimestampBatchStatus dans RFC_3161.zed¶
Contexte : RFC_3161.zed contient déjà BatchState ::= OPEN | SEALED | TIMESTAMPED | FAILED avec les mêmes valeurs que TimestampBatchStatus.
Décision (v2 — correction Gate 5) : Créer TimestampBatchStatus explicitement dans RFC_3161.zed (SECTION 1), conformément à la Spec §5.3 et au test TC-NOM-05. Le mapping _z_enum_type_mappings pour rfc-3161 doit référencer 'timestamp_batch': 'TimestampBatchStatus' (et non BatchState).
Justification : La Spec est le contrat canonique. BatchState peut coexister comme type interne non lié à l'ancrage Z-lint. L'ajout de TimestampBatchStatus est déclaratif et non-breaking.
9.3 Risque de régression sur les entity checks¶
Risque : La modification de _collect_zlint_entities() pourrait altérer le dict entities utilisé pour anchored_entities (les checks Anchor entity).
Mitigation : Le dict entities et le dict enum_states sont des structures séparées dans le retour de la fonction. La modification porte uniquement sur enum_states. Vérifier par diff que anchored.entities reste identique avant/après.
9.4 Ordre des faits Prolog et idempotence¶
Risque : Si l'ordre d'extraction des faits change entre runs (parcours de fichiers non déterministe), le multi-enum par entité pourrait produire des résultats différents.
Mitigation : extract-facts.py utilise déjà sorted() pour les clés. S'assurer que les valeurs enum sont également triées avant écriture.
9.5 Source de vérité deposit.status¶
Risque : La spec indique que l'existence de deposit.status est "à confirmer" (H-281-05). Si non confirmé au moment de l'implémentation, CA-08 reste en suspens.
Mitigation : Implémenter la logique conditionnelle dans le code. Le test TC-NOM-06/TC-NOM-07 couvrira les deux branches selon la source de vérité disponible.
9.6 Cohérence _z_enum_type_mappings entre normes¶
Attention : Une même entité peut avoir des mappings différents selon la norme. Par exemple anchor_batch est mappé BatchStatus dans pv-anchor mais pourrait nécessiter AnchorBatchStatus dans nf-z42-013 et iso-14641.
Action : Vérifier la cohérence des noms de types Z dans chaque .zed cible et ajuster les mappings par norme.
10. Hors périmètre¶
- Modification des entités TypeORM backend : aucune colonne base de données n'est ajoutée, modifiée ou supprimée
- Ajout de nouvelles normes au pipeline de vérification formelle
- Refonte du système de vérification formelle (architecture TLA+/Alloy/Prolog/Z)
- Modification du check Anchor entity (mapping entité → schéma Z) dans
z-notation-lint.py - Spécification des transitions métier entre valeurs d'état (ex: PENDING → FINALIZED). PD-281 ne définit que l'ancrage des types, pas les workflows.
- Modification de
z-notation-lint.py: le linter consomme les configs générées. La correction porte sur le générateur (extract-facts.py) et les specs (.zed), pas sur le linter.
11. Mécanismes cross-module¶
Aucune modification d'autres modules. PD-281 est entièrement contenu dans ProbatioVault-doc (scripts, specs .zed, configs générées). Aucun guard, middleware ou intercepteur n'est ajouté sur d'autres modules.
12. Périmètre de test¶
| Niveau de test | In scope | Hors scope (justification) |
|---|---|---|
| Unitaire | Vérification de la présence/absence des types Z dans les .zed (TC-NOM-05, TC-NOM-11, TC-NOM-12, TC-NEG-01 à TC-NEG-10) | — |
| Intégration | Exécution de extract-facts.py + vérification des _generated-z-lint.yaml (TC-NOM-01 à TC-NOM-04, TC-NOM-06, TC-NOM-07, TC-NOM-10, TC-NR-01, TC-NR-02) | — |
| E2E | Chaîne complète extract-facts.py → verify-norm.sh sur 9 normes (TC-NOM-08, TC-NOM-09, TC-NR-04, TC-ERR-03 à TC-ERR-07) | — |
Tous les niveaux de test sont couverts, aucune exclusion.
La couverture minimale de 80% s'applique à l'ensemble du périmètre. Les tests E2E sont le niveau principal (la chaîne extract-facts.py → verify-norm.sh est l'observable final pour la majorité des CAs).