vc=89: bound hide-shorts pagination burst + finish SP-write serialization

D-3 — hide-shorts no longer drains a whole channel/search in one burst. The
infinite-scroll trigger is derived from the FILTERED list while loadMore()
appends to the UNFILTERED one, so a shorts-heavy feed with hide-shorts ON kept
the trigger hot (the filter stripped each fetched page back to ~nothing) and
auto-fetched every page to the end of the continuation. Now it keeps loading
while pages are productive (the filtered list grows) and stops after 3
consecutive pages that add nothing visible; toggling hide-shorts off resumes.
Applies to both SearchScreen and ChannelScreen; normal (non-filtered) infinite
scroll is unaffected (productive pages keep the counter at 0).

PrefsWriter completeness — the four SP stores the vc=88 sweep didn't cover
(Enrichment, SearchCache, Playlists, FeedCache) now serialize their writes too,
so no store is left on a bare sp.edit().apply(). EnrichmentStore is the one that
mattered: put() runs 8-wide concurrently from the feed-enrichment fan-out
(Semaphore(8) on Dispatchers.IO), so its apply() ordering genuinely raced — a
real M-2 instance the audit's four-store scope missed. FeedCacheStore has no
StateFlow (caller-owned state) so it uses the captured-arg form; ordering is
safe because its sole writer (the feed VM) serializes save/clear from one
refresh coroutine.

Verified: headless compileDebugKotlin green on the straw-build image. The vc=88
PrefsWriter concurrency was cleared by an adversarial review before this builds
on it.
This commit is contained in:
Sulkta 2026-06-22 02:41:24 -07:00
parent ce7174cb5e
commit 41b3999ad3
7 changed files with 109 additions and 32 deletions

View file

@ -9,6 +9,25 @@ const val STRAW_SDK_TARGET = 35
// Sulkta fork — Straw
//
// vc=89 / 0.1.0-CW — pagination-burst fix + finish the SP-write serialization:
// * Hide-shorts no longer drains a whole channel/search in one burst. The
// infinite-scroll trigger is computed from the FILTERED list while loadMore()
// appends to the UNFILTERED one, so a shorts-heavy feed with hide-shorts ON
// kept the trigger hot (the filter stripped each fetched page back to
// ~nothing) and auto-fetched every page back-to-back to the end of the
// continuation. Now it keeps loading while pages are productive (the filtered
// list grows) and stops after 3 consecutive pages that add nothing visible;
// toggling hide-shorts off grows the list and resumes. Applies to both Search
// and Channel. (audit D-3)
// * Finished the vc=88 PrefsWriter migration: the remaining four SP stores
// (Enrichment, SearchCache, Playlists, FeedCache) now serialize their writes
// through the same single-thread-per-store dispatcher. EnrichmentStore is the
// one that mattered — put() runs 8-wide concurrently from the feed-enrichment
// fan-out, so its apply() ordering genuinely raced (a real M-2 instance the
// audit's four-store scope missed). No SP store is left on a bare
// sp.edit().apply() now.
// No user-visible behavior change beyond the pagination bound.
//
// vc=88 / 0.1.0-CV — deferred-hygiene sweep (audit #2 leftovers, no behavior change):
// * SharedPreferences writes across every store (Settings, History, Subs,
// ResumePositions) now route through one PrefsWriter — a single-thread
@ -266,6 +285,6 @@ const val STRAW_SDK_TARGET = 35
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
// strawcore-core (Sulkta-OSS/strawcore) via the UniFFI wrapper; no
// NewPipeExtractor in the runtime path.
const val STRAW_VERSION_CODE = 88
const val STRAW_VERSION_NAME = "0.1.0-CV"
const val STRAW_VERSION_CODE = 89
const val STRAW_VERSION_NAME = "0.1.0-CW"
const val STRAW_APPLICATION_ID = "com.sulkta.straw"