summaryrefslogtreecommitdiff
path: root/server/internal
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-10-20 06:52:17 +0000
committerClaude <noreply@anthropic.com>2026-10-20 06:52:17 +0000
commitd53a8c54351ccf5e0ecfd890abe53e13c65c60fa (patch)
tree4f439a8b6c27203754c34045dfa1574a5447acee /server/internal
parent9cc92e743dda2609a67595439c4694f6a4efae27 (diff)
Publish how many episodes shared each break, and plan the rest
The server has computed and stored Partners since ad detection shipped -- state.json carries it on every break -- but breakRange never put it on the wire, so the phone's new tap-a-break popup could only ever say "the server did not say how many other episodes carried this audio". That count is the one piece of evidence a listener can weigh before judging a break, and the popup exists to show it. My brief for the popup asserted the server already sent it. It did not. The implementing agent checked rather than taking my word, which is why this is a two-line fix now instead of a wrong "why" line forever. Also lands the handover plan for the correction channel and the signature library, written before a context compaction, including the gesture design Peter specified for the notification button and the jump-size argument he correctly rejected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'server/internal')
-rw-r--r--server/internal/api/api.go11
-rw-r--r--server/internal/api/api_test.go23
2 files changed, 33 insertions, 1 deletions
diff --git a/server/internal/api/api.go b/server/internal/api/api.go
index e50c94e..0aeef8a 100644
--- a/server/internal/api/api.go
+++ b/server/internal/api/api.go
@@ -74,6 +74,10 @@ type breakRange struct {
StartMs int64 `json:"startMs"`
EndMs int64 `json:"endMs"`
Kind string `json:"kind,omitempty"`
+ // Partners is how many other episodes were found to carry this audio. The phone shows it
+ // when a break is tapped: it is the only evidence a listener can weigh before deciding
+ // whether the detector got this one right.
+ Partners int `json:"partners,omitempty"`
}
// Handler returns the routed, authenticated handler.
@@ -164,7 +168,12 @@ func (s *Server) episode(w http.ResponseWriter, r *http.Request) {
breaks := make([]breakRange, 0, len(found.Breaks))
for _, br := range found.Breaks {
- breaks = append(breaks, breakRange{StartMs: br.StartMs, EndMs: br.EndMs, Kind: br.Kind})
+ breaks = append(breaks, breakRange{
+ StartMs: br.StartMs,
+ EndMs: br.EndMs,
+ Kind: br.Kind,
+ Partners: br.Partners,
+ })
}
writeJSON(w, http.StatusOK, episodeResponse{
Status: "ready",
diff --git a/server/internal/api/api_test.go b/server/internal/api/api_test.go
index 028bbd8..d4d3ee6 100644
--- a/server/internal/api/api_test.go
+++ b/server/internal/api/api_test.go
@@ -347,3 +347,26 @@ func seedMirrored(t *testing.T, st *store.State, audioDir, contents string) {
func episodeID(st *store.State) string {
return st.Match("https://f", "g1", "", "").ID
}
+
+// Partners -- how many other episodes carried the same audio -- is the one piece of evidence a
+// listener can judge a break by, and the phone's popup exists to show it. The server has always
+// computed and stored it; it simply never put it on the wire, so every break arrived claiming
+// nothing was known about it.
+func TestEpisodeResponseCarriesHowManyEpisodesSharedEachBreak(t *testing.T) {
+ s, st, _, audioDir := newServer(t)
+ seedMirrored(t, st, audioDir, "audio-bytes")
+ e := st.Match("https://f", "g1", "", "")
+ e.Breaks = []store.Break{{StartMs: 0, EndMs: 98_000, Kind: "ad", Partners: 6}}
+
+ rec := get(t, s, "/api/episode?feed=https://f&guid=g1")
+ var got episodeResponse
+ if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil {
+ t.Fatal(err)
+ }
+ if len(got.Breaks) == 0 {
+ t.Fatal("no breaks found")
+ }
+ if got.Breaks[0].Partners != 6 {
+ t.Errorf("partners %d, want 6", got.Breaks[0].Partners)
+ }
+}