Skip to content

Release v8.0.0 - #811

Merged
marionbarker merged 86 commits into
mainfrom
release/v8.0.0
Nov 2, 2026
Merged

marionbarker merged 86 commits into
mainfrom
release/v8.0.0

Conversation

@marionbarker

Copy link
Copy Markdown
Collaborator

Release v8.0.0.

Merging this PR triggers the tagging workflow, which creates tag v8.0.0 from LOOP_FOLLOW_MARKETING_VERSION in Config.xcconfig.

⚠️ Use "Create a merge commit" (not squash or rebase) so main keeps the original feature-PR commits.

marionbarker and others added 30 commits September 20, 2026 11:15
RemoteCommandTracker keeps one pending/done state per key (Trio meal id or
Loop sync identifier) with a 60 s timeout, and resolves it from the AID
app's return push through two thin adapters: TRC by command_id, Loop by
sync_identifier. Loop carb delete/edit require LoopFollow's return
credentials, like Trio, since the push is the only confirmation.

The detail view reads busy / failure message from the tracker and dismisses
on a success transition it observes itself, so a terminal state left from
an earlier visit never dismisses a freshly opened view.
An FPU child row offers Edit only when its root meal is loaded; the edit
sheet is then prefilled with the root's macros and time and addresses the
root's id. Delete stays available for every child and targets the whole
meal. The view model builds the fpuID → root lookup once per regroup.
A Nightscout carb document from Trio is an FPU child exactly when its
fpuID equals its id; a document without fpuID is a root meal.
Carbs, bolus, carb delete and carb edit share commandHeader for the otp,
remote-address, notes, entered-by, sent-at, expiration and alert fields.
The builders are static and take the send time, so tests pin the exact
key set and values of each payload.

A carb edit always carries carbs-edit-entry, carbs-edit-absorption-time
and carbs-edit-start-time; carbs-edit-food-type is sent only when set.
LabeledValueRow renders the label/value rows in the treatment detail
sections and both edit sheets. A Trio meal without carbs is listed as
"Meal" with its fat and protein grams as subtitle. When Trio Remote
Control is active but the Trio build's profile does not advertise the
meal commands, the Meal section footer says a newer Trio version is
needed.
A patched Loop lists carbs-delete and carbs-edit under
loop.testingDetails.remoteCommands in every devicestatus upload.
DeviceStatusLoop stores that list in loopRemoteCommands, clearing it
when the key is absent, and the treatment detail offers the Loop carb
remote actions only when both commands are advertised. With Loop APNS
active but the commands missing, the carb entry section explains that
the remote carb edit customization is needed.
* Add ability to double-click carbs to enter Treatments screen

* Keep the carb detail sheet across remounts and share carb treatment construction (#758)

* Share carb Treatment construction and root-meal lookup

Treatment.carb builds a carb entry for both the treatments list and the
chart's detail sheet, with one id fallback for entries without a
Nightscout _id. Treatment.rootMealsByFPUID and rootMeal(in:) back the
FPU-child root lookup in both places.

* Refresh treatments when a remote meal command completes

The chart's carb marks follow a remote meal edit or delete from any
screen by running the treatments task as soon as the command resolves.

* Keep the carb detail sheet in the chart shell

BGChartView owns the selected carb and its sheet, outside the subtree
that is remounted on foregrounding, so an open sheet survives a trip to
Notification Center or the background. Double-tap hit-tests carb marks
only, so a nearby bolus cannot shadow the carb it sits next to.

---------

Co-authored-by: Jonas Björkert <jonas@bjorkert.se>
…6.04 (#767)

* Update fastlane to 2.240.1 and move workflows off ubuntu-latest and macos-26

The Gemfile pins fastlane 2.240.1, which brings rubyzip and json onto version ranges without open security advisories.

Jobs that ran on ubuntu-latest now run on ubuntu-26.04, since the ubuntu-latest label is about to move to Ubuntu 26 and GitHub posts a notice about it on every run.

The build job runs on the xcode-27 runner image and selects Xcode 27.0. That image only ships Xcode 27, so the xcode-select path points at Xcode_27.0.app.

* Run all macOS jobs on macos-26

The build job stays on Xcode 26.2. The certificate, identifier and secret validation jobs move from macos-15 to macos-26, so every workflow runs on either macos-26 or ubuntu-26.04.

* Run all macOS jobs on the xcode-27 runner image

The build job selects Xcode 27.0, which is the default on that image. The certificate, identifier and secret validation jobs run on the same image, so every workflow runs on either xcode-27 or ubuntu-26.04.
Treatment parsers force-cast `timestamp`/`created_at` to String after a
`!= nil` check. A treatment carrying a JSON null or numeric timestamp
passes the nil check and traps in swift_dynamicCast on the main queue,
so the app aborts on every launch while that record is within the
download window.

Add NightscoutUtils.treatmentDate(from:) which accepts ISO 8601 strings,
epoch milliseconds/seconds, and falls back to created_at on null; use it
in the bolus, SMB, carb, note and stats parsers. Replace the remaining
forced casts in device status, SAge, CAge and IAge handling with
conditional binding so malformed data is skipped instead of crashing.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Add device status history graphs and scrubbed-time values

Cache the loop values from Nightscout devicestatus on the phone. The Show
Days Back window is downloaded once in pages of 100 records, after that
only missing ranges are fetched, and changing the Nightscout address drops
the cache.

Add optional IOB, COB, sensitivity ratio and smoothed BG graphs, shown as
separate graphs under the main graph or in the bottom of the main graph.
While scrubbing, the BG display and info table show the values at that
time together with the forecast from that loop cycle, and the pill only
lists treatments. A tap shows BG and treatments. Add a Smoothed BG info
row and remove the divider between the BG display and the info table.

* Remove the scrub pill and the main-graph legend

While scrubbing, the BG display and info table show the values at that
time, so the graph only shows the indicator and the forecast. The jump to
now button is hidden while scrubbing.

* Fade the jump to now button back in after scrubbing

The button hides while scrubbing and fades in a moment after the finger lifts, so it is not in the way while reading the values.

* Hide the jump to now button while panning and zooming

The button hides while the chart is panned, zoomed or scrubbed and fades back in a moment after.
* Larger Snoozer text

* Keep the Snoozer emoji and clock above the tab bar on small phones

In portrait the left column's heights scale down together to leave room for the emoji and clock, and its text can shrink to 30% to fit.
The environment is read from the isAPNSProduction value that Loop uploads to the Nightscout profile, so the manual Production toggle is removed from the Loop APNS settings. The current environment is shown under Debug / Info, the same as for Trio.
github-actions Bot and others added 24 commits October 26, 2026 23:45
* Define glucose range cut-offs in one place and pick the range mode from a full-width list

The 54/70/140/180/250 mg/dL cut-offs were repeated across the stats, GRI, the settings footer and the Live Activity. They now live in GlucoseThresholds in Shared, so the app, Live Activity and watch targets all see the same values. Each TimeInRangeDisplayMode carries its title and fixed thresholds, and the range settings, stats title and stats card toggle are built from allCases, so a new mode is its cut-offs plus one case.

The segmented TIR/TITR/Custom picker and its footer are replaced by a row showing the selected mode's full name and cut-offs, which opens a list with one line per mode. Choosing Custom Range stays on the list and shows the Low and High pickers there. The onboarding statistics page gets a navigation stack so the list can open from it.

* Add Extended Tight Range (TING, 63–140 mg/dL) as a range mode

TING is the tight range with its lower cut-off moved down from 70 to 63 mg/dL, also known as Time in Normoglycemia. It is shown as Extended Tight Range, sits between Time in Tight Range and Custom Range in the range list and the stats card toggle, and its cut-offs live in GlucoseThresholds next to TIR and TITR. The settings search finds Range Mode for "ting" and "extended".
…ied from PR #794 (#797)

* Modifications from PR #794

* Treat explicit-null failureReason and pump clock as missing, drop force cast in test helper

---------

Co-authored-by: Jonas Björkert <jonas@bjorkert.se>
* Switch Loop profiles from the Loop remote screen

Adds a Profiles button that lists the profiles Loop uploads to Nightscout, previews one the way Loop does and sends an OTP-protected profile switch with a fingerprint of the previewed values. Loop's return push is matched by profile name. The button is greyed out with an explanation when Loop does not list profile-switch in remoteCommands.

* Point the unavailable Profiles button at the profiles customization

* Say the Profiles button needs the profiles customization

* Require device authentication before sending a profile switch
* Ignore the previous pod's reservoir volume after a pod change

Loop keeps uploading its last reservoir reading for 15 minutes after a pod change, under the new pod's ID. LoopFollow now ignores volumes for a new Omnipod until it has uploaded a record without one or 15 minutes have passed, and dates readings by the record's pump clock.

* Treat a volume in an Omnipod record with no pod paired as the previous pod's

* Show no reservoir volume for an Omnipod with no pod paired

* Show no reservoir volume while Loop shows a pump status in its place

* Show the status Loop uploads in place of the reservoir
… meal and bolus buttons (#792)

* Open the watch bolus on the amount, start at the recommendation, and fit the meal and bolus buttons

The bolus screen opens on the amount instead of a list that, without
Quick-Pick boluses or a recent recommendation, held only Other Amount.
Loop / Trio's recommendation and the Quick-Pick boluses move behind a
Presets button under Continue, shown only when there are any, with the
Old Calculation Warning as before.

While the recommendation is under 5 minutes old the amount starts there,
with an icon line showing the amount and its age. It is filled in once,
so coming back from the confirmation keeps what was set. The Max line is
gone, since the crown and + stop at the maximum, and − and + click like
a crown detent.

The meal and bolus buttons use a compact capsule whose height scales
with the watch, so the amount and two buttons fit without scrolling;
the system buttons pushed Log off the bottom of the meal screen. On the
meal screen, Time and Log are both centered, so they line up.

* Warn when the watch's starting recommendation goes stale, and drop it from Presets while fresh

* Follow and check the latest bolus recommendation on the watch, and mark recommended amounts on the confirmation

* Offer a newer watch bolus recommendation with a tap instead of applying it

* Revert "Offer a newer watch bolus recommendation with a tap instead of applying it"

This reverts commit dda326a.

* Follow a lower watch bolus recommendation, offer a higher one with a tap, and catch up after the confirmation

* Offer a recommendation that arrives after the watch bolus opened, follow only untouched amounts while on top, clear an expired starting recommendation, and let compact buttons wrap

A loop cycle without a recommendation no longer counts as 0 U. The Presets button appears once the recommendation is old enough to be listed there, and meal Quick-Picks are centered like the other buttons.

* Don't offer a bolus recommendation calculated before the last bolus

After a bolus, the recommendation stays the same until Loop or Trio runs a
new cycle and LoopFollow fetches it, which can take a few minutes. During
that time the watch filled the old amount in again, so Continue and the
crown would have given the bolus twice. The phone's bolus screens offered
it too, with a tap.

The phone now leaves out a recommendation when a manual bolus came after
its loop cycle: from Nightscout, or sent from LoopFollow or the watch,
alone or with a meal. The watch also ignores one from before insulin it
sent itself, including when it sent directly.

For Loop, the recommendation's time is now the cycle's own timestamp. The
pump clock it used before also moves on failed cycles, which keep the old
recommendation, so after a bolus that recommendation looked newer than the
bolus.

* Order the watch's last-insulin key modifiers the way SwiftFormat expects

---------

Co-authored-by: Jonas Björkert <jonas@bjorkert.se>
…ity (#805)

* Don't send the watch a state that hasn't changed

Every Nightscout fetch rebuilds the chart, and every rebuild sent the full
24-hour state to the watch, even when nothing in it had changed. Each send
can wake the watch app.

The phone now skips a state equal to the last one sent, apart from its build
time. It still sends at least every 5 minutes, well inside the 10 minutes
after which the watch treats the phone's data as stale and fetches on its
own. It always sends after the session activates or the watch changes (new
install or pairing), and retries after a failed send.

* Fetch from the watch after a command it sends without the phone

When the phone is out of reach, the watch sends commands directly to Loop or
Trio. Nothing then fetched the result: the watch only fetches on its own once
the phone's last state is more than 10 minutes old, so a command sent soon
after losing the phone could take up to 10 minutes to show.

After a successful direct command, the watch now fetches 10, 20, 40 and 60
seconds after the send, even while the phone's last state is still fresh,
and stops at the first fetch that shows the change: the preset's override or
temp target, its cancellation, or a new bolus or carb entry with the sent
amount. A check that never matches only means all four fetches run.

The follow-up stops as soon as a newer state arrives from the phone, so a
fetch that was already under way does not replace it. While the phone's last
state is still fresh, its alarm stays shown, since the watch's own fetch has
no alarms.

The watch's own fetch now reads the override name from "notes" before
"reason", as the phone does, so Trio overrides show the same name on both.

* Fetch Nightscout early on the phone after a remote command

Loop and Trio apply a command within seconds, but the phone only saw the
result on its next regular fetch: up to 2 minutes for treatments (overrides,
temp targets, boluses, carbs) and about 5 minutes for device status (IOB,
recommended bolus). The phone, the watch and the Live Activity showed the
old state until then.

After a command is sent from the phone or the watch, the phone now fetches
treatments and device status 10, 20, 30, 60, 90 and 120 seconds later. It
stops as soon as the command's change is visible: the active override or
temp target differs from before the send, or a new bolus or carb entry with
the sent amount appears. A Trio meal sent with a bolus waits for both. A
newer command restarts the schedule.

Each fetch schedules the next, so when the app was suspended past several
of them, as in the Bluetooth heartbeat modes, it fetches once on resuming
instead of all the overdue ones at once.

* Don't offer the watch a recommendation from before a manual bolus

When the watch fetches on its own, it took the recommended bolus from the
latest device status as is. If a manual bolus came after that loop cycle,
the recommendation leaves that bolus out, and the watch could start the
bolus screen at it. The phone already drops such a recommendation, and the
watch only dropped one from before insulin it sent itself.

The watch's own fetch now drops it the same way as the phone: when a manual
bolus (not automatic, not an SMB) is at or after the loop cycle. This matters
more now that the watch also fetches right after a command it sends without
the phone, while the phone's last state is still fresh.
When no temp basal is running, the Basal info value is the scheduled rate at the current time, and the basal graph from the end of the last temp basal follows the schedule in the profile time zone, stepping at each schedule entry.
Trio in open loop uploads dosingMode "open" with only a suggested block and no temp basals, and Loop reports its Closed Loop setting as loopSettings.dosingEnabled in the profile. The loop status shows ⏀ in both cases, Trio IOB falls back to suggested.IOB, and the Basal row shows the scheduled profile basal when there are no temp basals.
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

⚠️ This PR targets the main branch. We do not accept PRs directly to main — please retarget your PR to the dev branch instead.

@marionbarker
marionbarker merged commit 0e6faaa into main Nov 2, 2026
3 checks passed
@marionbarker
marionbarker deleted the release/v8.0.0 branch November 2, 2026 18:44
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.

7 participants