Doc : cahier de la v0.9.0, hooks stricts et contrat de MIAM pour la délégation
- Ajout de `docs/hooks-delegation.md` - État de MIAM et des hooks, raisons de leur échec - Treize hooks avec priorités, découpage atomique (`.claude/miam-plan.json`) - Contrôle automatique des réponses (`controle`, `verdict`) et reprise sans suivi répété - Section 11 : contrat livré par MIAM (PH2-H11) : paramètres, réponses et états `fin`, commande `reprise` - Sept bouts à coder dans le plugin pour la v0.9.0, réveil après 11 min vérifié Aucun code : spécification, seuils à confirmer.
This commit is contained in:
1 parent
7841bcd89f
commit
7d3885a735
2 files changed
+412
No files matched your search
@@ -0,0 +1,64 @@
|
||||
# À faire pour la prochaine version du plugin
|
||||
|
||||
Notes issues de la session MIAM du 2026-11-01. Rien n'est encore appliqué.
|
||||
|
||||
## 1. Autoriser le repli sur un subagent Haiku (demande de l'utilisateur)
|
||||
|
||||
`plugins/miam/skills/deleguer/SKILL.md`, ligne 24, dit « Pas de subagent ». Vérifier aussi le texte équivalent injecté
|
||||
par les hooks et la documentation (`docs/agent-delegue.md`, `plugins/miam/README.md`).
|
||||
|
||||
Nouvelle règle, ordre de délégation :
|
||||
|
||||
1. MIAM d'abord, pour les tâches de son champ.
|
||||
2. Subagent `model: "haiku"`, sans demander, pour une tâche longue et simple si MIAM est arrêté ou mal adapté : lire un
|
||||
gros document ou une longue sortie pour en tirer peu, recherche large dans le code, tests répétitifs.
|
||||
3. Claude principal pour ce qui demande le contexte de la conversation, pour la sécurité et pour la dépense.
|
||||
|
||||
Pas de subagent pour une petite tâche : un `grep` ou une lecture directe coûte moins cher qu'un agent qui repart de zéro.
|
||||
|
||||
À décider : le coût d'un subagent Haiku doit-il entrer dans les événements `pilotage` et dans le cumul, et comment le
|
||||
distinguer d'un pilotage de MIAM ?
|
||||
|
||||
## 2. Vérifier avant de déléguer, dresser le bilan après
|
||||
|
||||
À ajouter au skill `deleguer` :
|
||||
|
||||
- **Faisable ?** OpenCode ne voit que le code commité et n'a pas de `.venv` utilisable : ne pas lui demander les tests.
|
||||
- **Le preset tient ?** Appeler `modeles` avant d'imposer un preset.
|
||||
- **Rentable ?** Pas de délégation si la consigne dépasse un tiers de la sortie attendue, ou si une recherche suffit.
|
||||
- **Indépendant ?** Ne pas importer un module pas encore livré : MIAM peut tourner depuis le dépôt qu'il modifie.
|
||||
- **Après un lot :** bilan des délégations, sans attendre la demande : coût, résultat, faute, règle ajoutée.
|
||||
|
||||
## 3. Rendre le cumul fiable (PH2-H10 de MIAM)
|
||||
|
||||
Le cumul mesure le volume, pas la justesse. Sur la session, 124 625 tokens évités sur 139 707 étaient fictifs.
|
||||
|
||||
- Ne compter aucun gain pour une réponse vide ou tronquée.
|
||||
- Pouvoir marquer une délégation fausse comme rejetée, pour annuler son gain et compter sa vérification.
|
||||
- Ne pas compter comme évitée une lecture que Claude n'aurait pas faite, comme une relecture imposée au démarrage.
|
||||
|
||||
## 4. Imposer les règles par des hooks
|
||||
|
||||
La mémoire et les skills sont du texte que Claude peut ignorer : la règle « un seul suivi » était notée et il y a eu plus
|
||||
de 15 `suivre`. Ne garder en texte que ce qui demande du jugement ; ce qui se mesure passe dans `tool.call`, à côté de
|
||||
`contrainte.ts`, en fonctions pures testées.
|
||||
|
||||
À vérifier d'abord : `tool.call` peut-il refuser un appel avant son exécution ? Les rappels actuels arrivent après coup.
|
||||
|
||||
| Contrôle | Déclencheur | Action |
|
||||
|---|---|---|
|
||||
| Suivi | `suivre` à moins de N secondes du précédent, ou Nᵉ appel sur la même demande | Refus, avec le délai à respecter |
|
||||
| OpenCode et tests | `deleguer(moteur="opencode")` dont la consigne demande de lancer pytest | Refus, avec la raison |
|
||||
| Preset | `deleguer(preset=…)` sans `modeles` dans la session | Refus |
|
||||
| Rentabilité | Consigne de plus de N caractères | Rappel, le seuil reste à régler |
|
||||
| Sortie longue | Lecture ou commande non filtrée | Rappel avant l'appel si possible, sinon après |
|
||||
| Fin de lot | Fin de tour après des délégations | Injection du bilan mesuré : coût, échecs, reprises |
|
||||
|
||||
Hors de portée d'un hook, à garder en texte : juger si une réponse est juste, ou si un `grep` suffisait.
|
||||
Chaque refus doit dire quoi faire à la place et se compter, pour régler les seuils sur des chiffres réels.
|
||||
|
||||
## 5. Relecture de démarrage
|
||||
|
||||
Si elle vient du plugin, la relecture de `plan/Plan.md` et `plan/tasks-P2.md` à chaque session coûte 62 000 tokens lus et
|
||||
a donné une réponse vide puis une réponse fausse. La remplacer par une consigne ciblée sur la demande en cours, ou par une
|
||||
recherche.
|
||||
@@ -0,0 +1,348 @@
|
||||
# Forcer la délégation par les hooks
|
||||
|
||||
État au 2026-11-02, base de travail du plugin. But : que Claude pilote et que MIAM, l'API ou Haiku écrivent.
|
||||
Claude découpe, confie, contrôle par des machines, et n'écrit lui-même que ce qui ne peut pas être confié.
|
||||
|
||||
## 1. Constat
|
||||
|
||||
**MIAM (v2.5.2-dev, phase 2)** sait déjà router : modèle local (27B, 15,5 Go, un seul à la fois), API OpenRouter
|
||||
(plafond 2 $/jour, ~0,0135 $ dépensés le 2026-11-02), OpenCode, et Haiku côté Claude. `moteur="auto"` choisit entre
|
||||
local et API. Les moyens existent ; c'est Claude qui ne les utilise pas spontanément.
|
||||
|
||||
**Journal** : 185 délégations (165 ok, 10 refusées, 3 erreurs, 3 annulées, 4 rejetées), dont 9 API et 5 OpenCode.
|
||||
Par jour : 103 (30/10), 52, 19, 12 (02/11, journée en cours). La baisse n'est pas rapportée au volume de travail : elle
|
||||
alerte, elle ne prouve rien.
|
||||
|
||||
**Session du 2026-11-02, tâche PH2-D1** : seule la lecture imposée par le hook de démarrage a été déléguée. 8 tests
|
||||
répétitifs écrits à la main alors que leur modèle était dans le fichier. Les délégations suivantes ont toutes suivi un
|
||||
rappel de l'utilisateur.
|
||||
|
||||
### Pourquoi les hooks actuels n'ont pas suffi
|
||||
|
||||
| Mécanisme actuel | Ce qui a échappé |
|
||||
|---|---|
|
||||
| Refus de `Read` si fichier ≥ 20 000 caractères sans `limit` ≤ 400 | Contourné légalement : plusieurs plages de 75 à 240 lignes sur `instances.py`, `allow_large` sur des lignes géantes de `tasks-P2.md` |
|
||||
| Retenue des sorties shell ≥ 20 000 caractères | Ne voit que la taille d'un appel, pas le cumul |
|
||||
| Rappel texte (1 toutes les 10 min), consigne `prompt.compose`, mémoire | Coût nul pour Claude au moment de décider : ignorés |
|
||||
| `Edit`, `Write`, `NotebookEdit` | **Aucun contrôle** : seule la comptabilité des corrections |
|
||||
| Suivi « ce tour a délégué » | **Non présent** pour l'agent principal |
|
||||
| Hooks de fin de tour et de soumission | **Non présents** |
|
||||
|
||||
Conclusion : une consigne perd face à la facilité. Il faut qu'écrire soi-même soit plus lent que déléguer, à l'instant
|
||||
où l'écriture est tentée.
|
||||
|
||||
### Frictions côté délégation
|
||||
|
||||
- `deleguer` sans `moteur` échoue si aucun modèle n'est chargé ; il faut penser à `moteur="auto"`.
|
||||
- Le bench occupe le GPU ; `auto` local peut le préempter (corrigé pour les refus, `093f110`, pas pour le reste).
|
||||
- `plugin-claude-miam` n'est pas autorisé pour l'API, et la config affiche `projets: null` pour OpenRouter (à éclaircir).
|
||||
- Un fichier hors projet (scratchpad) fait échouer le routage API.
|
||||
- Rien n'oriente vers Haiku quand local et API sont indisponibles.
|
||||
- Pas de contrôle automatique du résultat : Claude relit tout, ce qui annule une partie du gain.
|
||||
|
||||
## 2. Principes
|
||||
|
||||
1. **Refuser, pas rappeler.** Un `deny` au moment de l'écriture, avec l'appel prêt à copier dans le message.
|
||||
2. **Budget cumulé, pas seuil par appel.** Compter lignes écrites et caractères lus par tour et par fichier.
|
||||
3. **Découper avant d'écrire.** Pas de code sans liste de bouts.
|
||||
4. **La machine juge, Claude arbitre.** Tests, ruff et contrat décident ; Claude ne lit qu'en cas d'échec.
|
||||
5. **Une sortie de secours chère et visible.** Exception motivée, journalisée, affichée.
|
||||
6. **Ne jamais bloquer le travail.** Si aucun exécutant ne répond, la règle s'ouvre et le signale.
|
||||
|
||||
## 3. Pistes de hooks
|
||||
|
||||
Seuils proposés, à régler sur les mesures.
|
||||
|
||||
| # | Hook | Déclencheur | Action | Priorité |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **Portillon tests** | `Write`/`Edit` de l'agent principal sur `tests/**`, `*.test.*` ajoutant > 10 lignes | `deny` + appel `deleguer` prêt | P0 |
|
||||
| 2 | **Budget d'écriture** | Cumul > 30 lignes de code ajoutées par l'agent principal depuis la dernière délégation réussie | `deny` jusqu'à une délégation ou une exception | P0 |
|
||||
| 3 | **Portillon commit** | `git commit` sans message issu de `deleguer` dans le tour | `deny` | P0 |
|
||||
| 4 | **Écritures par shell** | `Bash`/`PowerShell` avec `Set-Content`, `Out-File`, `>`, `sed -i`, Python écrivant un fichier | Comptés comme le hook 2 ; sinon le contournement est immédiat | P0 |
|
||||
| 5 | **Budget de lecture** | Cumul par tour > 40 000 caractères, ou > 400 lignes par fichier, `allow_large` et `Grep` compris | `deny` + « confie la question » | P1 |
|
||||
| 6 | **Portillon découpage** | Premier `Edit`/`Write` de code d'un tour sans plan | `deny` : écrire `.claude/miam-plan.json` (section 4) | P1 |
|
||||
| 7 | **Sélecteur d'exécutant** | Tout `deny` ci-dessus | Message calculé : local si prêt et sans bench ; API si projet autorisé ; sinon subagent Haiku | P1 |
|
||||
| 8 | **Bilan de fin de tour** | Fin de tour de l'agent principal | Injecte lignes écrites par Claude et par délégation ; bloque la fin si un bout du plan reste « délégable » non traité | P2 |
|
||||
| 9 | **Compteur visible** | Ligne d'état | `délégué 82 % · exceptions 1` pour le tour et la session | P2 |
|
||||
| 10 | **Docs et docstrings** | `*.md` hors `plan/`, docstrings ajoutées en lot | Comme le hook 1 | P2 |
|
||||
| 11 | **Attente unique** | `deleguer` en file (`ok: null` avec `reprise`) ; commande `/attendre` lancée sans `run_in_background` | Injecte « lance `reprise` en arrière-plan » ; `deny` sur l'appel au premier plan | P0 |
|
||||
| 12 | **Pas de suivi pendant l'attente** | `suivre` sur une demande attendue, `sleep` ou boucle d'attente en `Bash` | `deny` : « tu seras réveillé à la fin » ; permis si `fin` vaut `besoin_claude` | P0 |
|
||||
| 13 | **Confiance au verdict** | `Read` complet du fichier `sortie` après `fin: "valide"` | `deny` hors chemins sensibles : aperçu ou `offset`/`limit` | P1 |
|
||||
|
||||
**Hors comptage** : fichiers écrits par MIAM (déjà mesurés en corrections), `plan/**` et journal de suivi,
|
||||
modifications ≤ 3 lignes, subagents (`agentId` défini), config locale.
|
||||
|
||||
**Exception** : outil `exception(raison, fichiers, lignes)` avec motifs fermés (`concurrence`, `securite`,
|
||||
`debogage-multi-fichiers`, `secret`). Elle ouvre un budget limité aux fichiers cités, est journalisée et apparaît dans le
|
||||
bilan. Pour la concurrence, liste de chemins sensibles à définir plutôt qu'un jugement de Claude.
|
||||
|
||||
**Repli** : si aucun des trois exécutants ne répond, règle ouverte pour le tour et toast. Réutiliser `joignable`.
|
||||
|
||||
**Options** : garder `imposer` et `incitation`, ajouter `budget_ecriture`, `budget_lecture`, `chemins_sensibles`.
|
||||
|
||||
### À vérifier avant de coder
|
||||
|
||||
- Existence d'un événement de fin de tour de l'agent principal capable de relancer Claude (`turn.complete` n'est
|
||||
utilisé que pour les subagents).
|
||||
- Capacité de `tool.call` à lire le contenu de `Write`/`Edit` avant exécution pour compter les lignes.
|
||||
- Possibilité de déclarer `exception` : outil MCP côté MIAM, ou fichier lu par le hook.
|
||||
|
||||
## 4. Découper en bouts atomiques
|
||||
|
||||
Plan attendu par le hook 6 :
|
||||
|
||||
```json
|
||||
{ "tache": "PH2-D1", "bouts": [
|
||||
{ "id": 1, "quoi": "tests start_all", "fichier": "tests/x.py", "moteur": "auto", "modele": "tests/x.py:613-637",
|
||||
"contrat": "4 instances, ordre d, b, c, erreurs par nom", "controle": "pytest -k start_all" },
|
||||
{ "id": 2, "quoi": "ordre des réservations dans start_instance", "moteur": "claude", "motif": "concurrence" }
|
||||
] }
|
||||
```
|
||||
|
||||
Un bout délégable : **une** fonction ou un groupe de tests, **un** fichier de sortie, ≤ 80 lignes, signature donnée,
|
||||
exemple à imiter en plage de lignes, contrôle exécutable. Consigne en anglais, langue de sortie précisée. Modules
|
||||
joints, car le 27B invente champs et valeurs.
|
||||
|
||||
Relecture de D1 avec ce découpage :
|
||||
|
||||
| Bout | Exécutant possible |
|
||||
|---|---|
|
||||
| Choix du port réservé, messages d'erreur, `start_all`, `stop_all` à partir de leur contrat | Délégable, contrôlé par tests |
|
||||
| 8 tests, tests du view model | Délégable, modèles présents |
|
||||
| Boutons du dashboard, branchement du tray | Délégable, patron existant |
|
||||
| Lignes de suivi, message de commit | Délégable |
|
||||
| Absence d'`await` entre contrôle et réservation dans `start_instance` | Claude (concurrence) |
|
||||
| Inventaire des fichiers à modifier | Délégable ; Claude lit le résultat |
|
||||
|
||||
Claude n'aurait gardé que le découpage, le point de concurrence et l'intégration.
|
||||
|
||||
## 5. Faire confiance aux retours
|
||||
|
||||
Contrôle par niveaux, du moins cher au plus cher :
|
||||
|
||||
1. **Automatique** : `format="code"`, `sortie=`, `retour="apercu"` ; pytest, ruff, `ruff format --check`.
|
||||
2. **Test rouge d'abord** : le test échoue sans le code, passe avec.
|
||||
3. **Taille du diff** : conforme au bout annoncé.
|
||||
4. **Lecture par Claude** : seulement si un contrôle échoue, ou en zone sensible.
|
||||
|
||||
En cas d'échec, renvoyer la sortie du test à l'exécutant (2 essais) avant toute reprise par Claude.
|
||||
|
||||
**Pièges connus du 27B à garder en contrôle** : docstrings omises, valeurs inventées ; ajouter docstrings et champs
|
||||
aux contrôles plutôt qu'à la relecture.
|
||||
|
||||
**Risque accepté** : des erreurs passeront les tests. La parade est de meilleurs contrats, avec la revue par Claude
|
||||
réservée aux zones sensibles et au diff final.
|
||||
|
||||
## 5 bis. Contrôle automatique des réponses par MIAM
|
||||
|
||||
Livré dans MIAM (PH2-H11), sauf `commande` et `perimetre` ; contrat exact en section 11. **But** : Claude reçoit un verdict plutôt que le code ; MIAM juge, retente, escalade.
|
||||
|
||||
**Contrat de la demande** : nouveau paramètre optionnel `controle` sur `deleguer`.
|
||||
|
||||
| contrôle | s'applique à | ce qu'il vérifie |
|
||||
|---|---|---|
|
||||
| `format` | tous | JSON valide, schéma si fourni ; Python via `ast.parse`, TypeScript via transpile uniquement |
|
||||
| `complet` | tous | non vide, non tronqué, symboles demandés présents (fonctions, nombre de tests) |
|
||||
| `symboles` | code | chaque identifiant importé ou appelé existe dans les modules joints, pour attraper les champs inventés (faiblesse connue du 27B) |
|
||||
| `docstrings` | code | fonctions publiques documentées (faiblesse connue du 27B) |
|
||||
| `lint` | code | `ruff check` et `ruff format --check`, ou l'équivalent du projet |
|
||||
| `commande` | code | commande de test lancée dans un clone à l'intérieur du bac à sable AppContainer existant, sans réseau, commandes limitées à celles déclarées par projet |
|
||||
| `perimetre` | code | seuls les fichiers déclarés changent |
|
||||
| `citations` | extraction, résumé | toute valeur, nombre ou identifiant cité doit se retrouver dans les fichiers source |
|
||||
| `traduction` | traduction | placeholders, code et nombres conservés, ratio de longueur plausible |
|
||||
|
||||
**Défauts par type** : `format` + `complet` toujours ; code ajoute `symboles`, `docstrings`, `lint` ; extraction ajoute `citations` ; `commande` seulement si fourni.
|
||||
|
||||
**Boucle** : en cas d'échec, MIAM renvoie l'échec exact au modèle, 2 tentatives maximum, puis escalade local → API → rend la main à Claude avec statut `echec_controle`, sans accepter silencieusement.
|
||||
|
||||
**Réponse à Claude** : champs `verdict` (`valide`, `echec_controle`, `non_controle`), `controles` (nom, résultat, message court), `essais`, `moteur_final`, `sortie` (chemin de fichier), stats de diff ; pas de corps de code quand `valide` et `retour="apercu"`.
|
||||
|
||||
**Application** : rien n'est écrit dans l'arbre de travail avant que tous les contrôles passent ; le résultat reste dans le clone ou un fichier temporaire ; puis écrit comme `sortie` déclarée.
|
||||
|
||||
**Sécurité** : la sortie du modèle est non fiable ; elle n'est jamais exécutée hors du bac à sable ; seules les commandes déclarées ; aucun secret dans le clone.
|
||||
|
||||
**Mesure** : le journal reçoit `controles`, `essais`, `verdict` ; `echec_controle` ne compte aucun gain ; le taux de réussite au premier essai par modèle alimente le choix de modèle (`type`/`seuil`) et le taux de réparation ; le temps passé dans les contrôles est suivi.
|
||||
|
||||
**Limites** : des tests écrits par le même modèle peuvent valider un comportement faux : exiger d'abord un test rouge (le test doit échouer sans le code) ou un test écrit séparément ; `citations` prouve la présence, pas le sens correct ; `commande` coûte du temps ; passer les contrôles ne remplace pas la revue de Claude sur les zones sensibles (concurrence, sécurité).
|
||||
|
||||
**Découpage en bouts** :
|
||||
|
||||
1. Contrôles purs (`format`, `complet`, `symboles`, `docstrings`, `citations`, `traduction`) avec leurs tests.
|
||||
2. Contrôles outillés (`lint`, `perimetre`) avec leurs tests.
|
||||
3. Boucle de retentative et escalade local → API → `echec_controle`.
|
||||
4. Contrôle `commande` dans le bac à sable, sur clone, sans réseau.
|
||||
5. Champs de réponse (`verdict`, `controles`, `essais`, `moteur_final`, `sortie`, stats de diff) et écriture différée.
|
||||
6. Journal (`controles`, `essais`, `verdict`, temps) et plugin (le hook lit `verdict` et saute la lecture quand `valide`).
|
||||
|
||||
### Reprise de la main sans suivi répété
|
||||
|
||||
Livré dans MIAM (PH2-H11), sauf le chien de garde. `miam-attendre` est devenu la commande `curl` donnée dans `reprise` : aucun exécutable à installer. Contrat exact en section 11.
|
||||
|
||||
Problème : `suivre` plafonne chaque attente à `call_wait_s` (45 s), donc une tâche longue fait revenir Claude toutes les 45 s, et chaque retour coûte des tokens.
|
||||
|
||||
Principe : le temps n'est pas une limite, le seul coût est les tours de Claude. MIAM exécute contrôles, corrections et escalade seul. Claude est réveillé une fois, à un état terminal.
|
||||
|
||||
**Attente unique** : Claude lance une fois, en arrière-plan via `Bash` avec `run_in_background`, une petite commande `miam-attendre <demande_id>`. Elle bloque sur une route HTTP locale authentifiée de MIAM (long polling), imprime une ligne JSON, sort. Claude Code réveille Claude quand une commande d'arrière-plan se termine : aucun token pendant l'attente. Claude termine son tour ou continue autre chose.
|
||||
|
||||
**États terminaux** :
|
||||
|
||||
| état | sens | ce que fait Claude |
|
||||
|---|---|---|
|
||||
| `valide` | tous les contrôles passés, fichier écrit | intègre, sans lecture |
|
||||
| `echec_controle` | tentatives et escalade épuisées ; le rapport donne le dernier échec | reprend la main ou re-découpe |
|
||||
| `besoin_claude` | décision réservée à Claude : permission, plafond de dépense API atteint, instruction ambiguë | répond puis attend à nouveau |
|
||||
| `annule` | demande annulée | rien |
|
||||
| `perdue` | MIAM redémarré ou injoignable | renvoie ou fait lui-même |
|
||||
|
||||
**Pas de limite de durée, mais pas d'attente sans fin** : pas de timeout total. Un chien de garde détecte l'absence de progrès : aucun token généré, aucun contrôle terminé et aucun changement de tentative pendant `mcp.sans_progres_s` (proposé 600 s) donne `besoin_claude` avec l'étape bloquée. Les tentatives sont bornées (2 par moteur, puis escalade), donc la boucle se termine toujours. `miam-attendre` sort en `perdue` après plusieurs échecs de contact avec MIAM.
|
||||
|
||||
**Hook du plugin** : tant que `miam-attendre` tourne pour une demande, `suivre` sur cette demande est refusé avec le message « tu seras réveillé à la fin » ; idem pour les boucles de `sleep` en `Bash`. Le hook part du `demande_id` renvoyé en « en_file ».
|
||||
|
||||
**Réponse de `deleguer`** : si non terminé dans `call_wait_s`, la réponse donne `demande_id`, `etat` et `reprise` : la commande exacte à lancer.
|
||||
|
||||
**Autres clients** : un client sans commandes d'arrière-plan (OpenCode) garde `suivre`, avec un `attente_s` long unique, plus plafonné à 45 s, dans la limite du timeout du client MCP, à vérifier.
|
||||
|
||||
**Coût attendu** : un lancement et un réveil par délégation, indépendamment de la durée.
|
||||
|
||||
**Bouts** :
|
||||
|
||||
1. États terminaux et chien de garde en logique pure, avec tests.
|
||||
2. Route de long polling authentifiée.
|
||||
3. Commande `miam-attendre`, avec tests.
|
||||
4. Champ `reprise` dans `deleguer`.
|
||||
5. Hook du plugin refusant `suivre`.
|
||||
|
||||
## 6. Évolutions côté MIAM
|
||||
|
||||
| Évolution | Effet |
|
||||
|---|---|
|
||||
| `moteur="auto"` par défaut, repli explicite si tout est indisponible | Supprime l'erreur « aucun modèle chargé » |
|
||||
| `routage` qui peut répondre « subagent Haiku » | Échelle complète local → API → Haiku |
|
||||
| Délégation courte pendant un bench : API plutôt que préemption | Ne coupe plus les tests |
|
||||
| Projet du plugin autorisé pour l'API ; clarifier `projets: null` | Lève le refus rencontré |
|
||||
| `deleguer(..., controle="pytest -k x", essais=2)` : exécute et renvoie l'échec au modèle | Niveau 1 sans Claude, détail en 5 bis |
|
||||
| `lot=[...]` : plusieurs bouts en un appel | Moins de tours de pilotage |
|
||||
| Écriture directe dans le dépôt avec diff court en retour | Claude ne reçoit pas le code |
|
||||
|
||||
## 7. Limites
|
||||
|
||||
- Un hook contrôle des appels, pas le raisonnement : Claude peut toujours rédiger puis découper artificiellement.
|
||||
- Chaque délégation coûte une consigne et un suivi : sous ~10 lignes, écrire peut être moins cher, d'où les seuils.
|
||||
- Des seuils trop stricts multiplieront les exceptions : les suivre dès le début.
|
||||
- Le hook 6 ajoute un tour au départ de chaque tâche ; il doit rester sans objet pour une correction minime.
|
||||
|
||||
## 8. Ordre de travail
|
||||
|
||||
Chaque lot est découpé en bouts confiés à MIAM : le plugin sert de premier terrain d'essai.
|
||||
|
||||
1. **Lot 1 (P0)** : compteurs par tour, hooks 1, 2, 3, 4, exception, repli. Vérifier d'abord les points de la section 3.
|
||||
2. **Lot 2 (MIAM)** : `auto` par défaut, API pendant le bench, projet du plugin.
|
||||
3. **Lot 3 (P1)** : budget de lecture, plan, sélecteur d'exécutant.
|
||||
4. **Lot 4** : `controle=`, `lot=`, écriture directe.
|
||||
5. **Lot 5 (P2)** : bilan, compteur, documentation.
|
||||
|
||||
**Mesures** par session : part des lignes écrites par délégation, net de tokens, taux de reprise, nombre d'exceptions par
|
||||
motif, refus jugés à tort. Objectif proposé après le lot 1 : ≥ 70 % des lignes hors zones sensibles.
|
||||
|
||||
## 9. Décisions attendues
|
||||
|
||||
- Seuils de départ : 10 lignes pour les tests, 30 pour le code, ou plus strict.
|
||||
- Mode : refus immédiat, ou une semaine en mesure seule pour régler les seuils (recommandé : refus immédiat sur les
|
||||
tests et le commit, mesure sur le reste).
|
||||
- Liste des chemins sensibles exemptés.
|
||||
- Part acceptable de Haiku dans l'échelle, à comparer au coût de l'API.
|
||||
|
||||
## 10. Version cible : plugin v0.9.0
|
||||
|
||||
**But** : une version dont les gains de tokens sont réels et mesurés, parce que Claude délègue par défaut.
|
||||
|
||||
**Valeurs retenues par défaut** (issues de la section 9, modifiables) : 10 lignes pour les tests, 30 lignes pour le code, refus immédiat sur tous les hooks P0, budget de lecture de 40 000 caractères par tour et 400 lignes par fichier ; chemins sensibles : `src/miam/services/mcp.py`, `src/miam/services/instances.py`, `src/miam/core/aio.py` plus tout ce qui correspond à `*sandbox*`, liste à confirmer.
|
||||
|
||||
**Périmètre plugin** (checklist) : compteurs de tour (lignes écrites par l'agent principal, caractères lus, dernière délégation réussie) ; hooks 1, 2, 3, 4 (section 3) ; hook 5 budget de lecture ; hook 6 plan de découpage ; hook 7 sélecteur d'exécutant ; `exception` avec raisons fermées ; repli quand aucun exécutant ne répond ; refus de `suivre` pendant `miam-attendre` ; rapport de fin de tour et compteur visible (hooks 8, 9) ; skill `deleguer` mis à jour pour enseigner le découpage, `moteur="auto"` et `reprise`.
|
||||
|
||||
**Prérequis MIAM** (PH2-H11, même livraison) : `moteur="auto"` par défaut ; API au lieu de la préemption pendant un benchmark ; projet plugin autorisé pour l'API ; `controle` avec contrôles purs et `lint` ; boucle de correction et escalade ; états terminaux, `miam-attendre`, `reprise`, chien de garde `mcp.sans_progres_s` ; `verdict`. `lot=`, écritures directes et `commande` en sandbox peuvent suivre plus tard sans bloquer la v0.9.0.
|
||||
|
||||
**Vérifications préalables** (bloquantes) : les trois points de « À vérifier avant de coder » en section 3 ; durée maximale de blocage tolérée par un client MCP. Fait le 03/11/2026 : une commande en arrière-plan de 11 min réveille bien Claude à sa sortie, sans token pendant l'attente.
|
||||
|
||||
**Critères d'acceptation** (checklist, chacun testable) : une édition de test de l'agent principal de plus de 10 lignes est refusée avec un appel prêt ; une écriture via le shell est comptée ; un `git commit` sans message délégué est refusé ; une tâche découpée en plusieurs morceaux lisant le même fichier dépasse le budget de lecture et est refusée ; tous les exécutants étant indisponibles, le travail continue et un toast le signale ; une délégation longue coûte un lancement et un réveil, sans appel à `suivre` ; un `echec_controle` rend la main avec le dernier échec ; les résultats `valide` sont intégrés sans que Claude lise le corps du code ; les exceptions apparaissent dans le rapport ; les sous-agents ne sont pas soumis aux budgets ; tous les tests du plugin passent.
|
||||
|
||||
**Mesure du gain** : gain réel = tokens Claude effectivement consommés, lus depuis `session.usage`, pas les `evites` estimés. Protocole : rejouer des tâches comparables (une fonction pure avec tests, une tâche UI multi-fichiers, une extraction de document) sur v0.8.0 et v0.9.0, mêmes prompts ; enregistrer tokens Claude totaux, coût cloud, durée, corrections, exceptions, actions refusées à tort, bugs trouvés ensuite. Comparer aussi `net` (`evites - pilotage - corrections`) à la différence mesurée pour calibrer l'estimation. Version valide seulement si les tokens Claude mesurés baissent sans hausse des bugs post-merge ; la cible est fixée après la première comparaison, aucun chiffre décidé maintenant.
|
||||
|
||||
**Ordre des bouts** :
|
||||
1. vérifications ;
|
||||
2. compteurs avec tests ;
|
||||
3. hooks P0 et exception ;
|
||||
4. prérequis MIAM ;
|
||||
5. budget de lecture, plan, sélecteur ;
|
||||
6. rapport de fin de tour et compteur ;
|
||||
7. skill et docs ;
|
||||
8. mesure puis release v0.9.0.
|
||||
|
||||
Chaque bout suit la section 4 et est délégué.
|
||||
|
||||
## 11. Contrat livré par MIAM (PH2-H11) et travail restant côté plugin
|
||||
|
||||
État au 03/11/2026, code de MIAM non commité, tests verts. Le MIAM installé doit être reconstruit pour l'exposer.
|
||||
|
||||
### Ce que MIAM accepte
|
||||
|
||||
`deleguer(..., controle=[...], attendus=[...])` :
|
||||
|
||||
- `controle` : liste parmi `syntaxe`, `complet`, `symboles`, `docstrings`, `citations`, `traduction`, `lint`. `[]` désactive les contrôles.
|
||||
- Par défaut : `format="code"` avec une `sortie` en `.py` donne `syntaxe`, `complet`, `symboles`, `docstrings`, `lint` ; tout le reste donne `complet`.
|
||||
- `attendus` : noms qui doivent figurer dans la réponse (fonctions, classes), vérifiés par `complet`.
|
||||
- `lint` : `ruff check --select F,E9` seulement (vraies erreurs, pas le style) ; ignoré si ruff est introuvable (cas de l'exe).
|
||||
- `symboles` et `citations` comparent la réponse aux fichiers joints : joindre les modules utilisés.
|
||||
- Tentatives : `mcp.controle_essais` (2) par moteur ; une demande locale passe ensuite à l'API.
|
||||
|
||||
Écarts avec la section 5 bis : `format` s'appelle `syntaxe` (Python seulement) ; `commande` et `perimetre` ne sont pas faits ; il n'y a ni `moteur_final` ni statistiques de diff.
|
||||
|
||||
### Ce que MIAM renvoie
|
||||
|
||||
| cas | réponse | `fin` (route `/attendre`) |
|
||||
|---|---|---|
|
||||
| terminé, contrôles passés | `ok: true`, `verdict: "valide"`, `controles: [{nom, ok, message?}]`, `essais`, `sortie` si demandée | `valide` |
|
||||
| terminé sans contrôle | `ok: true`, `verdict: "non_controle"` | `termine` |
|
||||
| contrôles échoués partout | `ok: false`, `erreur: "echec_controle : nom : message ; …"` | `echec_controle` |
|
||||
| annulé | `ok: false`, `etat: "annulee"` | `annule` |
|
||||
| demande inconnue (MIAM redémarré) | `ok: false`, sans `etat` | `perdue` |
|
||||
| permission OpenCode, autre cas en attente | `ok: null` | `besoin_claude` |
|
||||
| pas fini dans `call_wait_s` (45 s) | `ok: null`, `demande_id`, `etat`, `reprise`, `message` | (sans objet) |
|
||||
|
||||
`sortie` n'est écrite qu'après le verdict `valide`. Les tentatives rejetées sont journalisées dans `delegations.jsonl` avec `statut: "rejete"`, sans gain.
|
||||
|
||||
### Attente unique
|
||||
|
||||
`reprise` vaut exactement :
|
||||
|
||||
```
|
||||
curl -s -H @"<logs>/mcp/attente.hdr" "http://127.0.0.1:<port>/attendre?demande_id=<id>"
|
||||
```
|
||||
|
||||
- `attente.hdr` contient l'en-tête `Authorization`, écrit par MIAM au démarrage du serveur MCP : aucun jeton dans la commande ni dans la conversation.
|
||||
- `GET /attendre` répond une seule fois, à l'état final : la même réponse que `suivre`, plus `fin`. Chaque attente interne dure au plus `ATTENTE_ROUTE_S` (3600 s) et reprend en boucle tant que la demande n'est pas finie.
|
||||
- À lancer avec `Bash` et `run_in_background: true` (Git Bash sous Windows : la commande est en syntaxe POSIX). Vérifié le 03/11/2026 sur le vrai serveur HTTP.
|
||||
|
||||
### Encore manquant côté MIAM
|
||||
|
||||
- Chien de garde `mcp.sans_progres_s` : pas encore fait, car le modèle répond sans flux et rien ne mesure l'avancement. La seule limite reste `timeout_execution_s`, multiplié par le nombre de tentatives. Une très longue tâche peut donc finir en expiration (`ok: false`, `fin: "besoin_claude"` ou `perdue`).
|
||||
- Contrôles `commande` (tests en bac à sable) et `perimetre`.
|
||||
- `moteur="auto"` par défaut ; API au lieu de la préemption pendant un benchmark.
|
||||
|
||||
### À coder dans le plugin (v0.9.0), par bouts
|
||||
|
||||
1. **Hook PostToolUse sur `deleguer`** : si `ok` est `null` et qu'il y a une `reprise`, ajouter au contexte « lance `reprise` une fois avec `run_in_background`, puis termine ton tour ou fais autre chose », et noter `demande_id` comme en attente dans l'état du tour.
|
||||
2. **Hook PreToolUse sur `Bash`** : reconnaître une commande qui contient `/attendre?demande_id=` ; sans `run_in_background`, la refuser (« lance-la en arrière-plan ») ; avec, passer la demande à « attendue ».
|
||||
3. **Hook PreToolUse sur `suivre`** : refuser si la demande est « attendue », avec « tu seras réveillé à la fin ». Refuser aussi les `sleep` et les boucles d'attente en `Bash` pendant une attente. Laisser passer `suivre` si `fin` vaut `besoin_claude` (permission à donner).
|
||||
4. **Hook sur la sortie de la commande d'arrière-plan** (ou PostToolUse sur la notification) : lire `fin` et injecter la conduite :
|
||||
- `valide` : intégrer sans relire le corps, ne pas `Read` la `sortie` en entier ;
|
||||
- `termine` : relire ;
|
||||
- `echec_controle` : reprendre la main ou redécouper, avec le dernier motif ;
|
||||
- `besoin_claude` : répondre (`autoriser`), puis relancer `reprise` ;
|
||||
- `annule` : rien ;
|
||||
- `perdue` : renvoyer la demande ou faire soi-même.
|
||||
5. **Hook PreToolUse sur `Read`** : après `valide`, refuser une lecture complète du fichier `sortie` (aperçu ou `offset`/`limit` seulement), sauf chemin sensible.
|
||||
6. **Skill `deleguer`** : enseigner `controle` et `attendus`, joindre les modules pour `symboles`, lancer `reprise` en arrière-plan, et le tableau `fin` ci-dessus.
|
||||
7. **Compteurs** : `essais`, nombre de `echec_controle` et attentes uniques par session, pour la mesure de la section 10.
|
||||
|
||||
Tests de chaque hook sur des réponses figées tirées du tableau ci-dessus ; les autres hooks (sections 3 et 10) suivent l'ordre de la section 10.
|
||||
Reference in new issue
Block a user