architecture.md: 138 components, counted as the .tsx files of src/components outside tests (find src/components -name '*.tsx' ! -name '*.test.tsx' | wc -l), was 61; per domain, balance 12 with its actual names (BalanceOnboardingCard is gone), categories 8, reports 35, and the missing categories-migration/ line (5). The /reports hub has 5 navigation cards, named. The useImportWizard row says SET_IMPORT_FOLDER empties rootFiles and keeps the scanned sources until the next scan. "La mise en évidence" notes that a Ctrl+click on the Trends card fires onClick, so it checks the "Browse the reports" step while the router does not navigate; judged theoretical without tabs in the webview. CLAUDE.md: 138 components (was 53), import 18 (was 14). CHANGELOG (EN, FR): Changed for the help texts, Fixed for the root files warning after choosing another import folder. Resolves #446 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14 KiB
CLAUDE.md — Simpl'Résultat
@STATE.md
Contexte du projet
Simpl'Résultat est une application de bureau desktop privacy-first pour la gestion des finances personnelles. Elle traite localement les fichiers CSV bancaires sans aucune dépendance cloud. Projet solo entrepreneurial, en développement par Max.
Stack technique : Tauri v2 + React 19 + TypeScript + Tailwind CSS v4 Backend : Rust (commandes Tauri) Stockage : SQLite local (tauri-plugin-sql) Langues supportées : Français (FR) et Anglais (EN) Plateformes : Windows, Linux Version actuelle : 0.17.2 Licence : GPL-3.0-only
Principes fondamentaux
Privacy-first — NON NÉGOCIABLE
- Zéro donnée envoyée vers un serveur tiers
- Tout le traitement CSV et toutes les données financières restent en local
- Aucune télémétrie, aucun analytics cloud
Précision financière
- Toujours valider les montants selon les règles de parsing configurables (gestion des virgules/points, espaces, symboles monétaires)
- Gérer l'encodage des fichiers CSV (UTF-8, Windows-1252, ISO-8859-15)
Internationalisation (i18n)
- Toute chaîne affichée à l'utilisateur doit passer par le système i18n (i18next + react-i18next)
- Jamais de texte en dur dans les composants React
- Fichiers de traduction :
src/i18n/locales/fr.jsonetsrc/i18n/locales/en.json
Architecture & structure du code
src/
├── components/ # 138 composants React organisés par domaine
│ ├── adjustments/ # Ajustements
│ ├── budget/ # Budget
│ ├── categories/ # Catégories hiérarchiques
│ ├── dashboard/ # Tableau de bord
│ ├── import/ # Wizard d'import (18 composants)
│ ├── layout/ # AppShell, Sidebar
│ ├── profile/ # Profils (PIN, formulaire, switcher)
│ ├── reports/ # Graphiques et rapports
│ ├── settings/ # Paramètres
│ ├── shared/ # Composants réutilisables
│ └── transactions/ # Transactions
├── contexts/ # ProfileContext (état global profil)
├── hooks/ # 29 hooks custom (useReducer)
├── pages/ # 11 pages
├── services/ # 14 services métier
├── shared/ # Types et constantes partagés
├── utils/ # Utilitaires (parsing, CSV, charts)
├── i18n/ # Config i18next + locales FR/EN
├── App.tsx # Router principal (react-router-dom)
└── main.tsx # Point d'entrée
src-tauri/
├── src/
│ ├── commands/ # 3 modules, 17 commandes Tauri
│ │ ├── fs_commands.rs # Système de fichiers (6 commandes)
│ │ ├── export_import_commands.rs # Export/import chiffré (5 commandes)
│ │ └── profile_commands.rs # Gestion des profils (8 commandes)
│ ├── database/ # Schémas SQL et migrations
│ │ ├── schema.sql # Schéma initial (v1)
│ │ ├── seed_categories.sql # Seed catégories (v2)
│ │ ├── consolidated_schema.sql # Schéma complet (nouveaux profils)
│ │ └── profile_db.rs # Rattrapage des profils secondaires (ADR 0021)
│ ├── lib.rs # Point d'entrée, 17 migrations inline, plugins
│ └── main.rs
└── Cargo.toml
Règles d'architecture :
- La logique métier va dans
services/, jamais directement dans les composants - L'état de chaque domaine est géré par un hook
useReducerdédié danshooks/ - Les composants React sont responsables de l'affichage uniquement
- Toute opération sur les fichiers système passe par les commandes Tauri (Rust)
- Les requêtes SQL passent par les services TypeScript via
tauri-plugin-sql
Fonctionnalités principales
- Import CSV : wizard multi-étapes, détection auto (encodage, délimiteur, colonnes par libellé FR/EN, formats de banques connus) avec score de confiance, aperçu obligatoire à récap signé avant tout import, templates de config, déduplication par fichier. Le format d'import est persisté en entier sur la source et jamais re-deviné — ADR 0019
- Catégorisation : automatique (mots-clés avec priorité) et manuelle, drag-and-drop pour réorganiser
- Transactions : filtrage, tri, split sur plusieurs catégories, notes
- Budget : grille 12 mois, templates réutilisables, budget vs réel
- Rapports : tendances mensuelles, répartition par catégorie, évolution dans le temps, graphiques interactifs (SVG patterns, menu contextuel)
- Multi-profils : bases de données séparées, protection par PIN (Argon2), switching rapide
- Export/Import : JSON/CSV avec chiffrement AES-256-GCM optionnel (format SREF)
- Mises à jour : auto-updater intégré (tauri-plugin-updater)
- Changelog bilingue : historique complet des versions dans Paramètres → Système, sous les Journaux (
/changelogy redirige), notes de la version installée dépliées et autres versions repliées ; notes de version dynamiques FR/EN depuisCHANGELOG.md/CHANGELOG.fr.md(bundlés danspublic/)
Conventions de code
React / TypeScript
- Un composant = un fichier
.tsx, nommé en PascalCase - Hooks custom dans
hooks/, services dansservices/ - État local via
useReducerdans les hooks de domaine
Rust / Tauri
- Toutes les commandes Tauri retournent
Result<T, String>pour la gestion d'erreurs - Documenter chaque commande avec un commentaire sur son rôle
Général
- Commits en anglais, commentaires de code en anglais
- Messages d'interface en français ET anglais (via i18n)
- Tester les cas limites de parsing CSV (montants négatifs, cellules vides, formats inattendus)
Base de données
- 20 tables SQLite, 24 index (voir
docs/architecture.mdpour le détail). Le module Bilan en représente 7 tables (balance_categories,balance_accounts,balance_snapshots,balance_snapshot_lines,balance_account_transfers, puisbalance_securities+balance_snapshot_holdingsajoutées en Étape 2 — détail par titre) et 9 index. Ces deux comptes sont inchangés depuis v13 : v14-v16 ajoutent 2 tables + 2 index (déjà comptés), v17 est unALTER TABLEpur - 17 migrations inline dans
lib.rs(v1→v17, viatauri_plugin_sql::Migration). Étape 2 du Bilan (détail par titre) : v14 (balance_securities+balance_snapshot_holdings+ 2 index), v15 (balance_accounts.kind+detailed_since+ backfill), v16 (conversion des comptes cotés existants en détaillés 1-position) — voir ADR 0015. Import : v17 (import_sources.amount_mode+sign_convention, les deux avecCHECK, +header_signature+template_id) — voir ADR 0019 - Schéma consolidé (
consolidated_schema.sql) pour l'initialisation des nouveaux profils, suivi de l'historique complet des migrations. tauri-plugin-sql ne migre que le profil par défaut : les profils secondaires rattrapent leurs migrations à chaque ouverture (prepare_profile_db, copie préalable, clés étrangères actives) — voir ADR 0021. Les bases vivent sousapp_config_dir()(profile_db_path), qui n'est pasapp_data_dir()sous Linux - Les migrations appliquées sont protégées par checksum — ne jamais modifier une migration existante, toujours en créer une nouvelle
Documentation technique
La documentation technique est centralisée dans docs/ :
docs/architecture.md— Architecture technique complète (stack, BDD, services, hooks, commandes Tauri, routing, i18n, CI/CD)docs/adr/— Architecture Decision Records (décisions techniques structurantes)docs/guide-utilisateur.md— Guide utilisateurdocs/archive/— Anciennes spécifications archivées
Les règles de revue des PR (niveaux de risque, ce qui compte par zone, commandes de vérification, pièges) sont dans REVIEW.md, à la racine ; un changement qui rend faux un fait qu'il cite le met à jour dans la même PR.
Règle : quand un changement touche l'architecture, mettre à jour la documentation :
- Nouveau service, hook, commande Tauri, page/route, ou table SQL → mettre à jour
docs/architecture.md - Décision technique structurante (choix de librairie, pattern architectural, changement de stratégie) → créer un nouvel ADR dans
docs/adr/ - Changement affectant l'utilisation de l'app → mettre à jour
docs/guide-utilisateur.mdet les traductions i18n correspondantes (src/i18n/locales/fr.json,src/i18n/locales/en.json, clés sousdocs.*)
Règle CHANGELOG : tout changement affectant le comportement utilisateur → ajouter une entrée sous ## [Unreleased] dans les deux fichiers :
CHANGELOG.md(anglais) — source principaleCHANGELOG.fr.md(français) — traduction- Catégories : Added/Ajouté, Changed/Modifié, Fixed/Corrigé, Removed/Supprimé
- Format Keep a Changelog. Le contenu est extrait automatiquement par le CI pour les release notes et affiché dans l'app selon la langue de l'utilisateur.
- The
public/copies are synced automatically: Vite copies them ondev/buildstart viasyncChangelogs()invite.config.ts. No manual sync needed.
Points d'attention RS&DE / CRIC
Pour maintenir l'éligibilité aux crédits d'impôt R&D (RS&DE fédéral + CRIC Québec) :
- Documenter les incertitudes technologiques rencontrées pendant le développement
- Noter les expérimentations et les approches alternatives testées
- Garder un journal des avancées techniques (dans
/docs/rnd-journal/) - Les algorithmes de catégorisation automatique et le parsing multi-format sont des activités R&D éligibles
CI/CD
Workflows Forgejo Actions dans .forgejo/workflows/. Le runner est à capacité 1 — les jobs se suivent, ils ne tournent pas en parallèle.
check-rust.yml— déclenché sur les PR touchantsrc-tauri/**ou.cargo/**, plus chaque lundi à 06:30 UTC surmainet parworkflow_dispatch. Lancecargo check --locked, une vérification bloquante que les advisories acceptées restent non atteignables, une autre que les sept arêteswindows-sysà borne large du lock restent sur0.61.2(ADR 0023),cargo test --lockedet uncargo auditinformatif, avec le cacheSwatinem/rust-cache. Doit être vert avant tout merge. Avec--locked(#449), unCargo.lockqui ne correspond plus àCargo.tomlfait échouer le job au lieu d'être réécrit dans le checkout : les gardes et l'audit lisent le lock commité. Uncargo updatevoulu dans une PR reste possible, il commite le lock qu'il écrit. Uncargo updatequi re-résout ces arêtes (-p hyperet-p h2l'ont fait) fait rougir le gardewindows-sys: remettre les arêtes à la main selon l'ADR, ne jamais modifier la liste gardée pour le faire passer.check-frontend.yml— déclenché sur les PR, sauf si tous les fichiers modifiés sont du Rust, de la doc, du markdown,check-rust.ymlouaudit.yml;.claude/**etrelease.ymlle déclenchent, carscripts/build-latest-json.test.mjslitverify-release.shet exécute l'étape « Publish » derelease.yml(#419). Lancenpm run build(tsc + vite) etnpm test(vitest). Doit être vert avant tout merge. Aucune étapenpm audit: les advisories npm ne sont pas un gate de CI.npm audità 0 au 2026-10-10 (#358), un constat daté : aucun job ne le fait échouer, maisnpm cien imprime le compte à chaque run decheck-frontend, et le Défenseur relève les advisories de son côté (#358 en vient).audit.yml— audit RustSec quotidien (06:00 UTC) +workflow_dispatch, pour couvrir les avis de sécurité entre deux PR Rust. Échec bloquant. Un run vert signifie « zéro advisory hors de la liste acceptée », pas « zéro advisory » : cette liste est dans.cargo/audit.toml(preuve de non-atteignabilité et condition de retrait par entrée), encadrée par l'ADR 0018. Ne jamais y ajouter une advisory atteignable ni élargir une entrée à un crate entier.release.yml— déclenché par les tagsv*. Build Windows (NSIS.exe) + Linux (.deb,.rpm), signe les binaires et publie le JSON d'updater pour les mises à jour automatiques. Les deuxnpx tauri buildpassent-- --lockedà cargo (par cargo-xwin pour Windows) : les binaires livrés sont bâtis sur le lock commité, jamais réécrit, et l'étape 2 de/releasevérifie le lock édité à la main parcargo metadata --locked(#449). cargo-xwin, qui tourne avec la clé de signature dans son environnement, est installé en version épinglée (--version 0.23.1) ; la toolchain Rust (rustup,stable) ne l'est pas.
Aucun workflow check-* ne filtre sur branches: : une PR stackée sur une autre branche de feature déclenche donc bien la CI. Le cache Rust passe par Swatinem/rust-cache (#234) : le runner garde ce qu'écrit un run de PR pour cette seule PR, donc c'est le run du lundi (ou un workflow_dispatch) qui alimente le cache partagé que lisent les autres PR. Un run à froid reste normal après une nouvelle version stable de Rust ou une modification de l'env: du job, quand une entrée expire (7 jours sans usage, 30 jours d'âge), ou quand une PR sauvegarde sous la clé de l'entrée partagée et l'évince, le runner ne gardant qu'une entrée par clé. La liste complète est en tête de check-rust.yml ; seul un délai dépassé signale un retour de #234. Pas de cache npm : check-frontend ne tourne que sur des PR, et npm ci y prend 9 s. Le miroir .github/workflows/ est dormant (aucune PR côté GitHub) et volontairement pas aligné : rien n'y tourne, il garde l'ancienne mise en page combinée, et ni --locked ni l'épinglage de cargo-xwin n'y sont reportés (décision de Max sur #449).
Ressources clés
- Tauri v2 Docs
- React Docs
- SQLite via Tauri
- Architecture détaillée :
docs/architecture.md - Décisions techniques :
docs/adr/