Declared-but-empty projects persist locally (UserDefaults) until a
note carries the name, at which point the project syncs via notes as
usual. Empty declared projects can be swipe-deleted.
Claude-Session: https://claude.ai/code/session_014esDWi42URLEC6Cj17hGQ3
Notes gain an optional project (schema v4, additive). Projects derive
from note values like tags — no separate entity. Tab naming
(Projects/Categories) is a Settings choice applied across the UI.
Claude-Session: https://claude.ai/code/session_014esDWi42URLEC6Cj17hGQ3
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
Watch recordings arrived on the phone as 28-byte header-only m4a files
(record() reported success but no audio was ever captured). Activate the
audio session via watchOS's async activate(options:), drop the .spokenAudio
mode experiment, match the phone recorder's proven AAC settings
(44.1 kHz / 48 kbps), and surface silent failures: an AVAudioRecorderDelegate
catches encode errors mid-take, and a stop-time guard detects a dead
header-only file, deletes it, and shows the failure on the watch instead of
shipping an unplayable note to the phone.
Claude-Session: https://claude.ai/code/session_014esDWi42URLEC6Cj17hGQ3
Replace the Notes list's searchable accessory with a Tab(role: .search)
— the separated magnifier at the trailing edge of the iOS 26 tab bar —
backed by a new SearchView over text, tags, and capture location. The
Notes list drops its search state and always shows the context shelf
plus all notes.
Claude-Session: https://claude.ai/code/session_01GKfAiKiQKLDTmbiGva4FGE
A tester on real hardware has no console, so a failed start looked like
a dead button and there was no way to tell which build the watch had
actually installed. Show the stamped version/build under the record
button, display the last start error in place of the hint text, and
fall back to the default audio session mode if .spokenAudio is rejected
with the .record category.
Claude-Session: https://claude.ai/code/session_01GKfAiKiQKLDTmbiGva4FGE
- TranscriptionSettings/Service route per-locale to SpeechTranscriber or
DictationTranscriber (union of both supported sets, speech preferred);
neutral-confidence ranking fallback keeps mixed-engine auto-pick working
- NotesListView: text + voice capture buttons above the list (glassProminent),
search moved to the iOS 26 toolbar search accessory (.minimize)
- Changelog entries for languages, capture buttons, search placement
The watch app's permission-denied state was a computed property reading
AVAudioApplication.shared.recordPermission — external state Observation
cannot track — so after a denied prompt the UI never flipped to the
'Microphone access is off' screen and the record button silently did
nothing. Store the denied flag as a tracked property instead, updated
on every permission request.
Also: log (instead of swallow) recorder start failures, drop phantom
recordings when no file was written (the watch simulator has no audio
input device), sweep orphaned metadata sidecars on reconcile, and add a
DEBUG-only 'autorecord' launch argument for CLI-driven testing.
Claude-Session: https://claude.ai/code/session_01GKfAiKiQKLDTmbiGva4FGE
- Schema v2: Note.AudioInfo, .m4a sidecar next to the note JSON in iCloud
- AudioRecorderService (iOS/macOS) + SpeechAnalyzer transcription pipeline
with enabled-language set and confidence-based auto-pick, re-transcribe menu
- Settings > Transcription > Languages with asset download/reserve handling
- watchOS app + accessory complication: one-button recorder, WCSession
transferFile to phone, WatchInbox staging, direct-upsert ingestion
- Player UI, transcription status chips, quick-capture mic in notes list
- Version 0.2, changelog + README updated
The Mac App Store requires a globally unique CFBundleName, which follows
PRODUCT_NAME — 'Notes' is taken (by Notes). The module name stays 'Notes'
via PRODUCT_MODULE_NAME so code and tests are untouched; TEST_HOST is
pinned explicitly since xcodegen derives it from the target name.
IndieAbout: About section in iOS Settings and macOS About window; LICENSE
switched to the portfolio-standard ISC text (reflowed for in-app rendering).
IndieBackup: files-only backup/restore of the iCloud container documents
('.notesbackup' document type on both platforms), SwiftData cache rebuilt
via SyncEngine.rebuildCache() after restore, monitors suspended during the
file swap, onOpenURL import with pre-launch queueing.
Release pipeline: Scripts/release.sh (ios|mac|all) archives and uploads via
xcodebuild + ASC API key; platform-partitioned build numbers (iOS even,
macOS odd) since both targets share one bundle ID and build sequence.
XcodeGen project (Notes / NotesMac / NotesTests), Swift 6 strict concurrency,
iCloud-document storage via IndieSync with a SwiftData cache, automatic
capture context (location, place, time zone, device) on every note,
context-based relevance ranking with a 'Right here, right now' shelf,
tags, search, and the square.and.pencil-on-red app icon.