Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
369e875
Put the treatment button in the side tab bar on iPhone Duo
gordolio Oct 22, 2026
76961bc
Scale compact Dynamic Island text instead of truncating it in a narro…
Lamercho Oct 25, 2026
e614f41
Make schedule tests DST-safe
dnzxy Oct 25, 2026
aaa65a7
Merge pull request #1670 from dnzxy/fix/dst-unit-tests
dnzxy Oct 25, 2026
b4de747
CI: Bump APP_DEV_VERSION to 1.0.2.27 [skip ci]
github-actions[bot] Oct 25, 2026
596a197
Add logging contracts and scaffold
dnzxy Oct 18, 2026
1528566
Persist the LoopKit device log
dnzxy Oct 18, 2026
efd24bc
Route logging through os.Logger and a sink
dnzxy Oct 18, 2026
34947f9
Export device logs from Settings and device screens
dnzxy Oct 18, 2026
854c565
Store the app log in Core Data
dnzxy Oct 18, 2026
2b03e2c
Add diagnostic report with redaction
dnzxy Oct 18, 2026
20c3b6d
Simplify log export to one file per kind
dnzxy Oct 19, 2026
d2eb49d
Add log categories by subject
dnzxy Oct 19, 2026
2bac332
Clean up watch and Live Activity log messages
dnzxy Oct 19, 2026
84c1e6a
Clean up Nightscout and remote log messages
dnzxy Oct 19, 2026
94b0e8b
Clean up loop, pump and CGM log messages
dnzxy Oct 19, 2026
6ab7957
Clean up storage and lifecycle log messages
dnzxy Oct 19, 2026
c0932df
Clean up bolus, treatment and alert log messages
dnzxy Oct 19, 2026
96fd027
Retire legacy log categories
dnzxy Oct 19, 2026
fac0573
Rename Share Logs to Share Trio Logs
dnzxy Oct 19, 2026
96cd81f
Add logging stress tests
dnzxy Oct 19, 2026
2428660
Fix log store robustness bugs
dnzxy Oct 19, 2026
a233d37
Redact exact stored sensitive values only
dnzxy Oct 19, 2026
d581ac3
Finish log export performance pass
dnzxy Oct 19, 2026
b86d399
Re-run CI on current dev
Lamercho Oct 25, 2026
5628b0f
Add Report a Problem
dnzxy Oct 22, 2026
8426682
Retry store open after a missed unlock
dnzxy Oct 23, 2026
6df8c0c
Add shared watch log entry
dnzxy Oct 23, 2026
c7902e4
Keep watch logs in a persistent ring
dnzxy Oct 23, 2026
34e2838
Send watch logs as queued files
dnzxy Oct 23, 2026
6aed89e
Ingest structured watch logs on the phone
dnzxy Oct 23, 2026
85b756a
Label watch lines in exports
dnzxy Oct 23, 2026
96f8762
Clean up watch log messages
dnzxy Oct 23, 2026
e0d6af9
Add watch log tests
dnzxy Oct 23, 2026
9969176
Acknowledge watch logs only once stored
dnzxy Oct 23, 2026
2202523
Fix log retention at 8 days
dnzxy Oct 23, 2026
9f0b8fa
Clean up log messages added on dev
dnzxy Oct 24, 2026
ae755fb
Add 90-day long-run logging stress test
dnzxy Oct 24, 2026
7383cb2
Purge the device log a day at a time
dnzxy Oct 24, 2026
f6b1618
Purge the app log a day at a time
dnzxy Oct 24, 2026
d8c098e
Log errors as plain text
dnzxy Oct 24, 2026
9a068c2
Redact Syai IDs and capture Syai traces
dnzxy Oct 24, 2026
f168fd8
Merge branch 'dev' into fix/live-activity-compact-island
Lamercho Oct 25, 2026
bc66515
Re-run CI
Lamercho Oct 25, 2026
0f937d2
Merge branch 'dev' into fix/tab-rail-treatment-button
gordolio Oct 25, 2026
065f5f3
Remove code comments
Lamercho Oct 25, 2026
6054291
Merge pull request #1668 from Lamercho/fix/live-activity-compact-island
dnzxy Oct 25, 2026
a60d8b3
CI: Bump APP_DEV_VERSION to 1.0.2.28 [skip ci]
github-actions[bot] Oct 25, 2026
49b1096
Merge pull request #1654 from gordolio/fix/tab-rail-treatment-button
dnzxy Oct 25, 2026
7ff1f77
CI: Bump APP_DEV_VERSION to 1.0.2.29 [skip ci]
github-actions[bot] Oct 25, 2026
e40786a
Merge pull request #1660 from dnzxy/logging/main
dnzxy Oct 26, 2026
45cd2ca
CI: Bump APP_DEV_VERSION to 1.0.2.30 [skip ci]
github-actions[bot] Oct 26, 2026
f0b820b
sync(trio): merge upstream/dev at 45cd2cacb
mountrcg Oct 27, 2026
aa07440
Align Tai-specific logging with the new logging rules
mountrcg Oct 27, 2026
c5962d0
Fix build errors left by the upstream logging merge
mountrcg Oct 27, 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
150 changes: 150 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Contributing to Trio

Thank you for your interest in contributing to Trio.

Trio is a community effort, and contributions of all kinds are welcome. This document outlines some guidelines, good practices, and expectations for contributing to the project, with the goal of making collaboration and review as smooth as possible.

Whether you are helping other users, improving documentation, translating the app, testing builds, reviewing code, or contributing new features and fixes, your work matters.

## Ways to contribute

There are many ways to support the Trio community:

- **Help others** by answering questions and guiding users in support communities.
- Improve the **documentation** by updating or expanding TrioDocs.
- Improve the **app** by contributing code, fixes, features, or tests.
- Help with **translation and localization** through Crowdin.
- Support **testing and feedback** by validating changes and reporting issues clearly.

### Pay it forward

If Trio has helped you manage your diabetes successfully, consider paying it forward by helping others. Answering questions in [Discord](https://discord.triodocs.org) or [Facebook](https://facebook.triodocs.org) groups can make a real difference for someone getting started.

### Translate

Trio is translated into multiple languages to make it easier to understand and use around the world. Translation is managed through [Crowdin](https://crowdin.triodocs.org/) and does not require programming experience.

If your preferred language is missing, or you would like to improve an existing translation, please sign up as a translator on [Crowdin](https://crowdin.com/project/trio).

### Develop

Do you work with Swift? UI/UX? Testing? API optimization? Data storage?

Trio is a collaborative project, and contributions of all kinds are welcome. Whether you are writing code, improving the user experience, testing builds, helping with documentation, or contributing in other ways, your help matters.

## General principles

- Start small. Smaller, focused contributions are easier to review, test, and merge.
- For larger changes or new features, open or reference an issue first so there is a clear place for discussion and progress tracking.
- Reach out early if you are planning to work on something substantial, especially if it may overlap with work already in progress.
- Keep discussions constructive, respectful, and focused on improving Trio for the community.
- Remember that Trio is part of a wider open source AID ecosystem. Collaboration and maintainability matter just as much as shipping features.

## Development guidelines

### Coding conventions

- Use Xcode and follow the existing formatting and style used throughout the codebase.
- Keep indentation and formatting consistent in every file you change.
- Format your code before committing.
- Avoid unrelated formatting-only changes in files you are not otherwise modifying.
- Prefer clear, readable code over clever or overly compact solutions.
- Follow existing naming, file organization, and architectural patterns unless there is a good reason not to.

### Strings and localization

- Add new user-facing strings in the appropriate localization mechanism used by the app.
- Provide English source strings only unless the contribution is specifically about translations.
- Translation and localization for other languages should go through [Crowdin](https://crowdin.triodocs.org/).
- Translations from shared pump managers, CGM managers and services are handled by the [Loop lokalise project](https://loopkit.github.io/loopdocs/faqs/app-translation/#code-translation).

### Logging

Users share Trio's logs when they ask for help, so every line should make sense to someone who doesn't know the code.

- Use `debug`, `info`, `warning`, `error`, or `fault`; never `print`, `debugPrint`, or `NSLog`.
- `debug`: detail for developers.
- `info`: something meaningful happened (loop ran, bolus delivered, pump connected, setting changed).
- `warning`: something failed and Trio recovered or will retry.
- `error`: something failed and the user may notice.
- `fault`: something that should never happen.
- Pick the category by what the line is about (`loop`, `pump`, `cgm`, `nightscout`, `storage`, …), not by folder.
- Write a short plain sentence: no emoji, no decorative prefixes, no "function called" traces, units with numbers (`BG 123 mg/dL`, `IOB 1.25 U`).
- Don't log whole objects or JSON, and never log secrets, tokens, names, or emails.
- A `catch` that swallows an error logs at `warning` or higher and passes it: `warning(.nightscout, "Upload of glucose failed", error: error)`.

### Documentation

- Update docstrings when your change affects setup, configuration, behavior, workflows, or troubleshooting.
- Keep documentation changes clear and practical.
- +1: Documentation contributions are just as valuable as code contributions.

## Branches, commits, and pull requests

### Getting started

1. Fork the [Trio repository](https://github.com/nightscout/Trio) on GitHub.
1. Create a separate branch for each feature or fix with an [appropriate name](#branch-names).
1. Branch from the most recent appropriate development branch (typically `dev`).
1. Commit your changes to your fork.
1. When ready, open a pull request against the upstream repository (`nightscout/Trio`).

### Before opening a pull request

- Rebase or otherwise sync your branch with the latest target branch.
- Make sure your change is focused and does not include unrelated edits.
- Test your changes as thoroughly as you reasonably can.
- Update relevant documentation when needed.
- Double-check for debug code, commented-out code, accidental version changes, or temporary workarounds left behind.

### Pull request guidance

- Keep pull requests as small and focused as practical.
- Use a clear title and description.
- Explain **what** changed and **why**.
- Link the relevant issue when applicable.
- Mention any areas that need particular review attention.
- Be open to feedback and follow-up changes during review.
- Use AI tools, if at all, as a support for small, well-understood tasks rather than to generate large parts of a contribution
- Do not submit AI-heavy or "vibe-coded" pull requests; we welcome thoughtful use of tooling, but contributions need to be intentionally designed.

## Naming conventions

### Branch names

Use short, descriptive branch names that make the purpose of the change obvious. For example:

- `fix/watchstate-sync`
- `feature/onboarding-target-behavior`
- `refactor/therapy-editor`

### Pull request titles

Use concise, descriptive pull request titles. Good titles usually start with the type of change, for example:

- `Fix watch state sync timing issue`
- `Add onboarding step for target behavior`
- `Update build documentation`
- `Refactor therapy editor validation`

## Communication and coordination

For new ideas, larger features, or work that may affect multiple parts of the app, **discuss it with the community first** — reach out to the contributor core on [Trio Discord](https://discord.triodocs.org/). This helps reduce duplicate work, avoid merge conflicts, and improve the final design.

## Review expectations

Please remember that Trio is maintained by contributors with limited time. Reviews may take time, and some pull requests may require iteration before they are ready to merge.

To help keep reviews efficient:

- Keep the scope narrow.
- Explain your reasoning clearly.
- Respond to review comments directly.
- Avoid force-pushing large unexplained rewrites during active review unless necessary.
- AI-assisted work is welcome for limited, well-understood tasks, but contributions should remain author-driven and must be code you fully understand, and can explain.

We do not accept pull requests that are largely AI-generated or submitted without careful engineering judgment, testing, and alignment with Trio’s existing patterns.

## Final note

Trio exists because people choose to contribute their time, knowledge, and care to a shared effort. Thank you for helping improve the project and support the broader open source AID community.
2 changes: 1 addition & 1 deletion Config.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ TRIO_APP_GROUP_ID = group.org.nightscout.$(DEVELOPMENT_TEAM).trio.trio-app-group

// The developers set the version numbers, please leave them alone
APP_VERSION = 1.0.2
APP_DEV_VERSION = 1.0.2.26
APP_DEV_VERSION = 1.0.2.30
APP_BUILD_NUMBER = 1
COPYRIGHT_NOTICE = autoISF 3.01

Expand Down
1 change: 0 additions & 1 deletion LiveActivity/LiveActivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ struct LiveActivity: Widget {
.widgetURL(URL(string: "Trio://"))
.keylineTint(glucoseColor)
.contentMargins(.horizontal, 0, for: .minimal)
.contentMargins(.trailing, 0, for: .compactLeading)
.contentMargins(.leading, 0, for: .compactTrailing)
}

Expand Down
6 changes: 5 additions & 1 deletion LiveActivity/Views/LiveActivityView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,9 @@ struct LiveActivityCompactLeadingView: View {
var glucoseColor: Color

var body: some View {
LiveActivityBGAndTrendView(context: context, size: .compact, glucoseColor: glucoseColor).padding(.leading, 4)
LiveActivityBGAndTrendView(context: context, size: .compact, glucoseColor: glucoseColor)
.lineLimit(1)
.minimumScaleFactor(0.5)
}
}

Expand All @@ -274,6 +276,8 @@ struct LiveActivityCompactTrailingView: View {

var body: some View {
LiveActivityGlucoseDeltaLabelView(context: context, glucoseColor: glucoseColor).padding(.trailing, 4)
.lineLimit(1)
.minimumScaleFactor(0.5)
}
}

Expand Down
63 changes: 24 additions & 39 deletions Model/CoreDataStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ class CoreDataStack: ObservableObject {
do {
try await fetchPersistentHistoryTransactionsAndChanges()
} catch let error as NSError where error.code == NSPersistentHistoryTokenExpiredError {
debug(.coreData, "Persistent history token expired; clearing token and replaying available history.")
warning(.storage, "Persistent history token expired, replaying available history")
await recoverFromExpiredHistoryToken()
} catch {
debug(.coreData, "\(error)")
warning(.storage, "Fetching persistent history failed", error: error)
}
}

Expand All @@ -141,7 +141,7 @@ class CoreDataStack: ObservableObject {
do {
try await fetchPersistentHistoryTransactionsAndChanges()
} catch {
debug(.coreData, "Replay after token reset failed; jumping to current token. \(error)")
warning(.storage, "Replaying persistent history failed, jumping to current token", error: error)
let viewContext = persistentContainer.viewContext
let currentToken = persistentContainer.persistentStoreCoordinator
.currentPersistentHistoryToken(fromStores: nil)
Expand Down Expand Up @@ -200,12 +200,9 @@ class CoreDataStack: ObservableObject {
let deleteHistoryTokensRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: date)
do {
try taskContext.execute(deleteHistoryTokensRequest)
debug(.coreData, "\(DebuggingIdentifiers.succeeded) Successfully deleted persistent history from before \(date)")
debug(.storage, "Deleted persistent history before \(date)")
} catch {
debug(
.coreData,
"\(DebuggingIdentifiers.failed) Failed to delete persistent history from before \(date): \(error)"
)
warning(.storage, "Deleting persistent history before \(date) failed", error: error)
}
}
}
Expand All @@ -229,7 +226,7 @@ class CoreDataStack: ObservableObject {
Task { await self?.fetchPersistentHistory() }
}

debug(.coreData, "Set up persistent store change notifications")
debug(.storage, "Set up persistent store change notifications")
}

/// Loads the persistent stores asynchronously.
Expand All @@ -243,10 +240,10 @@ class CoreDataStack: ObservableObject {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
persistentContainer.loadPersistentStores { storeDescription, error in
if let error = error {
warning(.coreData, "Failed to load persistent stores: \(error)")
warning(.storage, "Loading persistent stores failed", error: error)
continuation.resume(throwing: error)
} else {
debug(.coreData, "Successfully loaded persistent store: \(storeDescription.url?.absoluteString ?? "unknown")")
debug(.storage, "Loaded persistent store \(storeDescription.url?.lastPathComponent ?? "unknown")")
continuation.resume(returning: ())
}
}
Expand Down Expand Up @@ -288,14 +285,14 @@ class CoreDataStack: ObservableObject {

setupPersistentStoreChangeNotifications()

debug(.coreData, "Core Data stack initialized successfully")
info(.storage, "Core Data stack initialized")

} catch {
debug(.coreData, "Failed to initialize Core Data stack: \(error)")
} catch let initError {
warning(.storage, "Core Data stack initialization failed", error: initError)

// If we still have retries left, try again after a delay
if retryCount < maxRetries {
debug(.coreData, "Retrying initialization (\(retryCount + 1)/\(maxRetries))")
debug(.storage, "Retrying Core Data initialization (\(retryCount + 1)/\(maxRetries))")

// Wait before retrying
try await Task.sleep(for: .seconds(1))
Expand All @@ -304,8 +301,8 @@ class CoreDataStack: ObservableObject {
try await initializeStack(retryCount: retryCount + 1)
} else {
// We've exhausted our retries
debug(.coreData, "Core Data initialization failed after \(maxRetries) attempts")
throw error
error(.storage, "Core Data initialization failed after \(maxRetries) attempts")
throw initError
}
}
}
Expand All @@ -318,7 +315,6 @@ extension CoreDataStack {
/// - Tag: synchronousDelete
func deleteObject(identifiedBy objectID: NSManagedObjectID) async {
let viewContext = persistentContainer.viewContext
debug(.coreData, "Start deleting data from the store ...\(DebuggingIdentifiers.inProgress)")

await viewContext.perform {
do {
Expand All @@ -327,9 +323,9 @@ extension CoreDataStack {

guard viewContext.hasChanges else { return }
try viewContext.save()
debug(.coreData, "Successfully deleted data. \(DebuggingIdentifiers.succeeded)")
debug(.storage, "Deleted object")
} catch {
debug(.coreData, "Failed to delete data: \(error)")
warning(.storage, "Deleting object failed", error: error)
}
}
}
Expand Down Expand Up @@ -383,14 +379,13 @@ extension CoreDataStack {
let batchDeleteResult = fetchResult as? NSBatchDeleteResult,
let success = batchDeleteResult.result as? Bool, success
else {
debug(.coreData, "Failed to execute batch delete request \(DebuggingIdentifiers.failed)")
throw CoreDataError.batchDeleteError(function: callingFunction, file: callingClass)
}
}

debug(.coreData, "Successfully deleted data older than \(days) days. \(DebuggingIdentifiers.succeeded)")
debug(.storage, "Deleted \(objectType) older than \(days) days")
} catch {
debug(.coreData, "Failed to fetch or delete data: \(error) \(DebuggingIdentifiers.failed)")
warning(.storage, "Deleting \(objectType) older than \(days) days failed", error: error)
throw CoreDataError.unexpectedError(error: error, function: callingFunction, file: callingClass)
}
}
Expand Down Expand Up @@ -449,17 +444,13 @@ extension CoreDataStack {
let batchDeleteResult = fetchResult as? NSBatchDeleteResult,
let success = batchDeleteResult.result as? Bool, success
else {
debug(.coreData, "Failed to execute batch delete request \(DebuggingIdentifiers.failed)")
throw CoreDataError.batchDeleteError(function: callingFunction, file: callingClass)
}
}

debug(
.coreData,
"Successfully deleted \(childType) data related to \(parentType) objects older than \(days) days. \(DebuggingIdentifiers.succeeded)"
)
debug(.storage, "Deleted \(childType) of \(parentType) older than \(days) days")
} catch {
debug(.coreData, "Failed to fetch or delete data: \(error) \(DebuggingIdentifiers.failed)")
warning(.storage, "Deleting \(childType) of \(parentType) older than \(days) days failed", error: error)
throw CoreDataError.unexpectedError(error: error, function: callingFunction, file: callingClass)
}
}
Expand Down Expand Up @@ -603,8 +594,8 @@ extension CoreDataStack {

do {
try context.save()
} catch {
debug(.coreData, "Error saving context \(DebuggingIdentifiers.failed): \(error)")
} catch let saveError {
error(.storage, "Saving view context failed", error: saveError)
}
}
}
Expand All @@ -620,15 +611,9 @@ extension NSManagedObjectContext {
do {
guard onContext.hasChanges else { return }
try onContext.save()
debug(
.coreData,
"Saving to Core Data successful in \(callingFunction) in \(callingClass): \(DebuggingIdentifiers.succeeded)"
)
debug(.storage, "Saved context in \(callingClass) \(callingFunction)")
} catch let error as NSError {
debug(
.coreData,
"Saving to Core Data failed in \(callingFunction) in \(callingClass): \(DebuggingIdentifiers.failed) with error \(error), \(error.userInfo)"
)
warning(.storage, "Saving context in \(callingClass) \(callingFunction) failed", error: error)
throw error
}
}
Expand Down
2 changes: 1 addition & 1 deletion Model/Helper/JSONConverter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class JSONConverter {
return jsonString
}
} catch {
debugPrint("\(DebuggingIdentifiers.failed) could not convert object to JSON: \(error)")
warning(.algorithm, "Encoding \(T.self) as JSON failed", error: error)
}

return "could not convert object to JSON"
Expand Down
Loading
Loading