feat(profiles): name the copy of a stopped catch-up, one copy per profile, startup error cleared (#395) #436

Merged
maximus merged 8 commits from issue-395-catch-up-followups into main 2026-10-14 00:28:51 +00:00
Owner

Generated autonomously by /autopilot run of 2026-10-12 (milestone planned-2026-10-10-fiabilite-tests, worker 4/4, last link of the stack #433 → #434 → #435 → this PR).

Suivis de la revue de la PR #394 (#390) consignés dans #395. Base : issue-389-i18n-mock (PR #435).

Ce qui change

1. Copie nommée sur un rattrapage arrêté

  • ProfilePreparation.catch_up_backup: Option<String> (Rust et TS), rempli dès que la copie existe, même si le rattrapage s'arrête ensuite. catch_up devient une enveloppe de test de catch_up_into(conn, migrations, backup, &mut CatchUp), que prepare_at appelle : le rapport partiel survit à l'erreur.
  • La ligne d'erreur du Journal finit par ; copy of the previous state kept as <nom>.
  • withoutFolders (db.ts) : dans toute ligne d'erreur de describePreparation et dans le message de repli de connectToProfile, un chemin est réduit à son dernier segment, tout autre séparateur devient - (« disk I/O error » → « disk I-O error »). Aucune ligne d'erreur ne contient / ni \ (testé, y compris <profile folder>/migration-backups/… laissé par scrub et C:\Users\L'Heureux\…).
  • Un test Rust par chemin d'erreur après la copie : history_write_failed (déclencheur qui refuse l'historique), pragma_failed (point de défaillance #[cfg(test)], voir décisions), apply_pending (v18 qui échoue).

2. Épingles RV4, RV7, RT2 : le test du profil sans catégorie 31 porte une violation préexistante (transaction vers une catégorie absente, clé SET NULL) : signalée dans fk_violations, jamais touchée. Test TS de la ligne point to a missing row.

3. Délai de démarrage

  • Bug corrigé : la logique de démarrage d'App.tsx passe dans src/utils/startupConnection.ts (startProfileConnection), qui fait setStartupError(null) au succès ; 5 tests aux faux minuteurs.

  • Test #[ignore] a_large_profile_catch_up_duration : plancher 6 (schéma v0.3.0 + seed), 100 001 transactions (17 Mo, 10 001 drapeaux texte), v7 → v17 avec copie, foreign_key_check et réparation.

    cargo test --release --lib -- --ignored a_large_profile_catch_up_duration --nocapture
    prepare_at: 100001 transactions, 17.0 MB, floor 6 -> v17, 10001 flags repaired: 306 ms (report: 306 ms)
    (debug : 422 ms)
    

    Machine : ThinkPad T490, Intel Core i5-8365U @ 1.60GHz (max 4,1 GHz), 8 CPU (4 cœurs × 2 fils, nproc 8), 16 Go, SSD NVMe, Linux 6.17.

  • Règle appliquée : 306 ms < 3 s → STARTUP_TIMEOUT_MS reste 10 000. Mesure, machine, règle et risque résiduel dans les points de vigilance de l'ADR 0021.

4. Rétention des copies

  • backup_to refait la copie à chaque rattrapage : .tmp, synchronisé, renommé par-dessus la copie de même nom (jamais supprimée d'abord).
  • Après un rattrapage réussi seulement, et après le renommage, remove_backups(dir, stem, Some(copie)) supprime les autres <profil>-before-v*.db / .db.tmp du profil (au mieux). Rien n'est supprimé sur erreur, sur copie échouée, ni quand rien n'est à appliquer. delete_profile_db passe None.
  • Guide (docs/guide-utilisateur.md) et docs.profiles.tips FR/EN : une seule copie par profil, la plus récente, refaite à chaque mise à jour, supprimée avec le profil.

5. CHANGELOG (EN + FR, sous [Unreleased], entrées existantes gardées) : Changed / Modifié (une copie par profil, copie nommée au Journal), Fixed / Corrigé (page d'erreur du démarrage).

Fichiers

  • src-tauri/src/database/profile_db.rs, src-tauri/src/commands/profile_commands.rs
  • src/services/db.ts, src/services/db.test.ts
  • src/App.tsx, src/utils/startupConnection.ts (nouveau), src/utils/startupConnection.test.ts (nouveau)
  • docs/adr/0021-migrations-profils-secondaires.md, docs/architecture.md, docs/guide-utilisateur.md, src/i18n/locales/fr.json, src/i18n/locales/en.json
  • CHANGELOG.md, CHANGELOG.fr.md

Décisions

  • [HIGH] catch_up_into + catch_up en enveloppe #[cfg(test)] : signature d'erreur String et tests existants inchangés.
  • [MEDIUM] pragma_failed : aucune façon de faire échouer PRAGMA foreign_keys = ON sur un vrai SQLite (pas de feature hooks/limits de rusqlite). Point de défaillance fail_point("pragma") lu dans un thread_local compilé sous #[cfg(test)] seulement, toujours Ok en production. Écartés : activer une feature rusqlite pour un test ; ne pas tester ce chemin.
  • [MEDIUM] Le critère « aucune ligne d'erreur ne contient / ni \ » est appliqué à toutes les lignes d'erreur de la préparation, pas seulement au nom de la copie : scrub (Rust) laisse <profile folder>/migration-backups/x.tmp. Écartés : changer le placeholder Rust (épinglé par #390) ; tronquer le message.
  • [MEDIUM] Élagage en best effort (let _ =) : une copie restée ne nuit pas, et un échec d'élagage ne doit ni bloquer l'ouverture ni écrire « database update stopped ».
  • [MEDIUM] Pas d'élagage quand aucune copie n'est prise (rien à appliquer) : le critère lie l'élagage au renommage de la nouvelle copie.
  • [HIGH] Logique de démarrage extraite dans src/utils/startupConnection.ts, drapeau d'annulation local à chaque exécution de l'effet (au lieu d'un useRef partagé entre exécutions).

Preuves

  • vitest : 119 fichiers / 2039 tests verts (base 118 / 2026) ; npm run build vert.
  • Rust : cargo test 262 verts + 1 ignoré (base 255) ; cargo check sans avertissement.

Mutations (une à la fois, restauration par git checkout, atterrissage vérifié)

# Mutation Rouges
0 RV4 : fk_violations toujours vide 1 (a_profile_without_category_31_still_catches_up)
1 RV7 : ensemble before vide dans la reprise 1 (idem)
2 Élaguer avant la copie 6
3 catch_up_backup perdu sur erreur 3 (les trois chemins d'erreur)
4 Copie de même nom gardée telle quelle (ancienne règle) 1
5 L'élagage supprime aussi la nouvelle copie 6
6 Pas d'élagage après succès 1
7 keep ignoré dans remove_backups 7
8 RT2 : le Journal perd la ligne des violations préexistantes 1
9 setStartupError(null) retiré au succès 1
10 La ligne d'erreur ne nomme pas la copie 5
11 withoutFolders identité 3
12 Message de repli non nettoyé 1
13 Séparateurs hors chemin gardés 1

14/14 rouges, aucune erreur de compilation.

À regarder

  • Le point de défaillance de test dans profile_db.rs (FAIL_STEP, fail_point).
  • La règle de withoutFolders (LOOKS_LIKE_PATH) : un chemin relatif à un seul séparateur devient a-b.db plutôt que b.db.
  • La mesure vient d'un SSD NVMe ; le risque d'un disque lent ou d'un antivirus est écrit dans l'ADR.

🤖 Generated with Claude Code

Generated autonomously by /autopilot run of 2026-10-12 (milestone `planned-2026-10-10-fiabilite-tests`, worker 4/4, last link of the stack #433 → #434 → #435 → this PR). Suivis de la revue de la PR #394 (#390) consignés dans #395. Base : `issue-389-i18n-mock` (PR #435). ## Ce qui change **1. Copie nommée sur un rattrapage arrêté** - `ProfilePreparation.catch_up_backup: Option<String>` (Rust et TS), rempli dès que la copie existe, même si le rattrapage s'arrête ensuite. `catch_up` devient une enveloppe de test de `catch_up_into(conn, migrations, backup, &mut CatchUp)`, que `prepare_at` appelle : le rapport partiel survit à l'erreur. - La ligne d'erreur du Journal finit par `; copy of the previous state kept as <nom>`. - `withoutFolders` (db.ts) : dans toute ligne d'erreur de `describePreparation` et dans le message de repli de `connectToProfile`, un chemin est réduit à son dernier segment, tout autre séparateur devient `-` (« disk I/O error » → « disk I-O error »). Aucune ligne d'erreur ne contient `/` ni `\` (testé, y compris `<profile folder>/migration-backups/…` laissé par `scrub` et `C:\Users\L'Heureux\…`). - Un test Rust par chemin d'erreur après la copie : `history_write_failed` (déclencheur qui refuse l'historique), `pragma_failed` (point de défaillance `#[cfg(test)]`, voir décisions), `apply_pending` (v18 qui échoue). **2. Épingles RV4, RV7, RT2** : le test du profil sans catégorie 31 porte une violation préexistante (transaction vers une catégorie absente, clé `SET NULL`) : signalée dans `fk_violations`, jamais touchée. Test TS de la ligne `point to a missing row`. **3. Délai de démarrage** - Bug corrigé : la logique de démarrage d'`App.tsx` passe dans `src/utils/startupConnection.ts` (`startProfileConnection`), qui fait `setStartupError(null)` au succès ; 5 tests aux faux minuteurs. - Test `#[ignore]` `a_large_profile_catch_up_duration` : plancher 6 (schéma v0.3.0 + seed), 100 001 transactions (17 Mo, 10 001 drapeaux texte), v7 → v17 avec copie, `foreign_key_check` et réparation. ``` cargo test --release --lib -- --ignored a_large_profile_catch_up_duration --nocapture prepare_at: 100001 transactions, 17.0 MB, floor 6 -> v17, 10001 flags repaired: 306 ms (report: 306 ms) (debug : 422 ms) ``` Machine : ThinkPad T490, Intel Core i5-8365U @ 1.60GHz (max 4,1 GHz), 8 CPU (4 cœurs × 2 fils, `nproc` 8), 16 Go, SSD NVMe, Linux 6.17. - **Règle appliquée : 306 ms < 3 s → `STARTUP_TIMEOUT_MS` reste 10 000.** Mesure, machine, règle et risque résiduel dans les points de vigilance de l'ADR 0021. **4. Rétention des copies** - `backup_to` refait la copie à chaque rattrapage : `.tmp`, synchronisé, renommé par-dessus la copie de même nom (jamais supprimée d'abord). - Après un rattrapage réussi seulement, et après le renommage, `remove_backups(dir, stem, Some(copie))` supprime les autres `<profil>-before-v*.db` / `.db.tmp` du profil (au mieux). Rien n'est supprimé sur erreur, sur copie échouée, ni quand rien n'est à appliquer. `delete_profile_db` passe `None`. - Guide (`docs/guide-utilisateur.md`) et `docs.profiles.tips` FR/EN : une seule copie par profil, la plus récente, refaite à chaque mise à jour, supprimée avec le profil. **5. CHANGELOG** (EN + FR, sous [Unreleased], entrées existantes gardées) : Changed / Modifié (une copie par profil, copie nommée au Journal), Fixed / Corrigé (page d'erreur du démarrage). ## Fichiers - `src-tauri/src/database/profile_db.rs`, `src-tauri/src/commands/profile_commands.rs` - `src/services/db.ts`, `src/services/db.test.ts` - `src/App.tsx`, `src/utils/startupConnection.ts` (nouveau), `src/utils/startupConnection.test.ts` (nouveau) - `docs/adr/0021-migrations-profils-secondaires.md`, `docs/architecture.md`, `docs/guide-utilisateur.md`, `src/i18n/locales/fr.json`, `src/i18n/locales/en.json` - `CHANGELOG.md`, `CHANGELOG.fr.md` ## Décisions - [HIGH] `catch_up_into` + `catch_up` en enveloppe `#[cfg(test)]` : signature d'erreur `String` et tests existants inchangés. - [MEDIUM] `pragma_failed` : aucune façon de faire échouer `PRAGMA foreign_keys = ON` sur un vrai SQLite (pas de feature `hooks`/`limits` de rusqlite). Point de défaillance `fail_point("pragma")` lu dans un `thread_local` compilé sous `#[cfg(test)]` seulement, toujours `Ok` en production. Écartés : activer une feature rusqlite pour un test ; ne pas tester ce chemin. - [MEDIUM] Le critère « aucune ligne d'erreur ne contient `/` ni `\` » est appliqué à toutes les lignes d'erreur de la préparation, pas seulement au nom de la copie : `scrub` (Rust) laisse `<profile folder>/migration-backups/x.tmp`. Écartés : changer le placeholder Rust (épinglé par #390) ; tronquer le message. - [MEDIUM] Élagage en best effort (`let _ =`) : une copie restée ne nuit pas, et un échec d'élagage ne doit ni bloquer l'ouverture ni écrire « database update stopped ». - [MEDIUM] Pas d'élagage quand aucune copie n'est prise (rien à appliquer) : le critère lie l'élagage au renommage de la nouvelle copie. - [HIGH] Logique de démarrage extraite dans `src/utils/startupConnection.ts`, drapeau d'annulation local à chaque exécution de l'effet (au lieu d'un `useRef` partagé entre exécutions). ## Preuves - vitest : **119 fichiers / 2039 tests** verts (base 118 / 2026) ; `npm run build` vert. - Rust : `cargo test` **262 verts + 1 ignoré** (base 255) ; `cargo check` sans avertissement. ### Mutations (une à la fois, restauration par `git checkout`, atterrissage vérifié) | # | Mutation | Rouges | |---|---|---| | 0 | RV4 : `fk_violations` toujours vide | 1 (`a_profile_without_category_31_still_catches_up`) | | 1 | RV7 : ensemble `before` vide dans la reprise | 1 (idem) | | 2 | Élaguer avant la copie | 6 | | 3 | `catch_up_backup` perdu sur erreur | 3 (les trois chemins d'erreur) | | 4 | Copie de même nom gardée telle quelle (ancienne règle) | 1 | | 5 | L'élagage supprime aussi la nouvelle copie | 6 | | 6 | Pas d'élagage après succès | 1 | | 7 | `keep` ignoré dans `remove_backups` | 7 | | 8 | RT2 : le Journal perd la ligne des violations préexistantes | 1 | | 9 | `setStartupError(null)` retiré au succès | 1 | | 10 | La ligne d'erreur ne nomme pas la copie | 5 | | 11 | `withoutFolders` identité | 3 | | 12 | Message de repli non nettoyé | 1 | | 13 | Séparateurs hors chemin gardés | 1 | 14/14 rouges, aucune erreur de compilation. ## À regarder - Le point de défaillance de test dans `profile_db.rs` (`FAIL_STEP`, `fail_point`). - La règle de `withoutFolders` (`LOOKS_LIKE_PATH`) : un chemin relatif à un seul séparateur devient `a-b.db` plutôt que `b.db`. - La mesure vient d'un SSD NVMe ; le risque d'un disque lent ou d'un antivirus est écrit dans l'ADR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 2 commits 2026-10-13 00:26:10 +00:00
The 10 s startup delay of App.tsx sets the error page while the profile
is still opening; a connection that succeeded afterwards set dbReady but
left the error, so the error page stayed. The startup logic moves to
src/utils/startupConnection.ts (startProfileConnection), which clears the
error on success and is tested with fake timers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(profiles): name the copy of a stopped catch-up and keep one copy per profile
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 2m1s
PR Check — Rust / rust (pull_request) Successful in 3m56s
419b20e769
- ProfilePreparation.catch_up_backup carries the file name of the copy as
  soon as it exists, so the Journal error line of a catch-up that stops
  afterwards (history_write_failed, pragma_failed, apply_pending) names it.
- Error lines and the JS fallback message keep only the last segment of a
  path (withoutFolders): no error line contains a slash or a backslash.
- The copy is taken again at every catch-up, written as .tmp, synced and
  renamed over a same-name copy; after a successful catch-up only, the
  other copies of that profile are pruned (remove_backups keep argument).
- Pins: a pre-existing foreign key violation in the profile without
  category 31 (RV4, RV7) and the Journal line that reports it (RT2).
- Ignored test measuring prepare_at on 100 000 transactions at floor 6:
  306 ms in release, so STARTUP_TIMEOUT_MS stays 10 s (ADR 0021).
- Guide, docs.profiles.tips, ADR 0021, architecture and changelogs.

Resolves #395

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus added the
autopilot:pending-human
label 2026-10-13 00:26:18 +00:00
maximus added 1 commit 2026-10-13 00:27:30 +00:00
style(profiles): rewrap the sync comment of backup_to
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 2m1s
PR Check — Rust / rust (pull_request) Successful in 4m1s
a46a94a314
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus force-pushed issue-395-catch-up-followups from a46a94a314 to fbecfffa6e 2026-10-13 00:27:37 +00:00 Compare
Author
Owner

Auto-revue (worker autopilot, non indépendante) — PR #436 (#395)

Verdict : APPROVE

Diff relu en entier contre issue-389-i18n-mock, tête fbecfff.

Critères de #395

  • catch_up_backup (Rust + TS), rempli dès la copie, même sur erreur ; ligne d'erreur du Journal qui le nomme ; un test Rust par chemin (history_write_failed, pragma_failed, apply_pending) ; nom seul, jamais de chemin ; aucune ligne d'erreur avec / ni \ (test TS), message de repli compris.
  • RV4, RV7, RT2 : chacune rougit au moins un test (tableau du corps).
  • Succès tardif : setStartupError(null) au succès, épinglé ; test #[ignore] lancé (306 ms release, 422 ms debug, machine dans le corps) ; règle appliquée (< 3 s → 10 000 inchangé) ; mesure, machine, règle et risque résiduel dans l'ADR 0021.
  • Rétention : copie refaite (.tmp, sync, rename par-dessus) ; élagage seulement après succès et renommage ; rien sur erreur ni sur copie échouée ; mutation « élaguer avant la copie » rouge (6 tests) ; guide FR + tips FR/EN.
  • CHANGELOG EN/FR : Modifié et Corrigé, entrées existantes gardées.

Contrôles

  • Sécurité / Loi 25 : aucun chemin dans les lignes d'erreur ; le point de défaillance FAIL_STEP est compilé sous #[cfg(test)] seulement, fail_point vaut Ok(()) en production.
  • Aucune migration SQL modifiée (checksums épinglés verts) ; i18n : pas de texte d'UI en dur (les lignes du Journal sont en anglais, comme avant) ; docs : ADR 0021, architecture.md, guide.
  • Suites : vitest 119/2039, build vert, cargo test 262 + 1 ignoré, cargo check sans avertissement.

Restes, non bloquants

  • « Jamais supprimer avant » n'est pas épinglé par un test : supprimer la copie finale avant le rename laisserait tous les tests verts (seul un plantage entre les deux le montrerait). Le code ne le fait pas ; le commentaire de backup_to le dit.
  • withoutFolders : un chemin relatif à un seul séparateur devient a-b plutôt que b (pas de dossier utilisateur dans ce cas).
  • La mesure vient d'un SSD NVMe ; un disque lent reste le risque résiduel écrit dans l'ADR.
## Auto-revue (worker autopilot, non indépendante) — PR #436 (#395) **Verdict : APPROVE** Diff relu en entier contre `issue-389-i18n-mock`, tête `fbecfff`. ### Critères de #395 - [x] `catch_up_backup` (Rust + TS), rempli dès la copie, même sur erreur ; ligne d'erreur du Journal qui le nomme ; un test Rust par chemin (`history_write_failed`, `pragma_failed`, `apply_pending`) ; nom seul, jamais de chemin ; aucune ligne d'erreur avec `/` ni `\` (test TS), message de repli compris. - [x] RV4, RV7, RT2 : chacune rougit au moins un test (tableau du corps). - [x] Succès tardif : `setStartupError(null)` au succès, épinglé ; test `#[ignore]` lancé (306 ms release, 422 ms debug, machine dans le corps) ; règle appliquée (< 3 s → 10 000 inchangé) ; mesure, machine, règle et risque résiduel dans l'ADR 0021. - [x] Rétention : copie refaite (`.tmp`, sync, rename par-dessus) ; élagage seulement après succès et renommage ; rien sur erreur ni sur copie échouée ; mutation « élaguer avant la copie » rouge (6 tests) ; guide FR + tips FR/EN. - [x] CHANGELOG EN/FR : Modifié et Corrigé, entrées existantes gardées. ### Contrôles - Sécurité / Loi 25 : aucun chemin dans les lignes d'erreur ; le point de défaillance `FAIL_STEP` est compilé sous `#[cfg(test)]` seulement, `fail_point` vaut `Ok(())` en production. - Aucune migration SQL modifiée (checksums épinglés verts) ; i18n : pas de texte d'UI en dur (les lignes du Journal sont en anglais, comme avant) ; docs : ADR 0021, `architecture.md`, guide. - Suites : vitest 119/2039, build vert, `cargo test` 262 + 1 ignoré, `cargo check` sans avertissement. ### Restes, non bloquants - « Jamais supprimer avant » n'est pas épinglé par un test : supprimer la copie finale avant le `rename` laisserait tous les tests verts (seul un plantage entre les deux le montrerait). Le code ne le fait pas ; le commentaire de `backup_to` le dit. - `withoutFolders` : un chemin relatif à un seul séparateur devient `a-b` plutôt que `b` (pas de dossier utilisateur dans ce cas). - La mesure vient d'un SSD NVMe ; un disque lent reste le risque résiduel écrit dans l'ADR.
Author
Owner

CI lue par l’orchestrateur de l’autopilot (2026-10-12) sur la tête fbecfff :

  • PR Check — Frontend, run 536, vert : Test Files 119 passed (119), Tests 2039 passed (2039).
  • PR Check — Rust, run 537, vert : cache restauré en full match: true, windows-sys pinning: 9 checks, exit 0, test result: ok. 262 passed; 0 failed; 1 ignored. Le test ignoré est la mesure a_large_profile_catch_up_duration.

Point cosmétique pour la revue : dans CHANGELOG.md comme dans CHANGELOG.fr.md, l’entrée Fixed / Corrigé de #395 est suivie d’une ligne vide avant celle de #320. Les deux puces de la section sont donc séparées par un blanc, à retirer.

CI lue par l’orchestrateur de l’autopilot (2026-10-12) sur la tête `fbecfff` : - `PR Check — Frontend`, run 536, vert : `Test Files 119 passed (119)`, `Tests 2039 passed (2039)`. - `PR Check — Rust`, run 537, vert : cache restauré en `full match: true`, `windows-sys pinning: 9 checks, exit 0`, `test result: ok. 262 passed; 0 failed; 1 ignored`. Le test ignoré est la mesure `a_large_profile_catch_up_duration`. Point cosmétique pour la revue : dans `CHANGELOG.md` comme dans `CHANGELOG.fr.md`, l’entrée Fixed / Corrigé de #395 est suivie d’une ligne vide avant celle de #320. Les deux puces de la section sont donc séparées par un blanc, à retirer.
Author
Owner

Revue indépendante (/pr-review) — PR #436 (#395)

Verdict : APPROVE

Le diff a été relu en entier contre issue-389-i18n-mock, tête fbecfff. Le code fait ce que le corps annonce : la copie est nommée sur les trois arrêts qui suivent la copie, une seule copie est gardée par profil, l'élagage n'a lieu qu'après un succès et après le renommage, et la page d'erreur du démarrage se retire au succès. Aucun bloquant.

Exécuté par le relecteur (copie hors dépôt par git archive de fbecfff)

  • npm ci puis vitest : 119 fichiers / 2039 tests verts.
  • cargo test --lib profile_db : 36 verts, 1 ignoré.
  • CI de la tête : /commits/fbecfff/status agrégé en success (Frontend et Rust).
  • Huit mutations du relecteur, une à la fois, restaurées par copie et vérifiées par cmp/diff -q. Toutes rougissent :
# Mutation Rouges
T1 setStartupError(null) retiré au succès 1 / 5
T2 clearTimeout retiré au succès (le délai réécrit l'erreur après l'ouverture) 1 / 5
T3 La ligne d'erreur ne nomme plus la copie 5 / 37
T4 withoutFolders appliqué au seul nom de copie, pas à la ligne 1 / 37
R1 Retour de l'ancienne règle if final_path.exists() { return Ok(name) } 1 / 36
R2 Élagage avant le match, donc aussi sur erreur 3 / 36
R3 catch_up_backup rempli au seul succès 3 / 36
R4 L'élagage oublie les .db.tmp 3 / 36

Lectures vérifiées

  • remove_backups filtre sur le préfixe <stem>-before-v, et les ids sont hexadécimaux (is_profile_db_filename). Un profil dont l'id est le préfixe d'un autre ne peut donc pas élaguer les copies de l'autre.
  • Le thread_local FAIL_STEP est compilé sous #[cfg(test)] seulement, et prepare_at ne lance aucun fil : l'injection reste propre au test qui la pose.
  • App.tsx teste startupError avant dbReady. Remettre l'erreur à null suffit donc à afficher l'application.
  • Les erreurs d'ouverture de prepare_at passent déjà par scrub côté Rust. Côté TypeScript, withoutFolders sert de défense en profondeur.

Suggestions (non bloquantes)

  1. withoutFolders laisse passer le premier mot d'un dossier qui contient une espace. Sonde sur la tête :

    • C:\Users\Jean Dupont\AppData\Roaming\com.simpl.resultat\profile_x.db donne Jean profile_x.db ;
    • /home/marie claire/.config/x.db donne marie x.db.

    Le critère « aucun / ni \ » tient, mais un morceau du nom d'utilisateur reste dans la ligne (Loi 25). Ce n'est pas une régression : avant la PR, le message de repli n'était pas nettoyé du tout, et Rust remplace déjà le dossier des bases par <profile folder>. En revanche, la phrase de l'ADR 0021 §7, « ne garde que le dernier segment de tout chemin », est inexacte dans ce cas. Le dire dans l'ADR et ajouter une épingle, ou traiter le cas en suivi (par exemple, remplacer d'abord les préfixes connus C:\Users\<…>\ et /home/<…>/ jusqu'au séparateur suivant).

  2. Détail d'affichage : 'C:\Users\Bob\x.db' devient x.db'. L'apostrophe ouvrante disparaît avec le segment, la fermante reste.

  3. Fermeture de #395 : le corps de la PR ne porte aucun mot-clé de fermeture, et la timeline de #395 montre ref_action: none pour #436. Les deux commits portent en revanche Resolves #395, donc l'issue se fermera quand ces commits arriveront sur main (merge ff de la pile). C'est cohérent avec le reste de la pile, et le signaler suffit.

Actions faites

  • Ce commentaire posté, puis status:review remplacé par status:approved sur #395.
  • Copie hors dépôt (scratchpad/pr436, cible Cargo scratchpad/ctarget) supprimée en fin de revue.
## Revue indépendante (/pr-review) — PR #436 (#395) **Verdict : APPROVE** Le diff a été relu en entier contre `issue-389-i18n-mock`, tête `fbecfff`. Le code fait ce que le corps annonce : la copie est nommée sur les trois arrêts qui suivent la copie, une seule copie est gardée par profil, l'élagage n'a lieu qu'après un succès et après le renommage, et la page d'erreur du démarrage se retire au succès. Aucun bloquant. ### Exécuté par le relecteur (copie hors dépôt par `git archive` de `fbecfff`) - `npm ci` puis vitest : **119 fichiers / 2039 tests verts**. - `cargo test --lib profile_db` : **36 verts, 1 ignoré**. - CI de la tête : `/commits/fbecfff/status` agrégé en `success` (Frontend et Rust). - Huit mutations du relecteur, une à la fois, restaurées par copie et vérifiées par `cmp`/`diff -q`. Toutes rougissent : | # | Mutation | Rouges | |---|---|---| | T1 | `setStartupError(null)` retiré au succès | 1 / 5 | | T2 | `clearTimeout` retiré au succès (le délai réécrit l'erreur après l'ouverture) | 1 / 5 | | T3 | La ligne d'erreur ne nomme plus la copie | 5 / 37 | | T4 | `withoutFolders` appliqué au seul nom de copie, pas à la ligne | 1 / 37 | | R1 | Retour de l'ancienne règle `if final_path.exists() { return Ok(name) }` | 1 / 36 | | R2 | Élagage avant le `match`, donc aussi sur erreur | 3 / 36 | | R3 | `catch_up_backup` rempli au seul succès | 3 / 36 | | R4 | L'élagage oublie les `.db.tmp` | 3 / 36 | ### Lectures vérifiées - `remove_backups` filtre sur le préfixe `<stem>-before-v`, et les ids sont hexadécimaux (`is_profile_db_filename`). Un profil dont l'id est le préfixe d'un autre ne peut donc pas élaguer les copies de l'autre. - Le `thread_local` `FAIL_STEP` est compilé sous `#[cfg(test)]` seulement, et `prepare_at` ne lance aucun fil : l'injection reste propre au test qui la pose. - `App.tsx` teste `startupError` avant `dbReady`. Remettre l'erreur à `null` suffit donc à afficher l'application. - Les erreurs d'ouverture de `prepare_at` passent déjà par `scrub` côté Rust. Côté TypeScript, `withoutFolders` sert de défense en profondeur. ### Suggestions (non bloquantes) 1. **`withoutFolders` laisse passer le premier mot d'un dossier qui contient une espace.** Sonde sur la tête : - `C:\Users\Jean Dupont\AppData\Roaming\com.simpl.resultat\profile_x.db` donne `Jean profile_x.db` ; - `/home/marie claire/.config/x.db` donne `marie x.db`. Le critère « aucun `/` ni `\` » tient, mais un morceau du nom d'utilisateur reste dans la ligne (Loi 25). Ce n'est pas une régression : avant la PR, le message de repli n'était pas nettoyé du tout, et Rust remplace déjà le dossier des bases par `<profile folder>`. En revanche, la phrase de l'ADR 0021 §7, « ne garde que le dernier segment de tout chemin », est inexacte dans ce cas. Le dire dans l'ADR et ajouter une épingle, ou traiter le cas en suivi (par exemple, remplacer d'abord les préfixes connus `C:\Users\<…>\` et `/home/<…>/` jusqu'au séparateur suivant). 2. Détail d'affichage : `'C:\Users\Bob\x.db'` devient `x.db'`. L'apostrophe ouvrante disparaît avec le segment, la fermante reste. 3. **Fermeture de #395** : le corps de la PR ne porte aucun mot-clé de fermeture, et la timeline de #395 montre `ref_action: none` pour #436. Les deux commits portent en revanche `Resolves #395`, donc l'issue se fermera quand ces commits arriveront sur `main` (merge ff de la pile). C'est cohérent avec le reste de la pile, et le signaler suffit. ### Actions faites - Ce commentaire posté, puis `status:review` remplacé par `status:approved` sur #395. - Copie hors dépôt (`scratchpad/pr436`, cible Cargo `scratchpad/ctarget`) supprimée en fin de revue.
maximus added 4 commits 2026-10-13 23:58:13 +00:00
- close() on a getDb() handle runs as a "connection close" section and
  checks its generation: a handle kept from a previous profile rejects
  with DbConnectionClosedError instead of closing the current profile.
- closeUnlocked never rejects: the state is cleared and the generation
  bumped before close(), a failing close of the old pool is logged with
  logWarn, connectToProfile still opens the next profile and closeDb()
  resolves without a connection.
- The blocking alert names a getDb() call in a callback as a possible
  cause only: a long restore also waits 15 s.
- The generation check of the transaction handle is documented as a
  defence, not reachable today.
- Tests: the "behind the queue" alert (pins the reset of lockHolder),
  close() on a stale and on the current handle, a failing close during a
  switch and in closeDb().

Refs #408

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- useOnboardingChecklist.wiring.test.ts: a data event, then a route
  change 100 ms later, still gets its deferred re-read (two LOAD_START).
  Pins the follower's deps: with `pathname` among them, the cleanup drops
  the pending timer and the test goes red (mutation M1 of the review). The
  comment that claimed "one subscription only" now points to this test.
- hookHarness.test.ts: a dependency array that shrinks, or grows by
  `undefined`, recomputes the memo (mutation M8, length branch of
  depsChanged).
- NOT SIMULATED, in hookHarness.ts and docs/architecture.md: the dispatch
  of a useReducer keeps the reducer of the first render, where React
  applies the latest one.

Refs #409

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- renderCategoryLabel.test.ts passes markedT itself, its assertions in
  the marked form (`t(balance.category.cash[defaultValue=cash])`): the
  second fake `t` of the repository is gone.
- i18n-mock-guard: a fake `t` is also caught as a binding (`t = (`,
  `t = vi.fn(`) or as a `t: vi.fn(` property, and a file that mocks
  react-i18next must call `i18nMock(`, comments dropped before the
  search. Each new rule has its own case in the guard's tests; the
  binding rule found renderCategoryLabel.test.ts before its migration.
- docs/architecture.md: the "only fake t" sentence is scoped to test
  files and says the guard only refuses the forms it recognises.

Refs #389

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(profiles): follow-ups of the review of PR #436
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 2m8s
PR Check — Rust / rust (pull_request) Successful in 3m54s
86eff9b7a3
- withoutFolders no longer keeps the first word of a folder that holds
  a blank (`C:\Users\Jean Dupont\...\profile_x.db` gave
  `Jean profile_x.db`): a path starts at a recognised prefix (drive,
  `/`, `~/`, `./`, UNC, `<profile folder>`) and runs to its last
  separator, blanks included, until a line break, a double quote, a
  backtick, `;`, `|`, a `,` or `:` before a blank, a closing parenthesis
  not followed by a separator, or a blank before another path. When
  nothing ends it, the text up to the last separator of the line is
  dropped rather than a folder let through. A quote before the path is
  not part of it, so `'C:\Users\Bob\x.db'` gives `'x.db'`. Relative runs
  with no recognised prefix keep the previous fallback. No error line
  contains a `/` nor a `\`, as before.
- ADR 0021 section 7 describes that rule and its limit.
- CHANGELOG.md, CHANGELOG.fr.md: no blank line between the two Fixed /
  Corrigé bullets.

Refs #395

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

Suivis des quatre revues, appliqués en tête de pile

Quatre commits ajoutés sur issue-395-catch-up-followups après fbecfff, un par PR de la pile. Les têtes revues des PR #433 (b161d54), #434 (79ba1ed) et #435 (2bd347b) n'ont pas bougé. Ces commits ont été ajoutés après les revues et ne sont pas relus.

Commit Revue Ce qu'il règle
74fae14 PR #433, commentaire 23505 S1 : test de l'alerte « behind the queue », qui épingle la remise à null de lockHolder. S3 : close() sur un proxy de getDb() passe par une section « connection close » et vérifie sa génération ; un ancien proxy rejette avec DbConnectionClosedError au lieu de fermer le profil courant. S4 : closeUnlocked ne rejette plus. L'état est vidé et la génération incrémentée avant le close(), et un échec part au Journal (logWarn). connectToProfile ouvre quand même le profil suivant, et closeDb() se résout sans connexion : switchProfile appelle closeDb() avant connectToProfile, donc un rejet aurait laissé l'app sans connexion. S5 : l'alerte cite le getDb() dans un rappel comme « possible cause ». S2 : le contrôle de génération du handle de transaction est commenté comme une défense, inatteignable aujourd'hui. docs/architecture.md est mis à jour.
a1c31a1 PR #434, commentaire 23495 S1 (M1) : un test de comportement, dans lequel un événement de données est suivi d'un changement de route à +100 ms, et la relecture différée a quand même lieu (deux LOAD_START). Le commentaire « one subscription only » renvoie à ce test. S2 (M8) : un tableau de dépendances qui raccourcit, ou s'allonge d'un undefined, recalcule le memo. S3 : le dispatch d'un useReducer garde le reducer du premier rendu, ajouté à « NOT SIMULATED » dans hookHarness.ts et dans docs/architecture.md.
ac3a981 PR #435, commentaire 23499 S1 : renderCategoryLabel.test.ts passe markedT, et ses assertions sont sous forme marquée. S2 : la garde reconnaît aussi t = (, t = vi.fn( et t: vi.fn(, et exige un appel i18nMock(, commentaires retirés avant la recherche. Avant la migration, la règle de liaison a trouvé renderCategoryLabel.test.ts:13 sur l'arbre réel. La phrase « seul faux t » de docs/architecture.md est restreinte aux fichiers de test, et la description de la garde est mise à jour.
86eff9b PR #436, commentaire 23511 S1 : withoutFolders réduit un chemin dont les dossiers contiennent des espaces à son dernier segment : C:\Users\Jean Dupont\…\profile_x.db donne profile_x.db, /home/marie claire/.config/x.db donne x.db. Un chemin part d'un début reconnu (lecteur, /, ~/, ./, \\, <profile folder>) et court jusqu'à son dernier séparateur. Il s'arrête à un saut de ligne, à ", à une apostrophe inverse, à ;, à |, à , ou : suivis d'un blanc, à une ) non suivie d'un séparateur, ou à un blanc suivi d'un autre chemin. Règle prudente : quand rien n'arrête le chemin, le texte est retiré jusqu'au dernier séparateur de la ligne (/a/x.db then I/O donne O), plutôt que de laisser passer un dossier. S2 : 'C:\Users\Bob\x.db' donne 'x.db'. Limite : un chemin relatif sans début reconnu garde l'ancien repli, qui remplace les séparateurs par -. Aucune ligne d'erreur ne contient / ni \, comme avant. La phrase de l'ADR 0021 §7 est réécrite. Dans les deux CHANGELOG, la ligne vide entre les deux puces Fixed / Corrigé est retirée.

Mutations (une à la fois, fichier restauré et vérifié après chaque passe)

# Mutation Rouges
A1 lockHolder jamais remis à null 1 / 42
A2 close() d'un proxy de getDb() sans contrôle de génération 1 / 42
A3a closeUnlocked relance l'échec du close() 2 / 42
A3b état vidé seulement après un close() réussi 1 / 42
A4 indice de l'alerte rendu comme une cause certaine 1 / 42
M1 pathname ajouté aux dépendances du suiveur 1 / 11
M8 branche « longueur différente » de depsChanged retirée 1 / 29
C1a renderCategoryLabel saute t 6 / 8
C1b renderCategoryLabel ne passe plus defaultValue 6 / 8
C2a ancienne règle t: ( seule 1 / 5
C2b t: vi.fn( non reconnu 1 / 5
C2c t = ( non reconnu 1 / 5
C2d frontière de mot retirée (faux positifs) 2 / 5
C2e le nom i18nMock suffit (ancienne règle) 1 / 5
C2f commentaires non retirés 1 / 5
D1 un blanc termine les dossiers (comportement de fbecfff) 12 / 54
D2 pas d'arrêt au chemin suivant 2 / 54
D3 pas d'arrêt à , ou : 1 / 54
D5 pas d'arrêt à ) 1 / 54
D6 toute ) arrête (Program Files (x86)) 1 / 54
D7 arrêts réduits aux sauts de ligne 4 / 59
D7b " retiré des arrêts 1 / 59
D7c saut de ligne retiré des arrêts 1 / 59
D8 un chemin peut commencer n'importe où (I/O) 3 / 54
D9 guillemets non sautés avant le chemin suivant 1 / 54

Une mutation a survécu : retirer le traitement de l'apostrophe fermante. L'apostrophe ouvrante ne fait plus partie du chemin, donc ce code était redondant, et je l'ai retiré plutôt que de garder une ligne qu'aucun test n'épingle. Le cas 'C:\Users\Bob\x.db' → 'x.db' reste testé. Le \r des arrêts n'a pas de test propre.

Validation sur 86eff9b (worktree, npm ci propre)

  • npm run build : vert.
  • npm test : 119 fichiers, 2065 tests verts (2039 sur fbecfff).
  • Rust non touché : pas de cargo test ni de cargo check.
  • Push en avance rapide, sans force.

Non traité, comme demandé : le badge best-effort de PriceFetchControl.tsx (point 4 de la revue de la PR #435).

## Suivis des quatre revues, appliqués en tête de pile Quatre commits ajoutés sur `issue-395-catch-up-followups` après `fbecfff`, un par PR de la pile. Les têtes revues des PR #433 (`b161d54`), #434 (`79ba1ed`) et #435 (`2bd347b`) n'ont pas bougé. **Ces commits ont été ajoutés après les revues et ne sont pas relus.** | Commit | Revue | Ce qu'il règle | |---|---|---| | `74fae14` | PR #433, commentaire 23505 | S1 : test de l'alerte « behind the queue », qui épingle la remise à `null` de `lockHolder`. S3 : `close()` sur un proxy de `getDb()` passe par une section « connection close » et vérifie sa génération ; un ancien proxy rejette avec `DbConnectionClosedError` au lieu de fermer le profil courant. S4 : `closeUnlocked` ne rejette plus. L'état est vidé et la génération incrémentée avant le `close()`, et un échec part au Journal (`logWarn`). `connectToProfile` ouvre quand même le profil suivant, et `closeDb()` se résout sans connexion : `switchProfile` appelle `closeDb()` avant `connectToProfile`, donc un rejet aurait laissé l'app sans connexion. S5 : l'alerte cite le `getDb()` dans un rappel comme « possible cause ». S2 : le contrôle de génération du handle de transaction est commenté comme une défense, inatteignable aujourd'hui. `docs/architecture.md` est mis à jour. | | `a1c31a1` | PR #434, commentaire 23495 | S1 (M1) : un test de comportement, dans lequel un événement de données est suivi d'un changement de route à +100 ms, et la relecture différée a quand même lieu (deux `LOAD_START`). Le commentaire « one subscription only » renvoie à ce test. S2 (M8) : un tableau de dépendances qui raccourcit, ou s'allonge d'un `undefined`, recalcule le memo. S3 : le `dispatch` d'un `useReducer` garde le reducer du premier rendu, ajouté à « NOT SIMULATED » dans `hookHarness.ts` et dans `docs/architecture.md`. | | `ac3a981` | PR #435, commentaire 23499 | S1 : `renderCategoryLabel.test.ts` passe `markedT`, et ses assertions sont sous forme marquée. S2 : la garde reconnaît aussi `t = (`, `t = vi.fn(` et `t: vi.fn(`, et exige un appel `i18nMock(`, commentaires retirés avant la recherche. Avant la migration, la règle de liaison a trouvé `renderCategoryLabel.test.ts:13` sur l'arbre réel. La phrase « seul faux `t` » de `docs/architecture.md` est restreinte aux fichiers de test, et la description de la garde est mise à jour. | | `86eff9b` | PR #436, commentaire 23511 | S1 : `withoutFolders` réduit un chemin dont les dossiers contiennent des espaces à son dernier segment : `C:\Users\Jean Dupont\…\profile_x.db` donne `profile_x.db`, `/home/marie claire/.config/x.db` donne `x.db`. Un chemin part d'un début reconnu (lecteur, `/`, `~/`, `./`, `\\`, `<profile folder>`) et court jusqu'à son dernier séparateur. Il s'arrête à un saut de ligne, à `"`, à une apostrophe inverse, à `;`, à `\|`, à `,` ou `:` suivis d'un blanc, à une `)` non suivie d'un séparateur, ou à un blanc suivi d'un autre chemin. **Règle prudente** : quand rien n'arrête le chemin, le texte est retiré jusqu'au dernier séparateur de la ligne (`/a/x.db then I/O` donne `O`), plutôt que de laisser passer un dossier. S2 : `'C:\Users\Bob\x.db'` donne `'x.db'`. Limite : un chemin relatif sans début reconnu garde l'ancien repli, qui remplace les séparateurs par `-`. Aucune ligne d'erreur ne contient `/` ni `\`, comme avant. La phrase de l'ADR 0021 §7 est réécrite. Dans les deux CHANGELOG, la ligne vide entre les deux puces Fixed / Corrigé est retirée. | ### Mutations (une à la fois, fichier restauré et vérifié après chaque passe) | # | Mutation | Rouges | |---|---|---| | A1 | `lockHolder` jamais remis à `null` | 1 / 42 | | A2 | `close()` d'un proxy de `getDb()` sans contrôle de génération | 1 / 42 | | A3a | `closeUnlocked` relance l'échec du `close()` | 2 / 42 | | A3b | état vidé seulement après un `close()` réussi | 1 / 42 | | A4 | indice de l'alerte rendu comme une cause certaine | 1 / 42 | | M1 | `pathname` ajouté aux dépendances du suiveur | 1 / 11 | | M8 | branche « longueur différente » de `depsChanged` retirée | 1 / 29 | | C1a | `renderCategoryLabel` saute `t` | 6 / 8 | | C1b | `renderCategoryLabel` ne passe plus `defaultValue` | 6 / 8 | | C2a | ancienne règle `t: (` seule | 1 / 5 | | C2b | `t: vi.fn(` non reconnu | 1 / 5 | | C2c | `t = (` non reconnu | 1 / 5 | | C2d | frontière de mot retirée (faux positifs) | 2 / 5 | | C2e | le nom `i18nMock` suffit (ancienne règle) | 1 / 5 | | C2f | commentaires non retirés | 1 / 5 | | D1 | un blanc termine les dossiers (comportement de `fbecfff`) | 12 / 54 | | D2 | pas d'arrêt au chemin suivant | 2 / 54 | | D3 | pas d'arrêt à `, ` ou `: ` | 1 / 54 | | D5 | pas d'arrêt à `)` | 1 / 54 | | D6 | toute `)` arrête (`Program Files (x86)`) | 1 / 54 | | D7 | arrêts réduits aux sauts de ligne | 4 / 59 | | D7b | `"` retiré des arrêts | 1 / 59 | | D7c | saut de ligne retiré des arrêts | 1 / 59 | | D8 | un chemin peut commencer n'importe où (`I/O`) | 3 / 54 | | D9 | guillemets non sautés avant le chemin suivant | 1 / 54 | Une mutation a survécu : retirer le traitement de l'apostrophe fermante. L'apostrophe ouvrante ne fait plus partie du chemin, donc ce code était redondant, et je l'ai retiré plutôt que de garder une ligne qu'aucun test n'épingle. Le cas `'C:\Users\Bob\x.db'` → `'x.db'` reste testé. Le `\r` des arrêts n'a pas de test propre. ### Validation sur `86eff9b` (worktree, `npm ci` propre) - `npm run build` : vert. - `npm test` : **119 fichiers, 2065 tests verts** (2039 sur `fbecfff`). - Rust non touché : pas de `cargo test` ni de `cargo check`. - Push en avance rapide, sans force. Non traité, comme demandé : le badge `best-effort` de `PriceFetchControl.tsx` (point 4 de la revue de la PR #435).
maximus added 1 commit 2026-10-14 00:01:15 +00:00
fix(balance): translate the best-effort badge of the price fetch button
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m59s
PR Check — Rust / rust (pull_request) Successful in 3m58s
c17bf5dcb4
The small badge next to "Fetch price" on a stock rendered the literal
"best-effort" without going through t, so French users saw English.
It now reads balance.priceFetching.bestEffortBadge ("non garanti" in
French). Raised by the review of PR #435 (suggestion 4).

Refs #389

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

Un cinquième commit s’ajoute aux suivis du commentaire 23535 : c17bf5d, « fix(balance): translate the best-effort badge of the price fetch button » (Refs #389). C’est la suggestion 4 de la revue de #435, que j’avais écartée à tort (voir la correction sur #435).

Le badge passe par t("balance.priceFetching.bestEffortBadge"), avec une entrée Fixed / Corrigé dans les deux CHANGELOG. La mutation qui remet le littéral fait rougir 1 test. Le worktree donne build vert et vitest 119 passed (119), 2065 passed (2065).

Tête de pile : c17bf5d. Comme les quatre autres, ce commit n’est pas relu.

Un cinquième commit s’ajoute aux suivis du commentaire 23535 : `c17bf5d`, « fix(balance): translate the best-effort badge of the price fetch button » (`Refs #389`). C’est la suggestion 4 de la revue de #435, que j’avais écartée à tort (voir la correction sur #435). Le badge passe par `t("balance.priceFetching.bestEffortBadge")`, avec une entrée Fixed / Corrigé dans les deux CHANGELOG. La mutation qui remet le littéral fait rougir 1 test. Le worktree donne build vert et vitest `119 passed (119)`, `2065 passed (2065)`. Tête de pile : `c17bf5d`. Comme les quatre autres, ce commit n’est pas relu.
maximus changed target branch from issue-389-i18n-mock to main 2026-10-14 00:28:41 +00:00
maximus merged commit c17bf5dcb4 into main 2026-10-14 00:28:51 +00:00
Author
Owner

Pile mergée le 2026-10-13 en avance rapide par l’API, du bas vers le haut, chaque enfant reciblé sur main juste avant son merge épinglé : #433 (b161d54) → #434 (79ba1ed) → #435 (2bd347b) → #436 (c17bf5d).

Tip validé avant le merge dans un worktree détaché, après npm ci propre : la sonde rouge sort en rc=1, tsc et npm run build passent, vitest donne 119 passed (119) et 2065 passed (2065), cargo test donne 262 réussis et 1 ignoré. CI de la tête : runs 541 et 542.

Les commits 74fae14, a1c31a1, ac3a981, 86eff9b et c17bf5d ont été ajoutés après les revues et ne sont pas relus. Leur relecture est le point 1 de #437.

Pile mergée le 2026-10-13 en avance rapide par l’API, du bas vers le haut, chaque enfant reciblé sur `main` juste avant son merge épinglé : #433 (`b161d54`) → #434 (`79ba1ed`) → #435 (`2bd347b`) → #436 (`c17bf5d`). Tip validé avant le merge dans un worktree détaché, après `npm ci` propre : la sonde rouge sort en rc=1, `tsc` et `npm run build` passent, vitest donne `119 passed (119)` et `2065 passed (2065)`, `cargo test` donne 262 réussis et 1 ignoré. CI de la tête : runs 541 et 542. Les commits `74fae14`, `a1c31a1`, `ac3a981`, `86eff9b` et `c17bf5d` ont été ajoutés après les revues et ne sont pas relus. Leur relecture est le point 1 de #437.
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#436
No description provided.