From cb3377b4856e3419df1cbc52044eda8bf68a006d Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sat, 31 Oct 2026 08:39:44 +0000 Subject: Shows get a priority, so the news is not stuck behind a back catalogue Peter's framing: a few shows are news and stop being worth hearing within a day, a tier he keeps up with, and a long tail that is not topical at all. It matters because recognising one episode costs the server about five minutes, so a five-hour back-catalogue episode arriving the same morning as the news takes most of a cycle before the news is looked at. Three tiers and no more. A number would invite fiddling and would not mean anything; these are the three distinctions that were described. The tier rides beside the subscription list rather than inside it, so a server that knows nothing about tiers reads the list exactly as before and a phone that knows nothing keeps pushing what it always did. Only the shows placed somewhere other than the default are sent -- "ordinary" for sixty shows is the same information as nothing, in sixty times the space. It orders both the poll and the recognition queue. Recognition now sorts on three things in the order they matter: a topical show first, then an episode whose answer checks itself against the length gap, then newest. **A tier only exists for a subscribed feed**, which a test caught: the list and the tiers arrive in one push, and setting the tiers after the list put back a placement for a feed that same push had just removed. A resubscription months later would have inherited it. Unrecognised tiers are ignored at both ends and read as the default, which is the one that starves nothing -- the same rule the label store follows for a verdict it does not know. Schema 20, migrated and checked on a device. The control says what it does not do as well as what it does: nothing about playback changes, and a show placed anywhere still plays, downloads and skips identically. A setting that looks like it changes playback and does not is worse than no setting. The coverage gate caught the push body having no test, which was fair -- it is hand-built JSON going over the wire, where a missing brace is a library that silently stops being mirrored. Its exact output is now pinned, including a URL with a quote in it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B --- server/cmd/listend/main.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'server/cmd') diff --git a/server/cmd/listend/main.go b/server/cmd/listend/main.go index 377fedc..43921bd 100644 --- a/server/cmd/listend/main.go +++ b/server/cmd/listend/main.go @@ -40,6 +40,18 @@ import ( "github.com/terst/listen/server/internal/transcript" ) +// tierRankOf orders the tiers for work: topical first, archive last. +func tierRankOf(tier string) int { + switch tier { + case store.TierTopical: + return 0 + case store.TierArchive: + return 2 + default: + return 1 + } +} + // minRecurringCorpus is how many of a show's episodes must have words before what they share // means anything. Three: the target and the two others a stretch has to turn up in. const minRecurringCorpus = 3 @@ -291,7 +303,9 @@ func (w *worker) save() { // pollAll refreshes every subscribed feed, then mirrors and detects what changed. func (w *worker) pollAll(ctx context.Context) { - feeds := w.state.Feeds() + // Topical shows first and archive last, so a five-hour back-catalogue episode cannot take + // a cycle's recognition budget before the morning news has been looked at. + feeds := w.state.ByTier(w.state.Feeds()) if len(feeds) == 0 { log.Printf("no subscriptions yet; waiting for the phone to send its list") return @@ -960,8 +974,20 @@ func (w *worker) pendingRecognition() []*store.Episode { } } } + // Three things decide the order, and they are in the order they matter. A topical show is + // worthless by Thursday. An episode whose answer checks itself against the length gap is + // worth more than one that cannot. And within that, newest first. + rank := map[string]int{} + for _, show := range w.state.Shows() { + for _, e := range show.Episodes { + rank[e.ID] = tierRankOf(w.state.Tier(show.FeedURL)) + } + } sort.SliceStable(pending, func(i, j int) bool { left, right := pending[i], pending[j] + if rank[left.ID] != rank[right.ID] { + return rank[left.ID] < rank[right.ID] + } if (left.MasterCues > 0) != (right.MasterCues > 0) { return left.MasterCues > 0 } -- cgit v1.2.3