diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 57 |
1 files changed, 47 insertions, 10 deletions
@@ -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. |
