- 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).
- #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).
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.
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
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.
- 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.
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.
- 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).
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 »)
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é
- 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
- 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
- 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
- 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