Decouple transcripts from note text; end-to-end watch transfer acks

Schema v3: transcription attempts accumulate on AudioInfo (text, locale,
confidence, forced flag) — latest attempt is displayed, payload.text is
purely user writing, v2 notes migrate at read time. Editor shows the
transcript in its own live-updating section with insert-into-note; search
matches transcripts weighted by confidence. Watch recordings are now
deleted only on the phone's durable-ingest ack (transferUserInfo), closing
every phone-side loss window; failed sidecar writes unstage instead of
orphaning, and unreadable inbox recordings are surfaced in logs.

Claude-Session: https://claude.ai/code/session_014esDWi42URLEC6Cj17hGQ3
This commit is contained in:
2026-07-16 12:29:49 -04:00
parent c29d25748a
commit c5c90e9441
15 changed files with 546 additions and 55 deletions
+3 -3
View File
@@ -77,13 +77,13 @@ struct NoteDocumentTests {
/// A file stamped with a newer schema (and carrying a field this build
/// doesn't model) must be quarantined by the gate Codable silently drops
/// the unknown key, so partial-decoding then rewriting would downgrade it.
@Test func forwardGateQuarantinesV3Fixture() throws {
@Test func forwardGateQuarantinesNewerFixture() throws {
let id = ULID(millisecondsSinceEpoch: 1_700_000_000_000, randomHi: 3, randomLo: 4)
let json = """
{
"meta": {
"id": "\(id.stringValue)",
"schemaVersion": 3,
"schemaVersion": \(Note.currentSchemaVersion + 1),
"createdAt": "2023-11-14T22:13:20Z",
"modifiedAt": "2023-11-14T22:13:20Z"
},
@@ -98,7 +98,7 @@ struct NoteDocumentTests {
}
"""
let note = try DocumentCoder.decode(Note.self, from: Data(json.utf8))
#expect(note.meta.schemaVersion == 3)
#expect(note.meta.schemaVersion == Note.currentSchemaVersion + 1)
#expect(!note.isReadable)
}