From 7e7b6f808633aebcaeb3417a75d464815b8d5df4 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Fri, 21 Aug 2026 01:54:34 +0200 Subject: [PATCH] fix(backend): declare LastIngestAt in the SessionsIndex manifest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bbc7a1d8 stamped LastIngestAt into IncrementSessionEventCountAsync's merge entity intending it as Sessions-only ("deliberately not mirrored"), but that same entity flows through SyncSessionIndexAsync into the index merge — the field was already landing in SessionsIndex on every ingest batch, just undeclared. The fail-soft drift guard has been warning ~1,200x/h in prod since the 08-20 deploy, and a StartedAt-shift full rebuild would silently drop the column, making index-served session reads lose lastIngestAt that Sessions-served reads carry. Declare it properly instead of suppressing: ConditionallyProjected in SessionIndexFieldManifest, projected in BuildSessionIndexEntity (read side MapToSessionSummary already handled it), sample value pinned in the bidirectional manifest tests. Co-Authored-By: Claude Fable 5 --- .../SessionIndexFieldManifestTests.cs | 1 + .../Services/SessionIndexFieldManifest.cs | 1 + .../Services/TableStorageService.Sessions.cs | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/src/Backend/AutopilotMonitor.Functions.Tests/SessionIndexFieldManifestTests.cs b/src/Backend/AutopilotMonitor.Functions.Tests/SessionIndexFieldManifestTests.cs index 6a159402..d63375ac 100644 --- a/src/Backend/AutopilotMonitor.Functions.Tests/SessionIndexFieldManifestTests.cs +++ b/src/Backend/AutopilotMonitor.Functions.Tests/SessionIndexFieldManifestTests.cs @@ -90,6 +90,7 @@ public class SessionIndexFieldManifestTests ["DiagnosticsBlobName"] = "diag-0001.zip", ["DiagnosticsBlobDestination"] = "Hosted", ["LastEventAt"] = new DateTimeOffset(StartedAt.AddMinutes(44)), + ["LastIngestAt"] = new DateTimeOffset(StartedAt.AddMinutes(46)), ["ResumedAt"] = new DateTimeOffset(StartedAt.AddMinutes(10)), ["StalledAt"] = new DateTimeOffset(StartedAt.AddMinutes(20)), ["AvgApiLatencyMs"] = 513.5, diff --git a/src/Backend/AutopilotMonitor.Functions/Services/SessionIndexFieldManifest.cs b/src/Backend/AutopilotMonitor.Functions/Services/SessionIndexFieldManifest.cs index f3e8bdf0..e7921fec 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/SessionIndexFieldManifest.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/SessionIndexFieldManifest.cs @@ -78,6 +78,7 @@ namespace AutopilotMonitor.Functions.Services "DiagnosticsBlobName", "DiagnosticsBlobDestination", "LastEventAt", + "LastIngestAt", "ResumedAt", "StalledAt", "AvgApiLatencyMs", diff --git a/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Sessions.cs b/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Sessions.cs index ffea48ed..8fc6872f 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Sessions.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Sessions.cs @@ -225,6 +225,13 @@ namespace AutopilotMonitor.Functions.Services if (lastEventAt.HasValue) indexEntity["LastEventAt"] = EnsureUtc(lastEventAt.Value); + // Server-frame ingest stamp (IncrementSessionEventCountAsync merges this on every + // batch, so the index already carries it) — must be in this superset or a + // StartedAt-shift full upsert drops it and index-served reads lose the field. + var lastIngestAt = sessionEntity.GetDateTimeOffset("LastIngestAt")?.UtcDateTime; + if (lastIngestAt.HasValue) + indexEntity["LastIngestAt"] = EnsureUtc(lastIngestAt.Value); + var resumedAt = sessionEntity.GetDateTimeOffset("ResumedAt")?.UtcDateTime; if (resumedAt.HasValue) indexEntity["ResumedAt"] = EnsureUtc(resumedAt.Value);