summaryrefslogtreecommitdiff
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md57
1 files changed, 47 insertions, 10 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 8308bb8..bca2a68 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -63,6 +63,13 @@ and a logcat capture started before launch:
app/build/outputs/apk/debug/app-debug.apk listen-debug.apk
```
+Migrations only prove themselves on a device. Every version bump runs:
+
+```
+/workspace/devtools/bin/gradle-verify /workspace/listen connectedDebugAndroidTest -- \
+ -Pandroid.testInstrumentationRunnerArguments.class=org.terst.listen.data.MigrationTest
+```
+
**Never read `app/build/reports/detekt/detekt.xml` directly.** An invalid config aborts detekt
before it analyses anything and leaves the previous run's report in place; it was read as
current findings three times. `gradle-report` refuses a report older than the run.
@@ -94,19 +101,24 @@ claims three separate times across these repos.
queue, play, variable speed, sleep timer, retention, unsubscribe, Android Auto with a browsable
library and voice search. 207 unit tests plus instrumented tests covering the media browse tree.
-**Phase 2, not started:** the auto-queue rule engine, slow-drip subscriptions, and intro/outro
-skipping. `skipIntroMs`/`skipOutroMs` exist on `SubscriptionEntity` and nothing reads them.
+**Phase 2 is in progress**, in three tracks. Peter deferred the audio-fingerprint skip detection
+(the design plan's Tier 3); the simple skip work comes last.
+
+- **Track 1, auto-queue rules — done.** Named, ordered, enable-able rules with conditions on show,
+ title, duration, age and season, and actions to queue top/bottom, mark played, auto-download, or
+ pin a speed. `RuleEngine` decides, `RuleApplier` carries it out, `RuleSummary` describes a rule
+ in one line, and every claimed episode records which rule fired and why.
+- **Track 2, slow-drip archives — not started.** Includes walking RFC 5005 `prev-archive` pages;
+ `ParsedFeed.previousArchiveUrl` is already parsed and tested and nothing consumes it.
+- **Track 3, simple skip — not started.** Manual per-show offsets (`skipIntroMs`/`skipOutroMs`
+ exist on `SubscriptionEntity` and nothing reads them) and Podcasting 2.0 chapter JSON via
+ `EpisodeEntity.chaptersUrl`, which the parser already captures.
**Known gaps in what is built** (deliberate, not forgotten):
-- **No artwork anywhere.** The parser captures `imageUrl` at channel and item level and the
- media session passes it to the lock screen, but no screen renders it. Needs an image-loading
- dependency (Coil) and caching.
-- **No player screen.** There is a transport bar only: play/pause, skip, title. No scrubbing,
- no per-show speed (the field exists on `SubscriptionEntity`, only the global is reachable).
-- **No queue reordering.** `QueueOrdering.move` and `QueueRepository.move` are written and
- tested with no UI, which the reachability guard tolerates because they are on a class rather
- than an object.
+- **Auto-downloads are not retried.** A rule's download that fails is left FAILED rather than
+ requeued: retrying forever against a 404 would burn the battery, and the failed row is visible
+ in the episode list. Retry is the manual Download button.
- **Android Auto is unverified in a real car.** The session protocol is proven by
`MediaBrowseTest`; whether Auto's own compatibility checks pass on a head unit is untested,
and a sideloaded media app may need Auto's developer mode enabled.
@@ -178,6 +190,31 @@ skipping. `skipIntroMs`/`skipOutroMs` exist on `SubscriptionEntity` and nothing
available and they take the slots, pushing skip-forward off the screen. They are removed in
`onConnect` so the seek buttons get the room.
+- **Bump `version` before you change an entity, not after.** Room rewrites the exported schema
+ for whatever version is declared, so editing `EpisodeEntity` while the database still says
+ `version = 1` silently overwrites `schemas/1.json` with the new shape. That file is the only
+ record of what is on the phone, and once it matches the new code the migration test validates
+ happily against a schema that never existed. It happened building the rules table; the fix was
+ `git checkout -- app/schemas/`.
+
+- **The migration test needs two pieces of plumbing that fail obscurely.** `MigrationTestHelper`
+ reads the exported schemas from the instrumented test APK's assets, so `schemas/` is wired into
+ `androidTest`'s asset dirs in `build.gradle.kts`. And Room 2.8's schema-bundle parser calls a
+ kotlinx-serialization method 1.7.3 does not have -- the version `lifecycle-viewmodel-savedstate`
+ drags in -- which surfaces as `AbstractMethodError` on `GeneratedSerializer` at runtime, not as
+ a build failure. The app's own serialization dependency is pinned at 1.8.1 to raise the floor;
+ AGP resolves the androidTest classpath consistently with the app's, so pinning it only in
+ `androidTestImplementation` does nothing.
+
+- **Rules must never fire on a first subscribe.** `SubscriptionRepository.applyEpisodes` takes a
+ `report` flag for exactly this: the first import discovers the entire back catalogue -- 2,753
+ episodes on a real feed -- and a catch-all rule would enqueue all of it. Verified on a live NPR
+ feed: subscribing stored four episodes and queued none.
+
+- **`android-ui dump` does not list every rendered Text.** The rule attribution line on the
+ episode screen is absent from the dump and plainly visible in a screenshot. Treat a missing
+ line in a dump as inconclusive, not as absence.
+
- **XML comments cannot contain `--`.** The house prose style uses it constantly and it makes
the manifest unparseable, with a merge error that doesn't name the line.