diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-09-25 17:09:13 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-09-25 17:09:13 +0000 |
| commit | 593ee7740de24aae22ddec1dc5a2e083fff06bd3 (patch) | |
| tree | a7cb89b34e2ff5f8ad32073a1078f70468daf0c0 /.githooks | |
| parent | f04a01c672c01f4913d4b9bbaf2fc4febbaec08c (diff) | |
Slow drip: work through an archive a few episodes at a time
The second of Phase 2's three tracks. A show can be set to release its back
catalogue on a cadence -- one a day, oldest first, by default -- and while drip
is on the show lists only what has been released rather than the raw feed. A 600
episode archive then reads as a show you are keeping up with instead of a wall.
A release always enqueues, and deliberately does not go through the rule engine.
Drip decides when you see an episode and the rules decide what happens to one;
running a released episode through the rules as well would let a rule silently
cancel a release, which reads as the drip being broken.
Three pieces of arithmetic worth keeping, all in DripSchedule and all tested:
The clock advances by whole intervals rather than to now. Moving it to now
discards the part-interval already elapsed, and a six-hourly worker against a
daily drip loses hours every day until "one a day" means one every day and a
bit.
A long absence is capped at ten and the excess is forgiven rather than carried.
A month away owes thirty episodes; handing those over empties the archive into
the queue, which is the thing the feature exists to avoid. Carrying the debt
instead would just pay it out as a capped burst on each of the next three runs.
Switching drip on settles what it already owes: everything played or already
queued is released up front, because hiding episodes you have heard makes a show
followed for a year look like it has three, and hiding one mid-queue strands it
where nothing can reach it. Re-enabling starts the cadence from now rather than
paying off a debt accrued while it was off.
Visibility is a query parameter, not stored state, so switching drip off
restores the whole catalogue without touching a row. Android Auto browses the
same set. Feed reconciliation deliberately still sees everything -- matching
against the visible set alone would read a held-back episode as missing and
re-insert it on every refresh.
RFC 5005 archive paging is what gives a drip anything to drip: FeedArchiveWalker
follows prev-archive pages, resolving relative hrefs against the page that
carried them, and stops on a loop, a page cap, or a fetch failure -- keeping what
it collected and reporting why, because half an archive is useful and a silent
half surfaces months later as "why did this show run out?".
Two gaps found while wiring it up, both fixed here: the subscription screen had
no snackbar at all, so a refresh started from it reported nothing and an archive
fetch would have been silent; and both toggles were bare Switches with no
accessible name, which TalkBack cannot announce and the UI checker cannot tap.
Schema v2 -> v3. The four non-null drip columns carry SQL defaults because
SQLite cannot add a NOT NULL column without one, declared on the entity too so
the migration and the exported schema describe the same table. Verified twice:
the migration test now covers 1 -> 3 straight through, and a real v2 database
seeded with a subscription, an episode at position 424242 and a rule was
upgraded in place on a device with all three surviving and drip defaulting off.
OPML moved to its own view model rather than growing ListenViewModel past its
function limit again -- it shares nothing with the library, is used by one
screen, and the settings screen now depends on it alone.
The exported-schema trap from the last commit happened again, after I had
written the warning down. Step 9 of the pre-commit hook now refuses a commit
that modifies an already-committed schema JSON, since a note was demonstrably
not enough.
No permission changes. 333 unit tests green, detekt and lint clean, coverage
63.63 -> 65.30.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to '.githooks')
| -rwxr-xr-x | .githooks/pre-commit | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 32b492a..4a3bcc5 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -9,23 +9,23 @@ fail() { exit 1 } -echo "== [1/8] Build ==" +echo "== [1/9] Build ==" ./gradlew :app:compileDebugKotlin -q -echo "== [2/8] Tests ==" +echo "== [2/9] Tests ==" ./gradlew :app:testDebugUnitTest -q -echo "== [3/8] Static analysis: lint ==" +echo "== [3/9] Static analysis: lint ==" if ! ./gradlew :app:lintDebug -q; then fail "lint failed -- fix all lint ERRORS before committing (warnings are fine to accumulate, see CLAUDE.md)." fi -echo "== [4/8] Static analysis: detekt (code-quality auditor) ==" +echo "== [4/9] Static analysis: detekt (code-quality auditor) ==" if ! ./gradlew :app:detekt -q; then fail "detekt found new issues beyond the tracked baseline. Fix them, or if they're genuinely pre-existing debt being touched incidentally, regenerate the baseline with './gradlew :app:detektBaseline' and explain why in the commit message." fi -echo "== [5/8] Coverage must not decrease ==" +echo "== [5/9] Coverage must not decrease ==" ./gradlew :app:jacocoTestReport -q REPORT="app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml" BASELINE_FILE=".coverage-baseline" @@ -59,7 +59,7 @@ else echo "pre-commit: coverage ${BASELINE}% -> ${CURRENT}%" fi -echo "== [6/8] WebView JavaScript justification ==" +echo "== [6/9] WebView JavaScript justification ==" STAGED_KT=$(git diff --cached --name-only --diff-filter=ACM -- '*.kt' || true) if [ -n "$STAGED_KT" ]; then for f in $STAGED_KT; do @@ -73,14 +73,14 @@ if [ -n "$STAGED_KT" ]; then done fi -echo "== [7/8] Manifest permission diff ==" +echo "== [7/9] Manifest permission diff ==" if git diff --cached --name-only | grep -q '^app/src/main/AndroidManifest.xml$'; then if git diff --cached -- app/src/main/AndroidManifest.xml | grep -qE '^\+.*<uses-permission'; then echo "pre-commit: WARNING -- AndroidManifest.xml adds a new <uses-permission>. Make sure the commit message says why." fi fi -echo "== [8/8] Secret scan ==" +echo "== [8/9] Secret scan ==" if [ -z "${SECRET_SCAN_OVERRIDE:-}" ]; then SECRET_HIT=$(git diff --cached -U0 -- . ':!*.lock' | grep -E '^\+' | grep -viE '^\+\+\+' | grep -E '(api[_-]?key|secret|password|token)["'\'' ]*[:=]["'\'' ]*[A-Za-z0-9_\-]{16,}' || true) if [ -n "$SECRET_HIT" ]; then @@ -91,4 +91,16 @@ else echo "pre-commit: secret scan skipped (SECRET_SCAN_OVERRIDE set)" fi +echo "== [9/9] Exported Room schemas are append-only ==" +# A schema JSON already committed describes a database version that exists on a phone. Room +# rewrites the file for whatever version the @Database annotation declares, so editing an entity +# before bumping that number silently overwrites the record of the old shape -- and the migration +# test then validates happily against a schema that never shipped. It happened twice building +# Phase 2, and a note in CLAUDE.md did not prevent the second time. +SCHEMA_EDITS=$(git diff --cached --name-only --diff-filter=M -- 'app/schemas/**/*.json' || true) +if [ -n "$SCHEMA_EDITS" ]; then + echo "$SCHEMA_EDITS" >&2 + fail "an already-committed Room schema was modified (see above). Bump the database version first so a new file is written, then restore these with: git checkout -- app/schemas/" +fi + echo "pre-commit: all checks passed." |
