Commit Graph
15 Commits
Author SHA1 Message Date
bvandeusenandClaude Opus 5 0a7480cf9b 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>
2026-08-17 23:12:26 -04:00
bvandeusenandClaude Opus 5 e8d6a4f423 android: vendor OpenSSL so the Rust core links (task 1864)
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m53s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m41s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android (Tauri) / Android APK (debug) (push) Successful in 3m40s
First Android build failed at openssl-sys: "Could not find directory of OpenSSL
installation". reqwest is pinned to native-tls, which is right for Windows — it
resolves to schannel there and keeps C and assembly out of the cross-compile —
but on Android it resolves to OpenSSL, and there is no Android OpenSSL in the
image to link against.

Vendored rather than rustls. rustls builds faster and was the obvious fix, but it
ships its own root store, so the phone would trust a different set of
certificates than the desktop: a self-hosted server behind a private or
enterprise CA would work on one surface and fail on another. Peer surfaces that
quietly disagree about who to trust is a worse outcome than a slower build, so
one TLS stack stays everywhere and OpenSSL gets compiled from source with the NDK
toolchain — which is what perl and make are in ci-tauri-android for.

Scoped to cfg(target_os = "android") so nothing changes for the Linux, Windows or
web lanes; declared as a direct dependency purely to flip the feature, since
cargo's unification then applies it to the copy native-tls pulls in.

Cargo.lock regenerated in the same commit, per the documented procedure — the
--locked gates in every lane fail otherwise. openssl-src 300.6.1+3.6.3 joins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 23:11:24 -04:00
bvandeusenandClaude Opus 5 1f140c7457 android: scaffold the Tauri mobile lane and build a debug APK in CI
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m38s
Android (Tauri) / Android APK (debug) (push) Failing after 21s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m26s
Desktop (Tauri) / Update manifest (push) Successful in 5s
The phone client is Tauri v2 mobile (operator decision), so it reuses the Vue
frontend and the Rust store and sync engine that already exist rather than
becoming a third implementation to keep in step by hand.

gen/android is committed. tauri android init generated it, its own .gitignore
already excludes the build outputs and every keystore file, and CI must not have
to regenerate a project that manifest edits will accumulate in.

What the scaffold confirms is that the image's JDK pin was load-bearing rather
than incidental: Tauri templated Gradle 8.14.3 with AGP 8.11.0, and CI-android's
versions.env records that JDK 25 needs Gradle 9.1.0+ and that anything older
fails with an opaque "25.0.3" message. Picking 17 for ci-tauri-android avoided
exactly that. namespace and applicationId came out as com.fabledsword.thoughtsync,
matching the desktop identifier, so the app-data story stays consistent.

The lane builds a DEBUG APK for arm64 only. Release APKs need signing, and the
keystore has to be generated by the operator and never pass through CI logs or an
agent session — the constraint recorded for the updater key applies unchanged.
Gradle's throwaway debug keystore needs nothing from anyone, so this can prove the
app compiles and packages today and grow a signed job when a key exists. arm64 is
every real device; the image carries the other three ABIs, so widening is a word.

Triggered by frontend/** as well as desktop/**, because generate_context! compiles
the frontend into the app — the same reasoning that widened desktop.yml. Android,
desktop and web are peers on one quality bar, and a frontend commit that skipped
this lane would ship a stale phone build.

Green here will mean it BUILT. A Linux runner cannot execute an APK, so nothing in
this lane proves the app runs, renders, or is usable by finger.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 23:01:32 -04:00
bvandeusenandClaude Opus 5 5c1ae574f6 desktop: commit Cargo.lock and gate CI on it (issue 2102)
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m52s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m37s
Desktop (Tauri) / Update manifest (push) Successful in 5s
The desktop crate is a binary, and binaries commit their lockfile. Without one
every run re-resolved the graph: a tagged .deb/.AppImage/.exe couldn't be
rebuilt from its tag, any semver-compatible upstream release landed
automatically on the next build — the failure mode hardest to read, because the
commit that broke it changed nothing relevant — and Renovate had no lockfile to
bump, leaving Rust dependency movement invisible to the Dashboard.

Generated with cargo generate-lockfile inside ci-tauri:1.97, the same image CI
builds in, so the format and the picked versions are what CI would have chosen
itself. That takes the artifact-upload round-trip the issue proposed off the
table: ci-requirements.md already blesses the image for cargo fmt, and resolving
a dependency graph is no more a build than formatting is. 503 packages.

Enforcement goes on each job's FIRST cargo invocation rather than the bundle
build: cargo clippy --locked on Linux, and its own cargo fetch --locked step on
Windows, whose only crate-graph command is otherwise the cross-compile itself.
Drift fails in the first thirty seconds instead of thirty minutes in, and
everything after the gate in that job compiles the recorded versions anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 10:41:18 -04:00
bvandeusen c1464228df docs: Fabled-Git, not Forgejo, where the instance is meant
Four references to "Forgejo" actually meant this instance, which has run Gitea
since the migration: the registry push, the missing /releases/latest/download
route, the API a packaging script resolves URLs against, and the 422 on an
illegal JSON escape.

Kept as-is — these are genuinely about the upstream Forgejo project, not us:
the `forgejo/upload-artifact` mirror and "the Forgejo project's fork".

Prose only — no workflow, path, or script change. Scribe issue #2272.
2026-07-31 23:44:29 -04:00
bvandeusenandClaude Opus 5 505904b1e5 ci: swap artifact upload to the mirrored action (issue 2270)
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m42s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m32s
Desktop (Tauri) / Update manifest (push) Successful in 3s
Both desktop upload steps used actions/upload-artifact@v3, which reports
success while Gitea stores the result in a format its v4-only artifact API
will never serve back — 110 artifacts on this repo are on disk, have valid
DB rows, and are invisible to the REST API, the web download route and the
MCP tools alike. Green jobs producing nothing retrievable.

Point both at bvandeusen/upload-artifact (pull mirror of the Forgejo
project's fork, GHES refusal disabled), pinned by SHA because the mirror
auto-syncs. Not actions/upload-artifact@v4: its isGhes() throws on the
hostname before opening a connection, so no server-side change reaches it.

Also drop continue-on-error and set if-no-files-found: error on both steps.
Between them, a failed or empty upload was reported as a green run — the
same silence that let this go unnoticed for a month.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:27:57 -04:00
bvandeusenandClaude Opus 5 13e48672c0 packaging: bare backticks — a heredoc's backslash isn't the JSON's
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m5s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 5m16s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Run 2981 built everything and then died posting the release: HTTP 422,
"invalid escape sequence \`". The body's other backticks are written \` because
they sit in an UNQUOTED heredoc, where that backslash is the shell's and is gone
before any JSON exists. Copying the idiom into a single-quoted variable changed
what it meant — single quotes already stop substitution, so the backslash
survived into the body as an escape JSON has no rule for.

bash -n passes either way; it checks syntax, not what a string becomes. So parse
the assembled body for every branch it can take instead, and write down the
recipe next to the one for formatting Rust.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKsUY9Z45KQd34V956hZ9Q
2026-07-27 23:10:42 -04:00
bvandeusenandClaude Opus 5 8b6dfab3a7 ci-requirements: the two things that cost a cycle each to rediscover
`git push origin dev` fails outright now that the rolling channel put a TAG
named `dev` beside the branch, and the error names neither. And nothing in CI
lints the packaging shell scripts, so a broken installer surfaces when a user
runs it rather than when it's built — record how to check them locally.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKsUY9Z45KQd34V956hZ9Q
2026-07-27 23:04:41 -04:00
bvandeusenandClaude Opus 5 1f294c4ad8 ci-requirements: record how to format the Rust lane without a local toolchain
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
2026-07-26 19:27:39 -04:00
bvandeusenandClaude Opus 5 9118680bb1 docs: record the CI consequences of the M10.6 TLS dependency
ci-requirements.md is the contract with CI-Runner (rule 39), so the two
things a future image change could silently break belong in it:

libssl-dev + pkg-config in ci-tauri are now load-bearing — native-tls
compiles against OpenSSL on Linux, so a slim-down of that image would fail
the Rust build at openssl-sys rather than anywhere obvious.

The TLS backend choice is a property of the WINDOWS lane, not a dependency
detail: native-tls resolves to schannel on windows-msvc, keeping C/assembly
out of the cross-compile. Swapping to rustls would pull in ring/aws-lc-rs
and their assembler — the same class of dependency that broke that lane
before. Flagged so it's treated as a lane change, not a version bump.

Also documented why libssl3 is left covered TRANSITIVELY rather than
declared. dpkg-shlibdeps now lists it, and verify.sh passes it through
webkit's recursive closure. Declaring it directly would be worse, not
better: the package name is release-dependent (libssl3 on bookworm,
libssl3t64 after the time_t transition), so hardcoding it freezes the .deb
to the build distro, whereas webkit's closure adapts. verify.sh fails loudly
if webkit ever stops pulling OpenSSL, which is what makes that safe.

Docs only — triggers no workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
2026-07-25 22:53:31 -04:00
bvandeusenandClaude Opus 5 ab961f13ce desktop: cross-compiled Windows NSIS installer lane (task 2015)
Desktop (Tauri) / Windows installer (cross-compiled) (push) Failing after 48s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m40s
Adds a `windows` job to desktop.yml on the new ci-tauri-win image, producing a
Windows -setup.exe without any Windows hardware. A Windows container can't run
on a Linux host, so cross-compilation is the only route: --runner cargo-xwin
supplies the MSVC CRT/SDK (pre-warmed into the image) and links with lld-link,
and makensis builds the installer.

NSIS only. .msi needs WiX v3, a Windows program — per Tauri, ".msi installers
can only be created on Windows". It comes back if a Windows node ever exists.

Kept as a separate job so a Windows-side failure can never block the Linux
artifacts, which are the primary product today. publish-release.sh now globs
the windows target root too; nullglob means each job uploads only what its own
workspace contains, and the release is created once and reused via the 409
path, so both jobs can publish to the same release safely.

No app code changes were needed. The AppImage self-integration UI already
gates on is_appimage (AccountView.vue:131, DesktopIntegrationPrompt.vue:22),
and $APPIMAGE is never set on Windows, so the OOBE prompt and Settings toggle
hide themselves.

Recorded plainly in ci-requirements.md that this is the weakest-verified lane
we have: Tauri calls Linux->Windows cross-compilation "not tested as much" and
a last resort, and a Linux runner cannot execute a Windows binary. Green means
it built. A real Windows machine check is mandatory before trusting a release,
and installers are unsigned until a certificate exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
2026-07-25 20:14:55 -04:00
bvandeusenandClaude Opus 5 85ca7c2a2d desktop: drop the redundant deb depends, correct the pacman docs
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 7s
CI & Build / Python tests (push) Successful in 8s
CI & Build / Build & push image (push) Successful in 17s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m28s
Follow-up to 8a8b2b1, driven by what run 2872's new checks actually printed.

The .deb verification did its job on its first run: tauri already infers
exactly libwebkit2gtk-4.1-0 + libgtk-3-0, so declaring the same two in
tauri.conf.json produced a control file listing each of them twice. Removed
the declaration — verify.sh is the real guard, and it fails the build if
inference ever stops covering what the binary links.

The pacman step revealed ci-tauri carries neither zstd nor bsdtar, so packages
currently ship as .pkg.tar.xz with no .MTREE. Both are working outcomes
(pacman reads xz; only `pacman -Qkk` needs .MTREE), but the docs promised
.zst, so the README, the release notes and ci-requirements.md now describe
what the build actually produces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
2026-07-25 18:26:24 -04:00
bvandeusenandClaude Opus 5 8a8b2b17e6 desktop: prebuilt pacman package + verified .deb (tasks 2022, 2074)
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m41s
Native packages the installer can actually fetch, before task 2014 wires up
the fetching.

Arch (task 2022, re-scoped): the source PKGBUILD is gone — asking every user
to install rust+node and compile for minutes isn't distribution. Replaced by
desktop/packaging/arch/package-prebuilt.sh, which wraps the binary the Linux
job already built into a .pkg.tar.zst. No second Rust build, no Arch CI image:
the binary bundles nothing and resolves webkit/gtk/soup by soname, identical
on both distros, with SQLite compiled in and glibc used in the safe
built-old/run-new direction. CI is Debian and has no pacman, so the step logs
.PKGINFO plus the full file listing for audit instead of pretending to verify.

Debian (task 2074): install.sh hands the .deb to every Debian/Ubuntu user and
nothing had ever inspected it. tauri.conf.json now declares
libwebkit2gtk-4.1-0 + libgtk-3-0 explicitly rather than trusting inference —
and deliberately declares no appindicator or sqlite dep, since tauri is built
with features=[] and rusqlite is "bundled". desktop/packaging/deb/verify.sh
prints the generated control file, cross-checks it against what the ELF
actually needs via dpkg-shlibdeps, confirms every declared dep exists in apt,
and clean-container installs when a docker CLI is available.

Both artifacts join the run artifact and the tagged release; install.sh grows
a pacman branch so Arch/CachyOS gets a native install instead of the AppImage
fallback. Still no release cut (rule 2).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
2026-07-25 18:19:30 -04:00
bvandeusenandClaude Opus 4.8 1607c77ee5 M10.8: desktop CI lane (.forgejo/workflows/desktop.yml) + app-icon source
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m19s
Separate workflow (not ci.yml) so the ~20-40min Rust/AppImage build only runs on
desktop/** changes, not every backend/frontend push. Builds on the ci-tauri:1.97
image: frontend build -> `cargo tauri icon app-icon.png` (from a committed 1024px
source PNG, sidestepping SVG-input questions) -> cargo fmt --check -> clippy -D
warnings -> cargo test -> `cargo tauri build` (deb + AppImage).
APPIMAGE_EXTRACT_AND_RUN=1 for FUSE-less CI containers. Verifies the M10.2 scaffold
end-to-end and is the foundation for the integrated-AppImage work (task 2013).
Also updates ci-requirements.md with the desktop lane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
2026-07-24 12:26:24 -04:00
bvandeusenandClaude Opus 4.8 af8aa25464 M0: Docker + Fabled-Git CI (typecheck + lint + test + build)
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 5s
CI & Build / Python tests (push) Successful in 7s
CI & Build / Build & push image (push) Failing after 15s
- 2-stage Dockerfile (node:22 build Vue -> python:3.12-slim runtime); CMD runs
  `alembic upgrade head` then hypercorn (rule 82).
- .forgejo/workflows/ci.yml on ci-python:3.14: typecheck (vue-tsc) + lint
  (ruff check src/) + test (uv venv + pytest, DB-free) + gated buildx push with
  the rule-46 tag scheme (:dev/:latest/:<sha>). No local integration lane yet.
- ci-requirements.md (rule 39); docker-compose for a local app+Postgres stack.
- Real README with layout, dev instructions, and the milestone arc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
2026-07-19 13:17:38 -04:00