Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159y7AeM6djQLDN2vZ8soPa
46 KiB
STATE — Simpl'Résultat
Derniere MAJ : 2026-09-13 (Étape 10 de v0.15.0 consignée — Max rapporte que la mise à jour automatique v0.14.0 → v0.15.0 fonctionne chez les deux testeurs. Corroboration Forgejo : assets v0.15.0 à exactement 1
.deb+ 1-setup.exetéléchargés, 0.sig— cohérent avec une machine Linux et une Windows passées par l'updater, sans en être la preuve ; invite polkit Linux non consignée ;.rpmhors test (#320). Trace rédigée pour le corps de la release (Forgejo n'a pas de commentaires de release ; lePATCHest une écriture externe bloquée en mode auto → remis à Max). Le « code à 6 chiffres » reçu par courriel par un testeur n'est pas une étape de la mise à jour : ni l'updater, ni la saisie de clé, ni l'activation machine n'en émettent — c'est le code de vérification Logto du bouton « Se connecter » de la carte Compte Maximus (Paramètres → Utilisateurs, juste au-dessus de la carte Licence), pour l'un de trois flux : connexion par courriel + code, mot de passe oublié, ou inscription ; MFA = TOTP seul. Le flux exact est dans le journal d'audit Logto (secret M2M sur le VPS seulement). Sans effet sur l'édition Premium. Cause probable : le texte de la carte dit « Le compte est requis uniquement pour les fonctionnalités Premium », faux depuis que Premium vient de la clé — à reformuler.)Précédente MAJ : 2026-09-12 (v0.15.0 shippée — tag
v0.15.0sur920dedd, run 402build-and-releasevert en 22 min, 7 artefacts +latest.jsonvérifiés par script (version,pub_date, signatures non vides pourlinux-x86_64etwindows-x86_64, URLs sur la bonne version, notes extraites du CHANGELOG). Pré-vol local sur le tip avant de tagger : 111 Rust, 1181 vitest, build Vite, aucun worktree. Contenu : gating par édition (#297-#301), chantier import + migration v17 (#323-#331), fusion des catégories custom (#259), trois lots d'advisories (#310/#311/#343) ;CLAUDE.mdremis à0.15.0(traînait à 0.6.3). Prérequis testeurs réglé avant le tag : v0.14.0 laissaitauto-updateouvert en Free, v0.15.0 le ferme (Base+), donc une installation sans clé aurait reçu v0.15.0 comme dernière mise à jour automatique — deux clés Premium 10 ans émises le 2026-09-12 (Max + Michel Tremblay, compte Logtomichelt) viaPOST /v1/licenses/generate, signature Ed25519 et claims vérifiés contre la clé publique embarquée du client ; la tablelicensesne portait qu'une Base révoquée pour Max (achat Stripe de test remboursé le 07-19), et la révocation n'est jamais vue hors ligne. Détour infra : le VPS était hors du tailnet depuis le 08-21 — clé de nœud Tailscale expirée (peer's node key has expired, aucun lien navigateur ne vient jamais dans ce cas), réparé par le Terminal Coolify (tailscale up --ssh) + Disable key expiry ; la clé du ThinkPad n'est pas autorisée sur l'IP publique54.39.97.77. Étape 10 (docs/qa-update-cycle.md) pas encore déroulée — à faire depuis les v0.14.0 installées avec clé Premium ; #320 : les.rpmne se mettront pas à jour.)
Position actuelle
v0.9.1 shippée (2026-05-10). Milestones spec-refonte-rapports, spec-refonte-seed-categories-ipc, spec-price-fetching, overnight-2026-04-26-bilan et overnight-2026-04-27-prices complétées et fermées. Maximus-api 0.3.0 LIVE en prod (api.lacompagniemaximus.com) — pubkey Ed25519 alignée, smoke test Phase B (#161) validé. Backlog actif concentré sur spec-monetisation (7/12) : activation en ligne (#53), pipeline Stripe desktop (#50, #52) et maximus-api Stripe webhooks (#135, #136).
Audit critique de la page Bilan livré (docs/audit-bilan-2026-05.md, revue CPA + UX). Étape 0 — quick wins terminologie « catégorie »→« type », symbole optionnel, date de snapshot déplaçable — mergée (#201, issues #198/#199/#200 fermées). Chantier structurel = Étapes 1-2 de l'audit (séparation véhicule fiscal × classe d'actif via vehicle_type, puis détail par titre balance_securities+holdings+book_cost, bascule agrégé→détaillé, réouverture ADR 0012). Étape 1 livrée (sprint 2026-06-01 : PRs #206-#209 mergées, milestone overnight-2026-06-01-bilan-axe-vehicule fermée 4/4) — vehicle_type nullable = enveloppe fiscale portée par le compte, catégorie = pure classe d'actif (5 classes), migrations additives v12/v13 (v1-v11 intactes), renommage via custom_label (fix bug I), axe graphique classe/enveloppe + rendements repliables persistés ; ADR 0014 Accepted, ADR 0012 Rejected. CHANGELOG sous [Unreleased] (pas encore taggé). Étape 2 livrée (merge manuel 2026-06-09 : pile de 9 PRs stackées #219-#227 mergées bottom-up, milestone overnight-2026-06-05-bilan-detail-titres à 9/10, issues #210-#218 fermées) — détail par titre via balance_securities + balance_snapshot_holdings, balance_accounts.kind ('simple'|'detailed') + detailed_since, migrations additives v14/v15/v16 (v1-v13 intactes), conversion des comptes cotés existants en détaillés 1-position (v16), service securities transactionnel, reducer holdings + dispatch account.kind, UI multi-titres (SecurityPicker), assistant détailler-un-compte (date pivot), drill-down par titre + gain latent, tests intégration/régression ; ADR 0015 Accepted. #228 mergé (PR #229, fix-forward : garde d'abort v16 scopée aux comptes convertibles via JOIN balance_categories + c.asset_type IS NOT NULL dans les 3 copies — Migration v16 / V16_SQL / V16_CORRUPT — + test régression ; CI vert car #229 ciblait main) → milestone overnight-2026-06-05-bilan-detail-titres complète 10/10 et fermée. v0.10.0 shippée (2026-06-29 : Étapes 1+2 du bilan, migrations v12→v16) puis hotfix v0.10.1 (2026-06-30, PR #230, déployé) — corrige un « database is locked » introduit en 0.10.0 (apparaissait après l'abandon d'un snapshot en cours) : tout l'accès DB est désormais sérialisé via withTransaction dans db.ts (tauri-plugin-sql = pool sqlx multi-connexions sans primitive de transaction JS → BEGIN/COMMIT en db.execute séparés pouvaient strander une transaction d'écriture = verrou zombie), appliqué aux 5 sites transactionnels ; + console de log live-update + API logInfo/logWarn/logError. 20 tables / 24 index, 16 migrations (v1→v16).
Milestone deps-security-2026-07 livrée (2026-07-01, 4/4 fermée) — 4 vulns de dépendances Defenseur remédiées en 2 PRs (#239 react-router-dom 7.18.1 ; #240 vite 6.4.3 + vitest 4.1.9) ; npm audit 5→0 — le dernier @babel/core low remédié le 2026-07-04 (override scopé @vitejs/plugin-react→@babel/core@^7.29.7, issue #241 / PR #242 mergée). Ce « 0 » n'est plus l'état courant : des advisories publiées depuis l'ont ramené à 3, traitées en #311 le 2026-07-27 → 2 restantes, acceptées (react-router, cf. entrée du jour). Nouveau milestone spec-ci-build-optimization (2/4) ouvert (baseline #231 : cause #2 = reserveCache timeout du runner ; #234 connectivité cache VPS, #232 split workflows).
v0.11.0 shippée (2026-07-05, tag v0.11.0 → CI release Windows/Linux + JSON updater) — milestone overnight-2026-07-05-bilan-rapports-ux complétée 5/5 et fermée via un run /autopilot (5 workers séquentiels, PRs #248-#252 toutes /pr-review APPROVE, puis merge local de la pile + réconciliation Forgejo). Contenu : rapport comparable réel-vs-réel hiérarchique avec sous-totaux (#247) ; netting des transferts ciblé (catégories type transfer → SUM signé ~0, autres types inchangés au byte) dans le compare (#243) ; landing Bilan en tuiles HubReportNavCard + guard empty-state découplé + gestion comptes accessible partout (#244) ; import CSV de titres dans un snapshot détaillé (front pur, autoDetectHoldingColumns, prix flexible) (#245) ; cible de migration catégories éditable sur chaque ligne + type-ahead CategoryCombobox feuilles-seulement (#246). Aucune migration DB (v1→v16 inchangées), 20 tables / 24 index, 16 migrations. Point de suivi non bloquant : nets de transfert signés traversent du code UI compare pensé pour des magnitudes positives (mord seulement les transferts déséquilibrés à une jambe).
v0.12.0 shippée (2026-07-05, tag v0.12.0 → CI release #311) — recadrage de #243 (montants de transfert inclus dans le comparable) par Max en « analyse de résultat » : deux rapports comparables refondus en états de résultat (income statement). Le compare réel-vs-réel (#253) et le tableau de tendance par catégorie (#256) affichent le revenu, un Résultat avant transferts (revenus − dépenses) et un Résultat net (après transferts), sections ordonnées revenu→dépense→transfert ; les rapports comparables ouvrent par défaut sur le mois complet précédent (#253). Modules purs extraits et testés (compareResults.ts, overTimeResults.ts) ; 2 blocages /pr-review corrigés (fuite du revenu dans les top-movers Cartes ; garde StrictMode sur le sync du mois de référence). Livré en 2 issues/PRs stackées #255→#257 (merge local + réconciliation Forgejo, branches supprimées), 701 vitest verts, aucune migration DB (v1→v16 inchangées), 20 tables / 24 index. Backlog ouvert au passage : #258 (db-locked proposeStarterAccounts) → fermé le 2026-07-07 (déjà corrigé v0.10.1, pas de nesting) ; #254 (collapse/expand) → livré le 2026-07-07 (PR #261) ; #259 (mapping manuel d'un compte standard sans similaire auto) et #260 (epic « rapports uniformes » — tous les rapports sur le modèle income-statement) ouverts.
v0.15.0 shippée (2026-09-12, tag v0.15.0 → CI release run 402, 7 artefacts + latest.json vérifiés) — première version où le gating par édition est actif : sans clé, Budget, Ajustements, rapports avancés, profils multiples, Bilan et mises à jour automatiques sont verrouillés (données intactes, ADR 0017). Les deux testeurs (Max, Michel) ont une clé Premium émise avant le tag. [Unreleased] vide. Aucune migration DB nouvelle (v1→v17). Étape 10 de /release consignée le 2026-09-13 : cycle réel v0.14.0 → v0.15.0 validé par les deux testeurs, trace rédigée pour le corps de la release Forgejo (invite polkit Linux non consignée, .rpm hors test → #320).
Decisions recentes
- 2026-09-13 : Étape 10 de v0.15.0 consignée — le cycle de mise à jour réel passe, et le « code à 6 chiffres » reçu par un testeur n'est pas une étape de la mise à jour. Max rapporte que v0.14.0 → v0.15.0 fonctionne chez les deux testeurs. Corroboration Forgejo au 09-13 : les assets v0.15.0 comptent exactement 1
.debet 1-setup.exetéléchargés, 0.sig(l'updater lit les signatures danslatest.json) — cohérent avec une machine Linux et une Windows passées par l'updater, sans en être la preuve ; invite polkit Linux non consignée. Trace rédigée pour le corps de la release (Forgejo n'a pas de commentaires de release —docs/qa-update-cycle.mddisait « commentaire », corrigé avec lePATCHà rejouer) ; lePATCHlui-même est une écriture externe bloquée par le classifier du mode auto → payload préparé, commande remise à Max en!. Le code par courriel, retracé dans le code plutôt que deviné : nitauri-plugin-updater, ni la saisie de clé (validation Ed25519 locale), ni l'activation machine (license_key+machine_id; côtémaximus-api, seulfulfillmentServiceenvoie un courriel) n'en émettent. La seule surface est « Se connecter » de la carte Compte Maximus, première carte de Paramètres → Utilisateurs, juste au-dessus de la carte Licence où un testeur vient saisir sa clé — PKCE dans le navigateur système → page Logto, dont le connecteur SMTP (Resend) envoie un code à 6 chiffres valide 10 min pour trois flux, d'après lesign-in-exppublic du tenant : connexion par courriel + code (verificationCode: truesur l'identifiant email), « mot de passe oublié », inscription (signUp.verify: true) ; MFA = TOTP seul, donc exclu. Lequel des trois : seul le journal d'audit Logto le dit (console, ouGET /api/logs?userId=avec le secret M2M qui ne vit que sur le VPS). Sans effet sur l'édition :current_entitlementsne lit le compte que pour un abonnementactive, sinon retombe sur la clé Premium. Cause probable du clic : le texte de la carte (« Le compte est requis uniquement pour les fonctionnalités Premium », hérité de v0.7) contredit v0.15.0 où Premium vient de la clé — piège pour tout testeur qui arrive avec une clé ; reformulation à ouvrir en issue.logto-ops.mdenrichi (flux émetteurs du code + requête d'audit). Suite dans la même séance : évaluation pré-vente par 3 audits parallèles (desktop, API, site ; synthèse en mémoireproject-pre-sale-gaps) — la chaîne Stripe est prouvée en mode test depuis le 07-19, ce qui bloque est de l'alignement : #270, Premium verrouillé sans offre vendable, identité vendeur fictive (CGV +merchantInfo.ts), copie produit « gratuit/bêta » contradictoire, mode live (#62/#141), backup Postgres des licences non prouvé ; puis/analyze 270→ corps réécrit et 3 issues ouvertes (#346, #347, la-compagnie-maximus#153). Aucune migration DB (v1→v17). (ref v0.15.0, #320, #270, #346, #347) - 2026-09-12 : v0.15.0 taggée et publiée (
920dedd, run 402 vert) — et deux clés Premium émises juste avant, parce que la release ferme la porte derrière elle. Point de départ : Max demande comment les testeurs (lui et Michel) gardent l'accès une fois le gating actif. Lecture du code plutôt que de la spec : en v0.14.0FEATURE_TIERSlaisseauto-updateouvert en Free (« temporarily open until the license server is live »), v0.15.0 le passe Base+ — donc une installation sans clé reçoit v0.15.0 automatiquement et plus jamais rien ensuite, et se retrouve Free : profil actif seul, Budget/Ajustements/rapports avancés/Bilan verrouillés. Ni clause de grand-père ni période de grâce dans le code,SR_DEV_EDITIONcompilé hors des builds publiés. Réponse : une clé par testeur, saisie avant le tag (la carte Licence existe en v0.14.0 et la clé publique est identique entre v0.14.0 etmain), validation locale sans réseau ; l'activation machine n'est requise que pour les cours du Bilan (activation.tokenen Bearer vers/v1/prices), et l'ancien chemin/licenses/activatedu binaire passe par un 308 vers/v1/avec produit inféré du préfixeSR-(#48, vérifié en prod). Émission viaPOST /v1/licenses/generate(clé admin de Coolify déposée en~/.maximus-admin-key, bearer passé par process substitution, clés écrites en 600 hors transcription), puis vérification indépendante : signature Ed25519 contre la clé publique embarquée,exp+iatexigés parstrict_validation(), préfixe/édition cohérents, expiration 2036-09-09. Le détour qui a coûté la séance : le VPS répondaitConnection timed outsans lien de réauth — pas une attente navigateur maispeer's node key has expired(Tailscale, 180 j,last seen 16d), invisible depuis la console admin et sans réparation à distance ; l'IP publique54.39.97.77a le port 22 ouvert mais refuse la clé du ThinkPad, réparé par Max via le Terminal Coolify + Disable key expiry. Le classifier du mode auto bloque toutsshvers le VPS (Tailscale et IP publique) → commandes remises à Max en!. Lecture Logto : 4 comptes, Michel =michelt(04-14), les deux autres sont le second compte de Max et une reliquat de La Suite Booking. Tablelicenses: une seule vraie clé, la Base de Max du 07-19, révoquée (Stripe test + remboursement) — et le client ne voit jamaisis_revokedhors ligne, seul le serveur (activation, cours) la refuse. Release : pré-vol vert (111 Rust, 1181 vitest), 5 manifestes + 2 changelogs + ligneCLAUDE.md0.6.3→0.15.0, tag annoté confirmé par Max avant push (publielatest.jsonaux installations existantes), run 402 en 22 min, 7 artefacts etlatest.jsonvérifiés par script. Étape 10 à dérouler. (ref v0.15.0, #297-#301, #323-#331, #259, #310, #311, #343) - 2026-09-04 : #343 ouverte, #344 mergée — l'audit quotidien rouge depuis 17 jours, corrigé (
main5154c52). Découvert au/warmup:audit.ymléchouait tous les matins depuis le 2026-08-19 (runs 375→391), dernier vert le 08-18, aucun push surmaindans l'intervalle. Une seule vulnérabilité : RUSTSEC-2026-0258 / GHSA-q83h-524g-xf6h,h20.4.13, datée du 08-17 — DoS de sévérité faible, les frames HTTP/2DATAvides sont acceptées et mises en file sans aucune limite, donc sur un flux non drainé la mémoire croît sans borne et la longueur peut déborder en panique. Le délai 08-17→08-19 est la propagation dans l'advisory-db. Ces 17 jours sont le mode d'échec que l'ADR 0018 visait à empêcher — « alarme permanente = alarme ignorée » — cette fois par le haut : le gate criait, personne ne regardait. Atteignabilité par triple explicite, jamais--target all:h2est sur les deux cibles livrées par deux chemins,hyper←hyper-rustls←reqwest 0.13.2←tauri-plugin-updater(le chemin de la mise à jour automatique) ethyper-tls←reqwest 0.12.28déclaré directement enCargo.toml:43(licence, auth, cours, rétroaction — 4 modules). Correctif publié + crate atteignable = règle 1 de.cargo/audit.toml, donc corrigé et non suppressé ; ni le fichier de suppressions ni le garde-fou decheck-rust.ymlne bougent. Le piège, et la raison d'un diff de 2 lignes :cargo update -p h2donne bien 0.4.19 mais réaiguille au passage 7 pointeurswindows-sys(colored,dirs-sys,errno,rustix,rustls-platform-verifier,tempfile,winapi-util) de0.61.2vers 0.48/0.52/0.59 — exactement ce que #321 avait défait délibérément le 08-15. Laisser passer aurait régressé cette décision en silence, 3 semaines après. Le lock a donc été restauré et seule la stropheh2appliquée (version + checksum), après avoir retracé depuis la source —git show v0.14.0:src-tauri/Cargo.lock, les 7 crates comparés un à un — que0.61.2est bien l'état du tag, donc la seule configurationwindows-sysdont on ait la preuve qu'elle compile sous Windows ; relayer la note de #321 n'aurait pas suffi.cargo metadata --lockedaccepte le résultat sans le réécrire./pr-reviewAPPROVE et a trouvé ce que j'avais manqué : une strophe écrite à la main ne garantit plus ni checksum ni liste de dépendances, eth20.4.19 resserre sa bornehttpde^1à^1.1— le genre de changement qui invalide un lock épinglé ; satisfait ici (http 1.4.0), checksum authentifié parsha256sumdu.crate, et nuance utile qu'il pose :cargo metadata --lockedprouve le graphe, pas le checksum — c'est leDownloaded→Compilingdu run 392 qui le prouve. Preuves plutôt que déductions : contrôle sur l'ancien lock (exit 1 sur RUSTSEC-2026-0258) montrant que l'outil local reproduit le rouge de la CI ; log de la CI lu ligne à ligne parce que l'étapecargo auditdecheck-rustestcontinue-on-error: trueet qu'un job vert n'y prouve rien ; garde-fou tracé (Suppression guard: 2 checks, exit 0) ; 111 Rust ; etworkflow_dispatchd'audit.ymlsurmainaprès merge (run 393,Job succeeded) plutôt que d'inférer le vert du run de PR. Deux acquis au passage : (1) le globsrc-tauri/**est enfin prouvé — cette PR est le cas test isolé attendu depuis #232 (lock + 2.mdseulement) :check-rusta tourné,check-frontendcorrectement sauté par sa denylist ; le suivi peut être fermé ; (2) #345 ouverte — la contraintewindows-sys 0.61.2en est à sa 3e rencontre (#310 l'introduit, #321 la défait, #344 l'évite à la main) et ne vit dans aucun ADR, aucun commentaire deCargo.toml, aucun garde CI : seulement 3 lignes de prose de STATE.md et un message de commit, donc le prochaincargo updatela régressera en silence. Merge via l'API Forgejo (rebase ff, SHA inchangé) plutôt qu'en local, ce qui évite la réconciliation manuelle des PRs. Aucune migration DB (v1→v17). (ref #343, #345, PR #344) - 2026-08-15 : #321 + #322 mergées ff-only (
main97d376b) — deux PRs de 18 jours, deux revues du 08-14 jamais traitées, et une revue qui se corrige elle-même. Passes de re-vérification lancées depuis le main loop (jamais en sous-agent, feedback-pr-review-subagent-forks) et séquencées plutôt que parallélisées — deux revues concurrentes partagent le même chemin de scratch et s'écrasent entre le Write et le POST (gotcha du chantier import). Les deux passes ont retracé les claims depuis la source au lieu de relayer, et les deux ont trouvé une erreur dans la revue qu'elles re-vérifiaient. (1) Sur #321, la revue du 08-14 lisait les 7 réaiguillageswindows-syscomme une dérive de re-résolution et demandait un build Windows avant de tagger : le sens est l'inverse — #310 les avait fait descendre de0.61.2vers 0.48/0.52/0.59, cette PR les remonte à0.61.2, l'état exact du tagv0.14.0(git show v0.14.0:src-tauri/Cargo.lock, crate par crate). v0.14.0 taggée le 07-18, #310 mergée le 07-27 → le build NSIS de v0.14.0 a réellement compilé contre0.61.2. La configuration non prouvée sous Windows était celle demain; la PR réduit le risque. (2) Sur #322, la revue du 08-14 annonçait « trois » déclencheurs et mappaitUpdateCard.tsx:201à la page d'erreur — faux dans les deux sens ::201est le retry de l'étaterrorde la carte, etErrorPage.tsx:82est un 4e site distinct qui détecte seulement (handleCheckUpdates'arrête àsetUpdateStatus("available")). Appliquer « écrire trois » aurait échangé une inexactitude contre une autre. Le blocage de #322 était réel et non traité : la checklist demandait de cocherreadyToInstallavant l'invite d'authentification, alors quedownload_and_installfaitdownload(...).await?puisinstall(bytes)dans le même appel (updater.rs:723-729), queFinishedest un no-op côté UI (useUpdater.ts:119-121) et queREADY_TO_INSTALLn'est dispatché qu'après le retour dedpkg -i— l'invite pkexec s'ouvre donc par-dessus « Téléchargement en cours… ». Conséquence exacte de l'ordre des cases : sans agent polkit l'app se fige endownloading, le testeur guette un blocage aprèsreadyToInstall, et rapporte « le téléchargement bloque » au lieu de « pas d'agent polkit » — la page produisait la fausse trace qu'elle existe pour éliminer. Corrigé en97d376bavec 3 mineurs (les 4 déclencheurs, les préfixes/api/v1/packages/(DELETE) vs/api/packages/(PUT) querelease.yml:217-219commente déjà, ordre du changelogSKILL.md) + titre de PR (« step 9 » → « as step 10 »). Merge : rebase des 2 branches (15 commits de retard) en pile linéaire, preuve que le rebase préserve.cargo//.forgejo//Cargo.lockau byte près vs le commit validé par la CI run 338, puis CI re-jouée quand même (runs 370/371 verts, garde-fou exécuté et tracé,cargo audit0 vulnérabilité / 22 warnings autorisés) etcargo test111 passed — le « 106 » de la description de #321 était périmé, le chantier import ayant ajouté 5 tests. Issues #312/#313/#315 auto-fermées parResolves, PRs fermées à la main (Forgejo ne détecte pas un merge local comme merged), branches supprimées. #314 trouvée caduque au passage :audit.ymltire bien tous les jours à 06:00 UTC — 19 runsscheduleverts du 07-28 au 08-15 — et sonworkflow_dispatchde référence datait du 07-27 à 23:56Z, soit ~6 h avant le premier tir programmé possible. L'issue a été écrite avant que le scheduler ait pu s'exprimer, pas après l'avoir vu échouer. (ref #312, #313, #314, #315, PRs #321/#322) - 2026-08-14 : Import CSV — chantier complet livré (10 issues, migration v17). Cycle intégral en une session : revue d'implémentation →
/spec→/review-spec→/plan-run→/autopilot→/pr-review×10 → merge ff-only. Le diagnostic a tenu, mais trois choses ont été trouvées en cours de route que personne n'avait vues. (1) Au re-ancrage Phase 3b :dataExportServicene sérialise niimport_sourcesniimport_config_templateset faitDELETE FROM import_sourcesà la restauration — restaurer une sauvegarde détruisait toutes les configurations d'import, 3e voie de perte du format, indépendante du bug racine (→ #331, +withTransactionque le service n'avait nulle part). (2) Un worker a mesuré que l'exemple « Solde 2024 » de l'issue n'est pas défectueux (parseFloats'arrête sur leS) et a ajouté la fixture qui l'est vraiment. (3) Un autre a mesuré que chez RBCCAD$et unCheque Numberpresque vide sont sparse-complementary : le générique les apparie et une ligne de chèque s'importe à −234,05 au lieu de −6,95 — d'où l'override par signature. Correction de la revue initiale :parseFrenchAmount("50,00-")ne rend pas 50 mais 5000 —"100,00 CAD"→ 10000,"1 234,56 CR"→ 123456 : erreur de facteur 100 qui passaitisNaNet comptait donc la ligne comme VALIDE. Le ×100 atteignait aussi l'import de titres #245. Durcissement global assumé : un relevé à suffixe de devise produit désormais des lignes en erreur plutôt que des valeurs fausses./pr-reviewa bloqué 3 des 10 PRs, tous fondés : (a) #336 — la règle débit/crédit testaitisNaN(debit) && isNaN(credit), donc une cellule illisible à côté du remplissage0,00calculait 0 − 0 = 0 et importait en silence, le bug même que la PR fermait, une cellule plus loin (rejoué sur sa propre fixture : 6 transactions à 0,00 $, 0 erreur) ; (b) #337 —detectDescriptionColumnretournait la colonne préférée sans veto par les données, et le mot-clétransactioncapturait la colonne DEBIT/CREDIT de Tangerine, tuant la catégorisation ; (c) #340 — régression réelle : la signature Desjardins est 4 libellés génériques appariés par sous-ensemble, et une signature court-circuitait le scan sparse-complementary → un fichierDate;Description;Débit;Crédit;Montant;Soldelu correctement AVANT devenaitpositive_expenseAPRÈS, chaque dépôt en dépense. Corrigés en 3 commits sur la tête de pile (484c4be,ef7de3c,37b832e), chacun vérifié par mutation — et cette vérification a révélé qu'un de mes propres tests passait la mutation (le fix sur les signatures génériques empêchait la branche gardée d'être atteinte) : il a fallu un fichier reconnu par une signature légitime pour l'exercer. Leçon : un garde qui ne peut pas échouer ne garde rien, y compris quand c'est soi qui l'écrit. Incident run : le worker #331 tué par une limite de session pendant sa validation — travail récupéré depuis son worktree et revalidé de zéro (le rationale de sa PR est une reconstruction, signalé comme tel). Gotcha parallélisation : deux agents de revue partageaientscratchpad/review.md, l'un a écrasé l'autre entre Write et POST → donner un chemin de scratch unique par agent. Suivis ouverts : refus du format à indicateur borné àamountCol ± 1; dérives de comptes préexistantes dansCLAUDE.md/architecture.md(mesurées et documentées, non corrigées). (ref #323-#332, PRs #333-#342) - 2026-07-27 : #310 + #311 mergées —
cargo audit9→0,npm audit3→2 acceptées (maina14258b, ff-only ; PRs #316 → #318 stackées, CI verte des deux côtés). Parties d'un/analyse-vulnerabilite: rapport Défenseur écarté comme source (VPS injoignable — SSH Tailscale en attente d'auth navigateur, aucun lien imprimé ; rapport local du 2026-05-06, 82 jours) → vérité live =cargo audit 0.22.2+npm auditsurmain, advisory-db0bfde9d6. Le triage d'origine de #310 révisé sur un point :quick-xml(2× 7.5 high) n'est compilé sur aucune des deux cibles livrées —cargo tree -ivide surx86_64-pc-windows-msvcetx86_64-unknown-linux-gnu, présent seulement surx86_64-apple-darwinviaplist←tauri(dép. conditionnelle Apple). Il n'y avait donc rien à attendre de Tauri, contrairement au « probablement bloqué en amont » du corps. Méthode retenue : tracer par triple explicite, jamais--target all(qui répond « présent » pour des deps Apple jamais compilées ici) — mémoire reference-cargo-audit-reachability-par-cible. 6 advisories atteignables corrigées parcargo update -p rustls-webpki -p tar(0.103.9→0.103.13 couvrant les 4, 0.4.44→0.4.46) sans toucherCargo.toml; dérive de lock expliquée et bornée (7 pointeurswindows-sysrepointés vers des versions déjà présentes, 666 paquets avant/après, deps Windows-only → no-op sur Linux ;--precisedonne le même résultat, c'est la re-résolution de cargo 1.94.1). Les 3 restantes →.cargo/audit.tomlversionné, lu par les deux workflows sans qu'aucun ne le sache. Le plan checker a levé un MAJOR fondé : une liste de suppressions sans déclencheur de retrait inverse le problème de l'alarme (l'audit resterait vert siquick-xmlredevenait atteignable) — d'où 3 règles en ADR 0018 : admission sur preuve par cible livrée ou absence de correctif ; clé par ID d'advisory jamais par crate (une nouvelle advisory sur le même crate repasse au rouge) ; garde-fou bloquant danscheck-rust.yml, placé aprèscargo check(index chaud) avec--locked, testant le code de sortie séparément de la sortie (un crate absent et uncargo treeen panne impriment tous deux du vide) + canaritar. Le pendant — suppression devenue inutile — est #312. Leçon du 1er run CI : le garde était muet en cas de succès, donc indiscernable dans les logs d'une étape non exécutée — exactement le mode d'échec silencieux que la PR combat (même famille que le « globsrc-tauri/**non prouvé » de #232) → il logge maintenant chaque vérification (Suppression guard: 4 checks, exit 0, visible run 336). Preuve que.cargo/audit.tomlest bien lu en conteneur : leworkflow_dispatchdeaudit.ymlsur la branche est vert, alors que le bump seul n'aurait éliminé que 6 des 9. npm (#311) :postcss8.5.13→8.5.23 sans override —vitedéclare^8.5.3, seul le lock était périmé (≠ cas #241 où le parent pinnait) ;nanoidsuit dans sa borne ; CSS émis identique octet pour octet (postcss est le pipeline CSS, un build vert n'aurait prouvé que la compilation) ;react-routeraccepté — advisory mode RSC, orApp.tsx:109monte unBrowserRouterclient-only, etreact-router-domest figé à 7.18.1 (v8 a fusionné le paquet dansreact-router) donc le « fix » npm est un downgrade en 7.11.0 → sortir de la plage = migration, pas bump (#317, titre portantreact-routerpour la dédup par sous-chaîne de/analyse-vulnerabilite). Asymétrie à connaître : aucunnpm auditen CI, donc rien ne rougit côté front — écrit dansarchitecture.md+CLAUDE.mdpour que les 2 high permanents ne se lisent pas comme une régression. Gotchas Forgejo relevés : logs de job uniquement via l'URL web (l'API v1 répond 404 partout) ethead_branch=#<PR>sur les runspull_request→ reference-forgejo-ci-logs-et-head-branch. 106 Rust + 871 vitest + builds verts. Aucune migration DB (v1→v16). (ref #310, #311, PRs #316/#318, #312-#315, #317) - 2026-07-27 : #232 mergée — CI split, caches morts retirés,
cargo-auditpré-buildé (main7779f7d, PR #309 rebase, milestonespec-ci-build-optimization3/4)./analyzea confirmé la prémisse mais révisé le cadrage sur 3 points mesurés : (1) les 2 jobs ne tournent pas en parallèle — runner à capacité 1,frontenddémarre à la seconde oùrustfinit sur tous les runs de l'historique → chaque PR payait rust+frontend ≈ 24,5 min ; (2) 3 des 40 derniers commits first-parent touchentsrc-tauri/, dont 2chore: release(push surmain, ne déclenche pas la CI) → le path-filter est le gain dominant, pas un effet de bord ; (3) le risque « required check skippé » est nul (enable_status_check: falsesur la protection demain, vérifié API). Chrono re-mesuré du run 326 : 12m15s de gaspillage sur 21m44 — savetarget/6m11 + save registry 43s (reserveCache failed, cause #234) +cargo install cargo-audit4m41 + restores ~40s ; nouveau vs baseline #231 : le restore timeout aussi désormais (getCacheEntry failed), le 30 juin n'avait qu'un MISS propre. Résultat : rust 21m44 → 8m55 (−59 %), frontend 2m28 → 1m43, PR frontend-only 24m12 → 1m43 (−93 %). Trou de couverture fermé :branches: [main]ne matchait pas une PR stackée → #305-#308 (pile feature-gating) n'ont eu aucune CI, seules #303/#304 (basemain) ont tourné ; plus aucun filtrebranches:. 2 écarts assumés vs le corps d'origine, tranchés sur mesure : cache npm retiré aussi (23s/run) et denylist pour le frontend (job à 2,5 min → le faire tourner pour rien est bon marché, ne PAS le faire tourner silencieusement ne l'est pas ; le job cher garde un allowlist strict)./pr-reviewAPPROVE — a récupéré les logs CI plutôt que juger le diff, et levé que l'overridePATHjob-level aurait pu masquer le binaire d'install-action(vérifié : audit bien exécuté) ; 2 des 6 suggestions appliquées (.claude/**au denylist, refcheck.ymlpérimée dans le skillrelease). Skip prouvé empiriquement : le 2e push (nisrc-tauri/**nicheck-rust.yml) n'a pas déclenchécheck-rust. Reste non prouvé — le globsrc-tauri/**lui-même (seule l'entrée chemin-exact a matché) : mode d'échec silencieux sur la PR Rust (1/40, nicargo checknicargo test) → #310 (cargo updatetouchantCargo.lockseul) sera le test isolé, à surveiller. Le retrait du|| truea découvert 9 advisories RUSTSEC réelles (préexistantes, seulement masquées) → #310 ouverte :rustls-webpki×4 +tar×2 atteignables viatauri-plugin-updater/reqwest(correctifs patch),quick-xml×2 bloqué en amont parplist←tauri(bump majeur),rsasans correctif publié mais seul parentsqlx-mysql— absent de tout arbre de compilation (cargo tree -i rsa --target allvide, projet SQLite) donc vraisemblablement inatteignable. Conséquence :audit.yml(quotidien, bloquant par choix) sera rouge dès son 1er run tant que #310 n'est pas traitée — alarme permanente = alarme ignorée, donc à lander tôt. Aucune migration DB (v1→v16). (ref #232, PR #309, #310) - 2026-07-21 : Milestone
planned-2026-07-19-feature-gatinglivrée 6/6 et fermée — le gating par tier est surmain([Unreleased], candidate v0.15.0). Reprise du run/autopilotinterrompu le 19 au soir (mort après la PR #303 ; worker #298 tué sans commit — 2 worktrees + branche vide nettoyés). Séquence :/pr-review#303 REQUEST_CHANGES (le retry backoff CWE-703 s'armait aussi sur un échec desubmitKey→ l'auto-refresh effaçait le message « clé invalide » ~1 s après ; etstatus: "error"persistant aurait laisséready=falseà jamais pour les futurs RequireFeature) → fixb9e13b5: validation de clé orthogonale au lifecycle de chargement (validationErrordédié,statusintouché, reducer exporté + 6 tests) → APPROVE → merge API. Relance/autopiloten session : 5 workers séquentiels en pile linéaire (#302 dépend de tout ; CHANGELOG centralisé dans #302 → zéro conflit inter-maillons) → PRs #304-#308, 0 needs-clarification, 0 blocked (rapport)./pr-review×5 parallèles → APPROVE ×5 ; seule retouche user-facing : coquille FRdocs.editions(« tout la » → « tout de la »,6de9617). Merge : tip cumulé validé (871 vitest + build + cargo 106) puis ff-only → pushmainrefusé : branche protégée (whitelistmaximus, règle du 2026-03-07, jamais rencontrée avant) — cause réelle :~/.git-credentialsporte 3 identités Forgejo etdefenseur-auto-boten 1re position shadowmaximus(git prend la 1re entrée du host ; les pushes de branches passaient, seulemainwhitelistée refusait). Fix scopé :git remote set-url origin https://maximus@…(ne PAS réordonner le fichier partagé — les agents defenseurs s'authentifient par lui). Push OK → 5 issues auto-fermées (Resolves #N), 5 PRs fermées + commentées, milestone 6/6 fermée, branches supprimées (+ purge de 7 branchesworktree-agent-*du harness ; le « résiduorigin/issue-259» signalé au warmup était un tracking ref périmé faute defetch --prune— la branche remote avait bien été supprimée le 19). Décisions workers notables : tuilesProfileSelectionPagenon verrouillées (page atteinte seulement sans profil actif résoluble → un verrou heuristique risquerait un soft-lock hors de tous les profils) ; dev-override en tête de résolution (sinon un compte Premium actif masqueraitSR_DEV_EDITION) ;#[serde(default)]préexistant surfeatures= les licences Base déjà émises sans le champ ne régressent pas. Gotcha process : un fork/pr-reviewlancé pendant que la CI estpendingse termine « en attente du monitor CI » sans jamais poster (rien ne survit au fork) → monitorer la CI depuis le main loop, relancer le skill après le vert. Post-merge non bloquant : extractionLockBadge/UpsellPanelsi une 3e surface apparaît ; label PIN « (annuler) » (bug cosmétique préexistant) ; job CI ponctuel--features dev-override. ADR 0017 Accepted. Aucune migration DB (v1→v16). (ref planned-2026-07-19-feature-gating, #297-#302, PRs #303-#308) - 2026-07-19 : Chantier gating par tier planifié via
/spec→ milestonespec-feature-gating(#297-#302). Origine : réflexion monétisation de Max (« changer le périmètre des abonnements »). Matrice tranchée : Free = Dashboard/Import/Transactions/Catégories/rapport Tendance/Export chiffré/Changelog (mono-profil) ; Base = + les 4 autres rapports/Budget/multi-profils/auto-update ; Premium = + Bilan complet (patrimoine + cours). Admin (Max) = licence Premium auto-émise (pas de 4e édition). Décisions clés : upsell verrouillé (pas masqué) ; durcissement sec non destructif (blocage d'accès, données conservées, récupérables par upgrade) ; entitlements statique + overridefeatures[]; enforcement UI-only (soft-paywall GPL assumé, seul le gate cours reste dur/server-enforced) ; #271 absorbé (auto-update Base+, fermé superseded). Re-ancrage Phase 3b : socle déjà là (3 éditions Ed25519,current_edition,check_entitlement) maisis_feature_allowedignorefeatures[]+useLicenseper-appel → LicenseProvider requis pour unuseEntitlementsync ; rapports = routes distinctes (gate par route) ; #271 = 1 ligne. 6 issues (socle→garde UI→routes/sidebar→multi-profils→#271 Rust→docs), specs force-add (gitignorées, précédent #295), aucune migration DB./review-spec3 experts → verdict 🟡, tout corrigé dans le plan : 2 🔴 (navreportsgaté à tort alors que hub Free ;advanced-reportsRust mort contreditreports-advanced) + 6 🟡 (overridefeatures[]fail-closed en Free CWE-863 : une clé copiée downgrade free mais expose ses features signées ;SR_DEV_EDITIONderrière une Cargo featuredev-overridepasdebug_assertionsCWE-489 ; LicenseProvider récup. d'erreur CWE-703 ;useEntitlement→{allowed,ready}anti-flash ;useIsPremium.testà migrer ; gate création profil dansProfileFormModal, point unique). Ajustements tranché Base par Max. Re-homéeplanned-2026-07-19-feature-gating(/plan-runStep 0 : bodies rendus auto-suffisants ; résiduel drainé — CTA « Obtenir » désactivé + « bientôt », dev-override gardé). Prête/autopilot. (ref planned-2026-07-19-feature-gating, #297-#302) - 2026-07-18 : Epic #260 « rapports uniformes » fermée + #259 livrée en PR #296 (review). #260 fermée après
/analyze: 4/5 rapports pleinement conformes (income-statement + filtres partagés + collapse par-profil) ; les 3 écarts restants vs le texte de l'epic sont des décisions de conception assumées, entérinés par Max — (1) lignes vides budget non masquées (spec décision 6 : grille = surface d'édition) ; (2) collapse budget replié « comme partout » (#289/ADR 0016 inverse le « sauf budget » de l'epic) ; (3) dashboard convergé sur le modèle Cartes plutôt qu'une table income-statement hiérarchique (décision 5). Correction STATE : les entrées 07-08/07-11 « reste de #260 = #259 » sont fausses — #259 est une migration de taxonomie sans lien avec l'epic (recadrée 07-12), et les vraies déviations #260 n'y étaient pas tracées. #259 (fusion des catégories custom) livrée en PR #296 (issue-259-merge-custom-categories) : bloc préservé deStepSimulaterendu enMappingRow, reducerRESOLVE_ROWrésout rows+preserved (unresolvedcompté sur seed only → ne bloque pas « Suivant »), writer via helperisResolvedTarget(fourre-tout créé seulement s'il reste une custom non fusionnée ; customs fusionnées désactivées au lieu d'être re-parentées). Plan-check pass (6 MINOR, 3 intégrés), 836 vitest + build tsc/vite propres, régression parent/enfant custom couverte, aucune migration DB./pr-reviewAPPROVE, mergée (rebase) le 2026-07-19 →main2314a64, #259 fermée, branche supprimée. (ref #260, #259 PR #296)
Blockers actifs
- Aucun blocker externe dur.
spec-monetisationfermée 12/12 (2026-07-08) — #50/#52/#53/#135/#136 livrées, ne sont plus des blockers. - Backlog ouvert (
status:ready, non bloqué) :spec-paiements4 issues ouvertes après l'analyse du 2026-09-13 : #270 (activation /v1 + product + URL localisée, périmètre strict ; un défaut de plus trouvé sur les mêmes lignes : l'enveloppe d'erreur/v1{error:{code,message}}est lue comme une chaîne, donc tout refus serveur s'affiche « Activation failed »), #346 (CTA « Obtenir Base » des UpsellGate + lien d'achat carte MAJ + texte carte Compte, dépend de #270 ; « Obtenir Premium » reste désactivé faute d'offre vendable), #347 (erreurs de licence en anglais brut, clé expirée/révoquée muette, limite de machines jamais affichée), et côté site maximus/la-compagnie-maximus#153 (téléchargement direct Windows depuis la page produit + redirection de l'ancienne URL/simpl-resultat) ; go-live = la-compagnie-maximus#141 + maximus-api#62 ; #271 absorbé par #301, livré ;spec-ci-build-optimization3/4 (#232 livrée le 2026-07-27 ; reste #234 connectivité du serveur de cache runner, qui débloqueraitSwatinem/rust-cache— accès hôte VPS requis). Le suivi « globsrc-tauri/**non prouvé » ouvert avec #232 est levé : la PR #344 est le cas test isolé attendu (Cargo.lock+ 2.mdseulement) —check-rusta bien tourné,check-frontenda bien été sauté par sa denylist. - Suivis des advisories : #312, #313 et #315 fermées le 2026-08-15 (PRs #321/#322 mergées). #314 fermée le 2026-08-15, prémisse infirmée — elle affirmait « zéro run
schedule», oraudit.ymltire tous les jours à 06:00 UTC (19 runsscheduleverts du 07-28 au 08-15, sans trou) ; elle a été écrite le 07-27 vers 23:56Z, ~6 h avant le premier tir programmé possible, et décrivait donc une fenêtre trop courte, pas un scheduler en panne. Ces 19 runs confirment au passage que.cargo/audit.tomlest bien lu en conteneur surmain— l'ADR 0018 ne l'avait vérifié que par unworkflow_dispatchsur une branche. #343 fermée le 2026-09-04 (PR #344) — RUSTSEC-2026-0258 surh2, la seule vulnérabilité qui rougissait l'audit depuis le 08-19 ; l'audit est de nouveau vert surmain, prouvé parworkflow_dispatch(run 393) et non déduit du run de PR. Restent #317 (re-évaluerreact-routerà une migration v8), #319 (déclencheur de retrait pourrsa, désormais seule entrée de.cargo/audit.toml— le garde-fou est passé à 2 vérifications) et #345 (domicilier la contraintewindows-sys 0.61.2— analysée le 09-04 : le corps d'origine, écrit de mémoire en fin de revue, se trompait sur trois points. Le déclencheur n'est pas « n'importe quelcargo update» mais tout bump atteignant le sous-arbre réseau/TLS — mesuré :-p anyhowne bouge rien,-p hyperfait descendre les 7 — soit précisément là où tombent les advisories. #310 avait bien vu la dérive et l'avait jugée inoffensive sur Linux, où elle l'est ; son coût tombe sur le build Windows que personne ne mesurait : 3 versions dewindows-syscompilées contre 5. Aucun risque de correction — les 7 crates déclarent des bornes larges couvrant les deux états. Et cargo annonce « Locking 1 package » en écrivant 8 changements, donc le silence est le sien. Décision : garde-fou CI sur la version + ADR portant la condition de sortie. Le check croisé Windows depuis Linux a été testé et écarté —cc-rss'arrête surlib.exe, il faudraitcargo-xwin). - Nouveau bug ouvert : #320 — les installations
.rpmne reçoivent aucune mise à jour (latest.jsonne porte qu'une entréelinux-x86_64construite depuis le.deb,release.yml:104-122), alors que les.rpmcontinuent d'être publiés en assets. Documenté en §3 dedocs/qa-update-cycle.md(« ne pas tester : cassé, pas seulement non vérifié »). - Suivis ouverts du chantier import (milestone fermée) : le refus du format « montant absolu + indicateur D/C » ne scanne que
amountCol ± 1, donc une colonne D/C placée ailleurs passe encore ; dérives de comptes préexistantes dansCLAUDE.mdetdocs/architecture.md(composants, pages, services,Version actuelle : 0.6.3vs tag v0.14.0) mesurées et documentées en revue, non corrigées. - Étape 10 de la release v0.15.0 consignée le 2026-09-13 — plus un blocker : cycle réel v0.14.0 → v0.15.0 validé par les deux testeurs ; trace rédigée pour le corps de la release (
PATCHà appliquer par Max) ; invite d'authentification Linux non consignée ; #320 reste ouverte (.rpm). Deux restes : (1) le texte de la carte Compte Maximus (« Le compte est requis uniquement pour les fonctionnalités Premium », hérité de v0.7) contredit v0.15.0 où Premium vient de la clé, et a vraisemblablement conduit un testeur à se connecter pour rien (code de vérification Logto reçu par courriel) — reformulation à ouvrir en issue ; (2) le flux Logto exact (connexion par code, mot de passe oublié ou inscription) reste à lire dans le journal d'audit. Position produit : v0.15.0,[Unreleased]vide.