v8.2.0
⚠ Ruptures
- nommage: les 73 slugs du roster sont renommés — toutes les invocations changent, aucun ancien nom ne survit comme alias, et une réinstallation (
./install.sh) est requise./ulk:brucedevient/ulk:aiguilleuse,/ulk:godspeeddevient/ulk:triageuse,/ulk:peondevient/ulk:pointeuse,/ulk:analyzedevient/ulk:controleuse,/ulk:vpsdevient/ulk:intendante-vps, les sixdeploy-*deviennentlivreuse-*; 71 slugs bougent au total,archivisteetsondeuseétaient déjà conformes. C'est le geste que l'entrée du 2026-09-01 avait explicitement écarté — elle chiffrait la voie slug à 787 illustrations, 144 fiches et 72 golden tests, et concluait « pour le même bénéfice de lisibilité ». Le chiffrage était juste (1 181 fichiers renommés, dont 568 illustrations), la conclusion non : le bénéfice n'est pas la lisibilité, c'est que le nom d'invocation cesse de demander une référence. Une épithète répare la fiche du site ; elle ne répare pas la ligne qu'on tape. Tant que le slug restaitgodspeed, l'auditagents-naming.md§ Le test échouait à l'endroit exact où il compte — au moment d'invoquer. Le corollaire est que le garde-fou 4 devait tomber d'abord : il posait qu'un nom publié ne disparaît jamais, au motif de scripts et d'utilisateurs tiers qui, sur un dépôt à mainteneur unique, n'existent pas. Il a été réécrit le 2026-09-01 (« renommer, c'est remplacer »), et c'est cette réécriture qui rend la passe possible sans traîner 71 alias morts. Les 22 alias qui restent déclarés désignent des cibles vivantes — les cinqcontroleuse-*et les dix-sept*-vpssont des fichiers d'action, pas des noms retirés. Tombe avec la passe l'exemption des neuf agents « à nom fonctionnel » (bruce,analyze,vps, les sixdeploy-*) : elle laissait le roster à deux régimes de nommage, l'un tenu par les six garde-fous, l'autre par personne ;agents-naming.md§ Ce qu'on ne renomme plus la remplace par une liste de noms stables, ce qui est une règle et non une dispense. Ce que la passe a fait remonter, et qu'aucun gate ne voyait : neuf textesPedagogy()du catalogue d'install dépassaient leurs bornes une fois les noms allongés — la borne existait, la marge était fortuite ; l'allowlistEXTENDS_ALLOWd'agents-coherence.test.mjsgelait six agents sous leur ancien slug, et le test « l'allowlist ne contient pas d'entrée périmée » les a tous nommés d'un coup ; et l'empreinte CSP du<script>de la page d'accueil a bougé, parce que le jeu de cartes embarque les noms — un renommage de slug est donc aussi un changement de header HTTP, ce que rien n'annonçait avant quecheck-csp.mjsne le refuse.
Ajouté
skills:
ulk skills lockaccepte--except <skill>— l'entrée nommée garde son empreinte épinglée, et son amont n'est pas interrogé. Né du gel degstackle 2026-09-01, dont la nouvelle version amont exécutait un binaire local pilotant ses propres règles de préambule :lockn'avait alors aucun ciblage, si bien que geler une entrée imposait de tout re-locker puis de restaurer la ligne à la main dans le JSON — un geste hors outil, non reproductible, et qu'unlockultérieur défaisait en silence. Le gel ne vivait nulle part ailleurs que dans la mémoire de qui l'avait posé. Trois choix portent la correction. Le flag refuse un nom absent du catalogue ou jamais épinglé, au lieu d'écrire le manifeste quand même : un--except gstakmal orthographié re-lockerait exactement l'entrée qu'on voulait protéger tout en annonçant un succès, et un gel qui échoue en silence est pire que pas de gel — on cesse de vérifier ce qu'on croit tenu. L'amont d'une entrée gelée n'est jamais relevé : le relever pour jeter le résultat dépenserait du quota API à produire une information déjà écartée, et ferait échouer le gel le jour où le dépôt est injoignable, alors qu'un gel n'a aucune raison de dépendre du réseau. Enfin le compteurfrozenest tenu à part dekept:keptest subi — l'amont n'a pas répondu — quandfrozenest voulu, et les fondre présenterait un incident réseau comme une décision, donc ferait cesser de relever un amont devenu muet. La sortie annonce chaque gel avec son empreinte et rappelle queulk skills verify --upstreamsignalera ces entrées comme divergentes : c'est le gel, pas une régression.agents:
sondeuse(94) entre au roster — l'enquête par les données en cinq temps (protocole, ressources, qualification, analyse, valorisation), une action chargée à la demande par temps. Agent-geste de la sphère des oracles, dérivé d'un profil réel anonymisé ; sa conviction fondatrice est qu'une donnée n'a de valeur que si l'on sait à quelle question elle répond, d'où elle vient, et à qui on va la raconter. Le roster passe de 72 à 73 et le prochain identifiant libre à 95 ;docs/Nouveaux Agents/est exclu du versionnage — c'est de la matière brute, avec les données personnelles qui vont avec. Deux sources MIT entrent dansskills-sources.jsoncomme optionnelles de l'agent, citées « si installées » et délibérément horsloads_skills:thematic-analysis(grille de codage qualitatif, Temps 4) etdata-science-claude-skills(auto-eda, dataset-doctor, experiment-tracker, paper-to-code, sql-optimizer, Temps 3). 35 → 37 skills externes aux crédits.gates: deux oracles ferment la même cause — aucun gate ne reliait une doctrine à l'existence de ce qu'elle prescrit.
check-dismantledrefuse qu'une doctrine vivante prescrive de l'infrastructure démantelée : c'est le mode d'échec qui avait justifié la sortie de Faru, retrouvé intact une couche plus haut. Le dépôt avait supprimé les symptômes — 139 fichiers, 12 751 suppressions — sans traiter ce qui les avait laissés mentir, si bien que le résidu ne s'est vu que des semaines plus tard, par un audit ; sans ce gate, chaque démantèlement futur aurait laissé le même et aurait été découvert de la même façon.check-agent-naming.cjsen est l'instance appliquée au nommage : les garde-fous 1, 2, 5 et 6 deagents-naming.mdne tenaient que parce que quelqu'un les relisait au bon moment, et la passe épithète du 2026-09-01 a montré ce que ça laisse passer — trois tautologies de sphère publiées sur le site (celebrimbor« forgeron » chez les Forgerons,cerbereetchromie« gardien·ne » chez les Gardiens) et une épithète annonçant cinq masques pour six modes, parce que rien ne comparait une épithète à sa source. Les deux tournent au pre-commit. (#723, #734)outils:
todo-triage.cjstrie mécaniquement les TODO/FIXME/HACK/XXX du dépôt. Le chiffre brut — 254 occurrences — surestimait la dette sans qu'aucun moyen ne permette de le dire sans relire les 254 : la majorité vit sous_archived/ou dans des gabarits de sortie, oùXXXest un masque de numéro et non une dette. Le tri appliqué, la dette réellement vivante tombe à 2 gabarits. (#733)
Modifié
nommage: les 72 épithètes du roster passent au corpus des métiers humains ordinaires, dans la couche épithète et nulle part ailleurs. Le corpus de 67 remplacements avait été proposé le 2026-08-28 par astride (
docs/reports/astride-nommage-arcanes-2026-08-28.md) comme une vague de renommages ; le garde-fou 5, écrit le même jour mais après le rapport, arbitre l'inverse — un métier proposé comme nouveau nom est presque toujours une épithète qui s'est trompée d'emplacement. Appliquer le corpus là où il appartient coûte 65 lignes de frontmatter ; l'appliquer aux slugs aurait touché 787 illustrations, 144 fiches de site et 72 golden tests pour le même bénéfice de lisibilité, et cassé les invocations. 65 épithètes réécrites, 7 laissées telles quelles parce qu'elles satisfaisaient déjà la forme (archiviste,bruce,benjamin,shuri,hardcode,peon,jony). Mesure après passe sur les 72 : 0 majuscule initiale, 0 point final, 0 article initial, médiane 4 mots — la grammaire du garde-fou 5 tient sur l'ensemble, pas seulement sur les nouvelles. La répartition des têtes passe à 57 féminines, 10 épicènes, 4 masculines plus la citation depeon: c'est le correctif du garde-fou 3 appliqué là où il ne coûte rien, et réversible à la même adresse — un genre d'épithète se change sans toucher une commande. Trois tautologies de sphère tombent au passage (celebrimbor« forgeron » chez les Forgerons,cerbereetchromie« gardien·ne » chez les Gardiens), que le garde-fou 1 interdisait déjà sans que rien ne le vérifie. Et un défaut factuel qu'aucun gate ne pouvait voir, parce qu'une épithète est de la prose :blackemperorannonçait « empereur aux cinq masques » pour six modes — la correction d'août n'avait touché quedescriptions-fr.jsoncôté site, jamais l'épithète du registre. Deux doc citaient l'ancien état comme exemple vivant et sont réalignées ;agents-naming.md§ 5 consigne la passe et sa mesure, pour que la prochaine proposition de renommage trouve le précédent au lieu de le refaire.cli: la TUI passe à Charm v2 — bubbletea 2.0.9, bubbles 2.2.1, lipgloss 2.0.6, sur les deux modules Go. Migration d'API sur toute la surface : imports
charm.land/*/v2,View()rend untea.View(l'écran alternatif se demande désormais sur la vue,tea.WithAltScreenetWithMouseCellMotionn'existent plus),KeyMsgdevientKeyPressMsgavec.Codeet.Textlà où.Typeet.Runesservaient,lipgloss.Colorcède la place àcolor.Color. Le point que le compilateur ne voyait pas : lipgloss v2 rend toujours en couleurs vraies et ne regarde plus si la sortie est un terminal, ce qui aurait injecté des séquences ANSI dans tout pipe et tout fichier.tui/output.goécrit donc vialipgloss.Fprint*, rétablissant la parité v1 — sortie non-TTY dépouillée,NO_COLORetCLICOLOR_FORCEhonorés ; vérifié,ulk status | cat -vne contient aucune séquence d'échappement. La règlerenovate.jsonqui désactivait le major Charm après trois reverts (#381/#382, #410/#411, #532) est retirée : Charm rentre dans le droit commun des majors Go, à approbation Dashboard. (#727)agents: le backlog des agents suit la bascule vers GitHub issues, décidée en 8.1.2 et jusque-là appliquée aux seules cartes.
shuriécrit spec et todo sur des issues et sait migrer un board Obsidian existant,task-runner,peonetgodspeedlisent le backlog là,scullyvérifie des issues, et les protocoles_sharedne parlent plus de cartes. Le HARD-GATEgate: spec-requireddebruceest retiré : il exigeait une surface qui n'existe plus, donc il bloquait sur une condition devenue insatisfiable. C'est le reste que la bascule avait laissé câblé — retirer les cartes sans reprendre ce qui les lisait aurait reconduit d'un cran plus bas exactement le défaut qu'elle corrigeait. (#713, #714, #715, #716)agents: les impératifs TOUJOURS/JAMAIS cèdent la place aux critères qui portent leur raison —
astride,tony,georges,agathe,sauron, plus 182 en-têtes reformulés. Un impératif dit quoi faire sans dire pourquoi : il se suit tant qu'il est lisible et se contourne dès qu'il gêne, alors qu'un critère se discute et se mesure. Les blocs générés sortent du compte au passage — ils sont réécrits par un générateur, les compter revenait à mesurer l'outil plutôt que la prose. (#725)gates: le seuil d'impératifs de la cheat sheet passe de 800 à 1200, sur une base assainie. Le seuil datait d'avant #725, posé comme la moitié de la mesure d'alors (1525, blocs générés compris) : la base a changé deux fois, la cible jamais, si bien que 1341 > 800 en permanence — et un ⚠ qui ne s'éteint jamais n'est plus un signal, c'est du décor. 1200 = base mesurée (1107) plus environ 8 % de marge, donc le vert redevient atteignable et la première retombée au rouge dit quelque chose. La regex cesse de compter les citations et le code — blocs, code inline, lignes
>, guillemets — soit 234 occurrences de bruit sur 1341 ; un signal de concentration par fichier s'ajoute au-delà de 25 (maximum mesuré : 20), un dossier qui accumule étant plus parlant qu'un total qui dilue. Non bloquant, et maintenu tel : un impératif peut protéger une perte irréversible, la conversion reste un jugement. (#740)site:
spheres.jsondevient la source unique des sphères —FIGURESy est absorbée,FAMILLESetSOCLESéchouent bruyamment plutôt que de diverger en silence, et neuf énumérations tombent à quatre. Le gate qui vérifiait la concordance des clés disparaît avec son objet : une énumération qui ne peut plus diverger n'a plus besoin d'être surveillée. (#722)
Corrigé
credits:
CREDITS.mdne publie plus de lien portant une expansion shell. Le générateur dérive le lien d'un outil CLI de la première URL trouvée dans sa recette d'installation, et écartait déjà les substitutions$(…)et les backticks — « pas un vrai lien ». Épingler la recette obscura à sa version (ci-dessous) a produit le cas qui manquait, la variable simple :.../releases/download/$VER/...est une URL correcte dans le shell qui la développe, et un lien mort partout ailleurs.CREDITS.mdétant publié, il a annoncé une release nommée « $VER ». La garde couvre maintenant toute expansion ; obscura retombe sur la page de son dépôt, qui est de toute façon la meilleure cible pour un lien de crédits. Le contrôle ajouté porte sur l'artefact et non sur la fonction — aucun lien deCREDITS.mdne contient$ni backtick — pour qu'une future source de liens en hérite sans qu'on y pense. (#742)agents: les gabarits de nom de fichier sous
docs/disentYYYY-MM-DD, le format que tous les fichiers réels pratiquent. La doctrine était partagée en deux :report-formats.mdet une trentaine de corps d'agents prescrivaient<agent>-YYYYMMDD.md, seize autresYYYY-MM-DD, etdocs/audits/README.mdénonçait le second — les treize fichiers datés dedocs/audits/etdocs/reports/sont tous enYYYY-MM-DD, aucun en compact. Le format compact a existé (six noms dans l'historique, tous horodatés-HHMM), il a été abandonné dans les faits sans que la doctrine le sache. Alignement de 99 lignes sur 49 sources, arbres générés resynchronisés ;dreddgarde son heure (YYYY-MM-DD-HHMM). Les noms de branche, de sauvegarde (.bak-,doc-backup-) et de dossier de captures (.visual-audit-) ne sont pas des rapports et restent tels quels. Suite de #731. (#737)audits:
docs/audits/etdocs/reports/portent chacun un README qui dit ce qui vit chez l'un et pas chez l'autre. La convention existait (framework/agents/CLAUDE.md§ Convention) mais n'était lisible que depuis là : l'audit sargeras du 2026-09-01 a trouvédocs/audits/presque vide — résidu du nettoyageb4d2b28a, 21 fichiers retirés — et en a conclu à deux dossiers pour un même genre, et à unaudit-to-backlog.cjsqui n'en connaîtrait qu'un. Le script prend le rapport en argument et n'en connaît aucun ; le critère qui sépare audit et rapport — la récurrence — est désormais écrit dans les deux README et dansbase-rules/report-formats.md, là où un auditeur choisit son dossier. (#731)skills:
ulk skills verify --upstreamne conseille plus une commande qui défait ce qu'il vient de signaler. Sur une divergence, il renvoyait versulk skills lock— seule issue quand le lock ne savait pas geler. Depuis--except, ce conseil est devenu piégeux : unlocksans argument re-locke tout, donc adopte aussi l'entrée qu'on tenait délibérément gelée, et le fait en silence puisque adopter un amont est exactement ce que la commande est censée faire. On suivait le message affiché par l'oracle et on perdait le gel que ce même oracle venait de relever comme divergent. La sortie présente maintenant les deux issues côte à côte — adopter, ou geler — avec les noms divergents pré-remplis dans la commande--except(le flag refusant un nom inconnu, recopier à la main ce que la sortie affiche déjà n'ouvrirait qu'une occasion de faute de frappe), et dit explicitement qu'unlocknu adopte tout ce qui a bougé. Le conseil est rendu par une fonction pure, commeunreachableAdvice(#607) : un conseil peut être faux, il doit pouvoir être mis en échec par un test.hooks: le pre-commit ne peut plus se lire « vert » en taisant ce qu'il n'a pas exécuté. Il portait seize
if command -v … ; thensanselse, plus la garde[ -f hardcode-check.sh ], et aucune ligne de verdict : un outil absent faisait taire son oracle sans que rien ne le dise, et le silence se lisait comme un succès — le défaut que le registre des skips avait fermé côté pre-push, retrouvé intact côté commit, dans un dépôt dont les hooks sont le seul gate. Le mécanismeSKIP_LOG/skip_notequitte donc le pre-push pourscripts/hooks/lib-skip.shet gagneskip_verdict, plutôt que d'être recopié : deux copies d'un registre divergent en silence, ce qui est précisément le mode de défaillance qu'il existe pour fermer. Le verdict reste non bloquant des deux côtés — un gate plus strict qui devient pénible se contourne, et un gate contourné est pire qu'un gate absent ; ce qui change n'est pas le droit de commiter, c'est l'impossibilité de lire « vert » sans lire à la même ligne ce qui n'a pas tourné. La mesure a corrigé l'énoncé au passage : les quatorze gardesnodene sont pas atteignables par l'absence de node, le § 1 échoue bruyamment avant elles ; les sauts réellement silencieux étaient shellcheck, gofmt et hardcode-check. (#741)gates: sept evals « live » déclarés mais jamais exécutés apparaissent désormais comme tels dans le verdict du pre-push. Skippés faute de clé d'API, ils donnaient une couverture apparente sans couverture réelle — et ils portent précisément ce que les tests structurels ne peuvent pas voir, le comportement réel des agents. Un test qui ne tourne jamais est une croyance, pas une mesure ; à défaut de tourner, il doit au moins se déclarer. (#732)
site: deux défauts d'accessibilité relevés par l'audit du 2026-09-02, nés de la même cause — une règle écrite pour la prose et appliquée trop large. Contraste (WCAG 1.4.3 AA) :
.prose :global(pre)compilait en.prose.astro-hash pre, de spécificité supérieure à.astro-code, donc le fond clair de la page écrasait celui de Shiki pendant que les six couleurs de token restaient calibrées pour un fond sombre — en thème clair, #E1E4E8 sur #EBE3F7 mesure 1,02:1 là où AA exige 4,5, les cinq autres montant au mieux à 3,87:1. Seul le fond étant disputé, il passe souspre:not(.astro-code)tandis que la géométrie du bloc reste commune : poser le:not()sur la règle entière, comme le suggérait le rapport, aurait privé tous les blocs Shiki de leur remplissage et de leur conteneur de défilement — la correction d'un contraste aurait cassé la mise en page de 73 fiches. Niveaux de titre (WCAG 1.3.1 A) : une carte d'arcane porte unh3et les pages de sphère n'avaient aucunh2entre leurh1et lui ; unh2sr-onlyest posé avant le paquet, le bandeau annonçant déjà le compte à l'écran. L'index du panthéon obtenait déjà ce résultat par un autre chemin — c'est cette asymétrie, jamais écrite, qui a fait passer le défaut pour corrigé pendant un mois. Les 12 pages de métier, hors du périmètre compté par l'audit, portaient le même défaut et sont corrigées avec. (#743)doc-counts: le filet des comptes nus s'étend à
.claude/rules/, qui porte la même prose que le site sans que rien ne la surveille — « 85 agents » y a survécu douze incréments de roster, dans deux fichiers. La cause passe avant les symptômes :checkBareCountsbalaie une liste de racines au lieu du seulsite/src, etBARE_COUNT_ALLOWaccepte une seconde forme de clé (chemin:terme) pour le fichier dont le terme est hors sujet de bout en bout —install-reference.mdcompte dix fois un bundle tiers, jamais le catalogue. La forme étroite reste le défaut :agents-authoring.mdn'obtient aucune exemption large sur « agent », son sous-ensemble est exempté à la ligne près et son défaut de roster reste attrapable. 17 emplois, 4 signalés, 13 déclarés ; les quatre signalés sont rattachés à leur source.agents-naming.mdrejoint au passage l'index des règles scopées deCLAUDE.md, où il manquait depuis sa création. (#745)nommage: la
fonction:dechromietenait en deux mots — « non-régression visuelle » nommait la technique, pas le métier. Le garde-fou 6 fixe 3 à 6 mots pour une raison mesurée : la fonction est ce que lit qui n'a pas la référence, et sous trois mots elle redevient une étiquette technique, donc elle ne remplit plus la colonne qu'elle est censée tenir. Seul écart sur les 72 après la passe épithète, et aucun gate ne pouvait le voir — la longueur d'une fonction est de la prose, comme l'épithète.check-agent-naming.cjsle voit désormais.gitignore: les copies
impeccable-*.mdécrites sous.claude/agents/rejoignent celles déjà ignorées sous.github/agents/. L'installeur du skill produit les deux jeux, seul le second était couvert depuis le correctif du 2026-08-12 : quatre fichiers restaient non suivis à chaque session sans qu'aucune règle ne les vise.
Sécurité
install: la recette d'installation d'obscura cesse d'exécuter du code non vérifié. Elle téléchargeait depuis
releases/latest— une cible qui change sous vous —, sans somme de contrôle, vers un chemin/tmpprévisible, puis faisaitsudo mvvers/usr/local/bin/: CWE-494 doublé d'un CWE-367, la fenêtre entre l'écriture dans/tmpet lemvprivilégié. Les quatre faiblesses tombent ensemble — version épinglée àv0.2.1,mktemp -dà la place de/tmp,shasum -a 256 -c -(macOS) /sha256sum -c -(Linux) avant toute extraction, etsudo install -m755qui réduit la part privilégiée à la copie finale. Vérifié par rejeu de bout en bout plutôt que par lecture : empreinte correcte → sortie 0 et les binaires en place ; empreinte falsifiée →shasum: WARNING: 1 computed checksum did NOT match, sortie 1, rien d'installé. L'amont ne publie aucun fichier de sommes : les deux empreintes sont calculées sur les archives du 2026-09-03, et un bump de version impose de les recalculer — c'est écrit dans lanotede l'entrée, faute de quoi le prochain bump les recopierait telles quelles. Un défaut d'exécution tombe au passage, invisible à la lecture de la recette : l'archive porte deux binaires,obscuraappelleobscura-workerà l'exécution, et l'ancienne recette ne posait que le premier — l'outil était donc installé cassé depuis le 2026-05-07. Ce que la correction ne fait pas, écrit dans la note pour ne pas être relu comme un acquis : l'architecture reste choisie sur la seule foi de la plateforme, donc un Mac Intel reçoit toujours l'archive arm64 ; l'empreinte vérifie l'archive, pas son adéquation à la machine. (#742)install: aucune nouvelle recette d'installation ne peut plus télécharger du code sans en vérifier l'empreinte — un cliquet au pre-commit, pas une campagne. Le chiffre qui a ouvert l'issue, « 198 recettes sur 204 sans somme de contrôle », était le mauvais dénominateur : il comptait
brew install,apt installetnpm i -g, qui délèguent l'intégrité à leur gestionnaire et n'ont rien à porter. Sur les 273 recettes du registre, 30 téléchargent réellement ; 6 déclarent un sha, 3 se vérifient elles-mêmes, 7 sont excusées par leunpinned_reasonposé par l'audit #466 pour les entrypoints vendeurs servis en continu. La dette réelle est de 14, et c'est elle queframework/tools/install-checksum-debt.jsonfige, entrée par entrée, épinglée par l'empreinte de la recette. D'où trois propriétés que ne donne pas une liste d'exceptions : modifier une recette de dette la fait redevenir rouge, puisque son empreinte ne correspond plus ; une entrée devenue inutile — recette vérifiée depuis, ou disparue du registre — est elle-même une erreur, donc résorber consiste à supprimer une ligne ; et le registre ne peut que rétrécir. Le check porte un second oracle, qui n'est jamais grand-père : déclarer un<plateforme>_sha256sur une recette qui n'est pas de la forme exactecurl <url> | shest refusé. Le champ route versinstaller.VerifiedInstall, dontParseCurlPiperejette tout le reste, etbundle.go:113compte l'erreur enfailed++sans repli : le sha casserait l'installation au lieu de la protéger. C'est ce qui a rendu inapplicable le correctif tel que l'issue le formulait pour obscura — « router parVerifiedInstall» — et la recette a été durcie dans le shell à la place. Le second temps de l'issue, résorber les 14, n'est pas dans ce lot : il est long, et le cliquet existe précisément pour que la dette ne croisse pas pendant. (#742)