PD-295 — Specification Review (cycle 3 v2)¶
Auditeur : Claude (revue contractuelle indépendante) Documents audités : PD-295-specification.md (cycle 3 v2), PD-295-tests.md (cycle 3 v2) Périmètre tests : ambiguïtés, contradictions, testabilité a priori, hypothèses dangereuses, risques sécu/conformité (pas de conclusion sur la couverture).
E-01¶
- Type : Contradiction
- Référence : spec §4.1 INV-295-11 vs §5.1 D-295-26 vs §5.14 vecteur V8
- Description : INV-295-11 fixe
count_configuredà 5/3/3 de manière rigide ("B5 fixecount_configuredà 5/3/3"). D-295-26 élargit l'énumération à "5/3/3 (ou 6 pour purge RGPD)", et le vecteur V8 utilisecount_configured=6pour un événementclarification_purgeden brique B2. L'invariant B5 est appliqué à un événement qui n'est pas B5, sans règle explicite couvrant le cas B2/purge. - Impact : Testeur ne peut pas déterminer si
count_configured=6est conforme ou une violation d'invariant. Implémenteur et reviewer auront des lectures divergentes. Vecteur V8 risque d'être invalidé par un contrôle strict INV-295-11. - Gravité : Majeur
E-02¶
- Type : Contradiction
- Référence : spec §4.1 INV-295-STATE-04 vs §5.9.2 vs INV-295-CL-05/06/15
- Description : INV-295-STATE-04 déclenche
purge-clarifications.py --storysuron_story_close(REJECTED, DONE_WITH_ANOMALY, DONE). Mais la machine à états §5.9.2 n'autorise queSUMMARY_INDEXED -> EXPIRED -> PURGED, conditionné pardate > retention_until. Purger une clarificationSUMMARY_INDEXEDnon expirée à la clôture de story = transitionSUMMARY_INDEXED -> PURGEDinterdite par INV-295-15 (ERR-295-STATE_TRANSITION_FORBIDDEN). - Impact : Flux
on_story_closeproduira soit une violation d'invariant d'état, soit une impossibilité de purger avant 540 jours. TC-NOM-20/22 sont testables mais contradictoires avec TC-NEG-07. - Gravité : Bloquant
E-03¶
- Type : Incohérence Spec↔Tests
- Référence : spec §8 (ST-295-01..28) vs tests.md
- Description : La spec §8 référence des scénarios
ST-295-01..28. Le document tests utilise exclusivementTC-NOM-*,TC-ERR-*,TC-NEG-*,TC-RUNTIME-*,TC-STATE-*,TC-META-*,TC-INV-*,TC-NR-*. Aucune table de correspondance ST → TC. - Impact : Traçabilité Spec↔Tests exigée par Gate 5 cassée. Un auditeur ne peut pas vérifier si ST-295-21..28 sont tous couverts.
- Gravité : Majeur
E-04¶
- Type : Hypothèse dangereuse
- Référence : spec §5.3 (B2 steps 2-3) + INV-295-RUNTIME-01
- Description : L'architecture impose un sous-subprocess
claude -pappelé depuisb2-sanitizer.py, lui-même déjà lancé depuis une session Claude Code active (step 0). L'env imposé se limite àCLAUDE_DISABLE_SESSION_LOG=1etTERM=dumb. La variableCLAUDECODEn'est pas désarmée alors qu'elle est connue pour faire échouerclaude -plancé depuis une session Claude Code existante (learning universel 2026-02-20, CLAUDE.md). Hypothèse implicite :claude -pfonctionne dans ce contexte sansunset CLAUDECODE. - Impact : Brique B2 échouera à l'exécution en conditions réelles. Tous les CA B2 et tests TC-RUNTIME-01/02/03 échoueront. Aucune détection possible avant implémentation.
- Gravité : Bloquant
E-05¶
- Type : Ambiguïté
- Référence : spec §5.12 + INV-295-BACKUP-01 + tests TC-NOM-21
- Description : La spec impose
tmutil addexclusion(macOS) ou.nobackupcomme mécanisme d'exclusion backup. §5.12 autorise "laptop dev OU serveur backend unique", impliquant Linux côté serveur.tmutiln'existe pas sur Linux. TC-NOM-21 utilisetmutil isexcluded. Aucun critère ne précise quel mécanisme s'applique selon OS, ni ce que fait le système sitmutilest indisponible. - Impact : CA-295-BACKUP-01 non testable en environnement serveur. INV-295-BACKUP-01 non vérifiable universellement.
- Gravité : Majeur
E-06¶
- Type : Ambiguïté
- Référence : spec §5.13 G-295-09 + §5.8 + §6
- Description : G-295-09 combine deux préconditions hétérogènes ("drift horloge + clé Vault disponible") en une seule règle avec deux codes d'erreur possibles. De plus,
timestamp_ntp_referencen'est défini nulle part (quel serveur NTP ? quel pool ? procédure offline ?). §5.8 mentionneclock_drift_max=500mssans préciser la source de référence. - Impact : Test TC-NEG-10 non reproductible : impossible de savoir contre quelle référence la dérive est mesurée. Un implémenteur peut choisir une source arbitraire, l'auditeur ne peut pas détecter la dérive.
- Gravité : Majeur
E-07¶
- Type : Contradiction
- Référence : spec §7 CA-295-02 vs §10 Q-295-05
- Description : CA-295-02 exige que
pii_ruleset_v1.yamlsoit "validé DPO par commit signé" comme condition d'acceptabilité. §10 Q-295-05 liste "Validation DPO des patterns PII pii_ruleset_v1" comme point à clarifier non résolu. - Impact : Le critère d'acceptation dépend d'un input non fourni. Gate 8 ne pourra pas être passée sans résolution préalable de Q-295-05.
- Gravité : Majeur
E-08¶
- Type : Ambiguïté
- Référence : spec §5.14 (vecteurs V1, V4, V6, V8) + §7 CA-295-05
- Description : CA-295-05 exige la vérification des "vecteurs V1/V4/V6/V8". Les numéros manquants (V2, V3, V5, V7) suggèrent l'existence d'autres vecteurs non fournis dans la spec, ou un choix arbitraire de numérotation. Aucune note explique la discontinuité.
- Impact : Un testeur ignore s'il manque 4 vecteurs clés ou si la numérotation est simplement lacunaire. Risque de croire à un corpus de tests incomplet.
- Gravité : Mineur
E-09¶
- Type : Hypothèse dangereuse
- Référence : spec INV-295-09 + §5.15
- Description : INV-295-09 affirme "clé immuable pour toute la session" et "rotation hors session". §5.15 mentionne
scripts/rotate-audit-hmac.shqui re-signe l'existant. Aucun invariant ou guard ne garantit qu'aucune session/govn'est active pendant l'exécution derotate-audit-hmac.sh(pas de lock exclusif entre le script de rotation et les sessions). - Impact : Fenêtre de course : session démarre avec clé v1, rotate re-signe en v2, session écrit une trace v1 dans un fichier déjà re-signé v2 → lignes incohérentes filtrées silencieusement à la lecture (INV-295-STATE-02). Perte d'audit invisible.
- Gravité : Majeur
E-10¶
- Type : Contradiction
- Référence : spec §5.6 B5 step 8 + step 9 + D-295-32 + INV-295-17
- Description : Step 8 : si écriture trace échoue et
fail_closed_depth < 3, incrémenter puis retournerEMPTY_BLOCK. Step 9 : nouvelle erreur qui impose d'incrémenter de 3 vers 4 → arrêt process. Comportement non spécifié quandfail_closed_depth == 3ET l'écriture trace échoue sans erreur imbriquée : la condition du step 8 est fausse, le step 9 pas déclenché. - Impact : Trou contractuel : l'implémenteur choisira arbitrairement entre arrêter, retourner
EMPTY_BLOCK, ou boucler. TC-NEG-17 ne couvre pas ce cas intermédiaire. - Gravité : Majeur
E-11¶
- Type : Contradiction
- Référence : spec §7 CA-295-04 vs §4.1 INV-295-04
- Description : INV-295-04 énumère exactement 6 artefacts purgés. CA-295-04 lit "Purge RGPD 6 artefacts + traces query + PD-XX-clarifications.md", ce qui laisse entendre 6 + 2 = 8 éléments, alors que les "traces query" et
clarifications.mdsont déjà deux des 6. - Impact : Confusion sur le périmètre exact de la purge. Auditeur RGPD peut conclure à un sous-ensemble manquant.
- Gravité : Mineur
E-12¶
- Type : Non testable
- Référence : spec INV-295-14 + §5.6 step 1 + §5.12
- Description : INV-295-14 restreint explicitement
rwlockà deux fichiers :learnings.jsonletlearnings-scores.jsonl. §5.6 step 1 de B5 dit "Acquisition locks lecturerwlock" sans préciser lesquels, alors que B5 lit aussiveille.jsonl,clarifications.jsonl, index FAISS, et écrit en session. §5.12 parle de "fichiers ciblés" sans liste exhaustive. - Impact : Impossible de tester TC-NOM-15 sans choix d'implémentation arbitraire sur le périmètre exact des fichiers lockés. Couverture concurrence B5 indéterminable.
- Gravité : Majeur
E-13¶
- Type : Incohérence Spec↔Tests
- Référence : spec INV-295-CL-04 vs tests matrice §2
- Description : La matrice déclare INV-295-CL-04 (
SUMMARY_VALIDATED -> SUMMARY_INDEXEDaprès écriture résumé) couvert par TC-NOM-14. TC-NOM-14 vérifie transitions CL-02/03 (validation PO), pas explicitement CL-04 (écriture du résumé indexé). - Impact : Transition
SUMMARY_VALIDATED -> SUMMARY_INDEXEDpotentiellement non observée en Gate 8. - Gravité : Mineur
E-14¶
- Type : Hypothèse dangereuse
- Référence : spec §5.12 "Non-répudiation" + INV-295-06..08
- Description : La spec reconnaît que HMAC symétrique ne fournit pas la non-répudiation forte ("administrateur avec accès Vault peut forger") et reporte la non-répudiation en candidat G33. Pour autant, les preuves d'audit PD-295 sont utilisées comme base de CA-295-05..08 et de la conformité RGPD (CA-295-04). Aucune mention de l'exposition résiduelle dans les hypothèses §9.
- Impact : Sous-estimation du risque juridique : preuves HMAC peuvent être contestées. Risque de non-conformité si audit externe exige preuves non-répudiables.
- Gravité : Majeur
E-15¶
- Type : Risque sécurité/conformité
- Référence : spec §5.3 B2 + INV-295-RUNTIME-02
- Description : INV-295-RUNTIME-02 interdit "tout fichier disque" pour le verbatim, mais autorise
os.pipe(). Le contenu transite via buffers kernel en mémoire, visible dans/proc/<pid>/fd/*tant que le pipe est ouvert. Aucune garantie de zéroisation mémoire après effacement des buffers (§5.3 step 9 mentionne "effacement" sans exigerzeroize()/overwrite explicite). - Impact : Résidus verbatim potentiels en mémoire process. TC-RUNTIME-01 ne couvre pas la mémoire process (seulement disque et argv). CA-295-RUNTIME-01 testable partiellement.
- Gravité : Majeur
E-16¶
- Type : Ambiguïté
- Référence : spec D-295-22 (enum
event_type) - Description : L'énumération
event_typeinclutinjection_failed_fail_closedqui n'apparaît nulle part dans les flux §5.2..5.6, ni dans les invariants, ni dans les CA, ni dans les tests. Valeur d'enum orpheline. - Impact : Un implémenteur peut émettre cet événement dans des conditions non spécifiées sans être détecté comme non conforme.
- Gravité : Mineur
E-17¶
- Type : Incohérence Spec↔Tests (cohérence diagramme)
- Référence : spec §5bis — Diagramme de séquence B5
- Description : Le diagramme de séquence B5 montre uniquement le chemin "fail-closed minimal" (guard → rwlock → key → signed event). Il n'explicite pas les transformations de données (construction du
payload_canonique, étape JCS, étape HMAC, exclusion du champ signature avant HMAC), alors que §5.14 exige JCS RFC 8785 + HMAC-SHA256 avec champ signature exclu. Implémenteur peut placer la canonicalisation au mauvais endroit, cassant la reproductibilité des vecteurs V1/V4/V6/V8. - Impact : Risque de divergence de signature non détectable avant TC-NOM-06. Axe 5bis (testabilité séquence).
- Gravité : Mineur
E-18¶
- Type : Incohérence Spec↔Tests (cohérence diagramme)
- Référence : spec §5bis — Diagramme d'état
learning.scopevs INV-295-LS-01 - Description : Le diagramme montre
STORY_ACTIVE --> DOMAIN_ACTIVE: seuil domainsans expliciter que la condition estreuse_score>=0.3 ET nb_domains>=1. INV-295-LS-01 ajoute "un learning jamais injecté (nb_domains=0) restescope:story", non représenté dans le diagramme (axe 5bis : transition documentée en texte absente du diagramme). TC-NOM-11 teste ce cas mais le diagramme le masque. - Impact : Gap visuel sur un cas fonctionnel testé. Lecture rapide trompeuse.
- Gravité : Mineur
E-19¶
- Type : Non testable
- Référence : spec §5.12 "Clearing: MEMORY_DEGRADED puis MEMORY_HEALTHY après 2 cycles conformes" + §11
- Description : Aucun invariant formel ne définit ce qu'est un "cycle conforme". Les états
MEMORY_DEGRADED/MEMORY_HEALTHYsont mentionnés dans §11 (observabilité) mais aucun CA ni test ne les couvre. Aucune règle de transition entre états mémoire (qui incrémente le compteur ? comment reset ?). - Impact : Observabilité non testable. Gate 8 ne peut pas vérifier le comportement du clearing.
- Gravité : Majeur
E-20¶
- Type : Hypothèse dangereuse
- Référence : spec §5.14 vecteur V6 + INV-295-09
- Description : Le vecteur V6 (rotation clé v1→v2) est signé avec
key_hex=aaaaaaaa...(nouvelle clé v2), pas avec l'ancienne clé v1. Sémantiquement, un événement "rotation v1→v2" devrait être signé par la clé sortante (v1) pour être vérifiable par un auditeur ne connaissant que v1. Aucun contrat n'explique cette convention. - Impact : Un auditeur lisant l'historique avec la clé archivée v1 ne pourra pas vérifier l'événement de rotation lui-même, cassant la chaîne d'audit exactement au point de changement de clé.
- Gravité : Majeur
Synthèse¶
| Gravité | Nombre | IDs |
|---|---|---|
| Bloquant | 2 | E-02, E-04 |
| Majeur | 11 | E-01, E-03, E-05, E-06, E-07, E-09, E-10, E-12, E-14, E-15, E-19, E-20 |
| Mineur | 7 | E-08, E-11, E-13, E-16, E-17, E-18 |
Les deux écarts bloquants : (1) machine à états clarifications vs hook on_story_close (transition interdite appelée sur clôture de story non expirée), (2) architecture B2 subprocess qui ignore le learning connu sur claude -p depuis une session Claude Code active (absence d'unset CLAUDECODE).