Skip to main content

max / goingson

Storage/hygiene: drop dead LLM schema, dedupe scheduler constant, fix backoff RNG - Migration 057 drops the orphaned llm_settings/llm_cache tables (provisioned by 014 for a removed AI feature, read by no code); remove them from restore.rs EXCLUDED_TABLES and fix the stale LlmProviderType doc mention. - email_sync_scheduler: import DESKTOP_USER_ID from state.rs instead of keeping a private copy that could drift; replace the clock-jitter (SystemTime nanos) backoff source with rand::random, since ~60s-apart ticks made the nanosecond field effectively arbitrary. - Delete the root .env postgres fossil (SQLite app, nothing reads it) and rename the "Claudetainment" project test fixture in parser.rs. Suite: 785 pass; clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-02 22:02 UTC
Commit: 3d70ab95fcb482ae9ff914355ee3c0aeead6287b
Parent: 1732163
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 }