Skip to content

feat: include starred repos in auto-link suggestions#686

Merged
rainxchzed merged 6 commits into
mainfrom
feat/637-starred-in-autolink
May 29, 2026
Merged

feat: include starred repos in auto-link suggestions#686
rainxchzed merged 6 commits into
mainfrom
feat/637-starred-in-autolink

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 28, 2026

Closes #637 (method 3).

@FumiyaSenro asked that when linking an already-installed app, the app should also look through the user's starred repos — most apps worth tracking are ones they've already starred, and the manual-URL fallback is annoying (nobody remembers repo URLs).

Rather than bloating the link sheet with a separate "pick from starred" step, this folds starred matching into the existing auto-match path. ExternalImportRepositoryImpl.resolveMatches (the single resolver behind both the "Scan GitHub Apps" link sheet AND the Obtainium import scan) now adds a 5th candidate source:

  • Fetches StarredRepository.getAllStarred() once per resolve.
  • Scores each starred repo against the device app by normalized name + package tail (alphanumeric-only, case-insensitive): exact match 0.78, substring match 0.60.
  • Emits RepoMatchSuggestion(source = STARRED, stars, description) merged with manifest / fingerprint / backend / Forgejo results.
  • Dedupe changed to sort-then-distinct so the highest-confidence entry survives when the same repo comes from multiple sources (previously distinctBy kept whichever source ran first, which could drop a higher-confidence starred hit).

Both flows surface starred matches automatically — no new screen, no extra button. Suggestions carry a "Starred" provenance chip.

Changes:

  • RepoMatchSource.STARRED + SuggestionSource.STARRED.
  • ExternalImportRepositoryImpl gains starredRepository dep (wired in SharedModule).
  • match_source_starred string + all 12 locales; chip label in LinkAppBottomSheet.
  • What's-new bullet (en + 12 locales).

Compile-verified :core:data + :feature:apps:presentation (Android) + :composeApp (jvm). Method 2 (reworking the "Add from Starred" screen to link-in-place) intentionally left out to keep this scoped.

Summary by CodeRabbit

  • New Features
    • Linking an installed GitHub app now searches and suggests your starred repositories, labeled with a new "Starred" match source.
  • Improvements
    • Starred-repo suggestions are scored, sorted and de-duplicated for better relevance.
    • Starred repo sync now surfaces latest release version and release link when available, improving suggestion detail accuracy.
  • Documentation
    • Updated release notes and localized strings to describe starred-repo suggestions and the new "Starred" label.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5d73932-fd6a-4b89-9323-df89d1e5cef2

📥 Commits

Reviewing files that changed from the base of the PR and between 7efad0c and 4b558d9.

📒 Files selected for processing (1)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt

Walkthrough

Adds starred-repo matching to the external import flow: data fetches starred repos, tokenizes and scores them against candidates, emits STARRED suggestions; domain, presentation, localization, and release notes updated to expose and render the new source.

Changes

Starred Repository Match Feature

Layer / File(s) Summary
Domain model & dependency wiring
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/system/RepoMatchSource.kt, core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt
RepoMatchSource enum adds STARRED; Koin binding now injects StarredRepository into ExternalImportRepositoryImpl.
Data layer star matching
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt
Fetches starred repos once per resolve, computes candidate eligibility from existing confidences, tokenizes/normalizes match needles, filters generic/short tokens, scores starred repos using exact/contains confidence thresholds, appends STARRED suggestions, sorts by confidence and dedupes by host/owner/repo; adds helpers and constants.
Starred repository asset detection
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/StarredRepositoryImpl.kt
checkForValidAssets() now returns StableReleaseInfo? (version + URL); syncStarredRepos() populates latestVersion/latestReleaseUrl when present; GitHub release model extended to parse tag_name and html_url.
Presentation integration
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/model/RepoSuggestionUi.kt, feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/ExternalImportViewModel.kt, feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/LinkAppBottomSheet.kt
Adds SuggestionSource.STARRED; view model maps RepoMatchSource.STARRED to UI suggestion source; MatchSourceChip renders the localized match_source_starred label.
Localization & release notes
core/presentation/src/commonMain/composeResources/values*/strings*.xml, core/presentation/src/commonMain/composeResources/files/whatsnew/*/19.json, docs/release-notes/1.9.0.md
Adds match_source_starred across locales; updates WhatsNew v19 JSONs to mention starred-repo suggestions when scanning GitHub apps; adds v1.9.0 release notes document.

Sequence Diagram

sequenceDiagram
  participant ImportVM as ExternalImportViewModel
  participant ExternalImportRepository
  participant StarredRepository
  participant MatchScoring as Match Scoring

  ImportVM->>ExternalImportRepository: resolveMatches(...)
  ExternalImportRepository->>StarredRepository: getAllStarred()
  StarredRepository-->>ExternalImportRepository: starred repo list
  ExternalImportRepository->>MatchScoring: build needles & score candidates vs starred repos
  MatchScoring->>MatchScoring: normalizeMatchToken(), exact/contains scoring
  MatchScoring-->>ExternalImportRepository: starred match suggestions
  ExternalImportRepository->>ExternalImportRepository: combine with other sources, sort, dedupe
  ExternalImportRepository-->>ImportVM: resolved suggestions (including STARRED)
  ImportVM->>ImportVM: toUi() map STARRED to SuggestionSource.STARRED
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐰 I nibble tokens, tidy and bright,
I hop through stars to find what's right.
Scans return your favorites near,
No copied links — suggestions appear.
Hooray — starred repos, soft and clear!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 PR title accurately describes the main change: adding starred repositories as a match source in the auto-link suggestion flow for app linking.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #637: integrating starred repos into existing matching flows (Scan GitHub Apps), reducing manual URL entry friction, and allowing preview before confirming.
Out of Scope Changes check ✅ Passed All changes directly support the starred-repo matching feature: domain/data layer additions (STARRED source enum, StarredRepository injection), presentation layer localization and UI wiring, and release notes documentation. No unrelated changes detected.

✏️ 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 feat/637-starred-in-autolink

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.

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: 5

🤖 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/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt`:
- Around line 270-272: The current use of runCatching around
starredRepository.getAllStarred().first() in ExternalImportRepositoryImpl (the
starred variable) swallows CancellationException and converts cancellations into
emptyList(); change it to explicitly preserve cancellation by replacing
runCatching with a try/catch that rethrows CancellationException and only
catches/logs non-cancellation exceptions, returning emptyList() on those; locate
the starredRepository.getAllStarred().first() call in
ExternalImportRepositoryImpl.kt and implement the try { val starred = ... }
catch (e: CancellationException) { throw e } catch (e: Exception) { Logger.d {
"starred fetch for match failed: ${e.message}" }; emptyList() } pattern.

In `@core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml`:
- Line 598: The Polish label for the starred source is ambiguous; update the
string resource named match_source_starred to use a clearer phrase for source
provenance by replacing its value "Z gwiazdką" with "Oznaczone gwiazdką" so it
matches the rest of the locale and reads unambiguously in the UI.

In `@core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml`:
- Line 598: The Russian label for the starred source is awkward; update the
string resource with name "match_source_starred" to use the consistent
terminology by replacing its value "Со звездой" with "Из избранного" so it
matches other locale strings that use "избранные" for starred repos.

In `@docs/release-notes/1.9.0.md`:
- Around line 5-48: The English release notes are missing the starred-repo
matching feature; insert a new subsection titled "### 🌟 Starred Repository
Matching" into the "New Features" section immediately after the "Desktop
Maturity" subsection and add the three bullets: "Linking installed apps now
matches against **your starred repositories**", "'Scan GitHub Apps'
automatically suggests starred repos as candidates", and "Reduces manual URL
pasting when linking apps you've already starred" so the English markdown
mirrors the localized JSON entries.
- Line 3: The document uses an h3 heading ("### The biggest visual overhaul
since launch. New Geist typography, hero app headers, redesigned Home cards with
platform glyphs, refreshed Library with updates banner + \"Ready to install\",
Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get
dedicated screens. Desktop finally feels native — window state persists, Windows
11 + macOS dark title bars, fluid content widths. Root installs work again on
modern Magisk via libsu rewrite.") immediately after the h1, skipping h2; change
that h3 to h2 (replace the leading "###" with "##") in
docs/release-notes/1.9.0.md so heading levels increment by one and restore
correct document structure and accessibility.
🪄 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: 2c85cba7-4ee5-4eaa-b33e-4606bc4cdf47

📥 Commits

Reviewing files that changed from the base of the PR and between 1636530 and f750cfa.

📒 Files selected for processing (33)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/system/RepoMatchSource.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ar/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/bn/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/es/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/fr/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/hi/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/it/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ja/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ko/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ru/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/tr/19.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/19.json
  • core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml
  • core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml
  • core/presentation/src/commonMain/composeResources/values-es/strings-es.xml
  • core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml
  • core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml
  • core/presentation/src/commonMain/composeResources/values-it/strings-it.xml
  • core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml
  • core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml
  • core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml
  • core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml
  • core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml
  • core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml
  • core/presentation/src/commonMain/composeResources/values/strings.xml
  • docs/release-notes/1.9.0.md
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/LinkAppBottomSheet.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/ExternalImportViewModel.kt
  • feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/model/RepoSuggestionUi.kt

Comment thread core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml Outdated
<string name="match_source_fingerprint">Подпись</string>
<string name="match_source_search">Поиск</string>
<string name="match_source_manual">Вручную</string>
<string name="match_source_starred">Со звездой</string>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Russian label is awkward and inconsistent with existing terminology.

Со звездой reads unnatural here. This locale already uses “избранные” for starred repos, so the source chip should be aligned (e.g., Из избранного).

Suggested fix
-    <string name="match_source_starred">Со звездой</string>
+    <string name="match_source_starred">Из избранного</string>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<string name="match_source_starred">Со звездой</string>
<string name="match_source_starred">Из избранного</string>
🤖 Prompt for 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.

In `@core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml`
at line 598, The Russian label for the starred source is awkward; update the
string resource with name "match_source_starred" to use the consistent
terminology by replacing its value "Со звездой" with "Из избранного" so it
matches other locale strings that use "избранные" for starred repos.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Declined — this would conflate two distinct features. The app separates starred (StarredRepository, "звёзды") from favourites (FavouritesRepository, "избранное"). This chip is GitHub-starred provenance, so it stays in the star vocabulary (Со звездой) rather than Из избранного.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@rainxchzed, understood — that's a clear and important distinction. Keeping Со звездой is correct here since it maps to GitHub stars (StarredRepository / "звёзды") and must not be conflated with local favourites (FavouritesRepository / "избранное"). I'll note this for future reviews.

🐰 ears droop slightly


✏️ Learnings added
Learnt from: rainxchzed
Repo: OpenHub-Store/GitHub-Store PR: 686
File: core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml:598-598
Timestamp: 2026-05-29T06:09:30.341Z
Learning: In the GitHub Store app (Kotlin Multiplatform), there are two distinct and separate features:
1. **Starred** (`StarredRepository`, "звёзды") — GitHub-starred repositories, referenced via `RepoMatchSource.STARRED` and `SuggestionSource.STARRED`. The Russian label for this is "Со звездой" (match_source_starred in strings-ru.xml).
2. **Favourites** (`FavouritesRepository`, "избранное") — locally saved favourite repositories.

Do NOT conflate these two in translations or code. In Russian, "звёзды/Со звездой" = GitHub stars, "избранное" = local favourites. The string `match_source_starred` correctly uses "Со звездой" and should NOT be changed to "Из избранного".

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@@ -0,0 +1,52 @@
# 🚀 GitHub Store 1.9 — Design Refresh & Desktop Maturity

### The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix heading level skip.

Line 3 uses h3 (###) directly after the h1 title on line 1, skipping h2. Markdown heading levels should increment by one level at a time for proper document structure and accessibility.

📝 Proposed fix
-### The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite.
+## The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite.
## The biggest visual overhaul since launch. New Geist typography, hero app headers, redesigned Home cards with platform glyphs, refreshed Library with updates banner + "Ready to install", Apple-style menus everywhere. Tablet two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels native — window state persists, Windows 11 + macOS dark title bars, fluid content widths. Root installs work again on modern Magisk via libsu rewrite.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 3-3: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🤖 Prompt for 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.

In `@docs/release-notes/1.9.0.md` at line 3, The document uses an h3 heading ("###
The biggest visual overhaul since launch. New Geist typography, hero app
headers, redesigned Home cards with platform glyphs, refreshed Library with
updates banner + \"Ready to install\", Apple-style menus everywhere. Tablet
two-pane lands. Inner Details pages get dedicated screens. Desktop finally feels
native — window state persists, Windows 11 + macOS dark title bars, fluid
content widths. Root installs work again on modern Magisk via libsu rewrite.")
immediately after the h1, skipping h2; change that h3 to h2 (replace the leading
"###" with "##") in docs/release-notes/1.9.0.md so heading levels increment by
one and restore correct document structure and accessibility.

Comment on lines +5 to +48
## ✨ New Features

### 🎨 Design Overhaul
- New **Geist** typography across the app
- Hero app header on Details with clickable owner avatar + ✓ verification badge
- Redesigned Home cards now show **every platform** a repo ships installers for
- Refreshed Library with **Updates banner** and **Ready to install** section
- Apple-style dropdown menus (`GhsDropdownMenu`) across all overflow surfaces
- Real Apple + Tux icons for macOS / Linux platform indicators
- Native Compose contribution calendar on developer profiles (hidden for orgs)
- Clickable @mentions and clickable company in bios

### 📱 Tablet Two-Pane
- Home / Search / Library list on the left, repo opens on the right
- Draggable divider, persists across sessions
- Inner Details (About, What's New) slide *within* the right pane

### 🖥 Desktop Maturity
- **Window state persists** — size, position, maximized survive across launches (#664)
- **Windows 11 + macOS** title bars match system dark mode (#663)
- **Fluid content width** — Compact / Wide / Extra wide scale as 55% / 75% / 95% of window
- Real GitHub Store logo in side drawer (no more "G" placeholder)
- MenuBar with About / Feedback / Licenses / Privacy

## 🐛 Bug Fixes
- **Mirror + direct download race** corrupting destination file (#667)
- **Root installer** on Android 14+ / Magisk 27+ rewritten on **libsu** (#651)
- Linked apps now show **"Update to X"** immediately instead of stale "Install vX"
- Multi-flavor APK repos no longer show false "Update" CTA (#638)
- Long release tags no longer wrap into one-char vertical date columns
- URL paste verifies repo exists before showing match
- Dynamic color contrast on Search TextField across all palettes
- README + release-notes keep scroll position on return

## ⚡ Performance
- Markdown no longer re-renders on every download progress tick (~10×/sec → 0)
- Chunked progressive markdown — large READMEs paint first screen in <100ms
- `LazyColumn.animateItem()` across discovery / search / library
- Direction-aware bottom-nav transitions based on tab index

## 🧹 Cleanups
- Anonymous telemetry removed completely
- App info moved from Tweaks → Profile (where users look for it)
- Discovery platforms moved into Tweaks → Sources
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add missing starred repository matching feature documentation.

The primary feature of this PR—starred repository matching in the external app import workflow—is documented in all 12 localized JSON release notes (line 15 in each file) but is completely absent from this English markdown release notes file. Users reading docs/release-notes/1.9.0.md will not learn about this significant new capability.

📝 Suggested addition to the "New Features" section

Insert a new subsection after "Desktop Maturity" (around line 27):

### 🌟 Starred Repository Matching
- Linking installed apps now matches against **your starred repositories**
- "Scan GitHub Apps" automatically suggests starred repos as candidates
- Reduces manual URL pasting when linking apps you've already starred
🤖 Prompt for 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.

In `@docs/release-notes/1.9.0.md` around lines 5 - 48, The English release notes
are missing the starred-repo matching feature; insert a new subsection titled
"### 🌟 Starred Repository Matching" into the "New Features" section immediately
after the "Desktop Maturity" subsection and add the three bullets: "Linking
installed apps now matches against **your starred repositories**", "'Scan GitHub
Apps' automatically suggests starred repos as candidates", and "Reduces manual
URL pasting when linking apps you've already starred" so the English markdown
mirrors the localized JSON entries.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR folds starred-repo matching into the existing resolveMatches auto-link path, adding a fifth candidate source (STARRED) with normalized name scoring and a skip-threshold guard consistent with the Forgejo source. It also refactors StarredRepositoryImpl.checkForValidAssets to return StableReleaseInfo? (carrying tagName + htmlUrl), so the DB now stores latestVersion / latestReleaseUrl and the new starredMatches function can pre-filter on those values.

  • Core matching logic (ExternalImportRepositoryImpl): starredEligible guard, GENERIC_MATCH_TOKENS, and STARRED_EXACT/CONTAINS_CONFIDENCE are well-considered; sort-before-distinct correctly preserves the highest-confidence entry when the same repo surfaces from multiple sources.
  • New dropSuggestionsWithoutReleases verifies BACKEND and FORGEJO_SEARCH suggestions via live getReleases calls; RELEASE_VERIFY_SKIP_SOURCES correctly excludes STARRED, MANIFEST, and FINGERPRINT, but does not exclude BACKEND — introducing up to 60 extra API calls per resolveMatches() invocation that pre-filtered backend results don't require.
  • Localization and UI wiring (13 locales + ViewModel + MatchSourceChip) is complete and consistent.

Confidence Score: 5/5

Safe to merge — the starred matching logic is well-guarded by the skip-threshold, GENERIC_MATCH_TOKENS, and the latestReleaseUrl sentinel; dropSuggestionsWithoutReleases is fail-safe.

The starred scoring, deduplication ordering fix, and StarredRepositoryImpl changes are all correct. Omitting BACKEND from RELEASE_VERIFY_SKIP_SOURCES causes redundant re-verification of backend-matched repos, increasing resolve latency, but doesn't produce wrong results. All locales and UI enum cases are fully wired.

ExternalImportRepositoryImpl.kt — specifically the RELEASE_VERIFY_SKIP_SOURCES set and whether BACKEND should be added to avoid redundant API calls on every resolveMatches() invocation.

Important Files Changed

Filename Overview
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt Core logic for starred matching, skip-threshold guard, and new dropSuggestionsWithoutReleases. BACKEND is not in RELEASE_VERIFY_SKIP_SOURCES, causing backend suggestions to be re-verified via up to 60 extra API calls per resolveMatches() invocation.
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/StarredRepositoryImpl.kt checkForValidAssets refactored to return StableReleaseInfo? with tagName/htmlUrl; latestVersion and latestReleaseUrl now populated on sync. Clean and consistent across both backend and direct GitHub paths.
core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt starredRepository dependency wired into ExternalImportRepositoryImpl; correct DI injection.
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/components/LinkAppBottomSheet.kt STARRED case added to MatchSourceChip when expression; complete and correct.
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/ExternalImportViewModel.kt STARRED mapped to SuggestionSource.STARRED correctly.
feature/apps/presentation/src/commonMain/kotlin/zed/rainxch/apps/presentation/import/model/RepoSuggestionUi.kt STARRED added to SuggestionSource enum; correct and complete.

Sequence Diagram

sequenceDiagram
    participant UI as LinkAppBottomSheet
    participant VM as ExternalImportViewModel
    participant Repo as ExternalImportRepositoryImpl
    participant StarredDB as StarredRepository (DB)
    participant BackendAPI as BackendApiClient
    participant ForgejoAPI as ForgejoClientRegistry

    UI->>VM: triggerResolve(candidates)
    VM->>Repo: resolveMatches(candidates)
    Repo->>BackendAPI: match(batch) [BACKEND]
    BackendAPI-->>Repo: backendResults
    Repo->>ForgejoAPI: searchRepositories(...) [FORGEJO_SEARCH, if eligible]
    ForgejoAPI-->>Repo: forgejoHits
    Note over Repo: bestConfidence < 0.7 - starredEligible
    Repo->>StarredDB: getAllStarred().first() [if starredEligible non-empty]
    StarredDB-->>Repo: starred list
    Note over Repo: starredMatches() scores each starred repo
    Repo->>Repo: sort-then-distinctBy(suggestionKey)
    Repo->>Repo: dropSuggestionsWithoutReleases(rawResults)
    Note over Repo: BACKEND + FORGEJO_SEARCH verified
    Repo->>BackendAPI: getReleases(owner,repo) [up to 60 calls, 8 concurrent]
    BackendAPI-->>Repo: verdicts
    Repo-->>VM: List RepoMatchResult
    VM-->>UI: suggestions with STARRED chip
Loading

Fix All in Claude Code

Reviews (5): Last reviewed commit: "fix(import): skip release check for iden..." | Re-trigger Greptile

@rainxchzed
Copy link
Copy Markdown
Member Author

rainxchzed commented May 29, 2026

@coderabbitai Pushed fixes for the review:

Fixed (commit ec155e7)

  • Generic-token false positives (greptile): added a GENERIC_MATCH_TOKENS blocklist (android, mobile, lite, beta, debug, release, client, application, free, alpha, demo, sample, test, plus, apps, main, core). These are dropped from both needles and starred repo names, so com.twitter.android no longer 0.60-matches every *-android-* repo. The app label needle still carries specificity when the package tail is generic.
  • Cancellation swallowed (CodeRabbit): the starred runCatching now rethrows CancellationException before logging/defaulting.
  • Redundant Room read (greptile): starred matching is now gated behind the same < FORGEJO_SEARCH_SKIP_THRESHOLD eligibility used for Forgejo search. The getAllStarred() query is skipped entirely when no candidate needs it, and only low-confidence candidates get starred suggestions — so a high-confidence fingerprint/backend match isn't diluted by weak starred guesses.

Fixed (commit deb8064)

  • pl label (CodeRabbit): Z gwiazdkąOznaczone gwiazdką (matches GitHub's Polish "Star" term).

Won't fix

  • ru label (CodeRabbit suggested Из избранного): declined. This app keeps starred (StarredRepository, "звёзды") distinct from favourites (FavouritesRepository, "избранное"). Из избранного would conflate the two features. Со звездой stays in the correct star vocabulary.
  • English release-notes (CodeRabbit docs/release-notes/1.9.0.md + MD001): deferred. This feature ships in the next version, not 1.9.0 (already released). The whatsnew JSON currently sits in 19.json and will be repositioned with the version bump — handled separately from this PR.

Compile-verified :core:data (Android + JVM).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Tip

For best results, initiate chat on the files or code changes.

@rainxchzed Thanks for the detailed update — all looks good!

Acknowledged fixes:

  • CancellationException rethrow — correct coroutines hygiene.
  • ✅ Polish chip label Oznaczone gwiazdką — matches GitHub's own Polish wording.
  • GENERIC_MATCH_TOKENS blocklist — solid precision improvement; the app-label needle providing specificity when the package tail is generic is a good fallback.
  • getAllStarred() gated behind FORGEJO_SEARCH_SKIP_THRESHOLD — avoids unnecessary Room I/O and keeps high-confidence matches uncontaminated.

Won't-fix decisions:

  • ru label — Reasoning is sound. Keeping Со звездой / star-vocabulary (StarredRepository, "звёзды") distinct from favourites-vocabulary (FavouritesRepository, "избранное") is the right call; conflating them would mislead Russian users about which feature they're interacting with.
  • Release-notes placement — Makes sense; 1.9.0 is already shipped and repositioning alongside the next version bump is the cleaner approach.

(ᵔ◡ᵔ)🐇⭐


✏️ Learnings added
Learnt from: rainxchzed
URL: https://github.com/OpenHub-Store/GitHub-Store/pull/686

Timestamp: 2026-05-29T06:37:00.645Z
Learning: In the OpenHub-Store/GitHub-Store project, `StarredRepository` uses "star" vocabulary (Russian: "звёзды", "Со звездой") while `FavouritesRepository` uses "favourites" vocabulary (Russian: "избранное"). The two features are intentionally kept distinct in all localisations, so Russian strings for starred content should use star-vocabulary ("Со звездой") and never use "избранное"/"Из избранного".

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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.

🧹 Nitpick comments (1)
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/StarredRepositoryImpl.kt (1)

219-246: ⚡ Quick win

Reuse matchesPlatform() in the GitHub fallback to avoid divergence.

The inline when (platform) filter here is a verbatim duplicate of matchesPlatform() (lines 165-174), which the backend path already uses at line 186. Consolidating keeps platform-extension logic in a single place so the two paths can't drift apart.

♻️ Proposed refactor
             val relevantAssets =
                 stableRelease.assets.filter { asset ->
-                    val name = asset.name.lowercase()
-                    when (platform) {
-                        Platform.ANDROID -> {
-                            name.endsWith(".apk")
-                        }
-
-                        Platform.WINDOWS -> {
-                            name.endsWith(".msi") || name.endsWith(".exe")
-                        }
-
-                        Platform.MACOS -> {
-                            name.endsWith(".dmg") || name.endsWith(".pkg")
-                        }
-
-                        Platform.LINUX -> {
-                            name.endsWith(".appimage") || name.endsWith(".deb") ||
-                                name.endsWith(".rpm") || name.endsWith(".pkg.tar.zst")
-                        }
-                    }
+                    matchesPlatform(asset.name)
                 }
🤖 Prompt for 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.

In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/StarredRepositoryImpl.kt`
around lines 219 - 246, The asset filter duplicates the platform-matching logic
already encapsulated by matchesPlatform(); replace the inline when(platform)
block inside the stableRelease.assets.filter in relevantAssets with a call to
the existing matchesPlatform() helper (i.e., use matchesPlatform(...) on the
asset/name as the helper is defined) so both backend and GitHub-fallback paths
use the same logic, keeping StableReleaseInfo(version = stableRelease.tagName,
url = stableRelease.htmlUrl) behavior unchanged when
relevantAssets.isNotEmpty().
🤖 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.

Nitpick comments:
In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/StarredRepositoryImpl.kt`:
- Around line 219-246: The asset filter duplicates the platform-matching logic
already encapsulated by matchesPlatform(); replace the inline when(platform)
block inside the stableRelease.assets.filter in relevantAssets with a call to
the existing matchesPlatform() helper (i.e., use matchesPlatform(...) on the
asset/name as the helper is defined) so both backend and GitHub-fallback paths
use the same logic, keeping StableReleaseInfo(version = stableRelease.tagName,
url = stableRelease.htmlUrl) behavior unchanged when
relevantAssets.isNotEmpty().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b1344fe-32d3-4417-a683-cb922e64860b

📥 Commits

Reviewing files that changed from the base of the PR and between deb8064 and 1699fb2.

📒 Files selected for processing (2)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/StarredRepositoryImpl.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt

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

🧹 Nitpick comments (2)
core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt (2)

338-355: Consider memoizing release verdicts to avoid repeated network checks.

Verification now issues up to RELEASE_VERIFY_BUDGET release lookups on the resolve path, and rescanSinglePackage re-runs this per call, re-verifying the same repos. A short-lived cache (keyed by suggestionKey, with a TTL) would cut redundant getReleases traffic during repeated scans.

🤖 Prompt for 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.

In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt`
around lines 338 - 355, The release-verdict loop (building verdicts in the
coroutineScope using suggestionKey and calling hasInstallableRelease with
RELEASE_VERIFY_TIMEOUT_MS) should be backed by a short-lived, coroutine-safe
memoization cache keyed by suggestionKey to avoid repeated network getReleases
calls across resolve path and rescanSinglePackage; modify the block that
computes ok (the runCatching/withTimeoutOrNull around hasInstallableRelease) to
first consult the cache and return a cached Boolean if present, otherwise call
hasInstallableRelease, store the result with a TTL (e.g., a time-based expiry)
and then return it; ensure the cache is safe for concurrent coroutines and that
RELEASE_VERIFY_BUDGET/rescanSinglePackage callers reuse it so redundant
verifications are skipped.

364-386: Confirm getReleases model contract; call out remaining first-page limitation (ExternalImportRepositoryImpl.kt, lines 364-386)

  • Both backendClient.getReleases(...) and forgejoClientRegistry.clientFor(host).getReleases(...) return Result<List<ReleaseNetwork>> with draft, prerelease, and assets where AssetNetwork exposes name, so the .draft/.prerelease/assets[].name checks are consistent.
  • hasInstallableRelease only fetches the first page (page defaults to 1 in both clients) and only uses RELEASE_VERIFY_PAGE_SIZE = 10, so APK assets that exist only in older releases will be missed and treated as “no installable release”.
🤖 Prompt for 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.

In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt`
around lines 364 - 386, hasInstallableRelease currently only fetches the first
page (using getReleases with default page=1 and RELEASE_VERIFY_PAGE_SIZE=10) and
can miss APKs on older pages; update hasInstallableRelease (and the branches
calling backendClient.getReleases /
forgejoClientRegistry.clientFor(host).getReleases) to paginate until either an
installable release is found or no more pages (request subsequent pages using
the same perPage/RELEASE_VERIFY_PAGE_SIZE), or explicitly document/return a
bounded-check result if pagination isn’t desired; ensure you use the
Result<List<ReleaseNetwork>> handling already present (fold onSuccess/onFailure)
for each page and stop early when list.any { release -> ... assets.any {
it.name.endsWith(".apk", ignoreCase = true) } } is true.
🤖 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/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt`:
- Around line 326-330: In dropSuggestionsWithoutReleases(), the current
construction of unique (which only excludes RepoMatchSource.STARRED) causes
MANIFEST and FINGERPRINT suggestions to be included in the first
RELEASE_VERIFY_BUDGET items and subject to hasInstallableRelease() filtering;
change the filter when building unique to also exclude RepoMatchSource.MANIFEST
and RepoMatchSource.FINGERPRINT so identity matches (high-confidence
fingerprint/manifest) are not dropped by the release-check path. Update the
.filter { it.source != RepoMatchSource.STARRED } to exclude the two identity
sources and verify the rest of the logic (suggestionKey(),
RELEASE_VERIFY_BUDGET, hasInstallableRelease()) remains unchanged.

---

Nitpick comments:
In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt`:
- Around line 338-355: The release-verdict loop (building verdicts in the
coroutineScope using suggestionKey and calling hasInstallableRelease with
RELEASE_VERIFY_TIMEOUT_MS) should be backed by a short-lived, coroutine-safe
memoization cache keyed by suggestionKey to avoid repeated network getReleases
calls across resolve path and rescanSinglePackage; modify the block that
computes ok (the runCatching/withTimeoutOrNull around hasInstallableRelease) to
first consult the cache and return a cached Boolean if present, otherwise call
hasInstallableRelease, store the result with a TTL (e.g., a time-based expiry)
and then return it; ensure the cache is safe for concurrent coroutines and that
RELEASE_VERIFY_BUDGET/rescanSinglePackage callers reuse it so redundant
verifications are skipped.
- Around line 364-386: hasInstallableRelease currently only fetches the first
page (using getReleases with default page=1 and RELEASE_VERIFY_PAGE_SIZE=10) and
can miss APKs on older pages; update hasInstallableRelease (and the branches
calling backendClient.getReleases /
forgejoClientRegistry.clientFor(host).getReleases) to paginate until either an
installable release is found or no more pages (request subsequent pages using
the same perPage/RELEASE_VERIFY_PAGE_SIZE), or explicitly document/return a
bounded-check result if pagination isn’t desired; ensure you use the
Result<List<ReleaseNetwork>> handling already present (fold onSuccess/onFailure)
for each page and stop early when list.any { release -> ... assets.any {
it.name.endsWith(".apk", ignoreCase = true) } } is true.
🪄 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: bb2a983a-6ed7-42da-8923-222427ec0144

📥 Commits

Reviewing files that changed from the base of the PR and between 1699fb2 and 7efad0c.

📒 Files selected for processing (1)
  • core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/ExternalImportRepositoryImpl.kt

@rainxchzed rainxchzed merged commit 4f0336a into main May 29, 2026
1 check was pending
@rainxchzed rainxchzed deleted the feat/637-starred-in-autolink branch May 29, 2026 07:36
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.

本地关联方面

1 participant