Skip to content

Commit 2b7a9d3

Browse files
committed
Fix sparse FSD drive detail attribution
Expand the drive detail FSD insights range to look around 24 hours so sparse counter bookends are included, preventing unknown/blank attribution for valid deltas. Adds backend and frontend regression coverage for the wider range behavior.
1 parent 1d67c53 commit 2b7a9d3

3 files changed

Lines changed: 50 additions & 4 deletions

File tree

internal/api/fsd/drive_aggregate_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,46 @@ func TestBuildDriveAnalytics_AttributesSynchronizedEvidenceToOneDrive(t *testing
101101
}
102102
}
103103

104+
func TestBuildDriveAnalytics_DriveDetailLookaroundIncludesSparseBookend(t *testing.T) {
105+
// Drive detail queries ±24h so previous.StartAt is before the last FSD
106+
// tick. A ±2 minute window left that bookend unattributed and the panel blank.
107+
start := at(t, "2026-03-02T12:18:00Z")
108+
end := at(t, "2026-03-04T12:30:00Z")
109+
driveStart := at(t, "2026-03-03T12:18:00Z")
110+
driveEndAt := at(t, "2026-03-03T12:30:00Z")
111+
distance := 6300.0
112+
samples := []Sample{
113+
trustedSample(SignalFSDDistance, at(t, "2026-03-03T09:00:00Z"), 10000),
114+
trustedSample(SignalDrivingDistance, at(t, "2026-03-03T09:00:00Z"), 50000),
115+
trustedSample(SignalFSDDistance, at(t, "2026-03-03T12:28:00Z"), 14500),
116+
trustedSample(SignalDrivingDistance, at(t, "2026-03-03T12:28:00Z"), 56300),
117+
}
118+
current := responseForRange(7, start, end, samples)
119+
previous := responseForRange(7, start.Add(-end.Sub(start)), start, samples)
120+
121+
analytics := BuildDriveAnalytics(current, previous, AnalyticsInput{
122+
CounterSamples: samples,
123+
Drives: []DriveRecord{{
124+
ID: 350,
125+
StartedAt: driveStart,
126+
EndedAt: &driveEndAt,
127+
DistanceM: &distance,
128+
}},
129+
}, time.UTC, true)
130+
131+
if len(analytics.ContributingDrives) != 1 {
132+
t.Fatalf("drives = %d, want 1", len(analytics.ContributingDrives))
133+
}
134+
drive := analytics.ContributingDrives[0]
135+
if drive.Confidence != ConfidenceEstimated {
136+
t.Errorf("confidence = %q, want estimated", drive.Confidence)
137+
}
138+
wantMeasured(t, drive.FSDDistanceM, 4500, "lookaround FSD distance")
139+
if len(drive.Evidence) == 0 {
140+
t.Fatal("evidence is empty; drive detail would show no positive counter increase")
141+
}
142+
}
143+
104144
func TestBuildDriveAnalytics_SparseIntervalAcrossDrivesIsAmbiguous(t *testing.T) {
105145
start := at(t, "2026-03-03T08:00:00Z")
106146
end := at(t, "2026-03-03T13:00:00Z")

web/src/features/driving/pages/DriveDetailPage.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,8 @@ describe('DriveDetailPage', () => {
494494
expect(screen.getByTestId('ai-coaching')).toHaveAttribute('data-drive-id', '42')
495495
expect(useFsdInsightsRangeMock).toHaveBeenCalledWith(
496496
'1',
497-
'2025-03-01T09:58:00.000Z',
498-
'2025-03-01T10:47:00.001Z',
497+
'2025-02-28T10:00:00.000Z',
498+
'2025-03-02T10:45:00.001Z',
499499
expect.any(String),
500500
true,
501501
)

web/src/features/driving/pages/DriveDetailPage.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,18 @@ export default function DriveDetailPage() {
6060
if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) {
6161
return { start: undefined, end: undefined };
6262
}
63+
// SelfDrivingMilesSinceReset is sparse. A ±2 minute window only served
64+
// the two-minute coverage-anchor and dropped the bookend samples that
65+
// actually close a drive's delta — drive detail then rendered Unknown
66+
// with "No positive counter increase". Look around far enough to load
67+
// those bookends and any intervening drives (so overlap stays honest).
68+
const lookaroundMs = 24 * 60 * 60 * 1000;
6369
return {
64-
start: new Date(startMs - 2 * 60_000).toISOString(),
70+
start: new Date(startMs - lookaroundMs).toISOString(),
6571
// The endpoint is half-open. One extra millisecond admits an anchor
6672
// exactly at the backend's two-minute coverage limit; later anchors
6773
// are still rejected by the attribution guard.
68-
end: new Date(endMs + 2 * 60_000 + 1).toISOString(),
74+
end: new Date(endMs + lookaroundMs + 1).toISOString(),
6975
};
7076
}, [drive]);
7177
const fsdQuery = useFsdInsightsRange(

0 commit comments

Comments
 (0)