Skip to main content

max / goingson

The pairing table is honest again, and it names what it never counted described-drift had four findings against goingson and every one of them was the doc rather than the code. Two ports landed on 2026-08-21 without a row: events (c649509) and the email accounts section (012ba4a). Two commits touched a counterpart and left the description alone, and both turn out to be answered already. WHAT THE ROWS SAY NOW. events.js joins the described table at 10 sites. email-accounts.js moves out of "stays JavaScript" into settings, where it belongs since 012ba4a measured the section: the OAuth handshake is host-bound, the accounts are State<Arc<AppState>> and synchronous. notifs.js joins the settings row too; it was counted in the header's 32 files and named in no row, so the check could not see drift in it. 35 files and 259 sites. The two drifts, acknowledged with their reasons. dda9a7c added the knob half of the notifications pane to settings.js, which quasi::settings already generates from the registry, so it is the shipped screen catching up. c238caa turned a pinned saved view's U+2022 into a data-pinned attribute, which is presentational and sits inside saved views, the part task_list's finding 4 declines to describe. THE THING THIS PASS ACTUALLY FOUND. Un-ported screens is empty for the first time: every screen the app has is described. That reads like the end of the porting and is not, because the accounting on this page is esc()-centric and the shell concatenates almost nothing. 38 files and 9,114 lines under frontend/js/ appear in no category at all, and a file with no call site was invisible to every count here. navigation.js 545, app.js 471, keyboard.js 279, mobile.js 447, and the rest. Nothing on the described side draws a tab bar: Chrome carries one panel and goingson spends it on the running-timer band. That is the last thing between the port and the swap a0617c03 describes, so it is filed rather than left in a commit message: quasicoherent 71aa29b4 for the vocabulary call, goingson 24aa5833 for the work behind it. Four files in the remainder are screen controllers rather than shell and are named as candidates for a row, unpaired for now because pairing one makes the check watch it from the commit its module was added.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-22 14:43 UTC
Signed with PGP, not checked
Commit: 77441f385b32a29452d0f4b1968b1587fda04734
Parent: b1a94a1
1 file changed, +91 insertions, -44 deletions
@@ -42,7 +42,7 @@
42 42 //! exist and both are counted. The count starts falling at the flip. Progress is
43 43 //! the first list, not the number.
44 44 //!
45 - //! ## Described, and retires at the flip (32 files, 239 sites)
45 + //! ## Described, and retires at the flip (35 files, 259 sites)
46 46 //!
47 47 //! | Module | JS counterpart | Sites |
48 48 //! |---|---|---|
@@ -54,14 +54,16 @@
54 54 //! | [`day_planning`] | `day-planning-render.js` 8, `day-planning-schedule.js` 4, `day-planning-paint.js` 1 | 13 |
55 55 //! | [`problems`] | `problems.js` | 10 |
56 56 //! | [`monthly_review`] | `monthly-review.js` 3, `monthly-review-render.js` 5 | 8 |
57 - //! | [`settings`] | `settings.js` | 11 |
57 + //! | [`settings`] | `settings.js` 11, `email-accounts.js` 10, `notifs.js` 0 | 21 |
58 58 //! | [`board`] | `tasks-kanban.js` 3, `task-board.js` 1 | 4 |
59 59 //! | [`task_list`] | `tasks.js` 2, `tasks-render.js` 8, `tasks-filter.js` 2, `task-forms.js` 1, `saved-views.js` 1 | 14 |
60 60 //! | [`data`] | `import-external.js` 11, `import.js` 5, `export.js` 2 | 18 |
61 61 //! | [`time_tracking`] | `time-tracking.js` 6, `time-summary.js` 3 | 9 |
62 + //! | [`events`] | `events.js` | 10 |
62 63 //!
63 - //! [`projects`] carries its dashboard as a submodule, which is the thirteenth
64 - //! described screen against twelve modules here.
64 + //! Two rows carry a second screen as a submodule: [`projects`] its dashboard,
65 + //! and [`settings`] its email accounts section. So the fourteen rows here are
66 + //! sixteen described screens.
65 67 //!
66 68 //! [`time_tracking`] is the one row here that is two things: the app's chrome,
67 69 //! described because the floating widget belongs to no screen, and the Timer
@@ -84,52 +86,51 @@
84 86 //! the rarer direction, a described screen's shape ported back, and it deletes
85 87 //! at the flip like the rest of the row, along with `commands::notifs`.
86 88 //!
87 - //! ## Stays JavaScript, by decision (4 files, 50 sites)
89 + //! `email-accounts.js` moved into that row on 2026-08-21 (`012ba4a`), out of
90 + //! the by-decision list below. The section had been called host-bound on the
91 + //! strength of its loudest feature; what is host-bound is the OAuth handshake,
92 + //! and the accounts themselves are `State<Arc<AppState>>` and synchronous
93 + //! throughout. [`settings::email`] describes them, and the handshake, Test
94 + //! Connection and Sync Now are what it refuses.
88 95 //!
89 - //! - `settings-sync.js` 17, `settings-sharing.js` 14, `email-accounts.js` 10.
90 - //! Host-bound: OAuth flow, credential handling, `invoke(`.
96 + //! ## Stays JavaScript, by decision (3 files, 40 sites)
97 + //!
98 + //! - `settings-sync.js` 17, `settings-sharing.js` 14. Every group and sync
99 + //! command awaits a network client (12 `.await` sites in `commands/group.rs`
100 + //! alone) and a route handler is synchronous, so it cannot await one.
101 + //! Measured 2026-08-21, and it is a different reason from the `AppHandle` one
102 + //! this list used to give: `group.rs` never mentions `AppHandle`.
91 103 //! - `search.js` 9. Settled 2026-08-13 (quasicoherent `d52884b0`): a described
92 104 //! field's state is its value, and the caret is the renderer's.
93 105 //!
94 - //! These 50 never reach zero by porting. Retiring `escape.js` means giving them
95 - //! typed escaping some other way, or accepting that four files keep an escaper.
106 + //! These 40 never reach zero by porting. Retiring `escape.js` means giving them
107 + //! typed escaping some other way, or accepting that three files keep an escaper.
96 108 //!
97 - //! ## Un-ported screens (1 file, 10 sites)
109 + //! ## Un-ported screens (0 files, 0 sites)
98 110 //!
99 111 //! A described counterpart could exist and does not. This is the candidate list,
100 - //! and it is the only place to look for what is portable next.
112 + //! and it is the only place to look for what is portable next. It is empty as of
113 + //! 2026-08-21, for the first time since it was written: every screen the app has
114 + //! is described.
101 115 //!
102 - //! The task list left this list on 2026-08-15 and is [`task_list`] now. It was
103 - //! the largest candidate here and the one this table had to name twice, because
104 - //! it is neither [`tasks`] (the single-task drawer at `GET /tasks/{id}`) nor
105 - //! [`board`] (the kanban). The three import and export files left it on
106 - //! 2026-08-16 and are [`data`] now — with a caveat this table cannot carry in a
107 - //! count: three exports and the on-demand backup are described nowhere, because
108 - //! a save destination has no word in the vocabulary and a described write cannot
109 - //! be offloaded. Their `esc()` sites are inside files that do retire, so the
110 - //! arithmetic here is honest and the screen is not yet whole. [`data`]'s findings
111 - //! 1 and 2 are what the flip waits on. `time-tracking.js` and `time-summary.js`
112 - //! left on 2026-08-20 and are [`time_tracking`]'s row above.
116 + //! What left it, in order. The task list on 2026-08-15, the largest candidate
117 + //! here and the one this table had to name twice because it is neither
118 + //! [`tasks`] (the single-task drawer at `GET /tasks/{id}`) nor [`board`] (the
119 + //! kanban). The three import and export files on 2026-08-16, now [`data`], with
120 + //! a caveat this table cannot carry in a count: three exports and the on-demand
121 + //! backup are described nowhere, because a save destination has no word in the
122 + //! vocabulary and a described write cannot be offloaded. Their `esc()` sites are
123 + //! inside files that do retire, so the arithmetic here is honest and the screen
124 + //! is not yet whole. [`data`]'s findings 1 and 2 are what the flip waits on.
125 + //! `time-tracking.js` and `time-summary.js` on 2026-08-20, now
126 + //! [`time_tracking`]'s row. `events.js` on 2026-08-21, now [`events`]: the
127 + //! largest file in `frontend/js/` at 1,037 lines, and the tab the thirteen flip
128 + //! tasks did not name.
113 129 //!
114 - //! - `events.js` 10. Measured 2026-08-21, and the sentence that stood here was
115 - //! wrong twice over: there is no grid in the file, and all ten sites are
116 - //! describable. It is three lists, a form and a detail modal — no calendar
117 - //! rendering of any kind, which lives elsewhere. Sites 543/553/558 and
118 - //! 618/623 are `renderEventRow`'s title, location and recurrence-pattern
119 - //! cells (a described table), 791/798/802/805 are the detail modal
120 - //! (`Region::Modal` plus text), and 123 is the reminder-preset checkbox list
121 - //! (a `Choice` set). The whole file is a real candidate, which is more than
122 - //! the old note claimed and not less.
123 - //!
124 - //! Three things the port will meet, none of them blockers. The three
125 - //! `VirtualScroller`s over `renderEventRow` are the case [`task_list`]'s
126 - //! finding 2 and [`emails`]'s `list` already settled: windowing rows you hold
127 - //! is a renderer technique, and a described list answers it with `Rest`.
128 - //! `start_time`/`end_time` carry a `transform` that natural-parses the text
129 - //! before submit and an `onInput` preview beside the box, which is host
130 - //! behaviour on either side of a `Field` rather than something the field
131 - //! says. The `timezone` field's validity depends on `tz_kind`, so a described
132 - //! form either validates it on the server or the two travel together.
130 + //! An empty list is not the end of the porting. It means the remaining
131 + //! JavaScript is in the three categories that are not screens: the by-decision
132 + //! files above, the bespoke one below, and the shell, which no row here reaches
133 + //! and which is the real remainder. It has its own section below.
133 134 //!
134 135 //! ## Bespoke (1 file, 6 sites)
135 136 //!
@@ -155,9 +156,53 @@
155 156 //! `autocomplete.js` 2, `bulk-actions.js` 2, `plan-review-toggle.js` 2,
156 157 //! `snooze.js` 2, `form-modal.js` 1, `groups.js` 1.
157 158 //!
158 - //! Together with the 50 above, these 77 sites are what the done-condition
159 + //! Together with the 40 above, these 67 sites are what the done-condition
159 160 //! actually has to answer for. Porting every candidate screen leaves them.
160 161 //!
162 + //! ## The shell, which no row above reaches (38 files, 9,114 lines)
163 + //!
164 + //! Measured 2026-08-22, and it is the reason a table that looks complete is
165 + //! not. The four categories above account for every `esc()` call site in the
166 + //! tree and for 49 of the 87 files under `frontend/js/`. The other 38 are 9,114
167 + //! lines and appear in no category, because the accounting here is `esc()`-
168 + //! centric and the shell concatenates almost nothing: it wires, routes, and
169 + //! switches panes. A file with no call site was invisible to every count on
170 + //! this page, so the remainder never read as missing.
171 + //!
172 + //! The largest of them, and what each is: `utils.js` 704, `components.js` 594
173 + //! (context menu, action sheet), `navigation.js` 545 (view switching, the tab
174 + //! bar, window title), `api.js` 500, `app.js` 471 (bootstrap, the Tauri menu),
175 + //! `virtual-scroller.js` 453, `mobile.js` 447 and `touch.js` 444,
176 + //! `keyboard.js` 279 (the shortcut table and the help overlay), `themes.js`
177 + //! 277, `router.js` 107, `dispatch.js` 109, `state.js` 158.
178 + //!
179 + //! Nothing on the described side draws any of it. [`quasi_router::Chrome`]
180 + //! carries key bindings and one panel, and goingson declares the panel as
181 + //! [`time_tracking::chrome`], the running-timer band. That is the whole of the
182 + //! app furniture that exists on this side. A tab bar is a second always-present
183 + //! thing and there is no second panel, which is a vocabulary question rather
184 + //! than a porting one: quasicoherent `71aa29b4`, with the goingson half on
185 + //! `24aa5833`.
186 + //!
187 + //! Four files in the remainder are screen controllers rather than shell, and
188 + //! are candidates for a pairing row once somebody reads them against the
189 + //! description: `day-planning.js` 541 (the day plan's own load and render, next
190 + //! to the three files [`day_planning`] already pairs), `compose-page.js` 304
191 + //! (Max's call on `3fb2526a`, compose becomes a widget of primitives),
192 + //! `focus-timer.js` 283 (the Focus control [`time_tracking`] leaves out, waiting
193 + //! on goingson `44cfcac7`), and `emails-threads.js` 116. They are named here
194 + //! rather than paired, because pairing one makes the drift check watch it from
195 + //! the commit the module was added, and each wants its own read first.
196 + //!
197 + //! ## Modules that describe no screen
198 + //!
199 + //! Read by `witchbroom`'s `described-drift` check, which otherwise takes every
200 + //! `pub mod` under this root for a screen whose pairing row somebody forgot.
201 + //!
202 + //! - [`assets`]. The stylesheets, scripts and fonts a described document asks
203 + //! for. It answers at addresses no route claims and pairs with nothing under
204 + //! `frontend/js/`.
205 + //!
161 206 //! # The ports checked against their counterparts
162 207 //!
163 208 //! Swept 2026-08-15, after [`tasks`] turned out to be describing a screen that
@@ -180,9 +225,11 @@
180 225 //! | [`projects`] | `0df3488`, both markers on the dashboard card | [`Availability::marker`] |
181 226 //! | [`day_planning`] | `0df3488`, the unblocks marker in the pool | [`Availability::frees_marker`] |
182 227 //! | [`projects`], [`tasks`] | `5e16cc1`, a CSS class rename across six files | presentational only: nothing a description can say changed |
228 + //! | [`settings`] | `dda9a7c`, the knob half of the notifications pane | already generated: [`quasi_notifs::pane`] emits a control per declared knob and [`settings`] reads the registry, so this is the shipped screen catching up |
229 + //! | [`task_list`] | `c238caa`, a pinned saved view's U+2022 became a `data-pinned` attribute | presentational, and inside saved views, which this module's finding 4 declines to describe |
183 230 //!
184 - //! Clean: [`contacts`], [`settings`], [`weekly_review`], [`emails`],
185 - //! [`monthly_review`], [`problems`]. Four of those now have a commit touching
231 + //! Clean: [`contacts`], [`weekly_review`], [`emails`], [`monthly_review`],
232 + //! [`problems`], [`events`]. Four of those now have a commit touching
186 233 //! their JS since the port, and each of those touched the description in the
187 234 //! same commit, which is the arrangement that works. [`weekly_review`] joined
188 235 //! them when the focus picker learned to name what a candidate waits on: the