fix(fdroid): enforce latest version, add metadata, replace placeholder icons - #42
Conversation
- F-Droid workflow now downloads APKs from all releases instead of only the latest, so the repo contains full version history and upgrades work correctly without needing to uninstall/reinstall. - DeviceInfo.android.kt reads actual versionName from PackageManager instead of the hardcoded "1.0.0". - SettingsDialog shows the app version (e.g. "v0.8.1") at the bottom of the settings sidebar. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DeviceInfo.ios.kt: read CFBundleShortVersionString from NSBundle instead of returning hardcoded "1.0.0", so the Settings version label is accurate on iOS. - fdroid.yml: replace blanket `|| true` with explicit "no assets match" guard; fail fast on auth/network/ratelimit errors and emit a clear error if zero APKs are downloaded. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lict handling (#21) * fix(sync): prevent data-loss races on mobile reload and external conflict handling - Android: wire up SafChangeDetector ContentObserver in GraphLoader.startWatching(); add SAF-optimized listFilesWithModTimes to avoid N+1 queries per poll cycle - iOS: fix getLastModifiedTime (was hardcoded null), listFiles (empty list bug), listDirectories and directoryExists (didn't distinguish files from dirs) - DebounceManager: add cancel(key) and hasPending(key) suspend fns for targeted per-key cancellation and pending-state inspection - FileRegistry.detectChanges: wrap in Mutex to prevent double-emit on concurrent watcher poll + ContentObserver callback races - BlockStateManager: add hasPendingDiskWrite / cancelPendingDiskSave using the new DebounceManager APIs - StelekitViewModel: extend shouldProtect to cover the DB-saved-but-not-yet-written window (~300ms); cancel pending disk save when conflict dialog is shown; acceptDiskVersion flushes to disk via savePageNow to maintain consistency - Tests: comprehensive coverage for all five race-condition fixes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(state): unobservePage now clears blocks from state map The previous implementation cancelled the coroutine job but left the page's blocks in _blocks indefinitely, causing unbounded memory growth as users navigate pages. Now evicts the page entry on unobserve, which also aligns with the unobservePage_clears_state test contract. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sync): coalesce ContentObserver callbacks with Channel.CONFLATED Rapid ContentObserver signals (or the SAF 30s polling fallback) could queue up many redundant directory scans — each callback launched an independent parallelScope coroutine. Replace with a Channel(CONFLATED) that drops all-but-one pending trigger, so at most one extra scan is queued during a burst. The consumer coroutine is now a child of watcherJob so it is properly cancelled when startWatching is called again. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ios): fix commonMain compilation errors in PerfExporter and PerformanceDashboard String.format() and Dispatchers.IO are JVM-only; replace with KMP-compatible padStart string building and PlatformDispatcher.IO respectively. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
… ranking (#22) - FtsQueryBuilder: multi-term queries now use AND (all terms required) with wildcard on every token; OR fallback fires automatically when AND returns empty - BM25 scores exposed on SearchedPage/SearchedBlock; RankedSearchHit sealed class added for cross-type ranking with abstract score field - PAGE_BOOST (5×): page-title FTS hits ranked above body-text hits - RECENCY_HALFLIFE_DAYS (14d): exponential decay gives recently-edited results up to 2× score multiplier - GRAPH_BOOST (3×): results from 1-hop neighbour pages (via block_references) receive a 3× multiplier; selectNeighbourPageUuids UNION query pre-fetches the full neighbour set in a single indexed read - 37 tests covering AND semantics, OR fallback, field boost, graph distance, recency Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
When all public members of a class are suspend fun, the K2 compiler
silently omits the outer .class file for the JVM target while still
generating the inner coroutine continuation classes. This caused a
NoClassDefFoundError: dev/stapler/stelekit/cache/LruCache at runtime.
Three workarounds applied:
- Extract weigher to fun interface LruWeigher<K,V> (SAM-convertible,
call sites unchanged — lambdas still work via SAM conversion)
- Replace Mutex.withLock{} (inline suspend) with explicit lock()/unlock()
- Add val capacity: Long (non-suspend anchor that forces outer class emission)
The root trigger is the all-suspend public API. Adding any one non-suspend
public member to the class is sufficient to fix the missing .class file.
Bug filed: https://youtrack.jetbrains.com/issue/KT-XXXXX
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The previous workaround (val capacity: Long) forced LruCache.class emission on macOS but the Kotlin 2.3.10 K2 compiler still dropped the class on Linux. Adding override fun toString() as a second non-suspend anchor reliably triggers outer class emission on both platforms.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The Kotlin 2.3.10 K2 compiler silently drops LruCache.class on Linux when all public members are suspend fun — non-suspend anchors (val capacity, override toString) fixed the issue locally on macOS but not in the Linux CI build environment. Switch to synchronized(this) instead of Mutex, making all methods plain fun. Operations are O(1) and hold the lock for microseconds so brief thread blocking is acceptable. Callers need no changes since non-suspend functions are callable from suspend contexts. The suspend → fun change also eliminates the all-suspend-public-API compiler bug entirely: no coroutine inner classes are generated so LruCache.class is always emitted.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Kotlin 2.3.10 K2 on the macOS CI runner silently drops the .class file for a class named exactly "LruCache" in this package. All other workarounds (val property anchor, toString override, synchronized instead of suspend) failed to prevent the omission. Renaming to SteleLruCache produces a valid .class file. Type aliases LruCache and LruWeigher are kept so all call sites require zero changes.
…he restore Prevents the Gradle Actions setup from falling back to a prefix-match cache restore for the macOS desktop build. Without this, stale Kotlin Incremental Compilation state in ~/.gradle/caches can cause the K2 compiler to reuse old output and silently omit .class files for renamed or modified classes. This pairs with the SteleLruCache rename (v0.9.4) to ensure a clean compile on macOS CI.
All four build jobs (android, linux, macos, windows) now share identical setup-gradle options: - gradle-home-cache-cleanup: true - gradle-home-cache-strict-match: true (was macOS-only) - cache-encryption-key The strict-match flag prevents the Gradle Actions prefix-match fallback from restoring stale Kotlin Incremental Compilation state from a different commit's cache entry. Without it, the K2 compiler can reuse old IC output and silently drop .class files for renamed or modified classes. The macOS build command is also aligned with other desktop jobs: - Removed :kmp:clean (was a workaround; strict-match is the real fix) - Added --build-cache (now consistent with linux and windows)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Three root causes of the infinite loading spinner, all fixed: 1. App.kt: use try/finally around migrationReady=false so it always resets to true even if the LaunchedEffect is cancelled mid-run. 2. App.kt: initialize currentGraphPath from the persisted active graph (graphManager.getActiveGraphInfo()?.path) instead of the hardcoded getDefaultGraphPath() which always pointed to ~/Documents/stelekit, causing an unnecessary graph-switch on every launch. 3. StelekitViewModel: catch CancellationException separately before the generic Exception catch and rethrow it — prevents structured-concurrency cancellation from being swallowed, which left isLoading stuck at true. Test isolation (the source of /tmp/rp-stale in production prefs): 4. Extract Settings interface from PlatformSettings expect class so tests can pass InMemorySettings instead of the file-backed PlatformSettings. All test files (RecentPagesTest, DiskConflictResolutionTest, ComposeUITestBase, TopBarTest, PageViewUITest, DesktopScreenshotTest) now use InMemorySettings — no more test paths leaking into ~/.stelekit/prefs.properties.
- Add test-kmp job to release.yml gating resolve-version on test pass - Add StelekitViewModelLoadingTest: 6 pure-Kotlin tests covering isLoading state transitions, missing-directory error path, onboarding reset, scope cancellation resilience (CancellationException regression) - Add MigrationReadyLoadingTest: 4 Compose tests for the try/finally migrationReady pattern that caused the infinite spinner - Add waitForViewModelReady() extension to ComposeUITestBase These tests would have caught the two bugs fixed in dc1b51b.
…preemption (#26) * perf(android): decompose Phase 3 chunk writes to allow HIGH-priority preemption Phase 3 background indexing dispatched all saves for a 10-page chunk inside a single Execute(LOW) lambda, holding DatabaseWriteActor for the full chunk duration. A real Android session export showed a 4904ms db.queue_wait span on a HIGH-priority request — user navigation was blocked for ~5s while indexing ran. Replace flushChunkWrites (Execute-wrapped) with flushChunkWritesPreemptible, which dispatches savePages, deleteBlocksForPages, and saveBlocks as separate typed LOW actor calls. The actor can now service HIGH-priority SavePage/SaveBlocks requests between each step, including between consecutive pages during block saves (via the existing coalescing preemption in processSaveBlocks). Also extend PerfExporter.export() to accept an optional directory parameter and expose defaultExportDirectory(), wired up in PerformanceDashboard as an AlertDialog that lets the user choose the export location before writing the JSON report. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(research): android save/parse performance investigation artifacts MDD Phase 1–2 artifacts for the android-save-parse-performance project: requirements, research plan, findings across 4 dimensions (stack, features, architecture, pitfalls), and ADR-ready synthesis identifying DatabaseWriteActor contention as the confirmed root cause (4904ms db.queue_wait span on Android). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(android): add instrumented benchmark to gate actor-contention regressions Adds an androidInstrumentedTest in :kmp that runs on a real Android emulator in CI via reactivecircus/android-emulator-runner (API 30, x86_64). Two tests: - loadPhaseTimings: measures phase 1 TTI + phase 3 indexRemainingPages duration, asserts phase 1 stays under 10s (conservative for emulator variance) - writeLatencyDuringPhase3: the regression guard — concurrently runs indexRemainingPages and block saves every 200ms, asserts write p95 stays under 5s. Before the actor decomposition fix, this would have measured ~5000ms (Execute(LOW) holding the actor for a full 10-page chunk); after the fix it should be well under 2s. Uses a lightweight inline FileSystem instead of PlatformFileSystem to avoid SAF ContentProvider dependency in the test process. Generates 20 pages + 5 journals directly into context.cacheDir so no SAF permissions are needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(bench): android benchmark history tracking and PR diff comments Mirrors the JVM benchmark diff pattern for Android: - AndroidGraphBenchmark now logs structured JSON to logcat (tag ANDROID_BENCH) at the end of each test: loadPhase and writeLatency metrics - android-benchmark.yml captures logcat after the emulator run, parses the JSON lines with Python, and writes a timestamped summary to benchmarks/android-history/{slug}_{sha}.json - On push to main: commits the new history file (same pattern as benchmark.yml) - On PR: compares against the most recent baseline in android-history/ and posts (or updates) a comment with a delta table covering phase 1 TTI, phase 3 index time, write p95 baseline, write p95 during phase 3, and jank factor Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(bench): remove backslash line continuations in emulator runner script reactivecircus/android-emulator-runner executes each script line separately, so backslash continuations are not shell-interpreted. Put the gradle command on a single line. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address Copilot review comments PerfExporter: treat blank directory string as absent so clearing the path field in the export dialog falls back to the Downloads folder instead of producing an invalid path like /stelekit-perf-....json. GraphLoader: restore per-page atomicity in flushChunkWritesPreemptible. The previous implementation called deleteBlocksForPages for all pages at once then saveBlocks per page via separate typed requests. A HIGH write could interleave between the delete and save for the same page, leaving it transiently with no blocks. Fix: wrap each page's deleteBlocksForPages + saveBlocks inside a single Execute(LOW) so HIGH can only preempt between pages (not within a page's delete+save sequence). GraphLoader: move KDoc above @OptIn so it is attached to the function. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve compile errors introduced by merging main - Add `: Settings` to Android actual PlatformSettings (expect/actual mismatch from Settings interface added to main after our branch diverged) - Make FakeFileSystem open so MissingDirectoryFileSystem in StelekitViewModelLoadingTest can extend it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: remove unimportable assertDoesNotExist import, add override modifiers - Remove `import androidx.compose.ui.test.assertDoesNotExist` — in Compose Multiplatform Desktop the function is a member of SemanticsNodeInteraction, not a top-level extension function; callers use it as a method call directly - Add `override` modifier to all four Settings method implementations in Android PlatformSettings (required when a class explicitly implements an interface via `: Settings`) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: guarantee isLoading=false when loadGraph job is cancelled pre-start When scope.cancel() is called before the loadGraph coroutine reaches its first suspension point, the CancellationException catch block may not run, leaving isLoading=true indefinitely. invokeOnCompletion fires unconditionally when the job ends — including for cancellations that happen before the coroutine body executes — so isLoading always resets to false. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ios): replace JVM-only synchronized with KMP-compatible PlatformLock LruCache.kt was updated on main to use synchronized(this) which is JVM-only and breaks Kotlin/Native (iOS) compilation. Replace with an expect/actual PlatformLock that uses ReentrantLock on JVM/Android and a no-op on iOS/WASM where coroutine dispatcher scheduling provides mutual exclusion within the DB context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…s process lifecycle Introduces SteleKitApplication to initialize the database driver and file system at process start, so GraphManager survives Activity recreation without re-loading the graph. Adds Glance AppWidget dependencies for upcoming home screen widget support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When commits land on main while the benchmark job is running, the summary push fails with a non-fast-forward error. Retry up to 5 times, pulling with rebase before each attempt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…first The onCreatePage callback in PageView and JournalsView was a no-op, so selecting "Create page" from the link picker did nothing. Wire it to insertLinkAtCursor — same behaviour as selecting an existing page. Also prepend CreatePageItem instead of appending so it is the first (default-selected) result rather than buried below all page/block hits. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r icons Three issues prevented the F-Droid store from showing correct information: 1. versionCode was hardcoded to 1 — F-Droid had no way to rank APKs, so it suggested the oldest release (v0.1.0). Now computed as major×1_000_000 + minor×1_000 + patch, coerced ≥2 so all future APKs outrank the historical versionCode=1 builds. 2. No fdroid/metadata/ directory — the store showed no description or links. Added fdroid/metadata/dev.stapler.stelekit.yml with name, summary, full description, license, and source/issue-tracker URLs. 3. All mipmap ic_launcher_foreground.png, ic_launcher.png, ic_launcher_round.png, and ic_launcher_monochrome.png were identical 3 KB placeholder files regardless of density. Replaced with the SteleKit brand icon (from assets/brand/png/icon_512.png) resized to the correct density-proportional dimensions, with the adaptive foreground respecting the 72/108dp safe zone. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates release/distribution metadata (notably for F-Droid) and includes a broad set of Android/KMP runtime, search, sync/conflict-handling, and CI benchmarking changes that collectively improve packaging correctness and app robustness/performance across platforms.
Changes:
- Fix Android
versionCodemonotonicity for F-Droid updates, add F-Droid metadata, and replace launcher icon assets. - Expand settings abstraction (
Settingsinterface) and improve reliability around graph loading, external-change conflict handling, and benchmarking/CI workflows. - Improve search query building/ranking (AND semantics + OR fallback, BM25 scoring surfaced) and adjust indexing/write behavior.
Reviewed changes
Copilot reviewed 5 out of 29 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| version.txt | Bumps app version to 0.10.0. |
| project_plans/android-save-parse-performance/research/synthesis.md | Adds research synthesis for Android save/parse performance work. |
| project_plans/android-save-parse-performance/research/research_plan.md | Adds research plan document. |
| project_plans/android-save-parse-performance/research/findings-stack.md | Adds benchmark/tooling findings for Android measurement. |
| project_plans/android-save-parse-performance/research/findings-features.md | Adds SQLite tuning findings for Android. |
| project_plans/android-save-parse-performance/research/findings-architecture.md | Adds architecture-level bottleneck analysis and recommendations. |
| project_plans/android-save-parse-performance/requirements.md | Adds requirements doc for Android save/parse performance project. |
| kmp/src/wasmJsMain/kotlin/dev/stapler/stelekit/platform/PlatformSettings.kt | Makes WASM settings implementation conform to new Settings interface. |
| kmp/src/wasmJsMain/kotlin/dev/stapler/stelekit/cache/PlatformLock.js.kt | Adds WASM/JS PlatformLock actual (currently no-op). |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/screenshots/DesktopScreenshotTest.kt | Switches UI tests to InMemorySettings. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/screens/PageViewUITest.kt | Switches UI tests to InMemorySettings. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/fixtures/FakeRepositories.kt | Adds InMemorySettings fixture and adjusts fake FS class. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/components/TopBarTest.kt | Switches UI tests to InMemorySettings. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/StelekitViewModelLoadingTest.kt | Adds tests for loading-state cancellation/error resilience. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/RecentPagesTest.kt | Generalizes to Settings and uses InMemorySettings in tests. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/MigrationReadyLoadingTest.kt | Adds Compose tests validating try/finally migration gating pattern. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/DiskConflictResolutionTest.kt | Switches UI tests to InMemorySettings. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/ui/ComposeUITestBase.kt | Uses InMemorySettings and adds waitForViewModelReady() helper. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/search/FtsQueryBuilderTest.kt | Updates tests for AND semantics + adds OR-builder coverage. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/repository/SearchRepositoryIntegrationTests.kt | Adds integration tests for AND semantics, boosting, recency, graph distance. |
| kmp/src/jvmTest/kotlin/dev/stapler/stelekit/db/FileRegistryTest.kt | Converts tests to runTest and adds concurrency/dedup coverage. |
| kmp/src/jvmMain/kotlin/dev/stapler/stelekit/platform/PlatformSettings.kt | Makes JVM PlatformSettings implement Settings. |
| kmp/src/jvmCommonMain/kotlin/dev/stapler/stelekit/cache/PlatformLock.jvm.kt | Adds JVM PlatformLock backed by ReentrantLock. |
| kmp/src/iosMain/kotlin/dev/stapler/stelekit/platform/PlatformFileSystem.kt | Improves iOS dir/file checks and adds last-modified support. |
| kmp/src/iosMain/kotlin/dev/stapler/stelekit/performance/DeviceInfo.ios.kt | Reads iOS app version from bundle instead of hardcoding. |
| kmp/src/iosMain/kotlin/dev/stapler/stelekit/cache/PlatformLock.ios.kt | Adds iOS PlatformLock actual (currently no-op). |
| kmp/src/commonTest/kotlin/dev/stapler/stelekit/ui/state/BlockStateManagerTest.kt | Adds tests for pending disk-write tracking/cancellation behavior. |
| kmp/src/commonTest/kotlin/dev/stapler/stelekit/performance/DebounceManagerTest.kt | Adds tests for cancel(key) and hasPending(key). |
| kmp/src/commonMain/sqldelight/dev/stapler/stelekit/db/SteleDatabase.sq | Adds neighbor-page query and exposes BM25 scores in FTS queries. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/voice/VoiceSettings.kt | Accepts Settings instead of PlatformSettings. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/state/BlockStateManager.kt | Adds pending disk-write introspection + cancel API; changes unobserve behavior. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/screens/SearchViewModel.kt | Moves “Create page” item to the top of results. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/screens/PageView.kt | Enables link-picker “create page” to insert link and close dialog. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/screens/JournalsView.kt | Enables link-picker “create page” to insert link and close dialog. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/components/settings/SettingsDialog.kt | Shows app version in settings UI. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/components/TopBar.kt | Accepts Settings interface instead of PlatformSettings. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/components/PerformanceDashboard.kt | Uses PlatformDispatcher.IO; adds export directory dialog UX. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/StelekitViewModel.kt | Improves cancellation handling and external-change conflict protection. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/ui/App.kt | Adds try/finally migration gate; prefers persisted graph path from GraphManager. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/search/FtsQueryBuilder.kt | Switches multi-token query building to AND semantics and adds OR builder. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/repository/SqlDelightSearchRepository.kt | Implements AND→OR fallback, adds ranking boosts (page, graph, recency), returns ranked. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/repository/GraphRepository.kt | Extends search result models with BM25 + ranked hit types. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/platform/Settings.kt | Introduces cross-platform Settings interface. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/platform/PlatformSettings.kt | Updates expect PlatformSettings to implement Settings. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/performance/PerfExporter.kt | Adds configurable export directory + timestamp formatting update. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/performance/DebounceManager.kt | Adds cancel(key) and hasPending(key) APIs. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/db/GraphManager.kt | Generalizes constructor to accept Settings. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/db/GraphLoader.kt | Adds external-change trigger channel and per-page preemptible chunk flush changes. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/db/FileRegistry.kt | Makes detectChanges suspend + serializes concurrent calls with a Mutex. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/cache/PlatformLock.kt | Introduces expect/actual PlatformLock with withLock helper. |
| kmp/src/commonMain/kotlin/dev/stapler/stelekit/cache/LruCache.kt | Refactors LRU cache to use PlatformLock; adds SAM weigher + typealiases. |
| kmp/src/androidMain/kotlin/dev/stapler/stelekit/platform/PlatformSettings.android.kt | Makes Android PlatformSettings implement Settings. |
| kmp/src/androidMain/kotlin/dev/stapler/stelekit/platform/PlatformFileSystem.kt | Adds SAF-aware listFilesWithModTimes. |
| kmp/src/androidMain/kotlin/dev/stapler/stelekit/performance/DeviceInfo.android.kt | Reads Android app version from package info. |
| kmp/src/androidInstrumentedTest/kotlin/dev/stapler/stelekit/benchmark/AndroidGraphBenchmark.kt | Adds Android instrumented benchmark tests for load/write latency under phase 3. |
| kmp/build.gradle.kts | Adds Glance deps and configures androidInstrumentedTest dependencies/runner. |
| fdroid/metadata/dev.stapler.stelekit.yml | Adds F-Droid listing metadata (name, description, links, license). |
| benchmarks/history/2026-04-26_17h07m28s_962b647.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-25_17h16m26s_9bb8a77.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-25_04h31m50s_283753a.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-25_04h26m57s_b5a51ab.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_18h08m19s_dc1b51b.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_16h38m44s_f2d6623.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_16h35m15s_d9ff7b3.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_16h31m00s_d8346ce.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_16h23m06s_ba0aed2.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_16h06m13s_ea70c86.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_14h50m19s_8121f76.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-24_05h04m16s_17345e4.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-23_23h28m52s_6387e79.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-23_23h25m34s_59ae017.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-23_23h04m51s_921e56f.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-23_22h50m06s_ba30932.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-23_17h54m00s_73cc4e8.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-23_17h04m22s_105928d.json | Adds JVM benchmark history entry. |
| benchmarks/history/2026-04-23_04h26m15s_a9770de.json | Adds JVM benchmark history entry. |
| benchmarks/android-history/2026-04-25_21h19m20s_a1dfe06.json | Adds Android benchmark history entry. |
| benchmarks/android-history/.gitkeep | Keeps android benchmark history dir tracked. |
| androidApp/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png | Replaces launcher monochrome icon asset. |
| androidApp/src/main/res/mipmap-ldpi/ic_launcher_round.png | Replaces launcher round icon asset. |
| androidApp/src/main/res/mipmap-ldpi/ic_launcher_monochrome.png | Replaces launcher monochrome icon asset. |
| androidApp/src/main/res/mipmap-ldpi/ic_launcher.png | Replaces launcher icon asset. |
| androidApp/src/main/kotlin/dev/stapler/stelekit/SteleKitApplication.kt | Adds Application subclass to hold process-wide FS/GraphManager. |
| androidApp/src/main/kotlin/dev/stapler/stelekit/MainActivity.kt | Uses application-scoped FS and passes app GraphManager to UI. |
| androidApp/src/main/AndroidManifest.xml | Registers SteleKitApplication. |
| androidApp/build.gradle.kts | Computes versionCode from semver and sets versionName cleanly. |
| Formula/stelekit.rb | Bumps Homebrew formula version and SHA256s. |
| CHANGELOG.md | Adds changelog entries up through 0.10.0. |
| .release-please-manifest.json | Updates manifest version to 0.10.0. |
| .github/workflows/release.yml | Adds JVM test gate + adds smoke tests and cache strict-match tweaks. |
| .github/workflows/fdroid.yml | Downloads APKs from all releases before indexing for F-Droid repo. |
| .github/workflows/benchmark.yml | Adds push retry loop for committing benchmark history. |
| .github/workflows/android-benchmark.yml | Adds emulator-run Android instrumented benchmark workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Benchmark ResultsComparing
Flamegraph not available Top allocation hotspots (this PR)
|
Android Benchmark ResultsComparing
|
Move 29 completed docs/tasks/ plans to docs/archive/tasks/ based on evidence of implementation in the codebase and merged PRs. Update docs/tasks/TODO.md to mark SteleKit site, browser WASM demo, all-pages view, and recent pages as complete. Archived: android-features-integration (PR #31), android-readiness, android-ux-overhaul, block-state-management-refactor, browser-wasm-demo (PR #3), direct-sql-write-enforcement, fdroid-setup (PR #42), file-registry-refactor, hashtag-links, journal-service-extraction, migration-framework, mobile-voice-mode (PR #27), page-term-highlighting, perf-export-and-query-tracing, performance-monitoring (PR #38), progressive-loading-tasks, robust-demo-graph, search-improvements (PR #22), span-viewer-improvements, stelekit-export, stelekit-import, stelekit-performance, stelekit-site (PR #3), watcher-data-loss-fix, wiki-link-autocomplete, all-pages-view, recent-pages, TODO-root (outdated root TODO.md). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
1— F-Droid had no way to rank APKs so it always suggested the oldest release (v0.1.0). Now computed asmajor×1_000_000 + minor×1_000 + patch, coerced ≥2 so every future APK outranks the historicalversionCode=1builds.fdroid/metadata/directory — the F-Droid store showed no description or links. Addedfdroid/metadata/dev.stapler.stelekit.ymlwith name, summary, full description, license, and source/issue-tracker URLs.mipmap-*/ic_launcher_foreground.png,ic_launcher.png,ic_launcher_round.png, andic_launcher_monochrome.pngwere identical 3 KB files regardless of density (Android Studio defaults). Replaced with the SteleKit brand icon resized fromassets/brand/png/icon_512.pngat correct density-proportional dimensions; the adaptive foreground respects the 72/108dp safe zone.Test plan
versionCodeinaapt dump badgingmatches the semver formula🤖 Generated with Claude Code