summaryrefslogtreecommitdiff
path: root/CLAUDE.md
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-11-02 07:51:10 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-11-02 07:51:10 +0000
commit5fc97b66bda97d3af14da6d3de69e84f29299773 (patch)
treee73aba68e2082173f97600347798f5fd4c8ff941 /CLAUDE.md
parentb9b91ba21c5a2d333b45d8c1c73ea77c6d1cd3b9 (diff)
Write down what the corpus actually says, and correct three stale claims
The findings doc's last entry was 2026-10-19 and ended by naming what was missing: "a mechanism that can tell a real short ad from a short coincidence". Words are that mechanism, it was built, and thirty-two server commits later the doc never recorded what it measured. A future session would have re-attempted the ad/furniture classifier that was already measured and deleted. The new entry is derived from the live state file rather than quoted, which is how it caught that publish.go's own figure describes a subset. "92% to 104% across eighteen episodes" is the publisher-transcript number. Across the 24 episodes now publishing an ad span, recovery runs 20% to 146%. Split by where the master came from, the spread stops being a spread: a publisher's transcript gives 94-104%, a refetch gives a median of 33%. Nothing in Publishable looks at which it has, and refetch masters are now fifteen of those twenty-four -- the majority of a tier the design had decided not to act on. It also caught me getting a conclusion wrong. Two Reply All episodes publish ad spans at 146% and 138% of their length gap, which reads as the detector over-claiming by half a minute of programme. Looking at the show instead of the episode: Reply All's gaps run from -0.48 to +1.02 minutes across eight episodes. They straddle zero, so the feed's declared durations are not a baseline for that show and the thing being exceeded is noise. The structural finding is the useful one -- HasInsertedAdvertising asks only whether this episode's gap clears one ad unit, and has no notion of whether the show's gaps are consistent enough to mean anything. Both versions are in the entry, because the wrong one is the one a reader would reach for next time. Nothing here changes behaviour. Whether a refetch should publish an ad at all is Peter's call and is written up as such. CLAUDE.md, separately: - It still said the episode description shows about two lines. That was changed to ten yesterday, for a reason that is recorded: the limit existed to stop long notes pushing the transport off-screen, and the merge had already put the notes below the transport. Left as it was, the doc was an instruction to reintroduce the bug. - "207 unit tests" has been wrong for most of the project's life; the suite is past nine hundred. Replaced with a note to ask the wrappers, since any number written here goes stale the same way. - The TDD rule banned backfilling tests onto existing code without qualification, which also forbids the one case where it is right -- pinning behaviour before moving it, so a refactor is provably behaviour-preserving. Narrowed to say that, and to say the order matters. The sixteen formatter tests in 2cc252a were written that way. - Added the rule those formatter files were moved to satisfy: anything the app says out loud lives in a named file of its own and is tested, never inside the composable that renders it. It was already followed by eight files and stated only in WaitingForServerText's own comment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
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,