Skip to content

fix: v0.7.x ultrareview follow-ups (#55–62)#63

Open
fmasi wants to merge 7 commits into
mainfrom
fix/v0.7.x-review-followups
Open

fix: v0.7.x ultrareview follow-ups (#55–62)#63
fmasi wants to merge 7 commits into
mainfrom
fix/v0.7.x-review-followups

Conversation

@fmasi

@fmasi fmasi commented May 20, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes for all 8 bugs surfaced by the ultrareview of #46. Bundled on one branch because several touch the same files (TranscriptionRunner, MenuView) and separate PRs would conflict. One commit per fix.

Issue Severity Fix
#55 bug_007 normal tagWithSourcePrefix made idempotent → no more Local Local Speaker N (+2 tests)
#56 bug_004 normal sort chunks by index in SpeakerReconciler.reconcile + finalize audio paths (+1 test)
#59 bug_003 normal skip chunk-audio merge when raw .wavs present → no deletion of kept evidence
#60 bug_010 normal per-segment echo dedup in crash-recovery run() (own DBs, not last-write-wins merge)
#57 bug_009 normal preserve contextOverheadPercent / maxOutputTokens on Settings Save
#58 bug_002 normal resync menu mic selection from config via onChange
#61 bug_011 nit time-decay reset for xpcRetryCount
#62 bug_001 nit guard --debug log-process terminate() against unlaunched task

Notes

  • Logic that lives in the testable TranscriberCore layer got new unit tests (bug_007, bug_004). Fixes in the app layer (TranscriptionRunner/SettingsView/MenuView/CLIHandler) aren't reachable by the CommandLineTools test target, so they're verified by swift build + the full suite.
  • bug_010's restructure leaves the common single-segment path behaviorally identical; only multi-segment crash recovery changes.

Test plan

  • swift test --filter TranscriberTests green (459/459) after each commit
  • swift build clean
  • Manual: chunked dual-stream recording → single Local Speaker N / Remote Speaker N, merged audio plays in order
  • Manual: change default mic in Settings → menu label updates without restart

🤖 Generated with Claude Code

fmasi and others added 7 commits May 20, 2026 12:36
Chunked dual-stream recordings tag speaker labels at chunk time in
ChunkProcessor and again in TranscriptionRunner.finalize(), producing
"Local Local Speaker N" / "Remote Remote Speaker N" in the saved
transcript, rename dialog, exports, and summary input. Guard against
re-tagging already-prefixed segments so repeated calls are safe.

Fixes #55

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Parallel ChunkProcessor tasks append to sessionState.chunks in
completion order, not recording order. SpeakerReconciler seeded its
global speaker namespace from whichever chunk landed first, and
AudioConcatenator stitched the merged archive in that same order —
scrambling audio relative to the (time-sorted) transcript.

Sort by chunk index inside SpeakerReconciler.reconcile (deterministic
regardless of caller order) and sort chunkAudioPaths in finalize().

Fixes #56

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AudioConcatenator deletes its source files after a successful export.
When a chunk is still a raw .wav — AAC archival failed (gotcha #38) or
a single-stream recording that never archived — merging re-encoded the
lossless evidence to a lossy .m4a and then deleted the originals,
breaking the "raw archive is never modified" invariant.

Only concatenate when every chunk is an archived .m4a; otherwise keep
the chunk files separate and log the reason.

Fixes #59

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each recovery segment is diarized independently, so "Speaker 1" in one
segment is a different person from "Speaker 1" in the next. run() merged
the per-segment speaker databases last-write-wins and then ran echo
dedup once over all segments, so dedup compared against whichever
segment wrote a given label last — dropping genuine local speech or
keeping mic bleed.

Tag, sort, and dedup within each segment using that segment's own
databases (mirroring ChunkProcessor), then accumulate. The common
single-segment path is unchanged.

Fixes #60

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SettingsView rebuilt SummaryConfig from only 6 of 8 fields, so
contextOverheadPercent and maxOutputTokens (config.json-only advanced
tunables with no UI) were silently reset to defaults on every Save.
Carry the existing values through.

Fixes #57

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bug_002: selectedMicId was seeded once in init() and never resynced, so
changing the default mic in Settings didn't update the menu label or the
device used on the next Start Recording until app restart. Add an
onChange to resync when config.lastMicrophoneDeviceId changes.

bug_011: xpcRetryCount only ever reset on a fresh startRecording, making
">2" a session-wide cumulative cap. A long recording with sparse transient
crashes could be killed even though every prior restart succeeded. Decay
the counter when the previous crash was >10 min ago; a genuine crash loop
(crashes within ms) still trips the cap.

Fixes #58
Fixes #61

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleTranscribe assigned logProcess unconditionally after `try?
proc.run()`. If /usr/bin/log failed to spawn, the defer called
terminate() on a never-launched Process, raising an uncatchable
NSException that crashes the CLI on exit. Only retain the process when
run() succeeds, and note the failure on stderr.

Fixes #62

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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