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
+11 -46
View File
@@ -159,49 +159,14 @@ backend/frontend push.
- No Postgres lane (unchanged): the desktop app's local store + sync behavior is
verified on the operator's machine, not in CI.
## Android (Tauri mobile) lane — `android.yml`, third image
## Android lane — REMOVED 2026-08-17
Android is a peer surface, not a desktop variant, so it gets its own workflow.
- **Image:** `git.fabledsword.com/bvandeusen/ci-tauri-android:1.97` (Rust + the
four Android ABIs + Android SDK/NDK + JDK 17 + Node + tauri-cli). Pins held in
lockstep with `ci-tauri` and `ci-tauri-win` — all three compile the same crate.
- **Why a third image.** `ci-android` says in its own header that it excludes the
NDK ("No Flutter, no NDK, no CMake") and carries no Rust; `ci-tauri` has Rust
but no SDK, plus a WebKitGTK stack Android never touches. Tauri needs the NDK
precisely because the core is Rust compiled to Android ABIs.
- **JDK 17, not the 25 `ci-android`/`ci-flutter` ship.** `tauri android init`
GENERATES the Gradle project, so its version isn't ours to choose — it produced
Gradle **8.14.3** + AGP **8.11.0** here. `CI-android/versions.env` records that
JDK 25 needs Gradle 9.1.0+ and that anything older fails with an opaque
`"25.0.3"` message. Bumping this JDK without checking what Tauri templates will
break the build with an error that names nothing useful.
- **`gen/android/` is COMMITTED.** `tauri android init` generates it, and the only
ignores are its own (build outputs, `local.properties`, `key.properties`,
`keystore.properties`). CI must not have to regenerate it, and manifest or
Gradle edits have to survive.
- **Debug APK only, arm64 only, today.** A release APK must be signed, and the
keystore has to be generated by the operator and never pass through CI logs or
an agent session — the same constraint recorded for the updater key (task 2136).
Gradle's throwaway debug keystore needs nothing from anyone, so this lane proves
the app compiles and packages. arm64 is every real device; the image carries the
other three ABIs, so widening is a one-word change.
- **Green means it BUILT.** Like the Windows lane, a Linux runner cannot execute
the artifact. Nothing here proves the app runs, renders, or is usable by finger.
- **TLS: OpenSSL is vendored on Android.** `reqwest` is pinned to `native-tls`,
which is deliberate for the Windows lane (it resolves to `schannel`, keeping
C/asm out of the cross-compile). On Android it resolves to **OpenSSL**, and the
first build duly failed with `openssl-sys`: *"Could not find directory of
OpenSSL installation"* — there is no Android OpenSSL to link against.
`Cargo.toml` now carries a `cfg(target_os = "android")` block enabling
`openssl-sys`'s `vendored` feature, which compiles OpenSSL from source with the
NDK toolchain. That is why the image ships `perl` + `make`.
- **Why not `rustls` on Android.** It builds faster and was the obvious fix, but
rustls ships its own root store — the phone would trust a DIFFERENT set of
certificates than the desktop. A self-hosted server behind a private or
enterprise CA would then work on one surface and fail on another. One TLS stack
across all surfaces is worth more than the build minutes. Revisit only if
vendored OpenSSL becomes the thing that breaks this lane repeatedly.
The Tauri-mobile Android lane is gone. Android is a native Kotlin/Compose client
over the shared `thoughtsync-core` crate instead — see Scribe note 2730 for the
decision and milestone M12 for the arc. Its CI lane and image will be rebuilt on
that basis; `ci-tauri-android` is being repurposed (drop `tauri-cli`, add
`cargo-ndk`) rather than deleted, because its NDK binutils symlinks and PATH fix
are load-bearing for any Rust-on-Android cross-compile.
## Formatting the Rust lane before pushing
@@ -212,8 +177,8 @@ a formatter is neither a test run nor a local stack:
```
docker run --rm --user "$(id -u):$(id -g)" -e CARGO_HOME=/tmp/cargo \
-v "$PWD/desktop/src-tauri:/w" -w /w \
git.fabledsword.com/bvandeusen/ci-tauri:1.97 cargo fmt --check
-v "$PWD:/w" -w /w \
git.fabledsword.com/bvandeusen/ci-tauri:1.97 cargo fmt --all --check
```
Drop `--check` to apply. `--user` keeps the container from leaving root-owned
@@ -225,7 +190,7 @@ literal — copying that shape caused one of the four failures.
## The desktop lockfile
`desktop/src-tauri/Cargo.lock` is **committed**, per Cargo's own guidance for
`Cargo.lock` is **committed** at the workspace root, per Cargo's own guidance for
binary crates. Without it every CI run re-resolved the graph, which meant a
released `.deb`/`.AppImage`/`.exe` couldn't be rebuilt from its tag, a build
could break with no repo change, and Renovate had nothing to bump (issue 2102).
@@ -244,7 +209,7 @@ above, and resolution is neither a test run nor a build:
```
docker run --rm --user "$(id -u):$(id -g)" -e CARGO_HOME=/tmp/cargo \
-v "$PWD/desktop/src-tauri:/w" -w /w \
-v "$PWD:/w" -w /w \
git.fabledsword.com/bvandeusen/ci-tauri:1.97 cargo generate-lockfile
```