La vérification existait sous un drapeau, et pour Ubuntu SEULEMENT : les
autres images arrivaient sans que rien ne les regarde, alors que leurs
éditeurs publient tous une somme. Six y entrent, relevées sur les dépôts
plutôt que devinées — Debian est en sha512, les familles RHEL nomment
« CHECKSUM », Rocky l'écrit en forme BSD, Arch et openSUSE posent une
somme par image. Fedora en reste dehors, son fichier portant un numéro de
construction que l'URL de l'image ne donne pas.
Injoignable n'arrête plus rien, un écart si. Le menu du cache sait aussi
oublier une URL, « --detient » faisant l'aperçu.
--- EN ---
Verification existed behind a flag, and for Ubuntu ONLY: the other images
arrived with nothing looking at them, though every publisher ships a sum.
Six now enter, read off the repositories rather than guessed — Debian is
sha512, the RHEL families name it "CHECKSUM", Rocky writes the BSD form,
Arch and openSUSE ship a sum per image. Fedora stays out, its file
carrying a build number the image URL does not give.
Unreachable no longer stops anything, a mismatch does. The cache menu can
also forget one URL, "--detient" serving as the preview.
Assisted-by: Claude Opus 5
Un invité sans magasin de confiance ne peut RIEN recevoir, et sur un hôte
Proxmox c'est l'HÔTE qu'il faut excepter : imbriqué, l'invité sort masqué
derrière lui et le pont ne voit jamais sa propre adresse. Mesuré depuis
l'invité : code 000 et vérification SSL 19, puis 200 et 0. Sans cela le
gestionnaire de paquets se rabattait sur 564 dérivations à construire.
Le journal par VM ne portait AUCUN message du menu : la cause vivait sur
une console qui défile, et le fichier qu'on rouvre après l'échec n'avait
que le symptôme. Et « db_drop_all » annonçait détruites des bases qui ne
l'étaient pas, son code de retour jeté.
--- EN ---
A guest with no trust store can receive NOTHING, and on a Proxmox host it
is the HOST that must be exempted: nested, the guest leaves masqueraded
behind it and the bridge never sees its own address. Measured from the
guest: code 000 and SSL verification 19, then 200 and 0. Without it the
package manager fell back to building 564 derivations.
The per-VM log carried NO message from the menu: the cause lived on a
console that scrolls away, and the file reopened after failure held only
the symptom. And "db_drop_all" announced databases as dropped that were
not, its exit status discarded.
Assisted-by: Claude Opus 5
Le détournement est TRANSPARENT et vaut pour tout le pont : ne pas donner
l'autorité à une VM ne la dispense pas d'être interceptée, elle échoue sur
« self-signed certificate in certificate chain ». NixOS n'a pas d'ancre de
confiance par fichier, et la poser par déclaration arriverait trop tard —
la première reconstruction EST le premier téléchargement. La VM est donc
exceptée par son adresse MAC, avant sa création, et cela se dit.
L'image Proxmox n'est mise en place qu'une fois complète : « wget -O »
écrivait dans la cible, et une coupure y figeait un fichier tronqué que
le test de présence acceptait à chaque déploiement suivant.
--- EN ---
Interception is TRANSPARENT and covers the whole bridge: withholding the
authority from a VM does not spare it, it fails on "self-signed
certificate in certificate chain". NixOS has no per-file trust anchor, and
declaring one would come too late — the first rebuild IS the first
download. The VM is therefore exempted by MAC, before creation, and it is
said.
The Proxmox image is put in place only once complete: "wget -O" wrote into
the target, and an interruption froze a truncated file there that the
presence test accepted on every later deployment.
Assisted-by: Claude Opus 5
Une machine, une question binaire : le chemin que le MENU emprunte
aboutit-il sur un système déclaratif. La commande d'installation est celle
du menu, prise telle quelle — un test qui installerait par ses propres
soins prouverait SON chemin, pas celui du produit, et c'est là que les
pannes se cachaient.
Le verdict est l'ÉTAT de la machine, non un code de retour :
« nixos-rebuild » rend 4 quand une unité n'a pas redémarré alors que le
système est activé. Il vit dans long_test/ parce qu'il crée une VM et
prend des heures ; le lanceur unitaire doit rester lançable en secondes.
--- EN ---
One machine, one binary question: does the path the MENU takes succeed on
a declarative system. The install command is the menu's, taken as is — a
test installing by its own means would prove ITS path, not the product's,
and that is where the failures hid.
The verdict is the machine's STATE, not an exit code: "nixos-rebuild"
returns 4 when a unit failed to restart while the system is active. It
lives in long_test/ because it creates a VM and takes hours; the unit
runner must stay runnable in seconds.
Assisted-by: Claude Opus 5
Neuvième système du catalogue, et le seul dont aucune distribution ne
publie d'image cloud : celle d'un tiers est épinglée et sa somme vérifiée
à chaque téléchargement. Son seed diffère aussi — networkd prend la clé
d'un bloc pour un NOM d'interface là où netplan honore un « match: », et
sshd refuse un compte dont le shell n'existe pas, d'où /bin/sh.
Ses dépendances se DÉCLARENT dans conf/nixos/erplibre.nix : envfs répond
à /usr/bin/env, nix-ld donne aux roues manylinux leur chargeur. Vérifié
sur une VM : le verrou de 362 paquets s'installe et Odoo répond.
--- EN ---
Ninth system of the catalogue, and the only one no distribution publishes
a cloud image for: a third party's is pinned and its sum verified at every
download. Its seed differs too — networkd reads a block's key as an
interface NAME where netplan honours a "match:", and sshd refuses an
account whose shell does not exist, hence /bin/sh.
Its dependencies are DECLARED in conf/nixos/erplibre.nix: envfs answers
/usr/bin/env, nix-ld gives manylinux wheels their loader. Checked on a VM:
the 362-package lock installs and Odoo answers.
Assisted-by: Claude Opus 5
Le cache ne diminuait jamais de lui-même et vit sur le disque de
l'orchestrateur : un parc qui déploie chaque jour finissait par le remplir,
sans que rien ne le signale. Deux règles existent désormais, éteintes par
défaut et réglées depuis le menu : l'âge du dernier usage, et un plafond de
taille qui efface le moins récemment servi jusqu'à tenir dessous, objets et
dépôts git confondus. Un minuteur systemd quotidien les applique, et une
réinstallation garde les valeurs choisies. Le menu montre d'abord ce qui
partirait.
--- EN ---
The cache never shrank by itself and lives on the orchestrator's disk: a site
deploying every day filled it up with nothing to say so. Two rules now exist,
off by default and set from the menu: the age of last use, and a size ceiling
that erases the least recently served until the cache fits under it, objects
and git mirrors alike. A daily systemd timer applies them, and a reinstall
keeps the chosen values. The menu first shows what would go.
Assisted-by: Claude Opus 5
A Proxmox VM always used the serial console as its display, so nothing inside
it could render. The form offers 3D: the VM gets « --vga virtio-gl » and the
account joins the render and video groups in the guest, which « qm set »
cannot write; without them GL falls back to software while VIRGL reports
success. The box is offered only where the host has a render node, VIRGL, GL
and EGL, Proxmox refusing to start the VM otherwise once its disk is written.
What is missing is named with its package, and a button installs it, then
reads the host again. Covered by test/test_proxmox_form.py.
--- FR ---
Une VM Proxmox prenait toujours la console série pour affichage : rien ne
pouvait y rendre. Le formulaire offre la 3D : la VM reçoit « --vga virtio-gl »
et le compte entre dans les groupes render et video dans l'invité, ce que
« qm set » ne sait pas écrire ; sans eux, GL retombe en logiciel alors que
VIRGL annonce un succès. La case n'est offerte que si l'hôte a un nœud de
rendu, VIRGL, GL et EGL, Proxmox refusant sinon de démarrer la VM une fois son
disque écrit. Ce qui manque est nommé avec son paquet, et un bouton le pose
puis relit l'hôte. Couvert par test/test_proxmox_form.py.
Assisted-by: Claude Opus 5
Without pipefail, « curl … | sh » returns the status of sh, 0 on empty input:
a failed download passed for an install, its fallback never ran, and the
failure surfaced later as « command not found ». mise and pyenv are downloaded
into a mktemp file, then run, and a check refuses any fallback behind a pipe
without pipefail. starship's installer runs as root under a root timeout, never
reaching the « sudo -v » that sudo-rs refuses, and its shell hook is guarded.
Each tool then reports the version found or « not installed », without
tripping « set -e ».
--- FR ---
Sans pipefail, « curl … | sh » rend le statut de sh, 0 sur une entrée vide :
un téléchargement raté passait pour une pose, son repli ne tournait jamais, et
l'échec se lisait plus loin sous « command not found ». mise et pyenv sont
téléchargés dans un fichier tiré par mktemp, puis exécutés, et un contrôle
refuse tout repli derrière un tube sans pipefail. L'installateur de starship
tourne en root sous un délai root, sans atteindre le « sudo -v » que sudo-rs
refuse, et son crochet de shell est gardé. Chaque outil dit ensuite la version
trouvée ou « non installé », sans faire tomber « set -e ».
Assisted-by: Claude Opus 5
Ten of the eighteen entries carried no pictogram, and the eye picks one out
of such a list before it reads. Each takes the icon its siblings already use
elsewhere in the tool — 📋 lists, 🧹 cleans, 📊 measures, 🔍 previews, 📥
downloads, 🗂 catalogues, 🖥 tunnels a desktop — so the same picture keeps
meaning the same thing from one menu to the next. A table in the tests holds
the pairing, in both languages, and checks the menu still shows the entries
it freezes.
--- FR ---
Dix des dix-huit entrées ne portaient aucun pictogramme, et l'œil s'y repère
avant de lire. Chacune reçoit l'icône que ses semblables portent déjà
ailleurs dans l'outil — 📋 liste, 🧹 nettoie, 📊 mesure, 🔍 prévisualise, 📥
télécharge, 🗂 catalogue, 🖥 ouvre un bureau —, si bien qu'une même image
garde le même sens d'un menu à l'autre. Un tableau dans les tests fige le
couple, dans les deux langues, et vérifie que le menu affiche encore les
entrées qu'il fige.
Assisted-by: Claude Opus 5
The long-test menu printed the command and ran it at once: a mistyped digit
created real VMs, and « --detruire » removes machines with their disks. The
command stays printed, since it is what makes the question answerable, and the
prompt follows it. Warning and question are chosen from the arguments, so a
destruction is never confirmed with the words of a launch. A dry run or a
report creates nothing and asks nothing. Covered by
test/test_longtest_confirm.py.
--- FR ---
Le menu des tests longs affichait la commande et l'exécutait aussitôt : un
chiffre tapé de travers créait de vraies VM, et « --detruire » efface des
machines avec leurs disques. La commande reste affichée, c'est elle qui rend
la question répondable, et l'invite la suit. L'avertissement et la question se
choisissent sur les arguments : une destruction n'est jamais confirmée avec
les mots d'un lancement. Un plan à blanc ou un rapport ne crée rien et ne
demande rien. Couvert par test/test_longtest_confirm.py.
Assisted-by: Claude Opus 5
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
A desktop install repeated « Could not get lock » for minutes, then could run
on an index never refreshed. Disabling the apt-daily timer does not interrupt
the apt-get it already started, so its service is stopped too. The guest agent
is laid by a detached unit that runs its own apt-get update: « cloud-init
status --wait » says done while that lock is still held, so the wait covers
that unit. Retries come back every two seconds, DPkg::Lock::Timeout not
covering the list lock, and an update that never succeeds says so at once.
--- FR ---
Une installation de bureau répétait « Impossible d'obtenir le verrou » pendant
des minutes, puis pouvait tourner sur un index jamais rafraîchi. Désactiver le
minuteur apt-daily n'interrompt pas l'apt-get qu'il a lancé : son service est
donc arrêté aussi. L'agent invité est posé par une unité détachée qui fait son
propre apt-get update : « cloud-init status --wait » dit « done » pendant que
ce verrou est tenu, et l'attente couvre donc cette unité. La reprise repasse
toutes les deux secondes, DPkg::Lock::Timeout ne couvrant pas le verrou des
listes, et un update qui n'aboutit pas le dit sur-le-champ.
Assisted-by: Claude Opus 5
Partager un fichier ou une base demandait d'anonymiser à la main,
colonne par colonne, ou de ne pas partager. L'entrée ouvre un fichier
externe — Excel, Access, CSV, XML, JSON — ou une base Odoo, dit ce
qu'elle porte, puis en tire une copie. L'original n'est jamais touché.
Deux règles gouvernent ce qui sort. Une colonne n'est laissée intacte
que sur son CONTENU mesuré, hors onze étiquettes structurelles : décider
sur l'étiquette recopiait les colonnes les plus identifiantes en les
annonçant protégées. Et la copie est relue octet par octet, un classeur
portant de la donnée en une douzaine d'endroits hors des cellules.
Vérifié : 5 748 tests — 13 rouges, déjà rouges sur master.
--- EN ---
Sharing a file or a database meant anonymising by hand, column by
column, or not sharing. The entry opens an external file — Excel,
Access, CSV, XML, JSON — or an Odoo database, says what it holds, then
draws a copy of it. The original is never touched.
Two rules govern what comes out. A column is left alone on its measured
CONTENT, bar eleven structural labels: deciding on the label copied the
most identifying columns verbatim while announcing them as protected.
And the copy is reread byte by byte, a workbook holding data in a dozen
places that are not cells.
Checked: 5,748 tests — 13 red, already red on master.
Assisted-by: Claude Opus 5
Trois clés étaient déclarées deux fois dans un littéral de trois mille
entrées. Rien n'avertit et rien ne lève : la seconde gagne, et le prix s'est
déjà payé en étiquette de menu, la traduction lue n'étant pas celle qu'on
venait d'écrire au premier endroit. Les trois portaient la même traduction des
deux côtés, donc aucun écran ne change ; c'est le piège qui part.
Le contrôle passe de « pas plus de trois » à « aucune », et il vit désormais
avec les tests d'internationalisation plutôt qu'avec ceux d'un menu. Il lit
l'ARBRE : une fois le dictionnaire construit, le doublon a déjà disparu et il
n'y a plus rien à demander.
--- EN ---
Three keys were declared twice in a literal of three thousand entries.
Nothing warns and nothing raises: the second wins, and the price was already
paid on a menu label, the translation read not being the one just written at
the first place. All three carried the same translation on both sides, so no
screen changes; what goes is the trap.
The check moves from "no more than three" to "none at all", and it now lives
with the internationalisation tests rather than with a menu's. It reads the
TREE: once the dictionary is built the duplicate is already gone, and there is
nothing left to ask.
Assisted-by: Claude Opus 5
L'entrée « Question IA » envoyait toute question à une seule API distante, sur
un modèle figé et sans historique : aucun serveur local n'était atteignable,
et rien ne disait où partait le texte.
Deux règles gouvernent ce qui la remplace. Un port ne dit jamais qui répond —
un seul en héberge jusqu'à trois, et une famille réémet l'API d'une autre en
entier — donc l'identité se lit dans le CORPS, par une échelle ordonnée. Et
une adresse ne devient jamais du texte d'invite : le détecteur du dépôt ne
voit pas les noms d'hôte, d'où une poignée opaque à sa place.
Vérifié : 284 tests, et contre un serveur du réseau — reconnaissance,
capacités lues, conversation multi-tours, triage juste.
--- EN ---
The "AI question" entry sent every question to a single remote API, on a
frozen model and with no history: no local server was reachable, and nothing
said where the text was going.
Two rules govern what replaces it. A port never says who answers — one hosts
up to three products, and one family re-serves another's native API in full —
so identity is read from the BODY, by an ordered ladder. And an address never
becomes prompt text: the repository's detector does not see host names, hence
an opaque handle in its place.
Checked: 284 tests, and against a server on the network — recognition,
capabilities read, a multi-turn conversation, a correct triage.
Assisted-by: Claude Opus 5
Le formulaire demandait « tout le trafic ? » à un pilote dont le SERVEUR
décide du routage, ne faisait rien de la réponse, et `status` la jugeait
quand même : un ✗ permanent sur un tunnel sain, et un profil annonçant
« tout le trafic » sans l'obtenir.
Un drapeau, sur le modèle de celui du MTU, dit quels pilotes posent cette
route. Les autres ne sont ni interrogés ni jugés, et un drapeau laissé à
vrai n'est plus conservé. L'honorer serait pire qu'inutile : forcer une
route par défaut contre une passerelle en tunnel scindé donne un trou
noir, une passerelle ne routant pas ce qu'elle n'a pas annoncé. Les routes
déclarées, elles, restent honorées — le formulaire le dit.
--- EN ---
The form asked "all traffic?" of a driver whose SERVER decides the
routing, did nothing with the answer, and `status` judged it anyway: a
permanent ✗ on a healthy tunnel, and a profile announcing "all traffic"
without getting it.
A flag, modelled on the MTU one, says which drivers lay that route. The
others are neither asked nor judged, and a flag left true is no longer
kept. Honouring it would be worse than useless: forcing a default route
against a split-tunnel gateway gives a black hole, a gateway not routing
what it never advertised. Declared routes are still honoured — the form
says so.
Assisted-by: Claude Opus 5
Le verdict se fondait sur le seul fichier d'état, que RIEN n'efface quand
un tunnel meurt sans passer par « down » — machine redémarrée, processus
tué, session expirée. Un état laissé derrière était annoncé monté sur
l'écran même qui déclarait le processus mort et l'interface absente.
L'interface arbitre désormais : son nom vient de l'état s'il y en a un,
sinon du pilote pour ceux qui la nomment d'avance, si bien qu'un tunnel
monté hors de l'outil est vu aussi. Sshuttle n'en crée aucune, là le
processus reste seul juge. La liste des profils le marque, connecter un
profil déjà monté demande confirmation, et déconnecter un profil déjà
tombé le dit sans l'empêcher : « down » nettoie l'état laissé.
--- EN ---
The verdict rested on the state file alone, which NOTHING clears when a
tunnel dies without going through "down" — machine rebooted, process
killed, session expired. A leftover state was reported as mounted on the
very screen declaring the process gone and the interface absent.
The interface now arbitrates: its name comes from the state when there is
one, otherwise from drivers that name it in advance, so a tunnel brought
up outside the tool is seen too. Sshuttle creates none, and there the
process stays the only judge. The profile list marks it, connecting an
already-mounted profile asks first, and disconnecting an already-down one
says so without refusing: "down" clears the leftover state.
Assisted-by: Claude Opus 5
Personne ne retient « /opt/cisco/secureclient/vpn/profile ». Le chemin
tapé oblige à le connaître ; le parcours seul n'aide pas quand le fichier
vient d'ailleurs, d'un courriel ou du portail d'un site. Les deux, dans
cet ordre, et la saisie reprend la main dès qu'on sort du parcours sans
rien choisir ou qu'on en rapporte un répertoire.
Le parcours n'est proposé que si l'un des répertoires connus existe :
l'ouvrir sur un chemin absent afficherait une liste vide, ce qui ressemble
à une panne. Vérifié : 7 tests unitaires, dont chacun des replis.
--- EN ---
Nobody remembers "/opt/cisco/secureclient/vpn/profile". A typed path
requires knowing it; browsing alone does not help when the file comes
from elsewhere, from an e-mail or a site's portal. Both, in that order,
and typing takes over as soon as one leaves the browser without choosing
anything, or brings back a directory from it.
Browsing is offered only if one of the known directories exists: opening
it on an absent path would show an empty list, which looks like a fault.
Checked: 7 unit tests, one for each fallback.
Assisted-by: Claude Opus 5
openconnect refuse les passerelles qui exigent un navigateur intégré, sur
« No SSO handler » : les distributions le bâtissent sans webview. Rien
n'installait le greffon qui fait cette étape, que le pilote attendait
pourtant — une machine paraissait équipée sans l'être.
La question n'est posée que pour le pilote qui peut s'en servir, et
seulement quand le greffon manque. Son amont est arrêté depuis 2023 :
épingles intenables sur un Python récent, Qt et lxml pris de la
distribution, correctif rejoué à chaque installation. Le venv appartient
à l'utilisateur, non à root, qui n'a ni affichage ni trousseau. Vérifié :
installation depuis rien en 6,5 s, paquets éprouvés sur debian et ubuntu.
--- EN ---
openconnect refuses gateways demanding an embedded browser, on "No SSO
handler": distributions build it without a webview. Nothing installed the
helper that performs that step, which the driver expected all the same —
a machine looked equipped without being so.
The question is asked only for the driver that can use it, and only when
the helper is missing. Its upstream has been unmaintained since 2023:
pins unsatisfiable on a recent Python, Qt and lxml taken from the
distribution, a patch replayed on every install. The venv belongs to the
user, not root, which has neither display nor keyring. Checked: install
from nothing in 6.5 s, package names proven on debian and ubuntu only.
Assisted-by: Claude Opus 5
Deux mécanismes désignent un service sur un concentrateur : un chemin
d'URL, une valeur de menu déroulant. Les confondre rend le formulaire d'un
AUTRE service — identifiants justes refusés, rien ne désignant le groupe.
Et certains ne comparent que les N premiers caractères du mot de passe,
que le profil déclare désormais sans jamais rien tronquer.
Une passerelle qui exige un navigateur intégré arrête openconnect sur
« No SSO handler », les distributions le bâtissant sans webview. Un greffon
fait l'étape web et rend un cookie ; le pilote monte lui-même, et le profil
garde son interface, son état et son diagnostic.
Vérifié : 249 tests unitaires, un tunnel monté contre une passerelle SAML.
--- EN ---
Two mechanisms designate a service on one concentrator: a URL path, and a
dropdown value. Confusing them hands over ANOTHER service's login form —
correct credentials refused, with nothing pointing at the group. And some
compare only the first N characters of the password, which the profile now
declares without ever truncating anything.
A gateway demanding an embedded browser stops openconnect on "No SSO
handler", distributions building it without a webview. A helper does the
web step and returns a cookie; the driver mounts the tunnel itself, so the
profile keeps its interface, its state and its diagnosis.
Checked: 249 unit tests, and one tunnel mounted against a SAML gateway.
Assisted-by: Claude Opus 5
Une VM prend son adresse dans le /24 de son réseau et en sort par le .1,
porté par le pont : déplacer ce /24 sous une VM allumée lui laisse un bail
qui ne mène nulle part, et abattre le réseau détache son tap, que libvirt
n'y remet pas. network_qemu.py tient les trois gestes dans l'ordre — arrêter
les VM attachées, redéfinir le réseau sur le préfixe voulu, rallumer celles
qu'il a arrêtées, elles seules. Une VM sourde au shutdown annule la
redéfinition plutôt que d'y perdre son pont ; --force-off coupe le courant.
Un préfixe qui recouvre ce que l'hôte route déjà est refusé.
Vérifié : 13 tests neufs, la numérotation du menu, une passe à blanc.
--- EN ---
A VM takes its address in its network's /24 and leaves through the .1,
carried by the bridge: moving that /24 under a live VM leaves it a lease
leading nowhere, and tearing the network down detaches its tap, which
libvirt does not put back. network_qemu.py holds the three steps in order —
shut down the attached VMs, redefine the network on the wanted prefix, start
again the ones it stopped, those only. A VM deaf to the shutdown cancels the
redefinition rather than losing its bridge; --force-off cuts the power.
A prefix overlapping what the host already routes is refused.
Checked: 13 new tests, the menu numbering, one dry run.
Assisted-by: Claude Opus 5
sudo ne dit jamais ce qu'il sert à faire : son invite tombe entre deux lignes
de journal, et l'on tape un mot de passe sans savoir s'il porte sur libvirt,
sur un paquet ou sur un fichier — d'autant qu'appartenir au groupe libvirt a
l'air de suffire. Il ne suffit pas, et libvirt n'y est pour rien : le disque
et le seed s'écrivent dans le pool par défaut, répertoire de root où le
groupe ne donne pas l'écriture. Le motif est constaté, non déduit :
l'écriture s'essaie, une ACL pouvant l'accorder là où le mode semble la
refuser. Dit une fois avant la première commande privilégiée, et en dernière
ligne du récapitulatif. Vérifié : 18 tests, root compris, à qui rien n'est dit.
--- EN ---
sudo never states what it is about to do: its prompt lands between two log
lines, and one types a password without knowing whether it covers libvirt, a
package or a file — the more so as being in the libvirt group looks like it
should be enough. It is not, and libvirt is not the reason: the disk and the
seed are written into the default pool, a root-owned directory where the
group grants no write right. The reason is checked, not deduced: writing is
tried, since an ACL can grant it where the mode seems to refuse it. Said once
before the first privileged command, and last on the review page. Checked: 18
tests, root included, which is told nothing.
Assisted-by: Claude Opus 5
L'option des outils d'assistance posait trois installateurs amont, puis
laissait tout à retaper : hook global de rtk, zdiff3, hooks git du dépôt,
commandes Claude, activation du venv. Elle les pose, en deux temps parce que
hooks et gabarits VIVENT dans le dépôt ; le complément suit le clone, rend
toujours 0 — un confort ne fait pas échouer une VM — et sans clone la moitié
manquante est nommée. core.editor n'est posé qu'à défaut, l'hôte transmettant
déjà le sien. L'aide « ? » et F1 dit ce que chaque option installe, là où un
libellé de case porte trois des huit poses. Vérifié : 51 tests, les deux
écrans montés sans terminal, « bash -n » sur les fragments distants.
--- EN ---
The AI tools box posed three upstream installers, then left every setting to
retype: rtk's global hook, zdiff3, the checkout's git hooks, the Claude
commands, activating the venv. It poses them, in two phases because hooks and
templates LIVE in the checkout; the complement follows the clone, always
returns 0 — a comfort must not fail a VM — and with no clone the missing half
is named. core.editor is posed only where there is none, the host already
transmitting its own. The `?` and F1 help says what each option installs,
where a checkbox label carries three of this one's eight poses. Checked: 51
tests, both screens mounted headless, `bash -n` on the remote fragments.
Assisted-by: Claude Opus 5
Le menu RTK est le seul dont les entrées n'affichaient rien avant leur
libellé : six lignes que seul leur numéro distinguait. Les six glyphes sont
pris parmi ceux que le fichier emploie déjà plutôt que choisis pour eux-mêmes
— un caractère récent s'affiche en tofu sur un terminal dont la police ne le
connaît pas. L'icône va dans la chaîne traduite, comme partout ailleurs dans
le menu, donc les deux langues la portent.
Vérifié : rendu en français et en anglais ; chacune des six clés ne sert
qu'ici, aucun autre menu n'est touché.
--- EN ---
The RTK menu is the only one whose entries showed nothing before their label:
six lines told apart by their number alone. The six glyphs are taken from
those the file already uses rather than picked for themselves — a recent
character renders as tofu on a terminal whose font does not know it. The icon
goes into the translated string, as everywhere else in the menu, so both
languages carry it.
Checked: rendering in French and English; each of the six keys is used here
only, so no other menu is affected.
Assisted-by: Claude Opus 5
Le dépôt n'avait aucun moyen de monter un tunnel VPN ni de dire pourquoi il
refuse de monter. Cinq technologies libres, un pilote chacune, derrière un
`vpn.py` qui monte, démonte et diagnostique.
Ce qui n'est pas secret — hôte, utilisateur, routes, MTU — vit dans une
configuration JSON lisible ; clés pré-partagées et mots de passe vivent dans
un coffre KeePassXC. Un profil se montre et se partage sans donner de quoi
monter le tunnel. Les secrets s'écrivent en tmpfs sous 0700, jamais sur un
disque persistant. Le diagnostic part du noyau et remonte, pour que la
première ligne fausse soit la cause et non une conséquence.
Vérifié : 138 tests, dont le rendu de chaque fichier généré.
--- EN ---
The repository had no way to raise a VPN tunnel, nor to say why one refuses
to come up. Five free technologies, one driver each, behind a `vpn.py` that
raises, tears down and diagnoses.
What is not secret — host, user, routes, MTU — lives in readable JSON
configuration; pre-shared keys and passwords live in a KeePassXC vault. A
profile can be shown and shared without handing over the means to raise the
tunnel. Secrets are written to tmpfs at 0700, never to a persistent disk.
Diagnosis starts at the kernel and climbs, so the first false line is the
cause and not a consequence.
Checked: 138 tests, including the rendering of every generated file.
Assisted-by: Claude Opus 5
Le menu qui déploie les commandes d'assistant était le dernier resté nu,
et cinq lignes qui ouvrent toutes sur le même mot se lisent une par une.
Chaque signe dit ce que la commande fait — message, fusion, plan, code,
inventaire — plutôt que de répéter qu'il s'agit d'une commande.
Les cinq emoji rendent sur deux colonnes, d'où l'espace unique qui les
sépare du libellé. Aucun ne servait ailleurs : un même signe pour deux
concepts distincts se lit comme un lien qui n'existe pas.
--- EN ---
The menu deploying assistant commands was the last one left bare, and
five lines all opening on the same word are read one by one. Each sign
says what the command does — message, merge, plan, code, inventory —
instead of repeating that it is a command.
The five emoji render two columns wide, hence the single space before
the label. None was in use elsewhere: one sign for two distinct concepts
reads as a link that does not exist.
Assisted-by: Claude Opus 5
Une case de plus au catalogue des outils, donc une case dans les deux
écrans sans y toucher. Cochée, elle découvre le choix de l'agent — Claude
Code ou opencode — et l'identité git, pré-remplie avec celle de l'hôte :
c'est ce que la VM reçoit déjà, et un champ vide la ferait croire absente.
Ce qui est saisi prime, champ par champ.
Phase « before », où chaque outil se garde lui-même. Chaque pose est aussi
privée d'entrée standard et bornée dans le temps : « || true » couvre
l'échec, pas l'ATTENTE, et un installateur amont qui pose une question
resterait pendu sur un SSH sans terminal — d'où « -y » pour starship.
--- EN ---
One more entry in the tool catalogue, hence one more box on both screens
for free. Ticked, it reveals the agent choice — Claude Code or opencode —
and the git identity, prefilled from the host: that is what the VM
already gets, and an empty field would suggest none. What is typed wins,
field by field.
Phase « before », where each tool guards itself. Every install is also
denied stdin and time-bounded: « || true » covers failure, not WAITING,
and an upstream installer asking a question would hang on a terminal-less
SSH — hence « -y » for starship.
Assisted-by: Claude Opus 5
Le rapport disait ce que l'HÔTE sait faire, jamais ce que chaque VM
recevra au prochain démarrage. Trois valeurs y répondent ensemble et
aucune seule : le type de vidéo, « accel3d », et le device figé par
libvirt, qui l'emporte sur les deux autres. La section les lit dans la
définition persistante et écrit la commande qui défige, sans la lancer.
Les sections Python du rapport sont désormais isolées. Le fichier
s'écrit d'un bloc à la fin : une section qui lève emportait tout ce qui
avait été relevé avant elle, laissant sans rapport au moment d'en avoir
besoin. Le double de test rendait un objet sans « returncode », ce qui
masquait le défaut.
--- EN ---
The report said what the HOST can do, never what each VM will get on its
next boot. Three values answer that together and none alone: video type,
« accel3d », and the device libvirt pinned, which outranks both. The
section reads them from the persistent definition and writes out the
unpinning command without running it.
Python sections of the report are now isolated. The file is written in
one block at the end: a section that raised took down everything
gathered before it, leaving no report exactly when one is needed. The
test double returned an object with no « returncode », hiding the fault.
Assisted-by: Claude Opus 5
Depuis libvirt 12.5.0, le <model> vidéo porte un attribut « device » qui
grave le device QEMU retenu, pour tenir l'ABI de l'invité stable d'un
démarrage à l'autre. Il l'emporte sur « accel3d ». Une VM démarrée une
première fois sans 3D — module GL absent, ou option non cochée — garde
donc le device sans GL, et l'activer ensuite n'écrit qu'une intention :
QEMU reçoit toujours « virtio-vga » nu.
L'état lisait « accel3d » sans lire cet attribut, si bien que le résumé
annonçait « 3D » sur une VM qui tourne sans. Il porte désormais le device
figé quand celui-ci contredit la demande.
--- EN ---
Since libvirt 12.5.0 the video <model> carries a « device » attribute
recording the chosen QEMU device, to keep the guest ABI stable across
restarts. It outranks « accel3d ». A VM first started without 3D — GL
module absent, or the box unticked — therefore keeps the non-GL device,
and enabling it afterwards writes an intent only: QEMU still gets plain
« virtio-vga ».
State read « accel3d » without that attribute, so the summary announced
« 3D » on a VM running without it. It now names the pinned device
whenever it contradicts the request.
Assisted-by: Claude Opus 5
Le menu Git avait posé la convention ; dix autres restaient nus, et une
liste sans icône se lit ligne à ligne. L'espacement suit la largeur
RENDUE, lue dans Unicode et non devinée : deux espaces derrière un emoji
d'une colonne, une derrière un large — d'où le changement de signe de la
Configuration, dont le sien décalait le menu principal. Un même concept
garde son signe d'un menu à l'autre. « Quitter » garde sa traduction nue,
sa clé servant onze fois en raccourci de pied de page : son icône se pose
sur la ligne du menu.
Vérifié : menus rendus dans les deux langues, aucune clé incomplète,
aucune icône divergente entre les langues.
--- EN ---
The Git menu had set the convention; ten others stayed bare, and a list
without icons is read line by line. Spacing follows the RENDERED width,
read from Unicode rather than guessed: two spaces after a one-column
emoji, one after a wide one — hence the new sign for Configuration, whose
own offset the main menu. One concept keeps its sign from menu to menu.
« Quit » keeps its bare translation, its key serving eleven times as a
footer shortcut: its icon sits on the menu's own line.
Checked: menus rendered in both languages, no incomplete key, no icon
diverging between languages.
Assisted-by: Claude Opus 5
Un problème de VM se résout souvent par quelqu'un qui n'a pas accès à la
machine : vingt et une sondes en lecture écrivent un relevé unique —
hôte, hyperviseur, GPU, outils, stockage — chacune bornée dans le temps,
une commande qui pend ne devant pas retenir le rapport. Sur le pilote
NVIDIA propriétaire, trois conditions vivent hors des briques Mesa et
QEMU, dont la liste de périphériques où libvirt n'ajoute jamais les nœuds
de la carte : le relevé les nomme et propose la commande qui installe ce
qui lui manque, affichée avant la question. Gérer se scinde en trois.
--- EN ---
A VM problem is often solved by someone with no access to the machine:
twenty-one read-only probes write one report — host, hypervisor, GPU,
tools, storage — each time-bounded, since a command that hangs must not
hold the report. On the proprietary NVIDIA driver three conditions live
outside the Mesa and QEMU pieces, among them the device list where
libvirt never adds the card's own nodes: the report names them and offers
the command installing what it lacks, shown before the question is put.
The Manage menu splits in three.
Assisted-by: Claude Opus 5
Le nœud de rendu peut exister sans qu'EGL y démarre : QEMU refuse alors
le domaine, et la 3D écrite dans la définition rend la VM inutilisable
jusqu'à ce que quelqu'un défasse le réglage. La création avait déjà son
repli, le réglage d'une VM existante n'en avait pas. Un relevé accompagne
le refus, car deux briques distinctes entrent en jeu : egl-headless ouvre
le nœud par GBM — c'est Mesa qui répond — et virglrenderer ne sert
qu'ensuite, donc l'installer ne répare pas un EGL qui refuse.
Vérifié : 11 tests, rougis par trois mutations — rattraper n'importe quel
échec, ne pas redémarrer après le retrait, éteindre l'autostart au passage.
--- EN ---
The render node can exist without EGL starting on it: QEMU then refuses
the domain, and the 3D written into the definition leaves the VM unusable
until someone undoes the setting. Creation already had its fallback,
adjusting an existing VM had none. A report comes with the refusal, since
two distinct bricks are involved: egl-headless opens the node through GBM
— Mesa answers — and virglrenderer only serves afterwards, so installing
it repairs no EGL that refuses.
Checked: 11 tests, turned red by three mutations — catching any failure,
not restarting after removal, switching autostart off along the way.
Assisted-by: Claude Opus 5
Une VM qui ne démarre plus garde ses fichiers : libguestfs monte son
qcow2 sans elle. Toute commande porte « --ro », et c'est ce qui change la
manœuvre : ouvrir en écriture le disque d'une machine allumée corrompt
son système de fichiers, la lire ne risque rien. Un arrêt reste proposé
et non imposé — une lecture vivante voit un état peut-être incohérent,
un fichier à moitié écrit, un journal non rejoué. Le nom du paquet suit
la distribution, celui de Debian n'existant nulle part ailleurs.
Vérifié : 16 tests, rougis par quatre mutations — perdre le « --ro »,
donner le nom Debian partout, dupliquer un numéro, retirer l'entrée.
--- EN ---
A VM that no longer boots still holds its files: libguestfs mounts its
qcow2 without it. Every command carries « --ro », and that is what
changes the operation: opening a running machine's disk for writing
corrupts its filesystem, reading it risks nothing. A shutdown stays
offered, not imposed — a live read sees a possibly torn state, a
half-written file, an unreplayed journal. The package name follows the
distribution, Debian's existing nowhere else.
Checked: 16 tests, turned red by four mutations — losing « --ro », using
Debian's name everywhere, duplicating a number, dropping the entry.
Assisted-by: Claude Opus 5
La section du menu des plugins prenait « Install » pour clé, déjà portée
par l'entrée « 📦 Installation » du menu principal. Dans un littéral de
dict Python la dernière définition gagne, sans erreur ni avertissement :
le menu principal affichait donc « Installer ». La clé de la section est
renommée, la première étant en place depuis longtemps.
Vérifié : le menu principal retrouve « 📦 Installation », et un balayage
des clés ne laisse que trois doublons, tous à valeurs identiques donc
sans effet.
--- EN ---
The plugin menu's section used « Install » as its key, already held by
the main menu's « 📦 Installation » entry. In a Python dict literal the
last definition wins, with no error and no warning: the main menu
therefore showed « Installer ». The section's key is renamed, the first
one having been there far longer.
Checked: the main menu gets « 📦 Installation » back, and a sweep of the
keys leaves only three duplicates, all with identical values and so
without effect.
Assisted-by: Claude Opus 5
Une VM sans console peut vouloir un virtio-gpu accéléré — rendu hors
écran, ou émulateur qui tourne dedans — et « auto » ne l'accorde jamais :
il s'abstient sans écran, pour ne pas poser un périphérique vidéo que
personne n'a demandé. La case envoie donc « --gpu on », qui l'accorde
désormais. « --graphics none » est alors écarté : il dit « aucun
affichage », et egl-headless EST un affichage. Le repli le rend quand la
3D tombe, sinon la VM repartirait sur le défaut de virt-install.
Vérifié : 8 tests neufs et 2 sur le repli, rougis par trois mutations.
Le rendu de la case dans le terminal plein écran n'est pas testé.
--- EN ---
A VM without a console may want an accelerated virtio-gpu — offscreen
rendering, or an emulator running inside — and « auto » never grants it:
it abstains without a screen, so as not to add a video device nobody
asked for. The box therefore sends « --gpu on », which now grants it.
« --graphics none » is then dropped: it means « no display », and
egl-headless IS a display. The fallback gives it back when 3D fails,
otherwise the VM would restart on virt-install's default.
Checked: 8 new tests and 2 on the fallback, turned red by three
mutations. The box's rendering in the full-screen terminal is not tested.
Assisted-by: Claude Opus 5
Une création interrompue laisse son qcow2 sans VM définie, et deploy_qemu
refuse ensuite d'écraser : la création échoue APRÈS avoir fait attendre.
Le disque est donc proposé à l'effacement, taille et chemin affichés.
Proposé et non effacé d'office : le même nom peut désigner le disque
d'une VM retirée à la main, dont on voulait garder les données. Un refus
ne laisse pas filer vers l'échec, il redemande. La proposition vient
après la fermeture du formulaire plein écran, parce qu'effacer là demande
root et qu'une invite de mot de passe n'y a nulle part où s'afficher.
Vérifié : 7 tests, rougis par deux mutations — ne pas redemander après un
refus, et sauter la proposition sur le chemin plein écran.
--- EN ---
An interrupted creation leaves its qcow2 with no defined VM, and
deploy_qemu then refuses to overwrite: creation fails AFTER the wait. The
disk is therefore offered for deletion, size and path shown. Offered, not
deleted outright: the same name may designate the disk of a VM removed by
hand, whose data was meant to be kept. A refusal does not drift into
failure, it asks again. The offer comes after the full-screen form
closes, because deleting there needs root and a password prompt has
nowhere to appear inside it.
Checked: 7 tests, turned red by two mutations — not asking again after a
refusal, and skipping the offer on the full-screen path.
Assisted-by: Claude Opus 5
Appartenir au groupe libvirt suffit à joindre qemu:///system : le sudo
écrit en dur n'y ajoutait aucun droit et réclamait un mot de passe à
chaque entrée de menu. La question se tranche en ESSAYANT, jamais en
lisant /etc/group : les groupes d'un processus sont figés à l'ouverture
de session, donc la table dit le déclaré, l'essai le faisable. C'est la
distinction que porte aussi l'avertissement d'avant-installation. Un
hyperviseur distant garde sudo, ses droits ne se sondant pas d'ici.
Vérifié : 10 tests, rougis par deux mutations — lire /etc/group, et
conclure « pas de sudo » sur un sondage mort.
--- EN ---
Membership of the libvirt group is enough to reach qemu:///system: the
hardcoded sudo added no right there and asked for a password at every
menu entry. The question is settled by TRYING, never by reading
/etc/group: a process's groups are frozen at session start, so the table
states what is declared, the attempt what is doable. The pre-install
warning carries that same distinction. A remote hypervisor keeps sudo,
its rights not being probeable from here.
Checked: 10 tests, turned red by two mutations — reading /etc/group, and
concluding « no sudo » from a dead probe.
Assisted-by: Claude Opus 5
Une VM ratée n'affichait que ses quatre dernières lignes, et l'épilogue
« Échec de la commande » avec sa ligne de commande les occupe entièrement :
le message de l'outil tombait juste au-dessus de la fenêtre. La sortie
étant jetée après la boucle, passé l'écran elle n'existait plus nulle
part. Trente lignes en cas d'échec, quatre en cas de réussite, et la
sortie complète va dans un fichier dont le chemin s'affiche. Quand la
commande portait la 3D, le rapport nomme « --gpu off » : le menu ne
l'expose pas, c'est la seule issue depuis là.
Vérifié : 7 tests, dont un nom de VM hostile qui ne doit pas composer un
chemin hors du répertoire de session.
--- EN ---
A failed VM showed only its last four lines, and the « Échec de la
commande » epilogue with its command line fills them entirely: the tool's
own message fell just above the window. The output being discarded after
the loop, past the screen it existed nowhere at all. Thirty lines on
failure, four on success, and the whole output goes to a file whose path
is printed. When the command carried 3D, the report names « --gpu off »:
the menu does not expose it, and it is the only way out from there.
Checked: 7 tests, among them a hostile VM name that must not compose a
path outside the session directory.
Assisted-by: Claude Opus 5
Les règles à appliquer avant de coder sont éparpillées entre les configs,
les hooks et les modules maison, et la documentation en contredit
plusieurs : autopep8 est recommandé alors que le script sort en 1, aucun
script oca-* n'est installé, la racine n'a ni .pre-commit-config.yaml ni
.pylintrc, et flake8 comme pylint-odoo ne vivent que dans le venv Odoo,
que rien ne lance. Le gabarit énonce ce que l'outillage impose, à effort
high, l'épinglage ultracode restant celui de l'utilisateur.
Vérifié : 146 règles relevées, 142 confirmées contre leur citation, 4
retirées. Deux gardes lient chaque entrée du menu à un gabarit présent
qui déclare le bon nom.
--- EN ---
The rules to apply before coding are scattered across the configs, the
hooks and the in-house modules, and the documentation contradicts several
of them: autopep8 is recommended although the script exits 1, no oca-*
script is installed, the root carries neither .pre-commit-config.yaml nor
.pylintrc, and flake8 as well as pylint-odoo live only in the Odoo venv,
which nothing invokes. The template states what the tooling enforces, at
high effort, the ultracode pin remaining the user's own.
Checked: 146 rules surveyed, 142 confirmed against their citation, 4
dropped. Two guards tie each menu entry to a template that exists and
declares the right name.
Assisted-by: Claude Opus 5
Ajouter une entrée commence par des décisions qu'aucun gabarit
n'imposait : quel menu parent, motif A ou B, quoi faire en cas d'échec,
ce qui est détruit, ce qui touche à une donnée client. La commande les
demande avant d'écrire, planifie avec superpowers quand le plugin est
là, et pose sa spécification dans tasks/, non versionné. Son frontmatter
fixe l'effort à max, seule valeur de l'énumération ; ultracode est un
épinglage de session que l'utilisateur tape, non un réglage de gabarit.
Vérifié : les deux gabarits déployés dans un HOME jetable, relus « à
jour » par l'écran de contexte.
--- EN ---
Adding an entry starts with decisions no template forced: which parent
menu, pattern A or B, what to do on failure, what gets destroyed, what
touches customer data. The command asks them before writing, plans with
superpowers when the plugin is there, and lays its specification in
tasks/, which is not versioned. Its frontmatter sets effort to max, the
only value in the enumeration; ultracode is a session pin the user
types, not a template setting.
Checked: both templates deployed into a throwaway HOME, read back as up
to date by the context screen.
Assisted-by: Claude Opus 5
Rien ne posait un plugin depuis TODO ; la CLI seule le faisait, hors du
menu. L'installation passe par « -y » : la sortie de TODO est un tuyau,
pas un terminal, et la CLI refuse sans lui toute installation qui
exécute une commande déclarée par un marketplace. La liste préférée
s'affiche donc AVANT la confirmation, seule occasion de la lire ; ses
quatre plugins travaillent sur le poste, sans service tiers ni compte.
La recherche lit les manifestes sur le disque, donc hors ligne.
Vérifié : 8 tests neufs, dont la frontière de mot qui sépare deux noms
dont l'un contient l'autre — une recherche naïve en rate trois.
--- EN ---
Nothing installed a plugin from TODO; the CLI alone did, outside the
menu. Installing goes through « -y »: TODO's output is a pipe, not a
terminal, and without it the CLI refuses any install that runs a
command declared by a marketplace. The preferred list is therefore
shown BEFORE the confirmation, the only chance to read it; its four
plugins run on the workstation, with no third party and no account.
Search reads the manifests from disk, hence offline.
Checked: 8 new tests, among them the word boundary separating two names
where one contains the other — a naive search misses three of them.
Assisted-by: Claude Opus 5
Une fusion apporte plusieurs commits d'un coup, et rien ne guidait les
deux écrits qu'elle demande : l'entrée de changelog et le message de
merge. La commande déployée impose la source — CHANGELOG.base.md, les
fichiers générés étant perdus au prochain doc_markdown — et le corps
bilingue avec son trailer, que le garde-fou ne verra jamais, commit-msg
ignorant tout message ouvrant sur « Merge ». Elle prépare, elle ne
fusionne pas : le message part dans tasks/, non versionné.
Vérifié : 45 tests du menu, et un déploiement dans un HOME jetable que
l'écran de contexte relit « à jour » face à son gabarit.
--- EN ---
A merge lands several commits at once, and nothing guided the two pieces
of writing it needs: the changelog entry and the merge message. The
deployed command imposes the source — CHANGELOG.base.md, the generated
files being lost at the next doc_markdown — and the bilingual body with
its trailer, which the guard rail never sees, commit-msg skipping any
message opening on « Merge ». It prepares, it does not merge: the
message goes to tasks/, which is not versioned.
Checked: 45 menu tests, and a deployment into a throwaway HOME that the
context screen reads back as up to date against its template.
Assisted-by: Claude Opus 5
Ces installateurs posent leur binaire dans un répertoire du HOME que le PATH
d'un shell ne porte pas toujours : sans la ligne d'export, le binaire est là
et la commande reste introuvable. Le répertoire diffère d'un outil à l'autre,
et l'un des deux écrit déjà sa propre ligne — la présence se teste donc sur
le répertoire, pas sur la graphie, et rien n'est ajouté deux fois.
Le PATH d'un processus est figé depuis son démarrage : un nouveau shell est
nécessaire, ce que la sortie dit. Troisième écrivain dans le fichier de
shell, starship compris, d'où l'écriture mise en commun. Vérifié : 31 tests.
--- EN ---
These installers put their binary in a HOME directory that a shell's PATH
does not always carry: without the export line, the binary is there and the
command stays not found. The directory differs from one tool to the other,
and one of the two already writes its own line — presence is therefore tested
on the directory, not on the spelling, and nothing is added twice.
A process PATH is frozen since its start: a new shell is needed, which the
output says. Third writer into the shell file, starship included, hence the
shared write. Checked: 31 tests.
Assisted-by: Claude Opus 5
Sept entrées sans repère visuel se lisent une par une. L'icône entre dans la
traduction, qui porte déjà le libellé affiché.
Un glyphe à présentation texte occupe une colonne quand les autres en
occupent deux, et décale la colonne des libellés : il prend deux espaces,
comme 🖥 et ⚙ ailleurs dans le fichier. Rendu vérifié dans les deux langues.
--- EN ---
Seven entries with no visual marker are read one by one. The icon goes into
the translation, which already carries the displayed label.
A text-presentation glyph takes one column where the others take two, and
shifts the label column: it gets two spaces, like 🖥 and ⚙ elsewhere in the
file. Rendering checked in both languages.
Assisted-by: Claude Opus 5
Poser le binaire ne change pas le prompt : c'est la ligne d'initialisation
dans le fichier du shell qui le fait, et les deux étapes échouent séparément.
Le paquet vient de la distribution quand elle le connaît, de l'installateur
amont sinon — starship n'est pas empaqueté partout. Le fichier du shell n'est
demandé que devant plusieurs candidats, et la ligne ne s'écrit qu'une fois.
L'entrée porte sa destination dans « method » : son rang suit le nombre
d'entrées de todo.json, qu'un numéro codé en dur ignorerait. Vérifié : 18
tests.
--- EN ---
Laying down the binary does not change the prompt: the init line in the shell
file does, and the two steps fail separately. The package comes from the
distribution when it knows it, from the upstream installer otherwise —
starship is not packaged everywhere. The shell file is only asked for when
several candidates exist, and the line is written only once.
The entry carries its destination in « method »: its rank follows the number
of todo.json entries, which a hard-coded number would ignore. Checked: 18
tests.
Assisted-by: Claude Opus 5
zdiff3 fait figurer la base commune dans les marqueurs de conflit et sort
de la zone contestée les lignes que les deux côtés ont en commun : il reste
moins à arbitrer à la main. Le style demande git 2.35, que toutes les
plateformes supportées dépassent.
La valeur est relue après écriture, « git config » ne rendant rien à
l'écriture. L'entrée est déclarée dans TestGitMenuNumbering : le menu Git
mêle entrées codées en dur et entrées de todo.json, qu'un rang de plus
décale. Vérifié : 117 tests au vert.
--- EN ---
zdiff3 puts the merge base into the conflict markers and lifts out of the
contested area the lines both sides share: less is left to arbitrate by
hand. The style needs git 2.35, which every supported platform exceeds.
The value is read back after writing, as « git config » returns nothing on
write. The entry is declared in TestGitMenuNumbering: the Git menu mixes
hard-coded entries with todo.json ones, which one more rank shifts.
Checked: 117 tests green.
Assisted-by: Claude Opus 5
Un processus garde le PATH qu'il avait au démarrage : rtk installé dans
~/.local/bin pendant que TODO tourne échappe à shutil.which, et « rtk » nu
sort en 127. Le menu le cherche donc aussi à l'emplacement de
l'installateur et l'appelle par son chemin absolu, sans confondre un
binaire hors PATH avec une absence.
L'installation annonce son résultat : version et chemin, ou échec.
Vérifié : 9 tests neufs, 117 au total.
--- EN ---
A process keeps the PATH it had at startup: rtk installed into
~/.local/bin while TODO runs escapes shutil.which, and a bare « rtk »
exits 127. The menu therefore also looks at the installer's location and
calls the binary by its absolute path, without mistaking a binary outside
the PATH for a missing one.
Installation now reports its outcome: version and path, or failure.
Checked: 9 new tests, 117 in total.
Assisted-by: Claude Opus 5
La sauvegarde double la place occupée et le défaut était OUI : sur un
disque presque plein, une entrée vide lançait une copie qui s'arrête à
mi-course et laisse un .bak tronqué. Les deux tailles passent donc avant
la question, et le défaut bascule à NON quand la place manque.
Le besoin annoncé est la taille ALLOUÉE : « cp --sparse=always » ne
recopie pas les trous d'un qcow2. La sortie du gestionnaire de paquets
enchaînait par ailleurs sur une question portant sur autre chose ;
l'étape se referme d'une ligne.
Vérifié : le défaut remis à OUI sans place, comme la taille apparente au
lieu de l'allouée, font tomber les tests. 4144 verts.
--- EN ---
A backup doubles the space used and the default was YES: on a nearly
full disk, an empty answer started a copy that stops midway and leaves a
truncated .bak. Both sizes now come before the question, and the default
flips to NO when the room is short.
The need shown is the ALLOCATED size: "cp --sparse=always" does not copy
the holes of a qcow2. The package manager output also ran straight into a
question about something else; the step now closes with a line of its
own.
Checked: putting the default back to YES without room, like the apparent
size instead of the allocated one, makes the tests fail. 4144 green.
Assisted-by: Claude Opus 5
La réduction sûre listait les outils manquants et s'arrêtait là. Trois
écritures séparées savaient installer, chacune un sous-ensemble
différent : openSUSE posait virt-viewer, mais ni navigateur CLI ni
lm-sensors.
Le binaire n'est presque jamais le paquet : sgdisk vit dans « gdisk »
chez Debian et Fedora, dans « gptfdisk » chez Arch et openSUSE. Deux
règles passent aussi dans la composante : l'ID de la distribution décide
avant le PATH, et la commande s'affiche avant la question.
Vérifié : commandes identiques sur les trois familles déjà couvertes,
4139 tests verts.
--- EN ---
The safe shrink listed the missing tools and stopped there. Three
separate writings knew how to install, each covering a different
subset: openSUSE could put virt-viewer down, but neither a CLI browser
nor lm-sensors.
A binary is almost never the package: sgdisk lives in "gdisk" on Debian
and Fedora, in "gptfdisk" on Arch and openSUSE. Two rules move into the
component as well: the distribution ID decides before the PATH, and the
command is shown before the question.
Checked: commands identical on the three families already covered, and
4139 tests green.
Assisted-by: Claude Opus 5
Un commit-msg non installé, c'est le garde-fou de convention qui ne tourne
jamais, et la pose restait une commande à recopier. git saute sans rien
dire un hook privé du bit d'exécution : l'installation le remet.
exec_command_live ne passe aucun cwd et ce dépôt porte 128 dépôts
imbriqués : sans « git -C racine », un lancement depuis un addon y écrivait
core.hooksPath et laissait la racine sans garde-fou. Et comme cette
fonction retourne le code sans jamais lever, un « fatal: not in a git
directory » annonçait « hooks installés ».
Vérifié : 4114 tests verts ; le nouveau garde tombe sur un dispatch
échangé comme sur une entrée sans branche.
--- EN ---
An uninstalled commit-msg is the convention guard never running, and
installing it stayed a command to copy by hand. git skips a hook without
the execution bit and says nothing: installing restores it.
exec_command_live passes no cwd and this checkout holds 128 nested
repositories: without the "git -C root", a launch from an addon wrote
core.hooksPath there and left the root unguarded. And since that function
returns the exit code without ever raising, a "fatal: not in a git
directory" still announced "hooks installed".
Checked: 4114 tests green; the new guard fails on a swapped dispatch as
well as on an entry with no branch.
Assisted-by: Claude Opus 5