max / goingson
5 files changed,
+20 insertions,
-18 deletions
| @@ -23,8 +23,8 @@ pub trait CssClass { | |||
| 23 | 23 | /// both `EnumString` and `Default`. This replaces identical boilerplate | |
| 24 | 24 | /// across 6+ enums. | |
| 25 | 25 | /// | |
| 26 | - | /// Enums with custom parsing logic (Priority, LlmProviderType, TaskSortColumn, | |
| 27 | - | /// SortDirection, EmailAuthType) keep their manual implementations. | |
| 26 | + | /// Enums with custom parsing logic (Priority, TaskSortColumn, SortDirection, | |
| 27 | + | /// EmailAuthType) keep their manual implementations. | |
| 28 | 28 | pub trait ParseableEnum: std::str::FromStr + Default { | |
| 29 | 29 | /// Parses a string into this enum, falling back to `Default` on invalid input. | |
| 30 | 30 | /// |
| @@ -256,9 +256,9 @@ mod tests { | |||
| 256 | 256 | ||
| 257 | 257 | #[test] | |
| 258 | 258 | fn test_with_project() { | |
| 259 | - | let result = parse_quick_add("Fix bug project:Claudetainment"); | |
| 259 | + | let result = parse_quick_add("Fix bug project:Website"); | |
| 260 | 260 | assert_eq!(result.description, "Fix bug"); | |
| 261 | - | assert_eq!(result.project_name, Some("Claudetainment".to_string())); | |
| 261 | + | assert_eq!(result.project_name, Some("Website".to_string())); | |
| 262 | 262 | } | |
| 263 | 263 | ||
| 264 | 264 | #[test] |
| @@ -55,8 +55,6 @@ pub const BACKUP_TABLES: &[&str] = &[ | |||
| 55 | 55 | pub const EXCLUDED_TABLES: &[&str] = &[ | |
| 56 | 56 | "users", // single fixed desktop user, recreated on init | |
| 57 | 57 | "email_accounts", // credentials/config; secrets live in the OS keychain, re-auth on restore | |
| 58 | - | "llm_settings", // local config / API keys, not user content | |
| 59 | - | "llm_cache", // regenerable cache | |
| 60 | 58 | "backup_settings", // local backup cadence/retention config | |
| 61 | 59 | "backup_settings_new", // transient table-rename scaffold (absent post-migration) | |
| 62 | 60 | "imap_folder_sync_state", // sync-transient IMAP state, re-derived on next sync |
| @@ -0,0 +1,11 @@ | |||
| 1 | + | -- Retire the orphaned LLM schema. | |
| 2 | + | -- | |
| 3 | + | -- Migration 014 provisioned `llm_settings` and `llm_cache` for an AI feature that | |
| 4 | + | -- was removed; no Rust or JS code has read or written either table since. Keeping | |
| 5 | + | -- them provisioned on every fresh install contradicts the product's no-AI rule and | |
| 6 | + | -- leaves a dead API-key column (`llm_settings.api_key`) in the schema. Drop both. | |
| 7 | + | ||
| 8 | + | DROP INDEX IF EXISTS idx_llm_cache_lookup; | |
| 9 | + | DROP INDEX IF EXISTS idx_llm_settings_user; | |
| 10 | + | DROP TABLE IF EXISTS llm_cache; | |
| 11 | + | DROP TABLE IF EXISTS llm_settings; |
| @@ -13,7 +13,7 @@ use tracing::{debug, error, info, warn}; | |||
| 13 | 13 | use crate::commands::sync_email_account_inner; | |
| 14 | 14 | #[cfg(not(any(target_os = "ios", target_os = "android")))] | |
| 15 | 15 | use crate::notifications::send_notification; | |
| 16 | - | use crate::state::AppState; | |
| 16 | + | use crate::state::{AppState, DESKTOP_USER_ID}; | |
| 17 | 17 | ||
| 18 | 18 | /// How often the scheduler checks for accounts needing sync (in seconds). | |
| 19 | 19 | const CHECK_INTERVAL_SECS: u64 = 60; | |
| @@ -21,9 +21,6 @@ const CHECK_INTERVAL_SECS: u64 = 60; | |||
| 21 | 21 | /// Maximum backoff multiplier (caps at ~16 minutes between retries). | |
| 22 | 22 | const MAX_BACKOFF_MULTIPLIER: u32 = 16; | |
| 23 | 23 | ||
| 24 | - | /// Desktop user ID (matches DESKTOP_USER_ID in state.rs). | |
| 25 | - | const DESKTOP_USER_ID: goingson_core::UserId = goingson_core::UserId::from_uuid(uuid::Uuid::from_u128(1)); | |
| 26 | - | ||
| 27 | 24 | /// Starts the email sync scheduler background task. | |
| 28 | 25 | /// | |
| 29 | 26 | /// This function runs indefinitely, checking every minute for email accounts | |
| @@ -164,13 +161,9 @@ async fn check_and_sync_accounts( | |||
| 164 | 161 | } | |
| 165 | 162 | ||
| 166 | 163 | /// Probabilistic skip for backoff: returns true (skip) with probability (backoff-1)/backoff. | |
| 167 | - | /// For backoff=2, skips ~50% of ticks. For backoff=16, skips ~94% of ticks. | |
| 164 | + | /// For backoff=2, skips ~50% of ticks. For backoff=16, skips ~94% of ticks; backoff=1 | |
| 165 | + | /// never skips. Uses a real RNG — timer ticks are ~60s apart, so the old | |
| 166 | + | /// clock-nanosecond source was effectively arbitrary rather than uniform. | |
| 168 | 167 | fn rand_skip(backoff: u32) -> bool { | |
| 169 | - | use std::time::SystemTime; | |
| 170 | - | // Use low bits of system time as a cheap pseudo-random source | |
| 171 | - | let nanos = SystemTime::now() | |
| 172 | - | .duration_since(SystemTime::UNIX_EPOCH) | |
| 173 | - | .unwrap_or_default() | |
| 174 | - | .subsec_nanos(); | |
| 175 | - | !nanos.is_multiple_of(backoff) | |
| 168 | + | !rand::random::<u32>().is_multiple_of(backoff) | |
| 176 | 169 | } |