summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-10-24 09:01:05 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-10-24 09:01:05 +0000
commit34e50c04b18313d92e3b3a565965675fd8b50b8a (patch)
treeba408851ac65071383d21060f6c133edd09cfb30 /docs
parentbc33a9bdee12e41ed8b017c3f751a5dcdf6d1c97 (diff)
Send the listener's labels to the server and keep them
The phone has had two kinds of label and nowhere to put them: a mark, which is a judgement on a break detection found, and a report, which is the notification button pointing at advertising it missed. They catch opposite errors, and the signature library needs both. POST /api/labels now takes them and state.json keeps them. Episodes are named by feed and guid rather than by either side's own id -- the phone never sees the server's, and the server's belongs to a mirror that forgets an episode after eight. The labels sit beside the shows rather than on them for the same reason: the corpus has to outlive the audio it was given on. A report's end is sent as guessed or measured. It is only measured once the listener came back and trimmed it; otherwise it is wherever the jump landed, and a signature cut to that would carry programme. The phone re-sends everything each sweep and keeps no record of what arrived. The server replaces rather than accumulating, so a re-send is free, there is no sync state to get wrong, and a label lost to a crash comes back on the next refresh. Gated on audioFromServer, like every other server re-query: a label names a position in a particular file, and dynamic ad insertion means the publisher's copy is a different one. Accepting a label writes the state file immediately. Everything else is written at the end of a poll cycle, which for 61 feeds is hours, and a tap made once should not wait that long to become durable. Deployed and checked against the live server: a label is accepted, a re-send leaves one, an unauthenticated POST gets 401, and the label is on disk within seconds. The probe label used for that check was removed afterwards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'docs')
-rw-r--r--docs/superpowers/plans/2026-10-20-corrections-and-signature-library.md34
1 files changed, 30 insertions, 4 deletions
diff --git a/docs/superpowers/plans/2026-10-20-corrections-and-signature-library.md b/docs/superpowers/plans/2026-10-20-corrections-and-signature-library.md
index 2572cc3..fb4ad2c 100644
--- a/docs/superpowers/plans/2026-10-20-corrections-and-signature-library.md
+++ b/docs/superpowers/plans/2026-10-20-corrections-and-signature-library.md
@@ -174,11 +174,37 @@ between them, sometimes followed by one or more skips back, is a pretty strong s
block."* A press and a skip-burst are two observations of the same event, and a skip *back* after
over-shooting brackets the end precisely. Treat them as one input stream.
-### 2. Push verdicts to the server — NOT STARTED
+### 2. Push verdicts to the server — DONE 2026-10-24
-Phone to server. Needs an endpoint and server-side storage. Design note: a verdict is about a
-*range of a specific episode's audio*, so it is only meaningful for episodes whose bytes the
-server itself served — gate on `audioFromServer`, the same invariant the refresh sweep uses.
+`POST /api/labels`, stored in `state.json` under a top-level `labels` list. Deployed and
+exercised live: accepts, de-duplicates a re-send, refuses without the token.
+
+**Both kinds of label go, not just verdicts.** A *mark* is a judgement on a break detection
+found; a *report* is the notification button pointing at advertising it missed. They catch
+opposite errors and the library needs both, so the wire format carries `source` to say which.
+
+**Identified by feed and guid**, not by either side's episode id. The phone never sees the
+server's id, and the server's id belongs to a mirror that forgets an episode after eight.
+
+**Labels outlive the episode.** They sit beside the shows rather than on them, so an episode
+leaving the eight-episode window does not take the corpus with it. That is the same reasoning as
+the app's own no-foreign-key decision for `break_verdicts`.
+
+**A report's end is marked as guessed or measured.** `endMeasured` is only true once the listener
+came back and trimmed it; otherwise the end is wherever the 25-second jump happened to land. A
+signature cut to a guessed end would carry programme audio, so the library has to know which it
+is holding.
+
+**The phone re-sends everything, every sweep.** No record of what arrived, deliberately: the
+server replaces rather than accumulating, so a re-send is free and there is no sync state to get
+wrong. A handful of taps does not need a protocol. It also means a label lost to a crash comes
+back on the next refresh.
+
+**Accepting a label writes the state file at once.** Everything else is written at the end of a
+poll cycle, which for 61 feeds is hours. A label is a tap made once and should not wait that long
+on the wrong side of a kill.
+
+Gated on `audioFromServer`, as the design note required.
### 3. The index spike — DONE 2026-10-24. **It passes.**