Skip to content

Audit fixes: app hang, Write for Me crash, stuck hotkey tap, dead code and stale docs - #2336

Merged
r3dbars merged 1 commit into
mainfrom
claude/project-audit-2026-11-09
Nov 12, 2026
Merged

r3dbars merged 1 commit into
mainfrom
claude/project-audit-2026-11-09

Conversation

@r3dbars

@r3dbars r3dbars commented Nov 10, 2026

Copy link
Copy Markdown
Owner

Why

A whole-repo audit: find what's broken, inconsistent, inefficient or no longer needed, fix what's certain, and list the rest for the owner. 30 read-only lanes covered the tree, fix groups re-checked every item before editing, and the more serious claims got a second adversarial read. This PR is the part that was safe to fix without an owner decision.

Product Impact

  • Affects: dictation / meetings / agent artifacts
  • Lane: dictation reliability / meeting reliability / agent workflow
  • Why this matters: it removes a path to a permanent app hang, a Write for Me crash and a stuck dictation key, and it clears out dead code and stale docs that keep misleading agents.

What changed

Bug fixes (read these diffs):

  • App hang. LiveMeetingTranscriptService was the only main-queue observer of UserDefaults.didChangeNotification. That makes every background defaults write wait for main, and a writer holding a lock main was waiting on then hung the app for good. It now hops to main asynchronously, the way AnalyticsReporter already does. The Live transcript toggle applies one run-loop turn later.
  • Write for Me crash. An error reply with no message read stderr with queue.sync on the queue it was already on. The buffer is guarded by the existing lock. New test spawns a real child process.
  • Stuck hotkey tap. PhysicalShortcutTapRecovery dropped a disable that landed during the OS re-enable call. pending is cleared before the call. New test.
  • Speaker names in the local log. Two merge log lines used keys the log sanitizer doesn't redact. They log profile ids.
  • FileLogger escapes every control character; SQLiteHandle closes the handle after a failed open; a saved-audio "request was cancelled" status now clears like its sibling failures.
  • "Listening…" could sit over finished dictation text; a paused failed-meeting player said Pause; F13–F20, Home, End and the Page keys showed as "Key105" in Settings.
  • The speaker eval harness and the Lab speaker sweep wrote to the real app log. They set TRANSCRIPTED_DISABLE_FILE_LOGGER like the other wrappers.
  • The QA bench release-health step still pinned the 1.1.75 fixtures after 1.1.76 shipped, so it failed under --strict. Ran both: 1.1.75 exits 1, 1.1.76 scores 100/100.
  • transcripted-qa treated a sibling folder as the legacy library (prefix match with no / boundary). New test.

Cleanup, no behavior change:

  • Dead code left by finished migrations, write-only state and unused parameters removed. Every removed symbol was re-grepped across Sources, Tests, Tools, scripts and string references.
  • Stale comments and docs corrected against the code, including the SECURITY.md storage table, docs/agent-merge-policy.md (a dangling reference and a stale appcast sentence) and the "most-pinned" list in root AGENTS.md. Owner-level docs: please read those three diffs.
  • Trivial compiler warnings fixed: 89 unique warnings down to 67.
  • The call-detection spec and a few comments now describe behavior without naming other products.

Tests and guardrails:

  • A fast-test suite that was never called (testDictationStoppedAudioRecoveryRetryRegistry) is wired in and passes.
  • 100 assertionFailure calls that crashed the whole run now record a failure; seven silent early returns now fail; two fixture-setup XCTSkips became failures. A reviewer checked every swapped site for code that would now run on into a crash.
  • The banned-calls FileHandle rule scans the files that do file I/O (its old list pointed at two files with none).
  • The test matrix selects the banned-calls check and the speaker smoke for more files; linux-checks.sh runs two more offline release self-tests.
  • Preflight reports a core.hooksPath that is unset or points somewhere else.

Nothing changed under scripts/release/, Casks/, docs/appcast.xml, Info.plist, release-candidate.yml, auto-merge-lanes.json or any *-baseline.json.

How I checked it

  • scripts/dev/agent-preflight.sh
  • bash scripts/dev/linux-checks.sh (98 checks pass; 96 on main, two self-tests added)
  • Selected checks from .agents/test-matrix.yml for the files changed (ran the whole check.sh full tier instead)
  • bash build.sh --no-open
  • bash run-tests.sh (52,643 assertions, 0 failed; 52,636 on main)
  • Performance budget passed (the bundle gate in build.sh)
  • bash run-integration-smoke.sh
  • swift test
  • bash run-e2e-smoke.sh / swift test --package-path Tools/<Package> (CaptureKit, CLI, MCP, QA, Live, Lab; CLI audio, meeting, retrieval and diarization builds; SpeakerEvalHarness build)
  • Also: bash check.sh full passed 18/18 on untouched main first and 18/18 after; run-slow-pasteback-smoke.sh (6 scenarios); test-speaker-settings-model.sh; test-speakers-directory-scroll.sh
  • Manual check: none done

scripts/dev/verify-change.sh:

Check Result Detail
paste PASS text pasted into the slow fake target
meetings SKIP Transcripted was already running on this Mac
ui SKIP Transcripted was already running on this Mac
speed SKIP Transcripted was already running on this Mac

Checks I could not run, and why:

  • The verify script's meetings, UI and speed checks: the owner's app was running, and the script won't launch a second copy.
  • bash check.sh hardware and any live dictation or meeting: needs a person, a mic and AirPods.
  • No by-eye look at the island or Settings. No .agent-review/visuals/.

Mac or hardware test still needed? Yes:

  • Dictate once with the built-in mic and once with AirPods as the default input. Expect the same behavior as main. Sources/Speech changes are comments, dead-code removal and one unwrapped always-true #available.
  • Hover a landed dictation in the island after a take spoken with the drop-down closed (first take after launch). On main a grey "Listening…" can sit over the text; here it shouldn't.
  • Play a failed meeting's kept audio from Home, then pause. On main the button still says "Pause audio"; here it says "Play audio".
  • Toggle Settings › Live transcript during a meeting. It should still apply right away (it now lands one run-loop turn later).

Risk Review

  • Privacy / local-first behavior reviewed (no new log or analytics line carries names, paths or text; two log lines stopped carrying names)
  • New analytics properties or Sentry tags avoid the sanitizer's drop fragments (none added; check-telemetry-keys.py passes)
  • Checked the text-pin tests for every file I edited (check-source-pins.py --changed-only passes; one planned deletion was dropped because a contract test pins text inside it)
  • Storage path or migration impact reviewed (none)
  • Public-facing copy stays concrete and matches current product scope (SECURITY.md storage table corrected to real paths; its privacy claims were left for the owner)
  • Release/update impact reviewed (no release surface touched; only the QA fixture pin moved to 1.1.76)
  • Agent PRs got an independent deep review of the full diff: two reviewers with no part in the fixes, one for Sources/ (131 files) and one for everything else. Verdict: no blockers, no should-fix. One nit fixed (a doc typo). The app-hang fix landed after the Sources/ reviewer started; it was designed by a separate adversarial reader and re-read by the coordinator.
  • UI changes include sanitized .agent-review/visuals/ evidence (not done; see the hardware steps)
  • No private transcripts, audio, tokens, personal paths, or customer data are included

Notes

  • One commit, 214 files. Most hunks are a few lines. The table under "Bug fixes" is the short list worth real attention.
  • Sources/Support/ClipboardRestoringTextPaster.swift loses its paste-retry state. At main that state was declared but never set: the parameter that would set it defaulted to false and no caller passed true. Traced three times, and the slow paste-back smoke passes.
  • Removed lines in this diff still show product names that were already in the repo's history. History is untouched.
  • The open findings (things that need a product or design call) went to the owner privately, not into the repo.

Agent handoff

COORD_DONE: GREEN | this PR | audit safe-fix pass: hang, crash and hotkey fixes, dead code, stale docs, test and guardrail repairs | none | owner reads SECURITY.md, agent-merge-policy.md and AGENTS.md diffs; open findings list delivered privately | check.sh full 18/18 before and after, quick tier, both Speakers smokes, Tools Live/Lab/harness/CLI modes, slow paste-back smoke, verify-change (paste PASS, native checks SKIP) | lanes used: Codex=0; Claude=30 auditors, 10 fixers, 5 skeptics, 2 reviewers; Local=all checks on the owner's Mac; Windows=0 | owner does the four hardware steps above, then marks ready

🤖 Generated with Claude Code

A whole-repo audit pass: 30 read-only lanes, then fix groups that
re-checked each item before editing. This is the part that was safe to
fix without an owner decision.

Bug fixes
- App hang: the only main-queue observer of UserDefaults changes
  (LiveMeetingTranscriptService) made every background defaults write
  wait for main. A writer holding a lock main was waiting on then hung
  the app for good. It now hops to main asynchronously, the way
  AnalyticsReporter already does.
- Write for Me: an error reply with no message read stderr with
  queue.sync on the queue it was already running on. The buffer is
  guarded by the existing lock now.
- Hotkey tap recovery dropped a disable that landed during the OS
  re-enable call, leaving the tap dead. `pending` is cleared before the
  call, so one more attempt queues behind it.
- Speaker merge logs wrote display names under keys the log sanitizer
  doesn't redact. They log profile ids now.
- FileLogger escapes every control character, SQLiteHandle closes the
  handle after a failed open, and a saved-audio "request was cancelled"
  status now clears like its sibling failures.
- "Listening..." could sit over finished dictation text, a paused
  failed-meeting player said Pause, and F13-F20, Home, End and the Page
  keys showed as "Key105" in Settings.
- The speaker eval harness and the Lab speaker sweep wrote to the real
  app log. They set TRANSCRIPTED_DISABLE_FILE_LOGGER like the other
  wrappers.
- The QA bench release-health step still pinned the 1.1.75 fixtures
  after 1.1.76 shipped, so it failed under --strict.

Cleanup, no behavior change
- Dead code left by finished migrations, write-only state and unused
  parameters removed; stale comments and docs corrected against the
  code; trivial compiler warnings fixed (89 unique warnings down to 67).
- The call-detection spec and a few comments describe behavior without
  naming other products.

Tests and guardrails
- A fast-test suite that was never called is wired in.
- 100 assertionFailure calls that crashed the whole run now record a
  failure, and seven silent early returns now fail.
- The banned-calls FileHandle rule scans the files that do file I/O.
- The test matrix selects the banned-calls check and the speaker smoke
  for more files; linux-checks runs two more offline self-tests.
- Preflight reports a hooks path that is unset or points elsewhere.

Checked: check.sh full 18/18 before and after, the quick tier, both
Speakers smokes, the Live tests, the CLI build modes, the harness and
Lab builds, and the slow paste-back smoke. Two independent reviews of
the full diff found no blockers. Not checked on hardware: no AirPods
take and no live meeting.
This was referenced Nov 10, 2026
This was referenced Nov 10, 2026
@r3dbars
r3dbars merged commit 6f28c33 into main Nov 12, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant