Rejeu du bras kit-gov — la gouvernance non dosée coûte, elle ne rapporte pas (lot F) — 2026-09-17¶
Issue #582 (phase 2bis « Cœur »), lot F. Commit rejoué :
main@6d9db6c6+ PR #592 (lots A #584, B #585, C #586 déjà surmain; #592 ajoute le braskit-govàscripts/bench/three_arms.py) — avant les lots G1 (#597), G2 (#598/#599) et G3 (en cours), qui sont la réponse aux chiffres de ce rejeu, pas leur contexte. Bras rejoués : les quatre (nu,ecc,kitrepris de la campagne du 2026-09-17,kit-govnouvellement joué), mêmes 20 tâches, graine 551,--full. Données brutes locales (Grimoire-Forge, hors dépôt kit), non poussées :_scratch/bench-f/workspace/state/results.jsonl(240 lignes),_scratch/bench-f/workspace/reports/2026-09-17/{report.md,report.json}, analyse tour par tour de 21 runs_scratch/bench-f/analyse-tours-kit-gov.md. Coût réellement dépensé pour cette campagne : 111,42 $ (60 runskit-gov, aucun nouvel appelnu/ecc/kit, repris tels quels). Verdict : le bras gouverné par défaut est pire que le bras nu sur les quatre axes mesurés (succès, pass^k, temps, coût), et pire que le braskitnon gouverné sur les mêmes quatre axes. Les phases 3 et 4 restent gelées (docs/plan-2026-q4.md§3). Nuance importante établie en §3 : une partie non négligeable de l'écart de réussite brut vient d'un incident d'infrastructure (expiration de session en toute fin de campagne), pas d'un défaut de gouvernance — l'écart de tours et de coût, lui, est entier et sans cette explication.
1. Méthode¶
- Worktree jetable
_scratch/kit-bench-fsurorigin/main+ PR #592 (feat/bench-kit-gov) fraîchement fetchée, venv dédié (uv venv .venv --python 3.12, dépendances installées en isolation). - Même précaution que le lot E :
PATH="<worktree>/.venv/bin:$PATH"préfixé explicitement avant lancement, vérifié (grimoire standard gate run-tests --helprépond dans cet environnement). setup_arm_kit_gov()(nouveau dans #592) provisionne, en plus du provisionnement du braskit(grimoire init . --backend local --no-cockpitpuisgrimoire host sync --host claude) :grimoire standard init .avec le profil par défautstarter— celui qu'un développeur seul obtient sans passer--profile/--needs, donc le scénario le plus représentatif d'une adoption réelle, pas un profil renforcé choisi pour l'occasion.- Une tâche de board posée directement en
in_progress, à l'id exact de la tâche du banc (_governed_task_id(), barres obliques remplacées par des doubles underscores — Grimoire refuse/dans untask_id), importée dans le Mission Ledger pargrimoire task migrate-standard(ADR-007) — seule voie CLI qui préserve un id exact. - Résultat vérifié en isolation avant tout rejeu réel :
grimoire standard activation-contextrend la directive complète avecgate run-tests --task-id <id>,_is_governed()vautTrue,active_task_id()résout cet id — jamaisbootstrap. Le défaut de méthode du lot E (docs/bench/rejeu-lot-e-2026-09-17.md§3, braskitjamais gouverné) est donc fermé pour ce rejeu. ARMSpasse de trois à quatre bras (nu,ecc,kit,kit-gov), chacun avec son propreHOMEisolé. Les brasnu/ecc/kitsont repris tels quels de la campagne du 2026-09-17 (--resume, aucun nouvel appel) ; seulkit-govest rejoué en réel, 60 runs (20 tâches × k = 3).- Campagne lancée en arrière-plan (
nohup, PID consigné), suivie par intervalles espacés (10-15 minutes). Durée réelle des 60 runskit-gov: ~4h25 (10:14–14:39 UTC) — nettement plus long quekit(189 s médian avant lots A/B/C, 57 s après), cohérent avec le coût de gouvernance mesuré en §4. - Rapport généré directement par le harnais (
--label "lot F kit-gov après A+B+C") :_scratch/bench-f/workspace/reports/2026-09-17/report.md, toutes les valeurs de ce document sont recalculées directement depuisresults.jsonl(240 lignes), pas recopiées du rapport sans vérification. - Analyse tour par tour complémentaire (
_scratch/bench-f/analyse-tours-kit-gov.md) : 21 des 60 runskit-govlus en détail (transcriptionsstream.jsonl, dépôts de tâche, notes mémoire écrites par l'agent lui-même) pendant que la campagne tournait encore — comptage par appel d'outil classé sur le contenu réel de la commande, pas une estimation.
2. Résultats agrégés¶
| Bras | n | Succès | IC95 % succès | pass^k (k=3) | Temps médian | Tours médians | Coût médian/tâche résolue | Coût total | Runs avec preuve de test (kit_test_run_evidence) |
|---|---|---|---|---|---|---|---|---|---|
| nu | 60 | 91,7 % (55/60) | [85 %, 98 %] | 90 % | 64 s | 6,0 | 0,399 $ | 27,04 $ | — (bras non gouverné) |
| ecc | 60 | 93,3 % (56/60) | [87 %, 98 %] | 90 % | 61 s | 7,0 | 0,629 $ | 45,30 $ | — (bras non gouverné) |
| kit | 60 | 90,0 % (54/60) | [82 %, 97 %] | 85 % | 57 s | 6,0 | 0,416 $ | 30,26 $ | 0 % (0/60 — défaut de méthode du lot E, fermé par ce rejeu) |
| kit-gov | 60 | 83,3 % (50/60) | [73 %, 92 %] | 75 % | 261 s | 28,5 | 1,915 $ | 111,42 $ | 68,3 % (41/60) |
Tours médians, coût médian/tâche résolue et part de preuve recalculés
directement depuis les 240 lignes de results.jsonl (num_turns,
total_cost_usd des runs success=true, champ kit_test_run_evidence) —
identiques à trois décimales près à ceux du rapport généré par le harnais.
kit-gov vs kit (le seul comparatif qui isole l'effet de la gouvernance,
tout le reste étant identique — même provisionnement de base, même modèle,
même graine, mêmes 20 tâches) :
- Tours : 6,0 → 28,5 médians, soit ×4,75.
- Coût médian/tâche résolue : 0,416 $ → 1,915 $, soit ×4,60.
- Coût total de campagne : 30,26 $ → 111,42 $, soit ×3,68.
- Temps médian : 57 s → 261 s, soit ×4,58.
- Succès : 90,0 % → 83,3 %, soit -6,7 points (IC95 % chevauchants : [82 %, 97 %] vs [73 %, 92 %] — pas de signal statistique isolé, voir §3 pour la lecture causale).
- pass^k : 85 % → 75 %, soit -10 points (17/20 tâches à 3/3 pour
kit, 15/20 pourkit-gov— voir §3, l'écart tient à deux tâches sur cinq non-parfaites). - Preuve de test réellement exécutée : 0 % → 68,3 % — c'est la seule
ligne où
kit-govrapporte quelque chose quekitne mesure jamais sur ce banc : le lot B (gate run-tests) est désormais mesurable, pour la première fois depuis son merge. Détail par langage : Go 15/15, JavaScript 15/15, Rust 11/15, Python 0/15 — le plancher Python n'est pas un raté de l'agent, c'est structurel (§3).
3. Les cinq tâches dégradées — tableau et causes lues sur disque¶
| Tâche | nu | ecc | kit | kit-gov | Cause du delta kit → kit-gov |
|---|---|---|---|---|---|
rust/poker |
3/3 | 3/3 | 3/3 | 0/3 | Incident d'infrastructure, pas de gouvernance — voir ci-dessous |
rust/two-bucket |
3/3 | 3/3 | 3/3 | 2/3 | 1 des 2 échecs de plus = même incident d'infrastructure ; l'autre run est vert |
go/palindrome-products |
0/3 | 2/3 | 1/3 | 1/3 | Aucun delta kit → kit-gov : tâche déjà difficile pour tous les bras (nu pire que kit-gov) |
javascript/transpose |
3/3 | 3/3 | 2/3 | 2/3 | Aucun delta kit → kit-gov : même taux qu'en non gouverné, cause détaillée ci-dessous |
rust/scale-generator |
1/3 | 0/3 | 0/3 | 0/3 | Aucun delta kit → kit-gov : plancher déjà à 0-1/3 sur tous les bras avant toute gouvernance |
rust/poker 0/3 — lecture attentive : ce n'est pas la gouvernance¶
Les trois runs kit-gov de rust/poker se terminent identiquement, en
1 tour, 0,000 $, 4 secondes, avec terminated_reason: "error". Le
résultat brut de chaque run (run*.stream.jsonl) est :
Aucun fichier de tâche n'a été écrit (tasks/rust__poker/kit-gov/run{0,1,2}/
sont vides à l'exception des transcriptions). Ce n'est ni un timeout, ni des
tests jamais lancés faute de temps, ni du code non écrit par épuisement de
tours en gouvernance : la session n'a jamais commencé à travailler, le
premier appel API a été refusé avant le premier tour utile.
Recoupement avec rust/two-bucket run 2 (le seul échec de cette tâche, même
signature exacte : 1 tour, 0,000 $, 4 s, même message d'erreur) : les
horodatages (recorded_at) des quatre runs en échec sont
consécutifs et regroupés dans les 16 dernières secondes de toute la
campagne (14:39:01 à 14:39:17 UTC, sur une campagne qui a commencé à
10:14:36 — soit 4h25 plus tôt) :
| Run | Horodatage |
|---|---|
rust/two-bucket run 2 |
14:39:01 |
rust/poker run 0 |
14:39:06 |
rust/poker run 1 |
14:39:11 |
rust/poker run 2 |
14:39:17 |
Ce sont, à l'exception près, les quatre derniers runs programmés de toute la
campagne. Aucun autre run (sur 240, tous bras confondus) n'a cette signature
(1 tour, coût nul, terminated_reason: "error") — recherche exhaustive sur
results.jsonl. Lecture la plus probable : la session Claude Code longue
durée utilisée pour lancer les runs en série a vu son jeton OAuth expirer en
toute fin de campagne (après ~4h25 d'exécution quasi continue), un artefact
de la durée totale du bras kit-gov — elle-même conséquence du surcoût de
tours/temps mesuré en §4, mais pas une défaillance du mandat de gouvernance
lui-même, du code produit, ni des gates.
Conséquence chiffrée sur le verdict brut : sur les 60 runs kit-gov, 4
(6,7 %) sont des échecs à coût et durée nul, non représentatifs d'un travail
de gouvernance raté. En les retirant du dénominateur (56 runs restants, tous
avec un travail réel effectué) : succès 50/56 = 89,3 % (contre 90,0 %
pour kit — écart résiduel de 0,7 point, largement dans le bruit) et pass^k
recalculé sur les mêmes 20 tâches, en traitant les 3 runs rust/poker
manquants comme non observés plutôt que comme des échecs : la tâche redevient
non tranchable côté kit-gov faute de données, ce qui ramène le nombre de
tâches non-parfaites à 3 sur 20 comme pour kit (go/palindrome-products,
javascript/transpose, rust/scale-generator) — pass^k reviendrait à 85 %,
identique à kit, si on accepte de traiter l'incident comme hors
périmètre de mesure. Ce document ne fait pas ce choix à la place du plan : le
tableau du §2 rapporte le chiffre brut (83,3 %/75 %, incident inclus, c'est
ce qui s'est réellement passé sur ce banc) et cette note documente
explicitement l’ampleur exacte de l'artefact pour qu'un futur rejeu (lot H)
puisse trancher en connaissance de cause. Ce que l'incident ne change
pas : le surcoût de tours et de coût (§4) est mesuré sur 56-60 runs qui ont
réellement tourné, il ne doit rien à ces 4 runs à coût nul — si quoi que ce
soit, leur coût nul sous-estime légèrement le coût total réel de la
campagne (un run complet aurait coûté plus que 0 $).
go/palindrome-products 1/3 — tâche déjà difficile, pas un effet de gouvernance¶
Le taux kit-gov (1/3) est identique au taux kit non gouverné (1/3) — nu
fait pire (0/3), ecc fait mieux (2/3). Cause technique de l'échec du gate,
sans lien avec la réussite de la tâche : les trois runs (y compris le run 1
qui réussit) reçoivent go: commande introuvable (exit 127) sur go test
./... — le binaire Go est absent de tous les runs Go de ce banc,
succès ou échec confondus (go/bottle-song, go/error-handling, go/pov,
go/simple-linked-list réussissent 3/3 avec la même erreur de gate). Ce
n'est donc pas la cause de l'échec des runs 0 et 2 : cette tâche est connue
comme difficile pour tous les bras (matrice §2 du rapport complet), et rien
dans ce rejeu ne montre qu'elle le devient davantage sous gouvernance.
javascript/transpose 2/3 — même taux qu'en non gouverné, mais un défaut de gate mis en évidence¶
Le taux kit-gov (2/3) est identique au taux kit (2/3). Le run en échec
(run 0) a produit du code plausible, validé par l'agent lui-même via un
script de vérification ad hoc (invariant d'involution
transpose(transpose(x)) === x, 0 échec relevé) — mais jamais par la
vraie suite de tests Exercism livrée avec l'exercice
(transpose.spec.js, présent sur disque). Cause : npm test échoue avec
jest: commande introuvable (exit 127) sur les 15/15 runs JavaScript de
kit-gov, gouvernés ou non — node_modules n'est jamais installé dans
l'environnement du banc. L'agent obéit à la directive (« corrige tout
échec » devient, en pratique, « déclare la non-applicabilité et justifie »
puisque relancer npm install sans réseau est impossible) et referme la
tâche sur la foi de sa propre vérification, qui a raté un cas limite que la
vraie suite aurait probablement détecté. C'est un angle mort du lot B déjà
identifié par l'analyse tour par tour (_scratch/bench-f/analyse-tours-kit-gov.md
§2) : le gate mandate gate run-tests sans jamais vérifier au préalable si
la commande de test peut seulement s'exécuter dans cet environnement — ni
un progrès ni un recul par rapport à kit, qui n'a jamais eu de gate du
tout sur ce point.
rust/scale-generator 0/3 — plancher préexistant sur les quatre bras¶
cargo test s'exécute avec succès (ok=true, exit 0) sur les trois runs
kit-gov — mais rapporte 0 tests exécutés (« running 0 tests… test
result: ok. 0 passed »), la vraie suite de tests de l'exercice n'étant
jamais compilée/liée dans cette configuration. Le gate passe au vert sur une
suite vide, pas sur un vrai résultat — mais cette tâche est déjà à 0-1/3 sur
les quatre bras (nu 1/3, ecc 0/3, kit 0/3, kit-gov 0/3) : c'est un plancher
de difficulté de la tâche elle-même, préexistant à toute gouvernance,
inchangé par ce rejeu.
4. Décomposition des tours par activité (reprise de l'analyse de 21 runs)¶
Comptage par appel d'outil (Bash/Read/Write/Edit), sur les 21 des 60 runs
kit-gov lus en détail (transcriptions complètes, _scratch/bench-f/analyse-tours-kit-gov.md) :
| Catégorie | Médiane (tours) | Part du total (≈31 tours médians observés sur l'échantillon de 21) |
|---|---|---|
Orientation gouvernance (lister _grimoire-output, lire les gabarits, git status) |
8,0 | ≈26 % |
| Spéléologie dans le source Grimoire installé (décoder un message de gate) | 8,0 | ≈26 % |
gate run-tests |
4,0 | ≈13 % |
gate check --strict |
3,0 | ≈10 % |
context build + découverte CLI (--help) |
3,0 | ≈10 % |
| Travail réel sur la tâche (édition du fichier solution, vérification locale) | 2,0 | ≈6-7 % |
standard verify |
1,0 | ≈3 % |
Écriture task-envelope.md |
1,0 | ≈3 % |
Écriture evidence-pack.md/acceptance-record.md/claim-ledger.md |
1,0 | ≈3 % |
| Écriture mémoire Grimoire | 0,0 | — |
Constats vérifiés sur les 21 transcriptions, pas supposés : zéro boucle
(même commande répétée) sur 21 runs ; zéro occurrence du hook Stop
bloquant une clôture (le hook est bien câblé mais n'intervient jamais dans
le flux observé). Le poste « travail réel » — le seul qui produit
directement le livrable — ne représente que 2 tours sur 31 en médiane
(6-7 %) ; gouvernance générale + spéléologie source, à eux seuls, en
représentent 16 sur 31 (environ la moitié).
5. Ce que coûte la gouvernance et ce qu'elle rapporte¶
Ce qu'elle coûte (comparé au bras kit non gouverné, même base, mêmes
tâches, même graine) :
- ×4,75 tours médians (6,0 → 28,5).
- ×4,60 coût médian par tâche résolue (0,416 $ → 1,915 $), ×3,68 coût total de campagne (30,26 $ → 111,42 $).
- ×4,58 temps médian (57 s → 261 s).
- -6,7 points de succès brut (90,0 % → 83,3 %) et -10 points de
pass^k (85 % → 75 %) — dont, d'après la lecture causale du §3, la quasi
totalité tient à un seul incident d'infrastructure (expiration de session
en fin de campagne, 4/60 runs à coût nul) plutôt qu'à une dégradation de la
qualité du travail produit sous gouvernance : sur les trois tâches où
kit-govetkitsont directement comparables sans cet incident (go/palindrome-products,javascript/transpose,rust/scale-generator), le taux de réussite est rigoureusement identique, tâche par tâche, entrekitetkit-gov. - Aucune tâche mieux réussie sous gouvernance : sur les 20 tâches, pas
une seule ne passe d'un échec (sous
kit) à un succès (souskit-gov) — le sens du delta, quand il existe, est toujours défavorable ou nul.
Ce qu'elle rapporte — la seule ligne positive de ce rejeu :
- La preuve de test devient mesurable pour la première fois : 68,3 %
des runs
kit-gov(41/60) écrivent effectivementtest-run.json(lot B), contre 0 % pourkit(défaut de méthode du lot E, fermé par ce rejeu). C'est un gain de méthode de banc, pas encore un gain de qualité démontré : aucune des deux tâches que le lot B ciblait explicitement (go/palindrome-products,javascript/transpose) ne voit son pass^k s'améliorer sous gouvernance — dans les deux cas le gate s'exécute (positivement pour la mesure) mais échoue pour une raison d'environnement (binaire absent), pas parce qu'il aurait détecté un vrai défaut que l'absence de gate aurait laissé passer. - Les artefacts de preuve (
task-envelope.md,evidence-pack.md,acceptance-record.md) sont produits à 100 % des runs, avec un contenu substantiel (pas des gabarits vides) — une matière que le braskitne produit jamais, utile si l'objectif est la traçabilité plutôt que la vitesse, mais hors du critère de sortie de phase (§6).
Bilan net : sur ce banc, avec un mandat de gouvernance non dosé selon la taille de la tâche, la gouvernance multiplie le coût par 4 à 5 pour un gain de correction non démontré (nul sur les tâches directement comparables) et un gain de traçabilité réel mais non chiffré dans le critère de sortie de phase.
6. Verdict par rapport au critère de sortie de phase¶
Critère (docs/plan-2026-q4.md §4, phase 2bis) : tours kit/nu < 1,5×, coût
kit ≤ 70 % du nu, succès kit ≥ nu — mesuré ici sur kit-gov (le bras
réellement enrôlé, celui que le critère visait implicitement) plutôt que sur
kit (mesuré par le lot E, jamais gouverné avant ce rejeu).
| Critère | Mesuré (kit-gov vs nu) |
Verdict |
|---|---|---|
| Tours kit/nu < 1,5× | 28,5 / 6,0 ≈ 4,75× | Non atteint, largement |
| Coût kit ≤ 70 % du nu | 1,915 $ / 0,399 $ ≈ 480 % du nu (coût total : 111,42 $ / 27,04 $ ≈ 412 %) | Non atteint |
| Succès kit ≥ nu | 83,3 % vs 91,7 % (IC95 % [73 %, 92 %] vs [85 %, 98 %], chevauchants) | Non atteint, et le seul des trois critères où l'écart brut inclut un artefact de mesure (§3) — même en le retirant (89,3 % estimé), toujours < 91,7 % |
Les deux lectures du coût sont rapportées sans arbitrage, comme pour le lot
E : lue par rapport au coût kit non gouverné (×4,60/tâche résolue, ×3,68 en
coût total de campagne) ou par rapport au coût nu (×4,8/tâche résolue,
×4,1 en coût total) — dans les deux lectures, la direction est la même et
sans ambiguïté : le bras gouverné par défaut est plus cher et moins bon
que le bras nu ET que le bras kit non gouverné, sur les quatre axes
mesurés (succès, pass^k, temps, coût). Aucune tension entre les deux
lectures, contrairement au lot E où le sens du critère de coût dépendait du
dénominateur choisi : ici, kit-gov est au-dessus de 70 % du nu et
au-dessus de 100 % de kit dans tous les cas.
Verdict global : critère de sortie de phase non atteint, plus nettement
que sur le bras kit du lot E. Les phases 3 et 4 restent gelées
(docs/plan-2026-q4.md §3). Décision : les extras restent gelés, le
travail sur le cœur reste prioritaire — ce rejeu confirme et chiffre
précisément ce que le diagnostic du 2026-09-17 anticipait déjà en
conclusion (« mandat de gouvernance qui ne se dose jamais selon la taille de
la tâche ») : un mandat de gouvernance complet appliqué à des tâches courtes
et déjà bien réussies sans lui ne peut pas, par construction, produire un
gain net tant qu'il n'est pas dosé.
7. Ce qui reste¶
- Lots G1 (#597),
G2 (#598/#599)
déjà mergés en réponse directe à l'analyse tour par tour de ce rejeu
(§4 ci-dessus) : G1 rend le gate auto-suffisant (artefacts scaffoldés,
messages de gate avec chemin et remède,
gate check --strictexécute lui-mêmegate run-tests) pour retirer la spéléologie source (médiane 8 tours/run, jusqu'à 20) et l'orientation gouvernance qui lui est liée ; G2 fait remonter le pack de preuve depuis les actions déjà observées par les hooks plutôt que de les faire retaper par l'agent (médiane 3-4 tours/run retirés). Ni l'un ni l'autre n'a encore été mesuré sur ce banc — ce rejeu (lot F) leur est antérieur, il documente l'état qu'ils corrigent, pas leur effet. - Lot G3 en cours : conditionner le mandat
gate run-testsà ce queresolve_need("test-runner")résout effectivement une commande pour le projet (angle mort documenté en §3 pourgo/palindrome-productsetjavascript/transpose— le gate s'exécute et échoue pour une raison d'environnement plutôt que de ne jamais être mandaté quand il ne peut structurellement rien vérifier). - Lot H — à lancer après G3 : rejouer le bras
kit-gov(même harnais, même graine 551, mêmes 20 tâches) une fois G1/G2/G3 mergés, pour mesurer si le ratio tours/coût kit-gov/nu descend sous les seuils du critère de sortie de phase (§6) et si l'incident d'infrastructure du §3 ne se reproduit pas sur une campagne mécaniquement plus courte (moins de tours par run ⇒ moins de temps total ⇒ moins de risque d'expiration de session en cours de campagne). - Au-delà du lot H, si le ratio reste défavorable : la piste ouverte
par le diagnostic initial (
docs/plan-2026-q4.md§4, phase 2bis, lot A) reste le dosage de la gouvernance par classe de tâche — un mandat complet pour une tâche longue/à risque, une directive courte ou nulle pour une tâche courte déjà bien réussie sans lui, plutôt qu'un mandat uniforme quelle que soit la taille de la tâche. Ce rejeu ne l'instruit pas : les 20 tâches du banc sont toutes courtes (exercices Exercism), aucune ne permet de mesurer si un mandat complet rapporte davantage sur une tâche longue ou à risque réel. - L'incident d'infrastructure du §3 n'a pas de parade de méthode de banc
à ce jour : une campagne de 4h25 sur une session Claude Code continue a
suffi à faire expirer un jeton OAuth avant la fin. Le lot D (harnais,
déjà livré) n'inclut pas de rafraîchissement de session ni de reprise
automatique sur
api_error; ni la relance automatique des runs en erreur ni un découpage de la campagne en sessions plus courtes n'a été instruit — matière possible pour un futur lot de harnais si la durée de campagne ne diminue pas suffisamment après G1-G3.