diff --git a/crates/core/src/paths.rs b/crates/core/src/paths.rs index 0cdbb7f..9229361 100644 --- a/crates/core/src/paths.rs +++ b/crates/core/src/paths.rs @@ -49,10 +49,6 @@ impl AppPaths { pub fn llm_models_dir(&self) -> PathBuf { self.models_dir().join("llm") } - - pub fn migration_sentinel(&self, name: &str) -> PathBuf { - self.app_data_dir.join(format!(".{name}.sentinel")) - } } pub fn app_paths() -> AppPaths { diff --git a/docs/architecture-map/05-core-storage-hotkey-build/core-paths.md b/docs/architecture-map/05-core-storage-hotkey-build/core-paths.md index ef64649..b7407ad 100644 --- a/docs/architecture-map/05-core-storage-hotkey-build/core-paths.md +++ b/docs/architecture-map/05-core-storage-hotkey-build/core-paths.md @@ -36,7 +36,6 @@ impl AppPaths { pub fn models_dir(&self) -> PathBuf; // /models pub fn speech_model_dir(&self, id: &ModelId) -> PathBuf; // /models/ pub fn llm_models_dir(&self) -> PathBuf; // /models/llm - pub fn migration_sentinel(&self, name: &str) -> PathBuf; // /..sentinel } pub fn app_paths() -> AppPaths; @@ -56,9 +55,23 @@ The `resolve_app_data_dir` function picks the root by `cfg(target_os = ...)`: The Linux legacy-path branch keeps existing users on `~/.lumotia` (early dogfooding default) without forcing a migration when the canonical XDG location is preferred. -### Sentinel files — `crates/core/src/paths.rs:53` +### Migration idempotency — no sentinel files -`migration_sentinel(name) -> /..sentinel`. Used for one-shot data migrations outside the SQLite schema (for example, a one-off file-system reorganisation). The pattern is: write the sentinel after the migration runs successfully; check for the sentinel on next startup; skip the migration if the sentinel exists. +There is no sentinel-file pattern. The two boot-time migrations +(`migrate_legacy_data_dir` for the legacy magnotia data dir, and +`migrate_tauri_app_data_dir_with_paths` for the Tauri bundle-identifier +move) are both idempotent by construction: each cheaply re-probes for +the legacy path via `Path::exists()` on every boot and short-circuits +on the steady state. A sentinel file would optimise that probe but is +not required for correctness, and the cost is one syscall per legacy +candidate — negligible at startup. + +If a future migration needs cross-restart "this ran already" state +(e.g. a destructive one-shot reorganisation that mutates the legacy +path in place), reach for `crates/storage/src/migrations.rs` +(the SQLite schema_version table) rather than reintroducing a +filesystem sentinel — schema_version is transactional and survives +backup/restore, sentinel files don't. ## Data flow / contract @@ -77,8 +90,6 @@ The Linux legacy-path branch keeps existing users on `~/.lumotia` (early dogfood - **`std::env::var(...).unwrap_or_else(|_| "/tmp".to_string())` is the fallback for missing `HOME`.** Should never trigger in practice; defensive. - **`AppPaths::current()` reads env vars at every call.** Cheap, but repeated calls are wasteful. Slice 2 caches a `OnceLock` so the value is resolved once at startup. - **No Windows fallback for missing `LOCALAPPDATA`.** Falls through to `.` (current working directory). On a misconfigured Windows host this could write the database next to the binary. Not great; the impact is limited to first-run scenarios where the env is broken. -- **Sentinel files are hidden on Unix (`..sentinel`) but visible on Windows.** Acceptable; sentinels live alongside `lumotia.db` so the user sees both. - ## See also - [Storage file paths](storage-file-paths.md) — re-exports.