Skip to content

fix(updates): sibling-app detection — Layer 5 stem filter (#591)#592

Merged
3 commits merged into
mainfrom
fix/591-cross-app-version-detection
May 13, 2026
Merged

fix(updates): sibling-app detection — Layer 5 stem filter (#591)#592
3 commits merged into
mainfrom
fix/591-cross-app-version-detection

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 13, 2026

Issue #591 — auto-update flagged APP B-2.20.apk as a higher version of an APP A-1.10.apk installed from the same repo, because Layer 5 (installer.choosePrimaryAsset) picks the highest-version asset from the auto-pick pool without checking that the candidate is actually the same app the user installed.

What landed:

  • AssetVariant.extractBaseStem(name) — lowercased, separator-stripped concatenation of every token that isn't a version-like number, arch token, or flavor token. APP A-1.10.apk → "appa", APP B-2.20.apk → "appb", app-arm64-v8a-1.10.apk and app-x86_64-1.10.apk → both "app". Empty stem (e.g. 2.0.apk) means "no signal — don't filter".
  • resolveTrackedRelease Layer 5 now receives installedAssetName (already on InstalledAppEntity). When the stem of the installed asset is non-empty, the auto-pick pool is filtered to assets with matching stem before choosePrimaryAsset runs.
  • Graceful fallback: if the stem filter would empty the pool (maintainer legitimately renamed the binary), we fall back to the broader filterByPackageFlavor pool so updates still flow.
  • 1.8.2 what's-new bullet under FIXED.

Not compile-verified locally~/.gradle SSD unmounted in this session. Visual review only. Please compile-check before merge: :core:domain, :core:data, :composeApp:compileDebugKotlinAndroid.

Closes #591.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed auto-update misidentifying a different sibling app from the same repository when selecting updates.
    • Improved update selection to prefer the currently installed asset’s base name, reducing incorrect update picks for similar assets.
    • Updated "What's New" to note the fix and preserve behavior for empty “Hide seen” results.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b6356139-e702-469d-a0a3-4820e01177ad

📥 Commits

Reviewing files that changed from the base of the PR and between ae22e40 and dd3218d.

📒 Files selected for processing (1)
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt

Walkthrough

The PR adds asset base-stem extraction and incorporates it into release resolution to disambiguate updates between sibling apps sharing a repository. The resolver now accepts the installed asset name, computes its stem by filtering version and platform tokens, and narrows candidate selection to assets with matching stems.

Changes

Auto-Update Disambiguation by Asset Stem

Layer / File(s) Summary
Base Stem Extraction Utility
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt
New extractBaseStem(assetName) function derives the base asset name by removing version-like and platform-specific tokens, returning an empty string when no stem remains. Private helper isVersionLikeToken() classifies numeric and version-prefixed patterns.
Stem-Filtered Release Resolution
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt
resolveTrackedRelease accepts a new installedAssetName parameter, extracts its stem, and filters the auto-pick candidate pool to assets with matching stems. Falls back to the full pool if the filtered set is empty. checkForUpdates now passes app.installedAssetName into the resolver.
Release Notes
core/presentation/src/commonMain/composeResources/files/whatsnew/17.json
What's New v17 documents that auto-update no longer confuses sibling apps from the same repository.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • OpenHub-Store/GitHub-Store#522: Both PRs modify update-check logic in InstalledAppsRepositoryImpl.checkForUpdates to refine how update availability is determined.
  • OpenHub-Store/GitHub-Store#570: Both PRs narrow auto-pick candidate assets during update checks, using different stem-filtering approaches: this PR via extractBaseStem versus the other via filterByPackageFlavor.

Poem

🐰 I nibble stems of filenames by night,
I chase away the mix-ups with a bite,
Two siblings now are rightly set apart,
Each update finds its own true counterpart,
Hooray—no more mismatched version art!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding stem filter logic to prevent sibling-app misdetection during auto-updates, which directly addresses the core issue.
Linked Issues check ✅ Passed All coding requirements from issue #591 are met: stem extraction logic prevents version misdetection between different apps by filtering candidates to matching base-names before selection.
Out of Scope Changes check ✅ Passed All changes directly address the sibling-app detection issue: stem extraction utility, resolver filtering, repository integration, and what's new documentation are all in-scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/591-cross-app-version-detection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 13, 2026

Greptile Summary

This PR introduces a stem-based sibling-app filter (Layer 5) to prevent choosePrimaryAsset from picking a higher-versioned sibling app shipped in the same GitHub release instead of the tracked one. The fix is well-structured and includes a graceful fallback when the stem filter would empty the candidate pool.

  • AssetVariant.extractBaseStem tokenises the asset filename and strips vocabulary tokens and version-like tokens, producing a stable name prefix (AppA-1.10.apk → \"appa\", AppB-2.20.apk → \"appb\") to distinguish siblings.
  • resolveTrackedRelease now receives installedAssetName and narrows the filterByPackageFlavor pool to matching-stem assets before choosePrimaryAsset runs; if no stem match is found the broader pool is used as a fallback.
  • isVersionLikeToken only recognises all-digit and v+digit tokens; common pre-release qualifiers (rc1, alpha1, beta3) are not stripped, causing them to leak into the stem and systematically defeat the filter for users with a pre-release build installed.

Confidence Score: 4/5

The core logic is sound and the fallback prevents broken updates, but the stem filter is silently disabled for any user whose installed build has a pre-release qualifier in its filename.

The sibling-app fix works correctly for stable-release filenames, which are the common case. However, isVersionLikeToken does not strip pre-release qualifier tokens such as rc1 or beta3 after tokenization, so a user who installed app-1.0-rc2.apk ends up with stem "apprc2". That stem never matches the stable-release stem "app", the filter always falls back to the unfiltered pool, and the original confusion bug reappears for that user on every subsequent update check.

core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt — the isVersionLikeToken function needs to cover pre-release qualifier tokens.

Important Files Changed

Filename Overview
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt Adds extractBaseStem and isVersionLikeToken; isVersionLikeToken does not recognise pre-release qualifier tokens (rc1, beta3, etc.), causing the stem filter to silently fall back to unfiltered behaviour for users who have a pre-release build installed.
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt Threads installedAssetName through resolveTrackedRelease and applies the new stem filter before choosePrimaryAsset; fallback logic and null-safety are handled correctly.
core/presentation/src/commonMain/composeResources/files/whatsnew/17.json Appends a 1.8.2 FIXED bullet describing the sibling-app detection improvement; no logic changes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[resolveTrackedRelease called\nwith installedAssetName] --> B[installableForApp\nfiltered by installer]
    B --> C{Layers 1-4\nfingerprint / position match?}
    C -- yes --> H[Return matched asset]
    C -- no --> D[filterByPackageFlavor\nnarrows by package flavor]
    D --> E{installedStem non-empty?}
    E -- no --> G[choosePrimaryAsset\nfull flavor pool]
    E -- yes --> F{Any pool asset\nshares stem?}
    F -- yes --> G2[choosePrimaryAsset\nstem-filtered pool]
    F -- no fallback --> G
    G --> H
    G2 --> H
Loading

Reviews (2): Last reviewed commit: "fix(stem): consume compound-vocab fragme..." | Re-trigger Greptile

Comment thread core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt`:
- Around line 278-289: extractBaseStem is leaving split architecture pieces like
"v8a"/"v7a" after tokenization (e.g., "arm64-v8a" → ["arm64","v8a"]) so stems
differ; fix by updating the ARCH_TOKENS constant (used by extractBaseStem) to
include the individual arch components such as "v8a" and "v7a" (and any other
split pieces you observe) so they are filtered out by the existing filterNot
check; ensure the tokens you add match the tokenization/normalization logic so
extractBaseStem produces the expected common stem for architecture variants.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 225728ef-6806-4b64-8d24-fd92eeb8ad39

📥 Commits

Reviewing files that changed from the base of the PR and between ffe4636 and ae22e40.

📒 Files selected for processing (3)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/InstalledAppsRepositoryImpl.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/AssetVariant.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/17.json

Comment on lines +325 to +330
private fun isVersionLikeToken(token: String): Boolean {
if (token.isEmpty()) return false
if (token.all { it.isDigit() }) return true
if (token.startsWith("v") && token.drop(1).all { it.isDigit() }) return true
return false
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 isVersionLikeToken misses pre-release qualifier tokens

After tokenize() splits on - and ., a filename like app-2.0-rc2.apk becomes ["app","2","0","rc2"]. The token rc2 is not all-digit, doesn't match v+digit, and isn't in VOCABULARY, so it leaks into the stem: installed app-1.0-rc2.apk → stem "apprc2", but new release app-2.0.apk → stem "app". These never match, the filter always falls back to the unfiltered pool, and the original sibling-app confusion bug reappears for every user who has a pre-release build installed. The KDoc explicitly lists 1.0-rc1 and beta3 as examples the function handles, but neither survives to true with the current implementation.

Suggested change
private fun isVersionLikeToken(token: String): Boolean {
if (token.isEmpty()) return false
if (token.all { it.isDigit() }) return true
if (token.startsWith("v") && token.drop(1).all { it.isDigit() }) return true
return false
}
private fun isVersionLikeToken(token: String): Boolean {
if (token.isEmpty()) return false
if (token.all { it.isDigit() }) return true
if (token.startsWith("v") && token.drop(1).all { it.isDigit() }) return true
// Pre-release qualifiers: rc1, rc2, alpha1, beta3, pre1, build456 …
// Targeted pattern avoids false-positives on real app-name tokens
// (e.g. `app2` has no common pre-release prefix and is left intact).
if (PRE_RELEASE_QUALIFIER_TOKEN.matches(token)) return true
return false
}
private val PRE_RELEASE_QUALIFIER_TOKEN = Regex("""^(rc|alpha|beta|pre|build)\d+$""")

@rainxchzed rainxchzed closed this pull request by merging all changes into main in 4925b1f May 13, 2026
@rainxchzed rainxchzed deleted the fix/591-cross-app-version-detection branch May 13, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Version detection issue

1 participant