29-Mar-2026
A major update to Mac Admins’ favorite MDM-agnostic, “set-it-and-forget-it” reminder now adds multiple language support, significantly more robust reminder display logic and streamlined upgrade functionality
🆕 DDM OS Reminder now resolves DDM-enforced macOS update deadlines from recent /var/log/install.log activity using a declaration-aware resolver that prioritizes applicable enforced-install signals over generic matches, suppressing reminders when declaration state is missing, conflicting, invalid, or no longer maps to an available update, and only honors setPastDuePaddedEnforcementDate when it safely matches the resolved declaration, before using a swiftDialog-enabled script and LaunchDaemon to deliver a more prominent end-user reminder dialog.
🆕 Upgrade-friendly: assemble.zsh can now import supported settings from a previously generated DDM OS Reminder .plist, infer the RDNN and deployment lane (dev, test, prod), and generate a matched assembled script, organizational .plist, and unsigned .mobileconfig in a single pass.
🆕 Full Multi-language Experience: Version 3.0.0 fully supports English, German, French, Spanish, Portuguese, and Japanese across the reminder experience, with localized dialog content, support messaging, and human-readable deadline dates that automatically match the resolved language for a more polished, native-feeling user experience.
- Hardened
reminderDialog.zshDDM resolution by replacing the oldEnforcedInstallDate | tail -n 1heuristic with a recent-window resolver that:- prioritizes
default applicable declarationandFound DDM enforced installover genericEnforcedInstallDatematches - suppresses the reminder when declaration state is missing, conflicting, invalid, or no longer maps to an available update
- only accepts
setPastDuePaddedEnforcementDatewhen it safely matches the resolved declaration - adds explicit suppression logging for
conflict,noMatch, and invalid-version cases
- prioritizes
- Added optional prior-plist import to
assemble.zsh, allowing Mac Admins to reuse supported values from an earlier DOR.plistwhile still generating current-version artifacts from the current sample- Imported values are read with
PlistBuddyagainst the current runtime preference-key map so older plist formatting differences do not affect import behavior - The documented upgrade-assist path is based on plists generated by DDM OS Reminder
2.2.0or later; earlier DOR plists now warn and continue on a best-effort basis - Missing newer keys now remain at current defaults instead of being dropped during upgrade reuse
- Passing a prior
.plistpath directly tozsh assemble.zshnow auto-enables the import flow and infers the RDNN and deployment lane from that plist when its filename is unambiguous, reducing the need for extra CLI flags during upgrades - Older supported plists without a
-dev,-test, or-prodfilename suffix still import, but continue to prompt for deployment mode because the prior lane cannot be inferred ScriptLogis preserved only when the imported basename matches the current RDNN; otherwise it is rewritten to the current assembly path to avoid domain leakage from older/internal plists- Assembled-script
scriptLogupdates now follow the same resolved path as the generated plist so operator messaging and deployed runtime behavior stay aligned
- Imported values are read with
- Updated free-disk-space reporting in
reminderDialog.zshto prefer Finder-aligned available capacity viaNSURLVolumeAvailableCapacityForImportantUsageKey, improving visibility of purgeable space such as local Time Machine snapshots and iCloud-managed capacity. (Pull Request #80; thanks, @huexley!)- Added sanity checks and automatic fallback to the previous
diskutilpath when the JXA/Foundation disk-space query returns invalid data, preserving safe reminder behavior on affected systems. - Retained percentage-based warning behavior while updating the human-readable free-space display to use decimal
GBformatting that better aligns with macOS/Finder conventions.
- Added sanity checks and automatic fallback to the previous
- Consolidated localization coverage across runtime and config generation, including FR-25 parity plus ES/PT/JA support (
LanguageOverride, localized key families, localized infobox labels, and restart/deadline/support-assistance copy paths). - Expanded locale-aware deadline date rendering so
%a/%binDateFormatDeadlineHumanReadablefollow the resolved dialog language (de,fr,es,pt,ja, fallbacken) across standard, padded past-due, and demo-mode flows. - Added an internal
installLogPathOverridefixture-testing hook for local validation ofreminderDialog.zshand the bundled Jamf EAs. - Updated
Resources/JamfEA-Pending_OS_Update_Date.zshandResources/JamfEA-Pending_OS_Update_Version.zshto use the same fail-closed trust model as the runtime resolver, while keeping Jamf inventory execution lightweight. - Updated
Resources/README.md,Diagrams/, and related diagram PNG exports to document the hardened resolver, fail-closed EA behavior, correcteddorm.zshclient-script paths, and current beta-series behavior. - Updated
Resources/createPlist.zshandResources/sample.plistfor restart-policy plus localization-key parity, then regenerated release artifacts from merged source. - Merged
main(2.6.0) into3.0.0while preserving2.6.0runtime behavior, including post-deadline restart workflow, KB support-assistance controls, and deadline/infobox urgency highlighting.
Multiple Language Support
Import prior-plist via drag-and-drop to assemble.zsh
Allows Mac Admins to reuse supported values from an earlier DOR
.plist(2.2.0 or later) while still generating current-version artifacts from the current sample
❯ zsh assemble.zsh '/Users/dan/Downloads/DDM-OS-Reminder-2.2.0/Artifacts/us.snelson.dorm-2026-01-06-073608.plist'
===============================================================
🧩 Assemble DDM OS Reminder (3.0.0)
===============================================================
Full Paths:
Reminder Dialog: DDM-OS-Reminder/reminderDialog.zsh
LaunchDaemon Management: DDM-OS-Reminder/launchDaemonManagement.zsh
Working Directory: DDM-OS-Reminder
Resources Directory: DDM-OS-Reminder/Resources
🔍 Checking Reverse Domain Name Notation …
Reminder Dialog (reminderDialog.zsh):
reverseDomainNameNotation = org.churchofjesuschrist
organizationScriptName = dorm
LaunchDaemon Management (launchDaemonManagement.zsh):
reverseDomainNameNotation = org.churchofjesuschrist
organizationScriptName = dor
📥 Prior plist provided via command-line argument: '/Users/dan/Downloads/DDM-OS-Reminder-2.2.0/Artifacts/us.snelson.dorm-2026-01-06-073608.plist'
ℹ️ Importing supported values from: /Users/dan/Downloads/DDM-OS-Reminder-2.2.0/Artifacts/us.snelson.dorm-2026-01-06-073608.plist
🔎 Inferred RDNN from prior plist: 'us.snelson'
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Using 'us.snelson' as the Reverse Domain Name Notation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Interactive Configuration
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ℹ️ Prior plist supplied; skipping IT support, branding and restart policy prompts.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Select Deployment Mode:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1) Development - Keep placeholder text for local testing
2) Testing - Replace placeholder text with 'TEST' for staging
3) Production - Remove placeholder text for clean deployment
[Press ‘X’ to exit]
Enter mode [1/2/3]: 3
📦 Deployment Mode: prod
🔧 Inserting reminderDialog.zsh into launchDaemonManagement.zsh …
✅ Assembly complete [2026-03-28-151200]
→ Artifacts/ddm-os-reminder-assembled-2026-03-28-151200.zsh
🔁 Updating reverseDomainNameNotation to 'us.snelson' in assembled script …
🔍 Performing syntax check on 'Artifacts/ddm-os-reminder-assembled-2026-03-28-151200.zsh' …
✅ Syntax check passed.
🗂 Generating LaunchDaemon plist …
🗂 Creating us.snelson.dorm plist from /Users/dan/Documents/GitHub/dan-snelson/DDM-OS-Reminder/Resources/sample.plist …
🔧 Updating internal plist content …
🔓 Production mode: removing placeholder text for clean deployment
🔧 Importing supported values from prior plist …
ℹ️ Preserving imported ScriptLog: /var/log/us.snelson.log
→ Artifacts/us.snelson.dorm-2026-03-28-151200-prod.plist
🧩 Generating Configuration Profile (.mobileconfig) …
→ Artifacts/us.snelson.dorm-2026-03-28-151200-prod-unsigned.mobileconfig
🔍 Performing syntax check on 'Artifacts/us.snelson.dorm-2026-03-28-151200-prod-unsigned.mobileconfig' …
✅ Profile syntax check passed.
🔁 Renaming assembled script …
🔁 Updating scriptLog path based on RDNN …
🏁 Done.
Deployment Artifacts:
Assembled Script: Artifacts/ddm-os-reminder-us.snelson-2026-03-28-151200-prod.zsh
Organizational Plist: Artifacts/us.snelson.dorm-2026-03-28-151200-prod.plist
Configuration Profile: Artifacts/us.snelson.dorm-2026-03-28-151200-prod-unsigned.mobileconfig
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️ Important Next Steps:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Production Artifacts Generated:
- All placeholder text removed (clean output)
- Supported configuration values imported from prior plist
- Prior plist: /Users/dan/Downloads/DDM-OS-Reminder-2.2.0/Artifacts/us.snelson.dorm-2026-01-06-073608.plist
- ScriptLog resolved to '/var/log/us.snelson.log'
Recommended review items:
- Support team name, phone, email, website
- Imported ScriptLog path and any carried-forward KB/help visibility
- Organization overlay icon URLs
- Button labels and dialog messages
Files to review:
- Artifacts/us.snelson.dorm-2026-03-28-151200-prod.plist
- Artifacts/us.snelson.dorm-2026-03-28-151200-prod-unsigned.mobileconfig
===============================================================
What's Changed
- 2.4.0 by @dan-snelson in #71
- 2.5.0 by @dan-snelson in #73
- 2.6.0 by @dan-snelson in #79
- Merge FR 81 into 3.0.0 by @dan-snelson in #83
- FR 81 by @dan-snelson in #82
Full Changelog: v2.6.0...v3.0.0