summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/detection-findings.md59
1 files changed, 32 insertions, 27 deletions
diff --git a/docs/detection-findings.md b/docs/detection-findings.md
index 4e8e721..6c7385d 100644
--- a/docs/detection-findings.md
+++ b/docs/detection-findings.md
@@ -1,14 +1,14 @@
# Tier 3 detection: what is known, and the root cause of why nothing confirmed
-Written 2026-09-29, in three passes across the same day as the picture sharpened. **Resolved as
-of the last pass**: the correlation algorithm and its tuned constants were never the problem.
-Up First specifically confirms nothing because `RetentionPolicy`'s one-day grace period usually
-reclaims a played episode's file before the *next* one is downloaded and processed, for a show
-this fast and this promptly listened to -- so the detector keeps discarding its only pending
-candidate and restarting, rather than ever getting two episodes' files to compare at once. See
-"The live test, and the actual root cause" below for the mechanism, and "What this means for a
-fix" for what's actually worth changing (a retention/fingerprint coupling decision, not a
-constant to tune).
+Written 2026-09-29, in four passes across the same day as the picture sharpened and then got
+fixed. The correlation algorithm and its tuned constants were never the problem. Up First
+confirmed nothing because `RetentionPolicy`'s one-day grace period usually reclaimed a played
+episode's file before the *next* one was downloaded and processed, for a show this fast and this
+promptly listened to -- so the detector kept discarding its only pending candidate and
+restarting, rather than ever getting two episodes' files to compare at once. See "The live test,
+and the actual root cause" below for the mechanism, and "The fix (made)" for what actually
+shipped -- protecting a show's pending episode from reclaim, for both fingerprinting mechanisms.
+**Not yet re-verified on a device.**
The rest of this document is kept in the order it was actually found, including the two
hypotheses that turned out to be wrong -- both cost real time to check and are worth not
@@ -31,6 +31,9 @@ not a score that failed. See "What the phone actually showed" for the detail.
real time. That's what found the actual mechanism -- see "The live test, and the actual root
cause."
+**Fourth pass, the fix:** Peter picked exempting a pending episode from reclaim over the other
+two options. Shipped for both intro/outro and midroll -- see "The fix (made)."
+
---
## What is settled
@@ -210,24 +213,26 @@ executed (Doze/battery-restriction deferral of a one-shot job, still unresolved
something force-stopped the app in a way that drops pending `WorkSpec` rows before they run.
Secondary to the retention-race finding above, and not reproduced in the live test.
-## What this means for a fix (not made -- needs a decision, not a desktop tweak)
-
-The retention-race finding points at the actual place to fix, and it isn't
-`DEFAULT_REFERENCE_SECONDS`/`MIN_SCORE`/`CONFIRM_THRESHOLD`: those were never the problem.
-Options, roughly in order of how invasive they are:
-
-1. **Exempt an episode retention would otherwise reclaim while it is a show's `pendingEpisodeId`.**
- Keeps the file exactly as long as Tier 3 actually needs it and no longer; the natural fix, but
- couples `RetentionSweeper` to fingerprint state it currently knows nothing about.
-2. **Extend the grace period specifically for a show still building its first candidate.** Cruder,
- and still racy for a show played even faster than its grace period, just less often.
-3. **Accept the miss and let it self-heal.** The current behaviour already does this -- it just
- restarts forever for a fast show instead of ever confirming. Only really an "option" if fast
- shows are an acceptable gap in Tier 3's coverage.
-
-This is Peter's call, not a desktop one -- it trades file-retention precision against Tier 3
-coverage for exactly the shows (short, frequent, promptly played) most likely to also carry the
-heaviest dynamic ad insertion, which is the case that matters most.
+## The fix (made)
+
+Peter chose option 1: exempt an episode retention would otherwise reclaim while it is a show's
+pending comparison basis, for both mechanisms. `RetentionSweeper` gained a `pendingEpisodeIds`
+seam (`suspend () -> Set<String>`, defaulting to empty so every existing caller and test was
+unaffected) and filters its reclaim candidates against it before the grace-period policy ever
+sees them. `RetentionWorker` wires the real thing: the union of
+`ShowFingerprintDao.getAllPendingEpisodeIds()` (intro/outro) and
+`MidrollDao.getAllMidrollPendingEpisodeIds()` (midroll, reading
+`SubscriptionEntity.midrollPendingEpisodeId`) -- both new, narrow queries added for exactly this.
+
+Applied to both mechanisms, not just the one that got diagnosed: `MidrollUpdater`'s discovery has
+the identical shape (a rolling pending episode compared against whatever downloads next) and
+would race retention the same way for a fast show, just silently -- discovery finding nothing
+that round rather than a visible frozen row, since it has no fallback state to get stuck in.
+
+Not yet re-verified on a device: the next live test is downloading two Up First episodes close
+enough together that the first is still within its (now-irrelevant, since it's protected) grace
+period, and confirming `pendingEpisodeId` actually advances to a real second-episode comparison
+instead of restarting again.
## Experiments, in order (updated)