Skip to main content

max / goingson

Strip historical narrative from documentation Remove what a doc used to say, when it changed, the incidents that justified a rule, finished migration narration, and counts and versions that rot. State the rules in the present tense instead. Keep every instruction, prohibition and threshold, and keep the measurements that make a rule actionable. Public-facing docs keep their explanatory voice.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01MptwXZ8k65v19rFmdGAyki
Author: Max Johnson <me@maxj.phd> · 2026-08-31 01:51 UTC
Signed with PGP, not checked
Commit: 4c7f9530f45d0d3e8677983ce7ccd71948793ca3
Parent: f589329
70 files changed, +1069 insertions, -2255 deletions
@@ -30,7 +30,7 @@
30 30 | Business logic (urgency, recurrence) | Say structure, and decline to say position |
31 31 | Aggregations and statistics | Leave depth, colour and spacing to the design system |
32 32
33 - Both halves are Rust now. The split is between the domain crates and
33 + Both halves are Rust. The split is between the domain crates and
34 34 `src-tauri/src/quasi/`, not between two languages.
35 35
36 36 ### Pre-compute Display Values
@@ -1,6 +1,6 @@
1 1 # GoingsOn Database Schema
2 2
3 - SQLite schema reference. 50 migrations. Migrations live in `migrations/sqlite/` and auto-run on first launch.
3 + SQLite schema reference. Migrations live in `migrations/sqlite/` and auto-run on first launch.
4 4
5 5 ## Domain Map
6 6
@@ -24,7 +24,7 @@
24 24 | Column | Type | Notes |
25 25 |--------|------|-------|
26 26 | `id` | TEXT PK | |
27 - | `user_id` | TEXT FK | -> users (CASCADE). Added migration 002 |
27 + | `user_id` | TEXT FK | -> users (CASCADE) |
28 28 | `name` | TEXT | NOT NULL |
29 29 | `description` | TEXT | Default '' |
30 30 | `project_type` | TEXT | Default 'Other' |
@@ -39,7 +39,7 @@
39 39 | Column | Type | Notes |
40 40 |--------|------|-------|
41 41 | `id` | TEXT PK | |
42 - | `user_id` | TEXT FK | -> users (CASCADE). Migration 002 |
42 + | `user_id` | TEXT FK | -> users (CASCADE) |
43 43 | `project_id` | TEXT FK | -> projects (SET NULL) |
44 44 | `description` | TEXT | NOT NULL |
45 45 | `status` | TEXT | Default 'Pending' |
@@ -48,19 +48,19 @@
48 48 | `tags` | TEXT | JSON array string, default '[]' |
49 49 | `urgency` | REAL | Computed score, default 0.0 |
50 50 | `recurrence` | TEXT | Default 'None' |
51 - | `recurrence_parent_id` | TEXT FK | -> tasks (SET NULL). Migration 003 |
52 - | `source_email_id` | TEXT FK | -> emails (SET NULL). Migration 009 |
53 - | `snoozed_until` | TEXT | Migration 010 |
54 - | `waiting_for_response` | INTEGER | Boolean, default 0. Migration 012 |
55 - | `waiting_since` | TEXT | Migration 012 |
56 - | `expected_response_date` | TEXT | Migration 012 |
57 - | `scheduled_start` | TEXT | Migration 013 |
58 - | `scheduled_duration` | INTEGER | Minutes. Migration 013 |
59 - | `is_focus` | INTEGER | Boolean, default 0. Migration 018 |
60 - | `focus_set_at` | TEXT | Migration 018 |
61 - | `contact_id` | TEXT FK | -> contacts (SET NULL). Migration 025 |
62 - | `milestone_id` | TEXT FK | -> milestones (SET NULL). Migration 028 |
63 - | `completed_at` | TEXT | Backfilled from created_at. Migration 031 |
51 + | `recurrence_parent_id` | TEXT FK | -> tasks (SET NULL) |
52 + | `source_email_id` | TEXT FK | -> emails (SET NULL) |
53 + | `snoozed_until` | TEXT | |
54 + | `waiting_for_response` | INTEGER | Boolean, default 0 |
55 + | `waiting_since` | TEXT | |
56 + | `expected_response_date` | TEXT | |
57 + | `scheduled_start` | TEXT | |
58 + | `scheduled_duration` | INTEGER | Minutes |
59 + | `is_focus` | INTEGER | Boolean, default 0 |
60 + | `focus_set_at` | TEXT | |
61 + | `contact_id` | TEXT FK | -> contacts (SET NULL) |
62 + | `milestone_id` | TEXT FK | -> milestones (SET NULL) |
63 + | `completed_at` | TEXT | |
64 64 | `created_at` | TEXT | `datetime('now')` |
65 65
66 66 Indexes: `project_id`, `status`, `priority`, `due`, `urgency DESC`, `user_id`, `recurrence_parent_id`, `source_email_id`, `snoozed_until`, `scheduled_start`, `contact_id`. Partial indexes: `waiting_for_response WHERE = 1`, `is_focus WHERE = 1`, `completed_at WHERE NOT NULL`.
@@ -75,7 +75,7 @@
75 75 | `text` | TEXT | NOT NULL |
76 76 | `is_completed` | INTEGER | Boolean, default 0 |
77 77 | `position` | INTEGER | Sort order, default 0 |
78 - | `linked_task_id` | TEXT FK | -> tasks (SET NULL). Migration 022 |
78 + | `linked_task_id` | TEXT FK | -> tasks (SET NULL) |
79 79 | `created_at` | TEXT | `datetime('now')` |
80 80
81 81 Indexes: `task_id`, `linked_task_id`.
@@ -93,7 +93,7 @@
93 93 Index: `task_id`.
94 94
95 95 ### milestones
96 - Scope boundaries within a project. "When these tasks are done, ship it." Migration 028.
96 + Scope boundaries within a project. "When these tasks are done, ship it."
97 97
98 98 | Column | Type | Notes |
99 99 |--------|------|-------|
@@ -114,7 +114,7 @@
114 114 ## Email
115 115
116 116 ### email_accounts
117 - IMAP/SMTP and OAuth2/JMAP account configuration. Credentials excluded from sync (migration 033).
117 + IMAP/SMTP and OAuth2/JMAP account configuration. Credentials excluded from sync.
118 118
119 119 | Column | Type | Notes |
120 120 |--------|------|-------|
@@ -129,14 +129,14 @@
129 129 | `username` | TEXT | |
130 130 | `password` | TEXT | IMAP/SMTP credential |
131 131 | `use_tls` | INTEGER | Boolean, default 1 |
132 - | `auth_type` | TEXT | 'password' or 'oauth2_fastmail'. Migration 017 |
133 - | `oauth2_access_token` | TEXT | Migration 017 |
134 - | `oauth2_refresh_token` | TEXT | Migration 017 |
135 - | `oauth2_token_expires_at` | TEXT | Migration 017 |
136 - | `jmap_session_url` | TEXT | Fastmail JMAP. Migration 017 |
137 - | `jmap_account_id` | TEXT | Migration 017 |
138 - | `archive_folder_name` | TEXT | Default 'Archive'. Migration 006 |
139 - | `sync_interval_minutes` | INTEGER | Default 15, NULL = disabled. Migration 021 |
132 + | `auth_type` | TEXT | 'password' or 'oauth2_fastmail' |
133 + | `oauth2_access_token` | TEXT | |
134 + | `oauth2_refresh_token` | TEXT | |
135 + | `oauth2_token_expires_at` | TEXT | |
136 + | `jmap_session_url` | TEXT | Fastmail JMAP |
137 + | `jmap_account_id` | TEXT | |
138 + | `archive_folder_name` | TEXT | Default 'Archive' |
139 + | `sync_interval_minutes` | INTEGER | Default 15, NULL = disabled |
140 140 | `last_sync_at` | TEXT | Device-local, excluded from sync |
141 141 | `created_at` | TEXT | `datetime('now')` |
142 142
@@ -148,26 +148,26 @@
148 148 | Column | Type | Notes |
149 149 |--------|------|-------|
150 150 | `id` | TEXT PK | |
151 - | `user_id` | TEXT FK | -> users (CASCADE). Migration 002 |
151 + | `user_id` | TEXT FK | -> users (CASCADE) |
152 152 | `project_id` | TEXT FK | -> projects (SET NULL) |
153 - | `email_account_id` | TEXT FK | -> email_accounts (SET NULL). Migration 004 |
153 + | `email_account_id` | TEXT FK | -> email_accounts (SET NULL) |
154 154 | `from_address` | TEXT | |
155 155 | `to_address` | TEXT | |
156 156 | `subject` | TEXT | |
157 157 | `body` | TEXT | Plain text, default '' |
158 - | `html_body` | TEXT | Original HTML for browser view. Migration 020 |
159 - | `message_id` | TEXT | RFC 2822 Message-ID. Migration 004 |
160 - | `in_reply_to` | TEXT | Threading header. Migration 016 |
161 - | `thread_id` | TEXT | Conversation grouping. Migration 016 |
158 + | `html_body` | TEXT | Original HTML for browser view |
159 + | `message_id` | TEXT | RFC 2822 Message-ID |
160 + | `in_reply_to` | TEXT | Threading header |
161 + | `thread_id` | TEXT | Conversation grouping |
162 162 | `is_read` | INTEGER | Boolean, default 0 |
163 - | `is_archived` | INTEGER | Boolean, default 0. Migration 005 |
164 - | `is_outgoing` | INTEGER | Boolean, default 0. Migration 004 |
165 - | `imap_uid` | INTEGER | IMAP UID for sync. Migration 006 |
166 - | `source_folder` | TEXT | IMAP folder. Migration 006 |
167 - | `snoozed_until` | TEXT | Migration 010 |
168 - | `waiting_for_response` | INTEGER | Boolean, default 0. Migration 012 |
169 - | `waiting_since` | TEXT | Migration 012 |
170 - | `expected_response_date` | TEXT | Migration 012 |
163 + | `is_archived` | INTEGER | Boolean, default 0 |
164 + | `is_outgoing` | INTEGER | Boolean, default 0 |
165 + | `imap_uid` | INTEGER | IMAP UID for sync |
166 + | `source_folder` | TEXT | IMAP folder |
167 + | `snoozed_until` | TEXT | |
168 + | `waiting_for_response` | INTEGER | Boolean, default 0 |
169 + | `waiting_since` | TEXT | |
170 + | `expected_response_date` | TEXT | |
171 171 | `received_at` | TEXT | `datetime('now')` |
172 172
173 173 Indexes: `project_id`, `is_read`, `received_at DESC`, `user_id`, `message_id`, `email_account_id`, `(email_account_id, imap_uid, source_folder)`, `is_archived`, `snoozed_until`. Partial indexes: `thread_id WHERE NOT NULL`, `in_reply_to WHERE NOT NULL`, `waiting_for_response WHERE = 1`.
@@ -177,7 +177,7 @@
177 177 ## Contacts
178 178
179 179 ### contacts
180 - People with structured metadata. Migration 023.
180 + People with structured metadata.
181 181
182 182 | Column | Type | Notes |
183 183 |--------|------|-------|
@@ -197,28 +197,28 @@
197 197 Indexes: `user_id`, `(user_id, display_name)`.
198 198
199 199 ### contact_emails
200 - Multiple email addresses per contact. Migration 023.
200 + Multiple email addresses per contact.
201 201
202 202 - FK `contact_id` -> contacts (CASCADE)
203 203 - `address`, `label` (default ''), `is_primary` (boolean)
204 204 - Indexes: `contact_id`, `address`
205 205
206 206 ### contact_phones
207 - Multiple phone numbers per contact. Migration 023.
207 + Multiple phone numbers per contact.
208 208
209 209 - FK `contact_id` -> contacts (CASCADE)
210 210 - `number`, `label` (default ''), `is_primary` (boolean)
211 211 - Index: `contact_id`
212 212
213 213 ### contact_social_handles
214 - Social platform handles per contact. Migration 023.
214 + Social platform handles per contact.
215 215
216 216 - FK `contact_id` -> contacts (CASCADE)
217 217 - `platform`, `handle`, `url` (optional)
218 218 - Index: `contact_id`
219 219
220 220 ### contact_custom_fields
221 - Arbitrary label/value pairs with optional URL. Migration 024.
221 + Arbitrary label/value pairs with optional URL.
222 222
223 223 - FK `contact_id` -> contacts (CASCADE)
224 224 - `label`, `value`, `url` (optional)
@@ -234,23 +234,23 @@
234 234 | Column | Type | Notes |
235 235 |--------|------|-------|
236 236 | `id` | TEXT PK | |
237 - | `user_id` | TEXT FK | -> users (CASCADE). Migration 002 |
237 + | `user_id` | TEXT FK | -> users (CASCADE) |
238 238 | `project_id` | TEXT FK | -> projects (SET NULL) |
239 239 | `title` | TEXT | NOT NULL |
240 240 | `description` | TEXT | Default '' |
241 241 | `start_time` | TEXT | ISO datetime |
242 242 | `end_time` | TEXT | Nullable |
243 243 | `location` | TEXT | |
244 - | `linked_task_id` | TEXT FK | -> tasks (SET NULL). Migration 008 |
245 - | `recurrence` | TEXT | Default 'None'. Migration 008 |
246 - | `recurrence_parent_id` | TEXT FK | -> events (SET NULL). Migration 008 |
247 - | `contact_id` | TEXT FK | -> contacts (SET NULL). Migration 025 |
248 - | `block_type` | TEXT | Time block category. Migration 026 |
244 + | `linked_task_id` | TEXT FK | -> tasks (SET NULL) |
245 + | `recurrence` | TEXT | Default 'None' |
246 + | `recurrence_parent_id` | TEXT FK | -> events (SET NULL) |
247 + | `contact_id` | TEXT FK | -> contacts (SET NULL) |
248 + | `block_type` | TEXT | Time block category |
249 249
250 250 Indexes: `project_id`, `start_time`, `user_id`, `linked_task_id`, `recurrence_parent_id`.
251 251
252 252 ### weekly_reviews
253 - Weekly review completion tracking. Migration 018.
253 + Weekly review completion tracking.
254 254
255 255 | Column | Type | Notes |
256 256 |--------|------|-------|
@@ -259,12 +259,12 @@
259 259 | `week_start_date` | TEXT | ISO week Monday (YYYY-MM-DD) |
260 260 | `completed_at` | TEXT | `datetime('now')` |
261 261 | `notes` | TEXT | Default '' |
262 - | `vacation_days` | TEXT | Comma-separated day indices. Migration 027 |
262 + | `vacation_days` | TEXT | Comma-separated day indices |
263 263
264 264 Unique: `(user_id, week_start_date)`. Index: `(user_id, week_start_date)`.
265 265
266 266 ### saved_views
267 - Persisted filter/sort configurations for quick access. Migration 015.
267 + Persisted filter/sort configurations for quick access.
268 268
269 269 | Column | Type | Notes |
270 270 |--------|------|-------|
@@ -286,7 +286,7 @@
286 286
287 287 ## Search
288 288
289 - FTS5 virtual tables with automatic sync triggers (INSERT/UPDATE/DELETE). Created in migration 011, contacts FTS in migration 023.
289 + FTS5 virtual tables with automatic sync triggers (INSERT/UPDATE/DELETE).
290 290
291 291 | FTS Table | Source Table | Indexed Columns |
292 292 |-----------|-------------|-----------------|
@@ -303,12 +303,12 @@
303 303 ## Sync
304 304
305 305 ### sync_state
306 - Key-value store for SyncKit configuration and state. Migration 030.
306 + Key-value store for SyncKit configuration and state.
307 307
308 308 Seeded keys: `device_id`, `pull_cursor`, `auto_sync_enabled`, `sync_interval_minutes`, `applying_remote`, `last_sync_at`, `initial_snapshot_done`.
309 309
310 310 ### sync_changelog
311 - Local change log captured by triggers, pushed to MNW server. Migration 030.
311 + Local change log captured by triggers, pushed to MNW server.
312 312
313 313 | Column | Type | Notes |
314 314 |--------|------|-------|
@@ -322,7 +322,7 @@
322 322
323 323 Partial index: `pushed WHERE pushed = 0`.
324 324
325 - Sync triggers exist on 11 tables: projects, tasks, events, contacts, contact_emails, contact_phones, contact_social_handles, contact_custom_fields, annotations, subtasks, milestones. Each trigger checks the `applying_remote` flag to prevent echo during pull. Email accounts sync config only (credentials excluded per migration 033).
325 + Sync triggers exist on 11 tables: projects, tasks, events, contacts, contact_emails, contact_phones, contact_social_handles, contact_custom_fields, annotations, subtasks, milestones. Each trigger checks the `applying_remote` flag to prevent echo during pull. Email accounts sync config only (credentials excluded).
326 326
327 327 ---
328 328
@@ -343,7 +343,7 @@
343 343 Index: `email`.
344 344
345 345 ### llm_settings (unused - tables retained for migration compatibility)
346 - Per-user LLM provider configuration. Migration 014.
346 + Per-user LLM provider configuration.
347 347
348 348 | Column | Type | Notes |
349 349 |--------|------|-------|
@@ -361,7 +361,7 @@
361 361 | `updated_at` | TEXT | |
362 362
363 363 ### llm_cache (unused - tables retained for migration compatibility)
364 - Prompt-response cache with date-aware invalidation. Migration 014.
364 + Prompt-response cache with date-aware invalidation.
365 365
366 366 | Column | Type | Notes |
367 367 |--------|------|-------|
@@ -376,15 +376,15 @@
376 376 Unique: `(user_id, prompt_hash, context_date)`. Index: `(user_id, prompt_hash, context_date)`.
377 377
378 378 ### backup_settings
379 - Automated local backup configuration. Migration 019, revised migration 029.
379 + Automated local backup configuration.
380 380
381 381 | Column | Type | Notes |
382 382 |--------|------|-------|
383 383 | `id` | TEXT PK | |
384 384 | `user_id` | TEXT FK | -> users. Unique |
385 385 | `auto_backup_enabled` | INTEGER | Boolean, default 1 |
386 - | `backup_frequency_minutes` | INTEGER | Default 15 (was hours, converted in 029) |
387 - | `max_backups_to_keep` | INTEGER | Default 1 (was 7, changed in 029) |
386 + | `backup_frequency_minutes` | INTEGER | Default 15 |
387 + | `max_backups_to_keep` | INTEGER | Default 1 |
388 388 | `last_backup_at` | TEXT | |
389 389 | `created_at` | TEXT | |
390 390 | `updated_at` | TEXT | |
@@ -399,7 +399,7 @@
399 399 - **Boolean as INTEGER:** SQLite has no native boolean; 0/1 INTEGER used throughout
400 400 - **FTS5 content tables:** Virtual tables with `content=` pointing to source table; triggers keep indexes in sync
401 401 - **Sync echo prevention:** All sync triggers check `sync_state.applying_remote` to avoid logging changes pulled from the server
402 - - **Credential isolation:** Email account sync triggers exclude passwords and OAuth tokens (migration 033)
402 + - **Credential isolation:** Email account sync triggers exclude passwords and OAuth tokens
403 403 - **Recurrence via parent links:** Both tasks and events use `recurrence_parent_id` self-referential FK for recurring series
404 404 - **Multi-entity linking:** Tasks can link to emails (`source_email_id`), contacts (`contact_id`), milestones (`milestone_id`), and events (`linked_task_id` on events)
405 405 - **Subtask-to-task links:** Subtasks can reference another full task via `linked_task_id` for multi-phase work
@@ -410,39 +410,3 @@
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 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 -
414 - ## Schema Evolution
415 -
416 - Major milestones across 50 migrations:
417 -
418 - | Migration | Change |
419 - |-----------|--------|
420 - | 001 | Initial schema (projects, tasks, annotations, events, emails) |
421 - | 002 | Users table, user_id FK on all data tables |
422 - | 003 | Task recurrence parent links |
423 - | 004 | Email accounts (IMAP/SMTP config), email message tracking |
424 - | 007 | Subtasks (checkbox items on tasks) |
425 - | 008 | Task-event linking, event recurrence |
426 - | 009 | Email-to-task linking |
427 - | 010 | Snooze/defer for tasks and emails |
428 - | 011 | FTS5 full-text search (emails, tasks, projects, events) |
429 - | 012 | Follow-up / waiting-for tracking |
430 - | 013 | Time blocking (scheduled_start, scheduled_duration) |
431 - | 014 | LLM settings and prompt cache |
432 - | 015 | Saved views / filters |
433 - | 016 | Email threading (in_reply_to, thread_id) |
434 - | 017 | OAuth2/JMAP support for email accounts (Fastmail) |
435 - | 018 | Weekly reviews, task focus mode |
436 - | 019 | Backup settings |
437 - | 022 | Subtask-to-task links (multi-phase work) |
438 - | 023 | Contacts (people, emails, phones, social handles, FTS) |
439 - | 024 | Contact custom fields |
440 - | 025 | Contact linking to tasks and events |
441 - | 026 | Time block types on events |
442 - | 027 | Vacation days on weekly reviews |
443 - | 028 | Milestones (scope boundaries within projects) |
444 - | 029 | Backup frequency: hours to minutes |
445 - | 030 | SyncKit changelog + state + 33 sync triggers (11 tables) |
446 - | 031 | completed_at on tasks, updated sync triggers |
447 - | 032 | Email account sync triggers |
448 - | 033 | Credential isolation (exclude passwords/tokens from sync) |
M src-tauri/build.rs +26 -80
@@ -60,42 +60,19 @@
60 60
61 61 /// Which of this app's stylesheet rules match something the app can emit.
62 62 ///
63 - /// goingson `43a682b0`, and the restoration of `check_vocabulary_use`.
63 + /// This points at the stylesheet and asks what it styles, because the set of
64 + /// emittable classes is knowable: `quasi_webview::vocabulary::covers` is every
65 + /// class a document served by this app can contain, and it is **closed**. A
66 + /// description has no word for a class -- `src/quasi/` names zero of them -- so
67 + /// nothing but the two renderer crates can put one in the document.
64 68 ///
65 - /// # What was lost, and why it could not be patched
66 - ///
67 - /// That check asked "which generated classes does no markup emit" and answered
68 - /// it by reading `index.html`, `compose.html` and every file under
69 - /// `frontend/js/`. The 2026-08-22 swap deleted all of them. The markup is
70 - /// `quasi-webview`'s emitter now, in another crate, so this repo cannot see its
71 - /// own document.
72 - ///
73 - /// Raising `DEAD_VOCABULARY_HIGH_WATER` from 42 to 73 would have made the build
74 - /// green and would have reported clean about a 9,830-line stylesheet nobody is
75 - /// reading. So the check was left out until the question could be asked
76 - /// properly, which is what this is.
77 - ///
78 - /// # Asked of the other side, which is what makes it exact
79 - ///
80 - /// The old check pointed at the markup and asked what it used. This points at
81 - /// the stylesheet and asks what it styles, because the set of emittable classes
82 - /// is now knowable: `quasi_webview::vocabulary::covers` is every class a
83 - /// document served by this app can contain, and it is **closed**. A description
84 - /// has no word for a class -- `src/quasi/` names zero of them -- so nothing but
85 - /// the two renderer crates can put one in the document.
86 - ///
87 - /// That closure is the whole value. A selector outside the set is dead, not
88 - /// merely unaccounted for, and a build that says "1,731 dead" is saying
89 - /// something a person can act on rather than something they have to go and
90 - /// check.
69 + /// That closure is the whole value: a selector outside the set is dead rather
70 + /// than merely unaccounted for.
91 71 ///
92 72 /// # No seal
93 73 ///
94 - /// This carried a `DEAD_SELECTOR_HIGH_WATER` of 1,021 when it was written,
95 - /// because the cut was `daac5cc7`'s job and not this one's. That cut landed on
96 - /// 2026-08-22 and took the count to zero, so the high water went with it: the
97 - /// list has to be empty, and a rule written against markup nothing emits fails
98 - /// the build where it is added.
74 + /// The dead list has to be empty, so a rule written against markup nothing
75 + /// emits fails the build where it is added.
99 76 ///
100 77 /// A failure here reads two ways and the message cannot tell them apart. Either
101 78 /// the rule is dead, or quasi-webview stopped emitting a class it used to and
@@ -147,10 +124,8 @@
147 124 /// What there is instead is the tree. An id in a selector that appears in no
148 125 /// source file under `src/` cannot be emitted, because the name would have to
149 126 /// be written down somewhere for the renderer to be handed it. Coarser than the
150 - /// class question and exact enough for the failure it is here to catch: after
151 - /// the swap this sheet still carried 16 id rules from the deleted `index.html`
152 - /// (`#tasks-view`, `#task-kanban-board`, `#timeline-slots` and the rest), and
153 - /// the class check could not see one of them.
127 + /// class question, and exact enough to catch an id rule the class check cannot
128 + /// see at all.
154 129 ///
155 130 /// A substring match, deliberately. `#task-pagination` is dead if the string
156 131 /// `task-pagination` is nowhere in the tree, and a name that appears inside a
@@ -185,9 +160,8 @@
185 160
186 161 /// Nothing in the sheets declares a name the sheets never read.
187 162 ///
188 - /// The two remaining ways a rule can be dead without naming a class, and the
189 - /// two the swap left behind: four `@keyframes` whose `animation` went with the
190 - /// SPA, and a `--timeline-slot-h` the day-plan scripts read.
163 + /// The two ways a rule can be dead without naming a class: an `@keyframes`
164 + /// nothing animates, and a custom property nothing reads.
191 165 ///
192 166 /// # Why the whole set is read at once
193 167 ///
@@ -502,12 +476,6 @@
502 476 out
503 477 }
504 478
505 - // `markup_files` stood here until 2026-08-22. It gathered the files this app's
506 - // markup lived in, and after the swap the only thing left for it to gather was
507 - // the generated scripts quasi-webview writes into `frontend/js/`. Those are
508 - // that crate's, and `check_stylesheet_reaches_markup` reads that crate's
509 - // vocabulary directly, so the indirection had nothing left in it.
510 -
511 479 /// Widths that are tuning inside the wide shell, not a shell boundary.
512 480 ///
513 481 /// A shell boundary is a [`SizeClass`] edge and belongs to makeover-geometry.
@@ -526,31 +494,17 @@
526 494 // answer to state.
527 495 //
528 496 // Reglo is the third token and it is here rather than in styles.css
529 - // because that is what layer 0 is for. It used to be a hand-written
530 - // `@font-face` plus a `--font-display` this repository defined for itself;
531 - // declared as an override it is the same shape as the two house slots. The
532 - // face is committed rather than cut: it is upstream's, unmodified, and
533 - // quasi-type has no part in it.
497 + // because that is what layer 0 is for. Declared as an override it is the
498 + // same shape as the two house slots. The face is committed rather than
499 + // cut: it is upstream's, unmodified, and quasi-type has no part in it.
534 500 //
535 501 // Note this app's `--font-heading` is an alias of sans and is NOT the
536 - // brand slot, unlike the MNW server's, which was renamed to
537 - // `--font-display` in the same programme so one name means one thing
538 - // tree-wide.
502 + // brand slot.
539 503 makeover_build::tauri_frontend_with(
540 504 env!("CARGO_MANIFEST_DIR"),
541 505 &makeover_build::Emit::default(),
542 - // No manual override, and this is settled rather than pending: Max
543 - // ruled it 2026-08-22 (goingson `c3d15a83`). Density is a question
544 - // about the viewport and the app answers it.
545 - //
546 - // This was `Some(".ui-mode-mobile")` until then, which had
547 - // makeover-geometry generate a touch-density override keyed on that
548 - // class. `js/bootstrap-uimode.js` set it from `?ui=mobile` or
549 - // `goingson.uiMode`, and that script went with the SPA, so the block
550 - // had been styling a class nothing could put in the document. The
551 - // escape hatch existed because the old detection sniffed user agents
552 - // and was sometimes wrong; the width condition is not wrong in that
553 - // way, so there is nothing left for a hatch to escape.
506 + // No manual override. Density is a question about the viewport and
507 + // the app answers it; the width condition needs no escape hatch.
554 508 None,
555 509 &makeover_build::Typography::house("../fonts").with_override(
556 510 makeover_build::FontOverride::new(
@@ -587,10 +541,7 @@
587 541 )
588 542 .expect("cut the house faces");
589 543
590 - // `css/tables.css`, `tables.columns.json` and `js/tables.generated.js` were
591 - // written here until 2026-08-22, out of a `TABLES` const holding the three
592 - // tables' columns and their pixel lengths. All of it is gone with the JS
593 - // frontend it served (goingson `7bf95b31`). A described table is
544 + // No table stylesheet is generated here. A described table is
594 545 // `display: table`, laid out by makeover's own stylesheet and aligned by
595 546 // position, so there is no `grid-template-columns` for an app to generate
596 547 // and no row class for one to key on: quasi-webview emits `.table`, `.row`
@@ -605,8 +556,7 @@
605 556 //
606 557 // A loop rather than a call each, because the shell links every one of these
607 558 // by default: a script the renderer starts shipping and this file forgets
608 - // is a 404 per document with nothing in the log. `quasi-download.js` was
609 - // exactly that until 2026-08-22.
559 + // is a 404 per document with nothing in the log.
610 560 for (name, source) in [
611 561 ("quasi-selection.js", quasi_webview::SELECTION_JS),
612 562 ("quasi-clock.js", quasi_webview::CLOCK_JS),
@@ -640,19 +590,15 @@
640 590 REVIEWED_OVERLAPS,
641 591 REVIEWED_ELEMENT_OVERLAPS,
642 592 );
643 - // The question `check_vocabulary_use` used to answer, asked of the thing
644 - // that now answers it. See `check_stylesheet_reaches_markup`, and goingson
645 - // `43a682b0` for the hole this closes.
593 + // See `check_stylesheet_reaches_markup`, and goingson `43a682b0`.
646 594 check_stylesheet_reaches_markup(
647 595 &frontend,
648 596 &Path::new(env!("CARGO_MANIFEST_DIR")).join("src"),
649 597 );
650 - // `check_touch_density` is dropped, on the instruction the check itself
651 - // gives for this case: it keeps every copy of `TOUCH_DENSITY` equal to
652 - // `makeover_geometry::Density::Touch`, and after the swap this frontend
653 - // asks no density question in script at all. `touch.js` and `mobile.js`
654 - // held the const and are gone; the generated stylesheet still carries the
655 - // condition and `check_breakpoints` above still checks that.
598 + // `check_touch_density` is not called: no script here asks a density
599 + // question, so there is no copy of `TOUCH_DENSITY` to keep equal to
600 + // `makeover_geometry::Density::Touch`. The generated stylesheet still
601 + // carries the condition and `check_breakpoints` above checks that.
656 602
657 603 println!("cargo:rerun-if-changed=build.rs");
658 604
@@ -30,13 +30,10 @@
30 30 /// Where backups are kept.
31 31 ///
32 32 /// Derived from [`AppState::data_dir`](crate::state::AppState::data_dir), which
33 - /// is the app data directory resolved once at startup. It was
34 - /// `app.path().app_data_dir().join("backups")` at four sites until 2026-08-16 —
35 - /// here, the on-demand path, `list_backups` and `delete_backup` — so four
36 - /// callers reached past the state for a path the state already held, and
37 - /// nothing that is not a Tauri command could ask where a backup lives at all.
38 - /// The described Import & Export screen is what could not: its handlers see
39 - /// `AppState` and nothing else.
33 + /// is the app data directory resolved once at startup. Ask here rather than
34 + /// building the path from an `AppHandle`: a described screen's handlers see
35 + /// `AppState` and nothing else, so a Tauri-only answer is one they cannot
36 + /// reach.
40 37 pub(crate) fn backup_dir(state: &AppState) -> PathBuf {
41 38 state.data_dir.join("backups")
42 39 }
@@ -1,10 +1,8 @@
1 1 //! GoingsOn's `user_config` keys and their sync posture.
2 2 //!
3 - //! Config used to live in the frontend's `localStorage`, unsynced. Step 6 of the
4 - //! portable-config migration moves it onto the shared config store: a
5 - //! `user_config` key/value table (migration 060) whose sync is governed by
6 - //! posture. GoingsOn is on synckit's `SyncStore` engine, so unlike audiofiles and
7 - //! Balanced Breakfast it uses the real config adapter:
3 + //! Config lives in the shared config store: a `user_config` key/value table
4 + //! whose sync is governed by posture. GoingsOn is on synckit's `SyncStore`
5 + //! engine, so it uses the real config adapter:
8 6 //! [`config_sync_table`](synckit_client::config_sync_table) folds this spec's
9 7 //! table into [`goingson_schema`](crate::syncstore::manifest::goingson_schema)
10 8 //! and the engine generates the export/import triggers, gated on the
@@ -16,8 +14,7 @@
16 14 //! stay on the device, and an undeclared key is `Local` (fail-closed).
17 15 //!
18 16 //! The store keys are the clean, unprefixed names (the family convention: theme
19 - //! is `theme`, not `goingson-theme`); the frontend maps its old `localStorage`
20 - //! keys to these once, on migration.
17 + //! is `theme`, not `goingson-theme`).
21 18 //!
22 19 //! <!-- wiki: go-config -->
23 20
@@ -21,8 +21,8 @@
21 21 /// The outbox drainer: what sends a queued message. See the module header.
22 22 pub mod outbox;
23 23 pub mod problems;
24 - /// The screens, described rather than built. The app is this as of the
25 - /// 2026-08-22 swap; see the module header.
24 + /// The screens, described rather than built. This is the app; see the module
25 + /// header.
26 26 pub mod quasi;
27 27 pub mod state;
28 28 pub mod syncstore;
@@ -122,9 +122,9 @@
122 122
123 123 let mut builder = tauri::Builder::default();
124 124
125 - // The described screens, at `quasi://localhost/<screen>`, and as of the
126 - // 2026-08-22 swap they are the app: the window opens on this scheme and
127 - // there is no other document. Registered here because a scheme can only be
125 + // The described screens, at `quasi://localhost/<screen>`, are the app: the
126 + // window opens on this scheme and there is no other document. Registered
127 + // here because a scheme can only be
128 128 // registered on the builder, and filled in from `setup` below because
129 129 // `AppState` needs an `AppHandle` that does not exist yet.
130 130 let (quasi_state, quasi_url) = {