Commit Graph

3 Commits

Author SHA1 Message Date
ff8dda06d0 agent: lumotia — Phase A.7 fix startup-order race that silently orphaned legacy data
Critical bug surfaced by the dogfood drill: every upgrading Magnotia user
would silently keep a fresh empty Lumotia install while their Magnotia
data sat orphaned next to it. Drill caught it on the first real run
under sandboxed HOME.

ROOT CAUSE

src-tauri/src/lib.rs::run() previously called the migrations from inside
the Tauri setup hook (post `tauri::Builder::default()`). But three
sequential actions BEFORE the setup hook had already created the
destination directories:

  1. init_tracing() -> logs_dir() -> create_dir_all(app_data_dir/logs)
     creates the lumotia/ root.
  2. install_panic_hook() -> crashes_dir() -> create_dir_all() ditto.
  3. Tauri's WebKitGTK runtime / plugin chain creates the bundle-id-keyed
     consulting.corbel.lumotia/ dir eagerly when the WebContext spins up
     (mediakeys, storage, WebKitCache subdirs appeared even without our
     hook explicitly creating them).

By the time the setup-hook migrations fired, every legacy candidate
returned `TargetAlreadyExists` (paths.rs) or `BothExistLegacyPreserved`
(tauri_app_data_migration.rs) — both silent no-op codepaths. Legacy
data was left untouched, fresh Lumotia install gained no transcripts,
settings, or window state.

FIX

Migrate BEFORE any other code touches app_data_dir().

src-tauri/src/tauri_app_data_migration.rs:
  - NEW_BUNDLE_ID const ("consulting.corbel.lumotia"). MUST agree with
    tauri.conf.json#identifier; reviewer-enforced invariant.
  - Renamed private `legacy_tauri_app_data_dir_for` -> public
    `tauri_app_data_dir_for(identifier)`. Function is parameterised by
    bundle id; the "legacy" name was misleading after this change.
  - New `current_tauri_app_data_dir()` resolves the NEW bundle path
    from platform env vars (same convention Tauri 2 uses), so the
    pre-runtime migration can address its destination without
    needing an AppHandle.

src-tauri/src/lib.rs:
  - New `migrate_user_data_pre_runtime()` orchestrates the two
    migrations + ambiguity guard. Uses `eprintln!` for surface events
    (tracing not yet initialised at this stage; stderr lands in
    journald / foreground terminal which is the right transport for
    boot-phase output). FATAL errors call process::exit(1) — the
    setup-hook version returned Err from the closure, equivalent
    effect.
  - run() now calls migrate_user_data_pre_runtime() as its first line,
    BEFORE init_tracing(), install_panic_hook(), and the Tauri
    builder.
  - Setup-hook migration blocks deleted (~90 lines). Setup hook now
    starts with a one-line comment pointing at the pre-runtime fn.

VERIFICATION

Re-ran the dogfood drill (scripts/dogfood-rebrand-drill.sh) — 8/8 probes
pass after the fix (was 4/8). Both stderr lines fire:

  [lumotia-startup] migrated legacy magnotia data dir to lumotia:
      .../magnotia -> .../lumotia (renamed_db=true, elapsed_ms=0)
  [lumotia-startup] migrated Tauri app_data_dir from legacy bundle
      identifier: .../uk.co.corbel.magnotia ->
      .../consulting.corbel.lumotia (elapsed_ms=0)

On-disk post-state confirms: magnotia/ gone, lumotia/ has migrated db
+ recordings, uk.co.corbel.magnotia/ preserved as backup,
consulting.corbel.lumotia/localStorage/leveldb/ has migrated data.

- cargo fmt --check: clean
- cargo clippy --workspace --all-targets -- -D warnings: clean
- cargo test --workspace: 409/0 (no regression)
2026-05-14 13:59:08 +01:00
27661c816e agent: lumotia — pin rust toolchain + workspace clippy/fmt sweep
rust-toolchain.toml pins to stable 1.94.1 so contributors and CI runners
share the exact rustc / rustfmt / clippy versions. Without the pin, every
machine surfaces a different lint set depending on its local install — six
pre-existing lints showed up on 1.94.1 that 1.93-era HANDOVER reported clean.

Clippy fixes (all pre-existing, not introduced by feature work):

- crates/storage/src/database.rs: std::iter::repeat().take() -> repeat_n()
- crates/llm/src/lib.rs (docs): "+ frontends" was parsed as a markdown bullet
  continuation by rustdoc, breaking doc-lazy-continuation. Reworded to "and".
- crates/llm/src/lib.rs (loop): while-let-on-iterator -> for-loop.
- src-tauri/src/commands/security.rs: .iter().any(|a| *a == x) -> .contains(&x).
- src-tauri/src/lib.rs: io::Error::new(Other, e) -> io::Error::other(e).
- src-tauri/src/tauri_app_data_migration.rs: drop function-tail `return`s
  inside cfg blocks; each platform's block now ends with a tail expression.

cargo fmt sweep across the workspace. Mechanical layout-only changes;
no semantics affected.

Workspace gates after this commit:
- cargo fmt --check: clean
- cargo clippy --workspace --all-targets -- -D warnings: clean
- cargo test --workspace: 405/0 (will become 409/0 with Phase A.1+A.2)
2026-05-14 07:19:59 +01:00
3f4e5cc9a4 agent: code-atomiser-fix — migrate Tauri app_data_dir on bundle-identifier change
Commit 14313cf changed the Tauri bundle identifier from
uk.co.corbel.magnotia to consulting.corbel.lumotia. Tauri 2 keys both
app_data_dir and the webview's data store (localStorage, IndexedDB,
cookies, service worker, cache) plus all Tauri-plugin state files
(window-state geometry, autostart enable flag) by the identifier.
Without an explicit migration, every user's webview state was
silently orphaned on first launch under the new identifier; the
JS-side migrateLocalStorageKey helper introduced in 1608109 ran
against an empty store and no-op'd.

Fix: in the Tauri setup hook, before webview navigation, resolve the
legacy app_data_dir under the OLD bundle identifier and recursively
copy it to the new identifier's app_data_dir via an atomic staging
rename. Idempotent: legacy preserved as a backup; if both paths
exist post-rename, a warning is logged and the new path is used
unchanged.

Regression tests cover the migrate-only, both-exist, no-legacy, and
idempotent (run twice) cases against synthetic legacy/new paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:35:42 +01:00