From 8d3cefa3f598f328dc39c18ed7d133ae343f8fc7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 12:40:49 +0200 Subject: [PATCH] Telemetry contract: the real session_summary shape, and the duplicate warning The documented meta was the THIN row -- the leftover second writer that amber-app just removed. It also never said that watchedS (elapsed watch clock) and positionS (where playback reached) are different numbers, which is exactly the distinction that made a never-started session readable. Records that aggregates over rows before 2026-08-07 double-count Android sessions, and gives the filter that excludes them. --- docs/telemetry-contract.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/telemetry-contract.md b/docs/telemetry-contract.md index 446d3a3..66868a0 100644 --- a/docs/telemetry-contract.md +++ b/docs/telemetry-contract.md @@ -21,11 +21,33 @@ older than 14 days nightly. | `error` | `uncaught` | `{library}` — Flutter framework errors | | `player` | `exo_error` | `{host, anime}` — native player error | | `player` | `av_delay_applied` | `{audioMs, host}` — user dialed in an audio offset (the "a track falls behind" signal) | -| `session` | `session_summary` | `{host, durationS, watchedS, stalls, audioDelayMs, anime}` on player close | +| `session` | `session_summary` | one row per playback — see below. **Written only by `SessionFeedbackService._write`.** | +| `qa` | `qa_session` | `{sid, lines[], truncated?}` — the QA decision log for one playback (issue #92), off by default | `host` is only the stream's `scheme://host` — **never** a full signed URL or an addon token (redacted client-side in `TelemetryService.redactUrl` before write). +### `session_summary`, and the two numbers that are not the same + +`{sessionId, title, probeKey, host, durationS, watchedS, positionS, stalls, +droppedFrames, renderedFrames, maxConsecutiveDropped, problemAtS[], promptOutcome, +rating?}` plus the player's provenance fields (`forcedAudio`, `sideloadedSubs`, +`resumed`, `seeks`, `seekStormMax`, codec/height/bitrate). + +- **`watchedS` is elapsed watch-clock time. `positionS` is where playback got to.** + They differ whenever someone seeks, rewatches, or stares at a spinner — a session + that never started reads `watchedS: 48, positionS: 0`. +- **`sessionId` is on every row**, and is what joins a summary to its `qa_session` + lines and to the amber-api health record. + +**Before 2026-08-07 this was written twice per Android playback**, by the native +player *and* by the feedback service, and the two disagreed: the extra row had no +`sessionId`, `title` or `probeKey`, and put the *position* in `watchedS`. In one +14-day window that was 542 rows of which only 294 were real. **Any aggregate over +this collection computed before that date counts Android sessions twice**, and +rows older than the fix still carry the duplicate. `sessionId:isset = true` is the +filter that excludes them. + ## How Claude queries it Superuser token (same as releases publishing), then filter/sort the collection: