Simpl-Resultat/STATE.md
le king fu 8c55e2670b state: sync after onboarding phase 1 merge (planned-2026-10-03-onboarding-phase1, PRs #377-#383)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 08:54:14 -04:00

51 KiB
Raw Blame History

STATE — Simpl'Résultat

Derniere MAJ : 2026-10-04 (Onboarding phase 1 mergée — pile de 7 PRs #377 → #383 en avance rapide, main 667e058, milestone planned-2026-10-03-onboarding-phase1 fermée 7/7. Produite par /autopilot le 2026-10-03 (19:14-20:29, 0 clarification, 0 blocage), relue par 7 /pr-review indépendants (42 mutations, 39 détectées), 3 suivis appliqués en tête de pile avant merge. Ce que voit l'utilisateur : états vides guidés (dashboard, Import, Transactions, Budget), liste de démarrage à 6 cases calculée depuis la base (Budget cadenassée en Free), bouton « Créer un dossier d'exemple » (2 CSV Desjardins/Tangerine, dates recalées sur les deux derniers mois), guide « Exporter un CSV depuis votre banque » (Desjardins, Tangerine, RBC, BNC, BMO). Suivis non bloquants dans #384 ; #371 (BMO sur compte réel) reste à Max ; [Unreleased] porte les entrées, candidate v0.17.0.)

Précédente MAJ : 2026-10-03 (#234 : Swatinem/rust-cache mergé — PR #375, main f821492, merge API en avance rapide. Une fois la connectivité du cache réparée sur le VPS (règle ufw, port fixe du runner), check-rust passe de 9 min 37 s à froid à 3 min 22 s, et seuls un run hebdomadaire sur main et workflow_dispatch écrivent dans le cache partagé ; #234 reste ouverte en status:blocked jusqu'aux vérifications d'après merge, dont le premier run planifié du lundi 2026-10-05.)

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).

v0.16.0 shippée (2026-10-03, tag v0.16.0 → CI release run 451, 7 artefacts + latest.json vérifiés, registre de l'updater en 0.16.0) — première version vendable de bout en bout : « Obtenir Base » ouvre la page produit (#346), le client de licence parle /v1 et nomme chaque refus (#270), l'édition ne dépend que de la clé (#347), l'activation envoie un identifiant dérivé (#350), une clé révoquée retombe en Gratuite après un contrôle au démarrage (#365), et rustls 0.23.45 (#348). [Unreleased] vide. Aucune migration DB nouvelle (v1→v17). À dérouler sur la release publiée : les vérifications manuelles de #346, #350 et #365, et l'étape 10 de /release (cycle de mise à jour réel, Linux et Windows, trace à consigner dans le corps de la release).

Decisions recentes

  • 2026-10-04 : Onboarding phase 1 livrée de bout en bout — /plan-run (2026-10-03) → /autopilot (7 PRs stackées en 1 h 15) → 7 /pr-review → /merge-stack ff (main 667e058). Trois faits d'ancrage avaient réécrit la spec : le seed IPC met 180 mots-clés dans tout profil neuf et le wizard catégorise à l'import (useImportWizard.ts:1016), donc la case « première règle » devient « une transaction classée à la main » (is_manually_categorized = 1) et les gabarits portent 7 + 4 lignes hors seed ; aucune commande Rust n'écrivait de fichier, d'où create_sample_import_folders(import_folder, today) avec today venu du webview (chrono compilé sans clock) ; une seule banque (Tangerine) a une page officielle d'export, chaque bloc du guide porte son statut de vérification. /review-spec avait trouvé 2 critiques (le corpus CsvFixtureName gelé à 15 par 6 boucles ; DocsContent non testable par appel direct) et corrigé une erreur du plan (33/40, pas 35/40, mesuré avec la vraie regex). Run autopilot : pile linéaire honorée malgré un graphe à 3 maillons parallélisables, pour un merge ff sans conflit fr.json/en.json ; un seul report inter-maillons (réalignement JSON du « Démarrage rapide », #355 → #357). Revues : 3 correctifs en tête de pile — statut BMO sans provenance interne (le corps d'issue avait fui dans l'UI), garde budgetReady sur la liste compacte (fenêtre 5/5 → 5/6 au boot), test formatLocalDate aveugle sous TZ=UTC (mutation toISOString vérifiée rouge). Merge : tip éprouvé en worktree npm ci propre (sonde rouge rc=1, tsc, 1440 vitest, build, 192 cargo), API ff du bas vers le haut avec recible de chaque enfant sur main → 7 PRs merged, pas closed. Constat de process : les workers autopilot posent status:approved eux-mêmes après auto-revue, le label ne prouve plus une revue indépendante (mémoire, #384). Max est client BMO, banque sans signature : la validation sur compte réel vit dans #371, la signature d'import BMO est reportée. Aucune migration DB (v1→v17). (ref #351-#357, PRs #377-#383, #384, #371)
  • 2026-10-03 : #234 mergée (PR #375, main f821492) — le cache Rust de la CI fonctionne, check-rust tombe de 9 min 37 s à 3 min 22 s, et la panne tenait au VPS, pas à l'adresse cache.host que visait l'issue. Diagnostic par la PR #374, fermée sans merge : ufw n'ouvrait l'hôte aux conteneurs que depuis 172.16.0.0/12, alors que Docker (daemon.json de Coolify) alloue ses réseaux, ceux des jobs compris, dans 10.0.0.0/8 ; et le runner, sans fichier de config, tirait le port de son proxy de cache au hasard à chaque démarrage. Correctif appliqué par Max sur le VPS : runner-config.yml avec cache.proxy_port: 28100, passé en --config par un fichier complémentaire systemd, règle ufw de 10.0.0.0/8 vers ce port, ports du cache vérifiés fermés depuis Internet ; consigné avec son retour arrière dans forgejo-ops.md (la-compagnie-maximus 66787ea). Le design vient d'un constat : le runner v12.7.0 isole ce qu'écrit un run pull_request sous la référence de la PR, et les autres PR ne retombent que sur les entrées non isolées. Décisions de Max : D1 Swatinem/rust-cache@v2 sur les PR, avec un run hebdomadaire sur main (lundi 06:30 UTC) et workflow_dispatch pour seuls écrivains du cache partagé ; D2 pas de cache npm (npm ci prend 9 s). Mesuré : run 459 à froid, sauvegarde d'environ 999 Mo ; run 461 en full match: true, cargo check en 9,9 s. /pr-review APPROVE, trois suggestions appliquées dans f821492 sans nouvelle relecture : le ménage du runner garde une entrée par clé, toutes portées confondues (une PR qui sauvegarde sous la clé partagée l'évince jusqu'au lundi) ; la clé hache aussi les variables CARGO*/RUST* du job et ignore la version de l'app ; un 404 un lundi après une semaine sans requête est une course connue du runner (forgejo/runner#1741), pas un retour de #234. Après le merge, check-rust lancé sur main par workflow_dispatch (run 464) : même clé que l'entrée de la PR, No cache found. (isolation prouvée), puis Cache saved successfully en portée partagée. #234 reste ouverte en status:blocked jusqu'à la restauration de cette entrée par la prochaine PR Rust, un premier run planifié vert le lundi 2026-10-05 et la taille de actcache relevée par Max ; fermeture à la main ensuite. Suite ouverte : #376 (Node 20 en fin de vie dans check-frontend et audit). Hors dépôt, /warmup doit filtrer les échecs check-rust sur l'événement schedule, et forgejo-ops.md décrire l'isolation des PR et l'éviction par clé. Aucune migration DB (v1→v17), pas d'entrée CHANGELOG (CI seule), pas de release. (ref #234, PR #375, #374, #376)
  • 2026-10-03 : v0.16.0 taggée et publiée (56fc612, run 451 vert) — sur le constat de Max qu'il n'y a pas d'autres utilisateurs que les testeurs, les vérifications manuelles passent après la publication. Aucune PR ouverte à merger. Pré-vol du skill sur le tip 6625d25 : build, vitest 1348, cargo check sans avertissement, 187 Rust ; aucun worktree résiduel ; les deux testeurs ont une clé Premium valide depuis la v0.15.0 et rien ne touche FEATURE_TIERS. Version mineure (fonctionnalités ajoutées) : 5 manifestes, 2 changelogs et la ligne de CLAUDE.md, Cargo.lock édité à la main et accepté par cargo metadata --locked. Tag annoté confirmé par Max avant le push, poussé seul (jamais --tags). Run 451 en 22 min ; verify-release.sh vert ; l'endpoint de tauri.conf.json (registre de paquets) sert la 0.16.0, au-delà de la copie attachée à la release que lit le script. Correction faite en route : j'avais écrit que les installations Base+ recevraient la mise à jour d'elles-mêmes ; docs/qa-update-cycle.md et le code disent l'inverse, aucune vérification automatique, quatre déclencheurs manuels. Personne ne reçoit donc la release sans cliquer « Vérifier les mises à jour ». Étape 10 due (rustls 0.23.45 est sur le chemin TLS de l'updater) : Linux par Max, enchaînée avec #350 sur le poste activé en v0.15.0 (à faire avant tout changement de clé, qui efface le jeton) ; Windows par Michel. Trace à consigner dans le corps de la release. En parallèle : la-compagnie-maximus#184 (politique de confidentialité) par Max ; #370 ouverte (retrait de la clé, status:triage). Tests de Max le jour même : mise à jour Linux v0.15.0 → v0.16.0 réussie, trace consignée dans le corps de la release (invite d'authentification apparue, type non noté ; Windows à dérouler par Michel) ; #365 vérifiée avec la clé réellement révoquée et fermée. Anomalie relevée : saisie en cours de session, cette clé gardait l'édition Base jusqu'au clic sur « Activer », comportement livré (contrôle au démarrage seulement) → #373 propose le contrôle dès la saisie. La vérification de #346, qui demande une édition Gratuite propre, est reportée à #370 (instructions ajoutées à son corps) ; #372 ouverte (la liste complète des versions repousse le Journal en bas de Paramètres → Systèmes). Aucune migration DB (v1→v17). (ref v0.16.0, #270, #346, #347, #348, #350, #365, #370, #372, #373)
  • 2026-10-03 : #365 mergée (PR #368, main b107cf4) — une clé révoquée retombe en Gratuite, et l'app envoie pour cela sa première requête non sollicitée. Constat du go-live (09-30) : une licence remboursée, révoquée côté serveur, restait pleinement fonctionnelle, parce que l'édition se résout hors ligne depuis la clé et que le drapeau de #347 (D13) ne vivait que la session. /analyze (10-01) : D1 contrôle au démarrage, au plus une tentative par 24 h ; D2 révocation nommée là où le verrou apparaît, sans bandeau global ; D3 fail-open ; D4 un seul état. Livré : license.check à côté de la clé ({key_sha256, attempted_at, revoked}, condensé SHA-256, jamais la clé) ; entitlements_from_dir rend ("free", []) pour une clé révoquée, auto-update compris ; check_license_revocation (POST /v1/licenses/verify, produit et clé seulement, rien sans clé valide, tentative écrite avant l'appel, seul valid:true lève et seul « révoquée » pose) ; l'activation, la liste et la désactivation enregistrent un license_revoked sous la clé envoyée ; LicenseContext lit info.revoked (fin de markRevoked), lance le contrôle une fois par démarrage, écrit son issue dans le Journal et ne recharge que si l'état change ; une clé révoquée ressaisie reste en Gratuite ; carte Licence, UpsellGate et carte Mises à jour nomment la révocation ; amendement #365 de l'ADR 0017. Écart tranché par Max le 10-02, après trois échecs du plan checker : la route des cours nomme le 403 license_revoked sans l'enregistrer, parce que /v1/prices juge la licence du jeton d'activation, que le client ne relie pas à la clé, et que la v0.15.0 laissait le jeton d'une clé précédente à côté d'une nouvelle (git show v0.15.0) : l'enregistrer aurait rétrogradé une clé valide. Ce jeton périmé est suivi en #367. Preuves : 33 mutations Rust et 27 front de l'auteur, toutes rouges ; la première passe en avait laissé 5 vertes pour 4 manques, corrigés dans 4225dad, dont la tentative écrite après la requête (un test lit maintenant le fichier d'état depuis le serveur mockito, pendant que la requête est en vol) et l'intervalle de 24 h que suivaient les lignes construites depuis la constante (mesuré sur e24c30f : 184 verts avec l'intervalle à 1 h). Contrat sondé en production : verify d'une clé factice répond 200 {"valid":false,"reason":"not_found"}. /pr-review APPROVE (37 mutations du relecteur, 35 rouges). Appliqué dans b107cf4 : le test « une tentative non inscriptible n'envoie rien » (mutation de la revue : 0 rouge sur 185, puis 1 sur 187) et celui du verdict non inscriptible ; storage reformulé, puisqu'il couvre aussi un verdict non écrit. Décisions de Max le 10-03 : une clé révoquée reste contrôlée une fois par 24 h tant qu'elle est enregistrée, et c'est déclaré dans le guide (aucune interface ne retire une clé : seul l'enregistrement d'une autre l'arrête) ; la carte Mises à jour nomme la révocation, la page d'erreur non. Rust 168 → 187, vitest 1314 → 1348, CI lue dans les logs (runs 446/447 puis 449/450). Hors PR : la-compagnie-maximus#184 corrigée, son commentaire du 10-02 disant à tort que l'édition Gratuite ne fait jamais cette requête. Non gardé par les tests : l'effet du fournisseur React qui lance le contrôle, l'enregistrement de la commande dans lib.rs et la réponse réelle du serveur pour une clé révoquée, d'où la vérification manuelle. Acquis : la fermeture match_request de mockito tourne pendant que la requête est en vol, ce qui prouve un ordre « écrit avant la requête » ; une suite Rust rouge imprime error: test failed, donc seul could not compile signale une erreur de compilation. Aucune migration DB (v1→v17). (ref #365, PR #368, #367, la-compagnie-maximus#184)
  • 2026-10-01 : #350 mergée (PR #366, main 0a9c9f7) — l'activation envoie un identifiant dérivé, et l'issue reste ouverte pour une vérification manuelle due avant le tag. /warmup (audit quotidien vert, 10 runs d'affilée), puis /analyze 350 : le corps du 09-24 n'avait jamais été édité (sa timeline ne porte que des références), et #270, #347 et le go-live l'avaient périmé sur quatre points — « avant go-live », « retomberaient en Gratuite » (faux depuis D12), current_entitlements qui ne compare plus rien au jeton, get_machine_id sans appelant. Trois sites manquaient : la suppression du jeton dans deactivate_machine_impl, le marqueur « Cette machine » de LicenseCard, et le Bearer des cours, qui envoie un jeton antérieur avec son claim brut. Décisions de Max : D1 une machine déjà activée garde son activation (jeton brut accepté) ; D2 le critère « aucun identifiant brut ne quitte le poste » est borné aux activations faites avec la version corrigée ; D3 le client ne suppose rien du serveur. Deux suites ouvertes dans leurs dépôts : maximus-api#82 (identifiants bruts déjà stockés, lignes désactivées comprises) et la-compagnie-maximus#184 (la politique de confidentialité ne déclare aucune donnée d'activation). Livré : derive_machine_id (HMAC-SHA256 sous la clé figée com.simpl.resultat/license-machine-id/v1, 64 caractères hexadécimaux, identifiant vide refusé) ; MachineIds (dérivé et brut) ; activation qui dérive juste avant l'appel et exige le dérivé dans le jeton reçu ; is_this_machine calculé côté Rust pour les deux formes ; règle de suppression du jeton à la désactivation ; get_machine_id retirée et ActivationStatus sans identifiant ; ADR 0020. Aucune dépendance ajoutée, Cargo.lock intact. Écarts au corps de l'issue, déclarés dans la PR : la règle de suppression est plus large que la phrase de l'issue (toute ligne de ce poste emporte le jeton, sauf l'ancienne ligne brute quand le jeton vaut pour la ligne dérivée), parce que la règle littérale laissait un jeton expiré sur le disque ; la liste, la désactivation et l'état d'activation n'échouent plus sur un identifiant du système illisible. Plan checker pass au premier tour, ses remarques mineures intégrées : tests épinglés sur les vecteurs en littéraux, « aucun appel » observé contre un mock et non déduit. /pr-review APPROVE, cinq suggestions appliquées dans 0a9c9f7 : la condition de retrait de l'ADR, reprise du corps de l'issue, comptait deux ans depuis la v0.15.0, or une v0.15.0 gratuite ne se met plus à jour et peut encore activer en brut des mois plus tard, donc la borne est la dernière émission d'un jeton brut, connue du serveur seul ; « plus aucune commande ne remet l'identifiant du système au webview » était plus large que le code (la liste porte encore la ligne brute d'une activation antérieure) ; deux lignes de test ; la seconde ligne que crée une réactivation tant que le serveur n'a pas rehaché ses lignes ; la vérification manuelle par la valeur et non par la longueur. Preuves : 17 mutations de l'auteur rouges ; la mutation de la revue restée verte remesurée à 0 rouge sur e5662ad, puis 1 rouge sur 0a9c9f7 ; vecteurs recalculés hors du code ; Rust 151 → 168, vitest 1311 → 1314 ; CI lue dans les logs sur les deux têtes (runs 441 à 444). Non gardé par les tests : le câblage des commandes à l'identifiant réel du poste, d'où la vérification manuelle. Acquis : mockito sert le premier mock correspondant qui attend encore des hits, donc un mock expect(0) posé à côté d'un mock valide ne prouve rien ; un seul mock avec match_request. Merge par l'API en fast-forward-only épinglé sur la tête, branche supprimée. Aucune migration DB (v1→v17). (ref #350, PR #366, maximus-api#82, la-compagnie-maximus#184)
  • 2026-09-30 : #347 mergée (PR #364, ferme aussi #360) — l'édition ne dépend plus que de la clé, et deux revues ont trouvé ce que les tests ne gardaient pas. /analyze (09-29) a revérifié le corps du 09-24 contre main et maximus-api, puis fait trancher deux décisions par Max. D12 : le jeton d'activation ne fait plus jamais retomber en Gratuite. Une clé copiée sans son jeton gardait déjà son édition, donc la règle ne protégeait rien ; elle piégeait la restauration légitime de données (#360) et toute machine activée au bout des 2 ans du jeton. D13 : clé révoquée = drapeau de session. Livré : LicenseError étendu aux erreurs de saisie (invalid_prefix, invalid_signature, malformed, unknown_edition), nommées à la source par store_license_in et read_license_in ; édition résolue depuis la seule license.key (entitlements_from_dir) ; LicenseKeyForm extrait ; compteur n/limite, dates fr-CA/en-CA, « Sans échéance » au-delà de 50 ans ; tests de #363 absorbés. Écart au corps de l'issue : le drapeau révoqué vit dans LicenseContext, parce que le plan checker a vu que le reducer du hook meurt avec la carte. Revue 1 (APPROVE) : 3 mutations vertes (état « Activée » non testé à son point de décision, filtre des erreurs sous Machines, coche verte) et un défaut préexistant, le jeton d'une clé précédente qui survivait à l'enregistrement d'une autre clé (« Activée » sans « Activer », cours refusés au nom de l'ancienne licence). Corrigés dans 3f90635 : activation_status_in, jeton supprimé au changement de clé, carte remontée par licenseIdentity, message invalidSignature qui renvoie d'abord à la copie (jsonwebtoken vérifie la signature avant de décoder la charge, donc une copie abîmée finit souvent en InvalidSignature). Revue 2 (APPROVE) : course activation / nouvelle clé, badge « Activée » d'une clé révoquée, renvoi de l'ADR ; corrigés dans cdf4df8. Preuves : reproduction rouge sur 5d549d6, puis 27 + 8 + 4 mutations rouges ; Rust 128 → 151, vitest 1266 → 1311, CI vérifiée dans les logs à chaque tête. Trouvé en route : le 403 license_revoked des cours s'affiche comme « abonnement premium requis » (consigné sur #365). Merge par l'API en fast-forward-only, milestone planned-2026-09-24-paiements fermée 3/3. Aucune migration DB (v1→v17). (ref #347, #360, #363, PR #364)
  • 2026-09-27 : #346 mergée (PR #362) — « Obtenir Base » ouvre enfin la page produit, et la revue a trouvé que le câblage des boutons n'était gardé par aucun test. Livré par la PR : upsellCtaFor (règle pure : Base achetable, Premium désactivé avec la note « pas encore offert en ligne ») ; PurchaseButton, HaveKeyButton (rendu seulement sous un routeur, via useInRouterContext) et DownloadLatestButton partagés ; UpsellGate suit la règle et ProfileFormModal le réutilise (fin du mode upsell compact) ; carte Mises à jour et page d'erreur en Gratuite ; texte de la carte Compte tranché le 09-24 ; amendement de l'ADR 0017. /pr-review APPROVE, avec un constat principal : la PR supposait que sans jsdom seules les fonctions pures se testent, or renderToStaticMarkup tourne dans l'environnement node de vitest et FilterPanel.test.tsx appelait déjà un composant comme une fonction ; six mutations des composants passaient la suite sans un rouge. Appliqué dans 170b22d : 9 tests de rendu (UpsellCtas.render.test.tsx) ; un logError sur l'échec d'openUrl dans les trois sites qui ouvrent la page produit (initLogCapture ne capte pas unhandledrejection, donc rien n'arrivait dans Journaux) ; un commentaire dans upsellCtaFor (le bouton ne vend que Base, donc rendre Premium achetable sans lui donner le palier ferait vendre Base sur le Bilan) ; la réserve sur la carte Compte dans l'ADR (check_account_edition accorde encore Premium à un abonnement actif, chemin dormant tant qu'aucun abonnement n'est vendu) ; l'étape d'achat placée avant la saisie de la clé dans le guide et les docs intégrées ; trois corrections de CHANGELOG. Preuve : 12 mutations rejouées sur la tête finale dans un worktree jetable (npm ci), 11 rouges avec code de sortie 1. La 12e (ProfileFormModal sans onNavigate={onClose}) demande un clic et reste à vérifier à la main ; LicenseCard n'a pas de test, ses hooks réels empêchent de l'appeler comme une fonction. Merge par l'API en fast-forward-only épinglé sur head_commit_id, #346 fermée par Fixes. Aucune migration DB (v1→v17). (ref #346, PR #362)
  • 2026-09-27 : #270 mergée (PR #359) — le client de licence parle /v1, nomme chaque refus, et trois revues ont trouvé ce que les tests ne gardaient pas. /fix-issue 270 sur le corps réécrit par le /plan-run du 09-24. Plan vérifié par le plan checker en 2 itérations (la 1re : un refus serveur sur cinq sans test). Livré : POST /v1/licenses/{activate,deactivate,verify} avec product et clé rognée dans *_inner ; LicenseError sérialisé {code} comme FetchPriceError, lu dans error.code de l'enveloppe (un 404 n'est jamais « route absente » ; un 404 sans code connu est server) ; verify répond toujours 200, donc {valid:false} devient une erreur codée au lieu de la liste vide qui affichait « Aucune machine activée » pour une clé révoquée ; corps d'un non-2xx lu sans exiger de JSON (page HTML 502 → server), corps 2xx illisible → invalid_response. TS : parseLicenseError + table vers 14 clés license.errors.* (pluriels i18next pour le délai), wrappers typés {ok, value|error}, hook useLicenseMachines à reducer exporté (erreur d'activation distincte de l'erreur de liste, parcours limite de machines en 4 requêtes sous le 5/min/IP du serveur, boutons inactifs pendant une désactivation ou un rechargement), purchaseUrl dans src/shared/. Désactivation idempotente (no_active_activation = fait), écart assumé et documenté. Méthode : chaque défaut d'origine réintroduit sur l'arbre final et compté en rouge (11 mutations) ; puis 3 passes /pr-review, toutes APPROVE, dont les mutations ont trouvé 7 comportements corrects mais non gardés (un 404 lu comme clé introuvable, une table de messages comparée à elle-même, l'idempotence qui avale license_not_found, la relecture du statut après désactivation, machine_name à null, un 2xx illisible, deux courses de boutons) — chacun épinglé puis prouvé rouge. Une puce du CHANGELOG retirée (l'édition ne change pas visiblement après une activation) et une reformulée (l'ancienne URL est redirigée par le site depuis la-compagnie-maximus#153). Hors PR, trouvés en route : maximus-api#70 (réactivation d'une machine déjà désactivée → 500 ; UNIQUE(license_id, machine_id) sans condition sur is_active + INSERT sans onConflict ; reproduit sur Postgres embarqué ; upsert recommandé, sans migration ; ajouté comme prérequis sur maximus-api#62) ; #360 (jeton d'une autre machine → Gratuite, section Machines masquée, ressaisie de clé inopérante ; trois pistes, status:triage). Merge par l'API (rebase ff, main 0c9b8f8). Aucune migration DB (v1→v17). (ref #270, PR #359, #360, maximus-api#70)
  • 2026-09-24 : Vague paiements planifiée via /plan-run spec-paiements — deux milestones planned-2026-09-24-paiements, un défaut de licence trouvé en revue, et des clés de 100 ans. Préambule : /warmup (audit quotidien vert, runs 415/416), puis commit des skills en attente depuis le 09-13 (11e12ab : /issue-license, verify-release.sh, garde « clés testeurs avant le tag » de /release) — le courriel personnel de Michel retiré avant le premier commit : le dépôt Simpl-Resultat est public et l'adresse n'était dans aucun fichier suivi ; celui de Max conservé, déjà présent dans 3 fichiers suivis. Le classifier a refusé de charger purge-secret-forgejo (« Git Destructive ») : scan des lignes ajoutées fait à la main. Étape 0 : spec d'origine dans la-compagnie-maximus (07-11), corps issus de /analyze 270 (09-13) jamais passés en /review-spec, donc toutes les affirmations revérifiées contre le code et la prod. Résultats : verify répond toujours 200 ({valid:false} → le client affiche « Aucune machine activée » pour une clé révoquée) ; un 404 d'activate est un license_not_found métier, pas une route absente ; ErrorPage est hors routeur ; machine_limit est déjà dans le JWT local ; le 429 rate_limit_exceeded n'était traité nulle part ; FetchPriceError est le modèle d'erreur à reproduire. Décisions de Max D1-D8 : #153 dans la vague (seule réparation du bouton « Acheter » des v0.15.0 gratuites, qui ne recevront jamais #270) ; 409 → message + liste dépliée via verify ; note Premium « pas encore offert en ligne » ; texte exact de la carte Compte ; removeLicense retirée ; Windows d'abord sans détection ; #270 établit le contrat des codes d'erreur ; périmètre inchangé. /review-spec (3 experts) → 🔴 : la tâche D2 effaçait le message 409 (loadMachines() commence par setMachineError(null)) et « Aucune machine » restait affichée en erreur ; le repli activateError (« Activation failed » en EN) contredisait #270. Constats chiffrés revérifiés à la source, aucun réfuté. Le défaut de fond : signActivationJwt signe le jeton d'activation pour 2 ans, la clé pour 10, et le client valide le jeton avec strict_validation() → une machine qui a cliqué « Activer » retombe en Gratuite au bout de 2 ans, alors qu'une clé jamais activée garde son édition ; « Activer » ne rappelait pas refresh(). D10 : un jeton expiré mais signé pour cette machine compte comme absent (#347), ce qui corrige aussi les jetons déjà émis. Durée des clés : retirer l'expiration est impossible (tous les binaires installés exigent exp, CWE-613) ; 100 ans passe partout, et les 10 ans de la décision #15 n'étaient que le maximum du schéma de /generate → D9 : 100 ans hors vague (maximus-api#63, et la-compagnie-maximus#157 pour les CGV, dont « mises à jour pendant sa durée » est à reformuler) ; l'état « clé expirée le {date} » est abandonné, « Sans échéance » au-delà de 50 ans. D11 : lien « Télécharger la dernière version » en Gratuite, sans appel réseau. Lot de 9 corrections intégré : hook useLicenseMachines à reducer testé, PurchaseButton partagé, délai de 5 s et repli complet côté site, routes /telecharger/* et repli latest.json retirés, redirection sans langue, etc. Mécanique : milestones créées (simpl-resultat id 87, site id 88) plutôt que renommage de spec-paiements, qui reste le parapluie des trois dépôts ; #350 ouverte (hachage HMAC de l'identifiant machine, avant go-live). Step 9 vérifié sur l'état réel de Forgejo. Gotcha skills : plan-run Step 6bis laisse entendre qu'un seul /autopilot <nom> couvre deux milestones homonymes, mais autopilot refuse le multi-dépôts (SKILL.md:53) → un run par dépôt. Aucune migration DB (v1→v17). (ref #270, #346, #347, #350, la-compagnie-maximus#153/#157, maximus-api#63)
  • 2026-09-21 : RUSTSEC-2026-0285 (rustls) corrigée — l'audit quotidien rouge depuis 7 jours, et la prémisse de #345 restreinte par la mesure. Trouvée au /warmup : audit.yml échouait tous les matins depuis le 2026-09-15 (runs 405→411), dernier vert le 404 du 09-14, sans aucun push sur main — le dernier commit était 97ee3c3 du 09-13. Une seule vulnérabilité : RUSTSEC-2026-0285 / GHSA-2mjx-qc3c-rqvc, rustls 0.23.36, datée du 09-14, CVSS 5.3 medium, vecteur C:L/I:N/A:N. L'advisory est parue après le tir du 09-14 à 06:00 UTC, d'où un premier rouge dès le lendemain — propagation plus rapide que les deux jours de h2. Ce n'est pas un contournement : rustls acceptait des messages de poignée de main TLS 1.3 au mauvais niveau de chiffrement lorsqu'ils suivaient un changement de clé dans le même enregistrement (un EncryptedExtensions en clair collé au ServerHello), là où la RFC 8446 §5.1 impose de fermer la connexion ; la transcription restant authentifiée, un attaquant en position réseau ne peut ni altérer ni compléter une poignée de main. Atteignabilité par triple explicite, jamais --target all : arbre identique sur les deux cibles livrées, mais un seul chemin — tauri-plugin-updater → reqwest 0.13.2, la mise à jour automatique. Plus étroit qu'h2 en #343 : le reqwest 0.12.28 déclaré en Cargo.toml:43 (licence, auth, cours, rétroaction) passe par hyper-tls → native-tls → openssl et est absent de l'arbre inverse de rustls. Correctif publié + crate atteignable = règle 1 de .cargo/audit.toml : corrigé, pas suppressé ; ni le fichier de suppressions ni le garde-fou (CRATES="rsa") ne bougent. Le piège attendu ne s'est pas produit, et c'est le résultat intéressant : #345 annonce que tout bump atteignant le sous-arbre réseau/TLS fait redescendre les 7 pointeurs windows-sys de 0.61.2 — or cargo update -p rustls --precise 0.23.45 n'en a déplacé aucun. Le déclencheur tient à hyper et à ce qu'il tire, pas à la couche TLS en général ; le corps de #345 est donc trop large et un commentaire l'y consigne, avec une suggestion : formuler le garde-fou sur la version résolue de windows-sys plutôt que sur une liste de crates déclencheurs — la liste est ce qui vient d'être trouvé faux, la version est ce qui est protégé. Conséquence pratique : aucune strophe écrite à la main, la sortie de cargo est conservée, donc les checksums viennent de l'index et non du rédacteur. rustls-webpki 0.103.13 → 0.103.15 l'accompagne et est requis : 0.23.45 resserre sa borne de ^0.103.5 à ^0.103.14, même forme que le http ^1 → ^1.1 relevé par la revue de #344, mais cette fois contraignante ; les quatre correctifs que #310 avait obtenus en posant 0.103.13 sont conservés. Preuves plutôt que déductions : contrôle sur l'ancien lock (exit 1 sur RUSTSEC-2026-0285) montrant que l'outil local reproduit le rouge de la CI ; cargo metadata --locked acceptant le lock sans le réécrire ; checksums vérifiés au sha256sum des .crate, et surtout les lignes Downloaded→Compiling rustls v0.23.45 du run CI, qui prouvent l'archive réellement utilisée là où cargo metadata ne prouve que le graphe ; garde-fou tracé (Suppression guard: 2 checks, exit 0) ; 111 Rust ; et workflow_dispatch d'audit.yml sur main après merge (run 414) plutôt que d'inférer le vert du run de PR. /pr-review APPROVE en re-prouvant depuis la source plutôt qu'en relayant — checksums authentifiés contre l'index crates.io, borne resserrée lue dans le champ deps de l'index, absence de reqwest 0.12.28 de l'arbre inverse complet — et a levé une note fondée appliquée en 9abf474 : « aucun changement de comportement » valait pour le code de l'app, pas pour la couche TLS, où une poignée de main non conforme est désormais fermée. Deux acquis de process : (1) sur un événement pull_request, le filtre paths s'évalue contre le diff complet de la PR et non le delta du dernier commit — un commit markdown seul a donc rejoué check-rust (run 413), ce qui est la bonne sémantique ; (2) merge par l'API Forgejo (rebase ff, SHA inchangé) plutôt qu'en local, ce qui évite la réconciliation manuelle des PRs. Pas de v0.15.1 : arbitrage de Max, l'entrée reste sous [Unreleased]. Aucune migration DB (v1→v17). (ref #348, #345, PR #349)

Blockers actifs

  • Aucun blocker externe dur. spec-monetisation fermée 12/12 (2026-07-08) — #50/#52/#53/#135/#136 livrées, ne sont plus des blockers.
  • Le paiement est live : « aucun tag avant le go-live » n'a plus de raison d'être, et la release desktop qu'attendait le go-live est publiée (v0.16.0, 2026-10-03). Bascule live le 2026-09-29 (Product et Price live à 19 $ CAD, webhook, variables Stripe de production, redeploy 3673d09 de maximus-api, URL cs_live_ vérifiée), puis achat réel de contrôle le 2026-09-30 : clé sur /merci et par courriel, activation dans l'app, remboursement complet et licence révoquée par charge.refunded. Sources : la-compagnie-maximus#141 (corps du 2026-09-30) et maximus-api#62 (revalidation du 2026-09-29), relus le 2026-09-30 ; depuis ce dépôt, seuls /healthz (200) et les CGV publiées ont été relus. Les deux issues restent ouvertes pour leur reliquat (réglages au Dashboard Stripe, la-compagnie-maximus#150 pour Link dans les textes légaux, ménage de configuration côté maximus-api), et la-compagnie-maximus#141 attendait ce dépôt : #346, #347, #350 et #365 sont publiées dans la v0.16.0 ; reste l'updater vérifié (Free refuse et invite à acheter, Base accepte). la-compagnie-maximus#184, qui déclare le contrôle au démarrage, est traitée par Max en parallèle de la release. Vérifications manuelles sur la release publiée : (3) #365 faite et fermée le 2026-10-03 ; (1) reportée à #370, puisqu'il faut une édition Gratuite propre ; (2) #350 encore à faire. Détail : (1) en Gratuite, le modal de création de profil ouvert depuis ProfileSwitcher, puis « J'ai déjà une clé », doit se fermer sur la carte Licence (seule mutation que les tests de #346 ne gardent pas) ; (2) pour #350, sur un poste activé par la v0.15.0 : état « Activée » et marqueur « Cette machine » conservés, puis « Désactiver » et « Activer » doivent donner un jeton dont le claim vaut le dérivé (commande dans #350, attendu 64 brut: False dérivé: True). La dérivation devient définitive à la première activation en production, et rien d'autre ne tient le câblage de l'activation à l'identifiant réel du poste ; (3) pour #365, avec la clé réellement révoquée du contrôle d'achat du 2026-09-30 : l'enregistrer, redémarrer, lire « the license server says the key was revoked » dans le Journal, puis constater l'édition Gratuite et la révocation nommée (carte Licence, écran verrouillé, carte Mises à jour) ; étapes dans #365.
  • Backlog ouvert (status:ready, non bloqué) : vague planned-2026-09-24-paiements close 3/3 (milestone fermée le 2026-09-30) : #270 (PR #359) et #346 (PR #362) mergées le 2026-09-27, #347 (PR #364, ferme aussi #360) mergée le 2026-09-30 ; volet site clos (maximus/la-compagnie-maximus#153, redirection de /simpl-resultat en prod). Hors vague, pour la release qui suit le go-live : #365 (vérification en ligne de la révocation : mergée le 2026-10-03 par la PR #368, ouverte en status:blocked jusqu'à sa vérification manuelle), #367 (status:triage : jeton d'activation d'une ancienne licence resté à côté d'une autre clé par la v0.15.0 ; carte « Activée », cours refusés, aucun moyen de réactiver), #370 (status:triage : retirer la clé enregistrée, seul moyen d'arrêter le contrôle quotidien d'une clé révoquée sans entrer une autre clé), #350 (hachage de l'identifiant machine : mergée le 2026-10-01 par la PR #366, ouverte en status:blocked jusqu'à sa vérification manuelle), reste de #363 (plafond de /issue-license à 36525 jours et essai d'une vraie clé de 100 ans, débloqués par le déploiement de maximus-api#63) ; réglés côté serveur et site : maximus-api#63 (clés de 100 ans) et maximus-api#70 (réactivation d'une machine désactivée), fermées et contenues dans 3673d09, le commit déployé à la bascule du 2026-09-29, et la-compagnie-maximus#157, fermée, dont les CGV sont publiées en tronc commun plus annexe Simpl-Résultat (annexe relue en production le 2026-09-30, plus aucune durée de 10 ans) ; côté serveur, /v1/prices ne vérifie pas qu'une machine est encore active (une machine désactivée garde les cours jusqu'à l'expiration de son jeton), issue maximus-api à ouvrir ; suites de #350 ouvertes le 2026-10-01, en status:triage : maximus-api#82 (identifiants machine bruts déjà stockés, lignes désactivées comprises ; à rehacher avant l'expiration des premiers jetons bruts, sinon une réactivation crée une seconde ligne) et la-compagnie-maximus#184 (données d'activation absentes de la politique de confidentialité, à quoi s'ajoute le contrôle de révocation de #365, faits livrés consignés le 2026-10-03) ; clôture du go-live = la-compagnie-maximus#141 + maximus-api#62 ; spec-paiements reste le parapluie des trois dépôts (ici 3/5, restent #350 et #363) ; spec-onboarding-phase1 0/7 (#351 à #357, status:ready, pile linéaire ; spécifiée et passée en /review-spec le 2026-09-25, mise en issues le 2026-09-26 ; analyse d'origine dans docs/analyse-onboarding-2026-09.md, plan détaillé dans les spec-*-onboarding-phase1.md locaux, ignorés par git) ; #271 absorbé par #301, livré ; spec-ci-build-optimization 3/4 (#232 livrée le 2026-07-27 ; #234 : connectivité du cache réparée sur le VPS et Swatinem/rust-cache mergé le 2026-10-03 par la PR #375, mais l'issue reste ouverte en status:blocked jusqu'aux vérifications d'après merge — le premier run de la prochaine PR Rust restaure l'entrée partagée, le premier run planifié du lundi 2026-10-05 à 06:30 UTC est vert, Max relève la taille de /home/ubuntu/.cache/actcache sur le VPS — puis sera fermée à la main ; la milestone reste à 3/4 d'ici là) ; #376 (status:triage : Node 20, en fin de vie depuis le 2026-04-30, encore installé par check-frontend.yml et audit.yml, et commentaire concurrency de check-rust.yml démenti par forgejo-ops.md pour les runs de PR). Le suivi « glob src-tauri/** non prouvé » ouvert avec #232 est levé : la PR #344 est le cas test isolé attendu (Cargo.lock + 2 .md seulement) — check-rust a bien tourné, check-frontend a 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 », or audit.yml tire tous les jours à 06:00 UTC (19 runs schedule verts 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.toml est bien lu en conteneur sur main — l'ADR 0018 ne l'avait vérifié que par un workflow_dispatch sur une branche. #343 fermée le 2026-09-04 (PR #344) — RUSTSEC-2026-0258 sur h2, la seule vulnérabilité qui rougissait l'audit depuis le 08-19 ; l'audit est de nouveau vert sur main, prouvé par workflow_dispatch (run 393) et non déduit du run de PR. #348 fermée le 2026-09-21 (PR #349) — RUSTSEC-2026-0285 sur rustls, seule vulnérabilité rougissant l'audit depuis le 09-15 (7 runs, 405→411) ; vert reprouvé par workflow_dispatch sur main (run 414 : 666 paquets, zéro vulnérabilité, 12 warnings autorisés). Deux advisories en cinq semaines sur le même chemin, celui de la mise à jour automatique. Restent #317 (re-évaluer react-router à une migration v8), #319 (déclencheur de retrait pour rsa, désormais seule entrée de .cargo/audit.toml — le garde-fou est passé à 2 vérifications) et #345 (domicilier la contrainte windows-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 quel cargo update » mais tout bump atteignant le sous-arbre réseau/TLS — mesuré : -p anyhow ne bouge rien, -p hyper fait descendre les 7 — soit précisément là où tombent les advisories. Prémisse restreinte le 2026-09-21 par #348 : -p rustls, au cœur de ce même sous-arbre, n'en déplace aucun — le déclencheur tient à hyper et à ce qu'il tire, pas à la couche TLS en général, donc « sous-arbre réseau/TLS » prédit une dérive là où il n'y en a pas et ferait écrire une strophe à la main sans nécessité. D'où la suggestion portée en commentaire sur l'issue : formuler le garde-fou sur la version résolue de windows-sys plutôt que sur une liste de crates déclencheurs — la liste est ce qui vient d'être trouvé faux, la version est ce qui est protégé. #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 de windows-sys compilé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-rs s'arrête sur lib.exe, il faudrait cargo-xwin). Côté npm, #358 ouverte le 2026-09-26 par /analyse-vulnerabilite : npm audit remonte 7 findings (4 high, 3 moderate) sur 4 dépendances racines, contre les 2 acceptés au 07-27, et chaque correctif tient dans la borne déjà déclarée ; le lock se régénère avec npx npm@11 (npm 10 plante sur un bump de vitest 4.1.x).
  • Nouveau bug ouvert : #320 — les installations .rpm ne reçoivent aucune mise à jour (latest.json ne porte qu'une entrée linux-x86_64 construite depuis le .deb, release.yml:104-122), alors que les .rpm continuent d'être publiés en assets. Documenté en §3 de docs/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 dans CLAUDE.md et docs/architecture.md (composants, pages, services, Version actuelle : 0.6.3 vs 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 livrée par #346 (PR #362), avec une réserve notée dans l'ADR 0017 (exacte tant que le chemin abonnement → Premium reste dormant) ; (2) le flux Logto exact (connexion par code, mot de passe oublié ou inscription) reste à lire dans le journal d'audit. Position produit : v0.16.0 (2026-10-03) ; [Unreleased] vide.