Skip to content

Main window speed, part 2: less repeated work in Settings and Home - #2023

Merged
r3dbars merged 7 commits into
mainfrom
claude/project-thread-896xw3
Oct 6, 2026
Merged

r3dbars merged 7 commits into
mainfrom
claude/project-thread-896xw3

Conversation

@r3dbars

@r3dbars r3dbars commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Requested by Justin · project thread

Why

This follows #2012 (merged) with more of the no-visible-change items from the UI speed plan. Pixels and behavior stay the same, and each commit stands alone. It's not part of the 1.1.70 test copy unless Justin or the release thread says so.

Product Impact

  • Affects: meetings / dictation
  • Lane: meeting reliability
  • Why this matters: Settings, Home and the Speakers page do less repeated work. Deleting a dictation no longer blocks the window while a save is in flight.

What changed

  • Settings auto-send apps (bb5b88f): the Apps row used to build the whole merged app list (a LaunchServices lookup per allowed app, then a sort) just to check whether it was empty, then built it again. The divider now checks the two inputs directly.
  • Meeting cache prune (7f34b8f): file stats now run outside the SQLite lock, so search and Today aren't held up by the once-a-minute sweep. Each missing row is re-checked under the lock before it's deleted.
  • Speakers review queue (7d9e0fb, tests 7878020): before, every scan re-read every transcript that still had an unnamed voice, which for most people is most of their meetings. Now the part that doesn't depend on profiles is cached by path, modified time and size. Profiles, clips and file dates are still applied fresh on every scan.
  • Dictation delete and undo (5f7a490, 53d9071): the file work now runs in a background task, the same pattern as meeting delete. It used to run on main, under the lock the dictation writer also takes. The save notification is now delivered on main. An ignored double-tap no longer sends a second delete event.
  • Opening Settings (ade777b): opening the window read every permission twice, once from the open and once from the app activation the open causes. Activation within 1 s of an open is now skipped.

How I checked it

  • bash check.sh quick: pass (66/66)
  • python3 scripts/dev/check-source-pins.py --changed-only, check-file-size, check-module-boundaries, check-test-shape, check-known-traps, check-telemetry-keys: pass
  • New tests: Tests/SpeakerReviewQueueScannerTests.swift (a named voice clears, a deleted profile drops, clips are applied fresh, repeat scans give the same rows) and Tests/SettingsClosedWindowPolicyTests.swift (activation window)
  • bash build.sh --no-open / bash run-tests.sh: CI

Checks I could not run, and why:

  • There's no Swift toolchain in this cloud session, so this code hasn't been compiled until CI runs on this head.

Mac or hardware test still needed?

  • Settings > Dictation > Auto-send apps: the rows and divider look the same as on main, with and without allowed apps.
  • Speakers: name a voice, and its row leaves the review queue on the next refresh, same as before.
  • Dictations: delete one, then Undo. The row comes back. Delete one and let the undo expire, and it stays gone.
  • Open Settings with ⌘, a few times. Permission rows are correct.

Risk Review

  • Privacy / local-first behavior reviewed (no new logging; one existing event now fires only for real deletes)
  • Checked the text-pin tests for every file I edited
  • Storage path or migration impact reviewed (none)
  • Agent PRs got an independent deep review of the full diff: CLEAN, with 3 minor notes. One is fixed in 53d9071. The other two are tiny windows, noted in the review file.
  • No private transcripts, audio, tokens, personal paths, or customer data are included

Notes

Plan: speed/2026-10-05/ui.md. Review: reviews/next-release/2023.md (project files).

🤖 Generated with Claude Code

https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU

The Apps row only needs to know whether the merged list is empty, but it
built the whole list (a LaunchServices name lookup per allowed app that
isn't running, then a sort) just for that, and the list below built it
again. The divider now checks the two inputs directly: the union is empty
exactly when both are. Same divider, same rows.

Claude-Session: https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU
pruneMissingPaths held the cache's SQLite lock across one fileExists per
cached row, so a Home search or Today build that needed the cache waited
for the whole sweep (once a minute, longer on a slow or iCloud library).
It now copies the paths under the lock, stats them unlocked, then locks
again to delete. Rows found missing are re-checked under the lock, so a
meeting saved again at the same path in between keeps its row. Same rows
removed, same count returned.

Claude-Session: https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU
@r3dbars r3dbars self-assigned this Oct 6, 2026
The review queue scan only skipped transcripts with no db_pending
speaker. Every transcript that still listed one was read (up to 256 KB),
parsed, and for voices without a clip parsed again into transcript lines,
on every refresh: each window open, rename, merge and meeting save. Users
who rarely name voices keep most meetings pending, so that was close to
reading the whole library each time.

Now the profile-independent part of that parse (title, dates, capture id,
duration, imported flag, the pending speakers, their sample lines, and the
retained-audio preview windows) is cached by path + mtime + size, the same
key as the no-pending cache. Each scan still applies the current profiles
and clips and resolves the audio files fresh, so naming, deleting or
re-clipping a voice shows up as before. If a voice lost its clip since the
summary was made without transcript lines, the file is read again.

pendingItems(in:...) keeps its signature and builds through the same
summary, so its tests cover both paths.

Claude-Session: https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU
Deleting a dictation read the whole day file twice, rewrote it (or moved
it to the Trash) and fixed its permissions on the main thread, under the
same lock the dictation writer holds while it saves. A delete during a
save, or on an iCloud library with the file evicted, could stall the
window. Undo's restore and the kept-audio cleanup when the undo window
closes did the same.

All three now run in a background task, the way meeting delete already
does. A delete already in flight for the same entry is ignored instead of
running twice. The store posts .dictationTranscriptDidSave from that
background task, so the Settings window now receives it on the main
queue (the menu bar already did). Same undo line, same alerts, same
refresh after each step.

Claude-Session: https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU
present() starts the full refreshState() for the new presentation and
then activates the app. That activation fired refreshAfterAppActivation,
which re-read permissions (TCC), shortcuts and the login-item status on
the main thread while the window was opening; refreshState already reads
all of them. present() now records when it ran, and an activation within
a second of it is skipped. Later activations (coming back after granting
a permission) refresh exactly as before.

The rule lives in SettingsClosedWindowRefreshPolicy with a test.

Claude-Session: https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU
Covers the pending-transcript cache: a newly named voice clears on the
next scan, a deleted profile drops its row, clip URLs are applied fresh,
and repeated scans of an untouched transcript give the same rows.

Claude-Session: https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU
…ete event

Review follow-up: track the confirm only once the delete actually starts.

Claude-Session: https://claude.ai/code/session_01VJosskWzmoGsWf4C4U3uJU
@r3dbars
r3dbars marked this pull request as ready for review October 6, 2026 10:36
@r3dbars
r3dbars merged commit 863d23a into main Oct 6, 2026
12 checks passed
@r3dbars
r3dbars deleted the claude/project-thread-896xw3 branch October 6, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant