Skip to main content

max / goingson

A time session records which feature started it, and what it counts down to Track and Focus wrote the same row and nothing in the store told them apart. `time-tracking.js` held the answer in `activeMode` and `focus-timer.js` held the countdown in `remainingSeconds`, both variables in the JS process, and the file said so itself: a timer found running after a reload read as Tracking whichever it had been. Migration 068 gives `time_sessions` a `mode` and an `ends_at`. `mode` defaults to 'track', which is what every existing row was displayed as anyway, so no backfill is owed. `ends_at` stays NULL on Track sessions and on a Focus session started before the column existed; there is no honest value to invent for one, and the band shows elapsed time instead of a countdown it cannot rebuild. Two entry points rather than a mode parameter: `start_timer` is Track and `start_focus_session` is Focus, because there are two features and a caller cannot pick the wrong one by accident. The end instant is computed server-side from the split, off the same clock `started_at` is stamped from. WHAT IT UNBLOCKS ON THE DESCRIBED SIDE, both recorded as refusals in `quasi::time_tracking`'s header since the port. The band names the mode off the session. A focus session's readout is `Node::Until` holding the instant it was started for where a tracked one is `Node::Since` holding its start, so the countdown survives a reload and neither is a number a route computed. And the Timer row offers Focus again, spending the split the address carries at the moment it is pressed, with the minutes in the label because a description has no `title` to hide them in. The overlay is still refused. `focus-timer.js` draws a full-screen countdown and an overlay needs a container quasi-webview emits only alongside a chrome binding, which is quasicoherent `858be2a6`. What this draws instead is the band, on every screen, counting to the same instant. The reason the control was left out was that a Focus button differing from Track only in its toast is worse than its absence; that is no longer what it would be. Reaching `ends_at` is not a state change. Nothing closes the session there: it ends when somebody stops it, the same as Track. Sync carries both columns, the triggers name them, restore writes them, and the shipped JS reads the mode off the session rather than out of its own process. Nine tests: the two entry points, a round trip through the store, a manual entry (started by no feature, so Track with no countdown), the one-timer rule not caring which feature asked, both bands, and the split the screen spends. Closes goingson 44cfcac7.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-22 14:59 UTC
Signed with PGP, not checked
Commit: dd39a4295ba27cd71639b8d06158bb04f1fa9f29
Parent: 77441f3
18 files changed, +580 insertions, -81 deletions
@@ -68,3 +68,4 @@
68 68 065 734a797c90c0447e413fb4fde8a19d26a52752adbea385ca30f2b2369a281bc4275e57d088a8fe73c6d5adef816a195e
69 69 066 ddea0b76234ce4d2aa4482b01ac304efb64624c854aa916ca7ad6a43bc978bce5a8f890378880c5b0d2469c8f753d50d
70 70 067 6ac04d5280e01472180139ba6a5ae83be242dbcae8ca5b1adf90c15ad01507f734047262fd987bc8eb50c238f96621d8
71 + 068 ccdddaa4176f36169bb90b1eec191ae776a87ee1cd56ba2bd789f905fb212bcd2b04b884e820498fab8b6d03fb7e5c6d
@@ -339,6 +339,7 @@
339 339 $crate::commands::import_ics,
340 340 // Time Tracking
341 341 $crate::commands::start_timer,
342 + $crate::commands::start_focus_session,
342 343 $crate::commands::stop_timer,
343 344 $crate::commands::discard_timer,
344 345 $crate::commands::get_active_timer,
@@ -92,9 +92,9 @@
92 92 ProjectStatus, ProjectType, Recurrence, RecurrenceRule, SavedView, SortDirection, SortField,
93 93 StatusToken, Subtask, SyncAccount, TOKEN_KIND_COMMIT, Task, TaskDependency, TaskEstimate,
94 94 TaskFilterQuery, TaskGraph, TaskGraphNode, TaskSortColumn, TaskStatus, TimeReport,
95 - TimeReportProject, TimeSession, TimeSummaryPanel, TimeSummaryProject, TimeTrackingSummary,
96 - TokenState, TzKind, UpdateEmailAccount, UpdateEvent, UpdateProject, UpdateTask, User,
97 - ViewFilters, ViewType, WeeklyReview, format_file_size, mime_from_extension,
95 + TimeReportProject, TimeSession, TimeSessionMode, TimeSummaryPanel, TimeSummaryProject,
96 + TimeTrackingSummary, TokenState, TzKind, UpdateEmailAccount, UpdateEvent, UpdateProject,
97 + UpdateTask, User, ViewFilters, ViewType, WeeklyReview, format_file_size, mime_from_extension,
98 98 roll_up_time_report, roll_up_time_summary, sanitize_reminder_offsets, snap_all_day_span,
99 99 split_description,
100 100 };