core: extract the store and sync engine into a shared crate (M12 step 1)
Desktop (Tauri) / Tauri desktop (Linux) (push) Failing after 48s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 1m50s
Desktop (Tauri) / Update manifest (push) Skipped

Android becomes a native Kotlin client over this same code (Scribe note 2730), so
the local store and sync engine stop being modules of the desktop app and become
`thoughtsync-core`, a crate with no UI framework in it at all.

This is a move, not a rewrite, and the measurement is why: every file in local/
and sync/ already carried ZERO Tauri references — 4,980 of 6,372 lines. The
coupling was 473 lines of command shim, which stays behind in the desktop crate
as src/commands/. Kept as git renames so history follows the files.

The desktop imports them under their old names (`use thoughtsync_core::{local,
sync}`) so every call site reads exactly as before. What moved is where they
live, not what they are.

Two things a workspace changes that are easy to miss, both caught before pushing:

[profile.release] now lives at the workspace ROOT. Cargo silently ignores
profiles declared by a non-root member — leaving it in the desktop crate would
have dropped lto/strip/opt-level from every release build with only a warning.

And a workspace shares ONE target dir, so the bundles moved from
desktop/src-tauri/target to target/. Thirteen references across publish-release,
debundle-graphics, verify.sh, package-prebuilt and the workflow now point there.
Pinning target-dir back would have been the smaller diff, but the Android lane
also produces Rust artifacts and they do not belong under desktop/.

Also retires the Tauri Android lane in the same push rather than leaving a path
that is being replaced: gen/android, android.yml and docs/android-dev.md are
gone, the mobile_entry_point attribute with them, and the lib drops to rlib —
staticlib/cdylib existed for Tauri mobile, and the .so Android loads will be
built from the core crate instead. Rule 22, no parallel path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-17 23:12:26 -04:00
co-authored by Claude Opus 5
parent c28f2bc00e
commit 0a7480cf9b
75 changed files with 246 additions and 1346 deletions
-117
View File
@@ -1,117 +0,0 @@
# Running the Android client locally
The Android app is the same Vue frontend and Rust core as the desktop, built by
Tauri v2 mobile. This sheet is the local setup; CI's copy of it is the
`ci-tauri-android` image (see `ci-requirements.md`).
**Open this directory in Android Studio:**
```
desktop/src-tauri/gen/android
```
Two things must be true first, and both fail confusingly if they aren't.
## 1. Gradle must run on JDK 17
Android Studio defaults to its own bundled JDK, which is currently 25. That
produces:
> Incompatible Gradle JVM version — The project's Gradle version 8.14.3 is
> incompatible with the Gradle JVM version 25 currently selected to run Gradle
> build.
Fix: `Settings → Build, Execution, Deployment → Build Tools → Gradle → Gradle
JDK`**17**. If none is listed, `Download JDK…` → version 17, vendor Eclipse
Temurin.
Prefer 17 explicitly over Android Studio's "apply a compatible JDK" quick-fix,
which will likely pick 21. 17 is what `ci-tauri-android` pins, and matching it
means a local build and a CI build resolve identically.
**Why 8.14.3 and not Gradle 9:** `cargo tauri android init` generated the wrapper
pin, and Gradle 9 removed `project.exec`, which the generated
`buildSrc/.../BuildTask.kt` still uses. The wrapper, the AGP pin and that file are
all tracked in this repo, so the bump is ours to make whenever it is worth doing —
it is scaffolding we inherited, not a requirement of Tauri. Tauri's own Android
layer targets `compileSdk 36` and registers back handling through
`OnBackPressedDispatcher`, so the library is current; only the template trails.
## 2. The Gradle glue has to be generated once
`settings.gradle` does `apply from: 'tauri.settings.gradle'`, and that file — with
`app/tauri.build.gradle.kts`, `app/tauri.properties`,
`app/src/main/assets/tauri.conf.json` and the compiled `.so` files — is generated
per build and gitignored. A fresh clone does not have them, and settings
evaluation happens before any Gradle task, so the sync fails before anything can
generate them.
From `desktop/src-tauri`:
```sh
cargo tauri android build --debug --target x86_64
```
After that, Android Studio syncs cleanly.
## Prerequisites
Gradle shells out to `cargo tauri android android-studio-script` on every build,
so the Rust toolchain is required on the machine running Android Studio — the CI
image cannot stand in for it.
```sh
rustup toolchain install 1.97.1
rustup target add aarch64-linux-android x86_64-linux-android
cargo install tauri-cli --version 2.11.4 --locked
```
Via the SDK Manager, matching `CI-tauri-android/versions.env`:
| | version | why it matters |
|---|---|---|
| Platform | API 36 | `compileSdk` / `targetSdk` |
| Build-Tools | 35.0.0 | AGP 8.11's default |
| **NDK** | 28.2.13676358 | the Rust core cannot cross-compile without it |
| Platform-Tools | latest | `adb` |
```sh
export ANDROID_HOME=$HOME/Android/Sdk
export NDK_HOME=$ANDROID_HOME/ndk/28.2.13676358
```
Node is needed too — `beforeBuildCommand` runs `npm ci && npm run build`, because
`generate_context!` compiles the frontend into the binary.
## Emulator
Create an AVD with an **x86_64** system image, API 24 or newer (`minSdk = 24`).
CI builds arm64-v8a and x86_64, so either runs, but x86_64 executes natively on a
desktop instead of being translated.
## The dev loop
Android Studio's Run button rebuilds the whole Rust core each time. For
iteration, with an emulator already running:
```sh
cd desktop/src-tauri
cargo tauri android dev
```
That starts Vite, wires `adb reverse` so the emulator reaches
`localhost:5173`, and hot-reloads frontend changes without a rebuild. Android
Studio earns its keep for the debugger, logcat and layout inspection.
## Known gaps, so they are not mistaken for bugs
- **Safe areas / viewport** are not handled yet (task 2706) — content may sit
under a notch or the gesture bar. Deliberately left until a device could
verify it.
- **`android:enableOnBackInvokedCallback`** is absent from the manifest, so
predictive-back animations will not engage on Android 13+. Tauri registers the
dispatcher correctly; only the opt-in flag is missing.
- **`android:usesCleartextTraffic`** is templated in app-wide. A
`networkSecurityConfig` resource is the better shape — see Scribe note 2437,
which hit this on Minstrel.
- Debug builds are large and slow to start. Do not read either as the product.