Aller au contenu

PD-295 — Spécification v3

1. Objectif

La User Story PD-295 DOIT transformer la bibliothèque documentaire existante en mémoire vivante injectée automatiquement au step 0, en ajoutant cinq briques contractuelles :

  • B1 : index sémantique des fiches de veille.
  • B2 : persistance et index des clarifications PO step 0.
  • B3 : scoring de réutilisation des learnings.
  • B4 : promotion de portée et éviction stale des learnings.
  • B5 : injection unifiée learnings + veille + clarifications.

La story DOIT préserver les mécanismes existants sans régression.

2. Périmètre / Hors périmètre

Inclus

  • Ajout des artefacts B1..B5 (JSONL, FAISS, NPY, scripts, commandes).
  • Injection step 0 des trois sources avec traces systématiques.
  • Ventilation compounding par source.
  • Migration fonctionnelle learnings.jsonl avec scope.
  • Promotion/éviction selon règles explicites.
  • Réindexation globale avec phases veille + clarifications.
  • Signature/horodatage des événements via clé HMAC Vault versionnée.
  • Script contractuel scripts/rotate-audit-hmac.sh (rotation manuelle clé audit).
  • Baseline CS-1 via scripts/measure-cs1-baseline.py.
  • KPI CS-2/CS-3/CS-4 via scripts/measure-cs2.sh, scripts/measure-cs3.sh, scripts/measure-cs4.sh.
  • /morning obligatoire avec top 3 réutilisés.
  • Rétention clarifications 18 mois avec purge déclenchée par step 10, hook on_story_close, et cron quotidien.

Exclu

  • Migration de stack hors FAISS + Ollama + Markdown/YAML/JSONL.
  • Remplacement des skills existants.
  • Nouveau moteur de stockage (Elasticsearch, pgvector, SQLite, graph DB).
  • Reranker neural, BM25 hybride, classifier d’intention, skill router auto, boucle adaptative.
  • Refonte specs-index/**/*.yaml.
  • Architecture L1/L2/L3/L4.
  • Attribution causale mono-facteur “injection => GO Gate 8” sans protocole statistique dédié.

3. Définitions

Terme Définition
Mémoire vivante Injection proactive et traçable de connaissances passées au step 0.
Learning Leçon issue des gates/REX, stockée dans learnings.jsonl.
Scope learning Portée d’un learning : story, domain, global.
Clarification PO Réponse verbatim aux 4 questions step 0.
Veille Fiche documentaire externe stockée dans docs/veille/....
Injection Ajout de contexte mémoire dans le prompt step 0.
tag_hash sha256(",".join(sorted(tags)))[:12].
learning_id Identifiant stable : {story}-{gate}-{tag_hash}.
nb_domains len(set(domain des events learning_injected pour un learning_id)) (peut valoir 0).
reuse_score_brut 0.4*nb_injections + 0.4*nb_stories_gate8_go_apres_injection + 0.2*nb_domains.
reuse_score tanh(reuse_score_brut/10) borné dans [0,1), sérialisé à 6 décimales.
Clé idempotence {story_id, step, operation, timestamp_bucket_5min} (sans query_hash).
Conflit idempotence Même clé idempotence + query_hash différent => ERR-295-10.
Fenêtre rate-limit 5 minutes glissantes (non bucketisées).
Source d’injection learning, veille, clarification.
key_version Version Vault de la clé audit utilisée pour HMAC d’un événement.
Réconciliation Contrôle périodique cohérence JSONL/FAISS/traces.

4. Invariants (non négociables)

ID Règle Justification
INV-295-01 Stack strictement FAISS 1.x + Ollama + Markdown/YAML/JSONL + scripts Python/Shell. Zéro migration d’outil.
INV-295-02 Les 4 skills de recherche existants et gov-compounder restent non-régressifs. Compatibilité ascendante.
INV-295-03 B1 produit un corpus veille interrogeable avec filtres impact_pv et verdict. Activation veille.
INV-295-04 Clarifications step 0 persistées dans tous les modes (local + Ringbearer) avant reprise rédaction besoin. Pas de perte de connaissance.
INV-295-05 Clarifications persistées en verbatim strict (pas de synthèse). Fidélité probatoire.
INV-295-06 B3 écrit data/learnings-scores.jsonl en parallèle sans mutation de data/learnings.jsonl. Immutabilité historique.
INV-295-07 Formule contractuelle reuse_score_brut/reuse_score avec nb_domains défini §3 ; reuse_score borné [0,1). Reproductibilité.
INV-295-08 Toute entrée learnings.jsonl possède scope in {story,domain,global} après migration initiale. Filtrage testable.
INV-295-09 Promotion : story->domain si reuse_score>=0.3 && nb_domains>=1 ; domain->global si reuse_score>=0.6 && nb_domains>=2. Montée de portée.
INV-295-10 Éviction : scope=story avec nb_injections==0 et âge >56 jours vers archive. Nettoyage stale explicite.
INV-295-11 Toute transition d’état non listée §5.4 est interdite. FSM fermée.
INV-295-12 B5 injecte 3 sections distinctes avec cardinalités fixes : learnings=5, veille=3, clarifications=3 ; si count=0, section présente avec “aucun résultat”. Lisibilité/traçabilité.
INV-295-13 L’injection step 0 trace chaque source avec story, step, domain, project, operation. Mesure fiable.
INV-295-14 search-clarifications exige --domain et --project. Pertinence clarifications.
INV-295-15 reindex-all inclut veille + clarifications après contracts/specs/plans. Couverture index.
INV-295-16 Toute migration in-place effective de learnings.jsonl exige un commit git préalable. Rollback atomique.
INV-295-17 analyze-compounding.py expose les métriques nécessaires à CS-½/¾ ; baseline CS-1 gelée via measure-cs1-baseline.py. Pilotage empirique.
INV-295-18 Concurrence obligatoire via fcntl.flock sur deux verrous : data/learnings.jsonl.lock et data/learnings-scores.jsonl.lock. B3: SH sur learnings + EX sur scores. B4: EX sur les deux. B5/search: SH sur les deux. Timeout 30s, fail-closed ERR-295-09, ordre d’acquisition fixe (learnings puis scores). Robustesse multi-exécution.
INV-295-19 Jointure learnings.jsonllearnings-scores.jsonl par {story,gate,tag_hash} ; score absent => fallback reuse_score=0.0. Tolérance trous.
INV-295-20 Tous les événements audit B1..B5 et restore-learning.py sont horodatés et signés HMAC avec clé lue depuis Vault (kv/pd-295/memory-audit-hmac), incluent key_version, et sont vérifiables a posteriori y compris version archivée. Vault indisponible au démarrage => fail-closed ERR-295-16. Auditabilité probatoire.
INV-295-21 Politique clarifications : rétention 18 mois, pas d’anonymisation ; purge via scripts/purge-clarifications.py déclenchée par (1) step 10, (2) hook on_story_close pour REJECTED/DONE_WITH_ANOMALY, (3) cron quotidien. Gouvernance rétention complète.
INV-295-22 /morning publie exactement 3 learnings top reuse_score; succès observable par code retour 0. Si impossible, échec explicite ERR-295-19. Exigence opérationnelle testable.
INV-295-23 Dans toute trace d’injection : count == len(result_ids). Sinon événement ignoré + journal audit_inconsistent_trace. Cohérence trace/résultats.

5. Flux nominaux

5.1 Modèle de données canonique (source unique)

5.1.1 Champs partagés

Donnée Validation Comportement si invalide
story_id ^PD-[0-9]{1,4}$ Rejet (ERR-295-01)
domain ^[a-z0-9]+(?:-[a-z0-9]+)*$ Rejet (ERR-295-01)
project ^(backend|app|site|infra|doc|formal|pixel-governance|ia-governance)$ Rejet (ERR-295-01)
date YYYY-MM-DD Rejet (ERR-295-02)
tag_hash ^[a-f0-9]{12}$ Ligne exclue + log
learning_id ^PD-[0-9]{1,4}-(?:[0-9]|10)-[a-f0-9]{12}$ Ligne exclue + log
schema_version entier >=1 Rejet entrée

5.1.2 data/veille.jsonl

Donnée Validation Comportement si invalide
slug ^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9-]+$ Ligne exclue + log
date YYYY-MM-DD Ligne exclue + log
title non vide Ligne exclue + log
tags[] ^[a-z0-9-]{1,50}$ Ligne exclue + log
verdict signal|bruit|veille Ligne exclue + log
impact_pv fort|modere|faible Ligne exclue + log
summary non vide Ligne exclue + log
source_path chemin relatif veille .md, sans traversal Ligne exclue + log

5.1.3 PD-XX-clarifications.md

Donnée Validation Comportement si invalide
frontmatter.story story_id valide Fichier ignoré + log
frontmatter.step 0 Fichier ignoré + log
frontmatter.date date ISO Fichier ignoré + log
frontmatter.domain domain valide Fichier ignoré + log
frontmatter.project project valide Fichier ignoré + log
Q1..Q4 verbatim non vide Fichier ignoré + log

5.1.4 data/learnings-scores.jsonl

Donnée Validation Comportement si invalide
learning_id canonique Ligne exclue + log
story story_id valide Ligne exclue + log
gate 0..10 Ligne exclue + log
tag_hash 12 hex Ligne exclue + log
nb_injections entier >=0 Ligne exclue + log
nb_stories_gate8_go_apres_injection entier >=0 Ligne exclue + log
nb_domains entier >=0 Ligne exclue + log
reuse_score_brut décimal >=0 Ligne exclue + log
reuse_score décimal [0,1) Ligne exclue + log

Jointure officielle avec learnings.jsonl : {story, gate, tag_hash}.
Score absent : reuse_score=0.0, reuse_score_brut=0.0.

5.1.5 data/learnings.jsonl (étendu)

Donnée Validation Comportement si invalide
story story_id valide Ligne exclue + log
gate 0..10 Ligne exclue + log
verdict GO|RESERVE|NON_CONFORME|ESCALADE Ligne exclue + log
tags[] ^#[a-z0-9-]{1,50}$ Ligne exclue + log
learning non vide Ligne exclue + log
scope story|domain|global Rejet migration/promotion
date date ISO Ligne exclue + log

Règle d’intégrité : les tags d’un learning historique sont immuables.
Si dérive détectée (learning_id recalculé différent), ligne exclue du scoring et log learning_id_drift.

5.1.6 data/learnings-injections.jsonl (trace unifiée signée)

Format événement obligatoire :

{
  "event": "learning_injected",
  "timestamp_iso8601": "2026-04-11T09:42:17Z",
  "story_id": "PD-295",
  "step": 0,
  "source": "learning",
  "domain": "auth-identity",
  "project": "backend",
  "operation": "step0_injection",
  "timestamp_bucket": "2026-04-11T09:40:00Z",
  "query": "mfa b2b priorites",
  "query_hash": "sha256:...",
  "count": 5,
  "result_ids": ["PD-19-3-a1b2c3d4e5f6"],
  "key_version": 12,
  "signature_hmac_sha256": "hex...",
  "schema_version": 3
}

Règles contractuelles :

  • Clé idempotence : {story_id, step, operation, timestamp_bucket}.
  • timestamp_bucket : arrondi 5 minutes UTC.
  • query.length <= 500 caractères, sinon ERR-295-17 QUERY_TOO_LONG.
  • count == len(result_ids), sinon événement ignoré + log audit_inconsistent_trace.
  • Signature : hmac_sha256(payload_canonique, secret_key).
  • Clé HMAC :
  • secret_key = vault_read("kv/pd-295/memory-audit-hmac").data.key
  • clé binaire 32 octets (générée au déploiement via openssl rand -hex 32)
  • key_version de Vault est sérialisée dans chaque événement
  • Rotation manuelle :
  • opérateur exécute scripts/rotate-audit-hmac.sh
  • nouvelle clé écrite dans kv/pd-295/memory-audit-hmac
  • ancienne clé archivée dans kv/pd-295/memory-audit-hmac/archive/{date}
  • événement audit_key_rotated signé émis avec nouvelle key_version
  • Vérification a posteriori :
  • vérificateur lit key_version
  • version active : lecture chemin principal
  • version historique : lecture archive correspondante
  • Vault indisponible au démarrage :
  • fail-closed
  • aucun événement audit émis
  • ERR-295-16 AUDIT_KEY_UNAVAILABLE
  • skill /gov interrompu
  • Bootstrap initial clé : opération humaine documentée, pas d’auto-bootstrap.

5.2 Bornes numériques obligatoires

Paramètre Défaut Min Max Unité Comportement hors bornes
embedding_dimension 768 768 768 dimensions Rejet
top_k_learnings_step0 5 5 5 éléments Rejet
top_k_veille_step0 3 3 3 éléments Rejet
top_k_clarifications_step0 3 3 3 éléments Rejet
min_score_default 0.00 0.00 0.99 score Rejet argument
score_weight_injections 0.40 0.40 0.40 ratio Rejet config
score_weight_gate8_go 0.40 0.40 0.40 ratio Rejet config
score_weight_domains 0.20 0.20 0.20 ratio Rejet config
score_tanh_divisor 10 10 10 brut Rejet config
promotion_threshold_domain 0.30 0.30 0.30 score Rejet config
promotion_threshold_global 0.60 0.60 0.60 score Rejet config
stale_evict_age 56 56 56 jours Rejet config
po_question_timeout 3600 60 86400 secondes ERR-295-18
parallel_sources_count 3 3 3 sources Rejet
morning_top_reused 3 3 3 éléments ERR-295-19
query_max_chars 500 1 500 caractères ERR-295-17
lock_acquire_timeout 30 5 120 secondes ERR-295-09
lock_stale_file_age 60 60 60 secondes Rejet config
idempotence_window 24 1 168 heures Rejet config
timestamp_bucket_size 5 5 5 minutes Rejet config
rate_limit_step0 3 1 10 requêtes / 5 min glissantes / story ERR-295-11
reconciliation_interval 5 1 60 minutes Rejet config
orphan_threshold 15 5 120 minutes MEMORY_DEGRADED
clearing_cycles_required 2 1 10 cycles Rejet config
clarifications_retention_months 18 18 18 mois Rejet config

5.3 Clarifications JSONL + SLA

5.3.1 Schéma data/clarifications.jsonl

{
  "story": "PD-42",
  "domain": "auth-identity",
  "project": "backend",
  "date": "2026-03-15",
  "path": "docs/epics/auth-identity/PD-42-mfa/PD-42-clarifications.md",
  "questions": [
    {"id": "Q1", "theme": "probleme", "answer": "..."},
    {"id": "Q2", "theme": "criteres", "answer": "..."},
    {"id": "Q3", "theme": "contraintes", "answer": "..."},
    {"id": "Q4", "theme": "priorites", "answer": "..."}
  ],
  "retention_until": "2027-09-15"
}
Champ Type Contrainte
story string ^PD-[0-9]{1,4}$
domain string ^[a-z0-9]+(?:-[a-z0-9]+)*$
project string enum projet valide
date string ISO YYYY-MM-DD
path string chemin relatif .md existant
questions array taille = 4
questions[].id string Q1..Q4
questions[].theme string probleme|criteres|contraintes|priorites
questions[].answer string non vide, verbatim
retention_until string ISO YYYY-MM-DD, exactement date + 18 mois

Fichier invalide : ligne exclue + log.

5.3.2 SLA temporels

Paramètre Valeur défaut Min Max Comportement à expiration
po_question_timeout 3600s 60s 86400s ERR-295-18
stale_evict_age 56 jours 56 56 Archivage
idempotence_window 24h 1h 168h Nouvelle tentative
lock_acquire_timeout 30s 5s 120s ERR-295-09
lock_stale_file_age 60s 60s 60s cleanup lockfile orphelin
orphan_threshold 15 min 5 120 MEMORY_DEGRADED
clarifications_retention_months 18 mois 18 18 purge automatique multi-déclencheurs

5.4 Machine à états des learnings (transitions + retours)

États : STORY_ACTIVE, DOMAIN_ACTIVE, GLOBAL_ACTIVE, ARCHIVED.

STORY_ACTIVE : - STORY_ACTIVE -> DOMAIN_ACTIVE : AUTORISÉE (reuse_score>=0.30 && nb_domains>=1). - STORY_ACTIVE -> GLOBAL_ACTIVE : INTERDITE. - STORY_ACTIVE -> ARCHIVED : AUTORISÉE (nb_injections==0 && age>56 jours).

DOMAIN_ACTIVE : - DOMAIN_ACTIVE -> GLOBAL_ACTIVE : AUTORISÉE (reuse_score>=0.60 && nb_domains>=2). - DOMAIN_ACTIVE -> STORY_ACTIVE : INTERDITE (downgrade auto). - DOMAIN_ACTIVE -> ARCHIVED : INTERDITE dans PD-295 (choix de périmètre explicite).

GLOBAL_ACTIVE : - GLOBAL_ACTIVE -> DOMAIN_ACTIVE : INTERDITE. - GLOBAL_ACTIVE -> STORY_ACTIVE : INTERDITE. - GLOBAL_ACTIVE -> ARCHIVED : INTERDITE dans PD-295 (choix de périmètre explicite).

ARCHIVED : - ARCHIVED -> STORY_ACTIVE : AUTORISÉE uniquement manuellement via scripts/restore-learning.py --id <learning_id>. - ARCHIVED -> DOMAIN_ACTIVE : INTERDITE. - ARCHIVED -> GLOBAL_ACTIVE : INTERDITE.

Règles :

  • Aucun downgrade automatique.
  • restore-learning.py émet un événement signé avec la même politique Vault (INV-295-20), incluant key_version.

5.5 Flux nominal N1 — B1 Index veille

  1. Collecter les fiches veille et produire data/veille.jsonl.
  2. Indexer embeddings 768 + FAISS.
  3. Exposer recherche avec --impact, --verdict.
  4. Tracer les injections veille (source=veille) avec signature Vault.
  5. Intégrer la phase veille dans reindex-all.

5.6 Flux nominal N2 — B2 Clarifications step 0

  1. Poser les 4 questions PO step 0 (tous modes).
  2. Persister PD-XX-clarifications.md verbatim avant rédaction besoin.
  3. Collecter en data/clarifications.jsonl conforme §5.3.1.
  4. Indexer corpus clarifications.
  5. Rechercher avec --domain et --project obligatoires.
  6. Tracer source=clarification signé.
  7. Purger >18 mois via :
  8. step 10 gov-compounder,
  9. hook on_story_close (REJECTED, DONE_WITH_ANOMALY),
  10. cron quotidien de sécurité.

5.7 Flux nominal N3 — B3 Scoring de réutilisation

  1. Acquérir locks (ordre fixe) : SH learnings.jsonl.lock, puis EX learnings-scores.jsonl.lock.
  2. Lire learnings.jsonl, learnings-injections.jsonl, metrics.jsonl.
  3. Calculer reuse_score_brut, puis reuse_score=tanh(reuse_score_brut/10).
  4. Écrire data/learnings-scores.jsonl (parallèle).
  5. Trier search-learnings par distance FAISS puis reuse_score secondaire.
  6. --min-score optionnel (<=0.99).
  7. /morning publie 3 top scores ; succès observable code retour 0, sinon ERR-295-19.
  8. Geler baseline CS-1 via scripts/measure-cs1-baseline.py à la date merge B5.
  9. Libérer les deux locks.

5.8 Flux nominal N4 — B4 Promotion + éviction

  1. Détecter si migration scope est nécessaire (au moins une ligne sans scope).
  2. Si migration nécessaire, vérifier précondition commit git (INV-295-16), sinon passer.
  3. Acquérir EX lock sur learnings.jsonl.lock puis learnings-scores.jsonl.lock.
  4. Migrer scope: story uniquement pour lignes sans scope.
  5. Promouvoir selon seuils.
  6. Archiver stale (scope=story, 0 injection, >56 jours).
  7. Exclure archivés de l’index actif à la réindexation suivante.
  8. Libérer locks ; journaliser événement signé avec key_version.

5.9 Flux nominal N5 — B5 Injection unifiée step 0

  1. Construire requêtes depuis {story_id,title,domain,project}.
  2. Acquérir SH lock sur learnings.jsonl.lock puis learnings-scores.jsonl.lock.
  3. Évaluer d’abord le rate-limit (fenêtre glissante) ; dépassement => ERR-295-11.
  4. Évaluer ensuite idempotence bucket 5 min.
  5. Lancer en parallèle :
  6. search-learnings ... --top-k 5 --trace
  7. search-veille ... --top-k 3 --trace
  8. search-clarifications.py --domain {story.domain} --project {story.project} --top-k 3 --trace
  9. Assembler bloc Markdown à 3 sections distinctes.
  10. Appliquer cardinalités fixes 5/3/3 ; si count=0 section présente avec “aucun résultat”.
  11. Tracer chaque source (événements signés, key_version).
  12. 1 ou 2 sources indisponibles : injection partielle + état dégradé.
  13. 3 sources indisponibles : blocage step 0 (ERR-295-06).
  14. Libérer les deux locks.

5.10 Stratégie de migration DDL

Aucune migration DDL SQL applicable.

5.11 Atomicité multi-composant

Aucun flux DB+queue introduit ; non applicable.

5.12 Mécanismes de protection distribuée

Mécanisme Spécification contractuelle
RWLock Deux lockfiles : data/learnings.jsonl.lock, data/learnings-scores.jsonl.lock. Ordre obligatoire d’acquisition : learnings puis scores. Timeout 30s -> ERR-295-09.
Reprise crash lock Lockfile orphelin mtime>60s : suppression puis nouvelle tentative.
Rate-limit (prioritaire) 3 req / 5 min glissantes / story évalué avant idempotence ; dépassement -> ERR-295-11.
Idempotence Clé {story_id,step,operation,timestamp_bucket} ; rejeu identique => même résultat logique sans doublon.
Conflit payload Même clé idempotence + query_hash différent => ERR-295-10.
Horloge Contrôle monotonicité UTC locale ; dérive détectée => MEMORY_DEGRADED + alerte, pas de suppression silencieuse de traces.
Réconciliation Intervalle 5 min ; orphelin >15 min => MEMORY_DEGRADED + rattrapage.
Clearing Retour MEMORY_HEALTHY après 2 cycles conformes consécutifs.
Audit key rotation scripts/rotate-audit-hmac.sh rotate + archive + événement audit_key_rotated.

5.13 Contraintes inter-modules

Élément Spécification
Points d’entrée protégés /gov-step-0, /gov-learnings-inject, /gov-compounder, /morning, hook on_story_close.
Scripts obligatoires measure-cs1-baseline.py, measure-cs2.sh, measure-cs3.sh, measure-cs4.sh, purge-clarifications.py, restore-learning.py, rotate-audit-hmac.sh.
Données partagées story_id, domain, project, gate8_go_v1, learning_id, query_hash, key_version.
Liaison cross-module learning_id + story_id + step.
Scope d’enregistrement Commandes ciblées uniquement.
Exceptions d’accès Aucune exception de rôle dans PD-295.

5bis. Diagrammes

Diagramme d’état

stateDiagram-v2
    [*] --> STORY_ACTIVE

    note right of STORY_ACTIVE
      Garde B4: migration scope autorisée
      seulement si commit git préalable
      quand au moins une ligne sans scope
    end note

    STORY_ACTIVE --> DOMAIN_ACTIVE : score>=0.30 && nb_domains>=1
    STORY_ACTIVE --> ARCHIVED : nb_injections=0 && age>56j
    DOMAIN_ACTIVE --> GLOBAL_ACTIVE : score>=0.60 && nb_domains>=2
    ARCHIVED --> STORY_ACTIVE : restore-learning.py (manuel, event signé Vault)

    note left of STORY_ACTIVE
      Transitions interdites explicitement rejetées:
      STORY->GLOBAL, DOMAIN->STORY, DOMAIN->ARCHIVED,
      GLOBAL->DOMAIN, GLOBAL->STORY, GLOBAL->ARCHIVED
    end note

Diagramme de séquence

sequenceDiagram
    participant S0 as gov-step-0
    participant INJ as gov-learnings-inject
    participant L1 as learnings.jsonl.lock
    participant L2 as learnings-scores.jsonl.lock
    participant SRCH as search-*
    participant TRACE as learnings-injections.jsonl
    participant VLT as Vault

    S0->>INJ: {story_id,domain,project,title}
    INJ->>VLT: read kv/pd-295/memory-audit-hmac
    alt vault indisponible
      INJ-->>S0: ERR-295-16 (fail-closed)
    else vault disponible
      INJ->>L1: LOCK_SH (timeout 30s, fail-closed ERR-295-09)
      INJ->>L2: LOCK_SH (timeout 30s, fail-closed ERR-295-09)
      INJ->>INJ: rate-limit glissant puis idempotence bucket
      par learning
        INJ->>SRCH: search-learnings --top-k 5 --trace
      and veille
        INJ->>SRCH: search-veille --top-k 3 --trace
      and clarification
        INJ->>SRCH: search-clarifications --domain --project --top-k 3 --trace
      end
      SRCH->>TRACE: append events signés + key_version
      INJ-->>S0: bloc mémoire 3 sections (ou "aucun résultat")
      INJ->>L2: unlock
      INJ->>L1: unlock
    end

6. Cas d’erreur

ID Condition d’erreur Réponse attendue
ERR-295-01 story_id/domain/project invalide Rejet immédiat, aucune écriture
ERR-295-02 Frontmatter/date/champs obligatoires invalides Fichier/ligne exclu + log
ERR-295-03 Enum invalide (impact_pv, verdict, scope, source) Rejet argument ou ligne exclue
ERR-295-04 search-clarifications sans --domain ou --project Rejet commande
ERR-295-05 Index FAISS manquant pour une source Source dégradée, injection partielle
ERR-295-06 Trois sources indisponibles au step 0 Blocage step 0
ERR-295-07 learnings-scores.jsonl absent pendant B4 Promotion désactivée, scopes inchangés
ERR-295-08 Précondition commit préalable non satisfaite (migration effective requise) Migration B4 refusée
ERR-295-09 Lock non acquis avant timeout Rejet sans effet de bord
ERR-295-10 Même clé idempotence + query_hash différent Rejet conflit idempotence
ERR-295-11 Quota rate-limit step 0 dépassé Rejet explicite
ERR-295-12 Échec d’écriture trace (Vault OK) Injection maintenue, observabilité dégradée
ERR-295-13 Signature HMAC absente/invalide ou key_version incohérente Événement rejeté, alerte audit
ERR-295-14 Échec purge clarifications sur un déclencheur Warning explicite + relance requise
ERR-295-15 restore-learning.py sur learning_id absent/invalide Rejet restauration
ERR-295-16 Vault indisponible pour lecture clé audit au démarrage Fail-closed, arrêt skill /gov
ERR-295-17 query > 500 caractères Rejet commande/trace
ERR-295-18 Timeout questions PO step 0 Step 0 interrompu
ERR-295-19 /morning ne peut pas publier exactement 3 entrées Échec conformité observable (exit != 0)

7. Critères d’acceptation — sortie Gate 8

ID Critère Observable
CA-295-01 B1 génère corpus veille conforme §5.1.2 data/veille.jsonl valide
CA-295-02 B1 indexe veille en dimension 768 index FAISS lisible, dim 768
CA-295-03 Filtres veille fonctionnent (impact,verdict) résultats conformes
CA-295-04 B2 persiste les 4 clarifications verbatim en mode local et Ringbearer PD-XX-clarifications.md complet
CA-295-05 Persistance clarifications avant rédaction besoin timestamp clarification <= besoin
CA-295-06 search-clarifications refuse sans domain/project échec explicite
CA-295-07 B3 écrit learnings-scores.jsonl sans muter learnings.jsonl hash inchangé
CA-295-08 reuse_score suit la formule normalisée tanh recalcul identique
CA-295-09 B4 ajoute scope: story aux entrées héritées aucune ligne sans scope
CA-295-10 Promotions respectent seuils 0.30/0.60 + nb_domains transitions exactes
CA-295-11 Éviction stale suit règle 56 jours + 0 injection archive correcte
CA-295-12 B5 produit bloc à 3 sections, avec “aucun résultat” si besoin bloc conforme
CA-295-13 Cardinalités strictes : 5 learnings, 3 veilles, 3 clarifications cardinalités exactes
CA-295-14 Traces d’injection incluent source, domain, project, operation, key_version, signature ventilables + vérifiables
CA-295-15 reindex-all inclut veille + clarifications après plans ordre vérifiable
CA-295-16 Protections concurrence (double lock/idempotence/rate-limit/réconciliation/clearing) effectives tests passent
CA-295-20 measure-cs1-baseline.py gèle baseline pré-B5 (10 stories) et calcule CS-1 artefact baseline + calcul reproductible
CA-295-21 /morning publie exactement 3 learnings top score avec observable d’échec sortie + code retour conformes
CA-295-22 Tous les événements B1..B5 + restore sont signés/horodatés, vérifiés via Vault et key_version vérification HMAC valide
CA-295-23 Purge clarifications >18 mois fonctionne suppressions conformes
CA-295-24 restore-learning.py --id <id> restaure scope=story avec audit signé Vault restauration contrôlée
CA-295-25 B5 propage explicitement --domain {story.domain} --project {story.project} --top-k 3 à search-clarifications.py commande observée conforme
CA-295-26 Purge clarifications déclenchée par step10, on_story_close, et cron quotidien 3 chemins observables

7 bis. Formules des KPI de mesure continue post-merge

CS-1 (itérations Gate 8)

  • Baseline gelée : 10 stories pré-B5.
  • Fenêtre post : 10 stories post-B5.
  • Formule : CS-1 = taux_GO_v1_10_post_B5 - taux_GO_v1_10_pre_B5.
  • Script : scripts/measure-cs1-baseline.py + calcul post.

CS-2 (réutilisation des learnings)

  • Fenêtre : T+90j après merge B5.
  • Formule :
    (nb_learnings_injectés_au_moins_1_fois_dans_la_fenêtre / total_learnings_créés_avant_B5) * 100
  • Cible : >= 30%.
  • Source : data/learnings-injections.jsonl + data/learnings.jsonl.
  • Script : scripts/measure-cs2.sh --window 90d.
  • Alerte : <20% à T+60j => signal /morning.

CS-3 (capitalisation clarifications)

  • Fenêtre : 30 jours glissants.
  • Formule :
    nb_occurrences où search-clarifications.py --domain D remonte >=1 résultat pour une nouvelle story du même domaine
  • Cible : >= 1 occurrence/mois.
  • Source : événements de session source=clarification.
  • Script : scripts/measure-cs3.sh.

CS-4 (activation veille)

  • Fenêtre : 5 premières stories post-merge B5.
  • Formule :
    nb_stories où une fiche veille impact_pv in ('fort','modere') est injectée au step 0
  • Cible : >= 5 sur 5.
  • Source : événements de session source=veille.
  • Script : scripts/measure-cs4.sh.
ID Horizon Critère Mesure
CA-295-17 T+90j CS-2 >=30% measure-cs2.sh
CA-295-18 Mensuel CS-3 >=1/mois measure-cs3.sh
CA-295-19 5 premières stories post-B5 CS-4 >=5/5 measure-cs4.sh
CA-295-27 Dès 10 stories post-B5 (max T+90j) CS-1 > 0 baseline CS-1 + recalcul

8. Scénarios de test (Given / When / Then)

  1. ST-295-01 Veille nominale.
  2. ST-295-02 Veille invalide.
  3. ST-295-03 Clarifications tous modes.
  4. ST-295-04 Clarifications sans filtres.
  5. ST-295-05 Scoring tanh.
  6. ST-295-06 Jointure score manquant.
  7. ST-295-07 Migration scope.
  8. ST-295-08 Promotion seuils + nb_domains.
  9. ST-295-09 Éviction stale.
  10. ST-295-10 Injection unifiée 3 sections + cardinalités.
  11. ST-295-11 RWLock double fichier.
  12. ST-295-12 Rate-limit prioritaire.
  13. ST-295-13 Idempotence/rejeu.
  14. ST-295-14 Dégradation partielle source.
  15. ST-295-15 Indisponibilité totale.
  16. ST-295-16 Signature + key_version.
  17. ST-295-17 /morning obligatoire et observable.
  18. ST-295-18 Baseline CS-1 gelée.
  19. ST-295-19 Purge 18 mois.
  20. ST-295-20 Restore archive signé.
  21. ST-295-21 Vérification signature sur clé archivée.
  22. ST-295-22 Purge via hook on_story_close.
  23. ST-295-23 Purge via cron quotidien.
  24. ST-295-24 Rejet query > 500.

9. Hypothèses explicites

ID Hypothèse Impact si faux
H-295-01 Métadonnées story/domain/project disponibles au step 0. Injection clarifications impossible.
H-295-02 Dépôts ProbatioVault-* accessibles en lecture pour collecte clarifications. Corpus incomplet.
H-295-03 metrics.jsonl contient données Gate 8 exploitables. KPI biaisés.
H-295-04 story/gate/tags restent stables ; édition tags historiques interdite. Dérive learning_id/score.
H-295-05 Inventaire des consommateurs externes est validé avant migration scope. Migration bloquée (précondition release).
H-295-06 Horloge UTC locale exploitable pour bucket/idempotence. Passage MEMORY_DEGRADED + alerte.
H-295-07 Vault (kv/pd-295/memory-audit-hmac) est accessible aux exécuteurs autorisés. ERR-295-16, arrêt /gov.
H-295-08 Ollama nomic-embed-text disponible localement. Recherche dégradée.
H-295-09 Bootstrap initial clé audit effectué manuellement avant activation B1..B5. Audit indisponible au démarrage.

10. Points à clarifier

10.1 Contraintes techniques (stack obligatoire)

  • Projet : ProbatioVault-ia-governance.
  • Langages : Python 3 + Shell Bash/Zsh.
  • Recherche sémantique : FAISS CPU 1.x + NumPy.
  • Embeddings : Ollama nomic-embed-text (768).
  • Formats : Markdown, YAML, JSONL, FAISS, NPY.
  • Orchestration : .claude/commands/*.
  • Épic : tooling.

10.2 Questions ouvertes

ID Point à clarifier Donnée attendue
Q-295-03 Politique de démotion manuelle global/domain -> story hors restore archive Processus gouvernance
Q-295-07 Escalade humaine en cas de MEMORY_DEGRADED prolongé Seuil d’alerte opérationnelle

Références :

  • Epic : tooling
  • JIRA : PD-295
  • Repos : ProbatioVault-ia-governance, ProbatioVault-doc, ProbatioVault-*
  • Scripts contractuels : measure-cs1-baseline.py, measure-cs2.sh, measure-cs3.sh, measure-cs4.sh, purge-clarifications.py, restore-learning.py, rotate-audit-hmac.sh

Changelog v2→v3

  • G3v2-B1 : remplacement HMAC file_mtime_ns par clé statique Vault (kv/pd-295/memory-audit-hmac), ajout key_version, rotation manuelle rotate-audit-hmac.sh, vérification archive, fail-closed ERR-295-16.
  • G3v2-M1 : nb_domains redéfini sans max(1, ...) ; promotion story->domain réellement bloquée si nb_domains==0.
  • G3v2-M2 : cardinalité clarifications fixée (top_k_clarifications_step0=3) et contractuelle dans INV/CA/B5.
  • G3v2-M3 : formules CS-2/CS-3/CS-4 ajoutées avec fenêtres, sources, scripts et seuils.
  • G3v2-M4 : politique lock étendue à B3/B4/B5 sur deux lockfiles avec ordre d’acquisition fixe.
  • G3v2-M5 : propagation explicite de --domain --project --top-k 3 de B5 vers search-clarifications.py (CA-295-25).
  • G3v2-M6 : purge clarifications découplée du seul step 10 (step10 + hook on_story_close + cron), CA-295-26.
  • G3v2-M7 : arbitrage rate-limit/idempotence explicite : rate-limit glissant évalué en premier (ERR-295-11), puis idempotence bucket.
  • G3v2-M8 : schéma canonique data/clarifications.jsonl ajouté en §5.3 avec contraintes types/champs.
  • G3v2-M9 : restore-learning.py aligné sur la même politique clé Vault et key_version que B1..B5.
  • G3v2-M10 : limite query <= 500 contractualisée (ERR-295-17).
  • G3v2-M11 : règle count == len(result_ids) contractualisée (INV-295-23, log audit_inconsistent_trace).
  • Corrections mineures intégrées : migration scope one-shot clarifiée, observabilité /morning explicitée, diagrammes alignés sur gardes contractuelles, hypothèse consommateurs externes rendue contrôlée, borne max min_score_default fixée à 0.99, garde de cohérence horloge ajoutée, stabilité learning_id durcie.