Aller au contenu

PD-293 — Décomposition multi-agents (step 6a)

Le code-contracts.yaml existe → workflow multi-agents activé. Voici la décomposition en tâches agents, cohérente avec les 4 waves du plan.


Wave 1 — Modules fondamentaux (parallélisables)

Agent 1 : agent-fsm → C1 state-machine

Fichier : scripts/lib/lord-state-machine.sh

Mission : Implémenter la machine d'états Ringbearer §5.2 avec matrice de transitions en declare -A.

Interfaces contractuelles : - lord_transition(story_id, from_state, to_state)OK | REJECTED - lord_is_terminal(state)true | false
- lord_allowed_transitions(from_state) → liste des to_state autorisés

Matrice des 8 états × transitions autorisées :

From To autorisés
STARTING RUNNING, START_FAILED
RUNNING ESCALADED, PAUSED, DONE, ABORTED, CRASHED
ESCALADED RUNNING, ABORTED, CRASHED
PAUSED RUNNING, ABORTED, CRASHED
DONE ∅ (terminal)
ABORTED ∅ (terminal)
CRASHED ∅ (terminal)
START_FAILED ∅ (terminal)

Invariants couverts : INV-293-09, INV-293-10, INV-293-06

Tests unitaires ciblés : TC-INV-01 (matrice 8×8 exhaustive), TC-INV-02 (terminaux immuables)

Approche alternative considérée : case/esac imbriqués — écarté car verbose et risque d'oubli d'une paire. Le declare -A offre un lookup O(1) et une matrice vérifiable par diff avec §5.2.


Agent 2 : agent-validator → C2 validator

Fichier : scripts/lib/lord-validator.sh

Mission : Implémenter les 8 validateurs de champs §5.1 + le logger de rejet §6.1.

Interfaces contractuelles : - lord_validate_story_id(value) → regex ^PD-[0-9]{1,4}$ - lord_validate_project_code(value) → enum 8 valeurs - lord_validate_message_type(value) → enum 8 valeurs - lord_validate_peer_id(value) → `[\p{C}

]{1,128}$(viagrep -P) -lord_validate_timestamp(value)→ regex stricte UTC Z :\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z$-lord_validate_escalade_text(value)[^\p{C}]{1,2000}$(viagrep -P) -lord_validate_summary_text(value)[\p{C}]{1,512}$-lord_validate_idempotency_key(value)^[A-Za-z0-9:_-]{16,80}$-lord_log_rejection(timestamp, peer_id, message_type, reason, story_id, event)` → JSONL

Décisions clés : - 6/8 validateurs en regex Bash natif [[ =~ ]] (zéro dépendance) - 2 validateurs (peer_id, escalade_text) nécessitent grep -P (GNU grep) pour \p{C} (H-TECH-04) - Convention rejets Sovereign : peer_id="sovereign", message_type=<COMMAND_NAME> - Timestamp : strictement Z, tout offset non-zero rejeté (H-TECH-08)

Invariant couvert : INV-293-05

Tests unitaires ciblés : TC-NOM-10, TC-ERR-03, TC-NEG-01..TC-NEG-10


Wave 2 — Modules infrastructure (parallélisables, dépendent de C2)

Agent 3 : agent-broker → C3 broker-adapter

Fichier : scripts/lib/lord-broker.sh

Mission : Couche d'abstraction claude-peers-mcp — listing, envoi, réception, health check, reconnexion.

Interfaces contractuelles : - lord_broker_list_peers() → JSON array | ERROR - lord_broker_send_message(peer_id, message_type, payload) → OK | ERROR - lord_broker_get_messages(peer_id) → JSON array | ERROR - lord_broker_status() → UP | DEGRADED | DOWN - lord_broker_reconnect() → OK | STILL_DEGRADED

Mécanisme de reconnexion : backoff exponentiel borné 1s → 2s → 4s → 8s → 10s (plafond). Mode DEGRADED explicite tant qu'indisponible.

Invariant couvert : INV-293-02

Point critique : C3 est l'unique point de communication avec les Ringbearers. C5 ne doit jamais contourner C3. L'implémentation réelle appelle claude-peers-mcp via MCP tools ; le mock (pour tests) simule via fichiers JSON temporaires.

Tests ciblés : TC-ERR-06, TC-ERR-10


Agent 4 : agent-persistence → C4 persistence

Fichier : scripts/lib/lord-persistence.sh

Mission : CRUD .gov-lord-state.json (schema §5.4), FIFO escalades, cache idempotency, audit JSONL.

Interfaces contractuelles : - lord_state_read() / lord_state_write(json) — écriture atomique (tmp + mv) + flock - lord_count_active_stories() → integer (stories non-terminales) - lord_create_story(story_id, project_code, state, peer_id) / lord_update_story(story_id, updates) / lord_get_story(story_id) - lord_enqueue_escalade(story_id, escalade_id, text, timestamp) — append FIFO - lord_dequeue_oldest_escalade(story_id) — retire le plus ancien OPEN de la story - lord_count_open_escalades(story_id) → integer - lord_check_idempotency(key, payload_hash) → NEW | REPLAY(result_hash) | CONFLICT - lord_record_idempotency(key, command, story_id, payload_hash, result_hash, ttl) - lord_purge_expired_idempotency() → count_purged

Décisions clés : - Fichier .gov-lord-state.json à la racine du repo (état runtime global, .gitignore) - Audit .gov-lord-audit.jsonl — jamais de escalade_text brut (RGPD), seulement escalade_id - Hash idempotency : sha256sum sur arguments normalisés - Valeur sentinelle "pending-{story_id}" pour peer_id en STARTING (H-TECH-09) - jq obligatoire (H-TECH-05) pour toute manipulation JSON

Invariants couverts : INV-293-13 (FIFO), INV-293-12 (idempotency), INV-293-04 (quota via count)

Tests ciblés : TC-NOM-02, TC-NOM-08, TC-NOM-13, TC-ERR-01, TC-ERR-08, TC-ERR-11

Pièges d'implémentation : - mv atomique uniquement si même filesystem → .tmp dans le même répertoire - flock autour des write pour protéger supervision loop vs commandes CLI concurrentes - Purge idempotency au démarrage ET opportuniste à chaque cycle


Wave 3 — Orchestrateur (séquentiel, dépend de C1+C2+C3+C4)

Agent 5 : agent-orchestrator → C5 orchestrator

Fichier : scripts/gov-lord.sh

Mission : Logique métier des 7 commandes, boucle de supervision, réconciliation, détection crash.

Interfaces contractuelles : - lord_start(story_id, project_code, idempotency_key) — gardes : quota → idempotence → format → doublon story - lord_status(story_id?) — consolidation C4 + C3 - lord_escalade(story_id?) — affichage file FIFO - lord_respond(story_id, response_text, idempotency_key) — gardes : idempotence → format - lord_pause(story_id, reason?, idempotency_key) / lord_resume(story_id, idempotency_key) / lord_stop(story_id, reason?, idempotency_key) - lord_supervision_loop() — boucle infinie avec sleep $interval & wait $! (trap-safe)

Boucle de supervision (chaque cycle) : 1. Vérifier broker status 2. Lister peers observés 3. Pour chaque story non-terminale : vérifier liveness / missed_polls / messages entrants 4. Réconciliation état local vs peers observés (INV-293-11) 5. Traitement messages (ESCALADE, WORKFLOW_DONE, STATUS_UPDATE, GATE_RESULT) 6. Purge idempotency expirée

Règle multi-escalade : transition ESCALADED→RUNNING uniquement quand toutes les escalades OPEN de la story sont traitées (lord_count_open_escalades == 0).

Invariants couverts : INV-293-01, INV-293-08, INV-293-11, INV-293-14

Pièges : - sleep N & wait $! pour réactivité SIGINT/SIGTERM - Lancement Ringbearer : unset CLAUDECODE && /Users/loic/.local/bin/claude -p "/gov PD-XX projet" & - Aucun import de jira-api.sh, state.sh ou module /gov existant


Wave 4 — Interface + Tests (parallélisables)

Agent 6 : agent-skill → C6 commands

Fichier : .claude/commands/gov-lord.md

Mission : Skill Claude Code /gov-lord — parsing arguments CLI, dispatch vers C5, whitelist 7 commandes.

Commandes :

/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> <réponse> [--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>]

Toute autre commande → message de blocage explicite : "Commande non reconnue. Commandes disponibles: start, status, escalade, respond, pause, resume, stop"

Invariant couvert : INV-293-01, CA-05


Agent 7 : agent-tests → C7 tests

Fichiers : tests/lord/**/*.bats + tests/lord/mocks/** + tests/lord/fixtures/**

Mission : Suites BATS couvrant tous les TC-* contractuels.

Structure :

tests/lord/
  helpers/         # setup/teardown, mock broker, fixtures loader
  mocks/           # mock-broker.sh (simule list_peers, send_message, get_messages)
  fixtures/        # JSON fixtures (.gov-lord-state.json préremplis par scénario)
  state-machine.bats    # TC-INV-01, TC-INV-02 (matrice 8×8)
  validator.bats        # TC-NOM-10, TC-ERR-03, TC-NEG-01..10
  persistence.bats      # TC-NOM-02, TC-NOM-08, TC-NOM-13, TC-ERR-01, TC-ERR-08, TC-ERR-11
  orchestrator.bats     # TC-NOM-01, TC-NOM-03..06, TC-NOM-07, TC-NOM-09, TC-NOM-12, TC-NOM-14
  integration.bats      # TC-ERR-02, TC-ERR-04..07, TC-ERR-09..10, TC-INV-03
  negative.bats         # TC-NEG-01..10 (validation dédiée)
  non-regression.bats   # TC-NR-01..04

Mock broker : Fonctions shell simulant lord_broker_* via fichiers JSON temporaires. Permet l'injection de scénarios (peer absent, broker DOWN, messages entrants).

Couverture : 40+ tests contractuels + additionnels pour ≥ 80%.


Matrice de couverture Test-ID → fichier de test

Test ID Fichier BATS
TC-INV-01, TC-INV-02 state-machine.bats
TC-NOM-10, TC-ERR-03, TC-NEG-01..10 validator.bats + negative.bats
TC-NOM-02, TC-NOM-08, TC-NOM-13, TC-ERR-01, TC-ERR-08, TC-ERR-11 persistence.bats
TC-NOM-01, TC-NOM-03..06, TC-NOM-07, TC-NOM-09, TC-NOM-12, TC-NOM-14 orchestrator.bats
TC-ERR-02, TC-ERR-04..07, TC-ERR-09..10, TC-INV-03 integration.bats
TC-NR-01..04 non-regression.bats
TC-MAN-01 Manuel (hors BATS)

Prérequis d'installation (vérification au démarrage C5)

Dépendance Commande de vérification Installation
Bash 5.x bash --version \| grep -q "version 5" brew install bash
jq command -v jq brew install jq
GNU grep (-P) echo "test" \| grep -P "\w" 2>/dev/null brew install grep
bats-core command -v bats brew install bats-core
claude-peers-mcp Vérifier MCP config H-293-01

Fichiers à créer (aucune modification d'existant)

Fichier Agent Nouveau
scripts/lib/lord-state-machine.sh agent-fsm
scripts/lib/lord-validator.sh agent-validator
scripts/lib/lord-broker.sh agent-broker
scripts/lib/lord-persistence.sh agent-persistence
scripts/gov-lord.sh agent-orchestrator
.claude/commands/gov-lord.md agent-skill
tests/lord/** agent-tests
.gov-lord-state.json runtime (C4) ✓ (.gitignore)
.gov-lord-audit.jsonl runtime (C4) ✓ (.gitignore)

Vérification INV-293-07 : Aucun fichier existant du workflow /gov n'est modifié par PD-293.