fix: onboarding phase 1 follow-ups: pins, Transactions card, Import messages, docs (#384) #474

Open
maximus wants to merge 7 commits from issue-384-onboarding-followups into issue-437-test-pins
Owner

Resolves #384
Generated autonomously by /autopilot run of 2026-10-18

Résumé

Maillon 7 sur 8 de la pile planned-2026-10-16-lot-v018, basé sur issue-437-test-pins (PR #473). Les quatre parties du corps réécrit par /analyze le 2026-10-16, avec les décisions D1 et D2 de Max :

  1. Épingles : deux tests Rust du dossier d'exemple (le second dossier de la boucle déjà présent, rien n'est écrit ; {{M1}} = mois le plus récent, ligne à ligne contre le gabarit), le commentaire « priority 0 » de onboarding-samples.test.ts corrigé, le cas « profil sans transaction » de TransactionsPage rendu.
  2. Transactions et Budget : D1, la carte « importez d'abord » de Transactions ne lit plus que totalInDb === 0 (même règle que Budget) ; lectures du montage de useTransactions et useBudget en Promise.allSettled.
  3. Import (#382) : D2, create_sample_import_folders refuse par des codes stables (sample_folder_exists:<nom>, import_folder_missing, invalid_date) que le front traduit (import.empty.sampleErrors.*, nom du dossier interpolé) ; tout autre message reste brut ; le message brut part au Journal en logWarn. Le bandeau de réussite n'est posé qu'après un rescan réussi. ImportEmptyState n'a plus que folderName.
  4. Documentation : commandes Tauri recomptées depuis generate_handler! (45, était 39), les cinq absentes documentées dans deux nouvelles sections de module, fetch_price décrite au présent ; hooks (29) ; CLAUDE.md (hooks seulement) ; guide sans « seed ».

Aucune migration (v1→v17), aucun appel réseau ajouté ni modifié.

Fichiers

  • Rust : src-tauri/src/commands/fs_commands.rs (constantes SAMPLE_ERROR_*, refus codés, docstring ; module de tests : codes assertés à l'identique, refuses_when_tangerine_folder_exists_and_writes_nothing, assert_months_follow_tokens appelée pour 2026-09-25 et pour le passage d'année). Les deux CSV de src/__fixtures__/samples/ compilés par include_str! sont intacts.
  • Import : src/hooks/useImportWizard.ts (sampleFolderError, WizardState.errorValues, SET_ERROR avec values facultatif, scanFolderInternal rend un booléen, createSampleFolders), src/pages/ImportPage.tsx (bandeau d'erreur avec les valeurs, appel d'ImportEmptyState), src/components/import/ImportEmptyState.tsx.
  • Transactions et Budget : src/hooks/useTransactions.ts (showsEmptyState, lectures du montage), src/hooks/useBudget.ts (lectures du montage).
  • i18n : import.empty.sampleErrors.{folderExists,importFolderMissing,invalidDate} et docs.gettingStarted.steps.5, FR et EN.
  • Tests : useImportWizard.samples.test.ts, useTransactions.wiring.test.ts, useBudget.wiring.test.ts (nouveaux, harnais partagé) ; useImportWizard.test.ts, useTransactions.test.ts, TransactionsPage.render.test.tsx, ImportPage.render.test.tsx, ImportEmptyState.test.tsx, onboarding-samples.test.ts.
  • Docs : docs/architecture.md, docs/guide-utilisateur.md, CLAUDE.md, CHANGELOG.md, CHANGELOG.fr.md.

Décisions

  • [MEDIUM] Interpolation du nom (D2) : SET_ERROR prend un values facultatif, rangé dans WizardState.errorValues (vidé par tout SET_ERROR sans valeurs et par RESET) ; la page rend t(state.error, { ...errorValues, defaultValue: state.error }), donc elle traduit toujours au rendu (suit un changement de langue) et un texte inconnu reste brut. Écartés : traduire dans le hook par useTranslation comme useDataImport (texte figé dans la langue du moment, puis repassé à t comme clé), une clé par dossier d'exemple (le corps demande un nom interpolé), un type d'erreur structuré (tous les SET_ERROR et la page changent).
  • [MEDIUM] Test de contrat Rust ↔ front : useImportWizard.test.ts lit fs_commands.rs, en extrait les const SAMPLE_ERROR_* (canari : exactement trois codes) et vérifie que chacun est traduit. Angle mort, écrit dans le test : une PR qui ne touche que src-tauri/** saute check-frontend, la dérive se voit au prochain run frontend. Écartés : pas de contrôle croisé (un code renommé côté Rust arriverait brut à l'utilisateur, en silence) ; un test Rust qui lirait le TS par include_str! (élargirait le piège REVIEW.md des fichiers compilés par include_str!).
  • [MEDIUM] ImportEmptyState seul : les « deux sites d'appel » du corps sont ImportPage.tsx l. 77-78 (entrée d'importCardModel, type ImportCardInput) et l. 153-155 (<ImportEmptyState>) ; seul le second appelle le composant. ImportCardInput.hasFolder est gardé : type partagé, onboardingPath.ts et ses tests hors de la liste de fichiers et du critère (« ImportEmptyState n'a plus que folderName »). Suite possible, voir plus bas.
  • [MEDIUM] Dossier d'import enregistré à "" : l'équivalence du corps (folderName !== null ⇔ hasFolder) ne tient pas pour "" (hasFolder vrai, folderName nul avec importFolder ? … : null). La page passe désormais importFolder !== null ? folderNameFromPath(importFolder) : null ("" donne "") : la sortie du composant est inchangée pour toute entrée et reste d'accord avec la carte de démarrage. Épinglé côté composant et côté page.
  • [MEDIUM] architecture.md au-delà de la liste : pour que chaque compteur dise sa liste (la règle du corps pour les commandes) : trois hooks non documentés ajoutés au tableau (useCategoryMigration, useCategoryTaxonomy, useFeedback, donc 28 lignes + useCollapsibleGroups = 29) ; la ligne commands/ de l'arborescence (6 modules) passe à 10 modules, 45 commandes ; les phrases « Phase 5 » sur les cours hors de la section des commandes (point 4 du service Bilan, « Les commandes Rust sont réservées… ») mises au présent.
  • [HIGH] Codes Rust en trois constantes SAMPLE_ERROR_*, assertés à l'identique (assert_eq!) : le code est épinglé et le chemin ne peut plus y figurer. Les erreurs d'écriture et les textes « Cannot compute… » (inatteignables) gardent leur anglais.
  • [HIGH] Épingle {{M1}} : la « première ligne de données » du corps porte {{M2}} dans les deux gabarits (lignes 2 à 21 en M2, 22 à 41 en M1), d'où une comparaison ligne à ligne avec le gabarit plutôt qu'une ligne. Côté vitest, onboarding-samples.test.ts rend les gabarits par son propre renderSampleTemplate et ne lit aucune ligne précise : pas d'épingle là (règle du corps), la propriété est épinglée côté Rust, où vit la commande.
  • [HIGH] Commentaire « priority 0 » réécrit après une sonde (supprimée) : sur les 180 mots-clés du seed, aucune ligne des deux exemples n'en accroche plus d'un (7 descriptions sans mot-clé, 45 avec exactement un), donc ni l'ordre des priorités ni celui des égalités ne bouge les comptes.
  • [HIGH] Journal : logWarn("[import] Sample folders not created: <brut>"). Textes bruts possibles, lus dans le code : les trois codes (nom du dossier d'exemple seulement), « Cannot compute… », Cannot create folder <exemple>: <erreur io>, Cannot write <exemple>/releve-AAAA-MM.csv: <erreur io> (le Display de std::io::Error ne porte aucun chemin). Le chemin ou le nom du dossier d'import ne peut pas y figurer ; l'erreur du rescan (Folder does not exist: <chemin>) reste à l'écran, jamais au Journal.
  • [HIGH] Promise.allSettled : chaque lecture réussie est dispatchée, le premier échec dans l'ordre du tableau (catégories, sources, compte ; comptes, compte) part en SET_ERROR, un compte en échec laisse totalInDb à null. Testé par le harnais partagé.
  • [HIGH] CHANGELOG : Modifié (carte Transactions), Corrigé (messages d'Import, bandeaux), FR et EN. Aucune entrée pour allSettled (hors liste du corps ; un COUNT en échec n'est pas un parcours utilisateur). REVIEW.md intact : aucun fait qu'il cite ne change.

D1 — totalInDb après un import (vérifié à la lecture)

totalInDb n'est lu qu'au montage de la page : seul useOnboardingChecklist s'abonne au canal d'événements de données (ADR 0022). Après un import, l'utilisateur revient de /import, ce qui remonte la page et relit le compte ; un changement de profil remonte toutes les routes (BrowserRouter key={refreshKey}). Aucune écriture de transactions ne peut se faire pendant que /transactions est montée (import sur /import, restauration dans Paramètres) : la carte ne peut donc pas rester affichée après un import aujourd'hui. Limite, non corrigée : si une écriture de transactions devenait possible depuis la page elle-même, la carte resterait jusqu'au prochain montage.

Preuves — mutations

Chaque mutation appliquée seule sur le code commité, restaurée par git checkout -- <fichier>, arbre propre vérifié après chacune (21 sur 21). Rust : cargo test --locked --all-targets commands::fs_commands (rc 101 = échec de test, aucune erreur de compilation) ; vitest : suite complète.

# Fichier Mutation Tests rouges rc
R1 fs_commands.rs boucle de vérification préalable retirée 2 (refuses_when_tangerine_folder_exists_and_writes_nothing, …desjardins…) 101
R2 fs_commands.rs m1_token et m2_token échangés dans les deux .replace 2 (writes_both_folders_for_the_two_previous_months, crosses_the_year_boundary) 101
R3 fs_commands.rs import_folder_missing → Folder does not exist: <chemin> 1 101
R4 fs_commands.rs sample_folder_exists:<nom> → Folder already exists: <nom> 2 101
R5 fs_commands.rs invalid_date → Invalid date '…': … 1 101
T1 useTransactions.ts D1 annulée (&& !isLoading) 2 (test réécrit du reducer, rendu de page « dès que le compte est connu ») 1
T2 TransactionsPage.tsx isEmptyProfile forcé à false 2 1
T3 useTransactions.ts ancien bloc Promise.all exact 3 1
T4 useTransactions.ts dernier échec au lieu du premier 1 1
T5 useBudget.ts ancien bloc Promise.all exact 2 1
T6 useImportWizard.ts SET_SAMPLE_CREATED quel que soit le rescan (ancien code) 1 1
T7 useImportWizard.ts scanFolderInternal rend true sur un échec 1 1
T8 useImportWizard.ts sampleFolderError ne traduit rien 6 1
T8b useImportWizard.ts nom du dossier non passé à la clé 2 1
T9 useImportWizard.ts ligne de Journal retirée 4 1
T10 ImportPage.tsx bandeau sans les valeurs 1 1
T11 useImportWizard.ts SET_ERROR garde les valeurs d'un refus précédent 1 1
T12 ImportEmptyState.tsx !folderName au lieu de folderName === null 2 1
T13 fs_commands.rs code renommé (date_invalid), test de contrat 1 1
T14 useImportWizard.ts ligne de Journal qui nomme le dossier d'import 4 1
T15 ImportPage.tsx appel écrit comme dans le corps (importFolder ? … : null) 1 1

R1 : avant cette PR, la même mutation laissait les cinq tests Rust verts (constat de la revue de #380) ; le test Desjardins rougit aussi désormais, par son code asserté à l'identique.

Comptes

  • vitest : 155 fichiers, 2498 tests (base dad81fa : 152, 2465), sonde rc=1 (src/zz_tmp_probe.test.ts, un expect faux, supprimé ensuite).
  • Rust : cargo test --locked --all-targets : 268 réussis, 1 ignoré (base : 267 et 1).
  • npm run build vert.
  • Recomptes cités : commandes = entrées de generate_handler! dans src-tauri/src/lib.rs (45 : fs 7, export_import 5, profile 8, license 11, auth 5, token_store 1, entitlements 1, feedback 2, backup 3, balance 2) ; hooks = ls src/hooks/*.ts | grep -v '\.test\.ts$' | wc -l (29).

Vérifications manuelles dues (prochaine release)

  1. Flash d'un profil vide (D1) : sur un profil neuf, ouvrir Transactions : la carte « Aucune transaction pour l'instant » s'affiche d'emblée, sans passer par la barre de filtres, les totaux et « Chargement... ». En FR et en EN.
  2. Messages d'Import traduits, en FR puis en EN (sélecteur de langue de la barre latérale), le dossier d'import choisi et vide :
    • dossier d'exemple déjà là : créer dans le dossier d'import un fichier vide nommé Exemple Desjardins (sans extension : ni source ni fichier de relevé, l'état vide reste), cliquer « Actualiser » (le fichier n'apparaît pas) puis « Créer un dossier d'exemple » : « « Exemple Desjardins » existe déjà dans le dossier d'import : aucun dossier d'exemple n'a été créé. Actualisez la liste ; s'il n'y apparaît pas, renommez-le avant de réessayer. » (EN : « "Exemple Desjardins" already exists in the import folder: … »), et dans Paramètres → Systèmes → Journaux une seule ligne d'avertissement [import] Sample folders not created: sample_folder_exists:Exemple Desjardins, sans chemin ;
    • dossier d'import introuvable : la page ouverte sur l'état vide, renommer ou supprimer le dossier d'import dans le gestionnaire de fichiers, puis cliquer « Créer un dossier d'exemple » sans actualiser : « Le dossier d'import est introuvable : … » ;
    • date illisible : non reproductible à la main (la date vient de l'horloge de la webview), couverte par les tests seulement.
  3. Chemin de réussite inchangé : dans un dossier d'import vide, « Créer un dossier d'exemple » montre le bandeau vert qui nomme les deux dossiers, et les deux cartes de source.
  4. Bandeaux après un rescan en échec : pas de façon fiable de faire échouer le rescan entre l'écriture et la relecture à la main ; couvert par useImportWizard.samples.test.ts. À tenter seulement si une manipulation se présente.
  5. Guide : Paramètres → guide, « Démarrage rapide », étape 6 : « les mots-clés fournis avec l'application » en FR, « the keywords that come with the app » en EN.

À regarder par le relecteur

  • Deux commits d'auto-revue en tête, sans changement de comportement du code : 1ed6a67 (le message « existe déjà » disait « Actualisez la liste pour le voir », faux quand l'entrée existante est un fichier, que la liste ne montre jamais : il dit désormais d'actualiser puis, si l'entrée n'apparaît pas, de la renommer ; CHANGELOG suivi) et 08c2f57 (formulations : paragraphe des lectures du montage, commentaire Rust des erreurs d'écriture, qui nomment le dossier d'exemple et son fichier mais jamais le chemin du dossier d'import). Suite complète, build et cargo test rejoués après eux.
  • Niveau calculé : normal (REVIEW.md de main) : fs_commands.rs est dans la liste « restent au moins normaux » ; aucun appel réseau ajouté ni modifié, aucun consentement touché. La ligne logWarn ajoutée peut partir avec une rétroaction quand l'utilisateur coche les journaux (les lignes non info) : elle ne porte qu'un code ou une erreur d'écriture sur un dossier d'exemple, jamais de donnée de l'utilisateur ni de chemin (même lecture que la ligne logWarn de #372, maillon 3).
  • Erreur du montage effacée sur Transactions (comportement antérieur, non changé) : au montage, l'effet de recherche différée relance une lecture de page à 300 ms, dont le SET_ERROR(null) efface l'erreur posée par les lectures du montage. Mesuré par une sonde sous le harnais, minuteries simulées (supprimée) : "count failed" après le montage, null 300 ms plus tard, deux lectures de page. Avant comme après cette PR, cette erreur ne reste donc qu'un instant à l'écran sur Transactions (sur Budget elle reste) ; le critère du corps (les catégories et les sources se chargent) n'en dépend pas. Suite possible.
  • ImportCardInput.hasFolder : même redondance que celle retirée d'ImportEmptyState, laissée (voir Décisions) ; suite possible.
  • CLAUDE.md : seule la ligne des hooks est touchée (consigne du maillon : #446 recompte les composants dans la même arborescence). La ligne commands/ y reste périmée (« 3 modules, 17 commandes Tauri », et les comptes par fichier) ; docs/architecture.md porte les bons comptes.
  • Nom du test refuses_when_tangerine_folder_exists_and_writes_nothing : il vérifie aussi qu'aucune autre entrée n'est créée dans le dossier d'import.

🤖 Generated with Claude Code

Resolves #384 Generated autonomously by /autopilot run of 2026-10-18 ## Résumé Maillon 7 sur 8 de la pile `planned-2026-10-16-lot-v018`, basé sur `issue-437-test-pins` (PR #473). Les quatre parties du corps réécrit par `/analyze` le 2026-10-16, avec les décisions D1 et D2 de Max : 1. **Épingles** : deux tests Rust du dossier d'exemple (le second dossier de la boucle déjà présent, rien n'est écrit ; `{{M1}}` = mois le plus récent, ligne à ligne contre le gabarit), le commentaire « priority 0 » de `onboarding-samples.test.ts` corrigé, le cas « profil sans transaction » de `TransactionsPage` rendu. 2. **Transactions et Budget** : D1, la carte « importez d'abord » de Transactions ne lit plus que `totalInDb === 0` (même règle que Budget) ; lectures du montage de `useTransactions` et `useBudget` en `Promise.allSettled`. 3. **Import (#382)** : D2, `create_sample_import_folders` refuse par des codes stables (`sample_folder_exists:<nom>`, `import_folder_missing`, `invalid_date`) que le front traduit (`import.empty.sampleErrors.*`, nom du dossier interpolé) ; tout autre message reste brut ; le message brut part au Journal en `logWarn`. Le bandeau de réussite n'est posé qu'après un rescan réussi. `ImportEmptyState` n'a plus que `folderName`. 4. **Documentation** : commandes Tauri recomptées depuis `generate_handler!` (45, était 39), les cinq absentes documentées dans deux nouvelles sections de module, `fetch_price` décrite au présent ; hooks (29) ; `CLAUDE.md` (hooks seulement) ; guide sans « seed ». Aucune migration (v1→v17), aucun appel réseau ajouté ni modifié. ## Fichiers - Rust : `src-tauri/src/commands/fs_commands.rs` (constantes `SAMPLE_ERROR_*`, refus codés, docstring ; module de tests : codes assertés à l'identique, `refuses_when_tangerine_folder_exists_and_writes_nothing`, `assert_months_follow_tokens` appelée pour 2026-09-25 et pour le passage d'année). Les deux CSV de `src/__fixtures__/samples/` compilés par `include_str!` sont intacts. - Import : `src/hooks/useImportWizard.ts` (`sampleFolderError`, `WizardState.errorValues`, `SET_ERROR` avec `values` facultatif, `scanFolderInternal` rend un booléen, `createSampleFolders`), `src/pages/ImportPage.tsx` (bandeau d'erreur avec les valeurs, appel d'`ImportEmptyState`), `src/components/import/ImportEmptyState.tsx`. - Transactions et Budget : `src/hooks/useTransactions.ts` (`showsEmptyState`, lectures du montage), `src/hooks/useBudget.ts` (lectures du montage). - i18n : `import.empty.sampleErrors.{folderExists,importFolderMissing,invalidDate}` et `docs.gettingStarted.steps.5`, FR et EN. - Tests : `useImportWizard.samples.test.ts`, `useTransactions.wiring.test.ts`, `useBudget.wiring.test.ts` (nouveaux, harnais partagé) ; `useImportWizard.test.ts`, `useTransactions.test.ts`, `TransactionsPage.render.test.tsx`, `ImportPage.render.test.tsx`, `ImportEmptyState.test.tsx`, `onboarding-samples.test.ts`. - Docs : `docs/architecture.md`, `docs/guide-utilisateur.md`, `CLAUDE.md`, `CHANGELOG.md`, `CHANGELOG.fr.md`. ## Décisions - **[MEDIUM] Interpolation du nom (D2)** : `SET_ERROR` prend un `values` facultatif, rangé dans `WizardState.errorValues` (vidé par tout `SET_ERROR` sans valeurs et par `RESET`) ; la page rend `t(state.error, { ...errorValues, defaultValue: state.error })`, donc elle traduit toujours au rendu (suit un changement de langue) et un texte inconnu reste brut. Écartés : traduire dans le hook par `useTranslation` comme `useDataImport` (texte figé dans la langue du moment, puis repassé à `t` comme clé), une clé par dossier d'exemple (le corps demande un nom interpolé), un type d'erreur structuré (tous les `SET_ERROR` et la page changent). - **[MEDIUM] Test de contrat Rust ↔ front** : `useImportWizard.test.ts` lit `fs_commands.rs`, en extrait les `const SAMPLE_ERROR_*` (canari : exactement trois codes) et vérifie que chacun est traduit. Angle mort, écrit dans le test : une PR qui ne touche que `src-tauri/**` saute `check-frontend`, la dérive se voit au prochain run frontend. Écartés : pas de contrôle croisé (un code renommé côté Rust arriverait brut à l'utilisateur, en silence) ; un test Rust qui lirait le TS par `include_str!` (élargirait le piège REVIEW.md des fichiers compilés par `include_str!`). - **[MEDIUM] `ImportEmptyState` seul** : les « deux sites d'appel » du corps sont `ImportPage.tsx` l. 77-78 (entrée d'`importCardModel`, type `ImportCardInput`) et l. 153-155 (`<ImportEmptyState>`) ; seul le second appelle le composant. `ImportCardInput.hasFolder` est gardé : type partagé, `onboardingPath.ts` et ses tests hors de la liste de fichiers et du critère (« `ImportEmptyState` n'a plus que `folderName` »). Suite possible, voir plus bas. - **[MEDIUM] Dossier d'import enregistré à `""`** : l'équivalence du corps (`folderName !== null ⇔ hasFolder`) ne tient pas pour `""` (`hasFolder` vrai, `folderName` nul avec `importFolder ? … : null`). La page passe désormais `importFolder !== null ? folderNameFromPath(importFolder) : null` (`""` donne `""`) : la sortie du composant est inchangée pour toute entrée et reste d'accord avec la carte de démarrage. Épinglé côté composant et côté page. - **[MEDIUM] `architecture.md` au-delà de la liste** : pour que chaque compteur dise sa liste (la règle du corps pour les commandes) : trois hooks non documentés ajoutés au tableau (`useCategoryMigration`, `useCategoryTaxonomy`, `useFeedback`, donc 28 lignes + `useCollapsibleGroups` = 29) ; la ligne `commands/` de l'arborescence (6 modules) passe à 10 modules, 45 commandes ; les phrases « Phase 5 » sur les cours hors de la section des commandes (point 4 du service Bilan, « Les commandes Rust sont réservées… ») mises au présent. - **[HIGH]** Codes Rust en trois constantes `SAMPLE_ERROR_*`, assertés à l'identique (`assert_eq!`) : le code est épinglé et le chemin ne peut plus y figurer. Les erreurs d'écriture et les textes « Cannot compute… » (inatteignables) gardent leur anglais. - **[HIGH]** Épingle `{{M1}}` : la « première ligne de données » du corps porte `{{M2}}` dans les deux gabarits (lignes 2 à 21 en M2, 22 à 41 en M1), d'où une comparaison ligne à ligne avec le gabarit plutôt qu'une ligne. Côté vitest, `onboarding-samples.test.ts` rend les gabarits par son propre `renderSampleTemplate` et ne lit aucune ligne précise : pas d'épingle là (règle du corps), la propriété est épinglée côté Rust, où vit la commande. - **[HIGH]** Commentaire « priority 0 » réécrit après une sonde (supprimée) : sur les 180 mots-clés du seed, aucune ligne des deux exemples n'en accroche plus d'un (7 descriptions sans mot-clé, 45 avec exactement un), donc ni l'ordre des priorités ni celui des égalités ne bouge les comptes. - **[HIGH]** Journal : `logWarn("[import] Sample folders not created: <brut>")`. Textes bruts possibles, lus dans le code : les trois codes (nom du dossier d'exemple seulement), « Cannot compute… », `Cannot create folder <exemple>: <erreur io>`, `Cannot write <exemple>/releve-AAAA-MM.csv: <erreur io>` (le `Display` de `std::io::Error` ne porte aucun chemin). Le chemin ou le nom du dossier d'import ne peut pas y figurer ; l'erreur du rescan (`Folder does not exist: <chemin>`) reste à l'écran, jamais au Journal. - **[HIGH]** `Promise.allSettled` : chaque lecture réussie est dispatchée, le premier échec dans l'ordre du tableau (catégories, sources, compte ; comptes, compte) part en `SET_ERROR`, un compte en échec laisse `totalInDb` à `null`. Testé par le harnais partagé. - **[HIGH]** CHANGELOG : Modifié (carte Transactions), Corrigé (messages d'Import, bandeaux), FR et EN. Aucune entrée pour `allSettled` (hors liste du corps ; un `COUNT` en échec n'est pas un parcours utilisateur). `REVIEW.md` intact : aucun fait qu'il cite ne change. ## D1 — `totalInDb` après un import (vérifié à la lecture) `totalInDb` n'est lu qu'au montage de la page : seul `useOnboardingChecklist` s'abonne au canal d'événements de données (ADR 0022). Après un import, l'utilisateur revient de `/import`, ce qui remonte la page et relit le compte ; un changement de profil remonte toutes les routes (`BrowserRouter key={refreshKey}`). Aucune écriture de transactions ne peut se faire pendant que `/transactions` est montée (import sur `/import`, restauration dans Paramètres) : la carte ne peut donc pas rester affichée après un import aujourd'hui. **Limite**, non corrigée : si une écriture de transactions devenait possible depuis la page elle-même, la carte resterait jusqu'au prochain montage. ## Preuves — mutations Chaque mutation appliquée seule sur le code commité, restaurée par `git checkout -- <fichier>`, arbre propre vérifié après chacune (21 sur 21). Rust : `cargo test --locked --all-targets commands::fs_commands` (rc 101 = échec de test, aucune erreur de compilation) ; vitest : suite complète. | # | Fichier | Mutation | Tests rouges | rc | |---|---|---|---|---| | R1 | `fs_commands.rs` | boucle de vérification préalable retirée | 2 (`refuses_when_tangerine_folder_exists_and_writes_nothing`, `…desjardins…`) | 101 | | R2 | `fs_commands.rs` | `m1_token` et `m2_token` échangés dans les deux `.replace` | 2 (`writes_both_folders_for_the_two_previous_months`, `crosses_the_year_boundary`) | 101 | | R3 | `fs_commands.rs` | `import_folder_missing` → `Folder does not exist: <chemin>` | 1 | 101 | | R4 | `fs_commands.rs` | `sample_folder_exists:<nom>` → `Folder already exists: <nom>` | 2 | 101 | | R5 | `fs_commands.rs` | `invalid_date` → `Invalid date '…': …` | 1 | 101 | | T1 | `useTransactions.ts` | D1 annulée (`&& !isLoading`) | 2 (test réécrit du reducer, rendu de page « dès que le compte est connu ») | 1 | | T2 | `TransactionsPage.tsx` | `isEmptyProfile` forcé à `false` | 2 | 1 | | T3 | `useTransactions.ts` | ancien bloc `Promise.all` exact | 3 | 1 | | T4 | `useTransactions.ts` | dernier échec au lieu du premier | 1 | 1 | | T5 | `useBudget.ts` | ancien bloc `Promise.all` exact | 2 | 1 | | T6 | `useImportWizard.ts` | `SET_SAMPLE_CREATED` quel que soit le rescan (ancien code) | 1 | 1 | | T7 | `useImportWizard.ts` | `scanFolderInternal` rend `true` sur un échec | 1 | 1 | | T8 | `useImportWizard.ts` | `sampleFolderError` ne traduit rien | 6 | 1 | | T8b | `useImportWizard.ts` | nom du dossier non passé à la clé | 2 | 1 | | T9 | `useImportWizard.ts` | ligne de Journal retirée | 4 | 1 | | T10 | `ImportPage.tsx` | bandeau sans les valeurs | 1 | 1 | | T11 | `useImportWizard.ts` | `SET_ERROR` garde les valeurs d'un refus précédent | 1 | 1 | | T12 | `ImportEmptyState.tsx` | `!folderName` au lieu de `folderName === null` | 2 | 1 | | T13 | `fs_commands.rs` | code renommé (`date_invalid`), test de contrat | 1 | 1 | | T14 | `useImportWizard.ts` | ligne de Journal qui nomme le dossier d'import | 4 | 1 | | T15 | `ImportPage.tsx` | appel écrit comme dans le corps (`importFolder ? … : null`) | 1 | 1 | R1 : avant cette PR, la même mutation laissait les cinq tests Rust verts (constat de la revue de #380) ; le test Desjardins rougit aussi désormais, par son code asserté à l'identique. ## Comptes - vitest : 155 fichiers, 2498 tests (base `dad81fa` : 152, 2465), sonde rc=1 (`src/zz_tmp_probe.test.ts`, un `expect` faux, supprimé ensuite). - Rust : `cargo test --locked --all-targets` : 268 réussis, 1 ignoré (base : 267 et 1). - `npm run build` vert. - Recomptes cités : commandes = entrées de `generate_handler!` dans `src-tauri/src/lib.rs` (45 : fs 7, export_import 5, profile 8, license 11, auth 5, token_store 1, entitlements 1, feedback 2, backup 3, balance 2) ; hooks = `ls src/hooks/*.ts | grep -v '\.test\.ts$' | wc -l` (29). ## Vérifications manuelles dues (prochaine release) 1. **Flash d'un profil vide (D1)** : sur un profil neuf, ouvrir Transactions : la carte « Aucune transaction pour l'instant » s'affiche d'emblée, sans passer par la barre de filtres, les totaux et « Chargement... ». En FR et en EN. 2. **Messages d'Import traduits**, en FR puis en EN (sélecteur de langue de la barre latérale), le dossier d'import choisi et vide : - dossier d'exemple déjà là : créer dans le dossier d'import un **fichier** vide nommé `Exemple Desjardins` (sans extension : ni source ni fichier de relevé, l'état vide reste), cliquer « Actualiser » (le fichier n'apparaît pas) puis « Créer un dossier d'exemple » : « « Exemple Desjardins » existe déjà dans le dossier d'import : aucun dossier d'exemple n'a été créé. Actualisez la liste ; s'il n'y apparaît pas, renommez-le avant de réessayer. » (EN : « "Exemple Desjardins" already exists in the import folder: … »), et dans Paramètres → Systèmes → Journaux une seule ligne d'avertissement `[import] Sample folders not created: sample_folder_exists:Exemple Desjardins`, sans chemin ; - dossier d'import introuvable : la page ouverte sur l'état vide, renommer ou supprimer le dossier d'import dans le gestionnaire de fichiers, puis cliquer « Créer un dossier d'exemple » sans actualiser : « Le dossier d'import est introuvable : … » ; - date illisible : non reproductible à la main (la date vient de l'horloge de la webview), couverte par les tests seulement. 3. **Chemin de réussite inchangé** : dans un dossier d'import vide, « Créer un dossier d'exemple » montre le bandeau vert qui nomme les deux dossiers, et les deux cartes de source. 4. **Bandeaux après un rescan en échec** : pas de façon fiable de faire échouer le rescan entre l'écriture et la relecture à la main ; couvert par `useImportWizard.samples.test.ts`. À tenter seulement si une manipulation se présente. 5. **Guide** : Paramètres → guide, « Démarrage rapide », étape 6 : « les mots-clés fournis avec l'application » en FR, « the keywords that come with the app » en EN. ## À regarder par le relecteur - **Deux commits d'auto-revue en tête**, sans changement de comportement du code : `1ed6a67` (le message « existe déjà » disait « Actualisez la liste pour le voir », faux quand l'entrée existante est un fichier, que la liste ne montre jamais : il dit désormais d'actualiser puis, si l'entrée n'apparaît pas, de la renommer ; CHANGELOG suivi) et `08c2f57` (formulations : paragraphe des lectures du montage, commentaire Rust des erreurs d'écriture, qui nomment le dossier d'exemple et son fichier mais jamais le chemin du dossier d'import). Suite complète, build et `cargo test` rejoués après eux. - **Niveau calculé : normal** (REVIEW.md de `main`) : `fs_commands.rs` est dans la liste « restent au moins normaux » ; aucun appel réseau ajouté ni modifié, aucun consentement touché. La ligne `logWarn` ajoutée peut partir avec une rétroaction quand l'utilisateur coche les journaux (les lignes non `info`) : elle ne porte qu'un code ou une erreur d'écriture sur un dossier d'exemple, jamais de donnée de l'utilisateur ni de chemin (même lecture que la ligne `logWarn` de #372, maillon 3). - **Erreur du montage effacée sur Transactions (comportement antérieur, non changé)** : au montage, l'effet de recherche différée relance une lecture de page à 300 ms, dont le `SET_ERROR(null)` efface l'erreur posée par les lectures du montage. Mesuré par une sonde sous le harnais, minuteries simulées (supprimée) : `"count failed"` après le montage, `null` 300 ms plus tard, deux lectures de page. Avant comme après cette PR, cette erreur ne reste donc qu'un instant à l'écran sur Transactions (sur Budget elle reste) ; le critère du corps (les catégories et les sources se chargent) n'en dépend pas. Suite possible. - **`ImportCardInput.hasFolder`** : même redondance que celle retirée d'`ImportEmptyState`, laissée (voir Décisions) ; suite possible. - **`CLAUDE.md`** : seule la ligne des hooks est touchée (consigne du maillon : #446 recompte les composants dans la même arborescence). La ligne `commands/` y reste périmée (« 3 modules, 17 commandes Tauri », et les comptes par fichier) ; `docs/architecture.md` porte les bons comptes. - Nom du test `refuses_when_tangerine_folder_exists_and_writes_nothing` : il vérifie aussi qu'aucune autre entrée n'est créée dans le dossier d'import. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 5 commits 2026-10-19 00:45:20 +00:00
create_sample_import_folders refuses with stable codes instead of English
sentences: sample_folder_exists:<name>, import_folder_missing (no longer
carrying the folder's path) and invalid_date. The wizard maps them to
import.empty.sampleErrors.* through the pure sampleFolderError, the folder
name interpolated from the new errorValues of the state; any other message
stays raw. The raw refusal also goes to the Journal as a warning: a code, or
a write error naming a sample folder and the OS error, never a path.

scanFolderInternal now tells whether the scan went through, and the success
banner only follows a rescan that did: a failed rescan leaves its error
alone on screen.

ImportEmptyState keeps folderName alone (null without a folder); the page
passes "" for a folder stored as "", as hasFolder did.

Rust tests: the three codes asserted exactly, the case of the second sample
folder already there (nothing written), and {{M1}} pinned as the most
recent month line by line against the templates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
D1 of Max: showsEmptyState of Transactions reads totalInDb === 0 only, like
Budget. On an empty profile the card comes up as soon as the count is
loaded, instead of the filter bar, the totals and the loading line until
the first page is back. A null count (not loaded, or failed) keeps the
regular layout.

The mount reads of useTransactions and useBudget run under
Promise.allSettled: each read that succeeds is dispatched, the first
failure becomes the page's error, and a failed count leaves totalInDb null
instead of emptying the category, source and account filters.

Tests: the reducer test of the loading page rewritten for D1, the page's
empty-profile case rendered (card in place of the filter bar, table and
pagination), and both hooks' mount reads run through the shared harness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The seed is not all priority 0: COSTCO and WALMART are at 50, COUCHE-TARD
ESSENCE at 10. The comment now says why the mock's sort stays right: it
sorts by descending priority like the SQL, and no line of either sample is
matched by more than one seed keyword, so the order cannot move the counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With folderName as its only prop, ImportEmptyState tells "no folder" from
"a folder" by null alone. The page hands "" for an import folder stored as
"", as hasFolder did, so the empty state keeps the "no source" variant the
step card describes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: commands and hooks recounted, sample folder codes, guide wording (#384)
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m59s
PR Check — Rust / rust (pull_request) Successful in 9m48s
a64ac3b8a8
architecture.md: the Tauri commands recounted from generate_handler! (45,
was 39), with the five that were never named documented in new sections
of their modules (feedback_commands.rs: send_feedback,
get_feedback_user_agent; backup_commands.rs: ensure_backup_dir,
get_file_size, file_exists) and fetch_price described as it is, not as
"to come in Phase 5" (also in the balance service and the tree's command
modules, now 10). Hooks: 29 files, the three undocumented ones added to the
table. The Transactions empty state reads the count alone (D1), the mount
reads stand alone, and the sample folder flow describes its codes, its
errorValues and its Journal line.

CLAUDE.md: 29 hooks in the tree. Guide and docs.gettingStarted.steps:
"the keywords that come with the app" instead of the seed jargon.
CHANGELOG (EN, FR): Changed for the Transactions card, Fixed for the sample
folder messages and banners.

Resolves #384

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus added the
autopilot:pending-human
label 2026-10-19 00:45:34 +00:00
maximus added 1 commit 2026-10-19 00:47:09 +00:00
fix(i18n): the "already exists" message also holds for a file (#384)
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 2m6s
PR Check — Rust / rust (pull_request) Successful in 3m46s
1ed6a67af0
Self-review finding: create_sample_import_folders refuses on any existing
entry named like a sample folder, a file included, which "Refresh the list"
never shows. The message now says to refresh the list and, if the entry
does not show up, to rename it before trying again (FR and EN), and the
changelog entries follow.

Resolves #384

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus added 1 commit 2026-10-19 00:48:06 +00:00
docs: wording of the read paragraph and of the write errors (#384)
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 2m4s
PR Check — Rust / rust (pull_request) Successful in 3m41s
08c2f57559
Self-review findings, no behaviour change. architecture.md: the mount reads
paragraph no longer plays on "compte" (count and account). fs_commands.rs:
the doc comment of create_sample_import_folders said a write error carries
"never a path", while it names the sample folder and its file; it now says
it never carries the import folder's path.

Resolves #384

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Niveau : normal
Verdict : APPROVE

Auto-revue de l'auteur (worker /autopilot), pas une revue indépendante. Checklist de pr-review appliquée au diff complet contre issue-437-test-pins (tête relue : 08c2f57), à la profondeur du niveau normal.

Niveau (REVIEW.md de main, fichier par fichier) : src-tauri/src/commands/fs_commands.rs est dans la liste « restent au moins normaux » ; CLAUDE.md et docs/architecture.md aussi ; code et tests de src/** sans appel réseau ajouté ni modifié, sans consentement touché ; traductions normales ; CHANGELOG*.md et docs/guide-utilisateur.md bas. La ligne logWarn ajoutée (src/hooks/useImportWizard.ts:506) peut partir avec une rétroaction quand l'utilisateur coche les journaux : elle ne porte qu'un code ou une erreur d'écriture sur un dossier d'exemple, jamais de donnée de l'utilisateur ni le chemin du dossier d'import ; même lecture que la ligne logWarn de #372. Niveau de la PR : normal.

Résumé

Les critères d'acceptation de #384, un par un :

  • épingles Rust (fs_commands.rs:442, fs_commands.rs:391) et épingle TransactionsPage : rouges sous leur mutation (R1, R2, T2) ;
  • D1 (useTransactions.ts:216) : carte dès que le compte est lu, test réécrit, rendu de page ;
  • Promise.allSettled (useTransactions.ts:237, useBudget.ts:437) : un compte en échec ne prive plus la page de ses filtres (T3, T5) ;
  • D2 : codes Rust (fs_commands.rs:228-230), traduction pure (useImportWizard.ts:88), nom interpolé par errorValues (useImportWizard.ts:251, ImportPage.tsx:110), texte inconnu brut ;
  • bandeaux : SET_SAMPLE_CREATED après un rescan réussi seulement (useImportWizard.ts:501, T6, T7) ;
  • ImportEmptyState n'a plus que folderName (ImportPage.tsx:155, cas "" épinglé, T12, T15) ;
  • docs : 45 commandes = somme des sections = generate_handler!, les cinq absentes documentées, fetch_price au présent, 29 hooks = 28 lignes + useCollapsibleGroups (vérifiés par script), CLAUDE.md, guide et docs.gettingStarted.steps.5 sans « seed » ;
  • CHANGELOG FR et EN : Modifié et Corrigé.

Rejoué : 21 mutations, toutes rouges (tableau du corps) ; sonde vitest rc=1 ; npm test 155 fichiers, 2498 tests ; npm run build vert ; cargo test --locked --all-targets 268 réussis, 1 ignoré ; arbre propre.

Passe vie privée faite quand même sur le Journal : chaque texte brut possible relu dans le code (trois codes, « Cannot compute… », Cannot create folder <exemple>: <erreur io>, Cannot write <exemple>/releve-AAAA-MM.csv: <erreur io>) ; l'erreur du rescan, qui porte le chemin, reste à l'écran ; test à quatre refus et mutation T14 (dossier d'import dans la ligne : 4 rouges). Aucun secret, aucune injection, aucune migration.

Constats (corrigés en tête de PR)

  1. src/i18n/locales/fr.json:339 et en.json (import.empty.sampleErrors.folderExists) : le message disait « Actualisez la liste pour le voir », faux quand l'entrée existante est un fichier (la commande refuse sur exists(), fichier compris, que la liste ne montre jamais). Corrigé dans 1ed6a67 : actualiser, puis renommer si l'entrée n'apparaît pas ; CHANGELOG suivi.
  2. src-tauri/src/commands/fs_commands.rs (doc de create_sample_import_folders) : « never a path » alors qu'une erreur d'écriture nomme Exemple …/releve-AAAA-MM.csv. Corrigé dans 08c2f57 : jamais le chemin du dossier d'import.
  3. docs/architecture.md, paragraphe des lectures du montage : jeu de mots sur « compte ». Corrigé dans 08c2f57.

Suggestions (non bloquantes, hors du corps)

  • S1. Sur Transactions, l'erreur posée par les lectures du montage est effacée 300 ms plus tard par le SET_ERROR(null) de la première lecture différée de la recherche (useTransactions.ts:293), mesuré par une sonde sous le harnais ; comportement antérieur, identique avant et après. Suite possible : ne pas effacer une erreur de montage depuis fetchData.
  • S2. ImportCardInput.hasFolder (src/shared/types/index.ts) garde la redondance retirée d'ImportEmptyState ; à retirer avec onboardingPath.ts et ses tests.
  • S3. CLAUDE.md, ligne commands/ de l'arborescence : « 3 modules, 17 commandes Tauri » et les comptes par fichier restent périmés (consigne du maillon : seule la ligne des hooks est touchée ici).
  • S4. Le test de contrat des codes (useImportWizard.test.ts) ne tourne pas sur une PR qui ne touche que src-tauri/** (check-frontend sautée) : angle mort écrit dans le test.

Vérifications manuelles dues à la prochaine release : liste dans le corps de la PR.

Niveau : normal Verdict : APPROVE **Auto-revue de l'auteur (worker `/autopilot`), pas une revue indépendante.** Checklist de `pr-review` appliquée au diff complet contre `issue-437-test-pins` (tête relue : `08c2f57`), à la profondeur du niveau normal. **Niveau** (REVIEW.md de `main`, fichier par fichier) : `src-tauri/src/commands/fs_commands.rs` est dans la liste « restent au moins normaux » ; `CLAUDE.md` et `docs/architecture.md` aussi ; code et tests de `src/**` sans appel réseau ajouté ni modifié, sans consentement touché ; traductions normales ; `CHANGELOG*.md` et `docs/guide-utilisateur.md` bas. La ligne `logWarn` ajoutée (`src/hooks/useImportWizard.ts:506`) peut partir avec une rétroaction quand l'utilisateur coche les journaux : elle ne porte qu'un code ou une erreur d'écriture sur un dossier d'exemple, jamais de donnée de l'utilisateur ni le chemin du dossier d'import ; même lecture que la ligne `logWarn` de #372. Niveau de la PR : normal. ## Résumé Les critères d'acceptation de #384, un par un : - épingles Rust (`fs_commands.rs:442`, `fs_commands.rs:391`) et épingle `TransactionsPage` : rouges sous leur mutation (R1, R2, T2) ; - D1 (`useTransactions.ts:216`) : carte dès que le compte est lu, test réécrit, rendu de page ; - `Promise.allSettled` (`useTransactions.ts:237`, `useBudget.ts:437`) : un compte en échec ne prive plus la page de ses filtres (T3, T5) ; - D2 : codes Rust (`fs_commands.rs:228-230`), traduction pure (`useImportWizard.ts:88`), nom interpolé par `errorValues` (`useImportWizard.ts:251`, `ImportPage.tsx:110`), texte inconnu brut ; - bandeaux : `SET_SAMPLE_CREATED` après un rescan réussi seulement (`useImportWizard.ts:501`, T6, T7) ; - `ImportEmptyState` n'a plus que `folderName` (`ImportPage.tsx:155`, cas `""` épinglé, T12, T15) ; - docs : 45 commandes = somme des sections = `generate_handler!`, les cinq absentes documentées, `fetch_price` au présent, 29 hooks = 28 lignes + `useCollapsibleGroups` (vérifiés par script), `CLAUDE.md`, guide et `docs.gettingStarted.steps.5` sans « seed » ; - CHANGELOG FR et EN : Modifié et Corrigé. Rejoué : 21 mutations, toutes rouges (tableau du corps) ; sonde vitest rc=1 ; `npm test` 155 fichiers, 2498 tests ; `npm run build` vert ; `cargo test --locked --all-targets` 268 réussis, 1 ignoré ; arbre propre. Passe vie privée faite quand même sur le Journal : chaque texte brut possible relu dans le code (trois codes, « Cannot compute… », `Cannot create folder <exemple>: <erreur io>`, `Cannot write <exemple>/releve-AAAA-MM.csv: <erreur io>`) ; l'erreur du rescan, qui porte le chemin, reste à l'écran ; test à quatre refus et mutation T14 (dossier d'import dans la ligne : 4 rouges). Aucun secret, aucune injection, aucune migration. ## Constats (corrigés en tête de PR) 1. `src/i18n/locales/fr.json:339` et `en.json` (`import.empty.sampleErrors.folderExists`) : le message disait « Actualisez la liste pour le voir », faux quand l'entrée existante est un fichier (la commande refuse sur `exists()`, fichier compris, que la liste ne montre jamais). Corrigé dans `1ed6a67` : actualiser, puis renommer si l'entrée n'apparaît pas ; CHANGELOG suivi. 2. `src-tauri/src/commands/fs_commands.rs` (doc de `create_sample_import_folders`) : « never a path » alors qu'une erreur d'écriture nomme `Exemple …/releve-AAAA-MM.csv`. Corrigé dans `08c2f57` : jamais le chemin du dossier d'import. 3. `docs/architecture.md`, paragraphe des lectures du montage : jeu de mots sur « compte ». Corrigé dans `08c2f57`. ## Suggestions (non bloquantes, hors du corps) - S1. Sur Transactions, l'erreur posée par les lectures du montage est effacée 300 ms plus tard par le `SET_ERROR(null)` de la première lecture différée de la recherche (`useTransactions.ts:293`), mesuré par une sonde sous le harnais ; comportement antérieur, identique avant et après. Suite possible : ne pas effacer une erreur de montage depuis `fetchData`. - S2. `ImportCardInput.hasFolder` (`src/shared/types/index.ts`) garde la redondance retirée d'`ImportEmptyState` ; à retirer avec `onboardingPath.ts` et ses tests. - S3. `CLAUDE.md`, ligne `commands/` de l'arborescence : « 3 modules, 17 commandes Tauri » et les comptes par fichier restent périmés (consigne du maillon : seule la ligne des hooks est touchée ici). - S4. Le test de contrat des codes (`useImportWizard.test.ts`) ne tourne pas sur une PR qui ne touche que `src-tauri/**` (`check-frontend` sautée) : angle mort écrit dans le test. Vérifications manuelles dues à la prochaine release : liste dans le corps de la PR.
Author
Owner

CI lue par l'orchestrateur du run /autopilot du 2026-10-18 sur la tête 08c2f57 : les deux contrôles sont verts.

  • check-frontend (run 589) : 155 fichiers, 2498 tests, tous verts. Ont tourné entre autres useImportWizard.samples.test.ts (bandeaux et les trois codes de refus), TransactionsPage.render.test.tsx (20), ImportPage.render.test.tsx (25), ImportEmptyState.test.tsx (14) et onboarding-samples.test.ts (9).
  • check-rust (run 590) :
    • Cache restored successfully ;
    • garde des advisories : canari webkit2gtk présent ;
    • garde windows-sys : 9 checks, exit 0 ;
    • cargo test : 268 passés, 1 ignoré (un de plus que sur main, le nouveau test de la boucle de vérification préalable) ;
    • cargo audit informatif : 663 crates, aucune vulnérabilité, 18 avertissements autorisés.
CI lue par l'orchestrateur du run `/autopilot` du 2026-10-18 sur la tête `08c2f57` : les deux contrôles sont **verts**. - `check-frontend` (run 589) : 155 fichiers, 2498 tests, tous verts. Ont tourné entre autres `useImportWizard.samples.test.ts` (bandeaux et les trois codes de refus), `TransactionsPage.render.test.tsx` (20), `ImportPage.render.test.tsx` (25), `ImportEmptyState.test.tsx` (14) et `onboarding-samples.test.ts` (9). - `check-rust` (run 590) : - `Cache restored successfully` ; - garde des advisories : canari `webkit2gtk` présent ; - garde `windows-sys` : `9 checks, exit 0` ; - `cargo test` : 268 passés, 1 ignoré (un de plus que sur `main`, le nouveau test de la boucle de vérification préalable) ; - `cargo audit` informatif : 663 crates, aucune vulnérabilité, 18 avertissements autorisés.
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 2m4s
PR Check — Rust / rust (pull_request) Successful in 3m41s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin issue-384-onboarding-followups:issue-384-onboarding-followups
git checkout issue-384-onboarding-followups

Merge

Merge the changes and update on Forgejo.
git checkout issue-437-test-pins
git merge --no-ff issue-384-onboarding-followups
git checkout issue-437-test-pins
git merge --ff-only issue-384-onboarding-followups
git checkout issue-384-onboarding-followups
git rebase issue-437-test-pins
git checkout issue-437-test-pins
git merge --no-ff issue-384-onboarding-followups
git checkout issue-437-test-pins
git merge --squash issue-384-onboarding-followups
git checkout issue-437-test-pins
git merge --ff-only issue-384-onboarding-followups
git checkout issue-437-test-pins
git merge issue-384-onboarding-followups
git push origin issue-437-test-pins
Sign in to join this conversation.
No reviewers
No milestone
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#474
No description provided.