Skip to content

Sync upstream Trio dev (e797b4adf) into Tai dev - #38

Merged
mountrcg merged 114 commits into
devfrom
sync/trio-2026-10-19
Oct 20, 2026
Merged

mountrcg merged 114 commits into
devfrom
sync/trio-2026-10-19

Conversation

@mountrcg

@mountrcg mountrcg commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Sync upstream Trio dev (e797b4a) into Tai dev

Tai dev (7daf1348f, 1.0.2) is merged with upstream nightscout/Trio dev at e797b4adf — 95 upstream commits since the last sync at efb5bea30. Branch sync/trio-2026-10-19: merge commit 17841352a, plus 93c3c79e9 re-adding the Tai-only localization keys on top of the upstream catalog and four small follow-ups, 66a3c49b5 (build fix in GlucoseSetup.swift), 9854cd2b1 (SwiftLint cleanups), 6f8bc95c7 (Home Statistics Panel hint wording) and 0cb7835aa (test-only adaptation of the merged CarbEntryMutationServiceTests to Tai's FPU split). Net against dev: 78 files, +3282 −707.

What comes in from upstream

  • Home Statistics Panel. The panel's face is configurable, and a new Home Statistics Range setting controls the span it averages over. The range is shared with the Statistics screen: opening Statistics from the panel lands on the same span. Both settings are part of the settings export.
  • GRI in Statistics. A glucose risk index calculator and its view (GRICalculator, GRIView) show the GRI alongside the existing statistics, backed by GRICalculatorTests.
  • Dexcom G7 direct connection. G7SensorKit on feat/trio-g7-direct lets the app talk to G7, ONE+ and Stelo directly, including owning glucose alerts when the sensor is paired to the app rather than the receiver. Stelo is named in the picker.
  • Remote meal mutation. New delete_meal and edit_meal remote commands (TrioRemoteControl+MealMutation), with unlinked meals and service errors rejected and meal roots resolved by handle; the payload publishes the fpuID and the supported command list (RemoteMealMutationPayloadTests).
  • CarbEntryMutationService. The meal deletion and replacement logic is extracted into one service shared by the remote commands and the app (CarbEntryMutationServiceTests), and a meal and its carb equivalents now share one fpuID.
  • LiveActivity. A widget configuration screen for the Live Activity settings (LiveActivitySimpleWidgetConfiguration), and LiveActitiyAttributes.swift renamed to LiveActivityAttributes.swift with its references in project.pbxproj updated.
  • Dependencies and submodules. Gemfile.lock bumps, and submodule pin moves for G7SensorKit 5c0fc075a, LibreLoop 82c92f2cf and OmnipodKit 928bb7950.

Tai specifics that stay

autoISF settings, the Garmin watchface and datafield selection, the profile scheduler, Bobble/CGM status, glucose peaks, the today-average display, Trio Chart, and Tai's versioning in Config.xcconfig (APP_DEV_VERSION = 1.0.2.12) are unchanged by the merge. Tai's 726 fork-only localization strings were re-added on top of the upstream catalog, so the file holds the exact union: 3475 keys, no Tai key lost, no key added beyond the two sides.

Tai also keeps its duration-based FPU carb-equivalent split with the 10-minute minimum instead of upstream's capped 3-entry split (upstream PRs #951/#1019/#1022), as recorded under "Deliberate Tai differences" in UPSTREAM_SYNC_BASELINE.md; the merged upstream CarbEntryMutationServiceTests was adapted to that behaviour in 0cb7835aa, and Tai's own CarbsStorageTests.swift does not carry the capped-split expectations either.

Conflicts

The 13 conflicted files were resolved by integrating both sides — upstream's Home Statistics Panel and range setting together with Tai's fork UI — and Localizable.xcstrings was taken from upstream and re-augmented with the Tai-only keys.

Known residual, cosmetic: the Home Statistics Panel hint text in Tai's UI still says "today's" although a range setting now exists. The banner itself follows the range; the wording is a follow-up.

Testing

  • xcodebuild -list -project Trio.xcodeproj parses and lists the six targets (Trio, TrioTests, LiveActivityExtension, Trio Watch App, Trio Watch AppTests, Trio Watch Complication Extension).
  • No conflict markers in the tree; git merge-base --is-ancestor e797b4adf HEAD passes; the merge carries 95 upstream commits.
  • All ten files this merge adds are wired into Tai's project.pbxproj (build file, sources phase, file reference and group), and the three new tests sit in the TrioTests target.
  • Localizable.xcstrings is valid JSON and the key union is exact (3475 keys, 0 missing, 0 extra).
  • A full xcodebuild -workspace Trio.xcworkspace -scheme Trio -destination 'generic/platform=iOS Simulator' build on the branch exits 0; the one build artifact of the merge (a GlucoseSetup scope error) is fixed in 66a3c49b5.
  • CI on 9854cd2b1: Algorithm Package (swift test) passed (56s) and Run Unit Tests failed (14m35s) with exactly two assertion failures, both in upstream's new CarbEntryMutationServiceTests.swift — the count == 3 expectations in "Delete removes the root and its carb equivalents" and "Delete on a carb equivalent row is rejected", which encode upstream's capped 3-entry fat/protein split. That split is a documented Tai exclusion (UPSTREAM_SYNC_BASELINE.md), so the tests, not the code, were wrong for this fork.
  • 0cb7835aa changes that one test file only (+15 −2): the family is asserted by provenance (one root with the meal's id, its carb equivalents by fpuID, non-zero carbs) instead of a hardcoded row count, and the rejected child delete asserts an unchanged before/after row set. No production code and no FPU algorithm change.
  • Narrow run after the fix: xcodebuild test -workspace Trio.xcworkspace -scheme 'Trio Tests' -destination 'platform=iOS Simulator,id=66ACB141-5FFF-47ED-9701-2957699F7522' -only-testing:TrioTests/CarbEntryMutationServiceTests — 10 tests in 1 suite passed, TEST SUCCEEDED, exit 0.
  • CI on 0cb7835aa: Algorithm Package (swift test) passes (54s) and Run Unit Tests passes (17m23s) — green on the fix head.

CI on the PR (Algorithm Package / Run Unit Tests) is the gate. The on-device check is done by the operator once CI is green.

Merge target: Tai/dev — do not merge before CI is green.

cachrisman and others added 30 commits September 16, 2026 12:54
When "Use CGM App Alerts" is on and the active CGM provides its own
glucose alerts, evaluateGlucoseAlarms() bails on every new reading and
called retractAllFiringIfNeeded(), which retracted every firing alarm —
including forecastedLow and carbsRequired. Those are determination-driven
and deliberately not covered by the CGM-ownership guard (a CGM app cannot
compute Trio's forecast or carbsReq), so the next determination a few
seconds later fired them again. The result was "Low Glucose Soon" and
"Carbs Required" re-delivered with sound on every loop cycle for as long
as the condition held; retractAlert also resets the 5-minute throttle,
so it never engaged.

Retract only the reading-driven alarms on that path. Forecast and
carbs-required alarms keep their firing state across reading updates and
retract through their own evaluators on recovery or snooze, as before.

Low Glucose Soon only started firing on these CGMs with #1428, which is
when the repeat became visible; Carbs Required had the same cycle before.
Points G7SensorKit at loopandlearn feat/trio-g7-direct, which carries the
direct-connection work from LoopKit/G7SensorKit#67 plus the changes needed
to build against the LoopKit Trio ships.

The direct G7 stack originates in DexKit by Erik Tolboom (MIT), built and
proven with Trio tester feedback over several weeks; Pete Schwamb ported it
into G7SensorKit in #67. Per-file attribution headers are retained in that
repository.

No picker change needed: the existing Dexcom G7 / ONE+ entry already routes
through G7UICoordinator, so onboarding picks up direct pairing with no
re-pairing.

Info.plist gains dexcomoneplus and stelo for the app-detection warning
(LoopKit/Loop#2512; dexcomg7 was already present) and a camera usage
description, without which TCC kills the app when the onboarding package
scanner asks for access.

Co-authored-by: ETolboom <e.s.tolboom@student.tue.nl>
Co-authored-by: Pete Schwamb <pete@schwamb.net>
G7SensorKit already drives Stelo (DX01 advertisements, its own sensor model
and title); only the picker still read G7 / ONE+.

Co-authored-by: ETolboom <e.s.tolboom@student.tue.nl>
The Lock Screen Widget Style picker only offered a Widget Configuration
screen for the Detailed style. The Simple style had no configuration at
all, and its glucose reading was hard-coded to `.title` and colored only
when the Dynamic glucose color scheme happened to be selected.

Add a Widget Configuration screen for the Simple style with:

- Font Face: Default, Rounded, Serif or Monospaced typeface for the
  glucose reading and its trend arrow.
- Font Size: Small, Medium, Large or Extra Large, backed by Dynamic Type
  text styles so the reading keeps scaling with the user's text size.
- Color Glucose Reading: when on, the reading, trend arrow and delta use
  the app's existing glucose coloring rules (Glucose Color Scheme plus
  the Low/High thresholds); when off, they use the default text color.

The screen shows a live preview of the Simple layout, with a Low / In
Range / High sample selector so the coloring can be judged per band. The
preview resolves its color the same way `LiveActivityView` does.

The settings live in `TrioSettings` and travel to the widget through a
new `LiveActivityAttributes.SimpleViewStyle` on the content state, so
changes push to a running Live Activity right away. The enums and the
style struct are shared by the app and the LiveActivityExtension target.

Along the way, `bgAndTrend` and `LiveActivityGlucoseDeltaLabelView` no
longer re-derive the static-color-scheme decision internally; callers now
pass the resolved color. The Dynamic Island call sites pass exactly what
those views resolved before, so their appearance is unchanged.

Behavior change: with Color Glucose Reading defaulting to on, users on
the Static glucose color scheme now see the Simple lock screen reading in
its band color, matching what the Detailed style already does. It can be
turned off on the new screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
Two follow-ups to the Simple style's Widget Configuration screen:

Color Glucose Reading now defaults to off, so the Simple lock screen
reading stays in the default text color until the user opts in.

The Detailed style gains a "Glucose, Trend, Delta (Double Width)" item
that puts the reading, its trend arrow and the delta on a single line and
takes two of the four configuration slots. Its button in the slot row
spans both slots, and the row keeps its overall width.

Slot occupancy is tracked with a `wideContinuation` placeholder written
into the slot the wide item swallows. The "Add Widget" sheet only offers
an item that still fits at the tapped slot, removing a wide item frees
both of its slots, and a saved order is repaired on load so a wide item
always owns its second slot and no placeholder is left stranded. The
widget drops the placeholders before laying out the row, so they earn no
divider or gap.

Also fixed while in this code: `loadOrder` could trap on a saved order
longer than four slots (negative `Array(repeating:count:)`), and the slot
`ForEach` keyed on `\.element`, which is not unique once more than one
slot is empty — it now keys on the slot's position.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
The Simple style's Widget Configuration briefly carried its own "Color
Glucose Reading" toggle. A second, Live-Activity-only color switch sat
awkwardly next to the app-wide Glucose Color Scheme: it read like a
global setting but governed only the iPhone lock screen Simple layout,
leaving the Watch/CarPlay widget and the Detailed layout on the app-wide
setting. Remove it and leave the Glucose Color Scheme in sole charge.

Reading color in every Live Activity layout is now exactly what it was
before this branch. `bgAndTrend`, `LiveActivityGlucoseDeltaLabelView` and
the Dynamic Island call sites are restored verbatim, so the call-site
color refactor those changes required is gone too — it was only there to
serve the toggle.

The Widget Configuration screen keeps Font Face and Font Size, and its
preview resolves color the way the widget does, so it shows an uncolored
reading while the Static scheme is selected. A line in the preview footer
says where that setting lives.

Also restores the "Simple:" hint sentence verbatim and puts the new
information in a sentence of its own. Editing the original would have
changed its String Catalog key and dropped every existing translation of
it back to English.

Net effect on users: nobody's Live Activity changes color on update.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
The screen offered four "+" buttons and an action sheet, and told users
that re-ordering meant removing every widget and adding it back in the
order they wanted. Replace that with direct manipulation:

- Drag a widget in the layout row to move it. Dropping it on another
  widget puts it in that position and shifts the rest along.
- An "Available Widgets" palette below the layout lists every widget.
  Drag one up into the row to add it at a chosen position, or tap it to
  append. Widgets already placed, or too wide for the room that is left,
  are dimmed.
- Drag a widget back down onto the palette to take it out of the layout.
  The trash button on each widget still removes it.

The layout is now held as an ordered list of widgets whose slot widths
spend a four-slot budget, rather than as a fixed array with holes. That
makes a move an ordinary list reorder and keeps the double-width widget
honest without per-position arithmetic. It is expanded back into the
four-slot array on save, and a saved array is read back leniently: gaps,
stranded continuation placeholders, repeats and widgets that would
overrun the four slots are all dropped.

Because the list has no interior gaps, a layout saved with an empty slot
in the middle compacts the first time it is edited. The widget renders
the same, minus that gap.

The drag payload is the widget enum itself, carried as JSON via
Transferable. An app-specific exported UTType would have to be declared
in Info.plist, and anything dropped from outside simply fails to decode.

Also drops an unused `color` gradient the file had been carrying.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
…t help, larger sizes

Addresses review feedback on the Simple Lock Screen widget configuration and the
double-width detailed item:

- Simple config preview now shows the real current CGM reading (value, delta,
  trend) fetched from Core Data and refreshed live, instead of a canned
  Low/In Range/High sample. Removed the sample picker and its footer text.
- Font Face "Rounded" now renders distinctly: glucoseFont uses SwiftUI
  .system(size:weight:design:); the previous UIFontDescriptor route silently
  dropped the rounded design.
- Filled in the "Glucose Reading Font" help sheet, which rendered blank because
  it read shared mutable @State that presented empty. Its content is now static.
- Enlarged every font-size step (Small 30pt through Extra Large 64pt, Dynamic
  Type scaled); Extra Large drops vertical padding to fill the widget; the
  double-width Glucose/Trend/Delta item is drawn much larger in both the
  configuration preview and the Live Activity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r palette

- Rename "Glucose and Trend, no Delta" to "Glucose and Trend (Colored)" and add
  an uncolored "Glucose and Trend".
- Rename "Glucose, Trend, Delta (Double Width)" to "... (Double Width, Colored)"
  and add an uncolored "Glucose, Trend, Delta (Double Width)".
- Add a colored single "Glucose, Trend, Delta (Colored)" so each glucose widget
  shape has a colored and an uncolored variant. Colored draws the reading (and,
  for the single/large items, the trend) in the glucose color; uncolored uses
  the default text color. New enum cases added to both the settings and the
  Live Activity attributes enums with matching raw values; saved layouts keep
  working since existing raw values are unchanged.
- Available Widgets palette: lay out on a three-column grid so each double-width
  widget spans two columns and shares its line with the matching single-width
  widget, and show the full widget title instead of truncating at two lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
storeCarbs mints the fpuID when the caller omits it, so the parent row and the FPU children always link. saveCarbsToCoreData keeps a UUID entry id and replaces only non-UUID ids.
Nightscout, Apple Health and Tidepool deletes are awaited before the Core Data rows are removed, and Nightscout failures are reported to the caller. The History screen delegates delete and edit to the service.
fetchMealRoot accepts a root id or the family fpuID. Nightscout carb and FPU treatments include fpuID, and the profile store lists the remote command types this build accepts.
meal_id is the Nightscout id of the meal root or of one of its carb equivalents. Edit replaces the meal through CarbEntryMutationService, so the id changes and the ack carries the new one. Acks include command_id, meal_id and result; unknown command types are rejected with an ack.
storeCarbs stores the fpuID a caller supplies and derives carb equivalents only when both fat and protein are set. A UUID-parsable entry id is kept as the Core Data row id.
A root with fat or protein but no fpuID is rejected with a clear message, and a failing deleteMeal/replaceMeal call yields a rejected ack. CarbEntryMutationService returns plain failure message lists, syncMealsWithServices is private, and CommandType derives its supported list from CaseIterable.
v1.0.1's submodule update moved G7SensorKit to c2abb4f, a lokalise
translations commit on main. Our pin tracks feat/trio-g7-direct, so the
gitlink conflicted; resolved by merging c2abb4f into that branch (6f015b9)
rather than dropping the translations.
Picks up LoopKit/G7SensorKit#70 (manual pairing robust when other sensors
are nearby) and #71 (Dexcom Share upload with follower management), merged
into feat/trio-g7-direct. No Trio-side changes needed.
Follows dnzxy's review on nightscout/Trio#1566.

- Remove the Simple layout's font face option end to end: the enum, the
  setting and its decoding, the state model binding, the picker, the
  settings-export row, and the SimpleViewStyle field. Font size stays.
  Help and hint text that mentioned the typeface now describe size only.
- Drop the `#if canImport(UIKit)` guards and their `#else` fallbacks in
  LiveActivityGlucoseStyle. Both targets are iOS-only, so UIKit is always
  available and the fallback never compiled in.
- Draw the double-width item's delta in the same font as its reading, in
  the Live Activity and in the configuration preview, so the two do not
  sit at mismatched sizes. The widget shares one `wideFont` so they
  cannot drift apart.
- Palette cells no longer box the label together with the widget. Each
  cell shows the widget as the same tile it becomes in the layout row,
  with its name beneath it. The row, the palette and the drag preview
  now share one `widgetTile` helper.
- Remove the explanatory comments flagged in review, and two more of the
  same kind in LiveActivityView.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
The file is not part of any target and never compiles: the Xcode project
has no reference to it, and Trio/Sources is not a synchronized folder.
It holds an obsolete copy of `LiveActivityAttributes` whose ContentState
no longer matches the real one in LiveActitiyAttributes.swift, and it
occupies the path that file should have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
Fixes the long-standing typo in the filename. Contents are unchanged.
The project keeps the file's existing references and IDs, so it stays in
both the Trio and LiveActivityExtension targets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
Drops the doc and inline comments this branch added that restate what the
code already says. Comment-only change: no code lines are touched.

Kept: the section MARKs, which Trio uses as navigation elsewhere; a
one-line note on the `wideContinuation` sentinel case; and a one-line
note on why the drag payload is JSON rather than a custom exported
UTType, which would need an Info.plist declaration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
…ents

Follows the rest of dnzxy's review on nightscout/Trio#1566.

- The Simple Widget Configuration preview no longer reads live CGM data.
  It shows a fixed 123 / → / +6 reading, the same mock the Detailed
  configuration's previews use. That removes the Core Data fetch, the
  glucose storage subscription, the injected GlucoseStorage and the
  three published reading properties from the settings state model,
  which now only adds the font size setting over upstream.
- Remove the two one-line comments kept in the previous cleanup: the
  `wideContinuation` case note and the note on the JSON drag payload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UVfX1mSyg6kjsVtCoW8mau
The delta in the double-width Glucose, Trend, Delta widget used the same
bold largeTitle as the glucose value, so "+6" was as prominent as the
reading itself. Give it the arrow's size (title2, title3 on watchOS), and
make the Widget Configuration preview mirror the widget: glucose at
largeTitle with the arrow and delta at title2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwMti9CLZgjS37Tzpz41Ut
dnzxy and others added 29 commits October 17, 2026 17:46
Add Glycemia Risk Index feature with calculations and UI components
fix race condition: Persist the restored pump manager's state on processQueue
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcptGJfxyCKscK3RaZuqby
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcptGJfxyCKscK3RaZuqby
Add a Statistics Range setting for the home stats panel
Add Dexcom G7, ONE+ and Stelo Direct Sensor Connection
The sync took the upstream catalog; re-insert the 726 keys that only
exist in Tai, keeping upstream values and key order for shared keys.
Tai keeps its duration-based FPU split and excludes upstream's capped
3-entry split (PRs #951/#1019/#1022), so the hardcoded count == 3
expectations in testDeleteMealRemovesFamily and testDeleteChildThrows
failed. Assert one root plus carb-equivalent children belonging to the
meal, and an unchanged row set after the rejected child delete.
Rework Trio Alarm Sounds: Add Urgency Levels and Voice Alarms
fixes crash when uploading carb entry without id to Tidepool issue #1425
@mountrcg
mountrcg merged commit 781f22e into dev Oct 20, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.