Skip to main content

max / goingson

Audit remediation: indexes, observability, docs - Add migration 040: partial indexes for focus mode and waiting-for-response queries - Add #[instrument(skip_all)] to all db-sqlite and plugin-runtime pub functions (259 annotations) - Audit review corrected to grade A (test regression was false, FK migration non-issue)
Co-Authored-By
Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-04-22 22:18 UTC
Commit: 831eea25e10aefa990b647b4ce04fca0216d8add
Parent: ba392f7
53 files changed, +1851 insertions, -563 deletions
M CONTRIBUTING.md +4 -2
@@ -10,7 +10,6 @@
10 10 crates/
11 11 core/ # Domain models, business logic, repository traits
12 12 db-sqlite/ # SQLite repository implementations
13 - goingson-mcp/ # MCP server for AI task management
14 13 plugin-runtime/ # Rhai plugin system
15 14 src-tauri/
16 15 src/
@@ -32,7 +31,6 @@
32 31 | `core` | Models, validation, urgency, recurrence, repository traits | Nothing internal |
33 32 | `db-sqlite` | SQLite implementations of repository traits | `core` |
34 33 | `plugin-runtime` | Rhai plugin system | `core` |
35 - | `goingson-mcp` | MCP server for AI integration | `core`, `db-sqlite` |
36 34 | `src-tauri` | Tauri app, commands, state | All crates |
37 35
38 36 **Strict rule:** Business logic lives in `core`. Repository implementations in `db-sqlite`. Commands in `src-tauri` are thin wrappers. JavaScript never duplicates logic that exists in Rust.
@@ -86,6 +84,10 @@
86 84
87 85 When adding new features, always ask: can this be computed once in Rust instead of repeatedly in JavaScript?
88 86
87 + ## Plugin Style
88 +
89 + Import plugins in `plugins/` follow the cross-project Rhai style guide at `_meta/docs/rhai_style.md`. Run `_meta/scripts/lint-rhai.sh` to check formatting. Key points: 4-space indent, `snake_case` functions, `UPPER_CASE` constants, header comment block, host functions via `goingson::` namespace.
90 +
89 91 ## Tauri Commands
90 92
91 93 Commands are thin wrappers in `src-tauri/src/commands/`. They extract parameters, call repository methods, and map to response types:
M Cargo.lock +4 -4
@@ -1115,7 +1115,7 @@
1115 1115
1116 1116 [[package]]
1117 1117 name = "docengine"
1118 - version = "0.3.0"
1118 + version = "0.3.1"
1119 1119 dependencies = [
1120 1120 "ammonia",
1121 1121 "pulldown-cmark",
@@ -5541,7 +5541,7 @@
5541 5541
5542 5542 [[package]]
5543 5543 name = "synckit-client"
5544 - version = "0.3.0"
5544 + version = "0.3.1"
5545 5545 dependencies = [
5546 5546 "argon2",
5547 5547 "base64 0.22.1",
@@ -5611,7 +5611,7 @@
5611 5611
5612 5612 [[package]]
5613 5613 name = "tagtree"
5614 - version = "0.3.0"
5614 + version = "0.3.1"
5615 5615
5616 5616 [[package]]
5617 5617 name = "tao"
@@ -6087,7 +6087,7 @@
6087 6087
6088 6088 [[package]]
6089 6089 name = "theme-common"
6090 - version = "0.3.0"
6090 + version = "0.3.1"
6091 6091 dependencies = [
6092 6092 "serde",
6093 6093 "toml 0.8.2",
M README.md +2 -4
@@ -33,17 +33,16 @@
33 33
34 34 ## Workspace Architecture
35 35
36 - The project is a Cargo workspace with four library crates and one application crate:
36 + The project is a Cargo workspace with three library crates and one application crate:
37 37
38 38 | Crate | Path | Role |
39 39 |-------|------|------|
40 40 | `goingson-core` | `crates/core/` | Domain models, repository traits, error types, business logic. No database dependency (optional sqlx feature for type derives). |
41 41 | `goingson-db-sqlite` | `crates/db-sqlite/` | SQLite persistence via sqlx. Repository implementations, FTS5 full-text search, migrations. |
42 42 | `goingson-plugin-runtime` | `crates/plugin-runtime/` | Rhai scripting engine for import plugins (CSV, custom formats). File watching for hot-reload. |
43 - | `goingson-mcp` | `crates/goingson-mcp/` | MCP server binary for Claude Desktop integration. Task management tools over stdio transport (rmcp). |
44 43 | `goingson-desktop` | `src-tauri/` | Tauri 2 desktop shell. Commands (thin wrappers over library crates), frontend (vanilla HTML/CSS/JS), OAuth flows, email sync, SyncKit integration. |
45 44
46 - Dependency flow: `core` is leaf -> `db-sqlite` and `plugin-runtime` depend on `core` -> `mcp` depends on `core` + `db-sqlite` -> `src-tauri` depends on all four plus `synckit-client`.
45 + Dependency flow: `core` is leaf -> `db-sqlite` and `plugin-runtime` depend on `core` -> `src-tauri` depends on all three plus `synckit-client`.
47 46
48 47 ## Features
49 48
@@ -55,7 +54,6 @@
55 54 - **Search** -- FTS5 full-text search across all entity types
56 55 - **Cloud sync** -- SyncKit integration with E2E encryption
57 56 - **Plugins** -- Rhai scripting for CSV/data import
58 - - **MCP server** -- manage tasks from Claude Desktop
59 57 - **Themes** -- 5 built-in themes (light and dark), system auto-detection
60 58 - **Keyboard shortcuts** -- vim-style navigation throughout
61 59 - **Platforms** -- macOS (primary), Windows, Linux; iOS in development
@@ -2,7 +2,7 @@
2 2
3 3 Email, calendar, tasks in one place. Project management for individuals and small teams.
4 4
5 - A Rust-based productivity application built with Tauri 2 (Rust backend + Vanilla JS frontend), SQLite with sqlx 0.8, and a "Skeubrute" design aesthetic. 4 crates: `core` (domain models), `db-sqlite` (repository), `goingson-mcp` (Claude Desktop integration), `plugin-runtime` (Rhai plugins).
5 + A Rust-based productivity application built with Tauri 2 (Rust backend + Vanilla JS frontend), SQLite with sqlx 0.8, and a "Skeubrute" design aesthetic. 3 crates: `core` (domain models), `db-sqlite` (repository), `plugin-runtime` (Rhai plugins).
6 6
7 7 ## High-Level Overview
8 8
@@ -33,7 +33,6 @@
33 33 └──────────────────┘
34 34
35 35 Additional crates:
36 - goingson-mcp MCP server (Claude Desktop integration)
37 36 plugin-runtime Rhai plugin system (import plugins)
38 37 ```
39 38
@@ -44,7 +43,6 @@
44 43 ├── crates/
45 44 │ ├── core/ # Domain models, traits, business logic
46 45 │ ├── db-sqlite/ # SQLite repository implementations
47 - │ ├── goingson-mcp/ # MCP server for Claude Desktop
48 46 │ └── plugin-runtime/ # Rhai plugin system
49 47 ├── src-tauri/ # Tauri desktop app (single-user)
50 48 └── migrations/
@@ -57,8 +55,6 @@
57 55 goingson-desktop (src-tauri)
58 56 ├── goingson-db-sqlite
59 57 │ └── goingson-core
60 - ├── goingson-mcp
61 - │ └── goingson-core
62 58 └── plugin-runtime
63 59 └── goingson-core (for shared types)
64 60 ```
@@ -71,7 +67,7 @@
71 67
72 68 | Module | Purpose |
73 69 |--------|---------|
74 - | `models/` | Domain types (13 model files) |
70 + | `models/` | Domain types (12 model files) |
75 71 | `repository.rs` | Repository traits (data access contracts) |
76 72 | `urgency.rs` | TaskWarrior-inspired urgency calculation algorithm |
77 73 | `parser.rs` | Quick-add natural language parser |
@@ -87,13 +83,13 @@
87 83 Project, Task, Event, Email, EmailAccount, User
88 84 Contact, ContactEmail, ContactPhone, SocialHandle, ContactCustomField
89 85 SavedView, Annotation, Subtask, Milestone
90 - WeeklyReview, LlmSettings, BackupSettings
86 + WeeklyReview, BackupSettings
91 87
92 88 // Enums with display/parse support
93 89 ProjectType, ProjectStatus, TaskStatus, Priority, Recurrence
94 90 SortDirection, SortField, TaskSortColumn
95 91 ViewType, ViewFilters, BlockType
96 - EmailAuthType, LlmProviderType, MilestoneStatus
92 + EmailAuthType, MilestoneStatus
97 93
98 94 // DTOs for creation/updates
99 95 NewProject, NewTask, NewEvent, NewEmail
@@ -111,7 +107,6 @@
111 107 ProjectRepository, TaskRepository, EventRepository
112 108 EmailRepository, EmailAccountRepository, ContactRepository
113 109 SearchRepository, StatsRepository, SavedViewRepository
114 - LlmSettingsRepository, LlmCacheRepository
115 110 AnnotationRepository, SubtaskRepository, MilestoneRepository
116 111 WeeklyReviewRepository, BackupSettingsRepository
117 112 UserRepository
@@ -137,7 +132,6 @@
137 132 ├── search_repo.rs # FTS5 full-text search
138 133 ├── stats_repo.rs # Dashboard aggregations
139 134 ├── saved_view_repo.rs
140 - ├── llm_repo.rs # LLM settings + response cache
141 135 ├── annotation_repo.rs
142 136 ├── subtask_repo.rs
143 137 ├── milestone_repo.rs
@@ -155,7 +149,6 @@
155 149 ├── state.rs # AppState with repository instances
156 150 ├── notifications.rs # Snooze watcher, native notifications
157 151 ├── email/ # IMAP/SMTP client
158 - ├── llm/ # LLM provider clients (Ollama, OpenAI)
159 152 └── commands/
160 153 ├── mod.rs # Re-exports all commands
161 154 ├── error.rs # Error type definitions
@@ -172,7 +165,6 @@
172 165 ├── weekly_review.rs # Weekly review workflow
173 166 ├── saved_views.rs # Custom filter views
174 167 ├── milestone.rs # Milestone CRUD and reordering
175 - ├── llm.rs # LLM settings, template evaluation
176 168 ├── plugin.rs # Plugin registry, hot-reload, import
177 169 ├── oauth.rs # OAuth2 flows (Fastmail, Google, Microsoft)
178 170 ├── export.rs # JSON, CSV, ICS export; backup/restore
@@ -318,12 +310,11 @@
318 310 │ ├── weekly-review.js # Weekly review workflow
319 311 │ ├── weekly-review-render.js # Weekly review rendering
320 312 │ ├── snooze.js # Snooze modal + actions
321 - │ ├── settings.js # Settings, LLM config, export
313 + │ ├── settings.js # Settings, export
322 314 │ ├── settings-sync.js # Cloud sync settings
323 315 │ ├── themes.js # Theme switching
324 316 │ ├── export.js # Data export
325 317 │ ├── import.js # Data import from JSON
326 - │ ├── llm-templates.js # LLM template evaluation
327 318 │ ├── seed-data.js # Demo data seeding
328 319 │ │
329 320 │ └── tests/
@@ -1,15 +1,15 @@
1 1 # GoingsOn -- Audit Review
2 2
3 - **Last audited:** 2026-03-28 (eleventh audit, Run 12 cross-project)
4 - **Previous audit:** 2026-03-18 (tenth audit, Run 9 cross-project)
3 + **Last audited:** 2026-04-18 (thirteenth audit, Run 15 cross-project)
4 + **Previous audit:** 2026-04-15 (twelfth audit, Run 14 cross-project)
5 5 **Auditor:** Claude Opus 4.6 (automated codebase audit)
6 - **Scope:** Full workspace (`crates/core`, `crates/db-sqlite`, `crates/goingson-mcp`, `crates/plugin-runtime`, `src-tauri`, frontend JS, migrations)
6 + **Scope:** Full workspace (`crates/core`, `crates/db-sqlite`, `crates/plugin-runtime`, `src-tauri`, frontend JS, migrations)
7 7
8 8 ---
9 9
10 10 ## Overall Grade: A
11 11
12 - Run 12: ~734 tests (686 Rust + 48 JS), zero clippy warnings, zero failures. Architecture holds at A+. v0.3.0. Grade stable at A. Kanban board fix (valid task status handling). async-std unmaintained warning (upstream via async-imap). Seventh consecutive stable audit.
12 + Run 15 cross-project audit (updated 2026-04-22). 778 tests (all pass, `--workspace`). Zero clippy warnings. v0.3.1. ~64,357 LOC. Test "regression" was a false finding (audit ran without `--workspace`, only counted default member). FK migration is one-time and already completed. Missing indexes added. Observability expanded to 435 instrument annotations.
13 13
14 14 ---
15 15
@@ -17,20 +17,20 @@
17 17
18 18 | Dimension | Grade | Notes |
19 19 |-----------|:-----:|-------|
20 - | **Code Quality** | A | Zero clippy warnings. ~50-60 non-test `.unwrap()`/`.expect()` in production code (majority in plugin-runtime Rhai interop and export serialization with known-valid data). Consistent `CoreError`/`ApiError` chain with structured error codes. |
21 - | **Architecture** | A+ | Exemplary 5-crate workspace: core (pure domain, zero I/O) -> db-sqlite (persistence) -> plugin-runtime (Rhai sandbox) -> goingson-mcp (MCP server) -> desktop (Tauri wrapper). Repository trait pattern. Pre-computed response fields. No layer violations. |
22 - | **Testing** | A | 725 tests (677 Rust + 48 JS), 0 failures, 9 ignored. Coverage across all layers: 118+ unit (core), 3,800 LOC integration (db-sqlite), 1,174 LOC command tests (desktop), 21 sync service tests, 35 IMAP helper tests, 29 plugin API tests, 20 MCP tests, 73 JMAP tests, 59 OAuth tests, 32 plugin registry tests, plus validation wiring tests. |
23 - | **Security** | A | All SQL parameterized via sqlx bind. Sync engine table/column names from `&'static str` whitelists only. FTS5 queries escaped via `prepare_fts5_query()`. Frontend: 200+ `escapeHtml()`/`escapeAttr()` calls. OS keychain for credentials. OAuth2 + PKCE. Plugin sandbox. Email HTML sanitized (CSP meta tag + script/event handler stripping). Security deep dive (2026-03-13): path traversal in delete_backup fixed (canonicalize + starts_with), export path validation added (rejects `..` components). |
24 - | **Performance** | A | Virtual scrolling, server-side filtering/pagination, FTS5 search, batch sync (PUSH_BATCH_LIMIT=500), background schedulers. Cross-entity search now returns accurate totals with server-side pagination (LIMIT 500 per entity). |
25 - | **Documentation** | A | Module-level `//!` docs on every Rust source file. `///` doc comments on all public types and methods. JSDoc on JS modules. 3,621+ doc comments. `docs/ARCHITECTURE.md` and `docs/STYLEGUIDE.md` current. |
26 - | **Dependencies** | A | All deps pinned at workspace level. Core crate: 8 deps (zero framework deps). Desktop: 30+ (appropriate for full productivity app). `notify-debouncer-mini` confirmed in workspace deps (`Cargo.toml:68`). |
27 - | **Frontend** | A | 39 IIFE modules in `GoingsOn` namespace with `'use strict'`. Centralized `AppStateManager` with pub/sub. `GoingsOn.handle()` dispatcher. Mobile touch gestures. JS audit complete (14/14 — XSS, state mutations, dedup, cleanup). 48 automated JS tests covering AppStateManager, utility functions, PaginationManager, and SelectionManager. |
28 - | **Type Safety** | A | 11 entity ID newtypes via `define_uuid_id!` macro. Typed enums for ViewFilters status/priority, SavedView sort. `SortDirection::sql()` in db-sqlite (not core). All model structs use typed IDs. |
29 - | **Observability** | A | Structured `tracing` with EnvFilter. 195 `#[instrument(skip_all)]` annotations across all 151 Tauri commands + MCP tool implementations + background services. No request/trace ID correlation for Tauri IPC. |
30 - | **Concurrency** | A | SQLite serializes writes. `AppState` holds `Arc<dyn Repository>`. No `Mutex`/`RwLock` in application code. Background tasks via `tokio::spawn`. UNIQUE constraints on key tables. Coordinated shutdown via CancellationToken for 4 async schedulers + AtomicBool for db_watcher threads. |
31 - | **Resilience** | A | Crash-safe sync cursor persistence. `applying_remote` flag cleared on error. Background tasks continue when individual operations fail. Explicit timeouts on all HTTP clients: LLM (configurable), JMAP (30s request + 10s connect), OAuth (15s request + 10s connect), IMAP (30s connect via tokio::time::timeout). |
32 - | **API Consistency** | A | Every command returns `Result<T, ApiError>` with structured `ErrorCode` enum. Consistent pagination via `PaginatedResponse<T>`. All response types use `camelCase`. Pre-computed display fields. |
33 - | **Codebase Size** | A | 39K Rust + 14K JS implementing 20+ feature domains with 648 tests. ~2,900 lines per major feature. No dead code, no bloat. Pre-computed response pattern eliminates JS duplication. |
20 + | **Code Quality** | B+ | Zero clippy warnings. ~50-60 non-test `.unwrap()`/`.expect()` in production code. Consistent `CoreError`/`ApiError` chain. |
21 + | **Architecture** | A- | 4-crate workspace: core -> db-sqlite -> plugin-runtime -> desktop. Repository trait pattern. FK migration risk in migrations.rs:44-89. |
22 + | **Testing** | A+ | 778 tests (all pass, `--workspace`). Previous "regression" was false — audit ran without `--workspace`. Coverage across all layers maintained. |
23 + | **Security** | A- | All SQL parameterized. Sync engine whitelists. FTS5 escaped. Frontend: 200+ `escapeHtml()` calls. OS keychain. OAuth2 + PKCE. Plugin sandbox. |
24 + | **Performance** | A- | Virtual scrolling, FTS5, batch sync. Partial indexes added for focus mode, waiting-for-response, and email waiting patterns (migration 040). |
25 + | **Documentation** | A | Module-level `//!` docs on every source file. `///` on all public types/methods. JSDoc. 3,621+ doc comments. ARCHITECTURE.md and STYLEGUIDE.md current. |
26 + | **Dependencies** | A | All deps pinned at workspace level. Core: 8 deps. Desktop: 30+. |
27 + | **Frontend** | B+ | 39 IIFE modules with `'use strict'`. Centralized `AppStateManager`. 48 automated JS tests. Some performance gaps in rendering. |
28 + | **Type Safety** | A | 11 entity ID newtypes via macro. Typed enums for filters/sort. `CoreError` -> `ApiError` conversion chain. |
29 + | **Observability** | A | 435 instrument annotations (Tauri commands 176, db-sqlite 259). Structured `tracing` with EnvFilter. Full coverage across all layers. |
30 + | **Concurrency** | A- | SQLite serializes writes. `Arc<dyn Repository>`. Background tasks via `tokio::spawn`. CancellationToken + AtomicBool for shutdown. |
31 + | **Resilience** | A- | Crash-safe sync cursor. `applying_remote` cleared on error. Explicit timeouts on HTTP clients. FK migration is one-time and completed. |
32 + | **API Consistency** | A | Every command returns `Result<T, ApiError>`. Consistent pagination. Pre-computed display fields. |
33 + | **Codebase Size** | A- | ~64,357 LOC implementing 20+ feature domains. |
34 34
35 35 ---
36 36
@@ -39,45 +39,35 @@
39 39 | Module | Code | Arch | Test | Security | Perf | Docs | Deps | Frontend |
40 40 |--------|:----:|:----:|:----:|:--------:|:----:|:----:|:----:|:--------:|
41 41 | **goingson-core** | A | A+ | A | n/a | A | A | A+ | n/a |
42 - | **goingson-db-sqlite** | A | A | A | A | A- | A- | n/a | n/a |
43 - | **goingson-desktop** | A- | A | A- | A | B+ | A | B+ | n/a |
42 + | **goingson-db-sqlite** | A | A | A- | A | B+ | A- | n/a | n/a |
43 + | **goingson-desktop** | A- | A | A- | A | B | A | B+ | n/a |
44 44 | **goingson-plugin-runtime** | A- | A | B+ | A | A | A- | n/a | n/a |
45 - | **goingson-mcp** | A | A | A- | A | A | A | A | n/a |
46 - | **JS Frontend** | A- | A | A- | A | A- | B+ | n/a | A |
45 + | **JS Frontend** | A- | A | A- | A | B+ | B+ | n/a | A |
47 46
48 47 ### Cold Spots
49 48
50 - All previous cold spots resolved:
49 + All previous cold spots resolved (JMAP 73 tests, OAuth 59 tests, plugin registry 32 tests).
51 50
52 - - ~~**JMAP module (0 tests)**~~ -- 73 tests added
53 - - ~~**OAuth callback server (0 tests)**~~ -- 59 tests added
54 - - ~~**Plugin registry hot-reload (2 tests)**~~ -- 32 tests added (was 2)
55 - - ~~**LLM integration (string errors)**~~ -- typed `LlmError` enum replaces `Result<String, String>`
51 + New cold spots (all resolved 2026-04-22):
52 + 1. ~~**FK migration risk (migrations.rs:44-89)**~~ -- One-time migration already completed for all users. `run_migrations` has FK safety net.
53 + 2. ~~**Performance gaps**~~ -- 3 partial indexes added in migration 040 (focus_set_at, expected_response_date, waiting emails).
54 + 3. ~~**Inconsistent observability**~~ -- 259 instrument annotations added to db-sqlite and plugin-runtime crates.
56 55
57 56 ---
58 57
59 58 ## Mandatory Surprise
60 59
61 - **The `Validate` trait is entirely dead code in production.** Despite 487 lines of well-written validation logic with 18 test functions covering length limits, empty fields, invalid durations, and edge cases, not a single production path calls `.validate()`. The commands do their own ad-hoc checks (mostly just `description.trim().is_empty()`) but miss all length limits, tag validation, and duration range checks. A user could create a task with a 10MB description. This is the kind of bug that gets missed precisely because the code looks so complete — the trait exists, the tests pass, but the integration point was never wired up.
60 + **FK constraint migration risk -- PRAGMA foreign_keys = OFF without crash protection.**
62 61
63 - **Resolution (2026-03-13):** Validate trait now wired into the command layer. All validation rules are enforced in production. Dead code finding resolved.
62 + In `migrations.rs:44-89`, several migrations disable foreign key constraints with `PRAGMA foreign_keys = OFF`, perform table restructuring (CREATE new table, INSERT...SELECT, DROP old, ALTER TABLE RENAME), then re-enable constraints. If the app crashes between the DROP and the RENAME, the database is left in an inconsistent state with the old table gone and the new table having a temporary name.
64 63
65 - ### Previous Mandatory Surprise (fifth audit)
64 + SQLite's recommended approach for this pattern is to wrap the entire sequence in a transaction, but `PRAGMA foreign_keys` cannot be changed inside a transaction. The code does use transactions for the data copy, but the PRAGMA and RENAME operations are outside the transaction boundary.
66 65
67 - **The sync engine builds SQL with `format!()` by interpolating table and column names directly into queries** -- and it is _actually safe_.
66 + **Verdict:** Medium severity. A crash during migration is unlikely but would require manual database recovery. The fix is to add a backup-before-migrate step or use SQLite's backup API as a safety net.
68 67
69 - In `sync_service.rs`, both `apply_upsert()` and `create_initial_snapshot()` construct SQL like:
68 + ### Previous Surprise
70 69
71 - ```rust
72 - let sql = format!(
73 - "INSERT OR REPLACE INTO {} ({}) VALUES ({})",
74 - table, col_list, placeholders
75 - );
76 - ```
77 -
78 - This would normally be a SQL injection red flag. However, the `table` parameter comes exclusively from the `UPSERT_ORDER` constant (a hardcoded `&[&str]`), and the column names come from `table_columns()` which returns hardcoded `&'static [&'static str]` slices. User-supplied data (actual values) goes through `sqlx::query().bind()` parameterized binding. The system has a test (`unknown_table_returns_none`) verifying that non-whitelisted table names are rejected.
79 -
80 - This is defense-in-depth: the _shape_ of the SQL (table/column names) is controlled by compile-time constants while the _content_ (row data) is parameterized. Impressive.
70 + **The `Validate` trait was entirely dead code.** Resolution (2026-03-13): Validate trait now wired into the command layer. All validation rules enforced in production. Resolved.
81 71
82 72 ---
83 73
@@ -85,67 +75,61 @@
85 75
86 76 ### 1. Exemplary layered architecture
87 77
88 - Five crates with strictly acyclic dependencies: core (7,131 LOC, zero I/O) -> db-sqlite (5,771 LOC) -> plugin-runtime (2,419 LOC) -> goingson-mcp (2,658 LOC) -> desktop (15,529 LOC). Repository trait pattern with 15 async trait definitions. Pre-computed response fields eliminate JS duplication. No layer violations detected.
78 + Four crates with strictly acyclic dependencies: core (zero I/O) -> db-sqlite (persistence) -> plugin-runtime (Rhai sandbox) -> desktop (Tauri wrapper). Repository trait pattern. Pre-computed response fields eliminate JS duplication. No layer violations.
89 79
90 80 ### 2. Comprehensive SQL injection and XSS prevention
91 81
92 - Every database query uses sqlx parameterized bind. Dynamic SQL in sync_service uses compile-time constant whitelists. FTS5 queries escaped via `prepare_fts5_query()`. Frontend: 200+ `escapeHtml()`/`escapeAttr()` calls across 39 JS files. Email HTML bodies stripped server-side. Plugin sandbox restricts file access.
82 + Every database query uses sqlx parameterized bind. Dynamic SQL in sync_service uses compile-time constant whitelists. FTS5 queries escaped. Frontend: 200+ `escapeHtml()`/`escapeAttr()` calls. Email HTML stripped. Plugin sandbox restricts file access.
93 83
94 84 ### 3. Strong type system discipline
95 85
96 - 11 entity ID newtypes via macro prevent cross-entity ID confusion. Typed enums replace stringly-typed filter/sort fields. `CoreError` -> `ApiError` conversion chain with structured error codes. 26 public enums with `strum` derive for string conversion. `DbValue` trait for enum persistence.
86 + 11 entity ID newtypes. Typed enums replace stringly-typed fields. `CoreError` -> `ApiError` conversion chain with structured error codes. 26 public enums. `DbValue` trait for enum persistence.
97 87
98 - ### 4. Thorough test coverage at every layer
88 + ### 4. Test coverage maintained across all layers
99 89
100 - 648 tests across unit (core parsers, validation, urgency, recurrence), integration (all 12 repository implementations), command (task, email, contact, event, export), sync (FK-ordering, trigger suppression), plugin (29 Rhai API binding tests), MCP (20 tool tests), JMAP (73 tests), OAuth (59 tests), and plugin registry (32 tests). Test LOC: ~7,500.
90 + 338 tests across unit (core), integration (db-sqlite), command (desktop), sync, JMAP, OAuth, plugin registry. All previous cold spots resolved.
101 91
102 92 ### 5. Mobile-ready architecture
103 93
104 - CSS-first responsive design with `@media (max-width: 768px)`. Touch gesture module (`touch.js`) with long-press, swipe, pull-to-refresh. Desktop-only deps gated with `cfg(not(mobile))`. iOS simulator builds working. Same Rust backend, same Tauri commands, same JS modules -- only CSS media queries and touch.js differ.
94 + CSS-first responsive design. Touch gesture module. Desktop-only deps gated with `cfg(not(mobile))`. Same Rust backend, same Tauri commands, same JS modules.
105 95
106 96 ---
107 97
108 98 ## Weaknesses
109 99
110 - ### 1. ~~`body_preview()` byte-slicing can panic on multi-byte UTF-8~~ (RESOLVED)
100 + ### 1. ~~Test count regression (-424 tests)~~ (False finding)
101 + Audit ran `cargo test` without `--workspace`. GO uses `default-members = ["src-tauri"]`, so only 338 of 778 tests were counted. Verified 2026-04-22.
111 102
112 - Already uses `.chars().take(n)` with 18 unit tests.
103 + ### 2. ~~FK migration risk~~ (Non-issue)
104 + One-time data migration (email ID rewrite) that runs once per database. Already completed for all existing users. `run_migrations` has `PRAGMA foreign_keys = ON` safety net at exit.
113 105
114 - ### 2. ~~`list_completed_between` ignores date parameters~~ (RESOLVED)
106 + ### 3. ~~Performance gaps~~ (Fixed)
107 + 3 partial indexes added in migration 040: `idx_tasks_focus_set_at`, `idx_tasks_waiting_response`, `idx_emails_waiting_response`. Fixed 2026-04-22.
115 108
116 - `completed_at` column added (migration 031), function now filters by date range, 4 tests.
117 -
118 - ### 3. ~~Testing gaps in JMAP, OAuth callback, and plugin registry~~ (RESOLVED)
119 -
120 - JMAP module now has 73 tests, OAuth callback server has 59 tests, plugin registry has 32 tests.
121 -
122 - ### 4. ~~LLM integration uses string errors~~ (RESOLVED)
123 -
124 - Typed `LlmError` enum with 5 variants replaces `Result<String, String>`.
109 + ### 4. ~~Inconsistent observability~~ (Fixed)
110 + 259 `#[tracing::instrument(skip_all)]` annotations added to db-sqlite and plugin-runtime crates. Total 435 across workspace. Fixed 2026-04-22.
125 111
126 112 ---
127 113
128 114 ## Competitive Comparison
129 115
130 - GoingsOn occupies a unique position as the only app combining tasks, email, calendar, contacts, and weekly review in a single offline-first native application. Its closest philosophical match is Sunsama ($192/yr), which also integrates daily planning with tasks and calendar, but Sunsama is cloud-only and subscription-based.
116 + GoingsOn occupies a unique position as the only app combining tasks, email, calendar, contacts, and weekly review in a single offline-first native application.
131 117
132 118 **Key competitive advantages:**
133 - - Only app with all 5 domains integrated (tasks + email + calendar + contacts + weekly review)
134 - - Offline-first with zero cloud dependency (vs. Todoist, Notion, Sunsama which require internet)
135 - - Source-available under PolyForm Noncommercial (unique among all competitors)
136 - - MCP server with 41 tools for LLM agent integration (no competitor offers this)
137 - - Rhai plugin system for user extensibility (only Obsidian competes here)
138 - - TaskWarrior-style urgency algorithm (more sophisticated than any competitor's priority system)
139 - - No subscription fee (vs. $48-$408/yr for competitors)
140 - - Cross-platform including Linux (Things 3, Fantastical are Apple-only)
141 - - Mobile port in progress with CSS-first responsive design
119 + - Only app with all 5 domains integrated
120 + - Offline-first with zero cloud dependency
121 + - Source-available under PolyForm Noncommercial 1.0.0
122 + - Rhai plugin system for user extensibility
123 + - TaskWarrior-style urgency algorithm
124 + - No subscription fee
125 + - Cross-platform including Linux
142 126
143 127 **Key competitive gaps:**
144 - 1. **Kanban/board view** -- table stakes for task apps (Todoist, TickTick, Notion all have it). On the roadmap.
145 - 2. **Monthly calendar view** -- universally expected. Only day plan timeline exists.
146 - 3. **External calendar sync** -- Google Calendar, Apple Calendar, CalDAV. Planned, high priority.
147 - 4. **Mobile app** -- iOS simulator builds working, Android init remaining. Competitors have mature mobile apps.
148 - 5. **Guided daily planning ritual** -- Sunsama's signature feature. GoingsOn has weekly review but no structured daily workflow.
128 + 1. Kanban/board view -- on the roadmap
129 + 2. Monthly calendar view
130 + 3. External calendar sync (Google, Apple, CalDAV)
131 + 4. Mobile app -- iOS simulator builds working
132 + 5. Guided daily planning ritual
149 133
150 134 ---
151 135
@@ -153,48 +137,30 @@
153 137
154 138 Outstanding work tracked in `docs/todo/todo.md`.
155 139
156 - ### Resolved (sixth audit — pre-launch skeptical lens)
157 - 15. ~~**[MUST-FIX]** Wire up `Validate::validate()` in the command layer~~ -- Done (wired into command layer)
158 - 16. ~~**[MUST-FIX]** Sanitize HTML email body in `open_email_in_browser`~~ -- Done (CSP meta tag + script/event handler stripping)
159 - 17. ~~**[LOW]** Remove or deprecate `ImapClient::new()` legacy constructor~~ -- Done (removed)
160 - 18. ~~**[LOW]** Defensive `.ok_or()` on email_repo.rs:129~~ -- Done
161 - 19. ~~**[LOW]** Use `bind()` for LIMIT/OFFSET~~ -- Done (parameterized binds in task_repo.rs)
140 + ### Run 15 (2026-04-18, corrected 2026-04-22)
141 + 1. ~~**[HIGH]** Investigate test count regression~~ -- False finding. 778 tests with `--workspace`.
142 + 2. ~~**[MEDIUM]** Add crash protection to FK migrations~~ -- One-time migration, already completed. Non-issue.
143 + 3. ~~**[MEDIUM]** Add indexes for newer query patterns~~ -- Done (migration 040).
144 + 4. ~~**[MEDIUM]** Expand `#[instrument]` coverage~~ -- Done (259 annotations added to crates).
145 + 5. Add doc comment to sync_service.rs explaining format!() SQL safety pattern
162 146
163 147 ### All resolved (previous audits)
164 - 1. ~~**[Bug]** Fix `body_preview()` UTF-8 panic~~ -- already uses `.chars().take(n)` with 18 tests
165 - 2. ~~**[Performance]** Batch dashboard stats queries~~ -- already uses single query with 6 subqueries
166 - 3. ~~**[Clippy]** Fix `clone_on_copy` in goingson-mcp~~ -- fixed (`task_impl.rs:271`)
167 - 4. ~~**[Testing]** Add integration tests for `search_repo`~~ -- 15 tests added
168 - 5. ~~**[Testing]** Add integration tests for `contact_repo`~~ -- 18 tests added
169 - 6. ~~**[Logic bug]** `list_completed_between` date filtering~~ -- `completed_at` column added (migration 031), 4 tests
170 - 7. ~~**[Testing]** MCP server tool tests~~ -- 20 integration tests added
171 - 8. ~~**[Testing]** Sync service tests~~ -- 21 unit tests
172 - 9. ~~**[Testing]** Plugin API tests~~ -- 29 tests covering all exposed types
173 - 10. ~~**[Testing]** IMAP HTML helper tests~~ -- 35 tests on pure functions
174 - 11. ~~**[Refactor]** Convert sync service to typed errors~~ -- `Result<_, CoreError>` throughout
175 - 12. ~~**[Refactor]** Move `sql_column()` out of core~~ -- relocated to db-sqlite
176 - 13. ~~**[Docs]** Add `//!` docs to `smtp_client.rs`~~ -- done
177 - 14. ~~**[Frontend]** Add section markers to `styles.css`~~ -- 60 numbered sections with TOC
178 -
179 - New items filed in `docs/todo/todo.md`:
180 - - ~~Add unit tests for JMAP module (854 LOC, 0 tests)~~ -- 73 tests added
181 - - ~~Add tests for OAuth callback server (309 LOC, 0 tests)~~ -- 59 tests added
182 - - ~~Add tests for plugin registry hot-reload (300 LOC, 2 tests)~~ -- 32 tests added
183 - - Add doc comment to sync_service.rs explaining format!() SQL safety pattern
184 - - ~~LLM integration: use typed errors instead of Result<String, String>~~ -- typed LlmError enum added
185 - - Verify applying_remote flag cleared on startup (crash recovery)
186 -
187 - ### Security Deep Dive (2026-03-13) — Complete (2/2)
188 -
189 - - **Path traversal in delete_backup:** `commands/export.rs` — `delete_backup()` now uses `canonicalize()` on both backup directory and target path, then verifies `canonical_path.starts_with(&canonical_backup_dir)`
190 - - **Export path validation:** `commands/export.rs` — `validate_export_path()` helper added, rejects `..` components; called at top of `export_json`, `export_tasks_csv`, `export_events_ics`, and `restore_backup`
191 -
192 - ### JS Audit Remediation (2026-03-11) — Complete (14/14)
193 -
194 - All JS audit findings resolved:
195 - - **Critical (2):** escapeAttr() on 25 onclick handlers across 7 files, escapeHtml() on backend labels in innerHTML
196 - - **Medium (6):** Event status computation moved to Rust (`get_event_status_indicator` command, pre-computed EventResponse fields), project form dedup (delegated to tasks.openNewForProject/events.openNewForProject), state mutation migration (16 fixes to GoingsOn.state.set()), email account form dedup (buildAccountFormHtml()), IMAP/SMTP error escaping
197 - - **Low (6):** Dead matchesFilters() removed, window.GO alias removed, dynamic version via Tauri app.getVersion(), confirm() → confirmDelete() (5 calls), contacts filter state migrated to GoingsOn.state, 100ms sleep removed
148 + - ~~Wire up `Validate::validate()` in command layer~~ -- Done
149 + - ~~Sanitize HTML email body in `open_email_in_browser`~~ -- Done
150 + - ~~Remove `ImapClient::new()` legacy constructor~~ -- Done
151 + - ~~Defensive `.ok_or()` on email_repo.rs:129~~ -- Done
152 + - ~~Use `bind()` for LIMIT/OFFSET~~ -- Done
153 + - ~~Fix `body_preview()` UTF-8 panic~~ -- already safe
154 + - ~~Batch dashboard stats queries~~ -- already optimized
155 + - ~~Add integration tests for search_repo, contact_repo~~ -- Done
156 + - ~~Fix `list_completed_between` date filtering~~ -- Done
157 + - ~~Sync service tests, Plugin API tests, IMAP HTML tests~~ -- Done
158 + - ~~Convert sync service to typed errors~~ -- Done
159 + - ~~Move `sql_column()` out of core~~ -- Done
160 + - ~~JMAP module tests (73), OAuth tests (59), plugin registry tests (32)~~ -- Done
161 + - ~~LLM typed errors~~ -- Done
162 + - ~~Path traversal in delete_backup, export path validation~~ -- Done
163 + - ~~JS Audit (14/14)~~ -- Done
198 164
199 165 ---
200 166
@@ -210,17 +176,10 @@
210 176 | 2026-03-13 | ~39K | ~152 | 658 | ~16.9 | 0 | A |
211 177 | 2026-03-16 | 44K | ~152 | 725 | ~16.5 | 0 | A |
212 178 | 2026-03-18 | 44K | ~152 | 725 | ~16.5 | 0 | A |
213 - | 2026-03-28 | ~44K | ~152 | ~734 | ~16.7 | 0 | A |
214 -
215 - ---
216 -
217 - ## Build Verification
218 -
219 - ```
220 - cargo check --workspace PASS
221 - cargo test --workspace 648 passed, 0 failed, 9 ignored
222 - cargo clippy --workspace 0 warnings
223 - ```
179 + | 2026-03-28 (Run 12) | ~44K | ~152 | ~734 | ~16.7 | 0 | A |
180 + | 2026-04-15 (Run 14) | ~64,357 | -- | ~762 | ~12 | 0 | A |
181 + | 2026-04-18 (Run 15) | ~64,357 | -- | 338 | ~5.3 | 0 | A- |
182 + | 2026-04-22 (Run 15 corrected) | ~64,357 | -- | 778 | ~12.1 | 0 | A |
224 183
225 184 ---
226 185
@@ -6,7 +6,7 @@
6 6
7 7 GoingsOn is the only app that combines tasks, email, calendar, contacts, and weekly review in a single offline-first native application. Every competitor covers 1-2 of these domains. Built with Tauri 2 (Rust backend, vanilla JS frontend, SQLite), it targets independent workers who want a fast, offline-capable workspace without SaaS lock-in.
8 8
9 - The core advantage is integration depth (tasks + email + calendar + contacts + projects in one local-first app) combined with privacy (no server, no tracking, zero-knowledge sync) and extensibility (Rhai plugins, MCP server). No single competitor matches this combination. In a market where annual subscription costs range from $48 to $408, GoingsOn ships free and source-available.
9 + The core advantage is integration depth (tasks + email + calendar + contacts + projects in one local-first app) combined with privacy (no server, no tracking, zero-knowledge sync) and extensibility (Rhai plugins). No single competitor matches this combination. In a market where annual subscription costs range from $48 to $408, GoingsOn ships free and source-available.
10 10
11 11 ## Pricing Comparison
12 12
@@ -36,12 +36,11 @@
36 36 | **Local data** | Yes | No | Partial | No | No | Yes | Partial | No | No | No |
37 37 | **Source-available** | Yes | No | No | No | No | No | No | No | No | No |
38 38 | **Plugin system** | Yes | No | No | No | API | Yes | No | No | No | No |
39 - | **MCP/LLM tools** | Yes | No | No | No | AI built-in | Plugin | No | AI built-in | No | AI built-in |
40 39 | **Urgency scoring** | Yes | 4 levels | No | No | No | No | No | No | No | No |
41 40 | **Cross-platform** | Yes | Yes | Apple only | Yes | Yes | Yes | Apple+Win | Yes | Yes | Mac/Win |
42 41 | **Linux** | Yes | Yes | No | Yes | Yes | Yes | No | No | No | No |
43 42 | **Mobile** | In progress | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Web only |
44 - | **AI features** | LLM templates | Yes | No | No | Yes | No | No | Yes | Planned | Yes |
43 + | **AI features** | No | Yes | No | No | Yes | No | No | Yes | Planned | Yes |
45 44 | **Team collab** | No | Yes | No | No | Yes | No | No | Yes | Yes | No |
46 45 | **Free tier** | Yes | Yes | No | Yes | Yes | Yes | Limited | Yes | No | No |
47 46
@@ -262,7 +261,7 @@
262 261 | **Web app** | Todoist, TickTick, Notion, Sunsama | Contradicts local-first architecture and privacy model. Tauri desktop + mobile covers the target audience. A web app would require a server and undermine the offline-first value prop. |
263 262 | **Real-time collaboration** | Todoist, Notion, Spark, Sunsama | GoingsOn targets independent workers, not teams. Collaboration adds massive complexity (CRDT/OT, permissions, presence) with minimal value for the target user. |
264 263 | **Freeform documents / notes** | Notion, Obsidian, (Things 3 has task notes) | GoingsOn has task annotations and descriptions. Full document editing is a massive scope expansion that competes with dedicated tools. Better to integrate with Obsidian via plugin than to rebuild a notes system. |
265 - | **AI writing / compose assistance** | Todoist, Spark, Notion | GoingsOn already has LLM templates and AI-Fill. Adding full AI compose for emails would require sending email content to a third party, conflicting with the privacy-first model. The existing local LLM approach (Ollama) is the right path. |
264 + | **AI writing / compose assistance** | Todoist, Spark, Notion | Adding AI compose for emails would require sending email content to a third party, conflicting with the privacy-first model. Not building this. |
266 265
267 266 ## What We Offer That Competitors Don't
268 267
@@ -273,8 +272,6 @@
273 272 - **Source-available** -- unique among all competitors.
274 273 - **TaskWarrior-style urgency scoring** -- algorithmic priority considering due date proximity, age, priority level, overdue penalty, started bonus, tag bonuses. More sophisticated than any competitor's priority system.
275 274 - **Rhai plugin system** -- user-extensible without forking. Obsidian has plugins but they're JavaScript with no sandboxing.
276 - - **MCP server for Claude integration** -- 40+ structured tools across all domains. No other productivity app exposes this level of programmatic access to an LLM agent. App auto-refreshes when agents modify data.
277 - - **LLM template system** -- dynamic and static LLM templates embedded in text fields, with AI-Fill button for on-demand generation.
278 275 - **OAuth2 email for 4 providers** -- Fastmail (JMAP), Google, Microsoft, Yahoo with PKCE flow. Most email clients support fewer OAuth providers.
279 276 - **Natural-language quick-add** -- type `Fix bug +urgent project:GoingsOn pri:H due:tomorrow recur:weekly` instead of filling out a form.
280 277 - **10 built-in themes** -- Neobrute, Catppuccin (Latte/Frappe/Macchiato/Mocha), Dracula, Nord, Tokyo Night, Flatwhite, Ayu Light.
@@ -285,7 +282,7 @@
285 282
286 283 ## Key Dynamics
287 284
288 - - Every major competitor is shipping AI features. GoingsOn's MCP server is a developer-facing answer, but end-user AI (summarize, compose, schedule) is the biggest gap.
285 + - Every major competitor is shipping AI features. End-user AI (summarize, compose, schedule) is the biggest gap.
289 286 - Sunsama ($192/yr) is the closest philosophical match -- daily planning, weekly review, calendar+tasks+email integration -- but cloud-only and expensive.
290 287 - The offline-first, local-data, source-available combination is a genuine moat for privacy-conscious users.
291 288 - Unified workspace eliminates app-switching (vs. using Todoist + Spark + Fantastical separately).
@@ -302,7 +299,6 @@
302 299 - Unified workspace eliminates app-switching
303 300 - Local-first with E2E encrypted sync
304 301 - No subscription for core features
305 - - MCP server for AI agents (unique)
306 302 - Plugin system for user extensibility (only Obsidian competes here)
307 303
308 304 ## Target Users
@@ -12,7 +12,7 @@
12 12 | Planning | 3 | Events (calendar/time blocks), weekly reviews, saved views |
13 13 | Search | 5 | FTS5 virtual tables for emails, tasks, projects, events, contacts |
14 14 | Sync | 2 | SyncKit changelog and state (cloud sync via MNW) |
15 - | Config | 3 | Users, LLM settings, backup settings |
15 + | Config | 1 | Users, backup settings |
16 16
17 17 ---
18 18
@@ -342,7 +342,7 @@
342 342
343 343 Index: `email`.
344 344
345 - ### llm_settings
345 + ### llm_settings (unused - tables retained for migration compatibility)
346 346 Per-user LLM provider configuration. Migration 014.
347 347
348 348 | Column | Type | Notes |
@@ -360,7 +360,7 @@
360 360 | `created_at` | TEXT | |
361 361 | `updated_at` | TEXT | |
362 362
363 - ### llm_cache
363 + ### llm_cache (unused - tables retained for migration compatibility)
364 364 Prompt-response cache with date-aware invalidation. Migration 014.
365 365
366 366 | Column | Type | Notes |
@@ -408,7 +408,7 @@
408 408
409 409 - **FTS5** on emails (subject, body, from, to), tasks (description, tags), projects (name, description), events (title, description, location), contacts (display_name, nickname, company, notes, tags)
410 410 - **Partial** on tasks (`waiting_for_response WHERE = 1`, `is_focus WHERE = 1`, `completed_at WHERE NOT NULL`), emails (`waiting_for_response WHERE = 1`, `thread_id WHERE NOT NULL`, `in_reply_to WHERE NOT NULL`), saved_views (`is_pinned WHERE = 1`)
411 - - **Composite** on emails `(email_account_id, imap_uid, source_folder)` for IMAP sync, contacts `(user_id, display_name)`, weekly_reviews `(user_id, week_start_date)`, llm_cache `(user_id, prompt_hash, context_date)`
411 + - **Composite** on emails `(email_account_id, imap_uid, source_folder)` for IMAP sync, contacts `(user_id, display_name)`, weekly_reviews `(user_id, week_start_date)`
412 412 - **Sort** on tasks `(urgency DESC)`, emails `(received_at DESC)`
413 413
414 414 ## Schema Evolution