24 KiB
24 KiB
issueRef, version, updatedBy, updatedAt
| issueRef | version | updatedBy | updatedAt | ||||
|---|---|---|---|---|---|---|---|
| #303 | 6 |
|
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 rouvertou{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éessayerrestaure le projet à sa position mémorisée s'il redevient accessible.Retirer de la repriseempê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:
<app_data_dir>/open-projects.json. - Distincte du registre
projects.json, qui liste les projets connus mais pas l'état d'ouverture. workspace.jsonne doit pas être recyclé pour #303: il modélise des layouts/tabs historiques et n'est pas alimenté paruseProjects.
Contrat recommandé:
{
"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()viaHashMapn'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 viaprojects.json, prévalider le root avecFileSystem, puis appeler la même logique d'ouverture queopen_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:
ProjectGatewaygagnerestoreOpenProjects,retryOpenProjectRestore,removeProjectFromRestore, optionnellementsaveOpenProjectRestoreOrder.useProjectsinitialise:listProjects(),restoreOpenProjects(), rempliropenTabsavecopeneddans l'ordre retourné, notifier seulement sifailures.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.rsou é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éessayerouvre B à sa position mémorisée. Retirer de la reprisesupprime 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é
OpenProjectRestoreStateet du port dédiéOpenProjectRestoreStore. - Ajout du store filesystem
<app_data_dir>/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 stablesnotFound,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_projectssans requête ->{ opened: ProjectDto[], failures: RestoreOpenProjectFailureDto[] }.retry_open_project_restoreavec{ projectId?: string }(requête omissible côté HTTP) -> même output.remove_project_from_restoreavec{ projectId: string }->OpenProjectRestoreStateDto.save_open_project_restore_orderavec{ 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_statesans requête ->{ version, entries, updatedAt }; chaque entrée expose{ projectId, name, root, position, lastFailure }etlastFailurevaut explicitementnullsans échec.- Une failure expose
{ projectId, name, root, position, code, message }aveccodeparminotFound | inaccessible | store | invalid. - DevFrontend doit encore appeler
restore_open_projectsà l'initialisation, reconstruireopenTabsdans l'ordre deopened, 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.rscrates/domain/src/lib.rscrates/domain/src/ports.rscrates/application/src/project/restore.rscrates/application/src/project/mod.rscrates/application/src/lib.rscrates/infrastructure/src/store/open_project_restore.rscrates/infrastructure/src/store/mod.rscrates/infrastructure/src/lib.rscrates/backend/src/dto.rscrates/backend/src/lib.rscrates/app-tauri/src/commands.rscrates/app-tauri/src/lib.rscrates/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 --allcargo 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 danstests/layout_usecases.rs(plugin packageintrouvable); 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
ProjectGatewayavec les cinq commandes backend (restore,retry,remove,save order,read state). - Adapters Tauri et HTTP alignés sur les noms de commandes et enveloppes
requestdu backend. - Initialisation de
useProjectspar 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étailsetRé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
positionmé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
closeProjectavant 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.
saveOpenProjectRestoreOrderest 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.tsfrontend/src/ports/index.tsfrontend/src/adapters/project.tsfrontend/src/adapters/project.test.tsfrontend/src/adapters/http/requestResponseGateways.tsfrontend/src/adapters/http/httpInvoker.test.tsfrontend/src/adapters/mock/index.tsfrontend/src/features/projects/useProjects.tsfrontend/src/features/projects/ProjectsView.tsxfrontend/src/features/projects/projects.test.tsxfrontend/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 webWebAgentCell,WebDesktopWorkspace,WebTicketsSprintssans rapport avec #303.cd frontend && npm test -- src/features/projects/ProjectsView.ls7.test.tsx— 6 tests passent, 3 tests échouent sur l'attente du boutonopen 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,useProjectsetProjectsView. - Les contrats Tauri et HTTP exposent les cinq commandes attendues:
restore_open_projectsretry_open_project_restoreremove_project_from_restoresave_open_project_restore_orderread_open_project_restore_state
- Le frontend lance
listProjects()etrestoreOpenProjects()à l'initialisation, reconstruitopenTabsselon l'ordreopened, reste silencieux sans échec, affiche une notification unique en cas d'échec, expose détails/retry/remove et conserve un onglet sicloseProjectéchoue. - Les side effects backend sont centralisés dans
BackendCore::activate_project: reconciliation layouts/live-state/background tasks, watcher orchestrateur, serveur MCP viaensure_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.
- OK: 1 passed; couvre absence initiale sans fichier, ordre, fermeture explicite, root inaccessible non bloquant, retry ciblé,
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_rootmutate_set_plugin_layout_state_persists_opaque_state
- Cause observée: panic sur
ErrNotFound("plugin package"). - Qualification QA: hors périmètre #303; fichiers/tests de layouts plugin, pas de lien avec restore/open projects.
- ROUGE avant d'atteindre infrastructure/backend à cause de
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.
- ROUGE: panic après 10 s,
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 typecheckOK,npm testciblé 3 fichiers/37 tests OK,npm run buildOK. - 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-ffdefeature/303-restore-open-projects-order→ commitbd1c55d.- develop n'avait pas divergé depuis la création de la branche (fast-path, contenu identique à
8df80ae). - Branche
feature/303-restore-open-projects-orderconservé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émentysur 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 buildOK, puis depuiscrates/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 appimageOK. - Artefact:
target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage, 119843320 octets, sha256e57cc136f17d887798d09468f2858b1b025cd2386abdc4d517ccb49f464df5b5(vérifié par Git).
Traçabilité Git:
- Commit dédié
--allow-empty(l'artefact vit soustarget/gitignoré):5b07596surdevelop—chore(appimage): rebuild AppImage develop — commit dédié v0.45.0 #303. - Tag annoté
v0.45.0posé sur5b07596(convention respectée:yincré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 reprisefonctionnels.