Status legend: ✅ accepted · 🔬 accepted pending Sprint 0 validation · Changing any ADR requires a PR approved by two team members.
Context. The original prototype used a FastAPI server only to host Whisper and proxy to HuggingFace's cloud Inference API. Hard requirements: zero connectivity operation, zero recurring cost, privacy-first for a vulnerable population. Decision. Delete the backend entirely. All STT, translation, and TTS run on the iPhone. Consequences. No auth/CORS/hosting/ops; App Review simpler; privacy label "Data Not Collected" is provably true (CI bans networking APIs). Cost: all models must fit the per-app memory ceiling (~3 GB on 6 GB-RAM devices). Rejected. Self-hosted server (cost, connectivity); hybrid (complexity, weakens privacy claim).
Context. The prototype used server-side openai-whisper small for transcript
and language detection. Hearth needs broad multilingual coverage even though the final launch-
language list is deferred; Apple's SFSpeechRecognizer / iOS 26 SpeechAnalyzer do not cover
several languages under consideration.
Decision. WhisperKit (MIT), CoreML, small
multilingual (~500 MB), bundled. Language detection replaces the /process endpoint.
Consequences. ANE-accelerated, maintained Swift API. small is the accuracy floor for
low-resource languages; base is the fallback if memory profiling forces it (quality loss —
measure first).
Rejected. Raw whisper.cpp (more glue, no CoreML/ANE path); Apple Speech (language coverage).
Context. Prototype called 4 CohereLabs/tiny-aya-* variants via HF cloud. On-device is
mandatory. Unknowns (NOT resolvable from the prototype repo): parameter count, GGUF
convertibility, on-device tokens/sec, exact license text. Apple Translation worked in Airplane
Mode only after language assets were downloaded/prepared; fresh Airplane Mode without downloaded
assets failed, so it does not satisfy the offline-from-first-launch goal as the planned v1 path.
Decision. Use only tiny-aya-earth as the provisional translation engine, 4-bit
quantized GGUF, via the llama.cpp Swift bindings. Development fixtures are useful for integration,
but final language support is not an engine-implementation prerequisite. Implement the prompt
and output-cleaning contract in docs/specs/aya-spike.md (the prefix-strip list encodes real
model behavior observed during the prototype spike).
Drop the earth/fire/water/global picker.
August integration gate: Tiny-Aya Earth runs on a supported iPhone, stays responsive, and
fits the app-size/memory budget. The license must permit Hearth's free non-commercial use.
Translation accuracy is explicitly not an August gate and remains unvalidated until the team
chooses target languages and recruits fluent speakers.
Fallback if gate fails: Apple Translation framework (iOS 17.4+, on-device, free) for
languages with prepared Apple assets only. Low-resource differentiation would be lost. Both engines
implement the same TranslationEngine protocol, so the swap is a one-line DI change.
License note. Aya models are CC-BY-NC. Hearth is free and non-commercial — compliant —
but attribution is required in Settings → About.
Context. 4 students new to iOS, ~130 total person-hours, ~6 screens, agent-heavy workflow.
Decision. SwiftUI; MVVM with plain @Observable view models; engine access through
protocols injected via init; async/await only.
Why. The prototype migration established a direct conceptual map from conversation state to
ConversationViewModel, domain structs, and SessionPhase. Mocks make all UI work
parallelizable and testable without models loaded.
Rejected. TCA / Clean Architecture layering (learning + ceremony cost dwarfs benefit at
this scale); UIKit (steeper curve, no benefit for custom-drawn UI).
Decision. Supported: iPhone 12 Pro/Pro Max, 13 Pro/Pro Max, 14/14 Plus and ALL newer. Recommended (marketing): iPhone 15 Pro+. Unsupported (4 GB RAM — app memory ceiling ~2 GB cannot hold Whisper + LLM): iPhone 11, 12, 12 mini, 13, 13 mini, SE all gens. Enforcement. Launch-time RAM check with a friendly explanation screen (App Review requires no-crash on any installable device); App Store description states requirements.
Decision. Messages exist in memory only; wiped on end-session and on >5 min background.
UserDefaults only for onboarding-seen + last manual language. No SwiftData/CoreData/Keychain.
Why. Matches privacy promise; transcript history is cut from v1; deletes an entire
workstream. v2 transcript feature would add encrypted storage behind a staff PIN.
Decision. Allowed packages: WhisperKit, llama.cpp Swift package. Nothing else — no analytics, no crash SDKs (Xcode Organizer suffices), no UI kits. Agents may not add dependencies; humans may, only by amending this ADR in a reviewed PR. Why. Primary defense against agent-introduced dependency sprawl and supply-chain surface.
Decision. whisper-small CoreML + tiny-aya Q4 GGUF live in Hearth/Resources/Models/,
inside the app binary. Total app ≤ 3.5 GB.
Why. Apple hosts the download for free (zero recurring cost); app is fully functional
offline from first launch (hard requirement). Cost: large initial Wi-Fi download — acceptable;
state it in the App Store description.
Rejected. First-launch download from HuggingFace CDN (violates offline-from-install;
fragile in App Review; third-party bandwidth terms can change).