Rejeu du banc à trois bras après les lots A/B/C — 2026-09-17¶
Issue #582 (phase 2bis « Cœur »), lot E. Commit rejoué :
main@6d9db6c6(lot A #584, lot B #585, lot C #586, harnais lot E #589, tous mergés le 2026-09-17 avant le rejeu). Bras rejoué :kituniquement (--arms kit --resume,scripts/bench/three_arms.py, graine 551, mêmes 20 tâches) ; brasnu/eccrepris tels quels de la campagne du 2026-09-17 (docs/bench/diagnostic-surcout-kit-2026-09-17.md). Données brutes locales (Grimoire-Forge, hors dépôt kit), non poussées :_scratch/bench-e/workspace/state/{results.jsonl,selection.json},_scratch/bench-e/workspace/reports/2026-09-17/{report.md,report.json}. Coût réellement dépensé pour ce rejeu : 30,26 $ (60 runskit, aucun nouvel appelnu/ecc). Verdict : critère de sortie de phase partiellement atteint. Le surcoût de tours/temps est éliminé (lot A confirmé en isolation), mais le lot B n'a été exercé sur aucun des 60 runs de ce rejeu — constat détaillé en §3 — et le coût kit reste au-dessus, pas en-dessous, de 70 % du coûtnu. Les phases 3 et 4 restent gelées (docs/plan-2026-q4.md§3).
1. Méthode¶
- Worktree jetable
_scratch/kit-bench-esurorigin/mainfraîchement fetché (git log --oneline -8vérifié avant lancement : lots A, B, C et le harnais lot E présents), venv dédié (uv venv .venv --python 3.12 && uv pip install --python .venv/bin/python -e '.[dev]'). - Vérification préalable critique : sans précaution,
grimoiresurPATHrésout vers le venv global de la Forge (grimoire-kit 3.54.0publié sur PyPI, sans le lot B —grimoire standard gate run-tests --helpy échoue avec « No such command »). Le harnais lance ses sous-processusgrimoireviaenv = {**os.environ, "HOME": ...}: il hérite duPATHdu processus appelant, pas d'un chemin figé. Lancement de la campagne avecPATH="<worktree>/.venv/bin:$PATH"en préfixe explicite, vérifié avant lancement (grimoire standard gate run-tests --helprépond correctement dans cet environnement, avec--task-id). - Workspace
_scratch/bench-e/workspace:state/selection.jsonetstate/results.jsonlde la campagne du 2026-09-17 copiés à l'identique, lignes du braskitretirées (120 lignesnu/eccconservées sur 180 d'origine). - Campagne :
python scripts/bench/three_arms.py --full --resume --arms kit --seed 551 --label "lot E après A+B+C" --workspace .../bench-e/workspace, lancée en arrière-plan (nohup, PID consigné), suivie par intervalles espacés (15 puis 5 minutes en fin de campagne). Durée réelle : ~81 minutes (08:25–09:46 UTC) pour 60 runskit, plus rapide que l'estimation de 2-3 h — aucunrun-timeout-satteint, aucune boucle détectée, disque jamais sous le seuil de 5 Go, aucun identifiant oublié en fin de campagne. - Rapport généré directement par le harnais du lot E (
--label, notes de reprise pournu/ecc, détail par run du braskit) :_scratch/bench-e/workspace/reports/2026-09-17/report.md.
2. Résultats agrégés — avant (2026-09-17, pré-lots) / après (ce rejeu)¶
| Bras | Succès avant | Succès après | pass^k avant | pass^k après | Temps médian avant | Temps médian après | Tours médians avant | Tours médians après | Coût médian/tâche résolue avant | Coût médian/tâche résolue après |
|---|---|---|---|---|---|---|---|---|---|---|
| nu | 91,7 % | 91,7 % (repris) | 90 % | 90 % (repris) | 64 s | 64 s (repris) | — | 6,0 | 0,399 $ | 0,399 $ (repris) |
| ecc | 93,3 % | 93,3 % (repris) | 90 % | 90 % (repris) | 61 s | 61 s (repris) | — | 7,0 | 0,629 $ | 0,629 $ (repris) |
| kit | 90,0 % | 90,0 % | 85 % | 85 % | 189 s | 57 s | 19 | 6,0 | 1,247 $ | 0,416 $ |
Les lignes nu/ecc « après » sont les mêmes lignes results.jsonl que
« avant » (--resume sans les rejouer, cf. §1) : identiques par
construction, listées ici uniquement comme référence de comparaison pour le
bras kit.
Le bras kit change radicalement sur l'axe gouvernance (tours, temps, coût)
et reste identique à la lettre sur l'axe correction :
- Tours : 19 → 6,0 médians, ratio kit/nu 19/6 ≈ 3,17× → 1,0× (6,0/6,0 — nu est aussi à 6,0 tours médians sur ce rejeu). Cible du lot A (< 1,5×) très largement atteinte : le mandat de gouvernance était bien l'intégralité du surcoût de tours, confirmé en isolation.
- Temps médian : 189 s → 57 s, désormais sous le temps médian
nu(64 s) etecc(61 s). - Coût médian/tâche résolue : 1,247 $ → 0,416 $, soit 33 % de l'ancien
coût kit (sous le seuil de 70 % lu comme « par rapport à l'ancien coût
kit »). Lu par rapport au bras
nu— la formulation du critère de sortie de phase dansdocs/plan-2026-q4.md§4 (« coût kit ≤ 70 % du nu ») — le résultat est différent : 0,416 $ / 0,399 $ ≈ 104 % du coûtnu(coût total campagne : 30,26 $ kit contre 27,04 $ nu, ratio ≈ 112 %) — au-dessus, pas en-dessous, de 70 % du nu. Les deux lectures du critère sont rapportées ici sans arbitrage :docs/plan-2026-q4.md(§4, phase 2bis) est mis à jour en conséquence. - Succès et pass^k : strictement identiques à la campagne du 2026-09-17,
tâche par tâche (
kit, les 20/20 tâches ont le même nombre de succès et le même pass^k avant/après — vérifié par comparaison programmatique des deuxreport.json). 90,0 % de succès (contre 91,7 %nu, 93,3 %ecc) et 85 % de pass^k (contre 90 %/90 %) — écart inchangé, IC95 % très chevauchants (kit [81,7 %, 96,7 %] vs nu [85 %, 98,3 %] vs ecc [86,7 %, 98,3 %] sur pass^k [70 %, 100 %] vs [75 %, 100 %] vs [75 %, 100 %]) : la différence n'était déjà pas significative avant, elle ne l'est pas plus après.
3. Constat majeur : le lot B n'a été exercé sur aucun des 60 runs¶
_grimoire-output/evidence/*/test-run.json — la preuve que grimoire
standard gate run-tests a tourné (lot B) — est absent des 60 runs kit
de ce rejeu, sans exception (colonne « test-run.json (lot B) » du
rapport : non sur les 60 lignes).
Diagnostic (code lu, pas supposé) :
_is_governed(project_root)(lot A,src/grimoire/hosts/decisions/activation.py) exige_grimoire/standard/sur le disque avant même de regardertask-board.yamlou un profil enregistré. Sans ce dossier, le projet reçoit la notice courte (deux lignes) au lieu de la directive complète — c'est exactement le comportement voulu par le lot A.setup_arm_kit()(scripts/bench/three_arms.py) provisionne le braskitavecgrimoire init . --backend local --no-cockpitpuisgrimoire host sync --host claude— jamaisgrimoire standard init.- Vérifié en isolation sur ce worktree (
grimoire init . --backend local --no-cockpitdans un dépôt jetable) : l'arborescence produite contient_grimoire/_memory/,_grimoire/overrides/,_grimoire/kit/— aucun_grimoire/standard/, et aucuntask-board.yamlnulle part dans les 60 dépôts de tâchekitde la campagne.
Conséquence : _is_governed() renvoie False pour les 60 runs kit de ce
banc, quelle que soit la tâche — le bras kit du banc à trois bras ne passe
jamais par le chemin gouverné depuis le lot A, donc ne reçoit jamais la
ligne gate run-tests --task-id {task_id} que le lot B a ajoutée à la
directive gouvernée (_DIRECTIVE_TEMPLATE, claude_activation.py). Le lot B
est mergé, testé unitairement (rouge-avant/vert-après) et fonctionnellement
correct en isolation — mais structurellement invisible sur ce banc tel
qu'il provisionne le bras kit aujourd'hui. C'est la cause directe de
l'identité parfaite, tâche par tâche, des résultats go/palindrome-products
(kit 1/3, pass^k 0, inchangé) et javascript/transpose (kit 2/3, pass^k 0,
inchangé) entre l'ancien et le nouveau rapport — les deux tâches que le lot B
visait explicitement (docs/plan-2026-q4.md, ligne du lot B).
Ce n'est pas un défaut du lot B : c'est un défaut de méthode du banc, révélé
par le lot A. Avant le lot A, la directive complète partait sur toute
session avec les hooks installés, gouvernée ou non — c'est le bug que le lot
A corrige. Le lot A corrigeant précisément cela, le bras kit du banc — qui
n'a jamais simulé un projet réellement enrôlé (grimoire standard init) —
bascule du côté « non gouverné » et n'exerce plus jamais la directive, lot B
compris. Le gain de tours/coût du bras kit (§2) est donc probablement optimiste
par rapport à un projet qui aurait réellement adopté le standard.
4. 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.
| Critère | Mesuré | Verdict |
|---|---|---|
| Tours kit/nu < 1,5× | 1,0× (6,0/6,0) | Atteint, largement |
| Coût kit ≤ 70 % du nu | ≈ 104-112 % du nu | Non atteint |
| Succès kit ≥ nu | 90,0 % vs 91,7 % (non significatif, IC très chevauchants) | Non atteint au sens strict, indécidable statistiquement |
Verdict global : critère de sortie de phase non entièrement atteint. Les
phases 3 et 4 restent gelées (docs/plan-2026-q4.md §3). Le lot A est validé
sans réserve sur son objectif propre (éliminer le surcoût de tours d'un
mandat de gouvernance non dosé). Le lot B reste à valider — il n'a
simplement pas encore été mis à l'épreuve par ce banc.
5. Ce qui reste¶
- Fermer la lacune de méthode du banc avant tout nouveau rejeu utile du
lot B : ajouter
grimoire standard init(ou équivalent) àsetup_arm_kit()pour que le braskitreprésente un projet réellement enrôlé — sinon_is_governed()restera toujoursFalseet le lot B restera toujours invisible sur ce banc, quel que soit le nombre de rejeux. C'est un chantier de harnais (comme le lot E), pas un chantier produit. - Rejouer
go/palindrome-productsetjavascript/transposeune fois cette lacune fermée — ce sont les deux tâches que le lot B visait explicitement ; tant qu'elles n'ont pas été rejouées avecgate run-testseffectivement invoqué, l'effet du lot B sur le pass^k reste une hypothèse non testée, pas un résultat. - Le coût kit est proche de la parité avec
nu(≈104-112 %), pas spectaculairement supérieur comme avant (×3,1), mais pas non plus sous le seuil de 70 % exigé par la phase. Rien dans les lots A/B/C n'agissait directement sur le coût par tour restant (le rapport §1.4 du diagnostic notait déjà un coût/tour kit inférieur à celui de nu — l'écart de coût total vient du nombre de tours, désormais aligné ; l'écart résiduel est marginal et pourrait se résorber ou s'inverser sur un échantillon plus large, n=60 par bras étant faible pour trancher à 10 points de pourcentage près). - La décision hors-lot sur la cascade de dispatch reste inchangée : aucun élément de ce rejeu ne la réexamine (hors périmètre du lot E).