8000 Release OpenGlucose 0.2.0 (metabolic context cockpit) by shroominic · Pull Request #126 · shroominic/OpenGlucose · GitHub
[go: up one dir, main page]

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
68edaf7
Fix sensor warmup and live notification recovery
Aug 15, 2026
0f9efe0
feat: add compact today cockpit
Aug 15, 2026
9e04b26
docs: note today cockpit
Aug 15, 2026
dda3ae6
feat(core): add local-first journal service
Aug 15, 2026
d913020
feat(core): add evidence-backed metabolic observations
Aug 15, 2026
b7b7671
feat: add source-aware health context imports
Aug 15, 2026
2c4b271
chore: clear changelog merge marker
Aug 15, 2026
960adf0
fix: document health enum fallthroughs
Aug 15, 2026
7670851
docs: document bounded health context imports
Aug 15, 2026
bc7dd7f
feat(app): show evidence-backed observations in today cockpit
Aug 15, 2026
f0941df
feat(alerts): add local glucose alert history
Aug 15, 2026
fddaff0
fix: suppress warmup alert episodes
Aug 15, 2026
70f9094
fix(alerts): keep history in protected local state
Aug 15, 2026
de87b79
chore: finish feature train notes
Aug 15, 2026
bffbb39
feat(ui): add manual health context integration controls
Aug 15, 2026
659f863
feat(journal): add Today quick-add context sheet
Aug 15, 2026
41c324a
feat(ui): add mixed local body timeline
Aug 15, 2026
61e326f
feat(core): add fail-closed local data deletion contract
Aug 15, 2026
5b414d5
feat(ui): surface journal context in body timeline
Aug 15, 2026
3053594
feat: add redacted glanceable surface contract
Aug 15, 2026
cdb5e84
feat(ai): add privacy-safe insight surface
Aug 15, 2026
8a4d6c1
test(ai): fix evidence label matcher
Aug 15, 2026
6b65ce2
feat(core): add meal response analytics
Aug 15, 2026
a470c33
feat(ui): load local context into body timeline
Aug 15, 2026
c8864ba
feat(ui): add meal response cockpit card
Aug 15, 2026
bf42694
fix(ui): avoid duplicate body timeline empty state
Aug 15, 2026
8a1b40d
feat(ui): show meal response in Today cockpit
Aug 15, 2026
e01c896
fix(ui): preserve meal response safety copy
Aug 15, 2026
4828e85
merge: sync feature train with main release
Aug 15, 2026
fa42a0e
style: format AI insight surface
Aug 15, 2026
f23c438
fix: satisfy app analyzer safety lints
Aug 15, 2026
49cadcc
test: align demo dashboard with warmup history policy
Aug 15, 2026
93aff58
fix: close final AI and Health context regressions
Aug 15, 2026
66315e0
Merge branch 'feature/metabolic-context-train' into release/v0.2.0-prep
shroominic Sep 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,41 @@ expectations are defined in [docs/compatibility.md](docs/compatibility.md).

## [Unreleased]

### Added

- Added a local-only glucose alert evaluator and bounded episode history for
low, high, and stale-reading conditions. The foreground monitor persists
transitions in the restricted local-state store without producing native
notification or lock-screen payloads; platform surfaces can consume the
same deterministic contract later.
- Added bounded, read-only Apple Health and Health Connect context import for
user-selected activity, sleep, heart-rate, energy, and distance categories,
with source-aware local deduplication and explicit permission handling.
- Added a compact Today quick-add sheet for meals, exercise, and notes. Entries
stay in the local journal, validate before saving, and show a same-day count
in the Today cockpit; no background or network sync is introduced.
- Added a compact local Body timeline that interleaves glucose, journal,
imported activity/sleep/heart-rate rows, and evidence-backed observations
with source and freshness labels.
- Added foreground-only context loading for the Today body timeline, including
explicit loading, empty, and fail-closed error states after Health sync,
journal capture, app resume, and pull-to-refresh.
- Added local meal-response coverage and peak/timing summaries to Today with
explicit insufficient-data states and non-causal wellness framing.
- Added an opt-in AI insight surface with disabled, loading, empty, error, and
evidence-backed states; it remains off by default and never exposes journal
text or provider secrets.
- Added a redacted-by-default, adapter-neutral glanceable surface contract for
current glucose, freshness, warmup, context summary, and alert state. Native
notification/widget payloads remain unchanged until a platform mapping is
reviewed separately.

### Changed

- The dashboard now opens with a compact "Today at a glance" cockpit showing
the latest reading, 24-hour context, freshness, and a conservative sparse-
data explanation before longer history and pattern views.

## [0.1.6] - 2026-08-31

### Added
Expand Down
4 changes: 4 additions & 0 deletions docs/architecture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ The meaningful critical journey is scan, connect, synchronize history, display
freshness accurately, handle a disconnect or stale reading, reconnect, and
recover persisted state without duplication or time shifts.

Compact platform surfaces use the adapter-neutral
[glanceable surface contract](glanceable-surfaces.md); native payload mappings
remain separate, opt-in changes.

## Cross-cutting constraints

- **Safety:** OpenGlucose is wellness/reference software. No component may
Expand Down
40 changes: 40 additions & 0 deletions docs/architecture/adr/0007-evidence-backed-observations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# ADR 0007: Evidence-backed observations and AI output boundaries

- Status: Accepted
- Date: 2026-08-15
- Owners: `@shroominic`

## Context

The Today experience needs compact metabolic context without presenting sparse
or correlated health data as a clinical conclusion. Optional AI can help users
explore patterns, but free-form provider text must not become an unsupported
medical, dosing, treatment, or emergency decision path.

## Decision

`cgm_core` exposes `MetabolicObservationEngine`, a deterministic provider-free
calculation over timestamped glucose readings and available local context. It
reports descriptive aggregates for glucose coverage, level/range/variability,
upward excursions, journal meals, imported activity, sleep, and heart rate.
Every observation carries one or more typed `ObservationEvidence` records with
a bounded window, aggregate value, sample count, and source label. Raw readings
and note bodies are never included in evidence or AI prompts.

`AiInsight` stores the evidence attached to the generated insight and a
persisted wellness safety boundary. `InsightService` includes evidence labels in
the prompt, validates provider text against an explicit output contract, and
fails closed on empty or explicit diagnosis/dosing/emergency guidance. AI stays
optional and BYO-key/local-first; deterministic observations do not require a
network provider.

## Consequences

- UI and future reports can show exactly which local aggregates support an
observation or AI artifact.
- Context overlap is intentionally descriptive; it does not claim causation.
- Existing serialized insights remain readable: missing evidence fields restore
as an empty list, while newly generated insights always include evidence.
- The output validator is a narrow fail-closed control, not a medical safety
classifier. Prompt/model adversarial evaluation and accountable review remain
required before external distribution.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ADR 0008: Source-aware, bounded health-context imports

- Date: 2026-08-15
- Status: Accepted

## Context

OpenGlucose needs activity, sleep, and heart-rate context from Apple Health and
Android Health Connect. Platform APIs return source identifiers and intervals,
but bounded reads may overlap or repeat records. Appending every returned point
would duplicate context and make downstream analytics unreliable. Health data
also must remain local and source permissions must be explicit.

## Decision

Use one app-owned importer seam around the existing `health` package. The seam
accepts a user-selected set of categories and a bounded foreground time window,
requests read-only permissions for exactly those categories, maps records into
the shared `cgm_core` activity/sleep/heart-rate models, and writes them to the
local `HealthRepository`.

Every imported sample carries optional `HealthSampleMetadata`: platform record
identifier, source bundle/package and name, source device, device model,
recording method, and a deletion flag for future anchored sync. The repository
uses a namespaced source-plus-record identifier as a unique local key. Legacy
rows remain readable and nullable identities are added by a SQLite migration.

Tests inject a deterministic fake reader and never require a HealthKit account,
Health Connect installation, network, or real health data.

## Alternatives considered

- Store only normalized values: rejected because repeated reads cannot be
de-duplicated safely and source attribution is lost.
- Add separate HealthKit and Health Connect domain models: rejected because it
would duplicate mapping and diverge analytics semantics.
- Add a new native SDK: rejected because the reviewed `health` dependency is
already present and covers the bounded foreground MVP.

## Consequences

- Imports are local-first, read-only, and user-selectable.
- Native permission declarations are required on iOS and Android; background
delivery and anchored deletion polling remain follow-up work.
- Source identifiers are restricted local metadata and must not be copied into
user-facing exports or logs.
- A future importer can persist deletion tombstones and anchors without
changing the normalized sample shape.

## Follow-up controls

- Add per-category Settings UI and explicit sync status messaging.
- Add native/device evidence for HealthKit and Health Connect permission flows.
- Implement anchored queries and deletion reconciliation before claiming
continuous background synchronization.
41 changes: 41 additions & 0 deletions docs/architecture/adr/0009-local-data-deletion-coordinator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ADR 0009: Coordinate complete local-data deletion through a dry-run contract

- Status: Accepted
- Date: 2026-08-15
- Owners: `@shroominic`

## Context

OpenGlucose stores restricted data across SQLite context records, alert
history, optional AI artifacts and secure keys, sensor state/history blobs,
archive manifests, native background surfaces, and temporary/derived files.
Clearing one screen or one sensor is not complete erasure. A destructive path
also needs a preview, explicit consent, interruption handling, and proof that
the stores are empty.

## Decision

`cgm_core` exposes `LocalDataDeletionCoordinator` and a
`LocalHealthDataDeletionBackend` adapter contract. `createPlan()` inventories
all known domains without mutation. The plan includes a stable non-sensitive
fingerprint and an exact confirmation phrase. `dryRun()` reports the proposed
scope, and `execute()` refuses missing confirmation or a changed inventory,
then deletes and verifies domains one at a time. Any failure stops subsequent
work and returns a partial, non-complete result.

The default domain set includes journal/context records, AI insights/API key,
alert history, active sensor state, sensor archive, background surfaces,
derived caches, and temporary exports. Existing UI is not wired to execute this
contract implicitly. Platform adapters remain responsible for stopping writers,
clearing secure/native stores, and implementing a complete post-relaunch
verification before the product can claim erasure.

## Consequences

- Users and reviewers get a deterministic, inspectable deletion report before
any destructive operation.
- A stale plan cannot erase a changed store under an old preview.
- Partial failures are honest and recoverable, but the adapter must provide a
retry/repair UI before calling the workflow complete.
- The in-memory backend and tests provide deterministic contract evidence
without using health data or platform storage.
3 changes: 3 additions & 0 deletions docs/architecture/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ enforcement detail is already implemented.
| [0002](0002-local-first-health-data.md) | Accepted | Keep health data local by default and make movement explicit |
| [0003](0003-platform-release-model.md) | Accepted | Build source-bound mobile artifacts and release only through fail-closed platform lanes |
| [0004](0004-app-localization.md) | Accepted | Use a user-controlled English/Simplified Chinese language policy across app and live UI |
| [0007](0007-evidence-backed-observations.md) | Accepted | Keep metabolic observations deterministic, typed, evidence-backed, and AI output bounded |
| [0008](0008-source-aware-health-context-import.md) | Accepted | Import bounded, read-only health context through one source-aware local contract |
| [0009](0009-local-data-deletion-coordinator.md) | Accepted | Coordinate complete local-data deletion through a dry-run, confirmation, and verification contract |

## Adding an ADR

Expand Down
59 changes: 59 additions & 0 deletions docs/architecture/glanceable-surfaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Glanceable surface contract

`openhealth/lib/src/glanceable_surface.dart` defines the adapter-neutral
contract for compact surfaces such as Android notifications, iOS Live
Activities, lock-screen widgets, and future watch complications. This slice is
deliberately Dart-only: it does not change an existing native payload or claim
that a platform adapter is implemented.

## Model and lifecycle

`GlanceableSurfaceSnapshot.fromSession` derives one deterministic snapshot from
the same `CgmSessionSnapshot` and warmup primitives used by the foreground UI.
It carries:

- a coarse phase (`noSession`, `connecting`, `warming`, `waiting`, `live`,
`stale`, or `error`);
- a freshness bucket with five- and ten-minute boundaries;
- a warmup countdown when the sensor is initializing;
- a timestamped glucose value only after warmup; and
- aggregate activity/sleep context and a low/high/stale alert state.

Advertisement-only or provisional warmup values are never treated as current
glucose. A platform surface can therefore show “Warming up” and the remaining
minutes without displaying a misleading initial value.

## Privacy boundary

`serializeGlanceableSurface` is redacted by default. Redacted payloads expose
phase, freshness, countdown, a generic surface string, whether context exists,
and whether any alert needs attention. They do not expose glucose, exact
timestamps, sensor labels, context measurements, or alert type.

The caller must explicitly pass `includeSensitive: true` to include those
fields. That opt-in is a policy gate, not a promise that a native surface may
publish them: callers still need to honor the user's per-surface setting and
the platform's privacy rules. Native adapters must not log or persist raw
payloads, and must clear their surface when consent is withdrawn.

## Native adapter contract

Platform code implements only:

```dart
abstract interface class GlanceableSurfaceAdapter {
Future<void> publish(GlanceableSurfacePayload payload);
Future<void> clear();
}
```

Adapters consume `GlanceableSurfacePayload` and never a domain snapshot or raw
health sample. They should treat `schemaVersion` as a compatibility boundary,
preserve the phase/freshness/warmup semantics, and fail closed when a payload
is malformed or a sensitive-content gate is unavailable. `NoopGlanceableSurfaceAdapter`
is the safe implementation for unsupported platforms and tests.

Future native work can add one adapter at a time (for example, Android live
notifications or an iOS Live Activity) with platform contract tests that prove
the exact mapping. Existing native payloads remain unchanged until such a
platform-specific change is reviewed separately.
36 changes: 27 additions & 9 deletions docs/privacy/data-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,35 @@ after relaunch. See `docs/runbooks/data-recovery.md`.
The current app does not provide a verified delete-all flow. Disconnecting or
clearing one selected sensor is not equivalent to complete erasure.

Foreground glucose alert episodes are stored locally in the restricted health
state store, capped at the most recent 200 transitions. Native builds keep the
store in the backup-excluded application-support directory; the web demo uses
origin-scoped preferences and is not a supported private health-data surface.
The evaluator records only alert type, timestamps, and the associated glucose
value; it does not send notifications, create lock-screen payloads, or upload
data. A future complete delete-all flow must clear this history alongside the
journal and database.

## Sharing and export

The iOS app implements an opt-in, write-only Apple Health integration. It sends
glucose values and their timestamps as blood-glucose samples only after the
user enables the integration and taps **Sync now**; simulated/demo data is
blocked. It does not read HealthKit data. Apple Health's privacy, retention,
backup, and deletion behavior applies once a sample is written. The local
contiguous export watermark and last-sync time use the restricted state store,
not backup-eligible preferences. Because a HealthKit write and the local cursor
cannot commit atomically, an interrupted sync can write a duplicate sample on
retry. The UI discloses this at-least-once behavior.
The iOS app implements an opt-in Apple Health integration. The glucose export
path sends glucose values and their timestamps as blood-glucose samples only
after the user enables the integration and taps **Sync now**; simulated/demo
data is blocked. The local contiguous export watermark and last-sync time use
the restricted state store, not backup-eligible preferences. Because a
HealthKit write and the local cursor cannot commit atomically, an interrupted
sync can write a duplicate sample on retry. The UI discloses this at-least-once
behavior.

The separate context-import path reads only the categories the user selects
(steps, workouts, sleep, heart rate, active energy, or walking/running
distance) for an explicit bounded foreground window. Samples remain on-device
and are de-duplicated by a namespaced platform record identifier. Source bundle
or package, source name, device, and recording method are restricted metadata;
they are not logged or included in user-facing glucose exports. Permission
denial and empty reads are reported without guessing which state the platform
returned. Background delivery, anchored sync, and source deletion polling are
not implemented yet and must not be claimed as available.

Archived sensor details provide an explicit single-file export through the
platform share sheet. A confirmation preview displays the sensor-session date
Expand Down
10BC8
13 changes: 7 additions & 6 deletions docs/product-foundations-2026.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,11 @@ disconnect or replace.

## Apple Health import and overlays

The current integration exports glucose only. Import must not ship until the
schema retains HealthKit UUID, source bundle/name, device, recording method,
interval, and deletion state with unique constraints; the current methods named
`upsert` are append-only and would duplicate repeated imports.
The integration exports glucose and now has a bounded context-import contract.
Imported data is not considered production-complete until the schema retains
HealthKit/Health Connect UUID, source bundle/name, device, recording method,
interval, and deletion state with unique constraints; repeated imports must
replace a source record rather than append a duplicate.

Initial read categories:

Expand All @@ -102,8 +103,8 @@ glucose line. Imported raw samples remain separate; display normalization uses
per-type source priority so overlapping Apple Watch, Whoop, Oura, and iPhone
records are not blindly summed.

MVP can bounded-requery with the existing Flutter `health` package and upsert by
HealthKit UUID. Production incremental sync uses native
The MVP bounded-requery contract uses the existing Flutter `health` package and
upserts by a namespaced source record ID. Production incremental sync uses native
`HKAnchoredObjectQuery`, one persisted anchor per type, and handles deleted
objects. Permissions are progressive and requested only when the user enables
an overlay. iOS can make denied and empty reads indistinguishable, so the UI
Expand Down
32 changes: 25 additions & 7 deletions docs/runbooks/data-recovery.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Health-data recovery and erasure

Owner: data owner, with privacy review. OpenGlucose currently has no verified
full backup/restore or delete-all feature; do not promise either to users.
Owner: data owner, with privacy review. OpenGlucose has a core deletion
coordinator contract, but the production store adapter and user confirmation
flow remain release work; do not promise verified delete-all until those checks
are complete.

## Recovery

Expand All @@ -26,8 +28,24 @@ a release blocker.

## Erasure

Stop sensor/background writers first. Remove all sensor histories, selected
sensor, native background target/payload, notifications/live activities,
journal/database, AI artifacts/key, exports, preferences, and caches. Relaunch
offline and verify that no record is restored. Document any OS/provider copy
outside app control and the user action required there.
Use `LocalDataDeletionCoordinator` as the single orchestration boundary. First
call `createPlan()` and show the returned non-sensitive counts and domains in a
confirmation preview. A dry run must not mutate anything. Execution requires
the exact plan confirmation phrase; a changed inventory invalidates the plan.

The complete default scope is:

- journal events and imported activity, sleep, and heart-rate samples;
- AI insights and the secure BYO API key;
- alert history;
- active sensor state and every restricted reading history blob;
- sensor archive manifests and archived reading blobs;
- native background/live surfaces, derived caches, and temporary exports.

The app adapter must stop sensor/background writers before invoking the plan,
delete each domain through its durable store, verify it empty, and stop on the
first delete or verification failure. A partial result must remain visibly
incomplete and must never be reported as erasure. Relaunch offline and verify
that no record is restored. Document any OS/provider copy outside app control
and the user action required there (for example, Apple Health data written by
an earlier explicit export).
Loading
Loading
0