refactor(ui): extract GitSyncCoordinator from StelekitViewModel - #379
Conversation
Moves the git sync status machinery (syncState/gitLastSyncAt derived StateFlows) and the git setup/conflict/journal-merge dialog surfaces into a new GitSyncCoordinator collaborator, following the SectionManagementCoordinator (PR #375) and LlmSuggestionCoordinator (PR #378) pattern. StelekitViewModel keeps one-line forwarders for every public method and the two public StateFlow properties that Compose call sites and dedicated tests read directly. Phase 3 of project_plans/stelekit-viewmodel-decomposition/plan.md.
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The extraction preserves existing behavior and lifecycle ownership while retaining all public entry points.
Review effort: Balanced
Findings: None
What changed in this PR
Extracts Git sync state and dialog coordination from StelekitViewModel into a focused collaborator while preserving its public API.
Changes:
- Adds
GitSyncCoordinatorfor sync flows, actions, and dialog state. - Replaces ViewModel implementations with forwarding methods and properties.
| File | Description |
|---|---|
StelekitViewModel.kt |
Delegates Git sync responsibilities to the coordinator. |
GitSyncCoordinator.kt |
Contains the extracted Git sync behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
JVM Load Benchmark (Desktop)Synthetic in-memory benchmark measuring load performance for the desktop (JVM) app.
Flamegraphs (this PR)**Allocation** — object allocation pressure (JDBC/SQLite churn)Alloc flamegraph not available CPU — method-level hotspots by on-CPU time CPU flamegraph not available Top allocation hotspots (this PR)`36.5%` byte[]_[k] `7.7%` java.lang.String_[k] `7.2%` java.util.LinkedHashMap$Entry_[k] `6.3%` int[]_[k] `3.4%` java.lang.Object[]_[k]Top CPU hotspots (this PR)`96.3%` /usr/lib/x86_64-linux-gnu/libc.so.6 `1.4%` /tmp/sqlite-3.51.3.0-d4b50008-d0fa-44ff-a32c-a1f86b1231bc-libsqlitejdbc.so `0.5%` __libc_pwrite `0.2%` fsync `0.2%` pthread_cond_signal |
Android Load BenchmarkInstrumented benchmark on an API 30 x86_64 emulator — 500-page synthetic graph. Comparing Graph Load
Interactive Write Latency (during Phase 3)
SAF I/O Overhead (ContentProvider vs direct File read)Measures Binder IPC cost added by ContentResolver per readFile() call.
|
… from breaking CI (#381) ## Why `downloads.sourceforge.net` had intermittent Cloudflare 522 ("origin unreachable") outages on 2026-10-01 that: - Failed the Bazel Android build/test jobs on PRs #370, #373, #378, #379 - Caused `v0.89.0`'s release safety gate to exhaust its 3 retries, skipping every downstream build/publish job — the release shipped with **zero assets** Root cause: `MODULE.bazel`'s `unzip_src`/`zip_src` `http_archive` rules had a single hardcoded SourceForge URL each, with no fallback. ## Fix Both tarballs (InfoZip `unzip60.tar.gz` / `zip30.tar.gz`) are now hosted as assets on a dedicated, non-app [GitHub release](https://github.com/tstapler/stelekit/releases/tag/ci-vendored-deps-infozip-v1) in this repo — verified `sha256`-identical to the hashes already pinned in `MODULE.bazel` (confirmed against the original SourceForge files before the outage, and cross-checked against MacPorts/OSUOSL mirrors). `MODULE.bazel`'s `urls` lists now try, in order: our own GitHub release → MacPorts/OSUOSL mirrors → SourceForge (demoted to last, kept only as a final fallback). `http_archive` tries each URL until one succeeds, so this requires no other code changes. ## Verification - `bazel build --repository_cache=<empty> @unzip_src//:unzip @zip_src//:zip` after deleting the previously-extracted external repos — forces a true cold fetch, confirms the new URL list resolves and the sha256 check passes. - `bazel build //:stelekit_android_toolchain_impl` — the consumer of these two targets — builds clean.
Summary
Phase 3 of the StelekitViewModel decomposition plan — extracts the git-sync status machinery and the git setup/conflict/journal-merge dialog group into a new
GitSyncCoordinatorcollaborator, following theSectionManagementCoordinator(#375) andLlmSuggestionCoordinator(#378) pattern.GitSyncCoordinatorowns thesyncState/gitLastSyncAtderivedStateFlows (built from the activeGitSyncService) and the 16 moved functions (triggerSync,triggerFetchOnly,setGitConfig, git setup wizard open/dismiss variants, conflict-resolution/journal-merge-review dialog methods, git-detection/browser-only-sync banner dismissal).StelekitViewModelkeeps one-line forwarders for every public method plus the two publicStateFlowproperties (syncState,gitLastSyncAt), both read directly by Compose call sites (GraphContentActiveShell,GraphDialogLayer,GraphContentLeftSidebar) and by dedicated tests — verified by grep before removal.Verification
./gradlew :kmp:compileKotlinJvm— BUILD SUCCESSFUL./gradlew :kmp:jvmTest --tests StelekitViewModelSyncStateTest --tests StelekitViewModelSyncStateIntegrationTest— all 4 tests PASSEDscripts/jvm-display-check.sh -- ./gradlew :kmp:jvmTest(full suite) — 5072 tests completed, 5 failed, 39 skipped; all 5 failures are the known pre-existing flakes (JournalViewFanoutBenchmarkTest,CapturePopupWindowTest,CapturePopupWindowUxTest,BlockItemGestureTest.shiftClick_extendsSelection_notEditMode), unrelated to this change