agent: lumotia-rebrand — data dir migration shim + paths.rs rename
Phase 5 of the rebrand cascade per locked decision D1 (migrate in place).
crates/core/src/paths.rs:
- Hardcoded subdir strings renamed: magnotia/Magnotia -> lumotia/Lumotia
across all four OS branches (Linux XDG + dot-legacy, macOS Application
Support, Windows LOCALAPPDATA, fallback dot-dir).
- Database filename: magnotia.db -> lumotia.db.
- Test path fixtures renamed: /tmp/magnotia-test -> /tmp/lumotia-test.
- New MigrationStatus enum (Migrated / TargetAlreadyExists / NoLegacyFound).
- New migrate_legacy_data_dir() that probes the platform-correct legacy
magnotia path, renames it to the lumotia equivalent via fs::rename, and
also renames magnotia.db -> lumotia.db inside if found. Idempotent: safe
to call on every boot. Refuses to overwrite an existing lumotia dir to
protect user data.
- Four new unit tests covering all branches via the test-friendly
migrate_legacy_data_dir_inner that takes an explicit legacy path.
crates/storage/src/database.rs:
- New migrate_legacy_setting_keys(pool) that renames any settings rows
with key matching magnotia_* to lumotia_*. Single SQL UPDATE with
NOT EXISTS guard so it leaves rows alone if a lumotia_ row already
exists. Re-exported from lib.rs.
src-tauri/src/lib.rs:
- Calls migrate_legacy_data_dir(&app_data_dir()) at the start of setup()
BEFORE database_path() resolves the now-renamed dir. Logs migration
outcome to lumotia_startup tracing target.
- Calls migrate_legacy_setting_keys(&db) immediately after init_db
returns. Logs only when rows are actually renamed.
src-tauri/src/{lib,commands/{diagnostics,rituals,transcripts}}.rs +
crates/storage/src/database.rs:
- All in-code references to magnotia_preferences, magnotia_history (in
comments), and magnotia_morning_triage_last_shown renamed to lumotia_*.
cargo build --workspace passes. cargo test --workspace: 334 pass, 0 fail
(up from 330; +4 paths::tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::types::ModelId;
|
use crate::types::ModelId;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ impl AppPaths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn database_path(&self) -> PathBuf {
|
pub fn database_path(&self) -> PathBuf {
|
||||||
self.app_data_dir.join("magnotia.db")
|
self.app_data_dir.join("lumotia.db")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recordings_dir(&self) -> PathBuf {
|
pub fn recordings_dir(&self) -> PathBuf {
|
||||||
@@ -67,7 +67,7 @@ fn resolve_app_data_dir() -> PathBuf {
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
let local_app_data = std::env::var("LOCALAPPDATA").unwrap_or_else(|_| ".".to_string());
|
let local_app_data = std::env::var("LOCALAPPDATA").unwrap_or_else(|_| ".".to_string());
|
||||||
return PathBuf::from(local_app_data).join("magnotia");
|
return PathBuf::from(local_app_data).join("lumotia");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@@ -76,56 +76,293 @@ fn resolve_app_data_dir() -> PathBuf {
|
|||||||
return PathBuf::from(home)
|
return PathBuf::from(home)
|
||||||
.join("Library")
|
.join("Library")
|
||||||
.join("Application Support")
|
.join("Application Support")
|
||||||
.join("Magnotia");
|
.join("Lumotia");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||||
let legacy = PathBuf::from(&home).join(".magnotia");
|
let legacy_dot = PathBuf::from(&home).join(".lumotia");
|
||||||
if legacy.exists() {
|
if legacy_dot.exists() {
|
||||||
return legacy;
|
return legacy_dot;
|
||||||
}
|
}
|
||||||
if let Ok(xdg) = std::env::var("XDG_DATA_HOME") {
|
if let Ok(xdg) = std::env::var("XDG_DATA_HOME") {
|
||||||
if !xdg.is_empty() {
|
if !xdg.is_empty() {
|
||||||
return PathBuf::from(xdg).join("magnotia");
|
return PathBuf::from(xdg).join("lumotia");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PathBuf::from(home)
|
PathBuf::from(home)
|
||||||
.join(".local")
|
.join(".local")
|
||||||
.join("share")
|
.join("share")
|
||||||
.join("magnotia")
|
.join("lumotia")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
|
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
|
||||||
{
|
{
|
||||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||||
PathBuf::from(home).join(".magnotia")
|
PathBuf::from(home).join(".lumotia")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Outcome of attempting to migrate an existing magnotia-era data directory
|
||||||
|
/// to its lumotia equivalent on first launch after the rebrand.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum MigrationStatus {
|
||||||
|
/// Renamed legacy dir to the new path. Includes optional db-file rename
|
||||||
|
/// if `magnotia.db` was found inside.
|
||||||
|
Migrated {
|
||||||
|
from: PathBuf,
|
||||||
|
to: PathBuf,
|
||||||
|
renamed_db: bool,
|
||||||
|
},
|
||||||
|
/// New path already exists. Did not touch legacy (if any) to avoid
|
||||||
|
/// destroying user data on the new path.
|
||||||
|
TargetAlreadyExists { target: PathBuf },
|
||||||
|
/// No legacy data dir on disk. Nothing to do.
|
||||||
|
NoLegacyFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Probe the legacy magnotia data dir paths on the current platform and
|
||||||
|
/// return the first one that exists, if any. Mirrors the search order
|
||||||
|
/// inside `resolve_app_data_dir()` but for the old name.
|
||||||
|
fn legacy_magnotia_data_dir() -> Option<PathBuf> {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
let local_app_data = std::env::var("LOCALAPPDATA").ok()?;
|
||||||
|
let candidate = PathBuf::from(local_app_data).join("magnotia");
|
||||||
|
return candidate.exists().then_some(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
let home = std::env::var("HOME").ok()?;
|
||||||
|
let candidate = PathBuf::from(home)
|
||||||
|
.join("Library")
|
||||||
|
.join("Application Support")
|
||||||
|
.join("Magnotia");
|
||||||
|
return candidate.exists().then_some(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let home = std::env::var("HOME").ok()?;
|
||||||
|
let dot_legacy = PathBuf::from(&home).join(".magnotia");
|
||||||
|
if dot_legacy.exists() {
|
||||||
|
return Some(dot_legacy);
|
||||||
|
}
|
||||||
|
if let Ok(xdg) = std::env::var("XDG_DATA_HOME") {
|
||||||
|
if !xdg.is_empty() {
|
||||||
|
let xdg_legacy = PathBuf::from(xdg).join("magnotia");
|
||||||
|
if xdg_legacy.exists() {
|
||||||
|
return Some(xdg_legacy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let xdg_default = PathBuf::from(home)
|
||||||
|
.join(".local")
|
||||||
|
.join("share")
|
||||||
|
.join("magnotia");
|
||||||
|
xdg_default.exists().then_some(xdg_default)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
|
||||||
|
{
|
||||||
|
let home = std::env::var("HOME").ok()?;
|
||||||
|
let candidate = PathBuf::from(home).join(".magnotia");
|
||||||
|
candidate.exists().then_some(candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Migrate a legacy magnotia data directory to the new lumotia path on
|
||||||
|
/// first launch. Idempotent: safe to call on every boot.
|
||||||
|
///
|
||||||
|
/// Rules:
|
||||||
|
/// * If the new path already exists, do nothing and return
|
||||||
|
/// `TargetAlreadyExists`. We do not destroy lumotia data, even if a
|
||||||
|
/// stale legacy dir is also present.
|
||||||
|
/// * If only the legacy path exists, rename it to the new path and
|
||||||
|
/// rename `magnotia.db` -> `lumotia.db` inside it if found.
|
||||||
|
/// * If neither exists, return `NoLegacyFound` — the first launch on a
|
||||||
|
/// clean system will create the new path itself.
|
||||||
|
pub fn migrate_legacy_data_dir(new_path: &Path) -> Result<MigrationStatus, std::io::Error> {
|
||||||
|
migrate_legacy_data_dir_inner(new_path, legacy_magnotia_data_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test-friendly inner shape: takes the legacy path explicitly so tests
|
||||||
|
/// don't depend on platform-specific HOME / LOCALAPPDATA env vars.
|
||||||
|
fn migrate_legacy_data_dir_inner(
|
||||||
|
new_path: &Path,
|
||||||
|
legacy: Option<PathBuf>,
|
||||||
|
) -> Result<MigrationStatus, std::io::Error> {
|
||||||
|
if new_path.exists() {
|
||||||
|
return Ok(MigrationStatus::TargetAlreadyExists {
|
||||||
|
target: new_path.to_path_buf(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(from) = legacy else {
|
||||||
|
return Ok(MigrationStatus::NoLegacyFound);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(parent) = new_path.parent() {
|
||||||
|
std::fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
std::fs::rename(&from, new_path)?;
|
||||||
|
|
||||||
|
let renamed_db = rename_db_file_if_present(new_path)?;
|
||||||
|
|
||||||
|
Ok(MigrationStatus::Migrated {
|
||||||
|
from,
|
||||||
|
to: new_path.to_path_buf(),
|
||||||
|
renamed_db,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rename_db_file_if_present(dir: &Path) -> Result<bool, std::io::Error> {
|
||||||
|
let legacy_db = dir.join("magnotia.db");
|
||||||
|
if !legacy_db.exists() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
let new_db = dir.join("lumotia.db");
|
||||||
|
if new_db.exists() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
std::fs::rename(&legacy_db, &new_db)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::AppPaths;
|
use super::*;
|
||||||
use crate::types::ModelId;
|
use crate::types::ModelId;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn derives_all_paths_from_one_base() {
|
fn derives_all_paths_from_one_base() {
|
||||||
let paths = AppPaths {
|
let paths = AppPaths {
|
||||||
app_data_dir: PathBuf::from("/tmp/magnotia-test"),
|
app_data_dir: PathBuf::from("/tmp/lumotia-test"),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
paths.database_path(),
|
paths.database_path(),
|
||||||
PathBuf::from("/tmp/magnotia-test/magnotia.db")
|
PathBuf::from("/tmp/lumotia-test/lumotia.db")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
paths.speech_model_dir(&ModelId::new("whisper-base-en")),
|
paths.speech_model_dir(&ModelId::new("whisper-base-en")),
|
||||||
PathBuf::from("/tmp/magnotia-test/models/whisper-base-en")
|
PathBuf::from("/tmp/lumotia-test/models/whisper-base-en")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
paths.llm_models_dir(),
|
paths.llm_models_dir(),
|
||||||
PathBuf::from("/tmp/magnotia-test/models/llm")
|
PathBuf::from("/tmp/lumotia-test/models/llm")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unique_tmp(base: &str) -> PathBuf {
|
||||||
|
let pid = std::process::id();
|
||||||
|
let nanos = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.map(|d| d.as_nanos())
|
||||||
|
.unwrap_or(0);
|
||||||
|
std::env::temp_dir().join(format!("lumotia-paths-test-{base}-{pid}-{nanos}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn migrate_with_legacy_present_renames_dir_and_db() {
|
||||||
|
let root = unique_tmp("legacy-present");
|
||||||
|
let legacy = root.join("magnotia");
|
||||||
|
let new_path = root.join("lumotia");
|
||||||
|
std::fs::create_dir_all(&legacy).unwrap();
|
||||||
|
std::fs::write(legacy.join("magnotia.db"), b"sqlite-stub").unwrap();
|
||||||
|
std::fs::write(legacy.join("recordings.placeholder"), b"x").unwrap();
|
||||||
|
|
||||||
|
let result =
|
||||||
|
migrate_legacy_data_dir_inner(&new_path, Some(legacy.clone())).expect("migrate ok");
|
||||||
|
|
||||||
|
match result {
|
||||||
|
MigrationStatus::Migrated {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
renamed_db,
|
||||||
|
} => {
|
||||||
|
assert_eq!(from, legacy);
|
||||||
|
assert_eq!(to, new_path);
|
||||||
|
assert!(renamed_db, "expected db file to be renamed");
|
||||||
|
}
|
||||||
|
other => panic!("expected Migrated, got {other:?}"),
|
||||||
|
}
|
||||||
|
assert!(!legacy.exists(), "legacy dir should be gone");
|
||||||
|
assert!(new_path.exists(), "new dir should exist");
|
||||||
|
assert!(new_path.join("lumotia.db").exists(), "db at new name");
|
||||||
|
assert!(!new_path.join("magnotia.db").exists(), "old db gone");
|
||||||
|
assert!(
|
||||||
|
new_path.join("recordings.placeholder").exists(),
|
||||||
|
"other files preserved"
|
||||||
|
);
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(&root).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn migrate_with_both_present_returns_target_exists_and_preserves_lumotia() {
|
||||||
|
let root = unique_tmp("both-present");
|
||||||
|
let legacy = root.join("magnotia");
|
||||||
|
let new_path = root.join("lumotia");
|
||||||
|
std::fs::create_dir_all(&legacy).unwrap();
|
||||||
|
std::fs::create_dir_all(&new_path).unwrap();
|
||||||
|
std::fs::write(new_path.join("lumotia.db"), b"new-data").unwrap();
|
||||||
|
std::fs::write(legacy.join("magnotia.db"), b"legacy-data").unwrap();
|
||||||
|
|
||||||
|
let result =
|
||||||
|
migrate_legacy_data_dir_inner(&new_path, Some(legacy.clone())).expect("migrate ok");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
MigrationStatus::TargetAlreadyExists {
|
||||||
|
target: new_path.clone()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert!(legacy.exists(), "legacy dir preserved");
|
||||||
|
assert!(new_path.exists(), "new dir preserved");
|
||||||
|
assert_eq!(
|
||||||
|
std::fs::read(new_path.join("lumotia.db")).unwrap(),
|
||||||
|
b"new-data".to_vec(),
|
||||||
|
"lumotia.db not overwritten"
|
||||||
|
);
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(&root).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn migrate_with_neither_present_returns_no_legacy() {
|
||||||
|
let root = unique_tmp("neither-present");
|
||||||
|
let new_path = root.join("lumotia");
|
||||||
|
|
||||||
|
let result = migrate_legacy_data_dir_inner(&new_path, None).expect("migrate ok");
|
||||||
|
|
||||||
|
assert_eq!(result, MigrationStatus::NoLegacyFound);
|
||||||
|
assert!(!new_path.exists(), "no dir created");
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(&root).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn migrate_with_legacy_present_but_no_db_inside() {
|
||||||
|
let root = unique_tmp("legacy-no-db");
|
||||||
|
let legacy = root.join("magnotia");
|
||||||
|
let new_path = root.join("lumotia");
|
||||||
|
std::fs::create_dir_all(&legacy).unwrap();
|
||||||
|
std::fs::write(legacy.join("recordings.placeholder"), b"x").unwrap();
|
||||||
|
|
||||||
|
let result =
|
||||||
|
migrate_legacy_data_dir_inner(&new_path, Some(legacy.clone())).expect("migrate ok");
|
||||||
|
|
||||||
|
match result {
|
||||||
|
MigrationStatus::Migrated { renamed_db, .. } => {
|
||||||
|
assert!(!renamed_db, "no db to rename");
|
||||||
|
}
|
||||||
|
other => panic!("expected Migrated, got {other:?}"),
|
||||||
|
}
|
||||||
|
assert!(new_path.exists());
|
||||||
|
assert!(new_path.join("recordings.placeholder").exists());
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(&root).ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -905,6 +905,31 @@ pub async fn get_setting(pool: &SqlitePool, key: &str) -> Result<Option<String>>
|
|||||||
Ok(row.map(|r| r.get("value")))
|
Ok(row.map(|r| r.get("value")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One-shot key rename for settings rows carried over from the magnotia era
|
||||||
|
/// (`magnotia_preferences`, `magnotia_morning_triage_last_shown`, etc.).
|
||||||
|
///
|
||||||
|
/// Idempotent: rows already on the new key are left alone; rows on the old
|
||||||
|
/// key are renamed only if the new key does not already exist. Returns the
|
||||||
|
/// number of rows actually renamed so the caller can log on first launch.
|
||||||
|
pub async fn migrate_legacy_setting_keys(pool: &SqlitePool) -> Result<u64> {
|
||||||
|
let result = sqlx::query(
|
||||||
|
"UPDATE settings \
|
||||||
|
SET key = 'lumotia_' || substr(key, length('magnotia_') + 1) \
|
||||||
|
WHERE key LIKE 'magnotia_%' \
|
||||||
|
AND NOT EXISTS (\
|
||||||
|
SELECT 1 FROM settings AS dst \
|
||||||
|
WHERE dst.key = 'lumotia_' || substr(settings.key, length('magnotia_') + 1)\
|
||||||
|
)",
|
||||||
|
)
|
||||||
|
.execute(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|source| Error::Query {
|
||||||
|
operation: "migrate_legacy_setting_keys".into(),
|
||||||
|
source,
|
||||||
|
})?;
|
||||||
|
Ok(result.rows_affected())
|
||||||
|
}
|
||||||
|
|
||||||
// --- Row types ---
|
// --- Row types ---
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -944,7 +969,7 @@ pub struct TranscriptRow {
|
|||||||
pub anti_hallucination: bool,
|
pub anti_hallucination: bool,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
// Task 2.5 — transcripts_meta (migration v5). Persists the UI metadata
|
// Task 2.5 — transcripts_meta (migration v5). Persists the UI metadata
|
||||||
// that previously lived in the removed localStorage `magnotia_history` cache.
|
// that previously lived in the removed localStorage `lumotia_history` cache.
|
||||||
pub starred: bool,
|
pub starred: bool,
|
||||||
pub manual_tags: String,
|
pub manual_tags: String,
|
||||||
pub template: String,
|
pub template: String,
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ pub use database::{
|
|||||||
insert_transcript, list_feedback_examples, list_implementation_rules, list_profile_terms,
|
insert_transcript, list_feedback_examples, list_implementation_rules, list_profile_terms,
|
||||||
list_profiles, list_recent_completions, list_recent_errors, list_subtasks, list_tasks,
|
list_profiles, list_recent_completions, list_recent_errors, list_subtasks, list_tasks,
|
||||||
list_transcripts, list_transcripts_paged, log_error, mark_implementation_rule_fired,
|
list_transcripts, list_transcripts_paged, log_error, mark_implementation_rule_fired,
|
||||||
prune_error_log, record_feedback, search_transcripts, set_implementation_rule_enabled,
|
migrate_legacy_setting_keys, prune_error_log, record_feedback, search_transcripts,
|
||||||
set_setting, set_task_energy, uncomplete_task, update_profile, update_task, update_transcript,
|
set_implementation_rule_enabled, set_setting, set_task_energy, uncomplete_task, update_profile,
|
||||||
|
update_task, update_transcript,
|
||||||
update_transcript_meta, DailyCompletionCount, ErrorLogRow, FeedbackRow, FeedbackTargetType,
|
update_transcript_meta, DailyCompletionCount, ErrorLogRow, FeedbackRow, FeedbackTargetType,
|
||||||
ImplementationRuleRow, InsertTranscriptParams, ProfileRow, ProfileTermRow,
|
ImplementationRuleRow, InsertTranscriptParams, ProfileRow, ProfileTermRow,
|
||||||
RecordFeedbackParams, TaskRow, TranscriptRow,
|
RecordFeedbackParams, TaskRow, TranscriptRow,
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ async fn generate_diagnostic_report_inner(
|
|||||||
|
|
||||||
if opts.include_settings {
|
if opts.include_settings {
|
||||||
out.push_str("## Settings (sanitised)\n\n");
|
out.push_str("## Settings (sanitised)\n\n");
|
||||||
match lumotia_storage::get_setting(&state.db, "magnotia_preferences").await {
|
match lumotia_storage::get_setting(&state.db, "lumotia_preferences").await {
|
||||||
Ok(Some(json)) => {
|
Ok(Some(json)) => {
|
||||||
out.push_str("```json\n");
|
out.push_str("```json\n");
|
||||||
out.push_str(&sanitise_preferences_json(&json));
|
out.push_str(&sanitise_preferences_json(&json));
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
//!
|
//!
|
||||||
//! Stored under the existing SQLite settings table via
|
//! Stored under the existing SQLite settings table via
|
||||||
//! `lumotia_storage::{get_setting, set_setting}` — same bag as
|
//! `lumotia_storage::{get_setting, set_setting}` — same bag as
|
||||||
//! `magnotia_preferences`.
|
//! `lumotia_preferences`.
|
||||||
|
|
||||||
use lumotia_storage::{get_setting, set_setting};
|
use lumotia_storage::{get_setting, set_setting};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
const LAST_TRIAGE_KEY: &str = "magnotia_morning_triage_last_shown";
|
const LAST_TRIAGE_KEY: &str = "lumotia_morning_triage_last_shown";
|
||||||
|
|
||||||
/// Returns the YYYY-MM-DD date string stored on the last successful
|
/// Returns the YYYY-MM-DD date string stored on the last successful
|
||||||
/// morning triage dismissal, or `None` if the user has never been
|
/// morning triage dismissal, or `None` if the user has never been
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use crate::AppState;
|
|||||||
///
|
///
|
||||||
/// Task 2.5 — `starred`, `manualTags`, `template`, `language`, `segmentsJson`
|
/// Task 2.5 — `starred`, `manualTags`, `template`, `language`, `segmentsJson`
|
||||||
/// were added to back the viewer metadata that previously lived only in the
|
/// were added to back the viewer metadata that previously lived only in the
|
||||||
/// removed `magnotia_history` localStorage cache.
|
/// removed `lumotia_history` localStorage cache.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct TranscriptDto {
|
pub struct TranscriptDto {
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ use sqlx::SqlitePool;
|
|||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
use lumotia_core::paths::{migrate_legacy_data_dir, MigrationStatus};
|
||||||
use lumotia_core::types::EngineName;
|
use lumotia_core::types::EngineName;
|
||||||
use lumotia_llm::LlmEngine;
|
use lumotia_llm::LlmEngine;
|
||||||
use lumotia_storage::{database_path, get_setting, init as init_db, prune_error_log, set_setting};
|
use lumotia_storage::{
|
||||||
|
app_data_dir, database_path, get_setting, init as init_db, migrate_legacy_setting_keys,
|
||||||
|
prune_error_log, set_setting,
|
||||||
|
};
|
||||||
|
|
||||||
/// How long to retain `error_log` rows. Pruned once on startup.
|
/// How long to retain `error_log` rows. Pruned once on startup.
|
||||||
/// 90 days is long enough to triage "this happened a few weeks ago"
|
/// 90 days is long enough to triage "this happened a few weeks ago"
|
||||||
@@ -97,7 +101,7 @@ async fn save_preferences(
|
|||||||
state: tauri::State<'_, AppState>,
|
state: tauri::State<'_, AppState>,
|
||||||
preferences: String,
|
preferences: String,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
set_setting(&state.db, "magnotia_preferences", &preferences)
|
set_setting(&state.db, "lumotia_preferences", &preferences)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
@@ -220,6 +224,29 @@ pub fn run() {
|
|||||||
|
|
||||||
builder
|
builder
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
|
// One-shot legacy data-dir migration: rename ~/.local/share/magnotia
|
||||||
|
// (and macOS/Windows equivalents) to the lumotia path on first
|
||||||
|
// launch after the rebrand. Safe to call every boot — idempotent.
|
||||||
|
let t_migrate = Instant::now();
|
||||||
|
let new_data_dir = app_data_dir();
|
||||||
|
match migrate_legacy_data_dir(&new_data_dir) {
|
||||||
|
Ok(MigrationStatus::Migrated { from, to, renamed_db }) => tracing::info!(
|
||||||
|
target: "lumotia_startup",
|
||||||
|
elapsed_ms = t_migrate.elapsed().as_millis(),
|
||||||
|
from = %from.display(),
|
||||||
|
to = %to.display(),
|
||||||
|
renamed_db,
|
||||||
|
"migrated legacy magnotia data dir to lumotia"
|
||||||
|
),
|
||||||
|
Ok(MigrationStatus::TargetAlreadyExists { .. }) => {}
|
||||||
|
Ok(MigrationStatus::NoLegacyFound) => {}
|
||||||
|
Err(e) => tracing::warn!(
|
||||||
|
target: "lumotia_startup",
|
||||||
|
error = %e,
|
||||||
|
"legacy data dir migration failed — continuing with new path"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
// Initialise database and startup settings in one runtime entry.
|
// Initialise database and startup settings in one runtime entry.
|
||||||
let db_path = database_path();
|
let db_path = database_path();
|
||||||
let (db, init_script) = tauri::async_runtime::block_on(async {
|
let (db, init_script) = tauri::async_runtime::block_on(async {
|
||||||
@@ -229,6 +256,25 @@ pub fn run() {
|
|||||||
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
|
||||||
tracing::info!(target: "lumotia_startup", elapsed_ms = t0.elapsed().as_millis(), "DB init complete");
|
tracing::info!(target: "lumotia_startup", elapsed_ms = t0.elapsed().as_millis(), "DB init complete");
|
||||||
|
|
||||||
|
// One-shot settings-key migration: rename any leftover
|
||||||
|
// `magnotia_*` rows from the magnotia era to `lumotia_*`.
|
||||||
|
// Idempotent; logs only when rows are actually renamed.
|
||||||
|
let t_keys = Instant::now();
|
||||||
|
match migrate_legacy_setting_keys(&db).await {
|
||||||
|
Ok(0) => {}
|
||||||
|
Ok(n) => tracing::info!(
|
||||||
|
target: "lumotia_startup",
|
||||||
|
rows_renamed = n,
|
||||||
|
elapsed_ms = t_keys.elapsed().as_millis(),
|
||||||
|
"renamed legacy magnotia_* settings keys to lumotia_*"
|
||||||
|
),
|
||||||
|
Err(e) => tracing::warn!(
|
||||||
|
target: "lumotia_startup",
|
||||||
|
error = %e,
|
||||||
|
"settings-key migration failed — continuing"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
// Prune old `error_log` rows so the table doesn't grow unbounded
|
// Prune old `error_log` rows so the table doesn't grow unbounded
|
||||||
// across months of dogfooding. Best-effort — a prune failure is
|
// across months of dogfooding. Best-effort — a prune failure is
|
||||||
// not worth blocking startup over.
|
// not worth blocking startup over.
|
||||||
@@ -247,7 +293,7 @@ pub fn run() {
|
|||||||
|
|
||||||
// Load saved preferences for webview injection.
|
// Load saved preferences for webview injection.
|
||||||
let t1 = Instant::now();
|
let t1 = Instant::now();
|
||||||
let prefs_json = get_setting(&db, "magnotia_preferences").await.unwrap_or(None);
|
let prefs_json = get_setting(&db, "lumotia_preferences").await.unwrap_or(None);
|
||||||
tracing::info!(target: "lumotia_startup", elapsed_ms = t1.elapsed().as_millis(), "preferences load complete");
|
tracing::info!(target: "lumotia_startup", elapsed_ms = t1.elapsed().as_millis(), "preferences load complete");
|
||||||
let init_script = build_preferences_script(prefs_json);
|
let init_script = build_preferences_script(prefs_json);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user