diff --git a/.codex/agents/explorer.toml b/.codex/agents/explorer.toml index 25db5db3..3e1ebf39 100644 --- a/.codex/agents/explorer.toml +++ b/.codex/agents/explorer.toml @@ -7,6 +7,7 @@ sandbox_mode = "read-only" developer_instructions = """ Cartographie uniquement les chemins réellement pertinents et retourne des références de fichiers, symboles, preuves et incertitudes. Ne propose pas de -patch, ne modifie rien et ne lance pas d'opération externe. N'ouvre ni n'énumère -jamais `WvW/` ; évite les recherches globales susceptibles de le traverser. +patch, ne modifie rien et ne lance pas d'opération externe. N'ouvre, n'énumère, +n'indexe ni ne modifie jamais `WvW/` ; évite les recherches globales susceptibles +de le traverser. """ diff --git a/.codex/agents/reviewer.toml b/.codex/agents/reviewer.toml index 2975310f..96644a7b 100644 --- a/.codex/agents/reviewer.toml +++ b/.codex/agents/reviewer.toml @@ -9,5 +9,6 @@ Reste indépendant de l'auteur et en lecture seule. Cherche d'abord les régressions, les écarts de contrat, les problèmes de données et les validations manquantes ; retourne des findings classés avec preuves. Ne corrige aucun fichier. Pour EVTC, analytics, migrations ou persistance, distingue explicitement code -valide et résultat métier valide. N'ouvre ni n'énumère jamais `WvW/`. +valide et résultat métier valide. N'ouvre, n'énumère, n'indexe ni ne modifie +jamais `WvW/`. """ diff --git a/.codex/agents/specialist.toml b/.codex/agents/specialist.toml index c0a2e533..a924049b 100644 --- a/.codex/agents/specialist.toml +++ b/.codex/agents/specialist.toml @@ -9,5 +9,6 @@ Interviens seulement sur une question explicitement délimitée qui dépasse le raisonnement courant : sémantique EVTC/EI, causalité analytique, migration, modèle de données ou architecture. Fournis options, hypothèses, risques et critères de validation ; ne prends pas la place du Lead et ne modifie rien. -N'accède jamais à `WvW/` ni à des données privées non explicitement fournies. +N'ouvre, n'énumère, n'indexe ni ne modifie jamais `WvW/` ou des données privées +non explicitement fournies. """ diff --git a/docs/agentic/backlog.md b/docs/agentic/backlog.md index d5de3779..11a2f29c 100644 --- a/docs/agentic/backlog.md +++ b/docs/agentic/backlog.md @@ -2,9 +2,8 @@ ## Dettes de revue — Phase 5 -**Statut : à traiter avant toute promotion au-delà du Level 1 et à réévaluer en -Phase 6.** Elles ne bloquent pas l'intégration de l'infrastructure minimale, -mais restent des prérequis à une autonomie élargie. +**Statut : ajustements Phase 6 validés.** Ils restent à réévaluer avant toute +promotion au-delà du Level 1. 1. Renforcer les tests sémantiques des garde-fous : indépendance du Reviewer, confidentialité `WvW/`, fallback indisponible, handoff et reprise. @@ -12,6 +11,9 @@ mais restent des prérequis à une autonomie élargie. utilisation réelle. 3. Compléter le protocole worktree avec une procédure explicite de conflit et d'abandon. +4. Ajouter une assertion ciblée du checkpoint Phase 6 : un état Herdr ne permet + jamais seul le nettoyage ; résultat récupéré et absence de travail utile, + diff, commit ou artefact non intégré doivent être vérifiés indépendamment. ## Initiative prioritaire — Git/GitHub Governance & Delivery Architecture diff --git a/docs/agentic/current-state.md b/docs/agentic/current-state.md index 3a29366a..f5ce6f4c 100644 --- a/docs/agentic/current-state.md +++ b/docs/agentic/current-state.md @@ -1,45 +1,50 @@ # État courant agentique -## Phase 5 — Infrastructure minimale +## Phase 6 — Validation réelle de l'infrastructure -- **Statut :** revue PASS conditionnel acceptée ; intégration Phase 5 en cours. - Phase 6 interdite sans accord. -- **Base :** `7a347f5dfe2c64cbcd0998e550477b64908448c9` sur `main`. -- **Objectif :** rôle Lead stable, profils Codex, politiques de routing, - communication, autonomie, worktrees/Herdr et reprise persistante. +- **Statut :** corrections des findings Phase 6 réalisées ; livrable candidat + à une seconde review indépendante. Phase 7 interdite sans accord. +- **Base :** `72ec29fd4eb71c41f02ff2bf1ccbff7c4468dc9c` sur `main`. +- **Objectif :** démontrer les profils et politiques dans Codex/Herdr réels, + sans développement fonctionnel. - **Profil Lead initial :** Codex / `gpt-5.6-terra` / `medium`. - **Autonomie :** Level 1 pour tous les domaines. - **Fallback :** non configuré et non déclaré opérationnel. - **Confidentialité :** `WvW/` interdit à toute lecture, énumération, indexation, modification ou ajout Git sans autorisation explicite. +- **Prérequis utilisateur local :** l'entrée de confiance Codex du dépôt est + présente et valide dans la configuration utilisateur ; son contenu hors de + cette entrée ne fait pas partie du dépôt. ## Dettes acceptées de la revue Phase 5 -À traiter avant toute promotion au-delà du Level 1 et à réévaluer pendant la -Phase 6 : tests sémantiques des garde-fous (Reviewer, `WvW/`, fallback, -handoff et reprise), critères objectivables pour Ultra, et procédure worktree -de conflit ou d'abandon. +Ajustements réalisés, en attente de seconde review indépendante avant toute +promotion au-delà du Level 1 : tests sémantiques des garde-fous (Reviewer, +`WvW/`, fallback, handoff et reprise), critères objectivables pour Ultra, et +procédure worktree de conflit ou d'abandon. ## Validations - PASS — `uv run pytest tests/scripts/test_agentic_infrastructure.py - tests/scripts/test_bmad_framework.py` : 10 tests. + tests/scripts/test_bmad_framework.py` : 11 tests. - PASS — Ruff ciblé sur les deux tests d'infrastructure. -- PASS — `codex --strict-config exec --help` : configuration sans clé inconnue. +- PASS — Codex réel : Lead `gpt-5.6-terra` / `medium`, interaction read-only, + profils custom et handoff Explorer → Reviewer validés. +- PASS — Herdr 0.8.0 : socket, pane temporaire, Codex read-only, état et + résultat récupérés, puis pane supprimé. +- PASS — reprise fresh-context : une session Codex éphémère reconstruit Phase + 6, Level 1, confidentialité, fallback, dettes et prochaine action depuis les + documents versionnés. - PASS — interfaces Codex/Herdr : CLI Codex 0.148.0 ; Herdr 0.8.0 expose les commandes d'intégration et de worktrees documentées. - PASS — résolveurs `resolve_config.py` et `resolve_customization.py` BMAD. - PASS — `git diff --check`. -- NOT RUN — conversation Codex réelle, spawn et routing de bout en bout : la - couche `.codex/config.toml` est chargée seulement lorsqu'un client marque le - dépôt fiable ; aucun appel modèle payant ni modification de configuration - utilisateur hors dépôt n'a été effectué. -- BLOCKED — statut live Herdr et flux macro : le socket serveur est inaccessible - depuis ce sandbox ; aucun worktree ou pane n'a été créé. +- BLOCKED — spawn dans `codex exec --ephemeral` : ce mode ne fournit pas de + thread parent au routeur. Le même test persistant read-only est passé. +- NOT RUN — worktree temporaire : non requis pour le flux Herdr sans écriture. ## Reprise -Avant la validation fonctionnelle de Phase 6, ouvrir le dépôt dans un client -Codex fiable, vérifier qu'il charge `.codex/config.toml`, puis tester un seul -cas read-only du Lead. Ne pas activer Herdr, créer de worktree, modifier les -niveaux d'autonomie ou configurer un fallback sans accord explicite. +Faire la seconde review indépendante read-only des ajustements Phase 6. Ne pas +créer de worktree d'écriture, modifier les niveaux d'autonomie ou configurer un +fallback sans accord explicite. diff --git a/docs/agentic/phase6-validation-2026-08-20.md b/docs/agentic/phase6-validation-2026-08-20.md new file mode 100644 index 00000000..90599795 --- /dev/null +++ b/docs/agentic/phase6-validation-2026-08-20.md @@ -0,0 +1,48 @@ +# Checkpoint — Phase 6 : validation réelle + +## Statut + +Validations Level 1 réalisées ; corrections de revue appliquées et livrable +candidat à une seconde review indépendante. Phase 7 interdite sans accord +explicite. + +## Preuves obtenues + +- Le dépôt est reconnu fiable par Codex ; la configuration de projet applique + `gpt-5.6-terra` / `medium`. +- Le Lead a traité une idée analytique en lecture seule et a demandé un accord + avant toute écriture. +- Explorer et Reviewer ont été créés comme subagents réels, avec handoff puis + verdict indépendant ; les trois autres profils ont aussi été reconnus par le + runtime dans un test séquentiel read-only. +- Herdr 0.8.0 a exécuté un flux contrôlé pane → Codex read-only → résultat. + L'état `idle` a seulement signalé la fin apparente de l'orchestration ; avant + la fermeture du pane temporaire, la session a vérifié indépendamment que le + résultat était récupéré et qu'aucun diff, commit, artefact ou travail utile ne + risquait d'être perdu. Cette vérification, et non l'état Herdr, a autorisé le + nettoyage du pane. +- Une session Codex fraîche et éphémère a reconstruit correctement la phase, + les limites Level 1, la confidentialité, le fallback et la prochaine action + depuis les documents versionnés seuls. +- Les trois dettes Phase 5 ont reçu un ajustement ciblé : invariants de test + structurés pour les profils, le handoff, le fallback, le parallélisme et + Ultra ; critères d'Ultra ; procédure worktree de conflit/abandon/récupération. + +## Limites et risques + +- `codex exec --ephemeral` ne peut pas accueillir un spawn : le routeur ne + trouve pas de thread parent. Utiliser une session persistante read-only pour + les délégations de validation. +- Aucun worktree n'a été créé : il n'est pas nécessaire sans flux d'écriture. +- Le fallback reste non opérationnel ; une indisponibilité GPT impose arrêt et + checkpoint, jamais un provider implicite. +- Les règles qui dépendent du comportement d'un client (sandbox, lecture seule, + absence de fan-out imbriqué) restent aussi couvertes par les essais live ; les + tests du dépôt protègent leurs contrats déclaratifs, pas une simulation du + runtime. + +## Prochaine action + +Faire une seconde review indépendante read-only du livrable Phase 6, y compris +les ajustements de garde-fous. Toute Phase 7 ou autonomie supérieure au Level 1 +reste soumise à accord explicite. diff --git a/docs/agentic/routing-policy.md b/docs/agentic/routing-policy.md index c34e62f0..b3fa22b9 100644 --- a/docs/agentic/routing-policy.md +++ b/docs/agentic/routing-policy.md @@ -33,6 +33,21 @@ La configuration projet utilise les efforts portables `low`, `medium`, `high` et `xhigh`. `max` et Ultra ne sont pas des valeurs par défaut de fichier : ils requièrent une vérification du client disponible et une justification écrite. +## Critères d'Ultra + +Ultra n'est autorisable que si le Lead consigne, avant le lancement, les quatre +preuves suivantes : + +1. au moins trois sous-problèmes indépendants, sans fichier ni contrat partagé ; +2. un gain attendu mesurable (temps de décision ou couverture de trois analyses) + qu'un seul agent ne peut pas produire dans le délai retenu ; +3. pourquoi des subagents Codex read-only ou Herdr/worktrees ne répondent pas au + besoin avec un coût et un risque plus faibles ; +4. le modèle, le niveau réellement disponibles et un plafond de coût approuvé. + +Sans ces quatre éléments, Ultra reste interdit. Son fan-out remplace toutes les +autres couches de parallélisme pendant la tâche. + ## Parallélisme Une seule couche possède le fan-out : diff --git a/docs/agentic/worktrees-herdr.md b/docs/agentic/worktrees-herdr.md index 9fa625f6..ef599780 100644 --- a/docs/agentic/worktrees-herdr.md +++ b/docs/agentic/worktrees-herdr.md @@ -18,6 +18,30 @@ recouvrants. 5. Le Lead intègre, demande une review indépendante, vérifie les validations, puis seulement nettoie branche et worktree après preuve d'intégration. +## Conflit, abandon et récupération + +- **Handoff :** il ne contient qu'une synthèse, l'état, les décisions, les + validations et des références. Un patch ou un diff reste un artefact externe + (branche, commit ou fichier archivé) référencé par le handoff ; il n'est + jamais recopié intégralement dans celui-ci. +- **Conflit :** le worker s'arrête, gèle son worktree, enregistre les fichiers + concernés, validations et référence d'artefact dans son handoff, puis le Lead + décide de l'intégration. Aucun autre worker ne résout le conflit dans ce + worktree ni ne devient propriétaire de ses fichiers sans handoff explicite. +- **Abandon :** conserver le worktree et son diff jusqu'à ce que le Lead ait + confirmé qu'aucun travail utile ne doit être transféré. Documenter la raison, + le propriétaire, la référence d'artefact et la prochaine action dans le + checkpoint. +- **Récupération :** une nouvelle session relit le handoff et le checkpoint, + localise l'artefact référencé, choisit reprise ou abandon explicite, puis + exécute les validations prévues. +- **Nettoyage sûr :** seulement après intégration prouvée ou abandon consigné, + vérifier que le worktree est propre ou que son diff est archivé et référencé, + puis supprimer worktree et branche par la procédure Git validée. Les états + Herdr `done`, `idle` ou `terminated` sont seulement des signaux + d'orchestration : ils ne sont jamais une preuve suffisante d'intégration ni + une autorisation de suppression. + Ne jamais partager une branche entre worktrees. Ne jamais laisser un worker modifier le même fichier ou contrat qu'un autre. `WvW/` ne doit être copié, listé ou rendu accessible à aucun worktree agentique. diff --git a/tests/scripts/test_agentic_infrastructure.py b/tests/scripts/test_agentic_infrastructure.py index a188a37d..a6e9a5bd 100644 --- a/tests/scripts/test_agentic_infrastructure.py +++ b/tests/scripts/test_agentic_infrastructure.py @@ -1,3 +1,4 @@ +import re import tomllib from pathlib import Path @@ -14,6 +15,42 @@ "specialist": ("gpt-5.6-sol", "high"), } +PRIVATE_CORPUS_ACTIONS = { + "lecture": (r"ouvr", r"lecture", r"accèd"), + "énumération": (r"énum",), + "indexation": (r"index",), + "modification": (r"modifi",), +} +REQUIRED_HANDOFF_FIELDS = { + "task_id", + "intent", + "risk", + "objective", + "acceptance", + "context_refs", + "allowed_paths", + "forbidden", + "validation", + "output", +} + + +def section(document: str, heading: str) -> str: + match = re.search( + rf"^## {re.escape(heading)}\n(.*?)(?=^## |\Z)", document, re.MULTILINE | re.DOTALL + ) + assert match, f"Section absente : {heading}" + return match.group(1) + + +def handoff_fields(document: str) -> dict[str, str]: + match = re.search(r"```yaml\n(.*?)```", document, re.DOTALL) + assert match, "Enveloppe YAML de handoff absente" + return { + key: value.strip() + for key, value in re.findall(r"^(\w+):\s*(.+)$", match.group(1), re.MULTILINE) + } + def load_toml(path: Path) -> dict: return tomllib.loads(path.read_text()) @@ -49,6 +86,22 @@ def test_custom_agent_profiles_are_complete_and_constrained() -> None: assert profiles["specialist"]["sandbox_mode"] == "read-only" assert profiles["implementer"]["sandbox_mode"] == "workspace-write" + for name, profile in profiles.items(): + instructions = profile["developer_instructions"] + assert "WvW/" in instructions, name + for action, patterns in PRIVATE_CORPUS_ACTIONS.items(): + assert any(re.search(pattern, instructions, re.IGNORECASE) for pattern in patterns), ( + f"{name} doit interdire {action} du corpus privé" + ) + + assert ( + "ne transforme jamais une discussion en écriture" + in profiles["gw2analytics_lead"]["developer_instructions"] + ) + assert "Reste indépendant de l'auteur" in profiles["reviewer"]["developer_instructions"] + assert "Ne corrige aucun fichier" in profiles["reviewer"]["developer_instructions"] + assert profiles["reviewer"]["sandbox_mode"] != profiles["implementer"]["sandbox_mode"] + def test_agentic_docs_define_level_one_recovery_and_governance() -> None: required = { @@ -72,3 +125,58 @@ def test_agentic_docs_define_level_one_recovery_and_governance() -> None: in (AGENTIC_DOCS / "routing-policy.md").read_text() ) assert "énumérer" in (ROOT / "AGENTS.md").read_text() + + +def test_agentic_guardrails_cover_fallback_handoff_and_ultra() -> None: + config = load_toml(CODEX_DIR / "config.toml") + profiles = {path.stem: load_toml(path) for path in AGENTS_DIR.glob("*.toml")} + architecture = (AGENTIC_DOCS / "architecture.md").read_text() + communication = (AGENTIC_DOCS / "communication-protocol.md").read_text() + routing = (AGENTIC_DOCS / "routing-policy.md").read_text() + worktrees = (AGENTIC_DOCS / "worktrees-herdr.md").read_text() + + # Les protections documentaires complètent les modes sandbox ; elles ne + # remplacent pas les essais live read-only de la Phase 6. + assert "Aucun fallback n'est configuré" in architecture + assert set(config) == {"model", "model_reasoning_effort", "agents"} + assert {profile["model"] for profile in profiles.values()} <= { + "gpt-5.6-luna", + "gpt-5.6-terra", + "gpt-5.6-sol", + } + + handoff = handoff_fields(communication) + assert set(handoff) >= REQUIRED_HANDOFF_FIELDS + assert handoff["forbidden"].startswith("WvW") + assert all(handoff[field] for field in REQUIRED_HANDOFF_FIELDS) + assert "jamais un dump" in communication + assert "done`, `idle` ou `unknown` exigent toujours diff, validations et" in communication + assert "review avant changement d'état canonique" in communication + + ultra = section(routing, "Critères d'Ultra") + criteria = re.findall(r"^([1-4])\.\s+(.+?)(?=\n\d\. |\n\n|\Z)", ultra, re.MULTILINE | re.DOTALL) + assert [number for number, _ in criteria] == ["1", "2", "3", "4"] + assert "sous-problèmes indépendants" in criteria[0][1] + assert "gain attendu mesurable" in criteria[1][1] + assert "subagents Codex read-only ou Herdr/worktrees" in criteria[2][1] + assert "plafond de coût approuvé" in criteria[3][1] + assert "Ultra reste interdit" in ultra + assert re.search(r"fan-out remplace toutes les\s+autres couches", ultra) + + parallelism = section(routing, "Parallélisme") + strategies = [ + strategy.strip() for strategy in re.findall(r"^\d\.\s+([^:]+):", parallelism, re.MULTILINE) + ] + assert strategies == [ + "tâche simple", + "exploration ou review réellement indépendantes", + "deux flux d'écriture réellement indépendants", + "Ultra exceptionnel", + ] + assert "ne sont jamais imbriqués" in parallelism + + recovery = section(worktrees, "Conflit, abandon et récupération") + for stage in ("**Conflit :**", "**Abandon :**", "**Récupération :**", "**Nettoyage sûr :**"): + assert stage in recovery + assert "référence" in recovery + assert "Herdr" in recovery and "preuve suffisante" in recovery