Skip to content

fix(dictation): make daily transcript appends crash-safe (atomic write)#1325

Draft
r3dbars wants to merge 2 commits into
mainfrom
claude/epic-benz-b4ba15
Draft

fix(dictation): make daily transcript appends crash-safe (atomic write)#1325
r3dbars wants to merge 2 commits into
mainfrom
claude/epic-benz-b4ba15

Conversation

@r3dbars

@r3dbars r3dbars commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Why

Dictation is the highest-frequency capture surface, and its day-file save was the one capture write that wasn't atomic. Everything else (meeting artifacts, journals, JSON state) already uses Data.write(options: .atomic) / String.write(atomically: true). Dictation appends did not.

DictationTranscriptWriter.appendSection opened the existing Dictations_YYYY-MM-DD.md with a FileHandle, seeked to the end, and wrote the new section in place. A crash, power loss, or interruption part way through that write would leave a half-written section glued onto the file and corrupt every earlier entry for that day.

What changed

  • Sources/Dictation/DictationTranscriptWriter.swiftappendSection now reads the existing day file, concatenates the new section, and writes the whole file atomically via String.write(atomically: true) (write-temp-then-rename) — the same safe-write pattern the rest of the app uses. The prior content survives untouched until the new file is renamed into place, so a failed write can lose only the in-flight section, never the existing day. Removed the now-unused fileEndsWithBlankLine byte-probe helper (replaced by a hasSuffix("\n\n") check on the in-memory string).
  • New-file creation (String.write(atomically: true)) and the delete-rebuild path (DictationTranscriptStore) were already atomic — this closes the last gap.

Markdown layout is unchanged (same \n\n separator and section shape), so the TranscriptedCaptureKit day-file parser needs no change.

Test

Added a regression suite in Tests/DictationTranscriptWriterTests.swift: it saves a first entry, then simulates an interrupted/failed append by making the day folder read-only so the atomic temp write throws mid-write, and asserts the existing day file is byte-for-byte intact with the second (doomed) entry never leaking in.

Verification

  • bash build.sh --no-open
  • bash run-tests.sh ✅ (10157 passed, 0 failed)

🤖 Generated with Claude Code

r3dbars and others added 2 commits June 25, 2026 06:09
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dictation is the highest-frequency capture surface, and its day-file
append was the one capture write that wasn't atomic: appendSection opened
the existing Dictations_YYYY-MM-DD.md with a FileHandle, seeked to the end,
and wrote the new section in place. A crash or interruption part way through
that in-place write would glue a half-written section onto the file and
corrupt every earlier entry for the day.

Rewrite the append to match the safe-write pattern the rest of the app
already uses for meeting artifacts (atomic write-temp-then-rename): read the
existing day file, concatenate the new section, and write the whole file with
String.write(atomically: true). The prior content survives untouched until
the new file is renamed into place, so a failed write can lose only the
in-flight section, never the existing day. New-file creation and the
delete-rebuild path were already atomic; this closes the last gap.

Add a regression test that simulates an interrupted append (read-only day
folder so the atomic temp write throws mid-write) and asserts the existing
day file is byte-for-byte intact with no partial second entry leaked in.

Co-Authored-By: Claude Opus 4.8 (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