More racing stripes for Mac Admins' favorite "set-it-and-forget-it" macOS update reminder: reminders now wait out Maintenance DarkWake, re-check deadlines after reboot, and keep your branding when the network lags, thanks to cached icons
26-Oct-2026
- ๐ A reminder that came due while the Mac slept could launch during a Maintenance DarkWake, before the network was ready. Both icon downloads then timed out (about 20 seconds), both icons fell back to the Finder icon, and the dialog waited on a sleeping or locked display until the user returned (Issue #172; thanks, James Anzaldua!)
- DarkWake deferral:
dor-starter.zshno longer launchesdor.zshwhilepmset -g systemstatelistsCPUwithoutGraphics. It logs[NOTICE] DarkWake detected (system capabilities: โฆ); deferring reminder launch to the next heartbeat.and leavesdor-state.plistunchanged, includingLastBootReevaluation, so the due reminder runs on the first heartbeat after a full wake. Unrecognizedpmsetoutput fails open and launches as before. dor.zshrepeats the check just before it builds the dialog (after anyMeetingDelaywait). A DarkWake run logs[NOTICE] No reminder shown: the Mac is in a Maintenance DarkWake (system capabilities: โฆ)and, for starter-launched runs, schedules an exactNextScheduledReminder60 seconds later instead of the nextDailyReminderTimesslot. No mode bypasses this check, so pre-deadline threshold, aggressive, and Force reminders appear at the first full wake; a Force-mode timer can no longer count down to a restart on a display nobody can see. Demo mode is unchanged.- Icon downloads:
downloadBrandingAssetswaits up to 5 seconds forscutil -rto report the macOS icon host as reachable (if it never does, both downloads are skipped and the cache and fallbacks below apply), then downloads the overlay and macOS icons in parallel with--connect-timeout 4 --max-time 10 --retry 1. Only image content counts as success, so a captive-portal page is rejected. The curl error now appears in the[ERROR] Failed to download โฆline instead of on a separate raw line. - Icon cache: each successful download is copied to root-owned
/Library/Management/<rdnn>/dor-icon-macos-<major>.png(dor-icon-macos-default.pngwhen the major version is not numeric),dor-icon-overlay-light.png, ordor-icon-overlay-dark.png(not in demo mode). When a download fails, the cached copy is used.AllandScriptredeployments keep the cache;Uninstallremoves it. - Fallbacks: with no download and no cache, the macOS icon falls back to
/System/Library/CoreServices/Software Update.app(thenFinder.app), and the dialog shows no overlay icon instead of the Finder icon. An emptyOrganizationOverlayIconURLnow also shows no overlay icon.SwapOverlayAndLogokeeps the macOS icon as the primary icon when no overlay icon is available.Resources/reminderDialogPreferenceTest.zshpreviews the no-overlay andSwapOverlayAndLogofallbacks; it still hands the macOS icon URL to swiftDialog, so it does not preview theSoftware Update.appfallback. Resources/diagnoseReminderState.zsh(1.6.3) includesDarkWakelines in the latest-run decision lines and describes the starter's DarkWake exit. A post-boot run's latest-run lines now start at the starter'sStarter launch after bootline and includePost-boot re-evaluationlines, which earlier helper versions omitted. Checklist updated toreminderDialog.zsh5.3.0b4.- No changes to preference keys, defaults, or DDM trust rules.
- Not covered: a locked screen or a sleeping display while the Mac is otherwise fully awake. These still show the reminder, which the user sees on return.
- DarkWake deferral:
- ๐ Re-assembling from a prior dev or test
.plist/.mobileconfiginto another lane carried itsAssembledorTESTplaceholder text into the new artifacts, so a dev configuration promoted with--lane prodstill showed that text in the production.plistand.mobileconfig(Issue #168)assemble.zshnow rewrites its own leading lane marker (Assembled, legacySample, orTEST, optionally after leading<br>tags) in imported string values to match the selected lane: removed forprod,TESTfortest, andAssembledfordev.- Matching is case-sensitive and anchored to the start of each value, so admin copy such as
Latest,Testing, orsampleelsewhere in a string is never changed. - Logged as
๐งน Normalized lane marker in <n> imported value(s) for '<lane>' lane. - A prior file ending in
-dev.plistor-test.pliststill selects that lane automatically; pass--lane prodto promote it. - A valid
--lanenow skips filename lane inference regardless of argument order, so--lane prodafter the file path no longer logs๐ Inferred deployment mode from prior plist: 'dev'before๐ฆ Deployment Mode: prod. - No changes to preference keys, defaults, or runtime behavior.
- ๐ A missing or invalid
--lanevalue (for example,--lane production) printedDefaulting to interactive prompt., then re-parsed that value as a positional RDNN, soassemble.zshcould exit0with artifacts named for RDNNproduction.assemble.zshnow exits1withโ Invalid lane: '<value>'. Valid options: dev, test, prodbefore importing or generating anything. - Fixed: after a reboot, macOS can re-pad a past-due deadline to about one hour after login (
setPastDuePaddedEnforcementDate โฆ (usersLoggedIn=1)), butdor-starter.zshkept exiting quietly until the storedNextScheduledReminder, so the new deadline and its pre-deadline thresholds could pass with no reminder (Issue #165)- Once per boot, the first heartbeat that finds a console user other than
loginwindow,_mbsetupuser, orrootlaunchesdor.zsheven whenNextScheduledReminderis in the future. Heartbeats before login exit quietly, so the re-evaluation is not used up at the login window. - The starter records the boot in the new runtime-only
dor-state.plistkeyLastBootReevaluation(thekern.boottimeepoch) on any launch with a console user, and logs[NOTICE] Starter launch after boot; re-evaluating deadline (NextScheduledReminder: โฆ). - The post-boot run re-resolves the deadline, including the existing wait of up to 5 minutes for a refreshed padded date. It then shows a reminder only when a pre-deadline threshold, aggressive mode, or Force mode is due, or when the stored schedule has come due. Otherwise it moves
NextScheduledReminderearlier for a pending threshold or aggressive-mode start, never later, and logsNo reminder shown: post-boot re-evaluation found no reminder due. NextScheduledReminder=FALSE,dor.pidoverlap protection, manual runs, and demo runs are unchanged. No changes to preferences, defaults, or DDM trust rules.- Not covered: logging out and back in without a reboot.
Resources/diagnoseReminderState.zsh(1.6.1) reportsLastBootReevaluationand describes the post-boot exception in its starter rule. Checklist updated toreminderDialog.zsh5.3.0b2.
- Once per boot, the first heartbeat that finds a console user other than
- Fixed: past-deadline aggressive mode was never scheduled when the next
DailyReminderTimesslot came after the aggressive-mode start, leaving a Mac past its deadline without reminders until the next baseline slot (for example, overnight after a final pre-deadline threshold reminder at 16:25 for a 16:29 deadline) (Issue #164)- The aggressive-mode start (effective deadline plus
AggressiveModePastDeadlineHours) is now a scheduling candidate alongside pending pre-deadline thresholds and baseline slots. The earliest wins. - Applies after pre-deadline threshold reminders, after regular reminders shown between the deadline and the aggressive-mode start, and to quiet-period and Update Tonight scheduling, which aggressive mode already bypasses at runtime.
- Skipped while
/Library/Management/<rdnn>/dor-aggressive-killexists; the next run re-evaluates once support removes it. - Logged as
[NOTICE] Scheduled next daemon reminder for โฆ (โฆ: aggressive-mode start (<n> hour(s) past effective deadline))or(Quiet-period bypass for aggressive-mode start โฆ). - No changes to preferences, defaults, DDM trust rules, or suppression rules.
- The aggressive-mode start (effective deadline plus
- Fixed: a pre-deadline threshold reminder was recorded as delivered even when swiftDialog never showed it to the user (for example,
Return Code: 1while macOS was shutting down), so a reminder the user never saw used up that threshold for the deadline- Only user-interaction return codes (
0,2,3,4,10, the same codes that start the quiet period) and the open-dialog refresh now add a threshold toPreDeadlineThresholdDelivered. - Any other return code logs
[NOTICE] Pre-deadline threshold <n>-minute reminder not marked delivered; return code <code> is not a user interaction.The next run shows that threshold if it is still the latest one due, or records it inPreDeadlineThresholdSkippedonce a later threshold is due.
- Only user-interaction return codes (
- Fixed: when a Mac woke from sleep after the effective deadline with a threshold reminder still open, the open-dialog check closed that reminder and nothing replaced it until the next scheduled run
- Once the effective deadline has passed, the open reminder now stays on screen, logged as
[NOTICE] Pre-deadline threshold <n>-minute reminder became due while a reminder dialog was open, but the effective deadline has passed; leaving the open dialog in place. - Before the deadline, a newly due threshold still closes the open dialog and shows the newer threshold reminder.
- Once the effective deadline has passed, the open reminder now stays on screen, logged as
Resources/diagnoseReminderState.zsh(1.5.0) adds--rdnn <rdnn>, so a Mac Admin running the helper on the affected Mac no longer types up to nine paths (Issue #166)- Each input that is not supplied is read from its live path: the effective
ScriptLog(Managed, then Local) or/var/log/<rdnn>.log,/var/log/install.log, both<rdnn>.dorm.plistpreference layers,dor-state.plist,dor-fallback-declaration.plist,SoftwareUpdateDDMStatePersistence.plist, the LaunchDaemon plist, and whetherdor-aggressive-killexists. - Explicit flags always win. An invalid RDNN exits
64; a missing client log orinstall.logexits66and names the path. The supplied RDNN joins RDNN detection, so a mismatch is still reported as aconflict. - Missing optional files are reported as
not present on this Mac(orunreadable on this Mac). A Mac with neither preference plist is reported with script defaults, which matches the runtime. - Fact-sheet and report-skeleton file statuses now read
supplied(wasreceived),not in upload,inferred (--rdnn),not present on this Mac, orunreadable on this Mac. - The fact sheet's Declaration candidates table now groups identical
install.logcandidates into one row with first seen, last seen, and count, so a fullinstall.logno longer produces hundreds of repeated rows. The fact-sheet title links to snelson.us/ddm, and the report's redaction sentence says "raw client files" instead of "raw uploads". install.logmarker headings and report bullets now quote the log text they match (for example,`No updates found for DDM to enforce` lines: none), so an empty section reads as "this log line was not found" rather than as a verdict.- Report section 7 (Do the client log and
install.logagree?) now opens with a helper table that puts the client log's lastDDM update check:result (version, declared deadline, status, source) beside the latestinstall.logdeclaration span. Pre-5.2.0logs use theirResolved DDM declarationlines. The agent still writes the Yes. / No. / Not comparable. answer. - Report section 8 (Likely cause and next steps) now opens with recorded evidence from the last run: its outcome, the runtime's own
No reminder shown:andNext step:text, the latest user action, the latest scheduling line, andNextScheduledReminder. The agent still writes the cause and next steps. - Non-root runs add a note that root-only files may be unreadable; run with
sudo. The helper stays read-only, the RDNN is never auto-discovered, and redaction is unchanged. - Checklist updated to
reminderDialog.zsh5.3.0b1; preference defaults are unchanged.
- Each input that is not supplied is read from its live path: the effective
Resources/diagnoseReminderState.zsh(1.6.0) adds opt-in--no-redaction, so a Mac Admin diagnosing several Macs can tell the reports apart and match them to MDM records- Usernames, first names,
/Userspaths,install.logcomputer names, and serial numbers appear as logged. The fact-sheet header and report section 9 say the output is not redacted. - The fact-sheet and report-skeleton identity tables add Computer name and Serial number rows: from this Mac (
scutil --get ComputerName,IOPlatformSerialNumber) with--rdnn, otherwise from the latestinstall.logcomputer name and a labeled client-logSerial Number:line. - The suggested report filename adds the computer name and
-unredacted, for exampleddm-os-reminder-us.snelson-Mac-Lab-07-unredacted-2026-10-21-161500.md. - With
--rdnn, the full output is also saved to/Library/Management/<rdnn>/โฆ-unredacted-<timestamp>-fact-sheet.md(mode0600). The helper never creates that directory; a missing, symbolic-link, non-root-owned, or unwritable directory still prints the fact sheet and exits73. Without--rdnn, nothing is saved. - Default runs are unchanged: redacted, and they write nothing.
- Usernames, first names,
- Fixed:
Resources/createSelfExtracting.zsh(2.4.1) with noddm-os-reminder-*.zshinArtifacts/ran a barels -t, so it could encode an unrelated file from the current directory instead of stopping. It now exits withโ Error: No file matching 'ddm-os-reminder-*.zsh' found in โฆ. Generated wrappers are unchanged.