Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@ let package = Package(
]),
]
),
.testTarget(
name: "quillTests",
dependencies: ["quill"]
),
]
)
17 changes: 10 additions & 7 deletions Sources/quill/Transcription/TranscriptionCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ private struct SessionMeta {

/// Canonical transcript. Property names are the JSON schema — this struct
/// exists to be serialized.
private struct Transcript: Codable {
struct Transcript: Codable {
struct Segment: Codable {
let speaker: String
let start_ms: Int
Expand All @@ -244,16 +244,19 @@ private struct Transcript: Codable {
let created_at: String
let segments: [Segment]

/// Write transcript.json and render transcript.md. Both writes are atomic
/// (temp file + rename), so a partially written transcript never exists on
/// disk — resumePending treats presence of transcript.json as "done".
/// Render transcript.md, then write transcript.json as the completion
/// marker. Both writes are atomic (temp file + rename), and writing JSON
/// last ensures resumePending retries if either artifact fails.
func write(to dir: URL) throws {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
try encoder.encode(self)
.write(to: dir.appendingPathComponent("transcript.json"), options: .atomic)
try Data(rendered(title: dir.lastPathComponent).utf8)
let json = try encoder.encode(self)
let markdown = Data(rendered(title: dir.lastPathComponent).utf8)

try markdown
.write(to: dir.appendingPathComponent("transcript.md"), options: .atomic)
try json
.write(to: dir.appendingPathComponent("transcript.json"), options: .atomic)
}

private func rendered(title: String) -> String {
Expand Down
40 changes: 40 additions & 0 deletions Tests/quillTests/Transcription/TranscriptTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation
import Testing

@testable import quill

struct TranscriptTests {
@Test("A failed Markdown write does not leave the completion marker")
func failedMarkdownWriteDoesNotLeaveCompletionMarker() throws {
let fileManager = FileManager.default
let session = fileManager.temporaryDirectory
.appendingPathComponent("quill-\(UUID().uuidString)", isDirectory: true)
try fileManager.createDirectory(at: session, withIntermediateDirectories: true)
defer { try? fileManager.removeItem(at: session) }

try fileManager.createDirectory(
at: session.appendingPathComponent("transcript.md", isDirectory: true),
withIntermediateDirectories: true
)

let transcript = Transcript(
engine: "parakeet",
model: "test-model",
created_at: "2026-07-28T00:00:00Z",
segments: []
)

do {
try transcript.write(to: session)
Issue.record("Expected writing transcript.md over a directory to fail.")
} catch {
// Expected: transcript.md is a directory.
}

let completionMarker = session.appendingPathComponent("transcript.json")
#expect(
fileManager.fileExists(atPath: completionMarker.path) == false,
"A failed transcript.md write must leave the session pending."
)
}
}