summaryrefslogtreecommitdiff
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md29
1 files changed, 25 insertions, 4 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index b5e4e5e..ee50c3d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -41,8 +41,14 @@ in that literal order:
6. Run the full quality gate before committing.
**If production code was written before or without a preceding failing test**, discard the diff and
-redo it test-first. Do not backfill tests onto existing code — a test written against working code
-confirms what the code does, not what it should do.
+redo it test-first. Do not backfill tests onto existing code *as a substitute for that* — a test
+written against working code confirms what the code does, not what it should do, so it cannot stand
+in for a red step.
+
+The one case it is right: **pinning behaviour before moving it.** A characterisation test written
+against the old location, confirmed green, then re-run after the move is what makes a refactor
+provably behaviour-preserving — the "keeping it green" in step 5 needs a green to keep. Write it in
+that order and say so; written afterwards it only proves the new code agrees with itself.
**Scoping exception:** pure resource/config/manifest changes with no unit-testable behaviour don't
get a red step; the static-analysis gate covers those.
@@ -115,7 +121,9 @@ claims three separate times across these repos.
**Phase 1 is complete.** Subscribe (share sheet, pasted URL, OPML import), refresh, download,
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.
+library and voice search, with instrumented tests covering the media browse tree. (Counts are
+deliberately not recorded here -- they went stale at 207 while the suite reached nine hundred.
+`gradle-test` and `go-verify` report the current ones.)
**Since Phase 2**, in response to a round of Peter's feedback: yt-dlp sources play (they are
fetched first), playback errors are surfaced instead of being swallowed, anything queued is
@@ -549,7 +557,11 @@ by default and correct; it is simply waiting for a publisher who marks breaks.
playing episode's screen, and steps aside on that screen. Speed is a chip (`SpeedChoices.OFFERED`
is 1.0/1.1/1.25/2.0); a show stored at 1.5 or 1.75 keeps playing at it, the chip just names it.
Secondary actions and chapters are in the `⋮` menu, whose contents `EpisodeMenu` decides, and
- the description shows ~two lines (`DescriptionPreview`) with the rest on tap.
+ the description shows about ten lines (`DescriptionPreview.PREVIEW_LINES`, with a character
+ budget beside it) and the rest on tap. It was two until 2026-11-01: that limit existed to
+ stop long notes pushing the transport off-screen, and the merge put the notes *below* the
+ transport, so the reason had already gone. What it actually did was cut at 140 characters
+ and leave the bottom three-fifths of the screen empty. Do not reinstate it.
- **Framework adapters are excluded from coverage, as of 2026-09-26.** The gate had been
overridden eight times, every one a class whose whole body is a call into ExoPlayer, Media3,
@@ -615,6 +627,15 @@ by default and correct; it is simply waiting for a publisher who marks breaks.
error colour through the dimming, because a feed failing to refresh looks exactly like a feed
with nothing new.
+- **Anything the app says out loud lives in a named file of its own, and is tested.** Not in the
+ composable that renders it: `SkipSummary`, `RuleSummary`, `FoundBySummary`, `WaitingForServerText`,
+ `RejectedTokenText`, `DescriptionPreview`, `SpeedChoices`, `TimeText` (`formatClock` for a
+ position, `formatDuration` for a length) and `EpisodeLabels` (`progressLabel`, the one line under
+ every episode title). Screens are excluded from coverage and verified on hardware, so wording left
+ inside one is wording nothing checks -- and a shared formatter inside a view means editing the
+ now-playing bar to change the transcript gutter, which is where `formatClock` actually was until
+ 2026-11-02. A new user-visible string either joins one of these or brings its own file.
+
- **What the skip detection has worked out is visible**, per show in the settings sheet and per
episode on its detail screen. `SkipSummary` owns the wording and is tested, because the
distinction that matters -- confirmed-and-acting versus still-gathering -- is the whole point,