Aller au contenu

PD-295 — Tests (cycle 2 v3)

1. Références

  • Spécification : PD-295-specification.md (cycle 2 v3)
  • Epic : tooling
  • Scripts ciblés : b2-sanitizer.py, compute-learning-scores.py, promote-learnings.py, gov-learnings-inject, rotate-audit-hmac.sh, sign-existing-state.py, lib/write-signed-state.py, check-no-verbatim-persisted.sh, purge-backups.sh, lib/story-hooks.sh

2. Matrice de couverture

Invariant / CA Test(s)
INV-295-01, CA-295-02 TC-NOM-02, TC-ERR-14
INV-295-02 (code invariant) TC-INV-02
INV-295-03, CA-295-03 TC-NOM-03
INV-295-04..05, CA-295-04 TC-NOM-04, TC-NOM-05, TC-ERR-16
INV-295-06..08, CA-295-05 TC-NOM-06, TC-NOM-06-bis
INV-295-09, CA-295-06 TC-ERR-10, TC-ERR-11
INV-295-10, CA-295-07 TC-NOM-08, TC-ERR-13
INV-295-11, CA-295-08 TC-NOM-08, TC-NEG-15
INV-295-12, CA-295-14 TC-NOM-09
INV-295-13, CA-295-09 TC-NOM-07, TC-NOM-10
INV-295-14, CA-295-12 TC-NOM-15, TC-ERR-08, TC-ERR-09
INV-295-15, CA-295-11 TC-ERR-15, TC-NEG-06, TC-NEG-07
INV-295-16, CA-295-15 (meta) TC-NOM-17, TC-META-01..04, TC-ERR-17
INV-295-17 TC-NEG-17
INV-295-18, CA-295-25 TC-INT-03
INV-295-RUNTIME-01..03, CA-295-RUNTIME-01 TC-RUNTIME-01, TC-RUNTIME-02, TC-RUNTIME-03
INV-295-RUNTIME-04, CA-295-23 TC-NOM-24
INV-295-STATE-01..04, CA-295-STATE-01 TC-STATE-01, TC-STATE-02
INV-295-STATE-05 TC-STATE-03
INV-295-BACKUP-01, CA-295-BACKUP-01 TC-NOM-21
INV-295-HOOK-01, CA-295-21 TC-NOM-22
INV-295-LS-01..12, CA-295-10/11 TC-NOM-11, TC-NOM-12, TC-NOM-13, TC-ERR-15
INV-295-CL-01..08 TC-NOM-14, TC-NOM-03, TC-NOM-04, TC-NOM-05, TC-NEG-07
CA-295-01 TC-NOM-01
CA-295-13 TC-NOM-10
CA-295-16..19 (post-merge) TC-NOM-18-A/B/C/D
CA-295-20 TC-NOM-19
CA-295-22 TC-NOM-23
CA-295-24 TC-NOM-25

3. Scénarios nominaux

TEST-ID GIVEN WHEN THEN
TC-NOM-01 Corpus veille valide + invalide B1 collecte/indexe veille.jsonl valide, filtres corrects, invalides ignorés tracés
TC-NOM-02 4 réponses PO avec PII test B2 synthèse + validation PO=oui Résumé non-PII persistant, verbatim absent
TC-NOM-03 Résumé en attente PO=non REJECTED, aucune écriture
TC-NOM-04 Story avec clarifications indexées purge --story --force 5 artefacts + traces query supprimés, état PURGED
TC-NOM-05 Cas frontière retention_until purge rétention date > retention_until seul expire/purge
TC-NOM-06 Vecteurs V1/V4/V6/V8 vérification HMAC JCS signatures exactes
TC-NOM-06-bis Objet arbitraire schéma v3 HMAC(key, JCS(objet)) signature attendue 2afd2cd6dcedc730e1abc41203bf40a67764d69704e19cfd032691879d0a3ab0
TC-NOM-07 Jeux de données saturants calcul reuse_score aucune valeur 1.0000, écrêtage 0.9999 si pré-arrondi >=0.99995
TC-NOM-08 Corpus nominal 3 sources + clé/lock OK exécution B5 3 sections avec count_configured, count_effective, under_corpus corrects + trace signée avant retour
TC-NOM-09 Index clarifications multi-domain/project appel sans puis avec flags rejet sans flags, résultat cloisonné avec flags
TC-NOM-10 Scores disponibles /morning exactement 3 lignes score=X.XXXX, tri stable
TC-NOM-11 Seuils promotion atteints promote B4 story->domain, domain->global
TC-NOM-12 learnings stale et témoins archive B4 stale -> ARCHIVED, témoins inchangés
TC-NOM-13 learning ARCHIVED restore manuel audité retour STORY_ACTIVE uniquement
TC-NOM-14 clarification VERBATIM_IN_MEMORY 2 runs (PO oui/non) transitions CL exactes
TC-NOM-15 concurrence + lock stale + lock occupé B3/B4/B5 en parallèle stale récupéré 1 fois, timeout >30s => erreur
TC-NOM-16 fenêtre 5 min + idempotence 4e requête + replay identique rate-limit rejeté, replay identique renvoie même résultat
TC-NOM-17 dépôt Gate 8 contrôle méta scripts CS 4 scripts présents + exécutables
TC-NOM-18-A T0+30/60/90 measure-cs1.sh 3 rapports horodatés
TC-NOM-18-B historique 90j measure-cs2.sh mesure + comparaison cible >=30%
TC-NOM-18-C fenêtre glissante 30j measure-cs3.sh fréquence + comparaison cible
TC-NOM-18-D 5 premières stories post-B5 measure-cs4.sh ratio + comparaison cible
TC-NOM-19 baseline SQL connue inspection livrables PD-295 aucun artefact DDL SQL
TC-NOM-20 story close REJECTED/DONE_WITH_ANOMALY hook on_story_close purge clarifications déclenchée
TC-NOM-21 exclusion backup configurée check exclusion + purge backup historique artefacts PD-295 exclus, purge-backups.sh effectif
TC-NOM-22 story close REJECTED/DONE_WITH_ANOMALY/DONE appel répété on_story_close purge appelée pour 3 états, comportement idempotent
TC-NOM-23 session /gov démarrée clé version N Vault roté/revenu pendant session session conserve N jusqu’au redémarrage; N+1 seulement session suivante
TC-NOM-24 repo de conformité vérification config/pii_ruleset_v1.yaml + historique git fichier présent + commit signé taggé dpo-approved
TC-NOM-25 bloc {{LEARNINGS}} généré validation regex ligne par ligne format exact conforme §5.9

4. Cas d’erreur

TEST-ID Référence Attendu
TC-ERR-01 ERR-295-INVALID_STORY_ID rejet immédiat, pas d’effet de bord
TC-ERR-02 ERR-295-INVALID_DOMAIN rejet immédiat
TC-ERR-03 ERR-295-INVALID_PROJECT rejet immédiat
TC-ERR-04 ERR-295-INVALID_QUERY_LENGTH rejet, pas de recherche aval
TC-ERR-05 ERR-295-INVALID_QUERY_HASH rejet trace invalide
TC-ERR-06 ERR-295-RATE_LIMIT_EXCEEDED 4e requête rejetée
TC-ERR-07 ERR-295-IDEMPOTENCY_CONFLICT rejet, état inchangé
TC-ERR-08 ERR-295-LOCK_TIMEOUT fail-closed sans écriture partielle
TC-ERR-09 ERR-295-LOCK_STALE_RECOVERY_FAILED fail-closed
TC-ERR-10 ERR-295-AUDIT_KEY_UNAVAILABLE arrêt /gov au démarrage de session
TC-ERR-11 ERR-295-AUDIT_KEY_FORMAT_INVALID SystemExit(1); /gov-step-0 ne reçoit aucun retour
TC-ERR-12 ERR-295-HMAC_VERIFICATION_FAILED trace rejetée + alerte
TC-ERR-13 ERR-295-TRACE_WRITE_FAILED EMPTY_BLOCK, step0 continue
TC-ERR-14 ERR-295-PII_DETECTED rejet résumé, aucune persistance
TC-ERR-15 ERR-295-STATE_TRANSITION_FORBIDDEN état inchangé
TC-ERR-16 ERR-295-PURGE_VERIFICATION_FAILED clôture purge rejetée + alerte
TC-ERR-17 ERR-295-MEASURE_SCRIPT_MISSING Gate 8 NON_CONFORME
TC-ERR-18 ERR-295-UNSIGNED_ENTRY compteur incrémenté, ligne ignorée, exécution continue
TC-ERR-19 ERR-295-FAIL_CLOSED_RECURSION arrêt process + alerte sécurité

5. Tests runtime B2 (subprocess verbatim)

TEST-ID: TC-RUNTIME-01
Référence: INV-295-RUNTIME-01, INV-295-RUNTIME-02, CA-295-RUNTIME-01

GIVEN
  - `scripts/b2-sanitizer.py` actif
  - verbatim fake contenant UUIDv4 unique
WHEN
  - B2 est exécuté jusqu'à production du résumé
THEN
  - `grep -R <UUIDv4> data/` retourne 0 occurrence
  - aucun argument process (`ps`) ne contient l’UUID
  - aucun fichier temporaire disque n’est créé
TEST-ID: TC-RUNTIME-02
Référence: INV-295-RUNTIME-01

GIVEN
  - appel subprocess `claude -p` via wrapper B2
WHEN
  - exécution avec `CLAUDE_DISABLE_SESSION_LOG=1`
THEN
  - aucun nouvel enregistrement verbatim dans `data/sessions/*.jsonl`
TEST-ID: TC-RUNTIME-03
Référence: INV-295-RUNTIME-03

GIVEN
  - verbatim fake avec termes sentinelles
WHEN
  - subprocess B2 est invoqué
THEN
  - événement signé `verbatim_subprocess_invoked` présent
  - payload contient `{story_id,subprocess_name,duration_ms,success}`
  - regex négative sur payload: aucune occurrence des sentinelles verbatim

6. Tests signature d’état

TEST-ID: TC-STATE-01
Référence: INV-295-STATE-01..03, CA-295-STATE-01

GIVEN
  - ligne manuelle non signée injectée:
    echo '{"story":"PD-ATTACK","nb_injections":9999}' >> data/learnings-injections.jsonl
WHEN
  - `scripts/compute-learning-scores.py` est exécuté
THEN
  - ligne malveillante ignorée
  - score de `PD-ATTACK` reste 0.0 / absent
  - compteur `ERR-295-UNSIGNED_ENTRY` incrémenté
  - événement signé `unsigned_entry_detected` émis
TEST-ID: TC-STATE-02
Référence: INV-295-STATE-04

GIVEN
  - fichiers d’état signés avec clé version N
WHEN
  - `scripts/rotate-audit-hmac.sh` est exécuté
THEN
  - lignes existantes re-signées version N+1
  - vérification avec nouvelle clé OK
  - vérification historique possible avec clé archivée N
TEST-ID: TC-STATE-03
Référence: INV-295-STATE-05

GIVEN
  - 2 writers parallèles sur `data/sessions/current.jsonl` et `data/learnings-scores.jsonl`
WHEN
  - append via `write-signed-state.py`
THEN
  - pas de lignes JSON entrelacées/corrompues
  - write-lock présent sur chaque append
  - lecture concurrente sans lock reste valide
  - payload >4096 bytes déclenche warning + append sous lock strict

7. Tests méta Gate 8

TEST-ID: TC-META-01
WHEN test -x scripts/measure-cs1.sh
THEN exit code 0
TEST-ID: TC-META-02
WHEN test -x scripts/measure-cs2.sh
THEN exit code 0
TEST-ID: TC-META-03
WHEN test -x scripts/measure-cs3.sh
THEN exit code 0
TEST-ID: TC-META-04
WHEN test -x scripts/measure-cs4.sh
THEN exit code 0

8. Tests d’invariants

Invariant Test dédié Observable
INV-295-02 TC-INV-02 lint statique passe
INV-295-17 TC-NEG-17 profondeur bornée et abort à 4
INV-295-18 TC-INT-03 transition healthy validée
INV-295-BACKUP-01 TC-NOM-21 exclusions actives
INV-295-RUNTIME-* TC-RUNTIME-01..03 aucune fuite verbatim
INV-295-STATE-* TC-STATE-01..03 lignes non signées neutralisées + writes sérialisés
INV-295-RUNTIME-04 TC-NOM-24 ruleset DPO validé
TEST-ID: TC-INV-02
Référence: INV-295-02

GIVEN
  - code source B2
WHEN
  - `scripts/check-no-verbatim-persisted.sh` est exécuté
THEN
  - aucune écriture disque liée au verbatim PO n’est détectée
  - toute violation produit exit code non nul

9. Tests négatifs et adversariaux

TEST-ID Entrée abusive Attendu
TC-NEG-01 learning_id invalide dans result_ids[] trace rejetée
TC-NEG-02 source hors enum trace rejetée
TC-NEG-03 count non numérique/>999 trace rejetée
TC-NEG-04 timestamp non UTC conforme trace rejetée
TC-NEG-05 reuse_score ⅗ décimales rejet format
TC-NEG-06 transition STORY_ACTIVE->GLOBAL_ACTIVE ERR-295-STATE_TRANSITION_FORBIDDEN
TC-NEG-07 sortie de REJECTED/PURGED ERR-295-STATE_TRANSITION_FORBIDDEN
TC-NEG-08 résumé avec PII ERR-295-PII_DETECTED
TC-NEG-09 query 0 et 501 chars ERR-295-INVALID_QUERY_LENGTH
TC-NEG-10 dérive horloge >500ms au trace fail-closed
TC-NEG-11 idempotence divergente <5min ERR-295-IDEMPOTENCY_CONFLICT
TC-NEG-12 4e requête <5min ERR-295-RATE_LIMIT_EXCEEDED
TC-NEG-13 frontière stale lock mtime=60s non stale (strict >60)
TC-NEG-14 key_version=0 trace rejetée
TC-NEG-15 corpus vide sur 3 sources B5 count_effective=0, under_corpus=true, pas de non-conformité
TC-NEG-16 bypass guard (10 cas G-295-01..10) chaque tentative bloque fail-closed
TC-NEG-17 chaîne fail-closed recursive depth=3 accepté, depth=4 => ERR-295-FAIL_CLOSED_RECURSION + arrêt process

10. Tests d’intégration

TEST-ID: TC-INT-03
Référence: INV-295-18, CA-295-25

GIVEN
  - cycle 1: un échec provoque `MEMORY_DEGRADED`
  - cycles 2 et 3: exécutions conformes
WHEN
  - troisième cycle terminé
THEN
  - transition `MEMORY_DEGRADED -> MEMORY_HEALTHY` observée
  - aucun basculement prématuré après un seul cycle conforme

11. Non-régression

Test ID Objet Attendu
TC-NR-01 B1 veille index/recherche existants non cassés
TC-NR-02 step0 sans mémoire dispo continue sans crash
TC-NR-03 interdiction mode dégradé non tracé aucun bloc non vide sans trace
TC-NR-04 intégrité learnings.jsonl B3 n’altère pas source
TC-NR-05 exclusion archived pas de sortie recherche active
TC-NR-06 guards scope limité seulement commandes listées
TC-NR-07 compatibilité stack aucun composant hors scope
TC-NR-08 absence DDL SQL confirmé

12. Observabilité minimale requise

  • États: scope, lifecycle_state, MEMORY_DEGRADED, MEMORY_HEALTHY, fail_closed_depth.
  • Logs: événements signés schéma v3 (schema_version, event_type, brick, key_version, sig_hmac_sha256).
  • Compteurs: ERR-295-UNSIGNED_ENTRY.
  • Preuves: rapports purge, rapports scripts CS post-merge, résultat test -x méta Gate 8.
  • Vérifications regex anti-fuite verbatim sur data/ et data/sessions/*.jsonl.

13. Verdict QA

  • ✅ Testable (instrumentation CI standard + scripts contractuels fournis)
  • Réserve mineure: campagnes CA-295-16..19 différées par calendrier post-merge (normal, non bloquant Gate 8)

Changelog cycle2-v2 → cycle2-v3

  • TC-NEG-17 testé avec depth=3 (accepté) et depth=4 (abort)
  • TC-NOM-06 utilise les nouveaux vecteurs HMAC V1/V4/V6/V8
  • TC-ERR-11 teste l'arrêt processus /gov (pas juste un code retour)
  • TC-NOM-22 : hook on_story_close appelé sur REJECTED/DONE_WITH_ANOMALY/DONE
  • TC-NOM-23 : clé Vault stable pendant toute la session
  • TC-NOM-24 : pii_ruleset_v1 existe + commit dpo-approved
  • TC-NOM-25 : regex sur template YAML §5.9
  • TC-INT-03 : transition MEMORY_DEGRADED → MEMORY_HEALTHY end-to-end