docs(release): add a real update-cycle QA checklist as step 10 #322
No reviewers
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#322
Loading…
Reference in a new issue
No description provided.
Delete branch "issue-315-qa-update-cycle"
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?
Resolves #315
L'etape 9 du skill
/releaseinspectaitlatest.jsonet s'arretait la. Ca prouve qu'il est bien forme, pas qu'il installe. Le reste de la chaine — telechargement TLS, verification de signature, execution de l'installeur, redemarrage — n'est exerce par aucun test :cargo checketcargo testprouvent que ce code compile, rien de plus. Une regression s'y voit chez l'utilisateur, au moment de la mise a jour, et l'auto-update est une fonctionnalite Base+.Ce n'est pas automatise, et c'est assume
Le test demande deux environnements de bureau reels. La valeur ne vient donc pas de l'automatisation mais des preconditions : ce sont elles qui separent « derouler le test » de « avoir l'air de le derouler ». Chacune a un mode d'echec ou la case se coche sans que rien n'ait ete verifie.
check()compare strictementrelease.version > version_courante. Tester sur la machine qui vient de builder la release affiche « a jour », et la checklist se coche comme « rien a mettre a jour » — le saut silencieux que #315 existe pour eliminer, avec en prime une trace ecrite affirmant qu'elle est passee.activation.tokenvenu d'une autre machine.useUpdaterappellecheck_entitlement("auto-update")et bascule ennotEntitledavant d'interroger le serveur ; un token lie a un autremachine_idfait retomber l'edition en Gratuite, et le testeur voit une carte anodine plutot qu'un echec.install_debcascadepkexec->zenity/kdialog->sudodans un terminal. Sans agent, l'app parait figee au lieu d'echouer, et un chemin de repli passe pour le chemin nominal.Deux flux separes, parce qu'ils different vraiment
Sous Windows,
downloadAndInstallne rend jamais la main : le processus faitexit(0)et l'installeur NSIS prend le relais. Les etatsreadyToInstalletinstallingne s'affichent jamais — les attendre ferait echouer un test qui a reussi.Sous Linux, l'app reste vivante, une invite d'authentification apparait, et l'utilisateur clique le controle de redemarrage.
La checklist part du seul point d'entree reel : Parametres -> Systemes -> « Verifier les mises a jour ». Il n'y a aucune verification automatique dans l'app, ce bouton et celui de la page d'erreur sont les deux seuls declencheurs.
Ce que le test ne prouve pas — et ca corrige la premisse de #315
J'avais ecrit dans #315 que le correctif
tar« ne se manifesterait qu'au moment de la mise a jour ». C'est faux.Dans
tauri-plugin-updater,tarn'est atteint que parinstall_appimageet la branche macOS.app.tar.gz. Nos bundles ne passent pas par la : le.debfaitpkexec dpkg -i, le NSIS ecrit le.exedans un temp et le lance. Tant qu'on ne livre pas d'AppImage, le chemintarest mort et aucune checklist manuelle ne le couvrira.La doc l'ecrit noir sur blanc. Ce qui est reellement prouve : TLS (
rustls-webpki), signature minisign, execution de l'installeur, redemarrage.Le chemin d'echec n'est pas un rollback
Republier le
latest.jsonprecedent arrete la propagation, mais la comparaison stricte decheck()fait que les utilisateurs deja passes a la version cassee ne se verront jamais proposer la precedente : ils sont bloques dessus. Le vrai correctif est une vN+1.Presenter le re-
PUTcomme « le rollback » serait faux dans la direction qui compte — quelqu'un le lirait en pleine incident. La doc enonce les trois faits : ce que ca arrete, ce que ca n'arrete pas, et ou est le vrai correctif. Elle note aussi que le corps dulatest.jsonprecedent est recuperable depuis les assets de la release precedente, sa copie dans le registre ayant ete detruite par leDELETEquerelease.ymlfait avant chaque envoi, et que l'operation demande lePACKAGE_TOKEN.rpm : casse, pas « non verifie »
Ecrit comme tel, et sorti en issue separee (#320) plutot que laisse en ligne de checklist :
latest.jsonne porte qu'une entreelinux-x86_64construite depuis le.deb, donc une installation rpm recoit des octets deb queInstaller::Rpmrejette. C'est un defaut produit, pas un trou de couverture.Forme
La checklist vit dans
docs/qa-update-cycle.md, sur le modele dedocs/qa-refonte-seed-categories-ipc.md(bloc Prerequis + cases- [ ]).SKILL.mdgarde le role de declencheur et pointe dessus — c'est le fichier qu'on lit en faisant une release. Son frontmatterupdated:et sa section## Changelogsont mis a jour selon sa propre convention.Derniere case : consigner le resultat sur la release Forgejo, et si la checklist n'a pas ete deroulee, l'ecrire avec la raison. Une etape non tracee est indiscernable d'une etape passee.
CI
Aucun workflow ne se declenche sur cette PR :
check-rust.ymlfiltre sursrc-tauri/**et.cargo/**,check-frontend.ymlignoredocs/**et.claude/**. C'est attendu pour un changement markdown seul — signale ici pour que l'absence de checks ne se lise pas comme un echec.Pas d'entree CHANGELOG : changement de processus, aucun effet visible pour l'utilisateur.
REQUEST_CHANGES
Doc-only PR, donc la revue a porté sur la seule chose qui compte ici : est-ce que chaque affirmation est vraie ? J'ai retracé les 14 claims dans le code. Treize tiennent, souvent au détail près. Une est fausse, et c'est la séquence d'états de la section Linux — la seule cible où ces états sont observables, donc le cœur opérationnel du document.
Bloquant
1.
docs/qa-update-cycle.md§2 « Linux (.deb) » — la séquence d'états est inverséeLa checklist annonce :
Le code fait l'inverse.
downloadAndInstallest un seul appel qui télécharge ET installe :node_modules/@tauri-apps/plugin-updater/dist-js/index.js:43-54→ uninvoke('plugin:updater|download_and_install').tauri-plugin-updater-2.10.0/src/commands.rs:180-194— l'événementFinishedest émis à la fin du téléchargement, puisdownload_and_installenchaîne sur l'installation.src/hooks/useUpdater.ts:119-121—Finishedest un no-op (// handled below), aucun dispatch. L'UI reste donc surdownloading.updater.rs:1048-1141—install_deb→try_install_with_privileges→pkexec: l'invite s'ouvre à l'intérieur du mêmeawait.src/hooks/useUpdater.ts:123—READY_TO_INSTALLn'est dispatché qu'après que l'installation est terminée.Séquence réelle :
downloading— barre de progressiondpkg -itermine →readyToInstall= « Mise à jour prête à installer » + bouton « Installer et redémarrer » ← c'est ça, le contrôle de redémarrageinstalling= « Installation en cours... » →relaunch()Trois conséquences, dans l'ordre de gravité :
downloading, pas enreadyToInstall. Un testeur qui suit cette checklist cherche un blocage aprèsreadyToInstall; ce qu'il voit, c'est une barre de téléchargement immobile. Il consigne « le téléchargement bloque » au lieu de « pas d'agent polkit » : le mauvais rapport de bug, soit exactement la fausse trace que la page dit combattre.installing, puis le contrôle de redémarrage apparaît » inverse cause et effet.installingest dispatché parinstallAndRestart(useUpdater.ts:129-132) quand l'utilisateur clique le contrôle de redémarrage. Le contrôle apparaît donc d'abord, etinstallingest ce qui le remplace.À ajouter au passage, parce que c'est le vrai piège de lecture de cet écran : quand
readyToInstalls'affiche, le paquet est déjà installé sur le disque. Le bouton « Installer et redémarrer » ne fait querelaunch(). Le libellé ment sur cette cible ; un testeur qui s'arrête là croit que rien n'a été installé, et celui qui voit la version changer après le clic attribue l'installation au clic.Séquence corrigée proposée :
C'est une correction d'un paragraphe. Le reste de la page n'est pas touché.
Suggestions (non bloquantes)
2. §1 Windows — « ce bouton et celui de la page d'erreur sont les deux seuls déclencheurs » : il y en a trois.
checkForUpdateest câblé enUpdateCard.tsx:60(étatidle),:89(petite icône rafraîchir dans l'étatupToDate) et:201(réessayer, étaterror). La phrase importante — « il n'y a aucune vérification automatique dans l'app » — est exacte : le seuluseEffect(:40-44) charge les notes de version. Il suffit d'écrire trois.3.
SKILL.md:62-65— la nouvelle entrée de changelog casse l'ordre du fichier.2026-07-27est insérée entre2026-07-01et2026-07-13, alors que le fichier était strictement croissant. Faible enjeu (les autres skills ne sont pas cohérents non plus), mais ici c'est une régression locale.4. Titre de PR. « to step 9 » — le changement ajoute une étape 10.
Vérifié et exact (pour que la correction ne relance pas le travail déjà fait)
J'ai retracé chaque affirmation ; toutes celles-ci tiennent, y compris les numéros de ligne :
release.version > self.current_version—updater.rs:532, aucunversion_comparatorcustom côté app. Le prérequis « partir de la version précédente » est fondé.check()—useUpdater.ts:79-86;auto-update= Base|Premium (entitlements.rs:22). Mieux que ce que dit la page : même avecauto-updatedans lesfeatures[]signées, Free est refusé (entitlements.rs:109, fail-closed).activation.tokenlié aumachine_id—license_commands.rs:210-266. La nuance de la page est exactement juste : token absent = pré-activation tolérée, token d'une autre machine = retour en Gratuite.dev-overrideabsent des builds de release — feature Cargo non-défaut (Cargo.toml:78), etrelease.yml:50ne passe aucun--features.pkexec→zenity/kdialog→sudo—updater.rs:1101-1141, littéralement dans cet ordre.exit(0)—updater.rs:865, doncreadyToInstall/installingne s'affichent effectivement jamais sur cette cible. La section Windows est juste de bout en bout, y compris la remarque surcontentLength(un serveur qui ne le renvoie pas →progressPercentnull → barre à 0, visible).tarinatteignable — les deux seuls usages sontinstall_appimage(updater.rs:1011) et la branche macOS (:1228) ;tauri.conf.json:26ne cible quensis, deb, rpm. La correction de la prémisse de #315 est juste, et le chemin est bien mort.latest.jsonne portelinux-x86_64que depuis le.deb(release.yml:104-122), l'installeur est choisi d'après le bundle installé (updater.rs:968), etinstall_rpmrejette sur les octets magiques (:1058-1063). Les.rpmsont bien publiés quand même (release.yml:50). Sortir ça en #320 plutôt qu'en ligne de checklist est le bon arbitrage.DELETEpuisPUTsurgeneric/simpl-resultat/latestavecPACKAGE_TOKEN(release.yml:215-240) ; la copie registre est bien détruite avant chaque envoi ; etlatest.jsonest bien récupérable, la boucle d'upload envoie toutrelease-assets/*en asset de release. Le point « republier vN-1 n'est pas un rollback » est juste et mérite d'être écrit noir sur blanc.check-frontend.ymlignoredocs/**et.claude/**,check-rust.ymlfiltre sursrc-tauri/**/.cargo/**. L'absence de checks est attendue, pas un échec.../../../docs/qa-update-cycle.mddepuis.claude/skills/release/→ résout bien à la racine. Modèledocs/qa-refonte-seed-categories-ipc.mdexistant.Le document est bien meilleur que la moyenne d'une checklist QA — les modes d'échec « la case se coche sans que rien ne soit vérifié » sont le bon angle, et l'auto-correction de la prémisse
tarde #315 est honnête et vérifiable. C'est précisément parce que sa valeur est sa précision que la séquence Linux doit être juste avant merge.REQUEST_CHANGES
Passe de re-vérification. Le head n'a pas bougé depuis la revue du 2026-08-14 (
10d8a79, commit du 2026-07-27 ; la revue a été postée à 19:57Z etupdated_atde la PR s'arrête là). Le blocage n'a donc pas été traité — mais je ne l'ai pas relayé sur parole, je l'ai retracé de zéro depuis les sources. Il est confirmé, et la ligne décisive n'avait pas été citée. Je corrige aussi un point de la revue précédente qui, appliqué tel quel, produirait une correction fausse.Bloquant — toujours ouvert
1.
docs/qa-update-cycle.md§2 « Linux (.deb) » — la séquence d'états ne correspond pas au codeConfirmé, et voici la ligne qui tranche —
tauri-plugin-updater-2.10.0/src/updater.rs:723-729:on_download_finish()est appelé àupdater.rs:710, à la fin du téléchargement ;install(bytes)s'exécute ensuite, dans le mêmeawait. La chaîne complète :@tauri-apps/plugin-updater/dist-js/index.js—downloadAndInstall= un seulinvoke('plugin:updater|download_and_install'), jamaisinstallséparément.commands.rs:180-194—Finishedest émis par la closureon_download_finish, donc avant l'installation.useUpdater.ts:119-121—Finishedest un no-op (// handled below). L'UI ne change pas d'état.useUpdater.ts:123—READY_TO_INSTALLest dispatché après que l'awaita résolu, c'est-à-dire après quedpkg -ia terminé.Séquence réelle sous Linux :
downloadingreadyToInstall+ bouton « Installer et redémarrer »installing→relaunch()La checklist demande de cocher
readyToInstallavant l'invite. C'est l'inverse, et les conséquences sont exactement celles que la page dit combattre :downloading, pas enreadyToInstall. Le testeur qui suit la checklist guette un blocage aprèsreadyToInstallet voit une barre de téléchargement immobile : il rapporte « le téléchargement bloque » au lieu de « pas d'agent polkit ». C'est la fausse trace, produite par la page censée l'éliminer.installing» inverse cause et effet.installingvient deinstallAndRestart(useUpdater.ts:129-132), déclenché par le clic sur le contrôle de redémarrage. Le contrôle apparaît d'abord ;installingest ce qui le remplace.readyToInstall, le.debest déjà installé sur le disque.installAndRestartne fait querelaunch()(useUpdater.ts:129-132) — aucune installation. Le libellé du bouton ment sur cette cible : qui s'arrête là croit que rien n'est installé, qui voit la version changer attribue l'installation au clic. À écrire, c'est le vrai piège de lecture de cet écran.La séquence corrigée proposée dans la revue précédente est juste ; elle reste valable telle quelle. C'est un paragraphe.
Correction de la revue précédente — à lire avant de corriger
2. Le décompte des déclencheurs était faux dans les deux sens. Il y a 4 sites, pas 2 et pas 3.
La revue du 14 disait « il y en a trois » et mappait
UpdateCard.tsx:201sur « la page d'erreur ». C'est inexact ::201est le retry de l'étaterrorde la carte elle-même, etErrorPageest un composant distinct avec son propre déclencheur. Le relevé réel :UpdateCard.tsx:60— bouton « Vérifier les mises à jour » (étatidle)UpdateCard.tsx:89— icône rafraîchir (étatupToDate)UpdateCard.tsx:201— réessayer (étaterrorde la carte)ErrorPage.tsx:82— bouton de la page d'erreur, via son proprehandleCheckUpdate(ErrorPage.tsx:18-38), qui détecte seulement : il affiche la version dispo (:90) et n'offre aucun chemin de téléchargement.Appliquer « écrire trois » remplacerait donc une imprécision par une autre. Si la phrase doit être corrigée, le fond utile est : quatre déclencheurs manuels, dont celui de la page d'erreur ne fait que détecter — et surtout la partie qui porte réellement, « aucune vérification automatique dans l'app », qui est exacte (le seul
useEffect,UpdateCard.tsx:40-44, charge les notes de version quand l'état est déjàavailable).Suggestion nouvelle — non bloquante
3. « Si ça casse » : le
DELETEet lePUTne visent pas le même préfixe d'API.La doc écrit «
DELETEpuisPUT, avec lePACKAGE_TOKEN». Dansrelease.yml:217-219:release.ymlporte un commentaire explicite là-dessus — donc le piège est connu et déjà tombé une fois. Quelqu'un qui suit la doc en incident et rejoue les deux verbes sur la même URL prend un 404 sur l'un des deux et brûle du temps au pire moment. La page fait l'effort de dire « lire cette section avant, pas pendant » ; le chemin exact des deux appels y a sa place. Le reste de la section est juste et vérifié (la copie registre est bien détruite par leDELETEavant chaque envoi, et lelatest.jsonprécédent est bien récupérable en asset de release).4. Rappels des points mineurs déjà signalés et toujours ouverts : le changelog de
SKILL.mdinsère2026-07-27entre2026-07-01et2026-07-13(le fichier était strictement croissant), et le titre de PR dit « to step 9 » alors que le changement ajoute une étape 10.Re-vérifié indépendamment — tient
Je n'ai pas repris ces points sur la foi de la revue précédente, je les ai retracés :
updater.rs:530-532:None => release.version > self.current_version, et aucunversion_comparatordéfini côté app (grep vide sursrc/etsrc-tauri/src/). Le prérequis « partir de la version précédente » et le raisonnement « republier vN-1 n'est pas un rollback » reposent tous deux sur cette ligne, et tiennent.check()—useUpdater.ts:79-86:check_entitlement("auto-update")puisNOT_ENTITLEDetreturnavantcheck().auto-update= Base|Premium (entitlements.rs:22).pkexec→zenity/kdialog→sudo—updater.rs:1107(pkexec),get_password_graphically(zenity puis kdialog),install_with_sudo:1173. Littéralement cet ordre.exit(0)—updater.rs:865.readyToInstall/installingne s'affichent effectivement jamais sur cette cible ; la section Windows est juste de bout en bout. La remarquecontentLengthl'est aussi : sans lui,progressPercentest null → barre figée à 0 et pas de pourcentage (UpdateCard.tsx:160-166), donc visible à l'œil.tarinatteignable — les deux seulstar::Archivesontupdater.rs:1011(dansinstall_appimage, fn 975, bornée parinstall_debà 1048) et:1228(dans#[cfg(target_os = "macos")] impl Update, ouvert à:1204).install_debfaittry_tmp_locations(bytes, "dpkg", "-i"), sans archive. La correction de la prémisse de #315 est juste et c'est la partie la plus utile du document.latest.jsonne portelinux-x86_64que depuis le.deb(release.yml:104-122, plage citée exacte), l'installeur vient du bundle installé (updater.rs:968-970), etinstall_rpmrejette surinfer::archive::is_rpm(:1058-1063). Sortir ça en #320 (ouverte,type:bug) plutôt qu'en ligne de checklist est le bon arbitrage.check-rust.ymlfiltresrc-tauri/**/.cargo/**,check-frontend.ymlignoredocs/**et.claude/**. L'absence de checks n'est pas un échec.Resolves #315est bien dans le message de commit de10d8a79, pas seulement dans le corps de la PR. L'issue se fermera.../../../docs/qa-update-cycle.mddepuis.claude/skills/release/SKILL.md→ résout à la racine du repo. Modèledocs/qa-refonte-seed-categories-ipc.mdbien existant, même forme (bloc Prérequis + cases). Frontmatterupdated: 2026-07-27cohérent avec la date du commit. Pas d'entrée CHANGELOG : correct, changement de processus.Le document vaut mieux que la moyenne des checklists QA — cadrer chaque prérequis par son mode d'échec « la case se coche sans que rien ne soit vérifié » est le bon angle, et l'auto-correction de la prémisse
tarde #315 est honnête et vérifiable. C'est précisément parce que sa valeur est sa précision qu'une séquence d'états inversée sur la seule cible où ces états sont observables doit être corrigée avant merge.10d8a79fd8to97d376b83cdocs(release): add a real update-cycle QA checklist to step 9to docs(release): add a real update-cycle QA checklist as step 10Blocage corrigé —
97d376bLes quatre points de la revue sont traités, chacun re-vérifié sur la source avant d'écrire.
Bloquant — séquence d'états Linux. §2 est réécrite dans l'ordre réel : l'invite d'authentification arrive pendant que la carte affiche encore « Téléchargement en cours… », puis
readyToInstall, puis le clic qui ne fait que relancer. La case qui demandait de cocherreadyToInstallavant l'invite disparaît. Le prérequis polkit du haut de page dit maintenant où l'app se fige sans agent (écran de téléchargement, pasreadyToInstall) — c'était le maillon qui produisait le mauvais rapport de bug. Ajouté aussi, parce que c'est le vrai piège de lecture de cet écran : àreadyToInstallle.debest déjà sur le disque,installAndRestartne fait querelaunch().Vérifié moi-même plutôt que repris sur parole :
updater.rs:723-729(download(...).await?puisinstall(bytes), même appel),on_download_finish()à:710,useUpdater.ts:119-121(Finished→// handled below, aucun dispatch),:123(READY_TO_INSTALLaprès résolution de l'await),:129-132(INSTALLINGpuisrelaunch(), rien d'autre).Déclencheurs — quatre, pas deux ni trois. La phrase §1 liste maintenant les quatre sites manuels et précise que celui de la page d'erreur détecte seulement. Confirmé :
UpdateCard.tsx:60/89/201+ErrorPage.tsx:82, dont lehandleCheckUpdate(:18-38) s'arrête àsetUpdateStatus("available")sans aucun chemin de téléchargement. La partie porteuse — « aucune vérification automatique dans l'app » — était exacte et reste telle quelle.Préfixes d'API. « Si ça casse » énonce les deux URL en clair,
DELETEsur/api/v1/packages/etPUTsur/api/packages/. Confirmé surrelease.yml:217-219, commentaire inclus.Ordre du changelog
SKILL.md. L'entrée2026-07-27repasse après2026-07-13.Titre de PR corrigé en « as step 10 ».
Branche rebasée sur
main(elle avait 15 commits de retard) et empilée surissue-312-313-drop-quickxml-suppressionspour un merge ff-only de la pile. Tip validé localement :cargo checkOK,cargo test111 passed, 0 failed.Mergée dans
mainen ff-only :f6418f7+97d376b(le correctif de revue), empilée sur #321. Forgejo ne détecte pas un merge local comme merged — d'où cette fermeture manuelle.Le blocage de la revue est corrigé avant merge : la séquence d'états Linux suit maintenant le code (invite d'authentification pendant « Téléchargement en cours… », puis
readyToInstall, puis le clic qui ne fait que relancer), le prérequis polkit dit où l'app se fige, les quatre déclencheurs manuels sont listés avec la page d'erreur marquée détection-seule, les deux préfixes d'API du playbook sont explicités, et le changelog du skill repasse en ordre de date. Détail dans le commentaire précédent.CI run 371 verte (la branche héritait du commit deps, donc
check-rusts'est déclenchée). #315 fermée automatiquement par leResolvesdu message de commit.Pull request closed