deps: domicilier la contrainte windows-sys 0.61.2 — aucun garde ne la tient, et tout bump du sous-arbre TLS la fait descendre en silence #345
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/Simpl-Resultat#345
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Analysé le 2026-09-04. Le corps d'origine, écrit en fin de revue de #344 sans ouvrir le code, portait trois affirmations fausses — voir le commentaire d'archive, qui conserve le texte original.
Contexte
Sept crates du fichier de verrouillage consomment
windows-syspar une borne large, et peuvent donc pointer plusieurs versions incompatibles entre elles sans qu'aucun contrat ne soit violé :colored3.1.1,winapi-util0.1.11>=0.48, <=0.61errno0.3.14,rustix1.1.3,tempfile3.24.0>=0.52, <0.62rustls-platform-verifier0.6.2>=0.52.0, <0.62.0dirs-sys0.5.0>=0.59.0mainles tient toutes les sept à0.61.2, l'état exact du tagv0.14.0(vérifié crate par crate contregit show v0.14.0:src-tauri/Cargo.lock), et la release run 322 confirme que cette configuration a traversé un vrai build Windows NSIS. L'état dérivé n'en a jamais traversé aucun : aucune release n'a eu lieu entre #310 (2026-07-27) et #321 (2026-08-15), la fenêtre pendant laquellemainle portait.Le mécanisme, mesuré
Le déclencheur n'est pas « n'importe quel
cargo update» mais la portée de la mise à jour :maincargo update -p anyhow0.61.2cargo update -p hyperUne mise à jour qui atteint le sous-arbre réseau/TLS re-résout ces arêtes ; une mise à jour d'un paquet feuille non. C'est le pire cas de figure : ce sous-arbre est exactement celui où tombent les advisories RustSec (
rustls-webpkiettaren #310,h2en #344,hyperici). La régression est donc corrélée aux correctifs de sécurité, c'est-à-dire aux changements les plus pressés et les moins relus.Et cargo n'en dit rien. Sur
cargo update -p hyper, il annonce :pendant qu'il écrit huit changements dans le fichier. Les 7 arêtes ne sont jamais mentionnées, parce que l'ensemble des paquets ne bouge pas — 666 avant, 666 après ; seules les arêtes changent. Le silence est celui de l'outil, pas celui du relecteur : personne ne peut « faire attention » à ce que cargo ne rapporte pas.
Ce que la dérive coûte
Pas un risque de correction — les deux états sont dans les bornes déclarées par les sept auteurs. Le coût est de taille et de temps de compilation, et il se mesure sur le graphe de la cible Windows :
windows-syscompilées pour Windowsmain(0.61.2)Deux crates
windows-syscomplets de plus dans chaque build Windows, ajoutés en effet de bord d'un correctif de sécurité, sans que rien ne le signale.Décision retenue
Garde-fou CI sur la version exacte, plus un ADR — le motif de l'ADR 0018 et de son garde-fou de suppressions, que le dépôt sait déjà lire et dont il connaît les pièges.
L'ADR porte la condition de sortie, ce qui évite le figeage indéfini : la borne
<=0.61decoloredforcera une décision quand l'écosystème passera à 0.62, et le garde doit alors être mis à jour sur preuve, pas contourné.Friction assumée : chaque bump du sous-arbre TLS fera échouer le garde et demandera l'édition manuelle du lock, comme dans #344. C'est l'objet — transformer un effet de bord silencieux en décision explicite.
Travail à faire
Verify windows-sys pinningdanscheck-rust.yml, aprèscargo check(index chaud) et avec--locked, sur le modèle du garde des suppressionsCargo.lockavecawk(pas de Python dans le conteneurubuntu:22.04), selon les règles de la section « Décisions prises en planification » — tester le code de sortie séparément de la sortieok: rustix -> windows-sys 0.61.2), plus une ligne de bilan typewindows-sys pinning: 7 checks, exit 00.61.2, ce que coûte la dérive (3 contre 5), et à quelle condition l'entrée change ou disparaît.cargo/audit.tomlen porte unFichiers concernés
.forgejo/workflows/check-rust.yml— le garde-fou. Se déclenche déjà sursrc-tauri/**, donc tout changement de lock le fait tourner ; le glob a été prouvé par #344docs/adr/00XX-windows-sys-pinning.md— nouvel ADRsrc-tauri/Cargo.lock— l'objet gardé, non modifié par ce travailSurface de test
Aucun test unitaire : c'est de l'infra CI. La vérification est le garde lui-même, et elle doit être faite par mutation — un lock dérivé se produit en une commande (
cargo update -p hypersur une copie) et doit faire rougir l'étape. Le précédent de #310 est explicite : un garde muet en cas de succès est indiscernable dans le log d'une étape qui n'a jamais tourné.Critères d'acceptation
0.61.2, prouvé par mutation et non par raisonnementmainet logge ses 7 vérifications plus son bilan.cargo/audit.tomlet son garde-fou restent intouchésComplexité estimée
Simple. Une étape CI et un ADR ; c'est la décision qui était structurante, pas le code.
Écarté au passage
Check croisé Windows en CI (
cargo check --target x86_64-pc-windows-msvcdepuis Linux) — l'option la plus attirante, puisqu'elle prouverait que le graphe Windows compile quel que soit l'état et dissoudrait l'argument « configuration non prouvée ». Testée, elle échoue :cargo checkn'est pas link-free dès qu'un build script compile du natif, etcc-rss'arrête surfailed to find tool "lib.exe". Il faudrait un vrai cross-toolchain MSVC (cargo-xwin) — un chantier, pas une étape CI bon marché. À rouvrir seulement si le besoin d'une preuve Windows revient pour d'autres raisons.Garde sur la propriété (« au plus 3
windows-sysdans le graphe Windows ») — encode le dommage mesuré plutôt qu'un numéro et survit au passage à 0.62 sans intervention, mais reste indirect à la lecture et tolérerait un échange à compte constant. Écarté au profit de la cohérence avec le motif existant.Commentaire seul dans
Cargo.toml—Cargo.tomln'est pas ce qu'on lit avant de lancercargo update, et rien ne l'exécute.Dépendances
Depends on
Fichiers concernés (complet)
.forgejo/workflows/check-rust.yml(nouvelle étapeVerify windows-sys pinning, aprèscargo check, avantcargo test)docs/adr/0023-windows-sys-pinning.md(nouveau)CLAUDE.md(section CI/CD : citer le garde à côté de « Verify suppressed advisories »),docs/architecture.md(section CI)src-tauri/Cargo.lock: lu, non modifié ;.cargo/audit.tomlet le garde des suppressions intouchésCritères d'acceptation (planification)
main, l'étape logge 7 lignesok: <crate> -> windows-sys 0.61.2, une ligneok:pour l'ensemble des versions, et le bilanwindows-sys pinning: N checks, exit 0.src-tauri/,cargo update -p hyper→ le garde sort en 1. Sortie de cargo et arêtes mutées collées dans la PR. Vérifié à la revue le 2026-10-10 : hyper 1.8.1 → 1.12.0, les 7 arêtes descendent vers 0.48.0/0.52.0/0.59.0. Si un jour la commande ne fait plus dériver, repli : éditer une arête à la main.[[package]] name = "windows-sys" version = "0.53.0"→ échec par l'ensemble des versions.awk(fichier absent) → échec, pas succès silencieux.bash -e -o pipefail) sur une copie du lock, pour les mutations.0.61.2(tag v0.14.0, build Windows prouvé, run 322), coût (3 contre 5 versions compilées pour Windows), déclencheur réel (hyperet ce qu'il tire ;rustlsseul ne bouge rien, mesure du 2026-09-22), condition de sortie (passage à 0.62, borne<=0.61decolored; convergence vers un seulwindows-sys= sortie souhaitée), procédure quand le garde échoue (éditer la strophe à la main comme #344, ou mettre à jour l'ensemble sur preuve).Review caveats
"windows-sys 0.61.2"que tant que plusieurs versions coexistent ; une arête nue"windows-sys"(sans version) apparaît quand il n'en reste qu'une. Un crate gardé peut apparaître en deux versions (deux blocs). Une nouvelle version dewindows-systirée par un autre crate est le coût réel. Traités par les règles ci-dessous.(registry+...): le tolérer.Décisions prises en planification
awksurCargo.lock, règles complètes + ensemble de versions (Max, 2026-10-10) :colored,dirs-sys,errno,rustix,rustls-platform-verifier,tempfile,winapi-util), chaque bloc[[package]]de ce nom doit avoir une arêtewindows-sységale àwindows-sys 0.61.2; zéro bloc trouvé = échec."windows-sys"passe seulement si le lock ne contient qu'un seul paquetwindows-sys, en 0.61.2.windows-sysdu lock doit égaler exactement{0.45.0, 0.48.0, 0.52.0, 0.59.0, 0.60.2, 0.61.2}(état du 2026-10-10, tous cibles confondues) ; toute version ajoutée ou retirée échoue avec un message qui renvoie à l'ADR 0023.tempfiledoit être trouvé ; sinon le garde ne prouve rien et échoue.check-rust.yml(pile linéaire) ; un échec de #376 sans rapport avec ce fichier ne doit pas bloquer celle-ci.Spec source
spec-plan-release-deps.md(local, ignoré par git)Corps réécrit après analyse — trois affirmations d'origine ne tenaient pas
Cette issue a été écrite en fin de revue de #344, de mémoire, sans ouvrir le code. L'analyse a mesuré ce qu'elle affirmait. Le fond tient — la contrainte n'est domiciliée nulle part et régressera — mais trois points étaient faux, et la correction change la nature du problème.
1. «
cargo update, quel que soit le paquet visé, les réaiguille » — faux. Mesuré sur des copies du lock demain:cargo update -p anyhow0.61.2cargo update -p hyperLe déclencheur est la cible, pas la commande : une mise à jour qui atteint le sous-arbre réseau/TLS re-résout les arêtes
windows-sys; une mise à jour d'un paquet feuille non. Ce n'est pas rassurant — ce sous-arbre est exactement là où tombent les advisories (rustls-webpki,tar,h2,hyper). La régression est corrélée aux correctifs de sécurité.2. « #310 les fait descendre, sans que ce soit remarqué » — faux.
STATE.mdmontre que #310 l'avait vu, expliqué et borné : « 7 pointeurswindows-sysrepointés vers des versions déjà présentes, 666 paquets avant/après, deps Windows-only → no-op sur Linux ». L'échec n'est pas l'inattention : la dérive a été jugée inoffensive sur Linux, alors que son coût tombe sur le build Windows que personne ne mesurait.3. « la seule configuration dont on ait la preuve qu'elle compile sous Windows » — vrai, mais la formulation laissait entendre un risque de correction qui n'existe pas. Les 7 crates déclarent des bornes larges couvrant les deux états, donc les deux sont contractuellement supportés par leurs auteurs. L'écart réel est ailleurs, et il se mesure : 3 versions de
windows-syscompilées pour Windows dans l'état0.61.2, contre 5 dans l'état dérivé.Corps d'origine (2026-09-04)
Relevé en revue de #344, non bloquant pour cette PR.
Le constat
Les 7 pointeurs
windows-sysdu fichier de verrouillage —colored,dirs-sys,errno,rustix,rustls-platform-verifier,tempfile,winapi-util— doivent rester à0.61.2, parce que c'est l'état du tagv0.14.0et donc la seule configurationwindows-sysdont on ait la preuve qu'elle compile sous Windows.cargo update, quel que soit le paquet visé, les réaiguille vers 0.48/0.52/0.59 par simple re-résolution.Cette contrainte en est à sa troisième rencontre :
cargo update -p rustls-webpki -p tarles fait descendre, sans que ce soit remarqué0.61.2après vérification crate par crate contre le tagcargo update -p h2recommence ; contourné en n'appliquant que la stropheh2à la mainLe problème
Hors
Cargo.lock, la contrainte n'est écrite nulle part d'exécutable ni de durable. Vérifié :docs/adr/0006mentionnewindows-syspour le trousseau Windows — sujet sans rapport)src-tauri/Cargo.toml.cargo/audit.tomlqui en ont un danscheck-rust.ymlSTATE.mdet un message de commitDonc le prochain
cargo updatela régressera en silence, exactement comme #310 l'a fait — et il faudra qu'un relecteur reconnaisse les 7 noms de crates pour le voir. C'est le même mode d'échec silencieux que l'ADR 0018 traite pour les suppressions d'advisories : une décision qui ne vit que dans de la prose n'est pas une décision tenue.Pistes
À arbitrer, pas tranché ici :
windows-sys 0.61.2et échouer sinon. Exécutable, tracé, cohérent avec l'existant. C'est aussi le seul qui attrape la régression sans relecteur.Cargo.toml— bon marché, maisCargo.tomln'est pas l'endroit que quelqu'un lit avant de lancercargo update.0.61.2et à quelle condition en sortir (par exemple : un build Windows réussi contre une autre configuration). À combiner avec 1 plutôt qu'à la place.Une question à trancher avant : est-ce que
0.61.2doit rester figé indéfiniment, ou est-ce que la vraie sortie est de prouver une autre configuration par un build Windows, puis de laisser cargo re-résoudre librement ? Aujourd'hui la contrainte est tenue faute de preuve, pas par choix technique — et elle coûte une manipulation manuelle du lock à chaque mise à jour de dépendance.deps: domicilier la contrainte windows-sys 0.61.2 — elle ne vit que dans STATE.md et sera régressée au prochain cargo updateto deps: domicilier la contrainte windows-sys 0.61.2 — aucun garde ne la tient, et tout bump du sous-arbre TLS la fait descendre en silenceMesure prise en traitant #348 (bump
rustls0.23.36 → 0.23.45), qui restreint la prémisse de cette issue.Le corps dit que le déclencheur est « tout bump atteignant le sous-arbre réseau/TLS ».
rustlsest au cœur de ce sous-arbre — c'est la couche TLS dereqwest 0.13.2viahyper-rustls,tokio-rustlsetrustls-platform-verifier— etcargo update -p rustls --precise 0.23.45n'a rien déplacé :windows-sysprésentes sont identiques avant et après (0.45.0, 0.48.0, 0.52.0, 0.59.0, 0.60.2, 0.61.2) ;diffsur toutes les lignes mentionnantwindows-sysdansCargo.lockest vide ;rustls+rustls-webpki, version et checksum).Le déclencheur tient donc à
hyperet à ce qu'il tire — ce que la mesure d'origine avait bien vu (-p hyperfait descendre les 7,-p anyhowne bouge rien) — et non à la couche TLS en général. La formulation « sous-arbre réseau/TLS » est trop large : elle prédit une dérive là où il n'y en a pas, et conduirait à écrire une strophe à la main sans nécessité, comme en #344.La décision de fond ne change pas : sans garde-fou ni ADR, la contrainte
windows-sys 0.61.2reste tenue par de la prose, et il faut re-mesurer à chaque bump pour savoir dans quel cas on est. C'est précisément ce qui vient d'être fait une quatrième fois.Suggestion pour le garde-fou envisagé : le formuler sur la version résolue de
windows-sys(échouer si un pointeur quitte0.61.2) plutôt que sur une liste de crates déclencheurs — la liste est ce qui vient d'être trouvé faux, la version est ce qui est réellement protégé.