vc=42: loop round 4/5 — surgical cleanup, round 7 hit diminishing returns
Round-7 Opus audits explicitly flagged the audit loop as past
diminishing returns. Landing the few real items + one race-order
fix; leaving the comment-hygiene tail for the next time someone
reads the files.
MED
R7-1 SearchViewModel cache-preview race: vc=41 wrote the cached
preview to UI before cancelling the prior inFlight, so the
prior coroutine (already past its ensureActive() gate)
could reach its terminal _ui.update and clobber the cache
preview. Moved inFlight?.cancel() above the preview write.
R7-2 StrawActivity.YT_HOSTS duplicated util.YtUrl.ALLOWED_YT_HOSTS
— drift risk where one gets a new host and the other
doesn't. Collapsed StrawActivity.looksLikeYouTube to call
util.isAllowedYtUrl, picking up the scheme + trailing-dot
defenses for free.
R7-3 Dropped dead once_cell dep from rust/strawcore/Cargo.toml.
Round-4's runtime rewrite uses AtomicBool + Mutex; nothing
consumes once_cell anymore.
Audit explicitly called out (and we agree) these as past the value
threshold for further rounds:
- Verified-clean: try_lock no deadlock, ensureActive fence, bad-URL
early-return cancel, duplicate-zip-entry rejection, LIMIT clauses,
SponsorBlock 50ms exclusion drop, applied++ count.
- False positive: H1 "Related videos always empty" — the section
already gates on `d.related.isNotEmpty()`; UI doesn't paint when
extractor stub returns empty.
- Deferred: R8 enable, Nav rememberSaveable, LazyColumn keys,
collectAsStateWithLifecycle, DASH/HLS max-resolution cap,
Gradle cargo-build input/output declarations (CI cost, not
runtime), legacy :app module trim, stale comment cleanup.
This commit is contained in:
parent
ecc54aaf38
commit
10154c380b
4 changed files with 15 additions and 14 deletions
|
|
@ -55,6 +55,6 @@ const val NEWPIPE_APPLICATION_ID_NEW = "net.newpipe.app"
|
|||
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
|
||||
// strawcore-core (Sulkta-Coop/strawcore) via the UniFFI wrapper; no
|
||||
// NewPipeExtractor in the runtime path.
|
||||
const val STRAW_VERSION_CODE = 41
|
||||
const val STRAW_VERSION_NAME = "0.1.0-BA"
|
||||
const val STRAW_VERSION_CODE = 42
|
||||
const val STRAW_VERSION_NAME = "0.1.0-BB"
|
||||
const val STRAW_APPLICATION_ID = "com.sulkta.straw"
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@ strawcore-core = { path = "../../../strawcore" }
|
|||
rquickjs-sys = { version = "0.11", default-features = false, features = ["bindgen"] }
|
||||
# Error glue.
|
||||
thiserror = "1"
|
||||
# Single-threaded init for the runtime + extractor singletons.
|
||||
once_cell = "1"
|
||||
# Android log integration — `log::info!()` ends up in `adb logcat -s strawcore`.
|
||||
log = "0.4"
|
||||
android_logger = { version = "0.14", default-features = false }
|
||||
|
|
|
|||
|
|
@ -42,11 +42,9 @@ import com.sulkta.straw.feature.search.SearchScreen
|
|||
import com.sulkta.straw.feature.settings.SettingsScreen
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
private val YT_HOSTS = setOf(
|
||||
"youtube.com", "www.youtube.com", "m.youtube.com",
|
||||
"music.youtube.com", "youtube-nocookie.com", "www.youtube-nocookie.com",
|
||||
"youtu.be",
|
||||
)
|
||||
// Allowlist now lives in util/YtUrl.kt with extra hardening (scheme
|
||||
// requirement, trailing-dot strip). Round-7 audit MED-4: prior shape
|
||||
// duplicated the host set here and would drift away from the util.
|
||||
private val YT_URL_RE = Regex(
|
||||
"https?://(?:www\\.|m\\.|music\\.)?(?:youtube(?:-nocookie)?\\.com/[A-Za-z0-9_/?=&\\-.%]+|youtu\\.be/[A-Za-z0-9_\\-]+)",
|
||||
)
|
||||
|
|
@ -231,8 +229,6 @@ class StrawActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun looksLikeYouTube(url: String): Boolean {
|
||||
val host = runCatching { java.net.URI(url).host }.getOrNull() ?: return false
|
||||
return host.lowercase() in YT_HOSTS
|
||||
}
|
||||
private fun looksLikeYouTube(url: String): Boolean =
|
||||
com.sulkta.straw.util.isAllowedYtUrl(url)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,6 +144,14 @@ class SearchViewModel : ViewModel() {
|
|||
.firstOrNull { it.query.equals(q, ignoreCase = true) }
|
||||
?.items
|
||||
} else null
|
||||
// Cancel any prior in-flight submit BEFORE writing the cached
|
||||
// preview to the UI — round-7 audit MED-1: previously a fresh
|
||||
// submit that hit the cache could be clobbered seconds later
|
||||
// by the prior submit's late terminal write, because the
|
||||
// prior coroutine had already advanced past its `ensureActive`
|
||||
// gate by the time the new submit got around to cancelling.
|
||||
inFlight?.cancel()
|
||||
|
||||
if (cached != null && cached.isNotEmpty()) {
|
||||
_ui.update {
|
||||
it.copy(
|
||||
|
|
@ -164,7 +172,6 @@ class SearchViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
inFlight?.cancel()
|
||||
inFlight = viewModelScope.launch {
|
||||
try {
|
||||
// strawcore.search() is suspend on the tokio runtime baked
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue