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

Closed
opened 2026-09-04 20:09:30 +00:00 by maximus · 2 comments
Owner

Planification du 2026-10-10 (/plan-run, milestone planned-2026-10-10-release-deps) : les sections ci-dessous, à partir de « Dépendances », priment sur le texte d'analyse en cas d'écart.

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-sys par une borne large, et peuvent donc pointer plusieurs versions incompatibles entre elles sans qu'aucun contrat ne soit violé :

crate exigence déclarée
colored 3.1.1, winapi-util 0.1.11 >=0.48, <=0.61
errno 0.3.14, rustix 1.1.3, tempfile 3.24.0 >=0.52, <0.62
rustls-platform-verifier 0.6.2 >=0.52.0, <0.62.0
dirs-sys 0.5.0 >=0.59.0

main les tient toutes les sept à 0.61.2, l'état exact du tag v0.14.0 (vérifié crate par crate contre git 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 laquelle main le portait.

Le mécanisme, mesuré

Le déclencheur n'est pas « n'importe quel cargo update » mais la portée de la mise à jour :

commande, sur une copie du lock de main effet
cargo update -p anyhow aucun — les 7 restent à 0.61.2
cargo update -p hyper les 7 descendent vers 0.48/0.52/0.59

Une 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-webpki et tar en #310, h2 en #344, hyper ici). 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 :

Locking 1 package to latest compatible version
Updating hyper v1.8.1 -> v1.11.1

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 :

état versions de windows-sys compilées pour Windows
main (0.61.2) 3 — 0.59.0, 0.60.2, 0.61.2
dérivé 5 — les mêmes, plus 0.48.0 et 0.52.0

Deux crates windows-sys complets 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.61 de colored forcera 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

  • Étape Verify windows-sys pinning dans check-rust.yml, après cargo check (index chaud) et avec --locked, sur le modèle du garde des suppressions
  • Lire les arêtes depuis Cargo.lock avec awk (pas de Python dans le conteneur ubuntu:22.04), selon les règles de la section « Décisions prises en planification » — tester le code de sortie séparément de la sortie
  • Logger chaque vérification, succès compris (ok: rustix -> windows-sys 0.61.2), plus une ligne de bilan type windows-sys pinning: 7 checks, exit 0
  • Canari : une entrée dont on sait qu'elle doit être trouvée, pour qu'un silence du garde ne puisse pas passer pour un succès
  • Tester le garde par mutation — le faire échouer sur un lock dérivé avant de le déclarer bon
  • ADR : pourquoi 0.61.2, ce que coûte la dérive (3 contre 5), et à quelle condition l'entrée change ou disparaît
  • Commentaire de renvoi vers l'ADR en tête de la liste gardée, comme .cargo/audit.toml en porte un

Fichiers concernés

  • .forgejo/workflows/check-rust.yml — le garde-fou. Se déclenche déjà sur src-tauri/**, donc tout changement de lock le fait tourner ; le glob a été prouvé par #344
  • docs/adr/00XX-windows-sys-pinning.md — nouvel ADR
  • src-tauri/Cargo.lock — l'objet gardé, non modifié par ce travail

Surface 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 hyper sur 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

  • Le garde échoue sur un lock où au moins un des 7 pointe ailleurs que 0.61.2, prouvé par mutation et non par raisonnement
  • Il passe sur main et logge ses 7 vérifications plus son bilan
  • Une panne de l'outil de lecture fait échouer l'étape au lieu de la faire passer
  • L'ADR énonce la condition de sortie et le coût mesuré (3 contre 5 versions)
  • .cargo/audit.toml et son garde-fou restent intouchés

Complexité 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-msvc depuis 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 check n'est pas link-free dès qu'un build script compile du natif, et cc-rs s'arrête sur failed 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-sys dans 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.toml n'est pas ce qu'on lit avant de lancer cargo update, et rien ne l'exécute.


Dépendances

Depends on

Fichiers concernés (complet)

  • .forgejo/workflows/check-rust.yml (nouvelle étape Verify windows-sys pinning, après cargo check, avant cargo 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.toml et le garde des suppressions intouchés

Critères d'acceptation (planification)

  • Sur main, l'étape logge 7 lignes ok: <crate> -> windows-sys 0.61.2, une ligne ok: pour l'ensemble des versions, et le bilan windows-sys pinning: N checks, exit 0.
  • Mutation 1 (réseau requis) : sur une copie de 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.
  • Mutation 2 : ajouter à une copie du lock un bloc [[package]] name = "windows-sys" version = "0.53.0" → échec par l'ensemble des versions.
  • Mutation 3 : renommer un crate gardé dans une copie → échec « bloc introuvable ».
  • Mutation 4 : forcer une erreur de awk (fichier absent) → échec, pas succès silencieux.
  • Le script du garde se lance tel quel en local (bash -e -o pipefail) sur une copie du lock, pour les mutations.
  • ADR 0023 : en-tête comme l'ADR 0018 (Status, Date, Issues #345/#344/#348), pourquoi 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 (hyper et ce qu'il tire ; rustls seul ne bouge rien, mesure du 2026-09-22), condition de sortie (passage à 0.62, borne <=0.61 de colored ; convergence vers un seul windows-sys = sortie souhaitée), procédure quand le garde échoue (éditer la strophe à la main comme #344, ou mettre à jour l'ensemble sur preuve).
  • Commentaire de renvoi vers l'ADR en tête de la liste gardée dans le workflow.

Review caveats

  • 🟡 ARCHITECTURE/TECHNIQUE/SECURITE : Cargo n'écrit "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 de windows-sys tirée par un autre crate est le coût réel. Traités par les règles ci-dessous.
  • 🟢 Les arêtes peuvent porter un suffixe (registry+...) : le tolérer.

Décisions prises en planification

  • Lecture awk sur Cargo.lock, règles complètes + ensemble de versions (Max, 2026-10-10) :
    1. Pour chacun des 7 crates (colored, dirs-sys, errno, rustix, rustls-platform-verifier, tempfile, winapi-util), chaque bloc [[package]] de ce nom doit avoir une arête windows-sys égale à windows-sys 0.61.2 ; zéro bloc trouvé = échec.
    2. Une arête nue "windows-sys" passe seulement si le lock ne contient qu'un seul paquet windows-sys, en 0.61.2.
    3. L'ensemble des versions de windows-sys du 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.
    4. Canari : tempfile doit être trouvé ; sinon le garde ne prouve rien et échoue.
  • ADR numéroté 0023.
  • Pas d'entrée CHANGELOG (CI seulement).
  • Dépend de #376 seulement parce que les deux PR touchent 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)

> **Planification du 2026-10-10** (`/plan-run`, milestone `planned-2026-10-10-release-deps`) : les sections ci-dessous, à partir de « Dépendances », **priment** sur le texte d'analyse en cas d'écart. 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-sys` par une borne **large**, et peuvent donc pointer plusieurs versions incompatibles entre elles sans qu'aucun contrat ne soit violé : | crate | exigence déclarée | |---|---| | `colored` 3.1.1, `winapi-util` 0.1.11 | `>=0.48, <=0.61` | | `errno` 0.3.14, `rustix` 1.1.3, `tempfile` 3.24.0 | `>=0.52, <0.62` | | `rustls-platform-verifier` 0.6.2 | `>=0.52.0, <0.62.0` | | `dirs-sys` 0.5.0 | `>=0.59.0` | `main` les tient toutes les sept à **`0.61.2`**, l'état exact du tag `v0.14.0` (vérifié crate par crate contre `git 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 laquelle `main` le portait. ## Le mécanisme, mesuré Le déclencheur n'est pas « n'importe quel `cargo update` » mais la **portée** de la mise à jour : | commande, sur une copie du lock de `main` | effet | |---|---| | `cargo update -p anyhow` | aucun — les 7 restent à `0.61.2` | | `cargo update -p hyper` | les 7 descendent vers 0.48/0.52/0.59 | Une 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-webpki` et `tar` en #310, `h2` en #344, `hyper` ici). 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 : ``` Locking 1 package to latest compatible version Updating hyper v1.8.1 -> v1.11.1 ``` 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 : | état | versions de `windows-sys` compilées pour Windows | |---|---| | `main` (`0.61.2`) | **3** — 0.59.0, 0.60.2, 0.61.2 | | dérivé | **5** — les mêmes, plus 0.48.0 et 0.52.0 | Deux crates `windows-sys` complets 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.61` de `colored` forcera 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 - [ ] Étape `Verify windows-sys pinning` dans `check-rust.yml`, après `cargo check` (index chaud) et avec `--locked`, sur le modèle du garde des suppressions - [ ] Lire les arêtes depuis `Cargo.lock` avec `awk` (pas de Python dans le conteneur `ubuntu:22.04`), selon les règles de la section « Décisions prises en planification » — tester le **code de sortie séparément de la sortie** - [ ] Logger **chaque** vérification, succès compris (`ok: rustix -> windows-sys 0.61.2`), plus une ligne de bilan type `windows-sys pinning: 7 checks, exit 0` - [ ] Canari : une entrée dont on sait qu'elle doit être trouvée, pour qu'un silence du garde ne puisse pas passer pour un succès - [ ] **Tester le garde par mutation** — le faire échouer sur un lock dérivé avant de le déclarer bon - [ ] ADR : pourquoi `0.61.2`, ce que coûte la dérive (3 contre 5), et à quelle condition l'entrée change ou disparaît - [ ] Commentaire de renvoi vers l'ADR en tête de la liste gardée, comme `.cargo/audit.toml` en porte un ## Fichiers concernés - `.forgejo/workflows/check-rust.yml` — le garde-fou. Se déclenche déjà sur `src-tauri/**`, donc tout changement de lock le fait tourner ; le glob a été prouvé par #344 - `docs/adr/00XX-windows-sys-pinning.md` — nouvel ADR - `src-tauri/Cargo.lock` — l'objet gardé, non modifié par ce travail ## Surface 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 hyper` sur 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 - [ ] Le garde échoue sur un lock où au moins un des 7 pointe ailleurs que `0.61.2`, prouvé par mutation et non par raisonnement - [ ] Il passe sur `main` et logge ses 7 vérifications plus son bilan - [ ] Une panne de l'outil de lecture fait échouer l'étape au lieu de la faire passer - [ ] L'ADR énonce la condition de sortie et le coût mesuré (3 contre 5 versions) - [ ] `.cargo/audit.toml` et son garde-fou restent intouchés ## Complexité 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-msvc` depuis 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 check` n'est pas link-free dès qu'un build script compile du natif, et `cc-rs` s'arrête sur `failed 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-sys` dans 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.toml` n'est pas ce qu'on lit avant de lancer `cargo update`, et rien ne l'exécute. --- ## Dépendances ## Depends on - #376 ## Fichiers concernés (complet) - `.forgejo/workflows/check-rust.yml` (nouvelle étape `Verify windows-sys pinning`, après `cargo check`, avant `cargo 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.toml` et le garde des suppressions intouchés ## Critères d'acceptation (planification) - [ ] Sur `main`, l'étape logge 7 lignes `ok: <crate> -> windows-sys 0.61.2`, une ligne `ok:` pour l'ensemble des versions, et le bilan `windows-sys pinning: N checks, exit 0`. - [ ] Mutation 1 (réseau requis) : sur une copie de `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. - [ ] Mutation 2 : ajouter à une copie du lock un bloc `[[package]] name = "windows-sys" version = "0.53.0"` → échec par l'ensemble des versions. - [ ] Mutation 3 : renommer un crate gardé dans une copie → échec « bloc introuvable ». - [ ] Mutation 4 : forcer une erreur de `awk` (fichier absent) → échec, pas succès silencieux. - [ ] Le script du garde se lance tel quel en local (`bash -e -o pipefail`) sur une copie du lock, pour les mutations. - [ ] ADR 0023 : en-tête comme l'ADR 0018 (Status, Date, Issues #345/#344/#348), pourquoi `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 (`hyper` et ce qu'il tire ; `rustls` seul ne bouge rien, mesure du 2026-09-22), condition de sortie (passage à 0.62, borne `<=0.61` de `colored` ; convergence vers un seul `windows-sys` = sortie souhaitée), procédure quand le garde échoue (éditer la strophe à la main comme #344, ou mettre à jour l'ensemble sur preuve). - [ ] Commentaire de renvoi vers l'ADR en tête de la liste gardée dans le workflow. ## Review caveats - 🟡 ARCHITECTURE/TECHNIQUE/SECURITE : Cargo n'écrit `"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 de `windows-sys` tirée par un autre crate est le coût réel. Traités par les règles ci-dessous. - 🟢 Les arêtes peuvent porter un suffixe ` (registry+...)` : le tolérer. ## Décisions prises en planification - Lecture **`awk` sur `Cargo.lock`, règles complètes + ensemble de versions** (Max, 2026-10-10) : 1. Pour chacun des 7 crates (`colored`, `dirs-sys`, `errno`, `rustix`, `rustls-platform-verifier`, `tempfile`, `winapi-util`), **chaque** bloc `[[package]]` de ce nom doit avoir une arête `windows-sys` égale à `windows-sys 0.61.2` ; zéro bloc trouvé = échec. 2. Une arête nue `"windows-sys"` passe seulement si le lock ne contient qu'un seul paquet `windows-sys`, en 0.61.2. 3. L'ensemble des versions de `windows-sys` du 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. 4. Canari : `tempfile` doit être trouvé ; sinon le garde ne prouve rien et échoue. - ADR numéroté **0023**. - Pas d'entrée CHANGELOG (CI seulement). - Dépend de #376 seulement parce que les deux PR touchent `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)
maximus added the
status:ready
type:infra
source:analyste
labels 2026-09-04 20:09:30 +00:00
Author
Owner

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 de main :

commande effet sur les 7 pointeurs
cargo update -p anyhow aucun, les 7 restent à 0.61.2
cargo update -p hyper les 7 descendent vers 0.48/0.52/0.59

Le 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.md montre que #310 l'avait vu, expliqué et borné : « 7 pointeurs windows-sys repointé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-sys compilées pour Windows dans l'état 0.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-sys du 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 tag v0.14.0 et donc la seule configuration windows-sys dont 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 :

#310 (2026-07-27) cargo update -p rustls-webpki -p tar les fait descendre, sans que ce soit remarqué
#321 (2026-08-15) la revue les remonte à 0.61.2 après vérification crate par crate contre le tag
#344 (2026-09-04) cargo update -p h2 recommence ; contourné en n'appliquant que la strophe h2 à la main

Le problème

Hors Cargo.lock, la contrainte n'est écrite nulle part d'exécutable ni de durable. Vérifié :

  • Aucun ADR ne la porte (docs/adr/0006 mentionne windows-sys pour le trousseau Windows — sujet sans rapport)
  • Aucun commentaire dans src-tauri/Cargo.toml
  • Aucun garde-fou CI, contrairement aux suppressions de .cargo/audit.toml qui en ont un dans check-rust.yml
  • Il reste 3 lignes de prose dans STATE.md et un message de commit

Donc le prochain cargo update la 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 :

  1. Un garde-fou CI, calqué sur celui des suppressions : vérifier que les 7 crates pointent bien windows-sys 0.61.2 et échouer sinon. Exécutable, tracé, cohérent avec l'existant. C'est aussi le seul qui attrape la régression sans relecteur.
  2. Un commentaire dans Cargo.toml — bon marché, mais Cargo.toml n'est pas l'endroit que quelqu'un lit avant de lancer cargo update.
  3. Un ADR qui documente pourquoi 0.61.2 et à 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.2 doit 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.

## 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 de `main` : | commande | effet sur les 7 pointeurs | |---|---| | `cargo update -p anyhow` | aucun, les 7 restent à `0.61.2` | | `cargo update -p hyper` | les 7 descendent vers 0.48/0.52/0.59 | Le 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.md` montre que #310 l'avait vu, expliqué et borné : « 7 pointeurs `windows-sys` repointé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-sys` compilées pour Windows dans l'état `0.61.2`, contre 5 dans l'état dérivé. <details> <summary>Corps d'origine (2026-09-04)</summary> Relevé en revue de #344, non bloquant pour cette PR. ## Le constat Les 7 pointeurs `windows-sys` du 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 tag `v0.14.0` et donc la seule configuration `windows-sys` dont 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** : | | | |---|---| | #310 (2026-07-27) | `cargo update -p rustls-webpki -p tar` les fait **descendre**, sans que ce soit remarqué | | #321 (2026-08-15) | la revue les **remonte** à `0.61.2` après vérification crate par crate contre le tag | | #344 (2026-09-04) | `cargo update -p h2` recommence ; contourné en n'appliquant que la strophe `h2` à la main | ## Le problème Hors `Cargo.lock`, la contrainte n'est écrite **nulle part d'exécutable ni de durable**. Vérifié : - Aucun ADR ne la porte (`docs/adr/0006` mentionne `windows-sys` pour le trousseau Windows — sujet sans rapport) - Aucun commentaire dans `src-tauri/Cargo.toml` - Aucun garde-fou CI, contrairement aux suppressions de `.cargo/audit.toml` qui en ont un dans `check-rust.yml` - Il reste 3 lignes de prose dans `STATE.md` et un message de commit Donc le prochain `cargo update` la 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 : 1. **Un garde-fou CI**, calqué sur celui des suppressions : vérifier que les 7 crates pointent bien `windows-sys 0.61.2` et échouer sinon. Exécutable, tracé, cohérent avec l'existant. C'est aussi le seul qui attrape la régression sans relecteur. 2. **Un commentaire dans `Cargo.toml`** — bon marché, mais `Cargo.toml` n'est pas l'endroit que quelqu'un lit avant de lancer `cargo update`. 3. **Un ADR** qui documente pourquoi `0.61.2` et à 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.2` doit 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. </details>
maximus changed title from deps: domicilier la contrainte windows-sys 0.61.2 — elle ne vit que dans STATE.md et sera régressée au prochain cargo update to 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 2026-09-04 20:44:34 +00:00
Author
Owner

Mesure prise en traitant #348 (bump rustls 0.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 ». rustls est au cœur de ce sous-arbre — c'est la couche TLS de reqwest 0.13.2 via hyper-rustls, tokio-rustls et rustls-platform-verifier — et cargo update -p rustls --precise 0.23.45 n'a rien déplacé :

  • les 6 versions de windows-sys pré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) ;
  • un diff sur toutes les lignes mentionnant windows-sys dans Cargo.lock est vide ;
  • 666 paquets avant et après, diff total du lock = 4 lignes (rustls + rustls-webpki, version et checksum).

Le déclencheur tient donc à hyper et à ce qu'il tire — ce que la mesure d'origine avait bien vu (-p hyper fait descendre les 7, -p anyhow ne 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.2 reste 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 quitte 0.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é.

Mesure prise en traitant #348 (bump `rustls` 0.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 ». `rustls` est au cœur de ce sous-arbre — c'est la couche TLS de `reqwest 0.13.2` via `hyper-rustls`, `tokio-rustls` et `rustls-platform-verifier` — et `cargo update -p rustls --precise 0.23.45` n'a **rien** déplacé : - les 6 versions de `windows-sys` pré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) ; - un `diff` sur **toutes** les lignes mentionnant `windows-sys` dans `Cargo.lock` est vide ; - 666 paquets avant et après, diff total du lock = 4 lignes (`rustls` + `rustls-webpki`, version et checksum). Le déclencheur tient donc à `hyper` et à ce qu'il tire — ce que la mesure d'origine avait bien vu (`-p hyper` fait descendre les 7, `-p anyhow` ne 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.2` reste 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 quitte `0.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é.
maximus added this to the planned-2026-10-10-release-deps milestone 2026-10-10 23:22:29 +00:00
maximus added
status:in-progress
and removed
status:ready
labels 2026-10-10 23:56:27 +00:00
maximus added
status:review
and removed
status:in-progress
labels 2026-10-11 00:18:00 +00:00
maximus added
status:approved
and removed
status:review
labels 2026-10-11 22:48:06 +00:00
maximus removed the
status:approved
label 2026-10-12 19:33:42 +00:00
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#345
No description provided.