--- issueRef: "#303" version: 6 updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"} updatedAt: 1789021152339 --- # Carnet #303 - Restaurer les projets ouverts au redémarrage ## Demande - Date: 2026-09-10. - À la fermeture puis relance d'IdeA, les projets qui étaient ouverts doivent être réouverts automatiquement. - L'ordre des projets ouverts doit être conservé. - Après livraison et QA, rebuild AppImage demandé par l'utilisateur. ## Contraintes produit initiales - Comportement plug-and-play et silencieux par défaut. - Reprise automatique cohérente avec la priorité projet: persistance et reprise. - Ne pas bloquer le démarrage si un projet n'existe plus ou n'est plus accessible. - Éviter une UI verbeuse sauf si UX décide qu'un état discret est nécessaire. ## Cadrage UX - Comportement par défaut, sans préférence UI pour cette première version. - À chaque lancement, restaurer automatiquement les projets ouverts lors de la dernière fermeture normale. - Reprendre strictement l'ordre visible au moment de la fermeture, sans tri par nom, activité ou date. - Les projets accessibles conservent leur position relative. - Projet supprimé/inaccessible: poursuivre le démarrage et ouvrir les autres projets. Conserver temporairement son emplacement dans l'état mémorisé pour ne pas casser l'ordre si l'indisponibilité est passagère. - Retour utilisateur: silencieux si tout est restauré. En cas d'échec uniquement, notification non bloquante: - `1 projet n’a pas pu être rouvert` ou `{n} projets n’ont pas pu être rouverts`. - actions: `Voir les détails`, `Réessayer`. - détail par projet: nom, chemin, cause compréhensible, action `Retirer de la reprise`. - Fermer explicitement un projet pendant l'utilisation le retire de la prochaine reprise. ## Critères UX QA - Après relance, tous les projets précédemment ouverts réapparaissent dans le même ordre. - Aucun message ni mouvement visuel parasite quand la reprise réussit. - Fermer explicitement un projet empêche sa réouverture au lancement suivant. - Un projet manquant/inaccessible/volume indisponible ne bloque ni IdeA ni les autres restaurations. - L'échec est signalé une seule fois par lancement, pluriel correct et actions fonctionnelles. - `Réessayer` restaure le projet à sa position mémorisée s'il redevient accessible. - `Retirer de la reprise` empêche les avertissements aux lancements suivants. - Premier lancement sans état mémorisé: comportement actuel inchangé. ## Cadrage Architect Source de vérité: - Créer une persistance machine-locale backend-owned sous l'app-data IdeA: `/open-projects.json`. - Distincte du registre `projects.json`, qui liste les projets connus mais pas l'état d'ouverture. - `workspace.json` ne doit pas être recyclé pour #303: il modélise des layouts/tabs historiques et n'est pas alimenté par `useProjects`. Contrat recommandé: ```json { "version": 1, "entries": [ { "projectId": "...", "name": "UnitySoundTool", "root": "/path/to/project", "position": 0, "lastFailure": null } ], "updatedAt": 1789017507046 } ``` Événements/commandes: - Ouverture explicite réussie: ajouter si absent, en fin de liste, ou laisser à sa position si c'était un tombstone/inaccessible restauré. - Création de projet réussie: même comportement qu'ouverture. - Fermeture explicite d'un projet: retirer l'entrée de `open-projects.json`. - Réordonnancement futur: prévoir `save_open_project_restore_order(orderedProjectIds)` même si l'UI ne sait pas encore réordonner. - Ne pas dépendre seulement du shutdown: côté Rust, l'ordre de `open_project_ids()` via `HashMap` n'est pas fiable. Contrats backend: - Use cases: `ReadOpenProjectRestoreState`, `SaveOpenProjectRestoreOrder`, `RecordProjectOpenedForRestore`, `RecordProjectClosedForRestore`, `RestoreOpenProjects`, `RetryOpenProjectRestore`, `RemoveProjectFromRestore`. - Port dédié: `OpenProjectRestoreStore { load() -> OpenProjectRestoreState, save(state) }`. - DTO `RestoreOpenProjectsOutputDto { opened: ProjectDto[], failures: RestoreOpenProjectFailureDto[] }`. - Failure DTO: `projectId`, `name`, `root`, `position`, `code: "notFound" | "inaccessible" | "store" | "invalid"`, `message`. - Requests: `RemoveOpenProjectRestoreEntryRequestDto { projectId }`, `RetryOpenProjectRestoreRequestDto { projectId?: string }`. Stratégie inaccessible: - Pendant `RestoreOpenProjects`, charger les entrées dans l'ordre, résoudre via `projects.json`, prévalider le root avec `FileSystem`, puis appeler la même logique d'ouverture que `open_project_for_adapter`. - Projet absent du registre: failure `notFound`, entrée conservée temporairement. - Root manquant/non lisible: failure `inaccessible`, entrée conservée à sa position. - Projet restauré: mêmes side effects que l'ouverture normale: layouts/live-state/background tasks, watcher, MCP server, run-dir repair. - `Réessayer`: relancer la restauration sur les entrées en échec, réinsérer les succès à leur position mémorisée. - `Retirer de la reprise`: supprimer l'entrée persistée et empêcher les warnings futurs. Contrats frontend: - `ProjectGateway` gagne `restoreOpenProjects`, `retryOpenProjectRestore`, `removeProjectFromRestore`, optionnellement `saveOpenProjectRestoreOrder`. - `useProjects` initialise: `listProjects()`, `restoreOpenProjects()`, remplir `openTabs` avec `opened` dans l'ordre retourné, notifier seulement si `failures.length > 0`. - Notification: `Voir les détails`, `Réessayer`, `Retirer de la reprise`. Fichiers probables: - Backend: `crates/domain/src/ports.rs`, `crates/domain/src/project_restore.rs` ou équivalent, `crates/application/src/project/restore.rs`, `crates/application/src/project/open.rs`, `crates/application/src/project/close.rs`, `crates/infrastructure/src/store/open_project_restore.rs`, `crates/app-tauri/src/commands.rs`, `crates/backend/src/lib.rs`, `crates/web-server/src/lib.rs`. - Frontend: `frontend/src/ports/index.ts`, `frontend/src/adapters/project.ts`, `frontend/src/adapters/http/requestResponseGateways.ts`, `frontend/src/features/projects/useProjects.ts`, `frontend/src/features/projects/ProjectsView.tsx`. Répartition: - DevBackend: modèle, store, use cases, commandes Tauri/HTTP, side effects d'ouverture réutilisables, tests Rust. - DevFrontend: init silencieuse, remplissage `openTabs`, notification échec, détails/retry/remove, tests React/adapters. Critères QA techniques: - Premier lancement sans `open-projects.json`: aucun onglet restauré, aucune notification. - Ouvrir A puis B, quitter, relancer: onglets A/B dans cet ordre. - Fermer B explicitement, quitter, relancer: seul A revient. - Root de B temporairement inaccessible: A revient, B reste mémorisé à sa position, notification unique. - Après restauration du root, `Réessayer` ouvre B à sa position mémorisée. - `Retirer de la reprise` supprime B du fichier et du warning futur. - Watchers/MCP servers sont armés pour les projets restaurés. - Tests Tauri + HTTP si le gateway web expose les mêmes commandes. - Après validation: rebuild AppImage demandé. ## Cycle - UX: fait. - Architect: fait. - Git: branche locale. - DevBackend/DevFrontend selon cadrage. - QA: tests réels + AppImage rebuild. ## Implémentation DevBackend — 2026-09-10 ### Livré - Ajout du modèle versionné `OpenProjectRestoreState` et du port dédié `OpenProjectRestoreStore`. - Ajout du store filesystem `/open-projects.json`; un fichier absent retourne un état vide sans créer de fichier au premier restore. - Ajout des sept use cases cadrés: lecture, record opened/closed, sauvegarde d'ordre, restore, retry et remove. - Sérialisation des mutations par un mutex applicatif partagé afin d'éviter les lost updates entre commandes concurrentes. - Conservation des tombstones à leur position avec `lastFailure`; codes stables `notFound`, `inaccessible`, `store`, `invalid`. - Prévalidation d'un root par metadata puis listing du répertoire: une entrée invalide n'interrompt pas les autres restaurations. - Centralisation de l'activation dans `BackendCore::activate_project`: reconciliation layouts, live-state, background tasks, watcher/orchestrator + serveur MCP, puis réparation des run dirs Claude. - Ouverture Tauri et HTTP, restore et retry utilisent cette même activation. La création enregistre aussi le projet puis l'active. La fermeture explicite retire l'entrée après succès du use case de fermeture. - Exposition Tauri et HTTP des commandes `read_open_project_restore_state`, `restore_open_projects`, `retry_open_project_restore`, `remove_project_from_restore`, `save_open_project_restore_order`. - Aucun fichier frontend modifié. ### Contrat frontend final - `restore_open_projects` sans requête -> `{ opened: ProjectDto[], failures: RestoreOpenProjectFailureDto[] }`. - `retry_open_project_restore` avec `{ projectId?: string }` (requête omissible côté HTTP) -> même output. - `remove_project_from_restore` avec `{ projectId: string }` -> `OpenProjectRestoreStateDto`. - `save_open_project_restore_order` avec `{ orderedProjectIds: string[] }` -> `OpenProjectRestoreStateDto`; les IDs omis restent après les IDs fournis dans leur ordre antérieur, doublons/IDs inconnus refusés. - `read_open_project_restore_state` sans requête -> `{ version, entries, updatedAt }`; chaque entrée expose `{ projectId, name, root, position, lastFailure }` et `lastFailure` vaut explicitement `null` sans échec. - Une failure expose `{ projectId, name, root, position, code, message }` avec `code` parmi `notFound | inaccessible | store | invalid`. - DevFrontend doit encore appeler `restore_open_projects` à l'initialisation, reconstruire `openTabs` dans l'ordre de `opened`, insérer un retry réussi à la position mémorisée, et porter notification/détails/actions UX. ### Fichiers modifiés - `crates/domain/src/project_restore.rs` - `crates/domain/src/lib.rs` - `crates/domain/src/ports.rs` - `crates/application/src/project/restore.rs` - `crates/application/src/project/mod.rs` - `crates/application/src/lib.rs` - `crates/infrastructure/src/store/open_project_restore.rs` - `crates/infrastructure/src/store/mod.rs` - `crates/infrastructure/src/lib.rs` - `crates/backend/src/dto.rs` - `crates/backend/src/lib.rs` - `crates/app-tauri/src/commands.rs` - `crates/app-tauri/src/lib.rs` - `crates/web-server/src/lib.rs` ### Tests ajoutés - Backend composition: absence initiale, ordre A/B et réordonnancement, fermeture explicite, root inaccessible non bloquant, retry ciblé, entrée absente du registre (`notFound`), remove et armement du watcher pour les succès. - HTTP: ouverture enregistrée, restore et remove, avec formes DTO identiques au transport Tauri. ### Commandes exécutées - `cargo fmt --all` - `cargo check -p application -p infrastructure` — vert. - `cargo check -p backend -p app-tauri -p web-server` — vert. - `cargo test -p backend open_project_restore_tests -- --nocapture` — vert, 1 test. - `cargo test -p web-server authorized_open_project_restore_commands_share_the_tauri_contract -- --nocapture` — vert, 1 test. - `cargo test -p domain -p application -p infrastructure -p backend` — partiellement vert: application atteint 180 unit tests verts puis 2 échecs hors périmètre dans `tests/layout_usecases.rs` (`plugin package` introuvable); backend 93 passés / 7 ignorés; domain vert; infrastructure 377 passés / 1 échec de timing existant sur la cancellation de process. - `cargo test -p infrastructure session::process::tests::run_turn_cancellable_kills_current_child_without_waiting_for_natural_exit -- --nocapture` — échec isolé après 10 s, hors fichiers #303. - `cargo test -p domain --quiet` — vert, 311 tests. - `cargo test -p backend open_project_restore_tests --quiet` — vert, 1 test ciblé. - `cargo test -p web-server authorized_open_project_restore_commands_share_the_tauri_contract --quiet` — vert, 1 test ciblé. - `git diff --check` — vert. ### Gaps / reprise - DevFrontend requis pour l'intégration au démarrage et les surfaces d'échec UX. - QA doit revalider les deux échecs application liés aux fixtures plugin et le test infrastructure temporel dans son environnement; aucun ne touche les fichiers ou chemins #303. - Aucun arbitrage Architect supplémentaire identifié dans le lot backend. - Pas de commit effectué. ## Implémentation DevFrontend — 2026-09-10 ### Livré - Ajout des DTO frontend de reprise et extension de `ProjectGateway` avec les cinq commandes backend (`restore`, `retry`, `remove`, `save order`, `read state`). - Adapters Tauri et HTTP alignés sur les noms de commandes et enveloppes `request` du backend. - Initialisation de `useProjects` par lecture du registre et restauration automatique; les succès reconstruisent les onglets dans l'ordre renvoyé, sans notification quand il n'y a aucun échec. - Protection contre une ouverture explicite concurrente à l'initialisation: les onglets déjà ouverts sont conservés et ajoutés après l'ordre restauré, sans doublon. - Notification non bloquante unique au lancement avec singulier/pluriel exact, fermeture, `Voir les détails` et `Réessayer`. - Détail accessible par projet avec nom, chemin, cause française dérivée du code stable, et `Retirer de la reprise`. - Un retry réussi est réinséré à la `position` mémorisée de l'échec; les échecs restants sont réconciliés sans recréer la notification après fermeture volontaire. - La fermeture explicite attend désormais le succès de `closeProject` avant de retirer l'onglet localement: un échec de persistance conserve donc l'onglet et affiche l'erreur existante. - Mock gateway enrichi avec l'état de reprise et des helpers de scénario; fixture structurelle des services plugin mise à niveau. - `saveOpenProjectRestoreOrder` est exposé mais non appelé: l'UI actuelle ne permet aucun réordonnancement manuel des onglets, conformément au périmètre. ### Fichiers frontend modifiés - `frontend/src/domain/index.ts` - `frontend/src/ports/index.ts` - `frontend/src/adapters/project.ts` - `frontend/src/adapters/project.test.ts` - `frontend/src/adapters/http/requestResponseGateways.ts` - `frontend/src/adapters/http/httpInvoker.test.ts` - `frontend/src/adapters/mock/index.ts` - `frontend/src/features/projects/useProjects.ts` - `frontend/src/features/projects/ProjectsView.tsx` - `frontend/src/features/projects/projects.test.tsx` - `frontend/src/plugins/runtime/services.test.ts` ### Tests ajoutés / couverts - Mapping exact des cinq commandes sur transports Tauri et HTTP. - Restauration silencieuse et ordre mémorisé. - Notification singulier/pluriel présente une seule fois. - Détail nom/chemin/cause compréhensible. - Retry d'un projet redevenu accessible et réinsertion entre ses voisins. - Retrait d'un tombstone de la reprise future. - Fermeture explicite persistée et conservation de l'onglet si le backend refuse la fermeture. ### Commandes exécutées - `cd frontend && npm run typecheck` — vert. - `cd frontend && npm run build` — vert (515 modules transformés; avertissement Vite existant sur la taille du chunk principal). - `cd frontend && npm test -- src/adapters/project.test.ts src/adapters/http/httpInvoker.test.ts src/features/projects/projects.test.tsx` — vert, 3 fichiers / 37 tests. - `cd frontend && git diff --check` — vert. - `cd frontend && npm test` — non vert hors tests ciblés: 1234 tests passent, 32 échouent dans 6 fichiers; la sortie inclut notamment des scénarios web `WebAgentCell`, `WebDesktopWorkspace`, `WebTicketsSprints` sans rapport avec #303. - `cd frontend && npm test -- src/features/projects/ProjectsView.ls7.test.tsx` — 6 tests passent, 3 tests échouent sur l'attente du bouton `open conversation conversation-open-me`; ces scénarios LS7 à menu flottant sont sensibles au timing et ne touchent pas les assertions de reprise #303. ### Gaps / reprise QA - QA doit rejouer la suite complète dans son environnement et qualifier les 32 échecs non ciblés; aucune erreur de typage ni échec dans les 37 tests du lot #303. - Le rebuild AppImage reste une étape post-validation QA/Git et n'a pas été réalisé dans ce lot frontend. - Aucun commit effectué. ## Validation QA — 2026-09-10 Verdict: VERT pour le périmètre #303, avec réserves hors périmètre maintenues ci-dessous. Inspection: - Carnet complet lu. - Diff inspecté sur modèle/port/store backend, use cases, composition `BackendCore`, commandes Tauri, route HTTP, adapters Tauri/HTTP frontend, mock gateway, `useProjects` et `ProjectsView`. - Les contrats Tauri et HTTP exposent les cinq commandes attendues: - `restore_open_projects` - `retry_open_project_restore` - `remove_project_from_restore` - `save_open_project_restore_order` - `read_open_project_restore_state` - Le frontend lance `listProjects()` et `restoreOpenProjects()` à l'initialisation, reconstruit `openTabs` selon l'ordre `opened`, reste silencieux sans échec, affiche une notification unique en cas d'échec, expose détails/retry/remove et conserve un onglet si `closeProject` échoue. - Les side effects backend sont centralisés dans `BackendCore::activate_project`: reconciliation layouts/live-state/background tasks, watcher orchestrateur, serveur MCP via `ensure_orchestrator_watch` -> `ensure_mcp_server`, puis réparation des run dirs Claude. - Couverture QA ajoutée: le test backend #303 vérifie maintenant explicitement que les projets restaurés/retry ont aussi une entrée dans `mcp_servers`, en plus de l'ouverture visible dérivée des watchers. Commandes exécutées et résultats verts: - `cargo test -p backend open_project_restore_tests -- --nocapture` - OK: 1 passed; couvre absence initiale sans fichier, ordre, fermeture explicite, root inaccessible non bloquant, retry ciblé, `notFound`, remove, watcher et serveur MCP. - `cargo test -p web-server authorized_open_project_restore_commands_share_the_tauri_contract -- --nocapture` - OK: 1 passed; contrat HTTP aligné sur commandes Tauri. - `npm test -- src/adapters/project.test.ts src/adapters/http/httpInvoker.test.ts src/features/projects/projects.test.tsx` - OK: 3 files / 37 tests passed; couvre mappings Tauri/HTTP et comportement frontend #303. - `cargo fmt --all -- --check` - OK. - `cargo check -p application -p infrastructure` - OK. - `cargo check -p backend -p app-tauri -p web-server` - OK. - `cargo check -p backend` - OK après ajout de l'assertion QA MCP. - `npm run typecheck` - OK. - `npm run build` - OK; avertissement Vite existant de chunk > 500 kB. - `cargo test -p domain --quiet` - OK: 311 unit tests + suites associées vertes. - `cargo test -p backend --quiet` - OK: 93 passed, 7 ignored. - `git diff --check` - OK. Suites larges rouges hors périmètre: - `cargo test -p application -p infrastructure -p backend` - ROUGE avant d'atteindre infrastructure/backend à cause de `application/tests/layout_usecases.rs`. - Échecs reproduits: - `create_layout_plugin_validates_active_runtime_contribution_and_persists_root` - `mutate_set_plugin_layout_state_persists_opaque_state` - Cause observée: panic sur `Err` `NotFound("plugin package")`. - Qualification QA: hors périmètre #303; fichiers/tests de layouts plugin, pas de lien avec restore/open projects. - `cargo test -p infrastructure session::process::tests::run_turn_cancellable_kills_current_child_without_waiting_for_natural_exit -- --nocapture` - ROUGE: panic après 10 s, `cancel should not wait for the child process natural exit`. - Qualification QA: hors périmètre #303; test temporel process cancellation déjà signalé, pas de lien avec restore/open projects. - `npm test` - ROUGE: 120 files passed, 6 failed; 1234 tests passed, 32 failed. - Fichiers en échec: - `src/features/projects/ProjectsView.ls7.test.tsx` — 3 tests LS7 conversation viewer. - `src/features/web/WebAgentCell.test.tsx` — 1 test mobile custom CLI launch failure. - `src/features/web/WebApp.test.tsx` — 3 tests pairing/session routing. - `src/features/web/WebDesktopWorkspace.test.tsx` — 1 test docking web desktop layout. - `src/features/web/WebMenus.test.tsx` — 8 tests menu/shell web #90. - `src/features/web/tickets/WebTicketsSprints.test.tsx` — 16 tests tickets/sprints web #86. - Qualification QA: hors périmètre #303; les 37 tests ciblés #303 sont verts et les fichiers rouges concernent web shell/LS7/tickets, pas la reprise des projets desktop. Réserves: - Je n'ai pas validé un cycle UI AppImage réel fermeture/relance; la validation est automatisée backend/frontend et transport. - Les rouges hors périmètre ci-dessus doivent rester suivis séparément; ils ne bloquent pas le symptôme #303. Conclusion: - Premier lancement sans état: restore vide sans création de fichier et sans notification. - Restauration A/B dans l'ordre mémorisé: couverte. - Fermeture explicite: retire de la prochaine reprise, et l'UI ne retire l'onglet qu'après succès backend. - Projet inaccessible/notFound: non bloquant, tombstone conservé avec notification/détails. - Retry: réouvre et réinsère selon position mémorisée. - Remove: supprime l'entrée de reprise future. - Watcher et serveur MCP: activés pour les succès restore/retry via le chemin partagé `activate_project`, avec assertion backend dédiée. - Aucun finding bloquant #303. ## Topologie Git — 2026-09-10 QA reconfirmée par Git avant commit (exécution réelle): - `rustup run stable cargo fmt --all -- --check` — OK. - `git diff --check` — OK. - `cargo test -p backend open_project_restore_tests` — 1/1. - `cargo test -p web-server authorized_open_project_restore_commands_share_the_tauri_contract` — 1/1. - `cargo test -p domain --quiet` — 311 unit tests + suites vertes. - `cargo check -p application -p infrastructure -p backend -p app-tauri -p web-server` — OK. - Frontend: `npm run typecheck` OK, `npm test` ciblé 3 fichiers/37 tests OK, `npm run build` OK. - Suites larges rouges hors périmètre (layout_usecases fixtures plugin, timing infrastructure, 32 échecs web shell/LS7/tickets) non rejouées par Git: qualifiées hors #303 par QA, à suivre séparément. Commit de la feature (28 fichiers, bookkeeping exclu): - Branche: `feature/303-restore-open-projects-order`. - Commit: `8df80ae` — `feat(domain,application,infrastructure,backend,app-tauri,web-server,frontend): restaurer les projets ouverts au redémarrage dans l'ordre mémorisé — #303`. - Inclus: 15 fichiers crates (dont nouveaux `crates/domain/src/project_restore.rs`, `crates/application/src/project/restore.rs`, `crates/infrastructure/src/store/open_project_restore.rs`) + 13 fichiers frontend/src (adapters, ports, domain, useProjects, ProjectsView, tests, mock). - Exclus (hors périmètre, restés en worktree): `.ideai/tickets/296/*`, `.ideai/tickets/299-303/*`, `counter.json`, `index.json`, `.ideai/attachments/`, `scripts/`. Merge local: - `develop` ← merge `--no-ff` de `feature/303-restore-open-projects-order` → commit `bd1c55d`. - develop n'avait pas divergé depuis la création de la branche (fast-path, contenu identique à `8df80ae`). - Branche `feature/303-restore-open-projects-order` conservée. - Aucune action distante (aucun push; develop reste en avance locale sur `origin/develop`). Restants après merge: - Rebuild AppImage demandé par l'utilisateur: commit dédié `--allow-empty` + tag annoté, incrément `y` sur develop → `v0.45.0`. - Validation live fermeture/relance depuis l'app installée (ordre des onglets, notification échec, retry/remove). - Suivi séparé des rouges hors périmètre (layout_usecases, timing cancellation process, web shell/LS7/tickets). ## Rebuild AppImage — 2026-09-10 Rebuild exécuté par Main depuis `develop` @ bd1c55d (incluant le merge #303): - Commandes: `npm --prefix frontend run build` OK, puis depuis `crates/app-tauri`: `mkdir -p /tmp/idea-cargo-home && CARGO_HOME=/tmp/idea-cargo-home APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ../../frontend/node_modules/.bin/tauri build --bundles appimage` OK. - Artefact: `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`, 119843320 octets, sha256 `e57cc136f17d887798d09468f2858b1b025cd2386abdc4d517ccb49f464df5b5` (vérifié par Git). Traçabilité Git: - Commit dédié `--allow-empty` (l'artefact vit sous `target/` gitignoré): `5b07596` sur `develop` — `chore(appimage): rebuild AppImage develop — commit dédié v0.45.0 #303`. - Tag annoté `v0.45.0` posé sur `5b07596` (convention respectée: `y` incrémenté pour un rebuild committé sur develop, v0.44.0 -> v0.45.0). - Bookkeeping carnet/index/counter et fichiers hors périmètre exclus, restés en worktree. Aucun push. Restant après rebuild: - Validation live fermeture/relance depuis l'app installée v0.45.0: onglets restaurés dans l'ordre, silence si tout réussit, notification unique en cas d'échec avec `Réessayer`/`Retirer de la reprise` fonctionnels.