Skip to main content

max / goingson

Audit Run 14: tests, security hardening, JSDoc, date_utils extraction Security: - Sanitize filename in open_attachment() before temp_dir.join() - Add escAttr() to onclick handlers in day-planning-render, attachments Code quality: - Extract date formatting functions to core::date_utils (4 functions, 16 tests) - Add doc comments to complex search query functions in db-sqlite Testing: - Add command error scenario tests (day planning, export, time tracking) - Add plugin hot-reload edge case tests (update while running, corrupt manifest) - Add full plugin lifecycle test (discover, load, execute, error, recover) Documentation: - Add JSDoc coverage across ~160 utility functions in 36 JS files
Co-Authored-By
Claude Opus 4.6 <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-04-16 01:38 UTC
Commit: ba392f7e899352730a484ec7717f994d5be2308a
Parent: c4f0b1f
62 files changed, +2967 insertions, -461 deletions
@@ -1,8 +1,7 @@
1 1 //! Database file watcher for detecting external changes.
2 2 //!
3 3 //! Watches the SQLite database file for modifications made by external
4 - //! processes (like the MCP server) and emits Tauri events to trigger
5 - //! UI refreshes.
4 + //! processes and emits Tauri events to trigger UI refreshes.
6 5
7 6 use notify::RecursiveMode;
8 7 use notify_debouncer_mini::new_debouncer;
@@ -9,7 +9,7 @@
9 9 init_pool, run_migrations,
10 10 SqliteAttachmentRepository, SqliteBackupSettingsRepository, SqliteContactRepository,
11 11 SqliteEmailAccountRepository, SqliteEmailRepository, SqliteEventRepository,
12 - SqliteLlmCacheRepository, SqliteLlmSettingsRepository, SqliteMilestoneRepository,
12 + SqliteMilestoneRepository,
13 13 SqliteProjectRepository, SqliteSavedViewRepository, SqliteMonthlyReviewRepository,
14 14 SqliteSearchRepository, SqliteStatsRepository, SqliteSyncAccountRepository,
15 15 SqliteTaskRepository, SqliteWeeklyReviewRepository,
@@ -65,8 +65,6 @@
65 65 attachments: Arc::new(SqliteAttachmentRepository::new(pool.clone())),
66 66 stats: Arc::new(SqliteStatsRepository::new(pool.clone())),
67 67 search: Arc::new(SqliteSearchRepository::new(pool.clone())),
68 - llm_settings: Arc::new(SqliteLlmSettingsRepository::new(pool.clone())),
69 - llm_cache: Arc::new(SqliteLlmCacheRepository::new(pool.clone())),
70 68 milestones: Arc::new(SqliteMilestoneRepository::new(pool.clone())),
71 69 saved_views: Arc::new(SqliteSavedViewRepository::new(pool.clone())),
72 70 weekly_reviews: Arc::new(SqliteWeeklyReviewRepository::new(pool.clone())),
@@ -120,7 +120,6 @@
120 120 MonthlyReflectionId,
121 121 SavedViewId,
122 122 EmailAccountId,
123 - LlmSettingsId,
124 123 UserId,
125 124 );
126 125
@@ -33,6 +33,7 @@
33 33 pub mod backup_restore;
34 34 pub mod constants;
35 35 pub mod contact;
36 + pub mod date_utils;
36 37 pub mod day_planning;
37 38 pub mod email_id;
38 39 pub mod email_sync;
@@ -56,16 +57,16 @@
56 57 pub use error::CoreError;
57 58 pub use id_types::{
58 59 AnnotationId, AttachmentId, ContactEmailId, ContactId, ContactPhoneId, CustomFieldId,
59 - EmailAccountId, EmailId, EventId, LlmSettingsId, MilestoneId, MonthlyGoalId,
60 + EmailAccountId, EmailId, EventId, MilestoneId, MonthlyGoalId,
60 61 MonthlyReflectionId, ProjectId, SavedViewId, SocialHandleId,
61 62 SubtaskId, SyncAccountId, TaskId, TimeSessionId, UserId, WeeklyReviewId,
62 63 };
63 64 pub use models::{
64 65 Annotation, Attachment, BackupSettings, BlockType, CssClass, DbValue, Email, EmailAccount,
65 - EmailAuthType, EmailThread, Event, LlmContext, LlmProviderType, LlmSettings, Milestone,
66 + EmailAuthType, EmailThread, Event, Milestone,
66 67 MilestoneStatus, MonthlyGoal, MonthlyGoalStatus, MonthlyReflection,
67 68 AttachmentMeta, NewAttachment, NewBackupSettings, NewEmail, NewEmailWithTracking, NewEvent, NewEventBuilder,
68 - NewLlmSettings, NewMilestone, NewProject, NewSavedView, NewTask, NewTaskBuilder, Priority,
69 + NewMilestone, NewProject, NewSavedView, NewTask, NewTaskBuilder, Priority,
69 70 Project, ParseableEnum, ProjectStatus, ProjectType, Recurrence, SavedView, SortDirection,
70 71 SyncAccount,
71 72 SortField, Subtask, Task, TaskFilterQuery, TaskSortColumn, TaskStatus, TimeSession,
@@ -26,7 +26,15 @@
26 26 }
27 27 }
28 28
29 - /// Add months to a DateTime, handling edge cases like month-end dates
29 + /// Add months to a DateTime, handling edge cases like month-end dates.
30 + ///
31 + /// Uses absolute month counting (year*12 + month) to add/subtract months, then
32 + /// clamps the day to the target month's length. Examples:
33 + /// Jan 31 + 1 month → Feb 28 (or 29 in a leap year)
34 + /// Mar 31 + 1 month → Apr 30
35 + ///
36 + /// Preserves the original hour/minute/second. Falls back to the input datetime
37 + /// if the target date is ambiguous (e.g., DST gap via `with_ymd_and_hms`).
30 38 fn add_months(dt: DateTime<Utc>, months: i32) -> DateTime<Utc> {
31 39
32 40 let year = dt.year();
@@ -54,7 +54,22 @@
54 54 calculate_urgency_with_config(priority, status, due, created_at, tags, &config)
55 55 }
56 56
57 - /// Calculate urgency with custom configuration
57 + /// Calculate urgency with custom configuration.
58 + ///
59 + /// Additive score from these factors (all configurable via `UrgencyConfig`):
60 + ///
61 + /// | Factor | Default | Condition |
62 + /// |-------------|---------|-------------------------------------------|
63 + /// | Priority | 3-7 | High=7, Medium=5, Low=3 |
64 + /// | Overdue | +12.0 | Due date in the past |
65 + /// | Due soon | 0-7.0 | Linear scale: 7 days out (0) → due (7.0) |
66 + /// | Age | 0-2.0 | Linear over 30 days, capped at 2.0 |
67 + /// | Started | +4.0 | Task status is Started |
68 + /// | "urgent" tag| +2.0 | Case-insensitive tag match |
69 + ///
70 + /// Result is rounded to 1 decimal place for stable sorting.
71 + /// Sub-day precision: hours are used internally so tasks due at 3pm sort
72 + /// differently from tasks due at 9am even on the same day.
58 73 pub fn calculate_urgency_with_config(
59 74 priority: &Priority,
60 75 status: &TaskStatus,