Skip to content

Click a transcript timestamp to play the meeting from there - #1772

Merged
claude[bot] merged 8 commits into
mainfrom
claude/clickable-timestamps-y7tfkj
Sep 24, 2026
Merged

claude[bot] merged 8 commits into
mainfrom
claude/clickable-timestamps-y7tfkj

Conversation

@claude

@claude claude Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Justin · project thread

Before: an opened meeting's transcript shows a time on each row, but it's just text. To hear that moment you had to hit play and drag the scrubber around to find it.

After: click a row's time and the meeting audio plays from that moment. Hovering the time underlines it and the tooltip says "Play from 0:42". Meetings with no saved audio keep plain, non-clickable times. Rows still never highlight or follow the playhead.

How: QuietMeetingExpansion wraps each row time in a small button when the meeting has audio and the time parses. The button calls MeetingAudioPlayback.play(_:from:rowSourceStem:). If this meeting's audio is loaded, it seeks and resumes if paused. Otherwise it starts this meeting's audio at that spot. A click past the end of the loaded file beeps and leaves playback alone. The source is picked in this order: the user's pick in the player menu, then (only while a meeting still has split Mic/System files and no Mix, e.g. right after it saves) the clicked row's own track, so a "You" row plays your mic. Otherwise it uses the default (the Mix or single recording). The player's source pick now lives in MeetingAudioPlayback, keyed by the meeting's audio folder and file stem, so it survives Home rescans. Time parsing (M:SS, including 3-digit minutes, and H:MM:SS) is a small pure helper on HomeMeetingTranscriptLine.

Why

User feedback (via email): transcripts have times in them, but the times don't link to the audio.

Product Impact

  • Affects: meetings
  • Lane: meeting reliability
  • Why this matters: makes it quick to check what was actually said at a spot in the transcript.

What changed

  • Sources/UI/Settings/QuietHomeLibrary.swift: clickable QuietTranscriptTimestamp (per-row accessibility id) for rows in meetings with saved audio.
  • Sources/UI/Shared/MeetingAudioPlayback.swift: play(_:from:rowSourceStem:), picked-source memory, past-end guard, start position re-applied after NSSound.play() in both the start and seek paths; MeetingAudioPlaybackLoadingPolicy.rowChoice / choice(in:sourceStem:).
  • Sources/UI/Settings/HomeMeetingAudioPlayer.swift: the player's source menu reads and writes the shared pick instead of view-local state.
  • Sources/UI/Settings/HomeMeetingPreviewFormatter.swift: HomeMeetingTranscriptLine.startSeconds + HomeMeetingTranscriptClock.seconds(from:).
  • Tests: clock parsing (HomeMeetingPreviewFormatterTests), row-source choice (MeetingAudioArchiveResolverTests).
  • Docs: AGENTS.md "keep these surfaces" line and Sources/UI/CLAUDE.md now say row times are clickable (rows still don't follow the playhead). Flagging the AGENTS.md line for sign-off at merge.
  • Ported from Fix the Core test freeze and a Parakeet timing flake, and capture stacks in CI #1785 (test-only): AudioInitializationTests no longer probes real mics/TCC. It's the likely cause of the intermittent Core test stall that hit this PR's CI once. Identical to Fix the Core test freeze and a Parakeet timing flake, and capture stacks in CI #1785's version, so it no-ops when that merges.

Transcript paragraph splitting is untouched (the feedback explicitly asked to keep it). No audio capture code is touched.

How I checked it

  • scripts/dev/agent-preflight.sh
  • bash build.sh --no-open / bash run-tests.sh via GitHub Actions CI (no macOS toolchain in this session)
  • Manual check on a Mac: steps in the project's clickable-timestamps/mac-test-after-1.1.62.md

Risk Review

  • Privacy / local-first behavior reviewed (no new data leaves the device, no new analytics)
  • Storage path or migration impact reviewed (none)
  • Public-facing copy stays concrete and matches current product scope
  • Release/update impact reviewed (not part of 1.1.62)
  • Agent PRs link the issue/workpad and stay draft until human review
  • UI changes include sanitized .agent-review/visuals/ evidence (no Mac in this session)
  • No private transcripts, audio, tokens, personal paths, or customer data are included

Notes

Each playback source is a single file (Mix, Recording, System or Mic), so only one file plays at a time. Mic vs System picking only matters in the short window after a meeting saves, before its Mix is written, or when writing the Mix failed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VGTtv8jq8HdgQtKY9PvCcm

Clicking a row's time in the opened meeting now plays the saved audio
from that moment. It seeks the loaded audio (keeping the chosen source)
or starts this meeting's audio there. Meetings with no saved audio keep
plain, non-clickable times. Transcript paragraph splitting is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VGTtv8jq8HdgQtKY9PvCcm
@claude claude Bot assigned r3dbars Sep 23, 2026
@claude
claude Bot requested a review from r3dbars September 23, 2026 17:23
Review follow-ups for clickable timestamps:
- Move the player's idle source pick into MeetingAudioPlayback so a
  timestamp click plays the source the menu shows (Mic stays Mic).
- A click past the end of the loaded source beeps instead of finishing
  instantly and resetting the player.
- Re-apply the start position after NSSound.play() so a fresh start
  from a timestamp can't rewind to zero.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VGTtv8jq8HdgQtKY9PvCcm
- AGENTS.md and Sources/UI/CLAUDE.md no longer say transcript rows are
  static; rows still never follow the playhead.
- Cover 3-digit minutes (125:30), which the transcript writer emits for
  meetings past 100 minutes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VGTtv8jq8HdgQtKY9PvCcm
Ported from #1785 (9faf88d): the Core package tests stalled on this PR
(CoreAudioSystemAudioCaptureTests, XCTest stall detector), and #1785
identifies this test as the likeliest cause. It never created its temp
root, so start() went on to probe real microphones and TCC.

(cherry picked from commit 9faf88d)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VGTtv8jq8HdgQtKY9PvCcm
@claude

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

spm-tests (and so build-and-test) failed on db02fe2. XCTest's stall detector aborted in CoreAudioSystemAudioCaptureTests.testDuplicateFinishCannotCancelTailBeforeFirstFinisherReachesBackend. This isn't this PR's failure. The PR only touches the app's Home UI, docs and a fast test, while spm-tests builds just TranscriptedCore and its tests. app-build and checks passed on the same commit.

It's the same intermittent Core test stall that #1785 is chasing. I ported #1785's suspected fix (9faf88d, test-only: AudioInitializationTests no longer probes real mics and TCC) and merged current main. That's pushed as the new head. I can't re-run jobs on this repo (403), so the new push is the retry.


Generated by Claude Code

Deep review follow-ups for clickable timestamps:
- S1: right after a meeting saves (or if its Mix failed) the audio is
  split Mic/System and System is the default, so clicking a You row
  played System. With no user pick, a row click now plays its own
  channel's track. The Mix, a single recording, or a user pick still win.
- M1: remember the picked source by file stem, keyed by the meeting's
  audio folder, so it survives the Home rescan after recompression.
- M3: per-row timestamp accessibility identifiers.
- M4: seek() re-applies the time after play(), like the start path.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VGTtv8jq8HdgQtKY9PvCcm
@claude

claude Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

checks failed on 0b11240: 15897/15898 fast tests passed. The one failure is ParakeetRecoveryStateTests.swift:282, "wedged setter must release notification handler within native budget".

This isn't this PR's failure. That test covers the dictation engine's AUHAL binding intent, which this PR doesn't touch (Home UI, meeting playback, docs). The same test and the same Parakeet code passed on this PR's previous head daba6f1. The assertion is a wall-clock bound: a 50 ms native timeout has to return within 200 ms of systemUptime. So it's load-sensitive on a busy shared runner.

I found no existing fix. Proposed patch, kept out of this PR: raise that bound to something like < 1.0. That still proves the handler is released by the bounded timeout rather than hanging, without a 150 ms scheduling margin. I can't re-run jobs here (403). This PR will get a fresh CI run when current main is merged in after 1.1.62 ships, and I'm keeping it watched until it's green.


Generated by Claude Code

claude Bot pushed a commit that referenced this pull request Sep 23, 2026
The wedged-setter check allowed 200ms for a 50ms budget and went red
on a loaded runner (#1772). Allow 1s; a handler that is never released
still fails. The success case had the same shape: a 40ms sleep that
overran its 250ms budget would time the setter out first. Give it a 5s
budget and require a wake well before it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJaM9jijGY2JYUhL6GsS9Z
…stamps-y7tfkj

# Conflicts:
#	Tests/TranscriptedCoreTests/AudioTests/AudioInitializationTests.swift
@claude
claude Bot marked this pull request as ready for review September 24, 2026 01:09
@claude
claude Bot merged commit 0fa11fe into main Sep 24, 2026
7 checks passed
@claude
claude Bot deleted the claude/clickable-timestamps-y7tfkj branch September 24, 2026 01:10
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.

2 participants