docs(collapse) : ADR 0016 persistance UI par-profil + guide utilisateur + architecture #295

Closed
maximus wants to merge 1 commit from issue-291-collapse-docs into issue-290-categories-unify
Owner

Resolves #291

Autopilot — generated autonomously by the /autopilot run of 2026-07-15.

Documentation-only. Documents the final multi-level category collapse behavior (shipped in #288/#289/#290) and the structural decision behind it:

  • ADR 0016 (accepted): profile-specific UI state (category collapse) is persisted in the profile's own SQLite user_preferences, not localStoragedeleteProfile drops the .db but purges no localStorage, so a per-profile localStorage key would be a residue surviving profile deletion (privacy leak). The ADR states the boundary explicitly: profile-specific → user_preferences; machine-global (theme, subtotals position, Cartes period mode) → localStorage.
  • guide-utilisateur.md sections 8 (Budget) + 9 (Reports) + matching in-app docs.* i18n keys (FR and EN): multi-level collapse, collapsed-by-default, "Expand all / Collapse all", memory per profile.
  • architecture.md: user_preferences line names the 4 collapse keys, useCollapsibleGroups hook note, ADR index row.

No DB migration, no behavior change. npm run build clean (0 TS errors), 828 vitest pass, cargo check clean.


Stacked PR: stacks on #294 (issue #290) and is the top of the 4-PR chain (#288 socle → #289 budget → #290 trees → #291 docs). Base is issue-290-categories-unify; review/merge the chain bottom-up.

Resolves #291 **Autopilot** — generated autonomously by the /autopilot run of 2026-07-15. Documentation-only. Documents the final multi-level category collapse behavior (shipped in #288/#289/#290) and the structural decision behind it: - **ADR 0016** (accepted): profile-specific UI state (category collapse) is persisted in the profile's own SQLite `user_preferences`, not `localStorage` — `deleteProfile` drops the `.db` but purges no `localStorage`, so a per-profile localStorage key would be a residue surviving profile deletion (privacy leak). The ADR states the boundary explicitly: profile-specific → `user_preferences`; machine-global (theme, subtotals position, Cartes period mode) → `localStorage`. - **guide-utilisateur.md** sections 8 (Budget) + 9 (Reports) + matching in-app `docs.*` i18n keys (FR **and** EN): multi-level collapse, collapsed-by-default, "Expand all / Collapse all", memory per profile. - **architecture.md**: `user_preferences` line names the 4 collapse keys, `useCollapsibleGroups` hook note, ADR index row. No DB migration, no behavior change. `npm run build` clean (0 TS errors), 828 vitest pass, `cargo check` clean. --- **Stacked PR**: stacks on #294 (issue #290) and is the **top of the 4-PR chain** (#288 socle → #289 budget → #290 trees → #291 docs). Base is `issue-290-categories-unify`; review/merge the chain bottom-up.
maximus added 1 commit 2026-07-16 01:44:16 +00:00
Document the final multi-level category collapse behavior (shipped in
#288/#289/#290) and the structural decision to persist per-profile UI
state in user_preferences rather than localStorage.

- ADR 0016 (accepted): profile-specific UI state (category collapse) lives
  in the profile's own SQLite user_preferences table, not localStorage.
  deleteProfile drops the .db but purges no localStorage, so a per-profile
  localStorage key would be a residue surviving profile deletion, leaking
  which categories a (possibly PIN-protected) profile explored. States the
  boundary: profile-specific -> user_preferences; machine-global (theme,
  subtotals position, Cartes period mode) -> localStorage.
- guide-utilisateur.md sections 8 (Budget) + 9 (Reports): multi-level
  collapse, collapsed-by-default, "Expand all / Collapse all" button,
  memory per profile.
- docs.* i18n keys (fr + en): mirror the guide additions in the in-app help
  page (docs.budget + docs.reports features/tips).
- architecture.md: user_preferences line now names the 4 collapse keys,
  useCollapsibleGroups cross-cutting hook note, ADR index row.

No DB migration, no behavior change (docs only). Build clean, 828 vitest.

Resolves #291

Generated autonomously by /autopilot run of 2026-07-15
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maximus added the
autopilot:pending-human
label 2026-07-16 01:44:33 +00:00
Author
Owner

/pr-review — Verdict : REQUEST_CHANGES

PR documentaire soignée et techniquement exacte. J'ai vérifié chaque affirmation de l'ADR 0016 contre le code du head de la pile (issue-291-collapse-docs, pas main qui porte encore l'ancienne implémentation localStorage), et tout concorde. Un seul défaut bloque : l'en-tête de l'ADR pointe vers deux fichiers spec que le .gitignore garantit absents du repo.

Issue (1)

1. docs/adr/0016-persistance-etat-ui-par-profil.md:5 — liens morts vers des specs gitignorées.

La ligne - Spec: lie ../../spec-decisions-collapse-multi-niveaux.md et ../../spec-plan-collapse-multi-niveaux.md. Ces deux fichiers matchent les patterns du .gitignore :

# Spec scratch (committed only when promoted to docs/archive/)
spec-decisions-*.md
spec-plan-*.md

Ils existent dans l'arbre de travail de l'autopilot mais ne sont pas commités dans la branche → liens 404 dans tout checkout frais et sur le miroir GitHub. Les ADR 0014/0015 ne portent aucune ligne Spec: : c'est un motif nouveau qui casse. À noter : même une promotion enverrait ces specs vers docs/archive/ (pas à la racine), donc le chemin ../../ serait de toute façon incorrect.

Trois fixes possibles :

  • Agit add -f les deux specs à la racine (précédent existant : spec-decisions-refonte-seed-categories-ipc.md / spec-plan-refonte-seed-categories-ipc.md sont déjà commités ainsi) → les liens résolvent tels quels.
  • B — promouvoir les specs dans docs/archive/ et corriger les chemins des liens en conséquence.
  • C — retirer la ligne - Spec: (l'ADR est auto-suffisant ; c'est le choix des ADR 0014/0015).

Vérifié conforme au code (échantillon)

  • Signature useCollapsibleGroups<T>(storageKey: string | null, acc, { defaultExpanded }) — exacte.
  • Persistance dans user_preferences via getPreference/setPreference (UPSERT) ; storageKey: null = état purement en mémoire, remis au défaut au montage — exact.
  • Les 4 clés annoncées matchent les constantes du code au caractère près : reports-compare-expanded, reports-bva-expanded, reports-trends-expanded, budget-grid-expanded.
  • 6 surfaces : 3 rapports (ComparePeriodTable, BudgetVsActualTable, CategoryOverTimeTable) + BudgetTable persistés ; CategoryTree + CategoryTaxonomyTree (guide + assistant migration) en null/mémoire — conforme.
  • Argument privacy central : deleteProfile (ProfileContext) ne touche jamais localStorage (aucun removeItem/clear dans tout src), il appelle deleteProfileDb → commande Rust delete_profile_db — exact, l'argument « résidu qui survit » tient.
  • Prefs machine-globales bien restées en localStorage : theme, *-subtotals-position, reports-cartes-period-mode, toggle chart/table — vérifié.
  • Schéma de clé p:<categoryId>, marche des ancêtres avec garde de cycle, polarité du Set via isCollapsedFor — conforme à collapsibleRows.ts.
  • Clés user_preferences préexistantes citées (import_folder, balance_show_returns, balance_starter_proposed) — existent réellement.
  • ADR 0002/0005/0014/0015 référencés — existent.
  • i18n : JSON valide FR+EN, parité des tableaux (docs.budget.features 7/7, .tips 4/4 ; docs.reports.features 10/10, .tips 6/6), bouton « Tout déplier / Tout replier » et clés reports.collapse.expandAll/collapseAll présents.
  • Aucune migration, aucun changement de schéma (table + service préexistants) — exact.
  • Aucun secret, aucune injection, zéro changement de code (docs + i18n uniquement).

Fix trivial, le reste est prêt à merger.


🤖 Review adversariale via /pr-review

## /pr-review — Verdict : REQUEST_CHANGES PR documentaire soignée et **techniquement exacte**. J'ai vérifié chaque affirmation de l'ADR 0016 contre le code du head de la pile (`issue-291-collapse-docs`, pas `main` qui porte encore l'ancienne implémentation localStorage), et tout concorde. Un seul défaut bloque : l'en-tête de l'ADR pointe vers deux fichiers spec que le `.gitignore` garantit absents du repo. ### Issue (1) **1. `docs/adr/0016-persistance-etat-ui-par-profil.md:5` — liens morts vers des specs gitignorées.** La ligne `- Spec:` lie `../../spec-decisions-collapse-multi-niveaux.md` et `../../spec-plan-collapse-multi-niveaux.md`. Ces deux fichiers matchent les patterns du `.gitignore` : ``` # Spec scratch (committed only when promoted to docs/archive/) spec-decisions-*.md spec-plan-*.md ``` Ils existent dans l'arbre de travail de l'autopilot mais **ne sont pas commités dans la branche** → liens 404 dans tout checkout frais et sur le miroir GitHub. Les ADR 0014/0015 ne portent aucune ligne `Spec:` : c'est un motif nouveau qui casse. À noter : même une promotion enverrait ces specs vers `docs/archive/` (pas à la racine), donc le chemin `../../` serait de toute façon incorrect. Trois fixes possibles : - **A** — `git add -f` les deux specs à la racine (précédent existant : `spec-decisions-refonte-seed-categories-ipc.md` / `spec-plan-refonte-seed-categories-ipc.md` sont déjà commités ainsi) → les liens résolvent tels quels. - **B** — promouvoir les specs dans `docs/archive/` **et** corriger les chemins des liens en conséquence. - **C** — retirer la ligne `- Spec:` (l'ADR est auto-suffisant ; c'est le choix des ADR 0014/0015). ### Vérifié conforme au code (échantillon) - Signature `useCollapsibleGroups<T>(storageKey: string | null, acc, { defaultExpanded })` — exacte. - Persistance dans `user_preferences` via `getPreference`/`setPreference` (UPSERT) ; `storageKey: null` = état purement en mémoire, remis au défaut au montage — exact. - Les **4 clés** annoncées matchent les constantes du code au caractère près : `reports-compare-expanded`, `reports-bva-expanded`, `reports-trends-expanded`, `budget-grid-expanded`. - **6 surfaces** : 3 rapports (`ComparePeriodTable`, `BudgetVsActualTable`, `CategoryOverTimeTable`) + `BudgetTable` persistés ; `CategoryTree` + `CategoryTaxonomyTree` (guide + assistant migration) en `null`/mémoire — conforme. - Argument privacy central : `deleteProfile` (`ProfileContext`) **ne touche jamais** `localStorage` (aucun `removeItem`/`clear` dans tout `src`), il appelle `deleteProfileDb` → commande Rust `delete_profile_db` — exact, l'argument « résidu qui survit » tient. - Prefs machine-globales bien restées en `localStorage` : `theme`, `*-subtotals-position`, `reports-cartes-period-mode`, toggle chart/table — vérifié. - Schéma de clé `p:<categoryId>`, marche des ancêtres avec garde de cycle, polarité du `Set` via `isCollapsedFor` — conforme à `collapsibleRows.ts`. - Clés `user_preferences` préexistantes citées (`import_folder`, `balance_show_returns`, `balance_starter_proposed`) — existent réellement. - ADR 0002/0005/0014/0015 référencés — existent. - i18n : JSON valide FR+EN, parité des tableaux (`docs.budget.features` 7/7, `.tips` 4/4 ; `docs.reports.features` 10/10, `.tips` 6/6), bouton « Tout déplier / Tout replier » et clés `reports.collapse.expandAll`/`collapseAll` présents. - Aucune migration, aucun changement de schéma (table + service préexistants) — exact. - Aucun secret, aucune injection, zéro changement de code (docs + i18n uniquement). Fix trivial, le reste est prêt à merger. --- 🤖 Review adversariale via `/pr-review`
maximus force-pushed issue-291-collapse-docs from e3ecfe7a6c to 5a3d87b31f 2026-07-18 18:43:28 +00:00 Compare
Author
Owner

REQUEST_CHANGES adressé — repassé APPROVE

Le blocage (liens - Spec: de l'ADR 0016 pointant vers des fichiers gitignorés → 404) est corrigé : les deux specs spec-decisions-collapse-multi-niveaux.md + spec-plan-collapse-multi-niveaux.md (v2, post-/review-spec) ont été force-add à la racine et intégrées au commit docs (amend e3ecfe75a3d87b). Les liens ../../spec-*.md de l'ADR résolvent désormais.

Choix du fix : force-add plutôt que suppression de la ligne. Motif : la convention réelle du repo est « spec trackée à la racine » — spec-refonte-rapports.md, spec-monetisation.md, spec-decisions-refonte-seed-categories-ipc.md y sont déjà commitées, dont une référencée par ADR. Retirer la ligne aurait perdu la traçabilité et laissé 0016 incohérent avec 0015.

Précision sur la review : l'affirmation « les ADR 0014/0015 n'ont pas de ligne Spec: » est inexacte — l'ADR 0015 a une ligne - Spec: (vers spec-decisions-bilan-detail-titres.md), dont la cible est elle aussi non trackée. Le lien de 0015 est donc cassé de la même façon : bug pré-existant, hors scope de cette PR, à corriger séparément (force-add de la spec bilan-detail-titres, même remède).

Issue #291 rebasculée status:needs-fixstatus:approved. La pile est de nouveau APPROVE ×4.

## REQUEST_CHANGES adressé — repassé APPROVE Le blocage (liens `- Spec:` de l'ADR 0016 pointant vers des fichiers gitignorés → 404) est corrigé : les deux specs `spec-decisions-collapse-multi-niveaux.md` + `spec-plan-collapse-multi-niveaux.md` (v2, post-`/review-spec`) ont été **force-add à la racine** et intégrées au commit docs (amend `e3ecfe7` → `5a3d87b`). Les liens `../../spec-*.md` de l'ADR résolvent désormais. **Choix du fix** : force-add plutôt que suppression de la ligne. Motif : la convention réelle du repo est « spec trackée à la racine » — `spec-refonte-rapports.md`, `spec-monetisation.md`, `spec-decisions-refonte-seed-categories-ipc.md` y sont déjà commitées, dont une référencée par ADR. Retirer la ligne aurait perdu la traçabilité et laissé 0016 incohérent avec 0015. **Précision sur la review** : l'affirmation « les ADR 0014/0015 n'ont pas de ligne Spec: » est inexacte — l'ADR 0015 **a** une ligne `- Spec:` (vers `spec-decisions-bilan-detail-titres.md`), dont la cible est elle aussi non trackée. Le lien de 0015 est donc cassé de la même façon : bug **pré-existant**, hors scope de cette PR, à corriger séparément (force-add de la spec bilan-detail-titres, même remède). Issue #291 rebasculée `status:needs-fix` → `status:approved`. La pile est de nouveau APPROVE ×4.
Author
Owner

Mergé en fast-forward dans main (pile #292→#295), commit 5a3d87b. Forgejo ne détecte pas le merge local → fermeture manuelle. L'issue liée s'est auto-fermée via Resolves #N.

Mergé en fast-forward dans `main` (pile #292→#295), commit `5a3d87b`. Forgejo ne détecte pas le merge local → fermeture manuelle. L'issue liée s'est auto-fermée via `Resolves #N`.
maximus closed this pull request 2026-07-18 18:47:39 +00:00

Pull request closed

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#295
No description provided.