Aller au contenu

PD-297 — Boucle d’enrichissement formel contractuelle (capitalisation inter-stories)

1. Objectif

La User Story PD-297 DOIT fermer la boucle formelle du workflow /gov : à chaque story DONE, la base formelle DOIT être enrichie de manière traçable et idempotente (Prolog, TLA+, Alloy, Z), puis validée par non-régression. Les scopes contracts, code et full DOIVENT produire un verdict GO ou FAIL (jamais SKIP).

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

Inclus

  • Enrichissement append-only des prédicats de story dans ProbatioVault-formal.
  • Persistance des invariants dérivés des code-contracts dans les 4 formalismes (prolog, tla, alloy, z).
  • Vérification de non-régression sur base formelle étendue.
  • Verdict formel obligatoire GO|FAIL pour contracts, code, full.
  • Intégration contractuelle au step 10 (gov-retrospective).
  • Protection distribuée (lock, idempotence, réconciliation, rate-limit, clearing).
  • Traçabilité story-source de tout artefact enrichi.

Exclu

  • Modification du contenu normatif existant (hors append-only d’enrichissement).
  • Enrichissement rétroactif des stories historiques déjà DONE.
  • Changement de comportement fonctionnel hors PD-297 dans les autres steps.
  • Toute règle non testable est hors périmètre.

3. Définitions

Terme Définition
Enrichissement formel Ajout de faits/prédicats/invariants d’une story dans la base formelle, sans suppression ni mutation de l’existant.
Append-only Écriture uniquement additive ; aucune ligne existante ne peut être modifiée/supprimée.
Non-régression formelle Vérification que les nouveaux artefacts n’introduisent pas de contradiction avec la base existante.
Scope contracts Vérification formelle sur artefacts contractuels de la story.
Scope code Vérification formelle sur artefacts code de la story.
Scope full Vérification formelle globale (contracts + code + base enrichie).
Fail-closed Toute erreur interne/timeout/outillage indisponible retourne FAIL.
État terminal État sans sortie autorisée ; toute sortie doit être explicitement interdite.
Réconciliation Détection et rattrapage des exécutions orphelines/partielles.
Transition retour Transition explicite vers un état antérieur (autorisée ou interdite).

4. Invariants (non négociables)

ID Règle Justification
INV-297-01 L’enrichissement est append-only : aucune suppression/modification d’un prédicat déjà persisté. Prévention des régressions silencieuses.
INV-297-02 Tout prédicat/invariant persisté DOIT porter une traçabilité story_id + trace_source (D-297-01, D-297-16). Opposabilité et audit.
INV-297-03 Le replay strictement identique (même idempotency_key, même payload) DOIT produire exactement le même résultat sans duplication. Robustesse retry.
INV-297-04 Toute erreur d’extraction/persistance place le run en ENRICHMENT_DEGRADED (pas DONE). Dégradation contrôlée.
INV-297-05 Toute erreur interne de vérification formelle retourne FAIL (fail-closed). Art. VIII constitutionnel.
INV-297-06 Les scopes contracts, code, full retournent uniquement GO ou FAIL (jamais SKIP). Fermeture du trou PD-296.
INV-297-07 Les 4 formalismes (prolog, tla, alloy, z) sont obligatoires à chaque enrichissement valide. Couverture formelle complète.
INV-297-08 Le check de non-régression est obligatoire après enrichissement avant clôture step 10. Détection de contradictions.
INV-297-09 États autorisés : PENDING, EXTRACTING, ENRICHED, ENRICHMENT_DEGRADED, VERIFICATION_PASSED, FAILED_BLOCKING, DONE. FSM fermée et testable.
INV-297-10 Toute transition non listée en §5.5 est interdite. Zéro ambiguïté de transitions.
INV-297-11 PENDING -> EXTRACTING est la seule entrée nominale du run d’enrichissement. Démarrage déterministe.
INV-297-12 EXTRACTING -> ENRICHED exige extraction + persistance réussies sur les 4 formalismes. Intégrité du statut ENRICHED.
INV-297-13 EXTRACTING -> ENRICHMENT_DEGRADED est obligatoire en cas de timeout/erreur d’extraction ou persistance. Contrat de dégradation.
INV-297-14 ENRICHED -> VERIFICATION_PASSED exige GO non-régression + GO scopes contracts,code,full. Validation complète.
INV-297-15 ENRICHED -> FAILED_BLOCKING est obligatoire si un check retourne FAIL, ERROR ou SKIP. Bloquant non contournable.
INV-297-16 ENRICHMENT_DEGRADED -> FAILED_BLOCKING est obligatoire (vérification bloquante). Cohérence avec INV-297-04.
INV-297-17 VERIFICATION_PASSED -> DONE est l’unique transition de clôture réussie. Clôture contrôlée.
INV-297-18 FAILED_BLOCKING -> PENDING est la seule transition retour autorisée, après correction explicite. Boucle de correction maîtrisée.
INV-297-19 DONE -> * : INTERDITE (état terminal, résolution manuelle uniquement). Immutabilité de fin de run.
INV-297-20 Les mécanismes distribués de §5.6 sont obligatoires en concurrence multi-runs. Robustesse distribuée.
INV-297-21 La clôture step 10 est interdite tant que l’état n’est pas DONE. Alignement inter-modules.
INV-297-22 Toute règle de cette spec est testable via §7/§8 ; sinon classée hors périmètre. Contractualisation vérifiable.

5. Flux nominaux

5.1 Modèle de données contractuel (source unique des formats)

ID Donnée Format / encodage Taille Jeu de caractères Case Regex / contrainte Si invalide
D-297-01 story_id String ASCII 4..11 [A-Z0-9-] Sensitive ^PD-[0-9]{1,4}$ Rejet + FAILED_BLOCKING
D-297-02 story_version Entier décimal UTF-8 1..2 chars [0-9] n/a ^[1-9][0-9]?$ Rejet + FAILED_BLOCKING
D-297-03 project_code Enum string UTF-8 6..13 [a-z-] Sensitive ^(ia-governance|formal)$ Rejet + FAILED_BLOCKING
D-297-04 domain_slug String UTF-8 3..64 [a-z0-9-] Sensitive ^[a-z0-9]+(?:-[a-z0-9]+)*$ Rejet + FAILED_BLOCKING
D-297-05 formal_scope Enum ASCII 4..9 [a-z] Sensitive ^(contracts|code|full)$ Rejet + FAILED_BLOCKING
D-297-06 job_state Enum ASCII 7..21 [A-Z_] Sensitive ^(PENDING|EXTRACTING|ENRICHED|ENRICHMENT_DEGRADED|VERIFICATION_PASSED|FAILED_BLOCKING|DONE)$ Rejet + FAILED_BLOCKING
D-297-07 verdict Enum ASCII 2..4 [A-Z] Sensitive ^(GO|FAIL)$ Rejet + FAILED_BLOCKING
D-297-08 failure_code Enum ASCII 4..32 [A-Z_] Sensitive ^(NONE|INPUT_INVALID|EXTRACTION_ERROR|PERSISTENCE_ERROR|NON_REGRESSION_CONTRADICTION|TOOL_UNAVAILABLE|TIMEOUT|CONCURRENT_RUN|IDEMPOTENCY_CONFLICT|INTERNAL_ERROR|SKIP_FORBIDDEN)$ Rejet + FAILED_BLOCKING
D-297-09 source_artifact_ref Chemin POSIX absolu UTF-8 1..512 UTF-8 printable Sensitive ^/.+$ Rejet + FAILED_BLOCKING
D-297-10 ontology_story_file_path Chemin POSIX absolu UTF-8 1..512 UTF-8 printable Sensitive ^.*/ProbatioVault-formal/coherence/ontology/stories/PD-[0-9]{1,4}\\.pl$ Rejet + FAILED_BLOCKING
D-297-11 predicate_atom Ligne Prolog ASCII 8..1024 chars ASCII printable Sensitive ^[a-z_][a-z0-9_]*\\(.+\\)\\.$ Rejet + FAILED_BLOCKING
D-297-12 predicate_fingerprint SHA-256 hex 64 chars [a-f0-9] Sensitive ^[a-f0-9]{64}$ Rejet + FAILED_BLOCKING
D-297-13 formalism Enum ASCII 1..6 [a-z] Sensitive ^(prolog|tla|alloy|z)$ Rejet + FAILED_BLOCKING
D-297-14 formal_model_target_path Chemin POSIX absolu UTF-8 1..512 UTF-8 printable Sensitive ^.*/ProbatioVault-formal/normes/[a-z0-9-]+/formal/.+$ Rejet + FAILED_BLOCKING
D-297-15 invariant_id String ASCII 9..15 [A-Z0-9-] Sensitive ^INV-[0-9]{1,4}-[0-9]{2}$ Rejet + FAILED_BLOCKING
D-297-16 trace_source String ASCII 12..128 [A-Z0-9_:\\-\\.#/] Sensitive ^PD-[0-9]{1,4}:[a-z0-9_-]+:[0-9]{1,6}$ Rejet + FAILED_BLOCKING
D-297-17 idempotency_key SHA-256 hex 64 chars [a-f0-9] Sensitive ^[a-f0-9]{64}$ Rejet + FAILED_BLOCKING
D-297-18 lock_key String ASCII 20..80 [a-z0-9:_-] Sensitive ^formal_enrich:PD-[0-9]{1,4}:v[1-9][0-9]?$ Rejet + FAILED_BLOCKING
D-297-19 correlation_id UUID v4 36 chars [a-f0-9-] Sensitive ^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$ Rejet + FAILED_BLOCKING
D-297-20 timestamp_utc RFC3339 UTC 20..30 chars ASCII Sensitive Horodatage parseable UTC (Z ou offset) Rejet + FAILED_BLOCKING
D-297-21 formal_result_json Objet JSON UTF-8 200..20000 bytes Clés ASCII, valeurs UTF-8 Clés sensitive Clés obligatoires: story_id, story_version, formal_scope, job_state, verdict, failure_code, idempotency_key, correlation_id, timestamp_utc, details Rejet + FAILED_BLOCKING

Référencement unique: toutes les sections suivantes réutilisent ces IDs (D-297-*) sans redéfinir les formats.

5.2 Bornes numériques contractuelles

Paramètre Défaut Min Max Unité Contexte de référence Percentile Hors bornes
extraction_timeout_sec 120 30 600 secondes Step 10 /gov-retrospective P95 Rejet config + FAILED_BLOCKING
persistence_timeout_sec 180 30 600 secondes Step 10 /gov-retrospective P95 Rejet config + FAILED_BLOCKING
formal_scope_timeout_sec 180 30 300 secondes Scopes contracts/code/full P95 Rejet config + FAILED_BLOCKING
non_regression_timeout_sec 180 30 600 secondes Check de cohérence post-enrichissement P95 Rejet config + FAILED_BLOCKING
distributed_lock_ttl_sec 600 60 1800 secondes Concurrence multi-runs n/a Rejet config + FAILED_BLOCKING
idempotency_window_h 24 1 168 heures Déduplication des relances n/a Rejet config + FAILED_BLOCKING
reconciliation_interval_sec 300 60 900 secondes Scan des runs orphelins n/a Rejet config + FAILED_BLOCKING
orphan_threshold_sec 900 180 3600 secondes Détection d’orphelin n/a Rejet config + FAILED_BLOCKING
rate_limit_runs_per_min_per_story 2 1 10 runs/min/story Protection de la base formelle n/a Rejet exécution (équivalent 429)
clearing_success_cycles_required 2 1 10 cycles Levée d’état dégradé n/a Rejet config + FAILED_BLOCKING
max_formal_scopes_per_run 3 3 3 scopes contracts,code,full n/a Rejet + FAILED_BLOCKING

5.3 SLA temporels (transitions d’état)

SLA Défaut Min Max Configurabilité Comportement à expiration
sla_extraction_window 120s 30s 600s Oui (extraction_timeout_sec) EXTRACTING -> ENRICHMENT_DEGRADED
sla_persistence_window 180s 30s 600s Oui (persistence_timeout_sec) EXTRACTING -> ENRICHMENT_DEGRADED
sla_non_regression_window 180s 30s 600s Oui (non_regression_timeout_sec) ENRICHED -> FAILED_BLOCKING
sla_contracts_scope_window 180s 30s 300s Oui (formal_scope_timeout_sec) ENRICHED -> FAILED_BLOCKING
sla_code_scope_window 180s 30s 300s Oui (formal_scope_timeout_sec) ENRICHED -> FAILED_BLOCKING
sla_full_scope_window 180s 30s 300s Oui (formal_scope_timeout_sec) ENRICHED -> FAILED_BLOCKING
sla_orphan_reconciliation_window 900s 180s 3600s Oui (orphan_threshold_sec) Run orphelin forcé en FAILED_BLOCKING

Aucune autre transition temporelle identifiée.

5.4 Flux nominaux contractuels

  1. F-297-01 — Déclenchement step 10
  2. Entrées minimales valides: D-297-01 à D-297-05, D-297-09, D-297-17 à D-297-21.
  3. Le lock distribué (D-297-18) DOIT être acquis avant toute écriture.
  4. Transition: PENDING -> EXTRACTING.

  5. F-297-02 — Extraction/persistance Prolog

  6. Transformation obligatoire: spec_markdown + code_contracts -> predicate_atom[] (D-297-11) + fingerprint (D-297-12).
  7. Persistance append-only dans D-297-10, avec trace_source (D-297-16) pour chaque entrée.
  8. En cas d’erreur/timeout extraction ou persistance: EXTRACTING -> ENRICHMENT_DEGRADED.

  9. F-297-03 — Enrichissement TLA+/Alloy/Z

  10. Transformation obligatoire: code_contracts -> invariant_id[] (D-297-15) + cibles D-297-14 pour formalism (D-297-13).
  11. Les 4 formalismes sont obligatoires pour un run validé.
  12. Échec partiel sur un formalisme: état non validable (ENRICHMENT_DEGRADED puis FAILED_BLOCKING).

  13. F-297-04 — Non-régression formelle

  14. Le check de cohérence DOIT s’exécuter sur la base étendue.
  15. Contradiction détectée: ENRICHED -> FAILED_BLOCKING, failure_code=NON_REGRESSION_CONTRADICTION.

  16. F-297-05 — Vérification scopes contracts, code, full

  17. Pour chaque scope (D-297-05), verdict obligatoire (D-297-07) sur D-297-21.
  18. SKIP est contractuellement interdit et traité comme FAIL (failure_code=SKIP_FORBIDDEN).
  19. Tous scopes GO + non-régression GO => ENRICHED -> VERIFICATION_PASSED.
  20. Sinon => ENRICHED -> FAILED_BLOCKING.

  21. F-297-06 — Clôture step 10

  22. Seul VERIFICATION_PASSED -> DONE autorise la clôture.
  23. Tant que l’état n’est pas DONE, la transition de clôture story est interdite.

5.5 Machine d’états (transitions retour explicites)

États: PENDING, EXTRACTING, ENRICHED, ENRICHMENT_DEGRADED, VERIFICATION_PASSED, FAILED_BLOCKING, DONE.

État PENDING

Transition sortante Statut Raison / condition Comportement
PENDING -> EXTRACTING AUTORISÉE Trigger step 10 valide + lock acquis Démarrage run
PENDING -> ENRICHED, ENRICHMENT_DEGRADED, VERIFICATION_PASSED, FAILED_BLOCKING, DONE INTERDITE Saut d’état Rejet

État EXTRACTING

Transition sortante Statut Raison / condition Comportement
EXTRACTING -> ENRICHED AUTORISÉE Extraction + persistance 4 formalismes réussies Continue vérification
EXTRACTING -> ENRICHMENT_DEGRADED AUTORISÉE Erreur/timeout extraction ou persistance Dégradation contrôlée
EXTRACTING -> PENDING, VERIFICATION_PASSED, FAILED_BLOCKING, DONE INTERDITE Transition hors contrat Rejet

État ENRICHED

Transition sortante Statut Raison / condition Comportement
ENRICHED -> VERIFICATION_PASSED AUTORISÉE Non-régression GO + scopes contracts/code/full = GO Autorise clôture
ENRICHED -> FAILED_BLOCKING AUTORISÉE Contradiction, timeout, erreur interne, ou verdict non-GO Blocage
ENRICHED -> PENDING, EXTRACTING, ENRICHMENT_DEGRADED, DONE INTERDITE Retour/saut non prévu Rejet

État ENRICHMENT_DEGRADED

Transition sortante Statut Raison / condition Comportement
ENRICHMENT_DEGRADED -> FAILED_BLOCKING AUTORISÉE Vérification bloquante fail-closed obligatoire Blocage
ENRICHMENT_DEGRADED -> PENDING, EXTRACTING, ENRICHED, VERIFICATION_PASSED, DONE INTERDITE Bypass de correction Rejet

État VERIFICATION_PASSED

Transition sortante Statut Raison / condition Comportement
VERIFICATION_PASSED -> DONE AUTORISÉE Clôture step 10 Fin réussie
VERIFICATION_PASSED -> PENDING, EXTRACTING, ENRICHED, ENRICHMENT_DEGRADED, FAILED_BLOCKING INTERDITE Retour/saut non prévu Rejet

État FAILED_BLOCKING

Transition sortante Statut Raison / condition Comportement
FAILED_BLOCKING -> PENDING AUTORISÉE Correction validée + relance explicite Nouvelle tentative
FAILED_BLOCKING -> EXTRACTING, ENRICHED, ENRICHMENT_DEGRADED, VERIFICATION_PASSED, DONE INTERDITE Contournement de la relance Rejet

État DONE (terminal)

Transition sortante Statut Raison / condition Comportement
DONE -> * INTERDITE État terminal état terminal, résolution manuelle uniquement

Comportement de retour/downgrade: - ENRICHED -> FAILED_BLOCKING: les données déjà enrichies sont conservées (aucune suppression), clôture interdite. - FAILED_BLOCKING -> PENDING: historique conservé, quotas/rate-limit réappliqués immédiatement, verrouillages maintenus jusqu’à DONE. - Transition inverse directe non listée: interdite.

Checklist machine à états: - [x] Chaque état liste ses transitions sortantes (autorisées ou interdites). - [x] État terminal explicite: DONE -> * : INTERDITE (état terminal, résolution manuelle uniquement). - [x] Modèle couvert par invariants dédiés (INV-297-09 à INV-297-19).

5.6 Mécanismes de protection distribuée

Mécanisme Contrat
Lock distribué Scope story_id + story_version (D-297-01, D-297-02), clé D-297-18, TTL=distributed_lock_ttl_sec; lock non acquis => FAIL, failure_code=CONCURRENT_RUN
Idempotence Clé D-297-17; fenêtre=idempotency_window_h; même clé + même payload => même D-297-21; même clé + payload différent => FAIL, IDEMPOTENCY_CONFLICT
Réconciliation Scan toutes reconciliation_interval_sec; run en EXTRACTING au-delà de orphan_threshold_sec => FAILED_BLOCKING
Rate-limiting Granularité story_id; quota=rate_limit_runs_per_min_per_story; dépassement => rejet de run
Clearing conditionnel Levée du statut dégradé uniquement après clearing_success_cycles_required cycles conformes consécutifs

Checklist protection distribuée: - [x] Lock: scope + TTL + comportement lock non acquis. - [x] Idempotence: clé + durée de déduplication. - [x] Réconciliation: intervalle + seuil orphelin + rattrapage. - [x] Rate-limit: granularité + quota + comportement dépassement. - [x] Clearing: nombre de cycles + mécanisme de comptage.

5.7 Contraintes inter-modules

Élément Contrat
Routes/commandes d’autres modules à protéger Step 10 gov-retrospective (clôture), vérifs formelles via formal-check scopes contracts/code/full, check de cohérence story suivante
Mécanisme de protection Guard de clôture: transition step 10 interdite tant que job_state != DONE
Données nécessaires cross-module story_id, story_version, artefacts source (spec, code-contracts, code), verdicts formal_result_json, état workflow
Résolution de liaison cross-module Liaison déterministe story_id -> PD-XXX.pl + story_id -> artefacts docs/epics/...
Scope d’enregistrement du guard Local aux commandes de workflow formel (pas de guard global hors périmètre PD-297)
Exceptions d’accès Aucune exception de bypass sur FAILED_BLOCKING

5.8 Clauses obligatoires “non applicables”

  • Stratégie de migration DDL: non applicable (aucune colonne SQL modifiée).
  • Atomicité multi-composant DB + queue/append-only: non applicable (pas de flux DB+queue dans PD-297).
  • Invariant envelope encryption crypto: non applicable (domaine formal/tooling, pas crypto/crypto-proof).

5bis. Diagrammes (obligatoire)

Diagramme d’état (stateDiagram-v2)

stateDiagram-v2
    [*] --> PENDING

    PENDING --> EXTRACTING: trigger_step10 + lock_ok
    PENDING --> ENRICHED: INTERDITE
    PENDING --> ENRICHMENT_DEGRADED: INTERDITE
    PENDING --> VERIFICATION_PASSED: INTERDITE
    PENDING --> FAILED_BLOCKING: INTERDITE
    PENDING --> DONE: INTERDITE

    EXTRACTING --> ENRICHED: extraction+persistance 4 formalismes OK
    EXTRACTING --> ENRICHMENT_DEGRADED: timeout|error extraction/persistance
    EXTRACTING --> PENDING: INTERDITE
    EXTRACTING --> VERIFICATION_PASSED: INTERDITE
    EXTRACTING --> FAILED_BLOCKING: INTERDITE
    EXTRACTING --> DONE: INTERDITE

    ENRICHED --> VERIFICATION_PASSED: non_regression=GO + contracts/code/full=GO
    ENRICHED --> FAILED_BLOCKING: contradiction|timeout|error|SKIP_FORBIDDEN
    ENRICHED --> PENDING: INTERDITE
    ENRICHED --> EXTRACTING: INTERDITE
    ENRICHED --> ENRICHMENT_DEGRADED: INTERDITE
    ENRICHED --> DONE: INTERDITE

    ENRICHMENT_DEGRADED --> FAILED_BLOCKING: fail_closed obligatoire
    ENRICHMENT_DEGRADED --> PENDING: INTERDITE
    ENRICHMENT_DEGRADED --> EXTRACTING: INTERDITE
    ENRICHMENT_DEGRADED --> ENRICHED: INTERDITE
    ENRICHMENT_DEGRADED --> VERIFICATION_PASSED: INTERDITE
    ENRICHMENT_DEGRADED --> DONE: INTERDITE

    FAILED_BLOCKING --> PENDING: correction + relance explicite
    FAILED_BLOCKING --> EXTRACTING: INTERDITE
    FAILED_BLOCKING --> ENRICHED: INTERDITE
    FAILED_BLOCKING --> ENRICHMENT_DEGRADED: INTERDITE
    FAILED_BLOCKING --> VERIFICATION_PASSED: INTERDITE
    FAILED_BLOCKING --> DONE: INTERDITE

    VERIFICATION_PASSED --> DONE: cloture_step10
    VERIFICATION_PASSED --> PENDING: INTERDITE
    VERIFICATION_PASSED --> EXTRACTING: INTERDITE
    VERIFICATION_PASSED --> ENRICHED: INTERDITE
    VERIFICATION_PASSED --> ENRICHMENT_DEGRADED: INTERDITE
    VERIFICATION_PASSED --> FAILED_BLOCKING: INTERDITE

    DONE --> PENDING: INTERDITE
    DONE --> EXTRACTING: INTERDITE
    DONE --> ENRICHED: INTERDITE
    DONE --> ENRICHMENT_DEGRADED: INTERDITE
    DONE --> VERIFICATION_PASSED: INTERDITE
    DONE --> FAILED_BLOCKING: INTERDITE
    DONE --> [*]

Diagramme de séquence (sequenceDiagram)

sequenceDiagram
    participant GR as gov-retrospective
    participant EP as extract-eb-predicates
    participant EI as extract-formal-invariants
    participant OF as ProbatioVault-formal (ontology/models)
    participant CO as coherence check
    participant FV as formal-verify
    participant FC as formal-check wrapper

    GR->>EP: parse(spec_markdown + code_contracts_yaml)
    EP-->>GR: predicate_atom[] + sha256(predicate_atom)

    GR->>OF: append_only_write(PD-XXX.pl, predicate_atom[], trace_source)
    GR->>OF: regenerate_ontology(domains+terms+synonyms) -> _ontology-facts.pl

    GR->>EI: parse(code_contracts_yaml) -> invariants{prolog,tla,alloy,z}
    EI-->>GR: invariants_by_formalism
    GR->>OF: append_only_write(normes/{norme}/formal/*, invariants_by_formalism)

    GR->>CO: verify(base_extended + new_predicates)
    CO-->>GR: coherence_result_json{verdict, issues[]}

    alt coherence verdict = FAIL
        GR->>FC: formal_result_json{scope=full, verdict=FAIL}
        FC-->>GR: FAIL
    else coherence verdict = GO
        GR->>FV: verify_contracts(contracts_yaml -> contract_predicates)
        FV-->>GR: formal_result_json{scope=contracts, verdict}
        GR->>FV: verify_code(code_manifest -> code_predicates)
        FV-->>GR: formal_result_json{scope=code, verdict}
        GR->>FV: verify_full(contract_predicates + code_predicates + ontology_facts)
        FV-->>GR: formal_result_json{scope=full, verdict}
    end

    GR->>GR: aggregate verdicts -> job_state (VERIFICATION_PASSED|FAILED_BLOCKING)

6. Cas d’erreur

ID Cas Réponse attendue
ERR-297-01 story_id invalide (D-297-01) Rejet immédiat, FAILED_BLOCKING, failure_code=INPUT_INVALID
ERR-297-02 Lock non acquis (D-297-18) FAIL, failure_code=CONCURRENT_RUN, aucun write
ERR-297-03 Collision idempotence (même clé, payload différent) FAIL, failure_code=IDEMPOTENCY_CONFLICT
ERR-297-04 Échec extraction/persistance EXTRACTING -> ENRICHMENT_DEGRADED puis FAILED_BLOCKING
ERR-297-05 Tentative mutation/suppression d’un fait existant FAIL, failure_code=PERSISTENCE_ERROR
ERR-297-06 Contradiction non-régression détectée FAIL, failure_code=NON_REGRESSION_CONTRADICTION
ERR-297-07 formal-verify indisponible FAIL, failure_code=TOOL_UNAVAILABLE
ERR-297-08 Timeout SLA dépassé FAIL, failure_code=TIMEOUT
ERR-297-09 Scope retourne SKIP FAIL, failure_code=SKIP_FORBIDDEN
ERR-297-10 Transition non autorisée selon §5.5 Rejet + maintien état courant
ERR-297-11 DONE reçoit une transition sortante Rejet explicite (état terminal)
ERR-297-12 formal_result_json non conforme (D-297-21) Rejet + FAILED_BLOCKING

7. Critères d’acceptation (testables)

ID Critère Observable
CA-297-01 Une story DONE produit un fichier PD-XXX.pl dans ontology/stories Présence du fichier cible (D-297-10)
CA-297-02 Chaque prédicat persisté contient une traçabilité story-source Chaque entrée contient trace_source conforme D-297-16
CA-297-03 Le rerun strictement identique ne duplique pas les prédicats Compte stable + fingerprints (D-297-12) inchangés
CA-297-04 Les 4 formalismes sont enrichis lors d’un run valide Artefacts nouveaux détectés pour prolog/tla/alloy/z
CA-297-05 Non-régression exécutée après enrichissement Rapport de cohérence post-enrichissement présent
CA-297-06 Scope contracts retourne GO ou FAIL Aucune sortie SKIP observée
CA-297-07 Scope code retourne GO ou FAIL Aucune sortie SKIP observée
CA-297-08 Scope full retourne GO ou FAIL Aucune sortie SKIP observée
CA-297-09 Toute erreur interne de vérification retourne FAIL failure_code non-NONE et verdict FAIL
CA-297-10 Contradiction non-régression bloque la clôture État FAILED_BLOCKING, pas de transition -> DONE
CA-297-11 Lock distribué bloque les runs concurrents Second run même story rejeté CONCURRENT_RUN
CA-297-12 Réconciliation force les runs orphelins en échec bloquant Run EXTRACTING > seuil devient FAILED_BLOCKING
CA-297-13 FAILED_BLOCKING -> PENDING conserve l’historique Artefacts existants inchangés, nouvelle tentative possible
CA-297-14 DONE est terminal Toute transition sortante rejetée
CA-297-15 Step 10 ne se clôture qu’en état DONE Guard de clôture actif
CA-297-16 procedures.md et workflow-rules.md reflètent le nouveau contrat PD-297 Sections formelles mises à jour et cohérentes

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

  1. TC-NOM-01
    GIVEN une story PD-XXX en step 10 avec artefacts valides
    WHEN le run démarre
    THEN PENDING -> EXTRACTING et lock acquis.

  2. TC-NOM-02
    GIVEN extraction et persistance réussies sur les 4 formalismes
    WHEN l’étape d’enrichissement se termine
    THEN EXTRACTING -> ENRICHED.

  3. TC-ERR-03
    GIVEN un timeout d’extraction
    WHEN sla_extraction_window expire
    THEN EXTRACTING -> ENRICHMENT_DEGRADED puis blocage en vérification.

  4. TC-NR-04
    GIVEN base étendue avec contradiction détectée
    WHEN le check non-régression s’exécute
    THEN ENRICHED -> FAILED_BLOCKING et clôture interdite.

  5. TC-SCOPE-05
    GIVEN scope contracts exécuté
    WHEN le moteur retourne un verdict
    THEN la valeur est strictement GO ou FAIL (jamais SKIP).

  6. TC-SCOPE-06
    GIVEN scopes contracts, code, full tous GO et non-régression GO
    WHEN l’agrégation est effectuée
    THEN ENRICHED -> VERIFICATION_PASSED -> DONE.

  7. TC-IDEMP-07
    GIVEN une relance avec même idempotency_key et même payload
    WHEN le run est rejoué
    THEN résultat identique et aucune duplication de prédicats.

  8. TC-CONC-08
    GIVEN deux runs simultanés sur même story_id/story_version
    WHEN le second tente l’acquisition lock
    THEN rejet CONCURRENT_RUN.

  9. TC-RET-09
    GIVEN état FAILED_BLOCKING après correction
    WHEN une relance explicite est déclenchée
    THEN FAILED_BLOCKING -> PENDING puis reprise nominale.

  10. TC-TERM-10
    GIVEN état DONE
    WHEN une transition sortante est demandée
    THEN rejet explicite (état terminal).

  11. TC-FC-11
    GIVEN formal-verify indisponible
    WHEN un scope est invoqué
    THEN verdict FAIL (TOOL_UNAVAILABLE), jamais GO.

  12. TC-DATA-12
    GIVEN un predicate_atom hors regex D-297-11
    WHEN persistance est demandée
    THEN rejet + FAILED_BLOCKING.

9. Hypothèses explicites

ID Hypothèse Impact si faux
H-297-01 Le mapping story -> domaine -> normes/{norme}/formal est déterministe. Cible d’enrichissement ambiguë, blocage de persistance.
H-297-02 Les artefacts spec, code-contracts, code de la story sont accessibles au step 10. Vérifications scopes impossibles, FAILED_BLOCKING.
H-297-03 Les permissions d’écriture sur ProbatioVault-formal sont disponibles en step 10. Enrichissement impossible, blocage systématique.
H-297-04 Le stockage d’idempotence (fenêtre idempotency_window_h) est durable entre retries. Déduplication non fiable.
H-297-05 Le bloc {{LEARNINGS}} n’a pas fourni de règles additionnelles pour PD-297. Risque de manque de contraintes contextuelles à intégrer.

10. Points à clarifier

10.1 Contraintes techniques (stack projet cible)

Projet Stack réelle contractuelle Indices vérifiables
ProbatioVault-ia-governance Shell (bash/zsh), Python 3, composant Node.js (mcp-signal), artefacts Markdown/YAML/JSON scripts/*.sh, requirements.txt, mcp-signal/package.json
ProbatioVault-formal Python 3, SWI-Prolog, TLA+ (TLC), Alloy, Z-lint (Python), extraction TypeScript (ts-morph) coherence/*.py, tools/*.jar, CLAUDE.md du repo formal

Contrainte explicite: aucune substitution de stack n’est autorisée (pas Swift/SwiftUI, pas Spring Boot).

10.2 Questions ouvertes

ID Question Impact
Q-297-01 Référence épique Jira exacte non fournie. Traçabilité incomplète en en-tête documentaire.
Q-297-02 Convention exacte de cible norme par story (règle de routage) non formalisée. Risque d’écritures dans mauvaise norme.
Q-297-03 Stratégie de versionnement inter-repo pour append-only en concurrence non précisée. Risque de conflits d’écriture.
Q-297-04 Format canonique attendu de details dans formal_result_json non détaillé. Hétérogénéité des preuves de verdict.
Q-297-05 Politique de conservation des métadonnées d’idempotence (durée > 168h ?) non arbitrée. Déduplication potentiellement insuffisante.
Q-297-06 Condition de blocage Jira Done sur ENRICHMENT_DEGRADED doit-elle être strictement globale ? Risque de clôture prématurée.

Références