From 2c9af6c684570aa89ee9a0635d496fba459777a7 Mon Sep 17 00:00:00 2001 From: Sylvain Bettinelli Date: Tue, 22 Sep 2026 09:20:30 +0000 Subject: [PATCH] =?UTF-8?q?Chaque=20pause=20effa=C3=A7ait=20jusqu'=C3=A0?= =?UTF-8?q?=2024=20points=20de=20trace=20d=C3=A9j=C3=A0=20parcourus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setPaused vidait `pendingRoute` pour « ne pas tracer la pause ». Mais ce tampon ne contient que des points DÉJÀ acceptés par le filtre et DÉJÀ comptés dans `distanceMeters` : ils attendent seulement leur écriture, qui ne part qu'au seuil de routeFlushThreshold (25). À 1 Hz, c'est une douzaine de points en moyenne, jusqu'à 24. L'écart était donc visible : la distance affichée les incluait, la HKWorkoutRoute non. Six arrêts sur une sortie VTTAE — feux, photos, ravito — et jusqu'à 144 points manquaient à la trace, avec un D+ recalculé depuis elle qui divergeait d'autant de celui affiché pendant la séance. Aucun log ne mentionnait l'effacement. Ils sont désormais écrits, via le même `Task { await flushPendingRoute() }` que le flush au seuil. Le raisonnement du commentaire d'origine visait le point qui SUIT la reprise, à des centaines de mètres de là — mais celui-là n'est pas encore dans le tampon quand la pause commence, et c'est `implausibleSpeed` qui l'écarte. Nettoyer la pause est l'affaire du filtre, pas du tampon d'écriture. Pas de course possible : `isPaused` est posé avant, et `ingest` sort dès qu'il est vrai — aucun point neuf ne peut s'ajouter pendant que le flush s'exécute. Relevé § 2.5 de docs/AUDIT-2026-09-22.md, seul constat de la section vérifié ligne à ligne avant correction. 88 tests Linux verts (RouteFilter compris), mais ils ne couvrent pas ce fichier : CoreLocation n'existe pas sur Linux. Co-Authored-By: Claude Opus 5 (1M context) --- ios/App/CoachWatch/LocationTracker.swift | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ios/App/CoachWatch/LocationTracker.swift b/ios/App/CoachWatch/LocationTracker.swift index a5d255e..3c1af90 100644 --- a/ios/App/CoachWatch/LocationTracker.swift +++ b/ios/App/CoachWatch/LocationTracker.swift @@ -142,9 +142,21 @@ final class LocationTracker: NSObject, ObservableObject { guard isTracking else { return } isPaused = paused if paused { - // Le point qui suivra la reprise serait à des centaines de mètres : - // on repart d'une origine neuve pour ne pas tracer la pause. - pendingRoute.removeAll() + // ⚠️ Ici se jouait une perte silencieuse. `pendingRoute` ne contient + // que des points DÉJÀ acceptés par le filtre et DÉJÀ comptés dans + // `distanceMeters` — ils attendent seulement leur écriture, qui ne + // part qu'au seuil de `routeFlushThreshold` (25). À 1 Hz, c'est une + // douzaine de points en moyenne, jusqu'à 24. Les jeter creusait un + // écart visible entre la distance affichée et la trace enregistrée : + // six arrêts sur une sortie, et jusqu'à 144 points manquaient à la + // HKWorkoutRoute, avec un D+ recalculé qui divergeait d'autant. + // + // On les écrit donc au lieu de les perdre. Le raisonnement du + // commentaire d'origine visait le point qui SUIT la reprise, à des + // centaines de mètres de là — mais celui-là n'est pas encore dans + // le tampon, et c'est `implausibleSpeed` qui l'écarte. Nettoyer la + // pause est l'affaire du filtre, pas du tampon d'écriture. + Task { await flushPendingRoute() } } }