Milestone 8. docs/fest-dataset.md is the interface to the out-of-repo
server job that flattens the M30 Rekvirent extract (FEST, not
Felleskatalogen — decision #8) into slim JSON. The repository
downloads on demand, parses BEFORE overwriting the cache (a failed
refresh can never clobber a working dataset), and serves all searches
from memory. Ranking (prefix > word-prefix > substring) is a pure
object with tests. Every failure mode degrades to manual entry —
autocomplete is a convenience, never a gate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 7. Human-readable A4 summary (meds, schedule, supply days,
rx expiry/reit, 30-day adherence, notes) — deliberately distinct from
the machine-readable JSON backup, zero PDF dependencies. The Norwegian
schedule phrasing (ScheduleText) and adherence math are pure and
unit-tested; a wrong schedule line on a doctor's desk is a clinical
communication error, so it gets engine-grade testing. FileProvider
exposes only cacheDir/summaries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 6. kage 0.4.0 over Jagged: Android-first (explicitly supports
API 26, matching minSdk), Kotlin, ships its own primitives through
BouncyCastle instead of relying on JCA ChaCha20-Poly1305 which Android
only provides from API 28. AgeBackupCrypto sits behind the same
BackupCrypto interface as the JCE baseline; age is now first in all()
so new encrypted backups are written as binary age v1 files,
decryptable anywhere with 'age -d' (decision #7). Old MDS1/JCE backups
keep decrypting via format auto-detection. The C2SP/CCTV scrypt vector
subset (19 files) is vendored into test resources: both success
vectors decrypt with matching payload SHA-256, all 17 failure vectors
are rejected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 5. One BackupManager entry point so export, import and
auto-backup share the same serializer and crypto by construction.
Explicit DTOs decouple the backup contract from the Room schema.
JCE baseline (PBKDF2-HMAC-SHA256 600k → AES-256-GCM) behind the
BackupCrypto interface with format auto-detection on decrypt; age
lands behind the same interface in milestone 6. SigV4 signer verified
against AWS's published documentation example signature. Settings in
SharedPreferences encrypted under a non-exportable AndroidKeyStore key
— replaces the now fully deprecated androidx security-crypto with the
same construction (~70 lines, zero deps); threat-model comments state
plainly that the stored auto-backup passphrase only protects against
bucket compromise. WorkManager daily periodic job, inexact by design.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 4. AlarmScheduler is the only AlarmManager writer; one stable
PendingIntent requestCode per dose-time means re-arming replaces and can
never stack (the no-drift invariant is OS-enforced). DoseAlarmReceiver
handles both occurrence and escalation alarms idempotently and always
re-arms the next occurrence before anything else so the chain can't
break. nagCount lives in dose_log so the ~6-nag cap survives reboot.
SCHEDULE_EXACT_ALARM added maxSdk 32 (USE_EXACT_ALARM is 33+ only).
Emulator-verified (API 35): exact alarm armed (window=0,
policy_permission), fired on time, HIGH notification with Tatt/Utsett,
escalation armed +10 min, next day re-armed, Tatt → TAKEN + inventory
10→9 + escalation cancelled, reboot → BootReceiver re-armed everything.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 3. Single authority for DoseTime semantics: weekly mask
(bit 0 = Sunday) vs every-N-days from anchor (mask ignored when
intervalDays > 1). nextOccurrence is strictly-after by contract so the
alarm layer can pass the occurrence it just fired and never re-arm the
same minute. Wall-clock LocalDateTime throughout — instant conversion
happens at the alarm edge, so an 08:00 dose stays 08:00 across DST.
Consumption rate is prospective (from schedule) because days-of-supply
predicts forward; historical consumption stays in the DAO.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 2. Medication / DoseTime / DoseLog per the brief's model.
DoseLog.doseTimeId is SET_NULL (schedule edits must not erase adherence
history) while medId CASCADEs; DoseLog.amount is snapshotted at logging
time so later dose changes don't rewrite the record. Inventory decrement
is a single SQL UPDATE clamped at 0 to stay race-free under escalating
notifications. Enums stored as TEXT; schema exported to app/schemas as
the migration baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 1 of the build brief. Version catalog pins verified against
Maven Central / Google Maven on 2026-06-10; Kotlin held at 2.3.10 to
match AGP 9.2's bundled KGP (built-in Kotlin) because KSP has no
Kotlin 2.4 release yet. compileSdk 37 (forced by core-ktx 1.19),
targetSdk 35 per brief. allowBackup=false + full dataExtractionRules
opt-out; own encrypted backup comes in milestone 5.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>