Aller au contenu

PD-293 — Orchestration contractuelle multi-stories “One Ring” via claude-peers et controle mobile (v2 corrigee)

1. Objectif

La User Story PD-293 permet a un Sovereign (humain) de piloter, depuis une session unique (One Ring), jusqu'a 5 Ringbearers executant chacun un workflow /gov PD-XXX [projet], avec :

  • supervision consolidee des stories,
  • remontee et traitement des escalades bloquantes,
  • pilotage a distance via /remote-control (iPhone/Safari),
  • separation stricte des responsabilites (One Ring route, Ringbearers executent).

2. Perimetre / Hors perimetre

Inclus

  • Orchestration multi-stories par une session One Ring unique.
  • Commandes /gov-lord : start, status, stop, escalade, respond, pause, resume.
  • Protocole de messagerie Ringbearer ↔ One Ring (types, validation, routage, dedoublonnage).
  • Detection de disparition/crash d'un Ringbearer et signalement au Sovereign.
  • Affichage consolide de l'etat de N stories.
  • Controle mobile du One Ring via /remote-control.
  • Persistance locale d'un etat consolide minimal .gov-lord-state.json.

Exclu

  • Toute modification du workflow /gov existant.
  • Toute execution metier/projet par le One Ring (lecture code, ecriture fichier, build, tests, Jira, GitLab, Vault).
  • Communication directe Ringbearer ↔ Ringbearer.
  • Application iOS native dediee.
  • Mutualisation/partage de credentials entre sessions.
  • Etat ESCALADE_EXPIRED en v1 (hors perimetre, evolution future).

3. Definitions

Terme Definition
Sovereign Humain decisionnaire final (PO/Architecte).
One Ring Session d'orchestration unique, strictement interface/routeur.
Ringbearer Session autonome dediee a une story, executant /gov.
Story Identifiant de workflow valide selon regex normative ^PD-[0-9]{1,4}$.
Broker Service claude-peers-mcp local assurant enregistrement/listing/messagerie des peers.
Escalade Demande bloquante d'un Ringbearer necessitant reponse humaine.
File d'escalades Queue FIFO des escalades ouvertes (multi-escalade simultanee).
Etat Ringbearer Etat contractuel du cycle de vie d'une story orchestree par One Ring.

4. Invariants (non negociables)

ID Regle Justification
INV-293-01 One Ring n'execute aucune action hors interface/routage ; toute demande hors scope est refusee avec message explicite. Separation des pouvoirs, securite, tracabilite.
INV-293-02 One Ring ne communique avec les stories qu'au travers du broker/peers ; aucune action directe sur les repos cibles. Confinement operationnel.
INV-293-03 Un Ringbearer est isole (contexte, credentials, execution) ; aucune contamination inter-story. Non-regression et robustesse.
INV-293-04 Maximum 5 Ringbearers actifs simultanes. Au-dela : rejet explicite. Limite de capacite declaree.
INV-293-05 Tout message doit etre valide selon le modele §5.1 ; sinon rejet deterministe et journalisation d'erreur au format contractuel §6.1. Contrat protocolaire testable.
INV-293-06 Une escalade suspend la progression de la story jusqu'a reception d'un PO_RESPONSE valide. Gouvernance humaine obligatoire.
INV-293-07 Le workflow /gov est consomme tel quel ; PD-293 ne modifie pas son comportement interne. Respect du perimetre.
INV-293-08 One Ring signale un Ringbearer disparu en etat CRASHED en 2 cycles de polling maximum (<= 20s par defaut). Resilience operationnelle.
INV-293-09 Les transitions d'etat Ringbearer respectent strictement la machine d'etats §5.2 ; toute transition non listee est interdite. Cohesion formelle.
INV-293-10 Etats terminaux (DONE, ABORTED, CRASHED, START_FAILED) : -> * INTERDITE (etat terminal, resolution manuelle uniquement). Fermeture explicite imposee.
INV-293-11 Reconciliation periodique obligatoire entre etat consolide One Ring et peers observes. Tolerance aux crashes/ecarts.
INV-293-12 Idempotence des commandes de controle (start, stop, respond, pause, resume) par cle dediee (§5.1). Retry-safe en environnement distribue.
INV-293-13 Multi-escalade simultanee geree en FIFO ; aucune escalade n'en remplace une autre. Determinisme de traitement et auditabilite.
INV-293-14 Ordre de gardes de commande contractuel : quota -> doublon idempotence -> validation format. Comportement stable et testable.

4.1 Notes de coherence etats

  • En v1, aucun etat ESCALADE_PENDING n'est defini.
  • En v1, aucun etat ESCALADE_EXPIRED n'est defini (hors perimetre, evolution future).

4.2 Commandes /gov-lord normatives

  • /gov-lord start <story_id> <project_code> [--idempotency-key <key>]
  • /gov-lord status [--story <story_id>]
  • /gov-lord escalade [--story <story_id>]
  • /gov-lord respond <story_id> <reponse> [--idempotency-key <key>]
  • /gov-lord pause <story_id> [--reason <texte>] [--idempotency-key <key>]
  • /gov-lord resume <story_id> [--idempotency-key <key>]
  • /gov-lord stop <story_id> [--reason <texte>] [--idempotency-key <key>]

5. Flux nominaux

5.1 Modele de donnees canonique (referentiel unique)

Regles globales normatives

  1. La regex story_id est la source de verite unique (pas de contrainte textuelle de longueur independante).
  2. Ordre de precedance dedoublonnage:
  3. si idempotency_key deja vue avec payload identique: retour deterministe sans effet secondaire,
  4. si idempotency_key deja vue avec payload different: rejet de conflit,
  5. sinon validation/pilotage normal par story_id.
  6. Ordre de gardes pour start (et seulement start) : quota -> doublon idempotence -> validation format complete.
  7. Pour stop/respond/pause/resume : doublon idempotence -> validation format complete (garde quota non applicable).
Donnee Format / encodage Caractere Sensibilite casse Regex / contrainte normative Si invalide
story_id ASCII [A-Z0-9-] case-sensitive ^PD-[0-9]{1,4}$ Rejet
project_code ASCII enum [a-z-] case-sensitive ^(backend|app|site|infra|doc|formal|pixel-governance|ia-governance)$ Rejet
message_type ASCII enum [A-Z_] case-sensitive ^(STATUS_UPDATE|ESCALADE|GATE_RESULT|WORKFLOW_DONE|PO_RESPONSE|PAUSE|RESUME|ABORT)$ Rejet
peer_id UTF-8 opaque printable UTF-8 case-sensitive ^[^\p{C}\n\r]{1,128}$ Rejet
timestamp RFC3339 UTC ASCII case-sensitive horodatage UTC parseable (suffixe Z ou offset UTC) Rejet
escalade_text UTF-8 printable UTF-8 case-sensitive ^[^\p{C}]{1,2000}$ Rejet
summary_text UTF-8 printable UTF-8 case-sensitive ^[^\p{C}]{1,512}$ Rejet
idempotency_key ASCII [A-Za-z0-9:_-] case-sensitive ^[A-Za-z0-9:_-]{16,80}$, unique dans fenetre TTL Doublon: resultat deterministe

Parametres numeriques contractuels

Parametre Defaut Min Max Unite Contexte Hors bornes
max_ringbearers 5 1 5 sessions MacBook local Rejet start
peer_poll_interval 10 1 10 secondes Broker local Clamp + avertissement
escalade_visibility_sla 5 1 10 secondes One Ring -> Sovereign Alerte SLA
start_detection_timeout 30 5 120 secondes Creation Ringbearer START_FAILED
first_liveness_timeout 10 2 30 secondes 1ere verification apres apparition peer START_FAILED
crash_detection_cycles_max 2 2 2 cycles Detection crash Non configurable
crash_detection_sla 20 2 20 secondes Derive: peer_poll_interval * 2 (defaut 10*2) Alerte critique
idempotency_ttl 24 1 168 heures Dedup commandes Rejet config

SLA temporels (transitions)

Parametre Defaut Min Max Configurabilite Comportement a expiration
escalade_wait_ttl 72h 1h 30j Oui Pas de nouvel etat en v1: reste ESCALADED, alerte repetee au Sovereign jusqu'a decision explicite
pause_ttl 24h 5min 7j Oui PAUSED persistant + alerte ; aucune reprise automatique
orphan_threshold derive N/A N/A Non (derive) orphan_threshold = 2 * peer_poll_interval; depassement -> CRASHED

5.2 Machine d'etats Ringbearer (avec transitions retour)

Etats officiels v1: STARTING, RUNNING, ESCALADED, PAUSED, DONE, ABORTED, CRASHED, START_FAILED.

  • STARTING :
  • Autorisees : STARTING->RUNNING, STARTING->START_FAILED
  • Regle STARTING->RUNNING : peer detecte et premiere liveness valide recue avant first_liveness_timeout.
  • Regle STARTING->START_FAILED : start_detection_timeout depasse ou peer detecte mais aucune liveness valide dans la fenetre.
  • RUNNING :
  • Autorisees : RUNNING->ESCALADED, RUNNING->PAUSED, RUNNING->DONE, RUNNING->ABORTED, RUNNING->CRASHED
  • ESCALADED :
  • Autorisees : ESCALADED->RUNNING (apres PO_RESPONSE valide), ESCALADED->ABORTED, ESCALADED->CRASHED
  • Retour couvert : RUNNING<->ESCALADED
  • PAUSED :
  • Autorisees : PAUSED->RUNNING (resume), PAUSED->ABORTED, PAUSED->CRASHED
  • Retour couvert : RUNNING<->PAUSED
  • DONE (terminal) : DONE->* INTERDITE.
  • ABORTED (terminal) : ABORTED->* INTERDITE.
  • CRASHED (terminal) : CRASHED->* INTERDITE.
  • START_FAILED (terminal) : START_FAILED->* INTERDITE.

Checklist machine a etats : - [x] Chaque etat a ses transitions sortantes autorisees/interdites. - [x] Chaque etat terminal declare explicitement -> * : INTERDITE. - [x] Couverture par invariants INV-293-09 et INV-293-10. - [x] Aucune reference a ESCALADE_PENDING ou ESCALADE_EXPIRED en etats v1.

5.3 Flux metier

1) start : garde quota -> idempotence -> validation format ; creation d'un Ringbearer si autorise.
2) start post-apparition : premiere verification de liveness obligatoire avant passage RUNNING.
3) status : consolidation de l'etat de tous les Ringbearers observes/attendus.
4) escalade : affichage de la file bloquante FIFO a traiter par le Sovereign.
5) respond : injection d'une decision humaine sur la plus ancienne escalade ouverte de la story cible (FIFO intra-story).
6) pause : gel intentionnel (RUNNING->PAUSED) sans perte de contexte.
7) resume : reprise (PAUSED->RUNNING) si gardes valides.
8) stop : arret intentionnel de la story (->ABORTED selon transitions autorisees).
9) Supervision continue : detection d'absence peer en <= 2 cycles et signalement CRASHED.
10) Reconnexion broker : tentative de reconnexion exponentielle bornee (1s, 2s, 4s, 8s, 10s max), mode degrade explicite tant que indisponible, reprise automatique des polls a la reconnexion.

5.4 Format de persistance .gov-lord-state.json (schema minimal v1)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "GovLordState",
  "type": "object",
  "required": ["version", "updated_at", "stories", "escalade_queue", "idempotency_cache"],
  "properties": {
    "version": { "type": "string", "const": "1.0" },
    "updated_at": { "type": "string", "format": "date-time" },
    "broker": {
      "type": "object",
      "required": ["status", "last_seen_at"],
      "properties": {
        "status": { "type": "string", "enum": ["UP", "DEGRADED", "DOWN"] },
        "last_seen_at": { "type": "string", "format": "date-time" }
      },
      "additionalProperties": false
    },
    "stories": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["story_id", "project_code", "state", "peer_id", "last_seen_at"],
        "properties": {
          "story_id": { "type": "string", "pattern": "^PD-[0-9]{1,4}$" },
          "project_code": { "type": "string", "enum": ["backend", "app", "site", "infra", "doc", "formal", "pixel-governance", "ia-governance"] },
          "state": { "type": "string", "enum": ["STARTING", "RUNNING", "ESCALADED", "PAUSED", "DONE", "ABORTED", "CRASHED", "START_FAILED"] },
          "peer_id": { "type": "string", "minLength": 1, "maxLength": 128 },
          "last_seen_at": { "type": "string", "format": "date-time" },
          "current_escalade_id": { "type": ["string", "null"] }
        },
        "additionalProperties": false
      }
    },
    "escalade_queue": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["escalade_id", "story_id", "created_at", "text", "status"],
        "properties": {
          "escalade_id": { "type": "string", "minLength": 1, "maxLength": 128 },
          "story_id": { "type": "string", "pattern": "^PD-[0-9]{1,4}$" },
          "created_at": { "type": "string", "format": "date-time" },
          "text": { "type": "string", "minLength": 1, "maxLength": 2000 },
          "status": { "type": "string", "enum": ["OPEN", "ANSWERED", "CLOSED"] }
        },
        "additionalProperties": false
      }
    },
    "idempotency_cache": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["key", "command", "story_id", "payload_hash", "result_hash", "expires_at"],
        "properties": {
          "key": { "type": "string", "pattern": "^[A-Za-z0-9:_-]{16,80}$" },
          "command": { "type": "string", "enum": ["start", "stop", "respond", "pause", "resume"] },
          "story_id": { "type": "string", "pattern": "^PD-[0-9]{1,4}$" },
          "payload_hash": { "type": "string", "minLength": 8, "maxLength": 128 },
          "result_hash": { "type": "string", "minLength": 8, "maxLength": 128 },
          "expires_at": { "type": "string", "format": "date-time" }
        },
        "additionalProperties": false
      }
    }
  },
  "additionalProperties": false
}

5bis. Diagrammes (si applicable)

Diagramme d'etat (>= 3 etats)

stateDiagram-v2
    [*] --> STARTING
    STARTING --> RUNNING: peer detecte + first liveness OK
    STARTING --> START_FAILED: timeout demarrage ou liveness KO

    RUNNING --> ESCALADED: message ESCALADE
    ESCALADED --> RUNNING: message PO_RESPONSE valide

    RUNNING --> PAUSED: commande PAUSE
    PAUSED --> RUNNING: commande RESUME

    RUNNING --> DONE: WORKFLOW_DONE
    RUNNING --> ABORTED: commande ABORT/STOP
    ESCALADED --> ABORTED: commande ABORT/STOP
    PAUSED --> ABORTED: commande ABORT/STOP

    RUNNING --> CRASHED: peer disparu (>2 polls)
    ESCALADED --> CRASHED: peer disparu (>2 polls)
    PAUSED --> CRASHED: peer disparu (>2 polls)

    DONE --> [*]
    ABORTED --> [*]
    CRASHED --> [*]
    START_FAILED --> [*]

Diagramme de sequence (>= 2 services)

sequenceDiagram
    participant S as Sovereign
    participant O as One Ring
    participant B as Broker
    participant R as Ringbearer

    S->>O: "/gov-lord start PD-293 ia-governance"
    O->>O: Gardes start: quota -> idempotence -> format
    O->>B: list_peers()
    B-->>O: peers[]
    O->>R: demande demarrage story_id=PD-293
    R->>B: register_peer(summary_text)
    R-->>O: STATUS_UPDATE (first liveness)
    O->>O: STARTING->RUNNING

    R->>O: ESCALADE{story_id, escalade_text, timestamp}
    O->>O: enqueue FIFO escalade_queue
    O-->>S: notification escalade la plus ancienne

    S->>O: "/gov-lord respond PD-293 <reponse>"
    O->>R: PO_RESPONSE{story_id, escalade_text=<reponse>}
    R-->>O: STATUS_UPDATE (reprise RUNNING)

    S->>O: "/gov-lord pause PD-293"
    O->>R: PAUSE
    S->>O: "/gov-lord resume PD-293"
    O->>R: RESUME

6. Cas d'erreur

ID Cas Reponse attendue
ERR-293-01 start avec quota atteint (>=5) Rejet explicite, aucune creation
ERR-293-02 start sur story_id deja active Rejet explicite idempotent
ERR-293-03 Message sans story_id valide Rejet + journalisation contractuelle
ERR-293-04 respond sans escalade active Rejet explicite
ERR-293-05 Commande hors scope One Ring Blocage explicite (INV-293-01)
ERR-293-06 Broker indisponible Etat degrade + alerte Sovereign + retries reconnection
ERR-293-07 Peer disparu au-dela de 2 cycles de polling Transition CRASHED + alerte
ERR-293-08 Reemission commande deja traitee (idempotency_key) Retour deterministe sans effet secondaire
ERR-293-09 idempotency_key rejouee avec payload different Rejet de conflit deterministe
ERR-293-10 Peer cree mais 1ere liveness absente avant first_liveness_timeout START_FAILED + cleanup session

6.1 Format de log de rejet (normatif)

Chaque rejet de validation/message doit produire une ligne JSON (JSONL) avec champs obligatoires:

{
  "timestamp": "2026-03-30T10:15:30Z",
  "peer_id": "peer-abc-123",
  "message_type": "ESCALADE",
  "reason": "story_id invalide (regex ^PD-[0-9]{1,4}$)",
  "story_id": "PD-99999",
  "event": "MESSAGE_REJECTED"
}

Champs obligatoires minimum: timestamp, peer_id, message_type, reason.

7. Criteres d'acceptation (testables)

ID Critere Observable
CA-01 One Ring liste les Ringbearers actifs status retourne la liste consolidee
CA-02 One Ring demarre un Ringbearer autonome Story visible en RUNNING avant start_detection_timeout et apres first liveness
CA-03 Escalade visible en moins de 5s P95 Mesure horodatee emission/reception
CA-04 respond debloque la story ciblee Transition ESCALADED->RUNNING observee
CA-05 One Ring refuse les actions hors scope Message de blocage explicite systematique
CA-06 Controle mobile operationnel Commandes /gov-lord utilisables depuis iPhone (test manuel)
CA-07 Crash Ringbearer detecte en <= 2 cycles (<=20s defaut) Transition ->CRASHED et alerte
CA-08 /gov inchange fonctionnel en Ringbearer Execution complete sans adaptation du workflow
CA-09 Limite de 5 appliquee 6e start rejete
CA-10 Validation de schema messages stricte Message invalide rejete sans effet de bord
CA-11 Multi-escalade simultanee preserve l'ordre Traitement FIFO sans ecrasement
CA-12 Reconnexion broker robuste Mode degrade explicite puis reprise automatique

8. Scenarios de test (Given / When / Then)

  • T-01 Start nominal
    Given quota disponible et broker operationnel, When start PD-XXX, Then etat RUNNING avant timeout et apres first liveness valide.

  • T-02 Limite capacite
    Given 5 actifs, When start 6e story, Then rejet explicite sans nouveau peer.

  • T-03 Escalade bloquante
    Given story en RUNNING, When message ESCALADE, Then story en ESCALADED jusqu'a PO_RESPONSE.

  • T-04 Reponse escalade
    Given story ESCALADED, When respond valide, Then retour RUNNING et reprise observable.

  • T-05 Commande hors scope
    Given One Ring actif, When demande lecture/ecriture/build/Jira, Then blocage explicite sans execution.

  • T-06 Crash detection
    Given peer present puis absent > 2 cycles, When cycle de supervision, Then etat CRASHED + alerte en <=20s (defaut).

  • T-07 Idempotence commande
    Given idempotency_key deja traitee, When repetition meme payload, Then meme resultat sans doublon d'action.

  • T-08 Mobile control (manuel)
    Given session One Ring accessible mobile, When status/escalade/respond/pause/resume, Then memes resultats fonctionnels que desktop.

  • T-09 Pause/Resume
    Given story en RUNNING, When pause puis resume, Then transitions RUNNING->PAUSED->RUNNING observees.

  • T-10 Multi-escalade FIFO
    Given 3 escalades ouvertes pour une story, When respond est emis 3 fois, Then traitement dans l'ordre d'arrivee sans remplacement.

  • T-11 Reconnexion broker
    Given broker indisponible puis restaure, When boucle de retries, Then mode degrade puis reprise automatique et reconciliation.

9. Hypotheses explicites

ID Hypothese Impact si faux
H-293-01 claude-peers-mcp est disponible et compatible avec version ^1.x. Blocage orchestration multi-sessions.
H-293-02 /remote-control est utilisable sur iPhone (Safari/claude.ai/code). Perte du cas d'usage mobile, pilotage desktop uniquement.
H-293-03 Le broker local supporte 1 One Ring + 5 Ringbearers sans instabilite majeure. Reduction du quota max ou reconfiguration infra.
H-293-04 Le protocole messages requis est supporte par les peers sans adaptation de /gov. Necessite d'une story separee de compatibilite.

10. Points a clarifier

10.1 Contraintes techniques (stack projet cible)

  • Projet cible de l'artefact : ProbatioVault-ia-governance.
  • Stack reelle constatee : Shell (Bash) + Markdown + YAML/JSON de configuration + outillage Python.
  • Dependance externe : claude-peers-mcp@^1.x (npm) comme composant d'integration.
  • Contrainte de conformite : One Ring reste une couche d'orchestration textuelle, sans logique applicative projet.

10.2 Questions ouvertes

ID Question Impact
Q-293-01 Disponibilite exacte de /remote-control en contexte mobile web (et limites eventuelles). Peut degrader CA-06 (manuel).
Q-293-02 Format normatif exact de peer_id expose par list_peers au-dela de "printable UTF-8". Ajustement mineur du validateur §5.1.
Q-293-03 Semantique officielle de pause/reprise cote sessions autonomes. Peut contraindre RUNNING<->PAUSED.
Q-293-04 Politique de persistance locale autorisee pour l'etat consolide One Ring. Impact auditabilite/recuperation apres crash.
Q-293-05 Politique de purge de idempotency_cache au redemarrage. Impact stockage local et forensique.

References

  • Epic : Reference epique non fournie dans l'entree (donnee manquante).
  • JIRA : PD-293
  • Repos concernes : ProbatioVault-ia-governance (orchestrateur), plus repos cibles des stories (backend, app, infra, site, etc.).
  • Documents associes : Besoin PD-293 (texte fourni), claude-peers-mcp (github.com/louislva/claude-peers-mcp).