<feed xmlns='http://www.w3.org/2005/Atom'>
<title>feedreader.git/app/build.gradle.kts, branch master</title>
<subtitle>FeeDreader — personal Android RSS reader
</subtitle>
<id>https://git.terst.org/feedreader.git/atom?h=master</id>
<link rel='self' href='https://git.terst.org/feedreader.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/'/>
<updated>2026-08-24T08:18:44+00:00</updated>
<entry>
<title>Batch-upgrade dependencies: 4 of ~14 safely bumped, rest blocked on compileSdk</title>
<updated>2026-08-24T08:18:44+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-24T08:18:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=e42bfbd6a65eefd0f24677aebb74993ae56490cf'/>
<id>urn:sha1:e42bfbd6a65eefd0f24677aebb74993ae56490cf</id>
<content type='text'>
DataStore, security-crypto (out of alpha), and the two androidx.test
libs upgraded cleanly. Everything else in the Compose/Activity/
Navigation/Lifecycle/Room/WorkManager family's current releases
requires compileSdk 35+/AGP 8.6+ (this project is on AGP 8.3.2/
compileSdk 34) -- reverted those to original versions rather than
force a much bigger AGP/compileSdk upgrade into what was scoped as a
routine freshness pass. Documented as its own BACKLOG item, along
with the EncryptedSharedPreferences/MasterKey deprecation the
security-crypto bump surfaced.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
</content>
</entry>
<entry>
<title>Add instrumented extraction fixture tests for WHT/Ars Technica</title>
<updated>2026-08-24T08:10:17+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-24T08:10:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=ead050b61fba23fd9f9d26dada997309dcb1d9ba'/>
<id>urn:sha1:ead050b61fba23fd9f9d26dada997309dcb1d9ba</id>
<content type='text'>
BACKLOG's "Extraction test coverage for WHT / Ars Technica" item:
ReaderExtractor's site-specific preprocessing had only ever been
diagnosed by hand against single live articles across several rounds
of fixes. Four hand-authored fixtures now pin down the exact behavior:

- West Hawaii Today's paywall-div unhide (class="paywall paywall_*")
- Lazy-loaded image src resolution (data-src -&gt; src)
- Ars Technica's duplicate PhotoSwipe caption removal
  (.pswp-caption-content vs. the real &lt;figcaption&gt;)
- Ars's split .post-content.post-content-double block merge

Needs a real WebView to run Readability.js, so these are instrumented
tests (app/src/androidTest/), not plain JVM unit tests -- that source
set didn't exist yet (testInstrumentationRunner + androidx.test deps
added to app/build.gradle.kts). Fixtures are minimal hand-authored
markup targeting the exact selectors ReaderExtractor's own code uses,
not scraped live pages, which rot and would fail for reasons unrelated
to the extraction logic.

Fixtures are served over real http:// via MockWebServer, since
ReaderExtractor's WebView has allowFileAccess = false and won't load
file:// URLs. Added a debug-only network security config override
(app/src/debug/res/xml/network_security_config.xml) permitting
cleartext to 127.0.0.1/localhost for this -- release is untouched.

Verified the tests actually catch regressions, not just pass
trivially: temporarily broke the paywall-unhide selector, confirmed
the corresponding test failed on both connected emulators, then
reverted and confirmed green again.

Ran on 2 connected devices (feedreader_test, doot_test_api36): 4/4
passing, 0 failures. Instrumented tests aren't counted by the JVM-only
jacoco coverage metric (expected, not a gate miss -- see CLAUDE.md).

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
</content>
</entry>
<entry>
<title>Add TDD quality gate: detekt, coverage tracking, pre-commit hook; fix theme crash risk</title>
<updated>2026-08-24T07:54:01+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-24T07:54:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=da942624a4abd82797f563cf8c57f1124c5de435'/>
<id>urn:sha1:da942624a4abd82797f563cf8c57f1124c5de435</id>
<content type='text'>
CLAUDE.md now documents mandatory red-green-refactor TDD, a per-commit
quality gate (build, tests, lint, detekt, coverage-non-decrease,
WebView-JS justification, manifest permission diff, secret scan), and
why each check exists (tracing back to real bugs this audit found).

Wires up detekt (static analysis) and a standalone Jacoco setup
(scoped to data/readability/opml/work/stash -- ui/ Compose screens
are impractical to unit-test for coverage) via app/build.gradle.kts.
Bumped Kotlin/KSP to 2.0.21/2.0.21-1.0.28 to match detekt 1.23.8's
compatibility requirement.

Also fixes the themes.xml bug the lint gate immediately caught:
Theme.FeedReader's parent required API 29 while minSdk is 26 -- a
real crash risk on Android 8.0-9.0 devices. Swapped to the plain
(non-DayNight) DeviceDefault parent, since Compose already owns all
real theming via a hardcoded darkColorScheme() regardless of system
day/night state.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
</content>
</entry>
<entry>
<title>Encrypt the Stash API key at rest</title>
<updated>2026-08-22T01:07:10+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-08-22T01:07:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=78bc7b3c48eae1b68d3f35c81ea2b959d8136477'/>
<id>urn:sha1:78bc7b3c48eae1b68d3f35c81ea2b959d8136477</id>
<content type='text'>
The key is a bearer credential for the user's own Stash server, sent
as X-API-Key on every request -- previously stored in plain DataStore
preferences, recoverable in cleartext from a rooted device, an
unlocked-bootloader device, or a local backup/extraction tool. Now
stored via EncryptedSharedPreferences (Android Keystore-backed
AES-256). An existing key is migrated from the old plaintext location
once, on first read, then the plaintext copy is cleared.
</content>
</entry>
<entry>
<title>First round of testing feedback: images, unread landing, dark, delete</title>
<updated>2026-07-22T16:26:45+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T16:26:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=52d7877fccd60a1dae33d877ba6aa765dc098a52'/>
<id>urn:sha1:52d7877fccd60a1dae33d877ba6aa765dc098a52</id>
<content type='text'>
- Fix OPML import status message reporting the raw pre-dedup count
  (144) instead of the actual unique feed count after Room's upsert
  collapses duplicates (108) -- cosmetic bug, the DB was already
  correct.
- Article thumbnails: extract media:thumbnail/enclosure or the first
  &lt;img&gt; in feed content, show via Coil in the article list.
- App now lands on Unread instead of the feed list; a top-bar icon
  gets from there to the feed list (forward nav, not back-stack pop,
  since Unread is now the root).
- Dark color scheme by default.
- Delete-feed action (with confirmation) on each feed row -- was
  entirely missing. Deletes its non-saved articles too, so they don't
  linger as orphaned rows in the cross-feed Unread view; saved articles
  are kept regardless.
- Room migration 1-&gt;2 for the new imageUrl column (real migration, not
  destructive fallback, since there's now real on-device subscription
  data from testing).

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
<entry>
<title>Initial commit: Feeds, an Android RSS reader</title>
<updated>2026-07-22T08:08:58+00:00</updated>
<author>
<name>Peter Stone</name>
<email>thepeterstone@gmail.com</email>
</author>
<published>2026-07-22T08:08:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.terst.org/feedreader.git/commit/?id=d8ce4b3c0f383e844b125d2d3abd618715bb3c89'/>
<id>urn:sha1:d8ce4b3c0f383e844b125d2d3abd618715bb3c89</id>
<content type='text'>
Feed subscriptions (RSS 2.0 + Atom, hand-rolled parser), OPML
import/export, WorkManager periodic fetch with conditional GET,
Room-backed local storage (feeds/articles/filter rules), keyword
mute/highlight filtering.

Reader mode: on-device extraction via Mozilla's Readability.js
(vendored from github.com/mozilla/readability, pinned to release
0.6.0) run in a hidden WebView, for feed articles whose provided
content is too thin to be the real article body.

Save: explicit action (from a feed article, or the Android share-sheet
target for any URL) archives to Stash via POST /artifacts?sync=true,
while a local on-device copy (Readability-extracted) is always kept so
saved articles read offline regardless of Stash's reachability.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
</content>
</entry>
</feed>
