github dan-snelson/DDM-OS-Reminder v5.2.0b4
DDM OS Reminder (5.2.0b4)

pre-release2 hours ago

Version 5.2.0b4 (19-Oct-2026)

  • Fixed: {minutesBeforeDeadline} in PreDeadlineThresholdMessage showed the crossed threshold (for example, 45) instead of the time actually left, so a late run (padded deadline from setPastDuePaddedEnforcementDate, heartbeat delay, wake from sleep, collapsed stale thresholds, or a manual run) overstated time remaining (Issue #160)
    • The placeholder now shows minutes remaining until the effective deadline, rounded up and never above the crossed threshold, computed when the dialog copy is applied. On-time runs still show the threshold value.
    • Pre-deadline threshold reminder due: … and Applying …-minute pre-deadline threshold dialog copy notices now append (<n> minute(s) remaining).
    • Threshold delivered/skipped state, scheduling, and preference keys are unchanged. Known: copy reads 1 minutes when one minute remains.
  • Resources/diagnoseReminderState.zsh checklist version now 5.2.0b4; no diagnosis logic changes.

Version 5.2.0b3 (18-Oct-2026)

  • New low-battery warning: when a Mac is running on its internal battery below MinimumBatteryPercentage, the reminder dialog asks the user to connect power and leave it plugged in until the macOS update completes (Issue #157; thanks, ebbg!)
    • New MinimumBatteryPercentage preference: integer 0–100, script and Resources/sample.plist default 50; 0 disables. A non-integer or out-of-range value logs [WARNING] and falls back to 50.
    • New BatteryWarningMessage string (plus BatteryWarningMessageLocalized_<code> for en, de, fr, es, pt, ja, nl, and it) with the {batteryPercentage} placeholder, inserted through the new {batteryWarningMessage} placeholder.
    • {batteryWarningMessage} added after {diskSpaceWarningMessage} in Message, AggressiveModeMessage, and PreDeadlineThresholdMessage, and appended to PastDeadlinePromptMessage and PastDeadlineForceMessage (all localized variants included). Restart mode still suppresses the uptime and disk-space warnings but keeps the battery warning, because a restart does not charge the battery.
    • Detection uses positive evidence only from pmset -g ps: the warning appears only when the header reads Now drawing from 'Battery Power', a charge parses from the -InternalBattery- line, and that charge is below the threshold. There is no warning on AC power (including AC attached; not charging), on UPS power, on Macs without an internal battery (desktops, VMs), or when the charge can't be read (logged at [WARNING]). The charge never comes from a UPS line.
    • Each run logs one [NOTICE] Battery: power source <source>; internal battery <charge>|not present|unreadable; MinimumBatteryPercentage threshold <n>%; warning shown|suppressed|disabled line.
    • Messaging only: PastDeadlineRestartBehavior Prompt / Force eligibility, timing, and suppression are unchanged.
    • Applied the same change to Resources/reminderDialogPreferenceTest.zsh and Resources/createPlist.zsh. The preference test helper adds --battery-percentage <0-100> to preview the warning on a desktop or AC-powered Mac; production dor.zsh never simulates power state.
    • When assemble.zsh imports a prior .plist / .mobileconfig, it prints an ℹ️ line listing each imported template that lacks {batteryWarningMessage} (and adds it to Recommended review items); it never rewrites admin-authored copy.
    • The non-English BatteryWarningMessageLocalized_<code> strings are new and would benefit from native-speaker review.
  • Resources/diagnoseReminderState.zsh (1.4.0) knows MinimumBatteryPercentage (flagged when outside 0–100) and BatteryWarningMessage, so neither is reported as an unknown key, and it now lists the runtime Battery: and Disk space: lines under decision-related client-log lines.
  • Fixed: when the Finder-aligned disk-space query failed, the fallback [WARNING] (and any [ERROR] for unreadable disk data) was attempted before the logging helpers were defined, so it was never written to the client-side log. Both are now logged right after the Initiating … preamble.
  • Upgrade note: Deploy the updated dor.zsh before, or together with, any Configuration Profile that contains {batteryWarningMessage}; older scripts replace only the placeholders they know, so the dialog would show the literal text {batteryWarningMessage}. Customized Message, AggressiveModeMessage, PreDeadlineThresholdMessage, PastDeadlinePromptMessage, and PastDeadlineForceMessage strings (and their Localized_<code> variants) need {batteryWarningMessage} added by hand; without it, the warning never appears.

Version 5.2.0b2 (16-Oct-2026)

  • Client-side log entries now speak to Mac Admins: every run that shows no reminder ends with what happened, why no dialog appeared, and what to do next (Issue #154)
    • Normal DDM resolver result: status=… is now DDM update check: <plain-language outcome> (status=…; source=…; reason=…); status=, source=, and reason= tokens are unchanged for support and tooling.
    • Resolver suppression summary: … is replaced by No reminder shown: … followed by Next step: …, logged at the end of the run just before Exiting …. missing and up-to-date outcomes log at [NOTICE]; conflict, noMatch, invalidVersion, and unrecognized states keep [WARNING].
    • Before: Normal DDM resolver result: status=missing; source=none; reason=No DDM declaration candidates found in install.log … MDM fallback requirement is not configured; preserving normal DDM resolver status 'missing'. After: DDM update check: no DDM-enforced macOS update found (status=missing; …), DDM Emergency Fallback not configured (dor-fallback-declaration.plist absent); nothing to fall back to, No reminder shown: this Mac has no DDM-enforced macOS update pending (…), and Next step: If an update should be enforced, confirm this Mac is in scope of a DDM software-update declaration in your MDM, then search '/var/log/install.log' for 'EnforcedInstallDate'.
    • Each unresolved state names its own next step (scope to one declaration, correct the target version, confirm the version is offered in Software Update, or configure a DDM Emergency Fallback). reason= text drops internal terms such as "declaration candidates", and Resolver context: lines are now install.log context:.
    • conflict now says which kind: two different declarations, an available update that does not match the declared version, or softwareupdated found no update to enforce, yet DDM-enforced macOS <version> is still declared (previously Declaration persisted after 'No updates found for DDM to enforce'), each with a matching next step. When installed macOS already meets the declared version and the conflict is either softwareupdated finding nothing to enforce or softwareupdated offering a different (for example, newer major) update, the outcome is [NOTICE] No reminder shown: macOS <installed> already meets DDM-enforced macOS <version>; … with a Next step: None; …; suppression and status=conflict are unchanged. Two different declarations always stay [WARNING].
    • Up-to-date Macs now log No reminder shown: macOS <installed> already meets DDM-enforced macOS <version> with Next step: None; this Mac is up to date.
    • Display-window, periodic, Update Tonight, quiet-period, and presentation exits now log [QUIT] No reminder shown: … naming the controlling preference (DaysBeforeDeadlineDisplayReminder, OutsideDisplayWindowPeriodicReminderDays, QuietPeriodMinutes, MeetingDelay) plus when the reminder returns. Missing swiftDialog, non-root, no console user, and overlapping-run exits name a next step in the same [FATAL ERROR] line.
    • Demo mode now logs that it ignores DDM state and always shows a sample reminder, even on an up-to-date Mac.
    • Other everyday lines are shorter and plainer:
      • Installed macOS Version: … and Installed macOS Build: … are combined into Currently installed macOS <version> (<build>).
      • Current Logged-in User: … and Current Logged-in User First Name (ID): … are combined into Current Logged-in User: <user>, <first name>, <UID>; while waiting for a console user, Logged-in User Counter: names the current console user.
      • The three Resolved DDM declaration source/version/enforcement date lines and the raw Resolved declaration context: … install.log line (declaration key, details URL, and all) are replaced by [INFO] DDM declaration logged by softwareupdated at <timestamp> (<source>); version and declared deadline are on the DDM update check: line.
      • install.log context: lines drop the computer name and process ID.
      • Software Update handoff: requirementSource=…; normalResolverStatus=…; target=…; deadline=…; action=… is now Opening Software Update for <user> to install macOS <version> (deadline <effective deadline>; source=…; status=…).
      • Resolved DailyReminderTimes, Resolved MinutesBeforeDeadlineReminderSchedule, and the dialog-language decision now log at [PRE-FLIGHT]; the language decision is logged once per run unless it changes.
    • Runtime and deployment logs say "DDM Emergency Fallback" (matching the documentation) instead of "MDM fallback requirement". Rejected Parameters 5 and 6 log a Next step: with the expected format.
    • No changes to reminder timing, DDM trust rules, suppression decisions, preferences, or log format <scriptName> (<version>): <timestamp> - [<level>] <message>.
  • Resources/diagnoseReminderState.zsh (1.3.0) reads both DDM update check: and earlier Normal DDM resolver result: lines, both Currently installed macOS and earlier Installed macOS Version / Build lines, and both logged-in user formats (for redaction), so logs from older clients still diagnose
    • The Client-log runs outcome column now shows the run's No reminder shown: line or specific [QUIT] reason instead of the closing Ticking away … line every run ends with.
    • A generic Mac install.log computer name is no longer redacted, so plain-language lines such as "this Mac" stay readable.
  • MinimumDiskFreeGB sample default (Resources/sample.plist, generated .plist / .mobileconfig) raised from 26 to 35 after a fully staged macOS update was observed at Update Size: 26.60 GB. The script default (999), 0-disables behavior, and imported values from a prior .plist are unchanged.
  • Upgrade note: Scripts, Extension Attributes, or log searches keyed on Normal DDM resolver result, Resolver suppression summary, MDM fallback requirement, Version Comparison Result: No DDM enforcement log entry found, Installed macOS Version, Resolved DDM declaration, or Software Update handoff must be updated; key on DDM update check:, status=, No reminder shown:, Currently installed macOS, Opening Software Update, and DDM Emergency Fallback instead.

Version 5.2.0b1 (16-Oct-2026)

  • assemble.zsh now accepts a prior .mobileconfig as well as a prior .plist for prior-configuration import, so a hand-edited Configuration Profile can be re-assembled without the generated .plist drifting out of sync (Issue #153; thanks, @anewhouse!)
    • Works from the command line (zsh assemble.zsh /path/to/prior.mobileconfig) and the interactive drag-and-drop prompt; a .mobileconfig argument is no longer mistaken for an RDNN.
    • CMS-signed profiles are decoded with security cms -D before reading. No new dependencies: extraction uses PlistBuddy -x, so dotted preference domains work on macOS 14 and earlier.
    • PayloadContent is walked rather than assuming index 0. Supported layouts: a com.apple.ManagedClient.preferences payload with a *.dorm domain (Forced preferred; Set-Once or Often used with a ⚠️ notice only when Forced is absent), or a payload whose PayloadType is the *.dorm domain (for example, iMazing Profile Editor), with Payload* metadata keys stripped.
    • Exactly one DDM OS Reminder domain is required. Missing, multiple, or ambiguous payloads fail with a clear ❌ message (for example, ❌ Prior mobileconfig contains multiple DDM OS Reminder preferences payloads (…)), as do multiple Forced entries and files that are neither a profile nor a CMS envelope.
    • RDNN and deployment-lane inference use the original filename. New lane suffixes: -<lane>.mobileconfig, -<lane>-unsigned.mobileconfig, and -<lane>-signed.mobileconfig; near-miss filenames such as …-prod-5.1.0.mobileconfig print the existing warning.
    • Version metadata comes from the profile's PayloadDescription (Configures DDM OS Reminder (<version>) …), falling back to PayloadDisplayName (DDM OS Reminder (<version>), as written by Resources/createPlist.zsh); profiles built elsewhere import with the existing best-effort missing version metadata warning.
    • RDNN is still inferred from the imported ScriptLog; a ⚠️ warning now flags a profile whose *.dorm domain disagrees with that RDNN (for example, a createPlist.zsh profile whose ScriptLog still uses the placeholder RDNN).
    • Extracted settings live in a mktemp -d directory under $TMPDIR (never Artifacts/), removed by an EXIT trap, including on Ctrl-C.
    • Supplying more than one prior .plist or .mobileconfig positional argument now fails with ❌ Only one prior .plist or .mobileconfig may be supplied … instead of treating the extra path as an RDNN.
    • A prior .plist that is actually a Configuration Profile now adds a hint to rename it with a .mobileconfig extension. Otherwise, prior .plist import, messages, lane and RDNN inference, and generated output are unchanged.

Don't miss a new DDM-OS-Reminder release

NewReleases is sending notifications on new releases.