51 Commits

Author SHA1 Message Date
b3372a80e4 v1.12.0 : fix #70 (inactif à Posologie projetait encore) + Doses (icônes de forme, marqueurs prise de sang)
- FIX #70 : generateForecastDoses garde !isActive — un injectable archivé
  avec Posologie et de l'historique projetait encore ses injections
  (graphique Prévision, extension de fenêtre, horizon LabTiming) ; le chip
  Prévision s'activait sur des Posologies inactives. FIX : garde moteur +
  horizon/chip sur les ACTIFS. §6.bis inchangé : l'historique reste simulé,
  seul le FUTUR s'arrête (comme les rappels v1.4.0). Miroir web. 3 tests
  ForecastDosesGuardTest + 3 miroirs web.
- Icônes de forme de prise dans Doses : routeIcon pur (pilule/seringue
  IM+SC teinte tertiary pour la SC/goutte/sparadrap/compte-gouttes ;
  ⚠️ MedicationLiquid absent d'icons-extended 1.7.8 → Colorize).
- Marqueurs « prise de sang le même jour » : labMarkersForDose pur (jour
  calendaire LOCAL, lab à l'heure exacte = APRÈS) → encart 🧪↑/🧪↓ en bout
  de ligne. 7 tests DosesExtrasTest + 10 miroirs web.

274 tests JVM + 14 UI + lint verts ; web 194 tests + E2E verts (check.sh).
Validé émulateur sur APK RELEASE avec données réelles seedées + scénario du
bug (2 inactifs à Posologie 7 j) : Prévision on/off 0 crash, glyphes et
marqueurs en place.
2026-09-29 23:24:34 +02:00
a0f3ac59f9 v1.11.0 : inactifs regroupés en bas de la page Traitements + cartes atténuées
- Demande : « les traitements inactifs sont peu discernables — les placer
  à la fin (tout en bas), et peut-être les rendre plus distincts ».
- Helper pur treatmentsForDisplay (tri STABLE : actifs d'abord, inactifs
  après, ordre relatif conservé dans chaque groupe).
- LazyColumn en 3 sections : actifs / en-tête inactive_section (seulement
  si des inactifs existent) / inactifs — cartes alpha 0.55 en plus du
  badge, lisibles et tappables (l'édition d'un archivé reste possible).
- Miroir web : treatmentsForDisplay dans js/data/models.js + rendu
  sections + opacity 0.55 + i18n inactive_section FR/EN (web v1.11.0 sync).
- 5 tests JVM TreatmentsDisplayTest + 4 tests web (miroir).

264 tests JVM + 14 UI + lint verts ; web 184 tests + E2E verts (check.sh).
Validé émulateur sur APK RELEASE avec données réelles seedées (5 actifs en
haut, en-tête + EV-old/EEn-old tout en bas, 0 crash).
2026-09-29 07:31:25 +02:00
d531ecae24 v1.10.1 : durcissement rappels — singleTop (onNewIntent vivant), instant planifié dans la garde, BootReceiver goAsync
- launchMode="singleTop" sur MainActivity : un tap notification app-ouverte
  délivre onNewIntent SANS recréer l'activité (l'ancien standard+CLEAR_TOP
  recréait tout et rendait le chemin v1.10.0 inopérant — code mort).
- EXTRA_PLANNED_AT : l'instant planifié voyage dans l'alarme (scheduleFor),
  le snooze le forward, le garde « créneau déjà honoré » évalue le jour du
  CRÉNEAU et non celui du feu (alarme inexacte/Doze glissant après minuit).
- BootReceiver : goAsync + coroutine (dette §20.bis #2), runBlocking supprimés.
- Audit doc : comptes résiduels 223 → 259 (§14 #64, §16.ter) ; §9/§20.bis à jour.

259 tests JVM + 14 UI + lint verts ; validations émulateur sur APK RELEASE
avec données réelles seedées (singleTop 0 recreation, planned_at A/B, 0 crash).
Web non concerné (aucune logique miroir touchée).
2026-09-28 21:49:06 +02:00
a4f859f854 v1.10.0 : fix #68 (reco de prise de sang avant stabilisation), fix #69 (dialog fantôme), rappel sauté si dose déjà loggée
- #68 : le « creux » était le minimum de la fenêtre entière — pour un ester à
  montée lente (EEn, pic ~J+5 ≈ intervalle 7 j) il tombait juste après
  l'injection PRÉCÉDENTE : date antérieure à la stabilisation affichée sur la
  même carte, « juste avant ton injection du … » faux de plusieurs jours
  (réel : creux 5 oct 02:32 < stab 7 oct 04:03). FIX : creux = niveau
  PRÉ-INJECTION du créneau (principe v1.8.0 restauré ; EV inchangé). Miroir
  web appliqué. Régression n°6 data-driven (backup-v1.9.8.json, balayage de
  now ±60 j) ; les 17 LabTimingTest passent inchangés.
- #69 : tap notification → dialog ; le fermer + changer d'onglet + revenir sur
  l'accueil le rouvrait à chaque fois (demande au niveau activité,
  consommation dans les remember de Home). FIX : MutableState<Long?> dans
  MainActivity consommé UNE fois + extras retirés de l'intent ; bonus : tap
  notification app-ouverte ouvre aussi le dialog. Test UI LogDoseRequestTest.
- Rappel sauté si dose déjà loggée (demande) : garde au déclenchement —
  hasDoseLoggedOnDay (pur, jour calendaire LOCAL) dans ReminderReceiver,
  re-programmation du créneau suivant dans tous les cas. 6 tests JVM +
  vérification A/B émulateur (EEn dose-du-jour → 0 notif, Fluoxetine → 1).

259 tests JVM + 14 UI + lint verts ; web 180 tests + E2E verts (check.sh).
Validations émulateur §16.ter sur APK RELEASE avec données réelles seedées
(reco : creux 11 oct 20:08 ≥ stab 7 oct 06:03 ; dialog 1× puis plus jamais ;
changelog 1.10.0 affiché ; 0 crash).
2026-09-27 23:11:04 +02:00
09cddfc8ee v1.9.8 : socle qualité — tests UI Compose (13), signature release dédiée, découpage Settings + README anglais
- 13 tests UI instrumentés (uitest/) : navigation 5 onglets, dialog Nouveautés
  (épinglage du fix v1.3.2), sections Paramètres, version installée = BuildConfig
  (anti-bug #46). Déterministes : pm grant shell, markChangelogSeen, assertions
  via targetContext (FR/EN), zéro dépendance aux données Room.
- Signature release conditionnelle : scripts/make-release-keystore.sh (shellcheck+shfmt)
  + keystore.properties gitignoré ; assembleRelease l'utilise s'il existe, sinon
  clé debug inchangée. Migration documentée §16.quater.
- SettingsScreen découpé : 840 lignes -> orchestrateur 377 + 11 cartes dans
  ui/screens/settings/ (déplacement pur, commentaires conservés). ChartScreen
  volontairement entier (garde de source #54).
- README.en.md complet + liens FR/EN croisés.

Vérifié : 249 tests JVM + 13 UI verts, lint vert, smoke RELEASE émulateur
(données réelles seedées : accueil, prochaines doses, suggestion creux,
graphiques ESE+TFS superposés, 0 crash logcat — anti-#64).
2026-09-22 22:39:42 +02:00
d6d95080b4 v1.9.7 : la suggestion de prise de sang ne fuit plus quand l'heure d'injection varie
Remontée : « la suggestion change tout le temps à chaque injection si
l'injection n'est pas faite pile à la même heure » — la règle v1.8.1
comparait l'écart inter-doses EXACTEMENT à l'écart précédent : un log
30 min plus tard cassait le régime et repoussait la stabilisation de
5 × t½ à chaque injection (creux recommandé fuyant).

Fix : chaque écart doit rester dans l'INTERVALLE DE POSOLOGIE ± 24 h
(GAP_TOLERANCE_MS — « je m'injecte le même jour, à l'heure près »). La
fenêtre se réfère à l'intervalle THÉORIQUE : des logs à 6,8 j puis 7,2 j
ne se déstabilisent plus en cascade ; un vrai changement de créneau
(2 j au lieu de 7 j) reste hors fenêtre. Le créneau affiché suit
toujours la dernière dose réelle (voulu).

Tests : « interval change » ré-épinglé (NOW+21 j) + 2 nouveaux (flou
d'heure ± 23 h → pas de reset ; écart 25 h → reset). 249 verts + lint.
versionCode 44 / 1.9.7 + CHANGELOG/§2/§7.11/README.
2026-09-21 22:19:46 +02:00
a40d9bc076 v1.9.6 : la carte Accueil liste TOUTES les prochaines doses (triées)
Remontée : le rappel quotidien (CPA) masquait le rappel hebdomadaire
(EEn) — « je ne le vois quasiment pas ».

- NextDoseCard : UNE ligne par traitement, TRIÉES par prochaine prise ;
  1ʳᵉ ligne (la plus proche) en avant, suivantes en style secondaire ;
  titre pluriel « Prochaines doses » dès 2 lignes (next_doses FR/EN)
- Helper PUR nextDoseLine (locale/zone paramétrables, template jours
  localisé next_dose_days) + 3 tests NextDoseLineTest
- Notifications inchangées (une alarme par traitement)
- versionCode 43 / 1.9.6 + CHANGELOG/§2/README/GUIDE
2026-09-21 21:56:32 +02:00
0f0ecc3d23 v1.9.5 : revue de code — fix #67 ticks X/fuseau, contexte moteur, Canvas mémoïsé, docs alignées
Corrigé (audit lecture-seule) :
- #67 : drawXLabels formatait dans le fuseau CHOISI mais xLabelTicks
  alignait sur le fuseau APPAREIL (zone non passée, contredisant #55) ;
  le pas fixe +24 h dérivait d'1 h aux transitions DST — zone passée +
  avancement Calendar, test DST épinglé (Europe/Paris 29/03)
- EstrannaiseCloud.compute : paramètre nowMs mort supprimé (KDoc
  mensonger), appelants + tests alignés

Perf (suite v1.8.2) :
- E2Context/prepareE2Context : le tracé labs (720 pts) reconstruisait
  les maps moteur par point — préparées une fois, résultat bit-identique
- Canvas : Paints texte mémoïsés (clé couleur|taille|align|gras),
  detectExtrema mémoïsé par série, formateurs X/marqueurs mémoïsés

Règles ré-appliquées :
- modelLabelRes (règle anti-#59) ; PKModels.* au lieu de literals ;
  scheduler2 supprimé

Docs : §7.2/§7.3 à jour (asset historique, cutoff 10×t½), PKPresets/
Treatment/LabTimingTest commentaires alignés, §14 #67, §2, README
2026-09-20 12:56:15 +02:00
e4f52bc6da v1.9.4 : presets du modèle ESE étendus à ses 6 esters injectables + éditeur dynamique
- PKPresets.kt (25 entrées) : EC huile (5 mg IM), EB (1 mg IM), EUCS
  suspension cristalline (10 mg SC) — champs Bateman informatifs = t½
  terminales des fits (pas utilisés au calcul)
- TreatmentEditorScreen.kt : liste des esters = Esters.choicesForModel
  (même helper que l'override du DoseDialog) ; au changement de modèle,
  l'ester rebascule sur EV si non couvert (garde anti 0 pg/mL)
- PKPresetsTest (nouveau, 6 tests) : couverture des 6 esters ESE,
  unicité, TFS 7 / WHSAH 6 inchangés, garde anti hors couverture,
  plausibilité des t½ informatives — 243 verts + lint
- versionCode 41 / versionName 1.9.4 + CHANGELOG/§2/§7.12/§8/README/GUIDE
- Doc : doublon de la section §7.12 supprimé (collé deux fois)
2026-09-19 23:30:23 +02:00
03fdbf61ed v1.9.2 : fix nuage vide sans traitement stocké ESE (filtre par ester effectif)
Bug remonté : activer Estrannaise dans le graphique puis le nuage
n'affichait rien tant qu'aucun traitement n'était STOCKÉ avec le modèle
ESE — « la seule méthode trouvée était de mettre un traitement en cours
sur le modèle ESE, mais ça ne devrait pas être un prérequis ».

Cause : le filtre du nuage testait le pkModel STOCKÉ du traitement, alors
que la courbe ESE affichée redessine toutes les doses E2 avec
modelOverride = "ESE" (peu importe le modèle stocké) — courbe et nuage
n'utilisaient pas la même définition de « quelles doses sont tracées en
ESE ».

Fix : EstrannaiseCloud.compute couvre toutes les doses E2 à profil
injectable dont l'ESTER EFFECTIF (override compris) est couvert par le
fit Estrannaise — indépendamment du modèle stocké. L'exclusivité ESE
reste portée par le chip (activable seulement si ESE est affiché).
L'oral Bateman reste hors nuage (pas d'ester échantillonnable). Les
traitements inactifs sont inclus (§6.bis : la courbe ESE les trace aussi).

Tests réécrits (237 verts / 207 sans données locales) : « les doses d'un
traitement TFS sont couvertes quand ESE est affiché » (épinglé Android +
web) ; oral seul → vide (conservé). Validé émulateur §16.ter sur le
profil réel de l'utilisatrice (EEn stocké TFS, ESE affiché, nuage visible
autour de la courbe, 0 crash). Web miroir v1.9.2. versionCode 40 /
versionName 1.9.2.
2026-09-19 22:10:35 +02:00
96a0dbc56a v1.9.1 : fix nuage calibré (il entoure la courbe ESE calibrée) + doc +4 pg/mL
- EstrannaiseCloud.compute : paramètre scalePerEster — le nuage reçoit la
  MÊME calibration que la courbe ESE (autoByModel ESE). Avant : courbe ESE
  calibrée mais nuage BRUT → les deux flottaient à des échelles différentes
  (remontée : « le nuage ne s'active que autour du tracé, pas autour du
  modèle Estrannaise »). ChartScreen passe autoM?.esterScales.
- +1 test : nuage calibré ×2 = nuage brut ×2 (le nuage suit la calibration).
- Validé émulateur §16.ter (release, seed réel + traitement EV ESE actif
  de test) : nuage rendu autour de la courbe ESE calibrée, 0 crash.
- CHANGELOG 1.9.1 : explication documentée du +4 pg/mL constaté depuis
  v1.8.2 (l'arrondi de la médiane de calibration — plus juste que la
  troncature — a déplacé une échelle auto-calibrée d'un cran ; le niveau
  actuel est le CORRIGÉ). §2 session, §7.12 nuage calibré, README.
- DIAG du test médiane nettoyé (l'épinglage de l'arrondi reste, en
  assertion propre ±0,005).
- versionCode 39 / versionName 1.9.1. Web sync v1.9.1.
2026-09-19 21:13:17 +02:00
8078d5cbed v1.9.0 : modèle Estrannaise ANALYTIQUE (abandon de l'ODS) + nuage d'incertitude MCMC
DÉCISION : tous les modèles ont maintenant leurs sources originelles —
on abandonne les liens avec le fichier ODS (qui lui-même était
l'échantillonnage horaire de ces formules ; fidélité vérifiée RMS 0,00).

- pk/EstrannaiseModels.kt (NOUVEAU) : forme close 3C de estrannaise.js
  (paramètres publiés EV/EU/EEn/EC/EB/EUCS), posterior MCMC (313
  échantillons/ester, asset mcmc_samples.json ≈ 48 Ko), t½ terminale
  analytique, cas dégénérés (taux égaux → limites, portés du JS source).
- PharmacokineticEngine : dispatch ESE → EstrannaiseModels (plus de
  fallback PKProfileStore) ; cutoffHours ESE → 10 × t½ analytique (plus
  la longueur de table).
- Esters : +EUCS (undécylate suspension cristalline, exclusif ESE) ;
  choicesForModel ESE = 6 injectables ; TFS = liste EXPLICITE (EUCS sans
  V3C TFS → dispatcher donnerait 0).
- LabTiming : t½ ESE analytique.
- pk/EstrannaiseCloud.kt (NOUVEAU) : nuage d'incertitude — 32 courbes du
  posterior (échelonnées, déterministes) superposant les doses des
  porteurs ESE ; exclusif ESE (TFS/WHSAH sans posterior publié).
- ChartScreen/CurveChart : chip « Nuage » (off, activable si ESE affiché,
  coupure auto si ESE off), ChartSeries.cloud dessiné en alpha faible
  sous les courbes, hors échelles/labels/extrema, légende dédiée.
- Runtime 100 % sources analytiques : PKProfileStore.init retirée du
  démarrage, asset pk_profiles.json → src/test/assets (−550 Ko d'APK ;
  conservé pour les tests de fidélité RMS 0).
- 11 nouveaux tests (234 verts / 204 sans données locales) : fidélité,
  pics ±0,2 %, dégénérés, MCMC 313×6, nuage (dispersion/fenêtre/
  exclusivité/gardes), choicesForModel ESE=6, t½ analytique.
- Validé émulateur §16.ter (release, seed réel v1.7.0) : courbe ESE
  analytique affichée, chips Extend/Cloud, recommandation de prise de
  sang, 0 crash. versionCode 38 / versionName 1.9.0. Web sync v1.9.0
  (publié séparément).
2026-09-19 20:44:30 +02:00
f6be3d86a9 v1.8.2 : maintenance — perf moteur, fraîcheur UI, code/doc nettoyés
Session d'audit complet (code + doc, 46 fichiers + 4 docs) et application
des constats sûrs.

PERF MOTEUR PK (résultat identique — ordre de sommation préservé, 223
tests verts inchangés) :
- e2At/computeCurve : cutoffHours pré-calculé par traitement (le chemin
  chaud du tracé labs recalculait ~430 k appels/refresh), Bateman
  paresseux (inutile pour les traitements à profil), doses pré-groupées
  par traitement (fin des scans complets).
- AppLog : purge sans recopie ; ChangelogHelper : Regex précompilées ;
  CurveChart : SimpleDateFormat hors boucle de dessin.

FIX FRAÎCHEUR (#65/#66) :
- Accueil : clés produceState complétées (allTreatments, tConfig) —
  éditer un scaleFactor/le modèle T actualise la courbe sans attendre le
  tick de 60 s.
- Graphiques : clé labResults (liste entière, plus .size) — éditer la
  valeur d'une analyse actualise les courbes.

MAINTENANCE :
- Code mort supprimé (Repository/DAO sans appelants, currentLevel,
  scheduler2, variable morte, Spacer no-op) ; imports morts/redondants
  nettoyés (9 fichiers) ; qualifications raccourcies.
- Médiane de calibration factorisée (4 copies → medianRounded) ;
  double-filtre computeTConfigCalibration supprimé (soussumé) ;
  formatDose dédupliqué ; en-têtes/KDoc obsolètes mis à jour.

DOC : ~35 corrections (compteurs actuels 223/193 partout — §16, §14,
§8.bis ; TOC + 4 ancres réparées ; inventaire §8 à jour + fusions
ChartZoom/WhsahEngine + ajout LabTiming/AutoBackupRetention/
ExportFileNames ; titres CHANGELOG doublés [1.4.9]/[1.4.8] supprimés ;
§19 backup = manuel + auto ; versions « v1.2.0 »/« Gradle 8.9 »/« API 34 »
corrigées ; ratios #61 complétés 383/391 ; §21 item v1.8.1 + chronologie ;
§14 #65/#66 ajoutés). Rétention Ratios (#14 #61) : vérifié via
ScaleFactorWhsahReproTest — les 5 valeurs 3,3/15,9/89,6/383/391 existent.

Validé émulateur §16.ter (release, seed réel v1.7.0) : accueil (encart
reco), Analyses (creux 27/09 + notes), Graphiques, 0 crash.
versionCode 37 / versionName 1.8.2. Web non concerné (aucun changement).
2026-09-18 14:21:32 +02:00
2ec9f63f93 v1.8.1 : fix stabilisation (changements récents vus) + suggestion sur l'Accueil
Critique v1.8.0 : « changé d'ester, de dosage ET de posologie, et l'app
me disait stabilisée depuis février » — le proxy « 1ʳᵉ dose du traitement »
était aveugle aux changements récents.

- LabTiming.regimeStartMs (nouveau, internal) : le régime courant = la
  séquence terminale de doses à (ester effectif, dose mg, écart
  inter-doses) CONSTANTS — l'écart est comparé exactement (7 j ± 1 h casse
  le régime) : tout changement récent réinitialise la stabilisation et la
  reco saute au premier creux post-stabilisation. KDoc complet (règle +
  justification conservatrice des intervalles irréguliers).
- LabRecommendation.ester = l'ESTER EFFECTIF de la dernière dose (override
  compris) — l'encart/carte racontent le bon ester.
- HomeScreen : carte compacte « Prochaine prise de sang (suggestion) »
  (creux daté + créneau associé + mention de stabilisation SEULEMENT si le
  régime n'était pas déjà stable) — même calcul que la page Analyses
  (demande v1.8.1 : suggestion aussi sur l'Accueil).
- 3 nouveaux tests (223 verts / 193 sans données locales) + lint :
  changement de dose / d'intervalle / d'ester → réinitialisation épinglée.
- Validé émulateur §16.ter (release, seed réel v1.7.0) : la carte passe de
  « stabilisé depuis le 06/02 » à « EEN pas stabilisé avant le 29/09 —
  premier creux fiable après ton dernier changement », creux 27/09 22:43,
  0 crash. versionCode 36 / versionName 1.8.1. Web sync v1.8.1.
2026-09-17 19:48:20 +02:00
8c19c8f295 v1.8.0 : recommandation de prochaine prise de sang (page Analyses)
Moteur pk/LabTiming.kt (PUR, miroir web js/pk/lab-timing.js) :
- traitement porteur = E2 injectable ACTIF à Posologie (sinon null) ;
- stabilisation = 1ʳᵉ dose du régime + 5 × t½ terminale (TFS/WHS
  analytiques ; Estrannaise : NOUVEAU PKProfileStore.terminalHalfLifeDays,
  refactor de la pente d'extrapolation de sample() — zéro duplication) ;
- creux = minimum de la courbe E2 prévisionnelle entre 2 injections
  (forme brute — sans calibration) ; ON SAUTE au premier creux dont le
  créneau est stabilisé (décision v1.8.0) ; filtres : ≥ now+6 h, jamais
  un creux déjà mesuré, horizon borné (ester ultra-long → null).

UI LabsScreen : carte « Prochaine prise de sang (suggestion) » (creux
exact + créneau associé + mention « ou la veille » + statut de
stabilisation + disclaimer) OU invite « renseigne une Posologie » quand
un injectable actif en est dépourvu (demande v1.8.0) — mutuellement
exclusives ; calcul produceState + tick minute.

Tests : LabTimingTest (8) + régression n°5 étendue (1) → 220 verts
(190 sans données locales) + lint. Validé émulateur §16.ter (release,
seed réel v1.7.0) : creux 20/09 10:37, injection EEN 20/09 11:20, EEN
stabilisé depuis le 06/02, 0 crash. versionCode 35 / versionName 1.8.0.
Web sync v1.8.0 (publié séparément).
2026-09-17 18:47:38 +02:00
23c62e85f2 v1.7.1 : fix « une seule note sur deux » dans les Analyses (Android) + régression n°5
- LabsScreen : labNotesForDisplay (helper PUR, testé) — notes DISTINCTES
  d'une prise E2+T → DEUX lignes préfixées du marqueur (« E2 : … » /
  « T : … ») ; note identique (dialog commun) → une seule ligne
  (dédupliquée) ; vides ignorées ; note seule → brute (v1.0 préservé).
  L'ancien code ne prenait que la première note non vide (hypothèse
  « toutes identiques » cassée par l'édition unitaire) : l'autre note
  était conservée mais perdue à l'affichage.
- RegressionUserCase5Test (7 tests) : NOUVEL export réel v1.7.0
  (local-test-data/backup-v1.7.0.json, gitigné) — 3 traitements dont CPA
  oral, 64 doses, 28 labs ; la dernière prise porte EXACTEMENT le cas du
  bug (deux notes distinctes E2/T) : les deux affichées, aucune perdue ;
  plausibilité moteur (CPA oral + EEn actif + EV inactif simulé),
  prévision 7 j, notes de doses préservées. Aucune valeur de santé en dur
  (data-driven, garde §8.bis).
- LabsGroupingTest : +5 tests sur labNotesForDisplay. 211 verts
  (181 sans données locales) + lint vert.
- Validé émulateur §16.ter : APK release seedé avec l'export v1.7.0 →
  écran Analyses affiche « E2 : … » ET « T : … », 0 crash.
- versionCode 34 / versionName 1.7.1. Web sync en miroir (web v1.7.1).
2026-09-17 12:29:44 +02:00
2c58418604 v1.7.0 : sauvegarde automatique quotidienne (opt-in) + unités des axes
AUTO-BACKUP JOURNALIER (en plus de l'export manuel, inchangé) :
- AppSettings : enabled / treeUri (SAF persisté) / keep (1..30, défaut 7) /
  lastRun ; BackupManager.writeAutoBackup : tree URI → createDocument d'un
  fichier horodaté (ExportFileNames.autoBackupFileName — signature
  LocalDateTime, règle anti-#48) → écriture via writeBackup TEL QUEL
  (leçon #45) → purge par AutoBackupRetention (PURE, testée : ne touche
  JAMAIS exports manuels/logs/étrangers, keep clampé ≥ 1).
- AutoBackupWorker + AutoBackupScheduler (pattern AlertWorker) : périodique
  24 h (KEEP au démarrage, no-op si désactivé), runNow à l'activation
  (premier backup immédiat), statut dernier run persisté + AppLog ;
  proguard : -keep explicite du worker (réflexion WorkManager, leçon #64).
- Paramètres : carte « Sauvegarde automatique quotidienne » — switch (ON
  sans dossier → picker SAF), dossier persisté (takePersistableUriPermission),
  nom lisible (folderDisplayName), copies conservées, statut OK/ÉCHEC.
- Validé émulateur §16.ter (APK release + données réelles seedées) : dossier
  Download/HormoneTrack choisi via le picker SAF, premier backup v2 écrit
  immédiatement (3 traitements/52 doses/26 labs, importable), rétention
  keep=2 vérifiée (3 vieux autos supprimés, export manuel intact), statut
  « OK (date) », 0 crash.

UNITÉS DES AXES (remontée « jamais ajoutées depuis v1.0 ») : pg/mL (E2,
gauche) / ng/mL (T, droite) au sommet des colonnes de labels (padTop
12→26 dp) — validé émulateur (screencap). Portées web (v1.7.0 web sync).

9 nouveaux tests (200 verts / 177 sans données locales) + lint vert.
versionCode 33 / versionName 1.7.0.
2026-09-16 19:52:04 +02:00
76a9c0eb63 Fix #64 : « Tracé labs » vide en release seule (R8 full mode) + étape émulateur obligatoire
- proguard-rules.pro : -keep class LabTrajectoryModel { *; } — le mapping
  R8 montrait R8$$REMOVED$$CLASS (inlining du calcul + du synthétique
  $default dans le producer) : courbe vide en release, debug et 191 tests
  JVM aveugles. Validé émulateur §16.ter sur l'APK release re-buildé
  (données réelles seedées) : ancrée + prolongée + avertissement + 0 crash.
- Checklist §16 : NOUVELLE étape 3.bis — validation émulateur sur l'APK
  RELEASE obligatoire AVANT le tag (leçon #64 : jamais oublier le
  simulateur) + procédure de re-tag si fix avant publication.
- §14 : bug #64 complet (symptôme, diagnostic mapping, fix) ; §2 et
  CHANGELOG mis à jour.
2026-09-13 03:43:11 +02:00
0768b83a88 Docs : v1.6.0 — prolongation tracé labs (§7.10.bis), session §2, compteur 191 tests
- DEVELOPPEMENT : §7.10.bis (recette ρ constant, horizon, split LAB/LABX,
  avertissement, limites), session v1.6.0 §2, §8 191/168 tests, §11
  séries LAB/LABX, §21 checklist manuelle, TOC 7.10/7.10.bis, fix coquilles
  (E3→E2, efecto→effet, trais→traits), checklist §16 compteur à jour.
- CHANGELOG : entrée [1.6.0]. README : statut + puce Tracé labs prolongé.
- GUIDE_INSTALLATION : chip Prolonger + avertissement (§8).
2026-09-12 23:49:24 +02:00
04f6b8bd64 v1.5.0 : « Tracé labs » — courbe hybride ancrée sur les labs (M(t)×ρ(t))
Demande débattue avec l'utilisatrice (hésitation assumée, décision v1.5.0)
: une courbe biologiquement plausible MAIS passant par tous les labs.
- pk/LabTrajectoryModel.kt (pur) : M(t) = modèle PK brut (scaleFactor
  forcé 1) ; ρ_k = lab/M(t_k) interpolé LOG-LINÉAIREMENT entre labs
  consécutifs — passgage exact garanti, morphing d'amplitude continu.
  Garde anti-#61 (labIsSignificant : lab hors fenêtre d'action PAS un
  ancrage). E2 uniquement, fenêtre [1er ; dernier lab], < 2 labs → vide.
  Indépendante de la calibration (elle est SA propre référence — sinon
  double correction, épinglé). JAMAIS dans levelAt/alertes/rappels.
- UI : chip 'Tracé labs' (off par défaut) dans la rangée modèles,
  horizontalScroll CONFINÉ à la rangée (chip 4ᵉ coupé hors fenêtre —
  constat émulateur) ; couleur ChartLabTrajectory #C2185B dashed ;
  ChartSeries.showExtrema (tracé labs hors pics/creux) ; leçon #63 :
  clé 'LAB' exclue de la boucle légende (sinon légende TFS dupliquée).
- 11 tests LabTrajectoryModelTest (183 verts total, 161 sans données
  locales) + lint ; validé émulateur §16.ter (courbe passe exactement
  sur le lab 248 avec seed réel, 0 crash).
- Docs : §7.10 (maths + décisions), §2, §8, §16, §21, CHANGELOG [1.5.0],
  README (status + feature).
2026-09-11 16:11:02 +02:00
3e4c7c44b3 v1.4.10 : fix #62 — pan mort sur la vue 24 h : les deltas de doigt (< 1 h) étaient tronqués à 0 à chaque événement → cumul fractionnaire (panDeltaHours, pur + 4 tests : cumul 24 h, 7 j, négatifs, dégénérés) ; limitation de test documentée (§16.ter : l'émulateur headless ne délivre pas les moves intermédiaires au chart — pan non testable par adb, validation sur Pixel 9) ; retrait de l'instrumentation de diagnostic ; 172 tests verts + lint 2026-09-07 19:28:41 +02:00
9b83533d72 v1.4.9 : fix #61 — calibration tombant à ×2,21 (auto ET manuelle) : garde de significativité labIsSignificant (prédiction ≥ 15 % du max observé, labs par timestamp croissant) sur les 3 pipelines — une dose EEn de test de janvier + des labs de janvier-mars produisaient des ratios aberrants (×3,3 → ×391) ; facteur WHSAH retombe à 0,55 (reproduit et épinglé par ScaleFactorWhsahReproTest) ; hint UX (auto ON → facteur manuel ignoré à l'affichage) ; nettoyage CHANGELOG (4 titres orphelins doublés) ; 168 tests verts + lint 2026-09-07 18:54:43 +02:00
22d4c66d81 v1.4.8 : calibration PAR MODÈLE PK (#60) — le graphique calcule une AutoCalibrated par modèle affiché (modelOverride propagé à computeEsterScaleFactors et computeTKPerEster) → chaque courbe calibrée passe par les labs (plus de facteur partagé absurde ×2,21 sur WHSAH) ; Home inchangé (modèle stocké) ; CalibrationPerModelTest (5 tests : couverture 3 modèles, délégation unique, k T par modèle, cas dégénérés) ; 164 tests verts + lint 2026-09-07 18:28:15 +02:00
1c9d2e4e6a v1.4.7 : toggles de modèles alignés sur les traitements à profil (#58) + calibration universelle épinglée (par ester, toutes les courbes) + fix #59 : chip de traitement affichait 'Estrannaise' pour WHSAH (ternaire → helper modelLabelRes centralisé) ; 158 tests verts + lint 2026-09-07 17:45:44 +02:00
ca917ba772 v1.4.6 : 3ᵉ modèle PK WHSAH (fit license-free du WHSAH Collective publié dans Mona — crédité) superposable au même niveau qu'Estrannaise/TFS : dispatch parallèle, 6 presets, dropdown 3 choix, chip + courbe verte/violette, choicesForModel (TFS=7/WHS=6/ESE=3) ; #57 : l'écran Graphiques défile (rangée toggles coupée sous le pli) ; 10 tests (155 verts) + lint ; validé émulateur (3 légendes + 3 courbes de couleurs distinctes) 2026-09-07 17:01:28 +02:00
15a7e6614c v1.4.5 : fix #55 — projection paraissant décalée d'un jour (génération exacte confirmée par régression #4 ; illusion = absence de marqueurs + labels à minuit UTC) → marqueurs de doses (prévisionnels pointillés + réels discrets) + xLabelTicks à minuit LOCAL dans le fuseau CHOISI (Paramètres) ; fix #56 — boucle de recomposition (nowMs relu à chaque frame → endMs dérive → main thread saturé, taps perdus) → nowMs mémoïsé sur le tick ; 6 tests (145 verts) + lint ; validé émulateur (chips réactifs, marqueur vertical rendu) 2026-09-07 11:27:38 +02:00
045ab5d060 v1.4.4 : fix #54 — prévision inaccessible depuis v1.4.1 : (a) captures FIGÉES dans pointerInput(Unit) (futurePanHorizon lu à 0 pour toujours → pan futur interdit) → rememberUpdatedState + garde de source ChartScreenSourceGuardTest ; (b) toggle sans effet visible (endMs=now → 0 dose générée) → forecastExtensionHours (extension jusqu'au 1er créneau, sans déplacer le début) ; 3 tests (140 verts) + lint ; validé émulateur (drag gauche → panHours<0, courbe dessinée au-delà de maintenant) 2026-09-07 07:56:32 +02:00
3fa7cf97ea v1.4.3 : fix #53 — le delta 'vs il y a 6 h' de l'accueil comparait en réalité il y a 24 h (firstOrNull sur la fenêtre 24 h) → pointHoursBefore (point au plus petit écart ≥ 6 h) + 4 tests ; régression n°4 épinglée sur le 4ᵉ export réel (contribution EEn J+1 < 12 %, plateau d'équilibre — comportement pharmacocinétique normal confirmé avec le vrai moteur) ; 137 tests verts + lint 2026-09-07 07:35:54 +02:00
633c6debf4 v1.4.2 : seuils d'alerte configurables E2/T (Paramètres, opt-in, validés haut>bas) — cartes d'avertissement sur l'accueil (niveau ESTIMÉ, calibration incluse) ; notification via worker WorkManager périodique 15 min + check one-time au save, anti-spam par état persisté (Alerts.encodeState/shouldNotify), canal dédié ; backup JSON v2 avec les paramètres utilisateur (langue/auto-cal/seuils, rétrocompat v1, -keep R8) ; 13 nouveaux tests (129 verts) + lint 2026-09-07 00:02:22 +02:00
922d49841f v1.4.1 : prévision étendue et sans saut — activer le chip ne déplace plus la fenêtre (endMs = now − panHours signé, panHours < 0 = futur borné par l'horizon 12×Posologie [30 j ; 1 an], clamp unique clampPanHours) ; delta en jours sur la carte Prochaine dose (> 24 h : '5 j 2 h · sam. 6 18:00') ; 5 tests (112 verts) + lint 2026-09-06 23:00:42 +02:00
208ba56843 v1.4.0 : modèle Transfem Science reconstruit sur la méta-analyse officielle (V3C forme close, 7 esters, pics de l'article reproduits à ~1 % — TransfemScienceModels + 5 tests) ; fix #52 : rappels sur la grille Posologie (nextReminderFireFor, re-programmation après notif et 'Pris', ReminderScheduleTest ×7) ; nouveaux presets EB/EC/ECS/PEP ; 102 tests verts + lint 2026-09-06 22:28:18 +02:00
fb942e4a24 v1.3.5 : agenda réparé de bout en bout (remontée par les logs AppLog exportés) — #50 ensureCalendar : CALLER_IS_SYNCADAPTER exige account+account_type ; #50bis deleteEvent : même défaut, 'supprimé' logué mais event restant ; #51 buildTreatment perdait calendarEventId → événements orphelins ; log agenda véridique (3 messages) ; cycle ON→save→OFF→save validé émulateur ; 90 tests verts + lint 2026-09-06 14:46:50 +02:00
71659b5647 v1.3.4 : 2 crashs v1.3.3 reproduits sur émulateur (données réelles) et corrigés — #47 Doses : stringResource hrt_duration 3 placeholders vs 2 args (né v1.3.1) ; #48 export logs : LocalDate.format('…-HHmm') levait UnsupportedTemporalTypeException au tap → util ExportFileNames + 3 tests ; #49 notif dose StringFormatMatches ; lint filet bloquant (app/lint.xml) ; recette émulateur §16.ter + scripts/seed-emulator.py ; 90 tests verts 2026-09-06 12:38:57 +02:00
cb64f0e57e v1.3.3 : 3 bugs racines de l'audit de reprise corrigés — permissions agenda absentes du manifest (#44), export logs réutilise BackupManager.writeBackup (+AppLog fail-safe) (#45), bump de version commité avant le tag (#46) ; doc de dev rafraîchie (DB v3, targetSdk 36, Pixel 9 /e/OS, #38/#43, §19-21) 2026-09-06 11:24:12 +02:00
af8d40726a v1.3.2 : 3 bugs remontés corrigés (toggle agenda, export logs, dialog changelog récurrent) + données réelles mises à jour
- FIX toggle agenda : Posologie requise (refus + message si absente),
  permission vérifiée RÉELLEMENT via ContextCompat au save (le flag
  remembered pouvait être faux après recomposition), AppLog à chaque étape
- FIX export logs : réutilise le pattern éprouvé de l'export JSON
  (openOutputStream wt), chaque étape journalisée dans AppLog
- FIX dialog changelog : version vue mémorisée AVANT l'affichage (sinon
  réapparaissait à chaque réouverture), titre = BuildConfig.VERSION_NAME
  (documenté : la version de l'APP, pas du contenu)
- RegressionUserCase3Test mis à jour (3 traitements : EV inactif + EEn
  actif + CPA oral avec posologie 1 j, 51 doses, 26 labs)
- versionCode 16, versionName 1.3.2
2026-09-06 10:45:00 +02:00
fa5d2df5a5 v1.3.1 : en-tête « temps sous THS » sur Doses, logs de diagnostic exportables, fix toggle agenda (callback async)
- DosesScreen : carte « Sous THS depuis le … » (HrtDuration, mois = 30 j estimés)
- AppLog : journal horodaté buffer circulaire 500 lignes persisté
  filesDir/debug-log.txt ; export SAF + effacement dans Paramètres ;
  alimenté par agenda/permissions/import/export/erreurs
- FIX toggle agenda (bug remonté) : le résultat de permission arrive
  ASYNCHRONE — le test synchrone post-launch() était toujours faux → le
  callback active le switch + journalisation
- AppLogTest (4) + HrtDurationTest (5) → 87 tests verts
- versionCode 15, versionName 1.3.1
2026-09-06 10:16:49 +02:00
14f15e69cf v1.3.0 : dialog « Nouveautés » post-update, événements d'agenda récurrents, version + lien releases dans Paramètres
- Dialog « Nouveautés » (v1.3.0) : au démarrage, si la version installée est
  plus récente que la dernière vue (DataStore changelog_seen_version), un
  AlertDialog affiche les sections CHANGELOG non vues (ChangelogHelper
  sectionsSince + comparaison SemVer NUMÉRIQUE — 1.2.9 < 1.2.10, lexicographique
  aurait tort) ; asset changelog.md synchronisé à chaque build par la tâche
  Gradle copyChangelog (gitignoré) ; fermable, ne réapparaît pas avant la
  prochaine mise à jour
- Événements d'agenda récurrents (CalendarEvents.kt) : calendrier LOCAL dédié
  « HormoneTrack » (CalendarContract, ACCOUNT_TYPE_LOCAL), événement avec
  RRULE FREQ=DAILY;INTERVAL=N dérivé de la Posologie (arrondi demi-supérieur
  EXPLICITE floor(x+0.5) — kotlin.math.round arrondit les ties vers l'entier
  PAIR : 6,5 → 6, piège épinglé), début = prochaine occurrence à l'heure de
  rappel (ou 12:00) ; switch dans l'éditeur sous « Rappels », permissions
  WRITE_CALENDAR + READ_CALENDAR demandées à l'activation ; suppression/
  recréation au save ; id stocké sur Treatment (Room v3, MIGRATION_2_3)
- Paramètres : version installée (BuildConfig.VERSION_NAME, buildConfig=true)
  + lien cliquable vers les releases Gitea
- Tests : ChangelogHelperTest (8) + CalendarRruleTest (3) → 78 tests verts
- versionCode 14, versionName 1.3.0
2026-09-06 08:30:31 +02:00
95ccb1cf9e v1.2.10 : sens des boutons de zoom inversé (+ = zoom avant / fenêtre courte, convention carte — retour utilisateur) ; docs ; versionCode 13 2026-09-05 23:36:30 +02:00
c888a718d5 v1.2.9 : zoom du graphique (pinch + boutons, 6 h → 300 j, focal stable, pas adaptatif) + disclaimer IA dans le README
- ChartScreen : detectTransformGestures unique (pan 1 doigt + pinch 2 doigts,
  pas de conflit de consommation) ; zoom autour du point focal (le temps sous
  le doigt reste fixe) ; plage bornée 6 h → 300 j (MIN/MAX_RANGE_H) ;
  boutons − / + en accessibilité
- CurveChart : stepForRange (15 min ≤ 12 h, 30 min ≤ 24 h, sinon 1 h) passé à
  computeCurve (courbes lisses à fort zoom) ; labels X adaptatifs 1 h/3 h
- README : disclaimer IA en en-tête (développé avec l'IA, contribution
  humaine essentielle = feedback/retours/suggestions, renvoi §2 doc dev)
  ; DEVELOPPEMENT §1 mention IA-assistée, §11 zoom, §8 67 tests
- ChartZoomTest : 3 tests stepForRange — 67 tests verts
- versionCode 12, versionName 1.2.9
2026-09-05 22:51:27 +02:00
7e707b2a39 CHANGELOG : entrée v1.2.8 (renommage Posologie) — étape 3 de la checklist §16.bis, appliquée à la lettre cette fois 2026-09-05 22:25:07 +02:00
9c27c4d9e2 v1.2.7 : prévision robuste aux oublis/retards + rattrapage complet de la doc de dev
- generateForecastDoses : les créneaux déjà passés (oubli d'injection) ne sont
  plus simulés — avant, dernier+intervalle tombait dans le passé → faux pic
  dans l'historique + rythme décalé ; la prévision démarre au premier créneau
  strictement futur, au rythme configuré
- un retard décale toute la prévision (part de la dernière prise réelle) —
  comportement voulu, épinglé par 2 tests
- doc §7.3b corrigée (mismatch doc-code détecté par l'utilisatrice)
- docs : §13 écrasement, §19/§20 import, §14 #34/#35, §21, historique
  (lignes v1.2.2/6/7), footer — vérification intégrée dans le script de patch
- versionCode 10, versionName 1.2.7 ; 64 tests verts
2026-09-05 22:14:19 +02:00
6e193db969 CHANGELOG : entrée v1.2.6 (extrema avec valeurs + import écrasement) 2026-09-05 21:49:58 +02:00
a77fd283e3 v1.2.5 : builds release optimisés (R8 + shrink, 20 Mo → 2,4 Mo), APK release + debug publiés
- build release signé avec la CLÉ DEBUG (même signature → mise à jour
  par-dessus l'app installée sans perte de données) mais avec R8 full mode
  + isShrinkResources : 20 Mo → 2,4 Mo, démarrage et fluidité améliorés
- proguard-rules.pro : -keep explicites pour la réflexion Gson (data.model,
  BackupData, TConfig) — sinon export/import JSON cassé en release uniquement
- release v1.2.5 : deux APK publiés (release recommandé 2,4 Mo + debug 20 Mo)
- docs : §16.bis (deux APK par release, garde-fous R8), CHANGELOG
2026-09-05 20:27:05 +02:00
eb7c4c1110 v1.2.5 : edge-to-edge propre (fix du double espace en haut de l'écran)
- enableEdgeToEdge() dans MainActivity (Material You)
- insets consommés UNE SEULE FOIS : TopAppBar M3 = barre de statut,
  NavigationBar = barre de navigation
- Scaffold racine + imbriqués (Doses/Analyses) : contentWindowInsets = 0
  pour ne pas cumuler (avant : fenêtre AppCompat + padding interne TopAppBar
  = double espace vide en haut)
- docs : §10 (pattern insets), §14 bug #33, historique, CHANGELOG
- versionCode 8, versionName 1.2.5
2026-09-05 20:15:33 +02:00
5d04667ea9 v1.2.4 : traitement inactif = drapeau administratif, jamais un filtre de données
Bug remonté : passer un traitement à inactif effaçait sa simulation du graphique
(Home/Chart collectaient activeTreatments) et faussait la calibration, tout en
permettant d'y loger des doses.

- HomeScreen / ChartScreen : simulation + calibration avec ALLTreatments
  (actifs + inactifs) — l'historique EV d'un traitement archivé reste simulé
- Home : chips « Log rapide », dialog de création et carte « prochaine dose »
  sur les traitements ACTIFS uniquement
- DosesScreen : création → actifs uniquement ; édition d'une dose existante →
  tous les traitements (rattachement préservé)
- Rappels : annulés pour les inactifs (save, boot via getAllOnce, prochaine dose)
- RegressionUserCase3Test (6 tests, données réelles HORS dépôt gitignoré) :
  transition complète EV inactif (29 doses) → EEn actif (9 doses), 22 labs sur
  les deux périodes, calibration par période couvrant EV + EEN, k T par ester,
  continuité de courbe, niveau actuel — 62 tests verts
- docs : §6.bis (sémantique isActive), §14 bug #32, §8, §21, CHANGELOG,
  README, GUIDE ; versionCode 7, versionName 1.2.4
2026-09-05 20:05:07 +02:00
e1b155b7fc Docs v1.2.3 : données de test hors dépôt (§8.bis, sécurité), releases Gitea avec APK (§16.bis), statut à jour
- §8.bis : les données de santé de test vivent dans local-test-data/ (gitignoré),
  tests Assume-skippés si absentes, historique nettoyé avant premier push
  (filter-branch) — aucune donnée de santé dans le dépôt ni l'historique
- §16.bis : procédure de release Gitea (API + upload APK par tag)
- README : statut v1.2.3, section Vie privée (dépôt sans données), Git à jour
2026-09-05 19:24:31 +02:00
34a7b588be v1.2.3 : pics/creux estimés sur le graphique (toggle) + calibration T par période d'ester
- detectExtrema : extrema locaux stricts (plateaux fusionnés), alternance
  pic/creux imposée, filtre d'amplitude (2 pg/mL E2, 0,02 ng/mL T) — sémantique
  zigzag documentée (oscillation sous le seuil = un seul pivot)
- CurveChart : triangles ▲▼ aux extrema de chaque série (couleur de la série),
  chip « Pics / creux » + légende (désactivé par défaut)
- computeTKPerEster : le k du modèle T est calibré PAR PÉRIODE D'ESTER (labs T
  attribués à la période de la dernière dose ≤ lab, médiane) — la suppression T
  diffère valerate vs enanthate ; la courbe T utilise le k de l'ester ACTIF à
  chaque instant (activeEsterAt + curseur de doses triées dans computeCurve)
- Fix : la calibration T automatique utilise désormais l'E2 CALIBRÉE
  (scalePerEster) — calibrer contre l'E2 brute faussait les k
- levelAt/currentLevel : paramètre tKPerEster (fallback = tConfig.k stocké)
- ExtremaTest : 6 tests (alternance, monotone, plat, court, filtre d'amplitude,
  anti-corrélation E2/T) — 56 tests verts
- versionCode 6, versionName 1.2.3 ; docs mises à jour (§7.5/§7.6, §11, README, guide)
2026-09-05 18:58:10 +02:00
c71a50429d v1.2.2 : prise de sang E2 + T en une entrée, affichage côte à côte, toggle T masque aussi les labs T
- LabDialog : mode création avec deux sections (E2 et T, chacune optionnelle,
  au moins une valeur requise) → insère 1 ou 2 LabResult au même timestamp ;
  mode édition inchangé (un seul marqueur, pré-rempli)
- LabsScreen : regroupement par timestamp (groupLabsForDisplay : E2 avant T,
  autres ensuite, tri desc) → affichage « E2 306 pg/mL · T 45 ng/dL » côte à
  côte ; tap sur une paire → sélecteur « Quelle entrée modifier ? » puis
  édition pré-remplie ; suppression = la prise entière (confirm nommant les
  valeurs) ; formatLabValue préserve les décimales
- ChartScreen : le toggle T masque désormais aussi les labs T (avant : seuls
  les points restaient visibles)
- LabsGroupingTest : 4 tests de regroupement (48 tests verts au total)
- versionCode 5, versionName 1.2.2 ; docs mises à jour
2026-09-05 18:33:19 +02:00
d9471cadaf v1.2.1 : calibration par période d'ester, édition des labs, tap accueil→Graphiques, auto-cal fusionnée E2+T, lab T en unité aberrante neutralisé
- computeEsterScaleFactors : chaque lab est attribué à la période d'injection
  dans laquelle il tombe (dernière dose E2 <= lab → son ester) ; facteur final
  par ester = médiane des ratios de cette période. Corrige le mélange des
  périodes valerate/enanthate qui gonflait les courbes (250-375 pg/mL)
- e2At/computeCurve : paramètre scalePerEster (chaque dose scalée par le facteur
  de SON ester, fallback = scaleFactor stocké du traitement)
- autoCalibrated : renvoie esterScales + tConfig recalibré, traitements inchangés
- convertTToNgMl : conversion défensive pg/mL et µg/L (un lab T « 38 pg/mL »
  écrasait l'axe T et rendait la courbe T invisible — remonté utilisateur)
- LabDialog create/edit : tap sur une ligne de l'écran Analyses → édition
  pré-remplie (formatLabValue préserve les décimales)
- Home : tap sur le mini-graphique → écran Graphiques + mini-légende E2/T
- Settings : UNE seule option « Calibration automatique (E2 + T) » (E2 par
  période d'ester + modèle T), placée au-dessus des réglages T manuels
- RegressionUserCase2Test : 2e export réel épinglé (9 doses EEn/TFS, 8 labs,
  fréquence 6 j) — vérifie l'état d'équilibre EEn (~270 pg/mL calibré, cohérent
  labs 306/248 ; non calibré ≈ 367 = les « 375 » rapportés)
- V120FeaturesTest : attribution des labs par période d'ester + scalePerEster
- 44 tests verts ; versionCode 4, versionName 1.2.1
- Docs : CHANGELOG, DEVELOPPEMENT (§7.6 réécrit), GUIDE, README
2026-09-05 17:38:08 +02:00
e8f87c096d Documentation v1.2.0 : changelog, guide (fonctions du graphique), doc de dev (AGP 9, migrations, panoramique, prévision, auto-cal), README (git, features)
- docs/CHANGELOG.md : entrée v1.2.0 complète
- docs/DEVELOPPEMENT.md : versions épinglées à jour, note Material Expressive
  (API encore internal), migration Room v1→v2, §7.3b (override modèle/prévision/
  auto-cal), §8 (36 tests), §10/§11 (panoramique, multi-séries), §16 (workflow git),
  §18 (disque), historique des sessions
- README : statut v1.2.0, features à jour, section Git (tags de release)
- GUIDE : nouvelle section « Fonctions du graphique » (panoramique, toggles
  modèles, prévision, calibration auto)
2026-09-05 16:58:32 +02:00