瀏覽代碼

Add algorithmError; sync locales

Deniz Cengiz 4 周之前
父節點
當前提交
d5b98a0190

+ 141 - 126
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -7994,18 +7994,6 @@
         }
       }
     },
-    "%@ – %@" : {
-      "comment" : "A description of the current day/night glucose alert configuration.",
-      "isCommentAutoGenerated" : true,
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "new",
-            "value" : "%1$@ – %2$@"
-          }
-        }
-      }
-    },
     "%@ (%@-%@)" : {
       "localizations" : {
         "bg" : {
@@ -10184,18 +10172,6 @@
         }
       }
     },
-    "%lld %@" : {
-      "comment" : "A label displaying the current value of a stepper. The value is automatically updated as the user adjusts the stepper.",
-      "isCommentAutoGenerated" : true,
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "new",
-            "value" : "%1$lld %2$@"
-          }
-        }
-      }
-    },
     "%lld %%" : {
       "localizations" : {
         "bg" : {
@@ -10816,10 +10792,6 @@
         }
       }
     },
-    "%lld sec" : {
-      "comment" : "A pluralized string that describes a number of seconds.",
-      "isCommentAutoGenerated" : true
-    },
     "%lld x" : {
       "localizations" : {
         "bg" : {
@@ -27518,6 +27490,9 @@
       "comment" : "A comparison operator.",
       "isCommentAutoGenerated" : true
     },
+    "Acknowledge" : {
+
+    },
     "Acknowledge all alerts" : {
       "localizations" : {
         "bg" : {
@@ -30667,6 +30642,7 @@
       }
     },
     "Add Glucose Source to Alarm" : {
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -32829,6 +32805,9 @@
         }
       }
     },
+    "Add Variant" : {
+
+    },
     "Add visual Rings around the contact image to highlight information." : {
       "localizations" : {
         "bg" : {
@@ -33850,9 +33829,6 @@
         }
       }
     },
-    "Adds your current glucose to the top right of your Trio icon as a red notification badge. Takes effect on the next glucose reading." : {
-
-    },
     "Adjust basal" : {
       "comment" : "Headline \"Adjust basal\"",
       "extractionState" : "manual",
@@ -34616,6 +34592,7 @@
       }
     },
     "Adjust these values if you would like the statistics to reflect different values than the internationally accepted Time In Range values used as the default." : {
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -36265,14 +36242,6 @@
       "comment" : "A section header for the alert sound settings.",
       "isCommentAutoGenerated" : true
     },
-    "Alert when any reading at or above this value persists for the chosen window." : {
-      "comment" : "Text displayed in a section header for a high glucose alert.",
-      "isCommentAutoGenerated" : true
-    },
-    "Alert when any reading at or below this value persists for the chosen window." : {
-      "comment" : "Footer for the low glucose alert section.",
-      "isCommentAutoGenerated" : true
-    },
     "Algorithm" : {
       "comment" : "Algorithm menu item in the Settings main view.",
       "localizations" : {
@@ -45744,10 +45713,6 @@
         }
       }
     },
-    "App Badge" : {
-      "comment" : "Section title for the glucose app badge setting.",
-      "isCommentAutoGenerated" : true
-    },
     "App diagnostic insights — based on crash reports only — help us enhance app stability, ensure safety for all users, and quickly identify and resolve critical issues." : {
 
     },
@@ -46624,14 +46589,6 @@
         }
       }
     },
-    "Appends the CGM source name to glucose notification text." : {
-      "comment" : "Verbose hint for the \"Add Glucose Source to Alarm\" setting.",
-      "isCommentAutoGenerated" : true
-    },
-    "Appends the source of the glucose reading (e.g. Dexcom G6) to the notification body." : {
-      "comment" : "A description of how the glucose source is appended to the glucose notification.",
-      "isCommentAutoGenerated" : true
-    },
     "Apple Health" : {
       "localizations" : {
         "bg" : {
@@ -47252,6 +47209,9 @@
         }
       }
     },
+    "Applies To" : {
+
+    },
     "Apply a Temporary Target" : {
       "localizations" : {
         "bg" : {
@@ -55806,6 +55766,9 @@
         }
       }
     },
+    "Battery Empty" : {
+
+    },
     "Battery is running low %@, consider charging your %@ device as soon as possible" : {
       "comment" : "Battery is running low %@, consider charging your %@ device as soon as possible",
       "extractionState" : "manual",
@@ -55938,6 +55901,9 @@
         }
       }
     },
+    "Battery Low" : {
+
+    },
     "Be warned of connectivity or looping issues." : {
       "localizations" : {
         "bg" : {
@@ -56558,6 +56524,9 @@
         }
       }
     },
+    "Behavior" : {
+
+    },
     "below" : {
       "comment" : "A label describing a glucose value is below.",
       "isCommentAutoGenerated" : true
@@ -59360,6 +59329,9 @@
         }
       }
     },
+    "Bolus Failed" : {
+
+    },
     "Bolus failed or inaccurate. Check pump history before repeating." : {
       "comment" : "Bolus failed or inaccurate. Check pump history before repeating.",
       "extractionState" : "stale",
@@ -73611,9 +73583,6 @@
         }
       }
     },
-    "Color Thresholds" : {
-
-    },
     "Coming soon." : {
       "localizations" : {
         "bg" : {
@@ -83465,6 +83434,9 @@
       "comment" : "A section of the settings screen that allows the user to set the start times for day and night.",
       "isCommentAutoGenerated" : true
     },
+    "Day and Night Window" : {
+
+    },
     "Day and Night windows are configured globally on the Alarms screen." : {
       "comment" : "A description of how the day and night windows are configured.",
       "isCommentAutoGenerated" : true
@@ -84375,6 +84347,9 @@
         }
       }
     },
+    "Decides when each alarm's Day or Night setting applies." : {
+
+    },
     "Decrease sensitivity when glucose is below target if a manual Temp Target < %@ %@ is set." : {
       "localizations" : {
         "bg" : {
@@ -85017,6 +84992,9 @@
         }
       }
     },
+    "Default notification banner. Suppressed by Silence and Focus Mode." : {
+
+    },
     "Default Percent: 70%" : {
       "localizations" : {
         "bg" : {
@@ -88137,6 +88115,9 @@
         }
       }
     },
+    "Default: Day starts 06:00, Night starts 22:00." : {
+
+    },
     "Default: Disabled" : {
       "localizations" : {
         "bg" : {
@@ -90977,10 +90958,6 @@
         }
       }
     },
-    "Delay Between Sounds" : {
-      "comment" : "A label displayed under the sound delay slider.",
-      "isCommentAutoGenerated" : true
-    },
     "Delay In Minutes" : {
       "comment" : "Delay",
       "extractionState" : "manual",
@@ -93173,6 +93150,9 @@
         }
       }
     },
+    "Delete Variant" : {
+
+    },
     "Delivery limits" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -93428,6 +93408,9 @@
         }
       }
     },
+    "Delivery Uncertain" : {
+
+    },
     "Delta" : {
       "localizations" : {
         "bg" : {
@@ -94692,6 +94675,9 @@
         }
       }
     },
+    "Device Alarms" : {
+
+    },
     "Device App Settings" : {
       "comment" : "A section header for settings related to a connected device.",
       "localizations" : {
@@ -94817,6 +94803,12 @@
         }
       }
     },
+    "Device Expiration Reminder" : {
+
+    },
+    "Device Expired" : {
+
+    },
     "Devices" : {
       "comment" : "Devices menu item in the Settings main view.",
       "localizations" : {
@@ -122379,6 +122371,12 @@
     "Fires when glucose drops to or below an urgent low threshold." : {
 
     },
+    "Fires when glucose is at or above this value." : {
+
+    },
+    "Fires when glucose is at or below this value." : {
+
+    },
     "Fires when glucose is forecasted to be low within the next 20 minutes." : {
 
     },
@@ -128195,6 +128193,9 @@
     "Glucose and Trend, no Delta" : {
       "comment" : "Live Activity widget icon label for Glucose and Trend, no Delta"
     },
+    "Glucose App Badge" : {
+
+    },
     "Glucose Calculation" : {
       "localizations" : {
         "bg" : {
@@ -128815,6 +128816,9 @@
         }
       }
     },
+    "Glucose Data Stale" : {
+
+    },
     "Glucose Data used for statistics" : {
       "comment" : "Debug option view Glucose Data used for statistics",
       "extractionState" : "manual",
@@ -129195,9 +129199,6 @@
         }
       }
     },
-    "Glucose Display" : {
-
-    },
     "Glucose Distribution" : {
       "localizations" : {
         "bg" : {
@@ -129694,14 +129695,6 @@
         }
       }
     },
-    "Glucose must stay above the threshold this long before alerting. Set 0 to alert immediately." : {
-      "comment" : "Glucose must stay above the threshold this long before alerting. Set 0 to alert immediately.",
-      "isCommentAutoGenerated" : true
-    },
-    "Glucose must stay below the threshold this long before alerting. Set 0 to alert immediately." : {
-      "comment" : "Glucose value that must persist below the set threshold for the alert to trigger.",
-      "isCommentAutoGenerated" : true
-    },
     "Glucose Notification visibility" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -129834,6 +129827,7 @@
       }
     },
     "Glucose Notifications" : {
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -133371,6 +133365,9 @@
         }
       }
     },
+    "Hardware Fault" : {
+
+    },
     "Has to be > 0 and <= 1.\nDefault is 0.65 (65 %) * TDD. The rest will be from average of total data (up to 14 days) of all TDD calculations (35 %). To only use past 24 hours, set this to 1.\n\nTo avoid sudden fluctuations, for instance after a big meal, an average of the past 2 hours of TDD calculations is used instead of just the current TDD (past 24 hours at this moment)." : {
       "comment" : "Weight of past 24 hours of insulin",
       "extractionState" : "manual",
@@ -135295,10 +135292,6 @@
       "comment" : "Display name for the high_chimes.caf alarm sound.",
       "isCommentAutoGenerated" : true
     },
-    "High Color Threshold" : {
-      "comment" : "A label displayed under the high glucose color threshold.",
-      "isCommentAutoGenerated" : true
-    },
     "High Contrast Mode" : {
       "localizations" : {
         "bg" : {
@@ -135673,6 +135666,7 @@
       }
     },
     "High Glucose Alarm Limit" : {
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -158467,10 +158461,6 @@
         }
       }
     },
-    "Look ahead this many minutes in the forecast curves; alert early if any value would dip below the threshold. Set 0 to disable." : {
-      "comment" : "Look ahead this many minutes in the forecast curves; alert early if any value would dip below the threshold. Set 0 to disable.",
-      "isCommentAutoGenerated" : true
-    },
     "Loop at %@ failed." : {
       "localizations" : {
         "bg" : {
@@ -158727,6 +158717,9 @@
         }
       }
     },
+    "Loop Not Running" : {
+
+    },
     "Loop statistics will appear here once data is available." : {
       "localizations" : {
         "bg" : {
@@ -160622,9 +160615,6 @@
         }
       }
     },
-    "Low Color Threshold" : {
-
-    },
     "Low Glucose" : {
       "localizations" : {
         "bg" : {
@@ -160875,6 +160865,7 @@
       }
     },
     "Low Glucose Alarm Limit" : {
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -163645,6 +163636,9 @@
         }
       }
     },
+    "Manual Temp Basal Active" : {
+
+    },
     "Manual Temporary Basal Rate (%@). Looping suspended." : {
       "localizations" : {
         "bg" : {
@@ -187980,6 +187974,9 @@
         }
       }
     },
+    "Occlusion" : {
+
+    },
     "of" : {
       "comment" : "Bolus progress view",
       "extractionState" : "manual",
@@ -189486,6 +189483,7 @@
     },
     "Only Alarm Limits" : {
       "comment" : "Option to notify only when glucose reaches alarm limits",
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -193153,9 +193151,11 @@
         }
       }
     },
-    "Override Silence & Do Not Disturb" : {
-      "comment" : "A toggle that allows the user to override silence and Do Not Disturb.",
-      "isCommentAutoGenerated" : true
+    "Override Silence & Focus" : {
+
+    },
+    "Override Silence & Focus Mode" : {
+
     },
     "Override Target" : {
       "localizations" : {
@@ -193793,6 +193793,12 @@
         }
       }
     },
+    "Overrides Silence & Focus Mode" : {
+
+    },
+    "Overrides Silence & Focus Mode. Always audible. For hazardous failures." : {
+
+    },
     "Overview" : {
       "localizations" : {
         "bg" : {
@@ -197093,14 +197099,6 @@
         }
       }
     },
-    "Persistence" : {
-      "comment" : "Title for the section that lets the user set how long a low glucose value must persist before triggering an alert.",
-      "isCommentAutoGenerated" : true
-    },
-    "Persistent" : {
-      "comment" : "\"Persistent\" is a generic term for \"how long\" or \"for how long\".",
-      "isCommentAutoGenerated" : true
-    },
     "Phone NFC required!" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -197356,9 +197354,11 @@
         }
       }
     },
-    "Play" : {
-      "comment" : "Title of a menu item that allows the user to select whether to play the sound of the alarm.",
-      "isCommentAutoGenerated" : true
+    "Pierces banner suppression but obeys Silence & Focus Mode." : {
+
+    },
+    "Play Sound" : {
+
     },
     "Please authorize notifications by tapping 'Open iOS Settings' > 'Notifications' and enable 'Allow Notifications' for 'Notification Center' and 'Banners' Alerts." : {
       "localizations" : {
@@ -198886,10 +198886,6 @@
         }
       }
     },
-    "Prediction" : {
-      "comment" : "Label for the prediction setting in the glucose alert editor.",
-      "isCommentAutoGenerated" : true
-    },
     "Prediction Type" : {
       "localizations" : {
         "bg" : {
@@ -199138,10 +199134,6 @@
         }
       }
     },
-    "Predictive" : {
-      "comment" : "Label for the number of minutes to look ahead in the forecast for predictive alerts.",
-      "isCommentAutoGenerated" : true
-    },
     "Preferences" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -207944,7 +207936,7 @@
         }
       }
     },
-    "Recommended to always override silence and Do Not Disturb." : {
+    "Recommended to always override silence and Focus mode." : {
 
     },
     "Recommended:" : {
@@ -209812,10 +209804,6 @@
         }
       }
     },
-    "Repeat" : {
-      "comment" : "Label for a picker that lets the user select how many times to repeat an alert sound.",
-      "isCommentAutoGenerated" : true
-    },
     "Replace pod" : {
       "comment" : "View/Header when pod expired",
       "localizations" : {
@@ -210189,6 +210177,9 @@
         }
       }
     },
+    "Reservoir Empty" : {
+
+    },
     "Reservoir is empty" : {
       "localizations" : {
         "bg" : {
@@ -210313,6 +210304,9 @@
         }
       }
     },
+    "Reservoir Low" : {
+
+    },
     "Reset and retry send" : {
 
     },
@@ -219809,6 +219803,9 @@
         }
       }
     },
+    "Sensor Failure" : {
+
+    },
     "Sensor Footer checksum" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -221598,6 +221595,7 @@
       }
     },
     "Set low and high glucose values for the main screen, watch app and live activity glucose graph." : {
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -221721,6 +221719,9 @@
         }
       }
     },
+    "Set low and high glucose values for the main screen, watch app, live activity, and contact image colors." : {
+
+    },
     "Set temp targets presets on iPhone first" : {
       "comment" : "Whatch app alert",
       "extractionState" : "manual",
@@ -225644,12 +225645,7 @@
         }
       }
     },
-    "Show your current glucose on the Trio app icon." : {
-      "comment" : "Hint for the glucose badge setting.",
-      "isCommentAutoGenerated" : true
-    },
     "Show your current glucose on Trio app icon." : {
-      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -226269,6 +226265,9 @@
         }
       }
     },
+    "Shutdown Imminent" : {
+
+    },
     "Sigmoid" : {
       "localizations" : {
         "bg" : {
@@ -230159,6 +230158,9 @@
         }
       }
     },
+    "Snooze" : {
+
+    },
     "Snooze Alerts" : {
       "localizations" : {
         "bg" : {
@@ -231166,6 +231168,12 @@
         }
       }
     },
+    "Sound off" : {
+
+    },
+    "Sound on" : {
+
+    },
     "Source of the glucose reading will be added to the notification." : {
       "extractionState" : "stale",
       "localizations" : {
@@ -237947,6 +237955,9 @@
         }
       }
     },
+    "Suspend Time Expired" : {
+
+    },
     "Suspend Zeros IOB" : {
       "localizations" : {
         "bg" : {
@@ -247608,6 +247619,9 @@
         }
       }
     },
+    "The Night window runs from 'Night Starts' back around to 'Day Starts' — so by default, Night covers 22:00 through 06:00 the next morning." : {
+
+    },
     "The Noisy CGM Target Increase raises your glucose target when the system detects noisy or raw CGM data. By default, the target is increased to 130% of your set target glucose to account for the less reliable glucose readings." : {
       "localizations" : {
         "bg" : {
@@ -250353,13 +250367,8 @@
         }
       }
     },
-    "These global times decide when each alarm's Day / Night setting applies." : {
-      "comment" : "A description of the day and night start times.",
-      "isCommentAutoGenerated" : true
-    },
-    "These thresholds set the **color boundaries** for the chart bands, watch widget, and contact image — they don't fire alerts on their own." : {
-      "comment" : "A description of the color thresholds.",
-      "isCommentAutoGenerated" : true
+    "These thresholds drive the chart band colors, statistics in-range buckets, Live Activity colors, and Contact Image colors." : {
+
     },
     "These two settings determine the range outside of which you will be notified via push notifications." : {
       "extractionState" : "stale",
@@ -250486,9 +250495,11 @@
         }
       }
     },
-    "These values drive the watch widget, contact image, and chart band colors. To configure when alarms fire, open Glucose Alarms." : {
-      "comment" : "A description of the glucose thresholds.",
-      "isCommentAutoGenerated" : true
+    "These two times define the Day and Night windows. Each alarm's Active setting picks one — Day & Night, Day only, or Night only — and only fires when that window is current." : {
+
+    },
+    "These windows are shared between Glucose Alarms and Device Alarms." : {
+
     },
     "This 15% minimum is a safety limit taken from oref (OpenAPS reference design) and AndroidAPS. It helps prevent insulin delivery from dropping to unsafe levels." : {
       "localizations" : {
@@ -258048,7 +258059,6 @@
       }
     },
     "This will add your current glucose on the top right of your Trio icon as a red notification badge. Changing setting takes effect on next Glucose reading." : {
-      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {
@@ -259932,6 +259942,9 @@
         }
       }
     },
+    "Time-Sensitive" : {
+
+    },
     "Tip: Decreasing this value allows automatic adjustments of basal rates to be lower and ISF to be higher." : {
       "localizations" : {
         "bg" : {
@@ -261296,9 +261309,8 @@
         }
       }
     },
-    "To configure when alarms fire (urgent low, low, forecasted low, high) including thresholds, sounds, schedules, and DND override, open **Glucose Alarms**." : {
-      "comment" : "A description of how to configure glucose alarm thresholds, sounds, schedules, and DND override.",
-      "isCommentAutoGenerated" : true
+    "To configure when alarms fire (urgent low / low / high / forecasted low), open Glucose Alarms." : {
+
     },
     "To configure your CGM, tap the button below. In the form that opens, enter your Nightscout credentials to connect to your instance." : {
       "localizations" : {
@@ -287574,6 +287586,9 @@
         }
       }
     },
+    "Window Boundaries" : {
+
+    },
     "With iOS Trio Notifications enabled, you can let Trio display most Algorithm Notifications in iOS Notification Center as a Banner, List and on the Lock Screen. It allows you to refer to Trio Information at a glance and troubleshoot any informational issue. Set iOS Notifications Banner Style to Persistent to display banners in the app until dismissed." : {
       "extractionState" : "stale",
       "localizations" : {

+ 7 - 3
Trio/Sources/Models/GlucoseAlerts/PumpAlertCategory.swift

@@ -21,6 +21,7 @@ enum PumpAlertCategory: String, Codable, CaseIterable, Identifiable {
     case podShutdownImminent
     case suspendTimeExpired
     case glucoseDataStale
+    case algorithmError
 
     var id: String { rawValue }
 
@@ -42,6 +43,7 @@ enum PumpAlertCategory: String, Codable, CaseIterable, Identifiable {
         case .podShutdownImminent: return String(localized: "Shutdown Imminent")
         case .suspendTimeExpired: return String(localized: "Suspend Time Expired")
         case .glucoseDataStale: return String(localized: "Glucose Data Stale")
+        case .algorithmError: return String(localized: "Algorithm Error")
         }
     }
 
@@ -53,7 +55,8 @@ enum PumpAlertCategory: String, Codable, CaseIterable, Identifiable {
              .hardwareFault,
              .notLooping,
              .occlusion,
-             .reservoirEmpty:
+             .reservoirEmpty,
+             .sensorFailure:
             return .critical
         case .batteryLow,
              .bolusFailed,
@@ -62,10 +65,10 @@ enum PumpAlertCategory: String, Codable, CaseIterable, Identifiable {
              .manualTempBasalActive,
              .podShutdownImminent,
              .reservoirLow,
-             .sensorFailure,
              .suspendTimeExpired:
             return .timeSensitive
-        case .deviceExpirationReminder:
+        case .algorithmError,
+             .deviceExpirationReminder:
             return .normal
         }
     }
@@ -88,6 +91,7 @@ enum PumpAlertCategory: String, Codable, CaseIterable, Identifiable {
         case .podShutdownImminent: self = .podShutdownImminent
         case .suspendTimeExpired: self = .suspendTimeExpired
         case .glucoseDataStale: self = .glucoseDataStale
+        case .algorithmError: self = .algorithmError
         default: return nil
         }
     }

+ 15 - 5
Trio/Sources/Services/Alerts/TrioAlertCategory.swift

@@ -47,9 +47,15 @@ enum TrioAlertCategory: Equatable {
     case commsTransient
     case other(String)
 
+    /// Whether an inbound alert in this category surfaces *immediately*.
+    /// `false` means the category is dwell-suppressed by `APSManager`
+    /// (count + time threshold) before it reaches `TrioAlertManager`.
+    /// `commsTransient` stays suppressed because connectivity blips
+    /// recover on their own.
     var isAlertWorthy: Bool {
         switch self {
-        case .batteryEmpty,
+        case .algorithmError,
+             .batteryEmpty,
              .batteryLow,
              .bolusFailed,
              .deliveryUncertain,
@@ -71,8 +77,7 @@ enum TrioAlertCategory: Equatable {
              .sensorFailure,
              .suspendTimeExpired:
             return true
-        case .algorithmError,
-             .commsTransient:
+        case .commsTransient:
             return false
         }
     }
@@ -85,7 +90,8 @@ enum TrioAlertCategory: Equatable {
              .hardwareFault,
              .notLooping,
              .occlusion,
-             .reservoirEmpty:
+             .reservoirEmpty,
+             .sensorFailure:
             return .critical
         case .batteryLow,
              .bolusFailed,
@@ -97,7 +103,6 @@ enum TrioAlertCategory: Equatable {
              .manualTempBasalActive,
              .podShutdownImminent,
              .reservoirLow,
-             .sensorFailure,
              .suspendTimeExpired:
             return .timeSensitive
         case .algorithmError,
@@ -207,6 +212,11 @@ enum TrioAlertClassifier {
         // by the not-looping monitor, not by any pump manager.
         if id.contains("notlooping") || id.contains("loop.notactive") { return .notLooping }
 
+        // Algorithm error — emitted by APSManager after dwell suppression.
+        // Re-classification by identifier needs to land here so the tier
+        // config in Device Alarms applies on the round-trip through issueAlert.
+        if id.contains("algorithmerror") || id.contains("apserror") { return .algorithmError }
+
         return .other(alertIdentifier)
     }