mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 21:19:38 +00:00
bbc7a1d8b2
LastEventAt is the maximum AGENT-supplied event timestamp and therefore lives in the device clock frame. The silence sweep compared it against a server-derived cutoff, mixing two frames. Measured over 11,068 sessions: 26 carry a genuine CMTrace timezone skew on their IME-derived events, from -17 h to +1 h (a further 27 carry plain device-clock offsets). At -17 h a live agent looks silent the moment it starts and is marked Stalled with a customer-visible "Agent silent for 1020min"; at +1 h the sweep is blind for an hour. Adds SessionSummary.LastIngestAt, stamped from the server clock in IncrementSessionEventCountAsync — reaching that method IS contact from the agent, so it is written unconditionally rather than gated on latestEventTimestamp. The sweep's server-side filter now spans both frames (LastIngestAt lt X or LastEventAt lt X): filtering on LastIngestAt alone would silently drop every row written before the field existed, because Table Storage does not match a filter against a missing property. The authoritative decision moves into ServerFrameLastContact() (LastIngestAt ?? LastEventAt ?? StartedAt) and is applied in both stages — before the Stalled marker and in the stage-2 alive guard. Deliberately not mirrored into SessionsIndex: the sweep reads Sessions, and the index manifest is pinned bidirectionally by tests. shared-manifests.json + .generated.ts regenerated — SharedManifestParityTests is the drift brake and correctly went red on the new field. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>