Commit graph

11 commits

Author SHA1 Message Date
f8e8dca081 Med editor: wrap day chips so the whole week is visible; Enhet follows Form
The 7 day chips overflowed the dose-time dialog (no scroll, week cut
off) — FlowRow wraps them, with full day names for TalkBack. Enhet
auto-fills from Form while it still holds the previous form's default
(tablett/kapsel/ml/dråper/doser), killing the apparent Enhet/Form
overlap; manual overrides like IU stick. Styrke stays Styrke — it
holds '500 mg', which is neither unit nor form.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:58:53 +02:00
4f1e76eca2 Release signing from gitignored keystore.properties
Keystore at ~/.keystores/meddetsamme-release.jks (4096-bit RSA, 30 yr),
credentials in keystore.properties — both outside version control. A
clone without the file still builds (unsigned release) instead of
failing, so nothing breaks for CI or a fresh checkout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:27:40 +02:00
2e7eadeb39 UI buildout: today screen, med editor with FEST autocomplete, schedule editor, settings
Milestone 9. Hand-rolled back-stack navigation (4 screens don't justify
navigation-compose). DoseActions is now the single implementation of
Taken/Snooze/Skip/Undo shared by notification receiver and UI — two
code paths for 'taken' would eventually disagree about inventory.
Screens read DAO Flows directly; every schedule mutation re-arms
alarms via armAll() (idempotent, OS-deduplicated). One-time battery
prompt + POST_NOTIFICATIONS request on the today screen. Settings:
S3/Garage config, typed-passphrase export/import (replace-all import
re-arms alarms), FEST refresh, PDF share. TalkBack: merged semantics
with full descriptions on dose cards, content descriptions on icon
buttons and day chips. Doctor PDF locale pinned to Bokmål — verified
on emulator after catching English month names on an en-US device.

Emulator-verified end-to-end: battery prompt → system dialog, med
created via editor (validation catches bad input), dose time added →
exact alarm armed immediately, Ta nå → TAKEN + inventory 20→19 +
reactive UI update, Angre → PENDING + inventory restored, settings
renders, PDF generated and share sheet opened.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:22:29 +02:00
c4d0ed9c3b FEST offline lookup: dataset contract, cached repository, ranked search
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>
2026-06-10 14:04:05 +02:00
e9a6679242 Doctor summary: one-page PDF via PdfDocument + FileProvider share
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>
2026-06-10 14:02:34 +02:00
c66bee5cf0 age crypto via kage, verified against the CCTV scrypt vectors
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>
2026-06-10 14:00:34 +02:00
8f5b18cf68 Backup: versioned JSON serializer, SigV4 + S3 client, JCE crypto, daily worker
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>
2026-06-10 13:54:13 +02:00
46a5d7e98e Reminder subsystem: exact alarms, escalation, boot re-arm — verified on emulator
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>
2026-06-10 13:48:39 +02:00
63f1ecd9fd Schedule engine: pure occurrence/supply/renewal math with 22 unit tests
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>
2026-06-10 13:36:51 +02:00
b884885e40 Data layer: Room entities, DAOs, database with exported schema
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>
2026-06-10 13:34:41 +02:00
7256c49112 Project skeleton: Gradle 9.4.1 wrapper, AGP 9.2.1, Compose, manifest with reminder permission set
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>
2026-06-10 13:31:39 +02:00