Simpl-Resultat/STATE.md
le king fu 7b852c9969 state: sync after /plan-run paiements (planned-2026-09-24-paiements)
Desktop wave #270 -> #346 -> #347 and site #153 planned in twin
milestones, one /autopilot run per repository. Review found that the
2-year activation token silently drops activated machines to Free
(handled in #347). 100-year keys decided out of wave (maximus-api#63,
la-compagnie-maximus#157); machine id hashing tracked in #350.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 19:47:32 -04:00

40 lines
52 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# STATE — Simpl'Résultat
> Derniere MAJ : 2026-09-24 (**Vague paiements planifiée — `/plan-run spec-paiements`, milestones `planned-2026-09-24-paiements` dans deux dépôts** : simpl-resultat #270 → #346 → #347 (pile linéaire) et la-compagnie-maximus#153 ; `/autopilot` refuse le multi-dépôts, donc **un run par dépôt**, le site d'abord. Les quatre corps ont été revérifiés contre le code et la prod, puis passés en `/review-spec` (verdict 🔴 résolu en séance : message 409 effacé par `loadMachines()`, repli `activateError` contradictoire). **Défaut de fond trouvé en revue** : le jeton d'activation expire après 2 ans (`signActivationJwt`) et le client le valide strictement, donc une machine activée retombe en Gratuite sans message ; corrigé dans #347 (jeton expiré mais signé pour cette machine = absent). **Clés de 100 ans** décidées (retirer `exp` est impossible, tous les binaires l'exigent) → maximus-api#63 + la-compagnie-maximus#157, hors vague. #350 ouverte (hachage HMAC de l'identifiant machine, avant go-live). Skill `/issue-license` et script `verify-release.sh` commités (`11e12ab`) après retrait du courriel d'un tiers — le dépôt est public.)
>
> Précédente MAJ : 2026-09-21 (**RUSTSEC-2026-0285 corrigée, l'audit quotidien de nouveau vert** — `rustls` 0.23.36 → 0.23.45, PR #349 mergée en rebase ff sur `main` `9abf474` (SHA inchangé, la branche était directement au-dessus de `main`), #348 fermée. `audit.yml` échouait depuis le **2026-09-15**, 7 runs (405→411), dernier vert le 404 du 09-14, **aucun push sur `main` dans la fenêtre** — deuxième occurrence du mode d'échec de #343 (17 jours en août, 7 ici) : le gate crie, personne ne regarde. Vert **prouvé** par `workflow_dispatch` sur `main` (run 414 : 666 paquets, zéro vulnérabilité, 12 warnings autorisés), pas déduit du run de PR — l'étape `cargo audit` de `check-rust.yml` est `continue-on-error: true`. **La dérive `windows-sys` de #345 ne s'est pas déclenchée** : `rustls` est au cœur du sous-arbre TLS et n'a déplacé aucun des 7 pointeurs (diff vide sur toutes les lignes `windows-sys`, 6 versions identiques, 666 paquets avant/après, diff total 4 lignes) — la prémisse « tout bump du sous-arbre réseau/TLS » est trop large, le déclencheur tient à `hyper` ; mesure consignée en commentaire sur #345. Donc **pas de strophe écrite à la main** contrairement à #344 : cargo a écrit le lock, les checksums viennent de l'index. `rustls-webpki` 0.103.13 → 0.103.15 est **requis** et non du collatéral — 0.23.45 resserre sa borne de `^0.103.5` à `^0.103.14`. Chemin d'atteinte **plus étroit qu'`h2`** : `tauri-plugin-updater → reqwest 0.13.2` seulement, le `reqwest 0.12.28` que l'app déclare passant par `hyper-tls → native-tls → openssl`. **Pas de v0.15.1** (arbitrage de Max) : l'entrée reste sous `[Unreleased]`, l'exposition réelle étant quasi nulle — la transcription reste authentifiée, il faudrait que le serveur de release soit lui-même hostile.)
## 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-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)
- 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 `.deb` et 1 `-setup.exe` téléchargés, 0 `.sig` (l'updater lit les signatures dans `latest.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.md` disait « commentaire », corrigé avec le `PATCH` à rejouer) ; le `PATCH` lui-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é** : ni `tauri-plugin-updater`, ni la saisie de clé (validation Ed25519 locale), ni l'activation machine (`license_key` + `machine_id` ; côté `maximus-api`, seul `fulfillmentService` envoie 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 le `sign-in-exp` public du tenant : connexion par courriel + code (`verificationCode: true` sur 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, ou `GET /api/logs?userId=` avec le secret M2M qui ne vit que sur le VPS). Sans effet sur l'édition : `current_entitlements` ne lit le compte que pour un abonnement `active`, 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.md` enrichi (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émoire `project-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.0 `FEATURE_TIERS` laisse `auto-update` **ouvert 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_EDITION` compilé 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 et `main`), validation locale sans réseau ; l'activation machine n'est requise que pour les cours du Bilan (`activation.token` en Bearer vers `/v1/prices`), et l'ancien chemin `/licenses/activate` du binaire passe par un 308 vers `/v1/` avec produit inféré du préfixe `SR-` (#48, vérifié en prod). Émission via `POST /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`+`iat` exigés par `strict_validation()`, préfixe/édition cohérents, expiration 2036-09-09. **Le détour qui a coûté la séance** : le VPS répondait `Connection timed out` sans lien de réauth — pas une attente navigateur mais **`peer's node key has expired`** (Tailscale, 180 j, `last seen 16d`), invisible depuis la console admin et sans réparation à distance ; l'IP publique `54.39.97.77` a 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 tout `ssh` vers 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. Table `licenses` : une seule vraie clé, la Base de Max du 07-19, **révoquée** (Stripe test + remboursement) — et le client ne voit jamais `is_revoked` hors ligne, seul le serveur (activation, cours) la refuse. Release : pré-vol vert (111 Rust, 1181 vitest), 5 manifestes + 2 changelogs + ligne `CLAUDE.md` 0.6.3→0.15.0, tag annoté confirmé par Max avant push (publie `latest.json` aux installations existantes), run 402 en 22 min, 7 artefacts et `latest.json` vé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é (`main` `5154c52`).** 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 sur `main` dans l'intervalle**. Une seule vulnérabilité : **RUSTSEC-2026-0258** / GHSA-q83h-524g-xf6h, `h2` 0.4.13, datée du 08-17 — DoS de sévérité faible, les frames HTTP/2 `DATA` vides 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` : `h2` est 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) et `hyper-tls`←`reqwest 0.12.28` déclaré directement en `Cargo.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 de `check-rust.yml` ne bougent. **Le piège, et la raison d'un diff de 2 lignes** : `cargo update -p h2` donne bien 0.4.19 mais réaiguille au passage 7 pointeurs `windows-sys` (`colored`, `dirs-sys`, `errno`, `rustix`, `rustls-platform-verifier`, `tempfile`, `winapi-util`) de `0.61.2` vers 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 strophe `h2`** appliqué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 — que `0.61.2` est bien l'état du tag, donc la seule configuration `windows-sys` dont on ait la preuve qu'elle compile sous Windows ; relayer la note de #321 n'aurait pas suffi. `cargo metadata --locked` accepte le résultat **sans le réécrire**. **`/pr-review` APPROVE et a trouvé ce que j'avais manqué** : une strophe écrite à la main ne garantit plus ni checksum ni liste de dépendances, et `h2` 0.4.19 **resserre sa borne `http` de `^1` à `^1.1`** — le genre de changement qui invalide un lock épinglé ; satisfait ici (`http 1.4.0`), checksum authentifié par `sha256sum` du `.crate`, et nuance utile qu'il pose : `cargo metadata --locked` prouve le graphe, **pas** le checksum — c'est le `Downloaded`→`Compiling` du 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'étape `cargo audit` de `check-rust` est `continue-on-error: true` et qu'un job vert n'y prouve rien ; garde-fou tracé (`Suppression guard: 2 checks, exit 0`) ; **111 Rust** ; et `workflow_dispatch` d'`audit.yml` **sur `main` après merge** (run 393, `Job succeeded`) plutôt que d'inférer le vert du run de PR. **Deux acquis au passage** : (1) le **glob `src-tauri/**` est enfin prouvé** — cette PR est le cas test isolé attendu depuis #232 (lock + 2 `.md` seulement) : `check-rust` a tourné, `check-frontend` correctement sauté par sa denylist ; le suivi peut être fermé ; (2) **#345 ouverte** — la contrainte `windows-sys 0.61.2` en est à sa 3e rencontre (#310 l'introduit, #321 la défait, #344 l'évite à la main) et ne vit dans **aucun** ADR, aucun commentaire de `Cargo.toml`, aucun garde CI : seulement 3 lignes de prose de STATE.md et un message de commit, donc le prochain `cargo update` la 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 (`main` `97d376b`) — 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éaiguillages `windows-sys` comme 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* de `0.61.2` vers 0.48/0.52/0.59, cette PR les *remonte* à `0.61.2`, l'état exact du tag `v0.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é contre `0.61.2`. **La configuration non prouvée sous Windows était celle de `main`** ; la PR réduit le risque. (2) Sur #322, la revue du 08-14 annonçait « trois » déclencheurs et mappait `UpdateCard.tsx:201` à la page d'erreur — faux dans les deux sens : `:201` est le retry de l'état `error` de la carte, et `ErrorPage.tsx:82` est un **4e** site distinct qui **détecte seulement** (`handleCheckUpdate` s'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 cocher `readyToInstall` **avant** l'invite d'authentification, alors que `download_and_install` fait `download(...).await?` puis `install(bytes)` dans le même appel (`updater.rs:723-729`), que `Finished` est un no-op côté UI (`useUpdater.ts:119-121`) et que `READY_TO_INSTALL` n'est dispatché qu'après le retour de `dpkg -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 en `downloading`, le testeur guette un blocage après `readyToInstall`, 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é en `97d376b` avec 3 mineurs (les 4 déclencheurs, les préfixes `/api/v1/packages/` (DELETE) vs `/api/packages/` (PUT) que `release.yml:217-219` commente déjà, ordre du changelog `SKILL.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.lock` au 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 audit` 0 vulnérabilité / 22 warnings autorisés) et `cargo test` **111 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 par `Resolves`, 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.yml` tire bien tous les jours à 06:00 UTC — 19 runs `schedule` verts du 07-28 au 08-15 — et son `workflow_dispatch` de 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 : `dataExportService` ne sérialise ni `import_sources` ni `import_config_templates` et fait `DELETE 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, + `withTransaction` que le service n'avait nulle part). (2) Un worker a **mesuré** que l'exemple « Solde 2024 » de l'issue n'est pas défectueux (`parseFloat` s'arrête sur le `S`) et a ajouté la fixture qui l'est vraiment. (3) Un autre a mesuré que chez RBC `CAD$` et un `Cheque Number` presque 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 passait `isNaN` et 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-review` a bloqué 3 des 10 PRs, tous fondés** : (a) #336 — la règle débit/crédit testait `isNaN(debit) && isNaN(credit)`, donc une cellule illisible à côté du remplissage `0,00` calculait 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 — `detectDescriptionColumn` retournait la colonne préférée **sans veto par les données**, et le mot-clé `transaction` capturait 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 fichier `Date;Description;Débit;Crédit;Montant;Solde` lu correctement AVANT devenait `positive_expense` APRÈ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 partageaient `scratchpad/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 dans `CLAUDE.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 audit` 9→0, `npm audit` 3→2 acceptées** (`main` `a14258b`, 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 audit` sur `main`, advisory-db `0bfde9d6`. **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 -i` vide sur `x86_64-pc-windows-msvc` **et** `x86_64-unknown-linux-gnu`, présent seulement sur `x86_64-apple-darwin` via `plist`←`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 par `cargo update -p rustls-webpki -p tar` (0.103.9→**0.103.13** couvrant les 4, 0.4.44→**0.4.46**) sans toucher `Cargo.toml` ; dérive de lock expliquée et bornée (7 pointeurs `windows-sys` repointés vers des versions **déjà présentes**, 666 paquets avant/après, deps Windows-only → no-op sur Linux ; `--precise` donne le même résultat, c'est la re-résolution de cargo 1.94.1). Les 3 restantes → `.cargo/audit.toml` versionné, 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 si `quick-xml` redevenait 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** dans `check-rust.yml`, placé après `cargo check` (index chaud) avec `--locked`, testant le **code de sortie séparément de la sortie** (un crate absent et un `cargo tree` en panne impriment tous deux du vide) + **canari** `tar`. 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 « glob `src-tauri/**` non prouvé » de #232) → il logge maintenant chaque vérification (`Suppression guard: 4 checks, exit 0`, visible run 336). Preuve que `.cargo/audit.toml` est bien lu en conteneur : le `workflow_dispatch` de `audit.yml` sur la branche est **vert**, alors que le bump seul n'aurait éliminé que 6 des 9. **npm** (#311) : `postcss` 8.5.13→**8.5.23 sans override** — `vite` déclare `^8.5.3`, seul le lock était périmé (≠ cas #241 où le parent pinnait) ; `nanoid` suit dans sa borne ; **CSS émis identique octet pour octet** (postcss *est* le pipeline CSS, un build vert n'aurait prouvé que la compilation) ; `react-router` **accepté** — advisory mode **RSC**, or `App.tsx:109` monte un `BrowserRouter` client-only, et `react-router-dom` est **figé à 7.18.1** (v8 a fusionné le paquet dans `react-router`) donc le « fix » npm est un **downgrade** en 7.11.0 → sortir de la plage = migration, pas bump (#317, titre portant `react-router` pour la dédup par sous-chaîne de `/analyse-vulnerabilite`). Asymétrie à connaître : **aucun `npm audit` en CI**, donc rien ne rougit côté front — écrit dans `architecture.md` + `CLAUDE.md` pour 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) et `head_branch` = `#<PR>` sur les runs `pull_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-audit` pré-buildé** (`main` `7779f7d`, PR #309 rebase, milestone `spec-ci-build-optimization` **3/4**). `/analyze` a 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, `frontend` démarre à la seconde où `rust` finit sur tous les runs de l'historique → chaque PR payait rust+frontend ≈ 24,5 min ; (2) **3 des 40 derniers commits first-parent** touchent `src-tauri/`, dont 2 `chore: release` (push sur `main`, 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: false` sur la protection de `main`, vérifié API). Chrono re-mesuré du run 326 : **12m15s de gaspillage sur 21m44** — save `target/` 6m11 + save registry 43s (`reserveCache failed`, cause #234) + `cargo install cargo-audit` 4m41 + 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 (base `main`) ont tourné ; plus aucun filtre `branches:`. 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-review` **APPROVE** — a récupéré les logs CI plutôt que juger le diff, et levé que l'override `PATH` job-level aurait pu masquer le binaire d'`install-action` (vérifié : audit bien exécuté) ; 2 des 6 suggestions appliquées (`.claude/**` au denylist, ref `check.yml` périmée dans le skill `release`). **Skip prouvé empiriquement** : le 2e push (ni `src-tauri/**` ni `check-rust.yml`) n'a **pas** déclenché `check-rust`. **Reste non prouvé — le glob `src-tauri/**` lui-même** (seule l'entrée chemin-exact a matché) : mode d'échec **silencieux** sur la PR Rust (1/40, ni `cargo check` ni `cargo test`) → #310 (`cargo update` touchant `Cargo.lock` seul) sera le test isolé, à surveiller. Le retrait du `|| true` a **découvert 9 advisories RUSTSEC réelles** (préexistantes, seulement masquées) → **#310** ouverte : `rustls-webpki` ×4 + `tar` ×2 atteignables via `tauri-plugin-updater`/`reqwest` (correctifs **patch**), `quick-xml` ×2 bloqué en amont par `plist`←`tauri` (bump majeur), `rsa` sans correctif publié mais seul parent `sqlx-mysql` — absent de tout arbre de compilation (`cargo tree -i rsa --target all` vide, 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-gating` livrée 6/6 et fermée — le gating par tier est sur `main`** ([Unreleased], candidate v0.15.0). Reprise du run `/autopilot` interrompu 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 de `submitKey` → l'auto-refresh effaçait le message « clé invalide » ~1 s après ; et `status: "error"` persistant aurait laissé `ready=false` à jamais pour les futurs RequireFeature) → fix `b9e13b5` : validation de clé **orthogonale** au lifecycle de chargement (`validationError` dédié, `status` intouché, reducer exporté + 6 tests) → **APPROVE** → merge API. Relance `/autopilot` en 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](reports/DAILY-REPORT-2026-07-20.md)). `/pr-review` **×5 parallèles → APPROVE ×5** ; seule retouche user-facing : coquille FR `docs.editions` (« tout la » → « tout de la », `6de9617`). Merge : tip cumulé validé (871 vitest + build + cargo 106) puis **ff-only** → push `main` **refusé : branche protégée** (whitelist `maximus`, règle du 2026-03-07, jamais rencontrée avant) — cause réelle : `~/.git-credentials` porte 3 identités Forgejo et `defenseur-auto-bot` en 1re position **shadow** `maximus` (git prend la 1re entrée du host ; les pushes de branches passaient, seule `main` whitelisté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 branches `worktree-agent-*` du harness ; le « résidu `origin/issue-259` » signalé au warmup était un tracking ref périmé faute de `fetch --prune` — la branche remote avait bien été supprimée le 19). Décisions workers notables : tuiles `ProfileSelectionPage` **non** 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 masquerait `SR_DEV_EDITION`) ; `#[serde(default)]` préexistant sur `features` = les licences Base déjà émises sans le champ ne régressent pas. Gotcha process : un fork `/pr-review` lancé pendant que la CI est `pending` se 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 : extraction `LockBadge`/`UpsellPanel` si 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)
## 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.
- Backlog ouvert (`status:ready`, non bloqué) : **vague `planned-2026-09-24-paiements` prête pour `/autopilot`** (planifiée le 2026-09-24) — simpl-resultat #270 → #346 → #347 (pile linéaire) et maximus/la-compagnie-maximus#153. `/autopilot` refuse le multi-dépôts : lancer `/autopilot planned-2026-09-24-paiements` depuis chaque dépôt, le site d'abord (côté site, `/autopilot` sans argument ramasserait aussi `planned-2026-08-15-monitoring-postes`). Hors vague, avant go-live : #350 (hachage de l'identifiant machine), maximus-api#63 + la-compagnie-maximus#157 (clés de 100 ans + CGV, à déployer ensemble) ; go-live = la-compagnie-maximus#141 + maximus-api#62 ; `spec-paiements` reste le parapluie des trois dépôts ; #271 absorbé par #301, livré ; `spec-ci-build-optimization` **3/4** (#232 livrée le 2026-07-27 ; reste **#234** connectivité du serveur de cache runner, qui débloquerait `Swatinem/rust-cache` — accès hôte VPS requis). 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`).
- 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 tranchée le 2026-09-24 et portée par #346 ; (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]` porte l'entrée `rustls` du 2026-09-21.