59 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
0bb259d12d Docs : audit post-release v1.10.0 — §3 (uiautomator), §5 (EstrannaiseModels, demande #69), §8 (259/225), §16 (259/225 + 14 UI), §21 (2 points manuels v1.10.0), README FR/EN (plage releases) 2026-09-27 23:17:09 +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
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
dd5a9e3013 Docs : audit doc↔code v1.5.0 — effectifs réels + leçon #63 (§14)
Constats de l'audit systématique (leçon #44 : toute affirmation doit être
vérifiée dans le fichier réel) :
- effectifs de régression RÉELS : 4 classes = 6/6/6/5 = 23 tests (doc
  disait 4 pour la classe 4) → « 160 sans les données locales » (183−23)
  corrigé dans §8, §8.bis, §16, footer et README ; les vieux chiffres
  (44/87 v1.3.x, 150/172 v1.4.x) épinglés comme historiques
- §14 : entrée #63 ajoutée à l'historique des bugs (elle était citée en
  §7.10 mais absente de la liste « à ne pas réintroduire »)
2026-09-11 16:27:46 +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
e508607919 Docs : portage web migré vers son dépôt séparé HormoneTrack-web (v1.4.10)
Le portage navigateur (ex-web/) vit désormais dans ~/projects/HormoneTrack-web
— dépôt séparé sur les mêmes instances Gitea, historique 100 % propre (la
dette de confidentialité §8.bis reste confinée à ce dépôt), versions
alignées (web v1.4.10 = portage de l'Android v1.4.10), changelogs
totalement séparés.
- §16 : étape 4.bis de la checklist de release = sync web (checklist §10
  du dépôt web) ; publication Gitea web gelée tant que la parité qualité
  n'est pas validée (commit + tag seulement)
- README §Git : note sur le dépôt web apparié
2026-09-08 14:36:30 +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
4045614f3b Docs + nettoyage : TOC de la doc de dev complétée (7.8/7.9/9.bis/16.ter), structure des tests dans le README, imports/constantes morts supprimés (Calendar, DAY_MS) — dernier tour de maintenance de la session 2026-09-07 19:07:55 +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
c1c2386ab2 Tests + docs v1.4.0 : TransfemScienceEngineTest (câblage moteur↔V3C : EB→pic article, EB/ESE→0, coupure PEP 10×t½, override TFS via computeCurve) ; ReminderScheduleTest : grille sous-quotidienne épinglée (limitation §19) ; compteurs 107/89 + paliers de test §16 2026-09-06 22:32:14 +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
6c7f2fa2e2 Docs : audit de fraîcheur complet — compteurs de tests (87), TOC (.bis), §3 buildConfig, §16 workflow, §8.bis, README versions, GUIDE logs
- DEVELOPPEMENT : §16 (87 tests), §16.bis étape 2 (87), footer (87/87),
  §8.bis (44 au lieu de 87), §3 (buildFeatures buildConfig), TOC (§6.bis,
  §8.bis, §16.bis ajoutés ; titre §16 harmonisé), README (releases v1.1.0 →
  v1.3.1), GUIDE (logs de diagnostic)
- 87/87 tests verts, scan confidentialité : 0 hit
2026-09-06 10:23:43 +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
dcf51d1d15 Licence GPL-3.0 + anonymisation docs (surnoms/facteurs ODS, chemin Owncloud, nom de clé SSH) 2026-09-06 08:58:08 +02:00
506b86f5f4 Docs : farewell synchronisé (12 releases avec APK vérifiés) ; piège lexicographique des tags épinglé (§14 #38, §16.bis) ; README/GUIDE deux instances 2026-09-06 08:47:55 +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
0eaaea40c1 v1.2.8 : « Fréquence d'injection » renommé « Posologie » ; script : renommage d'asset après upload (quirk Gitea)
- strings EN « Dosage » / FR « Posologie » ; hint « dernière prise » (au lieu
  de « dernière injection ») — adapté aux voies non injectables
- quirk Gitea découvert : le nom d'asset passé à l'upload peut être normalisé
  (observé : -release.apk → .apk générique) → le script renomme l'asset via
  l'API d'édition (PATCH …/assets/{id}) après chaque upload
- release v1.2.8 publiée avec les 2 APK (release 2,5 Mo + debug 21 Mo)
- docs : §16.bis quirk, §8 (64 tests), historique, footer, README
- versionCode 11, versionName 1.2.8
2026-09-05 22:29:08 +02:00
55d0e5134f UI : « Fréquence d'injection » renommé « Posologie » (terme inadapté aux voies non injectables) ; hint généralisé « dernière prise » ; docs à jour
- strings EN (Dosage) / FR (Posologie) ; freq_hint : « dernière injection » →
  « dernière prise » (les deux langues)
- README, GUIDE, DEVELOPPEMENT §10/§21 : mentions mises à jour
- versionCode 11, versionName 1.2.8 ; 64 tests verts
2026-09-05 22:24:24 +02:00
b96571856a Docs : guide d'installation — section « télécharger sans compiler » (releases Gitea, différences APK release vs debug) ; README statut v1.2.7/64 tests (replace silencieux raté corrigé) 2026-09-05 22:18:20 +02:00
e16d370b2f v1.2.6 : valeurs des pics/creux sur le graphique + import JSON en mode écrasement
- CurveChart : drawExtremum = triangle ▲▼ + VALEUR estimée de l'extremum
  (au-dessus d'un pic, en dessous d'un creux), couleur de la série
- Import JSON : mode ÉCRASEMENT (bug remonté : échec dès que l'app contenait
  des données — conflit d'IDs backup vs existants)
  · DAOs : deleteAll() sur les 3 tables (ordre enfants → parents, FK CASCADE)
  · BackupManager.importJson : wipe puis restauration (IDs conservés → FK
    valides), retourne aussi le tConfig du backup
  · SettingsScreen : après import → tConfig restauré dans DataStore + rappels
    reprogrammés (rescheduleAll) ; dialog renommé « Effacer & restaurer »
    avec avertissement explicite
- docs : §13 (écrasement), §11 (valeurs extrema), §14 #34, §16.bis CHECKLIST
  DE DÉPLOIEMENT complète numérotée (version → tests → docs → commit/tag →
  push 2 remotes → build 2 APK → releases 2 instances → smoke-test R8),
  §20/§21, CHANGELOG, README
- versionCode 9, versionName 1.2.6
2026-09-05 21:41:58 +02:00
fb9806afd3 Docs : audit de complétude post-release-build (§16 workflow assembleRelease, §16.bis les deux APK par tag, §20 keystore dédié restant, §19 smoke-test R8, historique, README) 2026-09-05 20:28:47 +02:00