Changelog

Every release, newest first — the same notes the app shows you when it updates itself. Downloads are on the releases page.

The new app icon now looks right on macOS 26. In dark mode the arrow and the ring no longer run together into solid blobs, and the cat's head and ears have clean edges instead of jagged ones.

Closing the Workbench now frees the memory its release notes were using. Before, a release notes page you had opened kept running in the background until you quit DuoUpdater.

A new app icon. A Siamese cat curled up, with its tail as the refresh arrow. On macOS 26 and later it follows your light, dark or tinted icon style.

Background checks wait while you're in Low Data Mode or on a metered connection, such as an iPhone hotspot. They run as soon as you're back on a regular network. Checking by hand and installing updates work as before.

Checks that fail because of a network hiccup are retried once, two minutes later. Before, the failed rows and the warning stayed until the next scheduled check, six hours later by default.

Under the hood. Release-note images take up much less disk space, the Requests pane no longer counts a revalidated request twice, and checking Muse no longer downloads its sign-in page.

Homebrew apps that install through their own setup script, such as Quark Cloud Drive, now update with one click. Before, every attempt ended with "did not contain an installer package".

Homebrew apps whose installer needs you to run it now show their update with a link, instead of failing. Before, DuoUpdater downloaded the whole installer and then stopped with an error.

More Homebrew apps are recognised. Apps that Homebrew installs under a different name, like Visual Paradigm Community Edition, are now checked for updates, and a similarly named app is no longer mistaken for them.

Apps whose Homebrew cask has been withdrawn are no longer offered an update Homebrew refuses to install. They're checked through the app's own update feed instead, where it has one.

VS Code release notes show up again.

Under the hood. Settings › Diagnostics no longer keeps warning about Antigravity and OpenLens once their updates are found another way.

Blender downloaded from blender.org now gets updates, with one click. Before, only a copy installed with Homebrew was checked.

Blender alpha, beta and release-candidate builds follow their own track. DuoUpdater reads which kind of build you have and tells you when a newer build of that same kind is out. An alpha is never offered a stable release, or the other way round.

Muse updates show up again. After Muse started asking for a sign-in on its download link, its row showed an error instead of the new version.

Under the hood. The menu bar popover and the Workbench open a little faster the first time after launch, and duo check now lists apps whose check failed instead of saying everything is up to date.

DuoUpdater now speaks Italian, Traditional Chinese, Portuguese (Brazil) and Turkish. That makes eleven languages. In German, Spanish and Japanese, wording that was wrong or cut off has been fixed. Settings → Folders now shows folder names the way Finder does in your language.

Apps you installed with Homebrew are now updated through Homebrew. This covers brew-installed apps that also have their own updater. Before, DuoUpdater updated them in place and Homebrew didn't notice, so the next brew upgrade downloaded and reinstalled the version you already had.

Windows open on the Space you're on. Before, reopening Settings or another DuoUpdater window after closing it could switch you back to the Space where it was last shown.

One update, one notification. Some apps are checked through two sources, and those could announce the same new version again and again.

The Release Log no longer shows releases dated in the future. If a developer's feed gives a date later than when DuoUpdater first saw the release, the log uses the time it was seen.

With the backup disk unplugged, the Workbench window fits on the screen again, and Copy Now tells you why it can't copy. Before, the window could grow taller than the screen so lists couldn't scroll to the end, and Copy Now silently did nothing.

Xcode betas and release candidates now update with one click. Sign in to your Apple Developer account once in Settings → Xcode, and DuoUpdater downloads and installs new betas and RCs like any other update. Before, it could only tell you a new one was out.

Install any Xcode version beside the ones you have. Settings → Xcode lists every Xcode release, grouped by version, with the group for your macOS already open. Install puts the one you pick in Applications as its own copy (for example Xcode-26.6.app) and replaces nothing. You can also save just the archive (.xip). When you're signed in, Apple's own list is included, so new releases appear within minutes.

Your Apple Developer sign-in keeps itself going. Apple ends a developer session after about eight hours. DuoUpdater now gets a new one in the background, with no window and no password, as long as Apple still recognizes this Mac. You can turn this off in Settings → Xcode.

New Xcode releases show up sooner. On weekdays during Apple's usual release hours, DuoUpdater looks for a new Xcode every five minutes.

A few apps work again after their developers changed things. CapCut beta and Superconductor nightly are found and installed again, and Superconductor keeps its current app identity. VLC 3.0.24 and JetBrains Air show their release notes again.

Qoder CN IDE is now supported. The mainland-China edition of Qoder's IDE gets update checks, a one-click install and its release notes in the window. Qoder CN, the desktop app, now shows its release notes too.

Qoder IDE no longer changes its mind about the latest version. Qoder rolls updates out gradually, and DuoUpdater now asks the same way your copy of the IDE does, so it gets the same answer every time. Before, the offered version could flip between two releases, and a one-click install could stop halfway because the answer changed.

Sparkle apps that downloaded an update that is now out of date can be updated again. If an app that updates itself through Sparkle fetched a build and a newer one came out later, DuoUpdater now clears the old download and installs the latest. Before, Update kept saying that installing now would be undone, and the row never changed.

duo install tells you when the latest build is already waiting. If an app's own updater has already downloaded the latest version, it now says so and suggests quitting the app or running duo restart. Before, it wrongly said the app could only be checked, not installed.

搜狗输入法 now updates with one click. DuoUpdater applies it the way the input method updates itself — keeping the installed bundle in place — and takes a copy of your dictionary and settings first, so a rollback brings them back too.

Backups on another disk now keep an input method's dictionary and settings. Before, only the app itself was moved to the backup disk, so rolling an input method back from there left the newer version's data behind.

No more updates that can never be installed. JetBrains Air now shows the build Toolbox actually offers. An App Store app left behind under its old name, after its developer renamed it, now tells you which copy the App Store updates, instead of offering an update that only reinstalls the other copy.

More apps' release notes show up formatted. Apps that publish their notes as Markdown now get the same native list as everyone else. Before, the notes showed up as plain text.

Relaunch no longer competes with an app's own installer. When an app has downloaded its own update and its rollback backup belongs on an external disk, that copy now waits until the installer is finished. Before, it ran during the few seconds macOS gives the installer to do its work.

Rollback backups can now live on another disk. A backup is a whole copy of an app, and on the boot volume they add up. Settings → Backups lists the connected disks with the space each one has, moves the backups you already have, and writes new ones there in the background — unplug the disk and you get a delay, not a missing rollback point. duo backups gained disks, sync, verify and probe.

千问输入法 is now supported. DuoUpdater tells you when a new version is out, and updates it the way the input method updates itself.

Release notes no longer stay stuck on the previous version. When a developer publishes an update a few minutes before their changelog page mentions it, the older notes were filed under the new version and kept forever. CleanShot X had been showing 5.0's notes under 5.0.1 since the day it shipped.

Deleting a backup now frees the space it says it freed. Backups taken by older versions of DuoUpdater were written in a form macOS refuses to delete, so Clean Up left most of the copy on disk while counting it as reclaimed.

Clean Up opens right away. It used to measure every stored backup from scratch each time you pressed it.

WorkBuddy shows its newest version again. Its update service answers with the next step of an upgrade chain rather than the newest build, so all four WorkBuddy editions sat two releases behind.

Under the hood. An app that never declared which binary it runs — Meta's Muse among them — now has its frameworks and SDK read anyway.

Updates finished by Relaunch can now be rolled back. When an app has already downloaded its own update and you press Relaunch, DuoUpdater now saves the current version first, so the update shows up under Rollback like any other. Before, only updates DuoUpdater installed itself could be undone.

See what an update changed inside an app. For any app with a saved previous version, the workbench now has a Bundle Diff view next to Release Notes: signing and permissions, minimum macOS, background and login items, bundled libraries, and the files that were added, removed or resized. duo diff does the same from Terminal for any two copies of an app.

Relaunch tells you when an app's own updater didn't apply the update. If the app closes and the update never lands, the row now says so and still offers Relaunch. Before, the spinner stopped without a word.

Cherry Studio shows its newest version again. After Cherry Studio renamed its Mac downloads, DuoUpdater treated an older release as the latest, so recent copies read "up to date" and older ones were offered that older build.

App Store updates no longer stall behind an unresponsive helper. When DuoUpdater's helper stopped answering, the update sat at 0% and every App Store update queued behind it waited until you quit DuoUpdater.

Update All no longer relaunches apps you've ignored. With "Relaunch updated apps automatically" on, the end of a batch could quit and reopen an ignored app, or one whose downloaded version you had skipped.

Tinycast and SuperCmd are now supported. DuoUpdater tells you when a new version of Tinycast, or its beta, is out, and now recognizes the original open-source SuperCmd alongside SuperCmd 2. Kimi Code's release notes now show up too.

Release notes match the version on offer. The latest VS Code release notes page came up empty, and right after a new version appeared, the notes pane could show the previous release instead.

The Release Log no longer adds the same release again on every launch. Apps such as Claude, Raycast and Cline gained a duplicate entry each time DuoUpdater started.

The workbench sidebar is split into Apps, Brew and Rollback tabs. Click a tab or drag across them to switch; search narrows all three at once.

Under the hood. Relaunch and Roll back on the same app no longer run over each other; an update feed can no longer make DuoUpdater delete files outside its download folder; background checks no longer risk macOS asking for access to other apps' data; a very large number in the Requests filter no longer quits the app; and the menu bar now respects an administrator prompt you declined in duo install.

iStat Menus updates are detected correctly. When iStat Menus re-released an update, DuoUpdater could keep offering it after it was installed, and pressing Update again ended in an error.

Memoh Desktop is now supported. DuoUpdater tells you when a new version is out and can update it in one click.

Update All no longer asks you to relaunch an App Store app that has already reopened. When the App Store closes an app and reopens it on its new version, its row now shows it as updated right away, instead of saying "Relaunch now" until the whole batch finished.

An app that has already downloaded its own update no longer gets it downloaded twice. Some apps fetch an update quietly in the background and then wait for you to restart them. Press Update in DuoUpdater and it now installs the copy already sitting on your disk: the update lands in seconds and costs nothing to download. Before, DuoUpdater fetched the same release all over again.

Cua Driver and Petex are now supported. DuoUpdater tells you when either one has a new release, and Cua Driver also updates with one click and shows its release notes.

The same update no longer notifies you over and over. When a developer's server kept switching between two versions, every check looked like a new update and posted another notification.

A package that has just installed no longer offers to install again. Its row used to keep saying the downloaded package would be re-opened.

DuoUpdater now requires macOS 15 Sequoia or later. A Mac still on macOS 14 keeps the version it has and is no longer offered updates; every Apple Silicon Mac can upgrade to a supported macOS.

Under the hood. Routine checks no longer look up download links ahead of time, which could briefly hide a working Update button whenever a developer's download server timed out.

Aside is now supported. DuoUpdater tells you when a new build of the Aside browser is out and shows its release notes.

Updates your macOS can't run are no longer offered, and the row now says why. When a developer states which macOS versions a release supports, DuoUpdater follows it: after a macOS upgrade, Little Snitch no longer offers a build capped below your system; Xcode no longer offers a build that needs a newer macOS than yours; and an installer package whose app needs a newer macOS is refused instead of installing an app that won't open. Rows that used to show a blank dash now say "Not for this macOS yet" or "Needs a newer macOS", with the details a click away.

See which SDK each app was built with. Click the mark beside an app's name and its details now say, for example, "Built with the macOS 27.0 SDK." — handy for telling which apps have been rebuilt for the latest macOS.

Homebrew apps that ship as two packages — one for older systems, one for the newest — are now read from the right one. OnyX splits that way for macOS 27, and DuoUpdater looked only at the package meant for older systems: it either showed nothing for OnyX at all, or called it up to date against a version Homebrew refuses to install on 27. Whichever of the two you installed is now the one DuoUpdater follows.

TestFlight betas no longer lose their update when TestFlight starts. A beta with a new build waiting could fall back to "up to date" and stay that way until you opened TestFlight again.

One-click updates for Dropbox and ToDesk fetch the right download. On Apple silicon, Dropbox used to download the Intel-only build, which the safety check then refused; ToDesk was offered the early-access build it rolls out to only some users, instead of its general release.

Relaunch finishes Spotify's update right away. When Spotify had already downloaded its own update, Relaunch used to spin for minutes before the update went through.

Beta builds move on to their releases. An Xcode beta is offered its release candidate instead of showing as up to date, CotEditor betas see its release candidates, and a Carbon Copy Cloner beta whose testing cycle has ended is offered the release it became, instead of a failed check.

Release notes match the version you have. Blender 5.2 used to show the notes for 5.1, and Raycast 1.x showed the notes for 2.x. Gemini's "Open page" now opens Google's current desktop page instead of an error.

Settings explain each option right next to it. Long notes under whole sections are replaced by a short line under each control, and pickers describe only the option you've picked.

The app now spells its name DuoUpdater, matching the website.

Homebrew packages from taps you haven't trusted show up again. Since Homebrew 6, brew quietly skips packages from untrusted taps when it lists what's installed, so they disappeared from DuoUpdater with no explanation. They're now listed as "Not checked · tap not trusted", with the brew trust command ready to copy — and once you run it (or an upgrade) in Terminal, the window catches up as soon as you switch back.

Apps that need an administrator password to update themselves are no longer updated twice. When an app like Tailscale had already downloaded its own update and was waiting for a restart, DuoUpdater couldn't see it and still offered Update, which could collide with the app's own installer. The row now offers Relaunch instead, and Update All leaves it alone.

The Homebrew section can now update Homebrew itself. When a new Homebrew release is out, a row at the top offers to run brew update for you. It stays hidden if you've set HOMEBREW_NO_AUTO_UPDATE.

A failed Homebrew upgrade now shows brew's actual error. The row used to show the advice brew prints after the error, or nothing at all, so a message like "your Command Line Tools are too outdated" never reached you. Upgrading several packages at once also no longer reports finishing before it has.

The menu bar icon counts past 50. With more than 50 updates waiting, it used to stay at 50.

Release notes: Claude's are grouped into New, Improved and Fixed, the way Claude itself shows them; Mac Mouse Fix's appear as text instead of an embedded page; and Homebrew's own app now has notes.

duo check no longer says "Everything is up to date." when it couldn't actually check. It now says so when it couldn't read TestFlight or when the app scan was abandoned.

Release notes now show up in your own language when an app publishes them in several. Some apps ship their notes translated alongside each release; DuoUpdater used to take whichever translation the app happened to list first or last, so one app's notes read in German for everyone and another's changed language from one release to the next.

Apps that added a Mac version no longer show as "not supported on this Mac." An iPhone or iPad app you run on Apple silicon was mistakenly flagged the moment its developer shipped a real Mac build — the one change that makes the update more available, not less.

An App Store app you also beta-test is no longer mistaken for a TestFlight build. When a developer promoted a beta unchanged, the two carried the same build number and your purchased copy was handed to TestFlight — so the App Store could never offer it an update.

An App Store update that needs a newer macOS than you're running now says so. Before, the row offered to install it anyway and the App Store refused at the last step, with nothing on screen explaining why.

The Requests window now shows how far back its log actually reaches, and marks date ranges it can't fully cover. Before, picking "Last 30 days" on a log that only went back a few hours looked exactly like picking "Last 24 hours," with nothing on screen explaining why.

You choose how much DuoUpdater does about TestFlight betas, in Settings → General. When I refresh reads what TestFlight already knows and asks it for a fresh answer when you press Refresh; Keep it fresh also lets DuoUpdater ask on its own, so a beta TestFlight installed in the background no longer sits as a question mark until you refresh, and a build waiting for you no longer goes unnoticed behind an "up to date"; Off reads nothing and says so on those rows instead of guessing. Macs that already had Full Disk Access start at When I refresh, everyone else at Off.

Opening TestFlight yourself is now enough for DuoUpdater to notice what it installed. A beta you installed through TestFlight used to sit as a question mark until the next refresh — and on macOS 27, where TestFlight's "Ready to Test" notice no longer arrives for apps you already have, that could be hours.

Cline now gets updates, on both its release and beta builds, and shows its release notes. Until now it sat with a question mark instead of a version — it ships no update feed of the kind DuoUpdater could read, and there is no Homebrew package for it. DuoUpdater now asks the same address Cline's own updater asks, so the update offered is the one Cline would have installed itself, and the beta build stays on the beta track.

Release notes that group changes under headings like Added and Fixed now keep those headings. Before, every group was merged into one flat list, so you couldn't tell which changes were new features and which were bug fixes.

Mac Mouse Fix now offers its beta releases if you've turned on "Get Beta Versions" in its own General settings. Before, DuoUpdater could only see Mac Mouse Fix's regular releases, so a beta build sat unnoticed until the next regular version shipped.

Apps whose build number is a plain counter no longer hide their own patch releases. For an app reporting a version like 12.10 with build 282987, a 12.10.1 release used to read as "already up to date".

A new build of an app that keeps the same version name is announced again. Once one build had been announced, every later build under that name arrived silently — the row lit up, the badge counted it, but no banner ever came.

"Update All" now counts only apps that were actually updated. An app that opens Apple's Installer for you to finish used to be counted as done while its window was still open, so "2 apps were updated" could mean nothing had changed yet.

An update that landed but left a leftover behind is now reported as installed, not as "grant App Management." The new version was already running while the row sent you to System Settings.

Stopping "Update All" now stops the download in progress. Before, a multi-gigabyte transfer kept going to the end, retrying up to five times, and only then noticed it had been cancelled.

Release notes are no longer mixed up between two apps that share one changelog page, and stay current after an update for apps whose notes live on per-version pages. Antigravity and Antigravity IDE could show each other's notes for a quarter of an hour; Thunderbird, WeChat, Opera and a few others kept showing the previous version's notes for a while after updating.

Searching the app list ignores accents, as the Settings search already did. Typing "cafe" now finds "Café".

The Diagnostics page lists a health line per release channel. A broken beta or preview rule used to be hidden behind its healthy stable sibling.

A GitHub "forbidden" answer is no longer reported as a rate limit. A repository that went private or a token missing a scope used to nudge you toward adding a token that would not have helped.

Relaunch is no longer offered for a self-updating app whose waiting build is older than the one running.

Under the hood. Installs, backups and package checks no longer tie up the threads the rest of the app runs on, so the menu stays responsive while one is in progress; the menu also stays smooth while a large download is in progress; the Release Log counts every release a vendor ships under one version name; a rollback backup is refused rather than stored when it would be missing the app's own executable; the first launch on a fresh Mac no longer logs spurious database errors; duo verify and duo reconcile now report a changelog whose entries collapsed and an installer address that has been failing for days; a stalled duo command gives up on its scan after twenty seconds instead of hanging.

Some self-updating apps no longer look up to date while a newer version is out. For apps whose update information sits behind a slow-to-refresh download server, DuoUpdater could keep seeing an older version for days after a release.

Kimi's release notes now show up in DuoUpdater.

CodeEdit updates now show up. A newer CodeEdit used to leave its row as a question mark instead of offering the update.

Under the hood. The Homebrew list in the menu fills in faster, and duo commands start faster.

Check Again on a TestFlight beta now gives its real answer. It used to turn the row into a question mark until the next refresh.

TestFlight betas keep their answers while the refresh button checks with TestFlight. For a few seconds they could all turn into question marks.

super.engineering is now supported: update checks, release notes and one-click install. A new nightly shows up with what changed in it, and Update installs it for you.

Microsoft Edge Beta could offer you a stable Edge build. While Microsoft's beta feed was briefly empty, the row named a stable version, and updating would have put it over your beta.

TestFlight betas now show the updates TestFlight has for them, including betas of iPhone and iPad apps. The refresh button checks with TestFlight in the background, an update no longer vanishes minutes after you find it, and the TestFlight button opens that beta's page.

When DuoUpdater can't tell whether a TestFlight beta is current, it says so. The row shows a question mark instead of calling it up to date — for example when you're signed out of TestFlight or no longer testing that beta.

Full Disk Access is now explained, and nothing nags you without it. Without it, DuoUpdater no longer sets off macOS's warnings about reading other apps' data; if a TestFlight beta or CotEditor needs it, you're told once why and where to grant it.

Relaunch goes away once an updated app has quit. An app that left a helper running kept asking to be relaunched long after its update had taken effect.

An unanswered macOS privacy prompt no longer stalls update checks. Checking carries on without that app's setting.

Release notes for Rockxy and Ollama are complete again. Rockxy's go back through its recent releases instead of only the newest, and Ollama's newest release is no longer left out.

duo, the optional command-line companion, handles TestFlight better. --refresh-testflight works whether or not TestFlight is open, without taking your screen, and duo check no longer calls a beta up to date when TestFlight has announced a newer build.

Telegram Desktop gets update checks again. Telegram changed the name of the file it publishes, and the row could no longer read a version out of it — so it showed a check failure instead of the update waiting behind it.

A TestFlight build of an iPhone or iPad app is recognized as one. DuoUpdater read it as an App Store purchase instead, so the row named the wrong keeper while the store was asked about a listing that does not exist — on every check, for as long as the app stayed installed.

A TestFlight beta is marked with TestFlight's own icon. Rows the App Store looks after already carried the store's icon; the ones TestFlight looks after spelled the name out instead, so the same kind of row was marked two different ways.

The Network window's header stays put when you switch tabs. Its two tabs sat their headlines at slightly different heights, so moving between them made the window look like it twitched.

Scrolling through your whole app list is smooth again. A fast scroll through the full list dropped frames; each row now reports its height without having to be built first.

Release notes for an App Store app always come from the App Store now. When the store's own lookup for an app missed or failed, the window could fall back to the notes for that app's other distribution — a different build, on its own version numbers — describing a release your copy was never going to be offered.

Windscribe on its Beta or Guinea Pig channel is offered that channel's builds. DuoUpdater reads which update channel you picked in Windscribe's own settings, so a copy following a pre-release line is no longer told it is up to date while newer builds exist on that line. The window also shows the notes for those pre-release builds, which it previously listed only for stable ones.

Windscribe now gets update checks, with its release notes. A copy running an older build is listed with the version it can move to and what changed in it; before, DuoUpdater had no way to see Windscribe's version at all. Updating is still done through Windscribe's own installer, which sets up parts of the app that live outside the app itself.

An update is no longer applied to an app that went away while you were clicking. If the app is uninstalled, replaced, or stops being readable between the click and the install starting, DuoUpdater now stops and says so, rather than installing over that location anyway.

duo, the optional command-line companion, stops calling a package install finished before it is. Installing an app that ships as a .pkg opens the macOS installer and leaves the rest to you, but the summary counted it as installed — "1 installed" while nothing had been replaced yet. Those are now counted separately. Its --json output also labels every line with what happened to that app, so a script no longer has to read the English explanation to tell a failure from a deliberate skip.

Under the hood. The pre-install re-check that protects a one-click Update now protects duo install too, and the checks a download must pass before it replaces an app are held in one place for both routes that use them.

Clicking Update no longer does nothing when an update source contradicts itself. If the check that runs the moment you click comes back with an older version than the one the row was offering, DuoUpdater now says so and keeps the update on offer. It used to report the app as already up to date and drop it from the list, and the same update reappeared on the next check.

Fork updates are offered again when Fork is set to its Develop channel. DuoUpdater read Fork's channel setting backwards and followed its Stable feed, which sits well behind — so a Develop copy was listed as up to date while Fork itself was offering a newer version.

Mac Performance Monitor now shows its release notes. The app publishes them in its repository rather than in the feed we read, so the window had nothing to show for it.

CotEditor is now covered, on both its release and its beta line. Which line a copy follows comes from the version it is running and from CotEditor's own "Update to prereleases when available" setting, so a beta copy is offered the next beta instead of a release that would take it backwards.

An app you installed from the App Store is never offered a download from anywhere else. When the store's own lookup for it fails or comes back empty, the row now says the store is managing it, without a version number. Before, the check could fall through to the app's other distribution — a different build with its own version numbers — and offer to install that over your store copy.

An app is never offered an update that would move it to an older version. Some feeds list a stable release above a prerelease that is in fact further along, and taking it would have moved the app backwards.

A long error on a row no longer pushes the rest of the list down. It is kept to two lines, with the full text on hover.

Four more apps are covered: WhatCable, Qoder IDE, Qoder and Yaak. Each one gets update checks and a one-click install, and their release notes are read into the window as text rather than an embedded page.

Qoder's two Mac apps are told apart. The IDE and the desktop app share a name and a download page but ship on separate version lines, so each is now followed on its own.

Beta builds of WhatCable and Yaak are followed on their own track. A copy running a beta used to have no source at all and sat on "Failed"; it is now offered the next beta, with release notes kept apart from the stable ones. For WhatCable that also includes the stable release a beta eventually graduates into — taking that one moves the copy onto the stable track.

Checking your App Store apps uses a fraction of the network it did. Each check used to fetch every App Store app's product page again; the pages are now kept for an hour and the store is asked about all your apps in a few requests instead of one per app. On a five-minute check interval that is roughly a third less traffic overall; on the default six-hour interval the pages still expire between checks, so the saving there is smaller.

Checking one app again no longer re-fetches every App Store app. A single "Check Again" used to throw away every cached product page, so the next scheduled check paid for all of them; it now refreshes only the app you asked about.

Checking apps that ship through GitHub costs a fraction of the network it did. Each check used to download every release's full description again even when nothing had been published; it now asks GitHub whether the release has changed since last time and downloads nothing when it has not. Once a day it re-reads each release in full, so a release that is withdrawn is noticed within a day.

Apps followed on a GitHub beta or nightly track now ask for one release instead of a page of them. The newest release is the answer almost every time, and the full page is only fetched on the rounds where it is not.

Vorssaint's update check no longer rides a redirect. Its repository was renamed, and following the old name silently dropped the request onto GitHub's anonymous rate limit; the check now goes to the new name directly.

Under the hood. The release artifact is now built, signed and notarized on a GitHub-hosted Mac with build provenance anyone can verify, and the recorded request log distinguishes a cached answer from a network one.

Request logs you export no longer carry your account name. Every row for an app installed in your home folder used to spell out the full path; it now shows ~ instead, whichever way you take the log out.

The Requests tab now says what it does and does not cover. It records the fetches DuoUpdater makes itself. A release-notes page loads its own images and fonts, and App Store and Homebrew updates are carried out by separate tools — none of that appears there, and the window now says so instead of leaving you to assume.

Copy URL now escapes the address it gives you. Paths containing a space — Firefox, Thunderbird and Bartender downloads among them — were copied raw, which a browser forgives and a command line does not.

CapCut's beta row no longer reports a check that failed. Between betas — after one graduates and before the next opens — its maker publishes nothing on that track, which showed as a red row and a Retry that could not have worked. The row now simply has no answer from that source until the next beta appears.

Audacity now shows the mark saying what it is built with. It starts through a small launcher that hands off to the real program beside it, and the mark was being read from the launcher, which links nothing at all.

App Store release notes now arrive in your language. They were always fetched in the store's own default language, so a Mac running in Chinese or Japanese still read them in English.

DuoUpdater's own updates now appear under its own name in the Network window. Its release check, its release notes and its download were all filed with a blank app column.

Under the hood. A credential carried inside a web address's path is now removed before the request is recorded, as one in a query string always has been.

The Download Traffic window is now Network, and it has a second tab. Downloads is the ledger you already had — what each update cost as a file. Requests is new: every fetch DuoUpdater makes on your behalf, what it was for, which app it belonged to, and what it cost, with a filter field for asking the log questions rather than scrolling it. The figures at the top answer whatever you have filtered to, not the lifetime total.

Checking Spotify for updates barely uses the network now. Spotify publishes no version file anywhere, so the check reads the version out of a two-megabyte installer stub — and was fetching the whole thing every time, around the clock. It now asks whether that file has changed and skips the download when it has not.

PDF Expert now finds its updates, and shows what changed. It was reading a release list its maker stopped adding to in 2022, so it called itself up to date whatever version you had. It now follows the list the app's own updater uses, and it now carries the release notes for past versions too, not only the newest.

UTM preview builds now get their own updates, install with one click, and show the matching release notes. A preview copy used to be measured against the final track and silently told it was up to date; it now follows its own line, and once it takes that line's final release it moves back to the final track.

Under the hood. The recorded requests moved into the same store as the download ledger, so the two accounts of one download can no longer drift apart. Your existing ledger carries across untouched.

Fifteen more apps are watched for updates, and all but one install in a single click. AgentsView, AnythingLLM, Chatbox, ChatGPT Classic, DSH Desktop, FluidVoice, GitHub Copilot, Kun, Meetily, Microsoft 365 Copilot, OpenLogi, OpenSuperWhisper, Paseo, T3 Code — on both its alpha and nightly tracks — and Vorssaint. ChatGPT Classic is the exception: it is checked for you, but it ships an installer that updates the app itself, so that one stays yours to run.

Eight apps now show their release notes inside DuoUpdater instead of sending you to a web page. Xcode, Antigravity, Antigravity IDE, AnyDesk, AnythingLLM, Chatbox, Headlamp and Helium. Three of them had nothing to send you to in the first place: AnyDesk's page answers a bot challenge, Helium's makers publish no notes page at all, and Antigravity IDE had no link of any kind.

Xcode betas say what changed in each beta. The row used to offer a build number and a link; it now lists Apple's notes for the beta you are on and every beta before it in that release.

Word, Excel, PowerPoint, Outlook and OneNote now notice when their update has landed. These five kept offering to re-open the installer you had already finished with, and never moved on to offering the restart — waiting or re-checking could not have shaken it loose.

A row waiting to relaunch explains itself again, in both windows. When an app was both ahead of what its vendor publishes and waiting on a relaunch, the line above the button described something else entirely; the window and the menu also disagreed about rows waiting on an Update All restart.

Row tags no longer crowd out the app name in Russian. A few tags were borrowing wording from unrelated strings in every translated language, too.

Release notes now appear for apps whose vendor dates a release without timing it. Those releases used to leave no trace anywhere.

A release-notes page we refuse to open now says why. It went blank before, which looked exactly like an app that publishes no notes at all.

WeChat DevTools nightly release notes are no longer empty.

Under the hood. Version and release-date handling were consolidated so a build number can never be read as a marketing version, and the release timeline no longer invents a time of day the vendor never gave.

Failed checks are visible in the window now. The window drew nothing for a row whose check had failed — or one you ignored, one you skipped, or one the App Store, Toolbox or TestFlight manages — which looked exactly like "up to date". The two windows now say the same thing about the same app, and the retry button is in both.

"Check Again" on any row asks about just that app. It also re-reads which apps are running, so it is the quick way to correct a green dot that looks wrong.

Skipping and ignoring now work from the window's right-click menu. Both rows told you to right-click to undo, in a window whose menu had no such thing.

The background check no longer takes away release notes you are reading. The hourly check threw away every note it had already loaded, so an open Release Notes pane blinked back to a spinner. Only a refresh you asked for starts them over now.

Interrupted downloads are checked before they are called complete. A server that resent the whole file, or stopped short, was taken on trust, and the trouble surfaced one step later as an install that failed on a broken archive. Downloads behind a proxy that used to fail permanently work now.

Updating one app no longer rescans every app on your disk. Each click did two full sweeps of all your applications to look at the one you asked about.

Chrome's release notes cannot freeze any more. A routine restyle of Google's blog was enough to stall them for minutes; they now load promptly whatever the page looks like.

A downloaded installer is no longer discarded and fetched again. When a vendor spelled the same release two ways (v1.2.3 and 1.2.3), the waiting package went unrecognised and Relaunch could keep waiting on a swap that had already happened.

A failed administrator install is no longer mistaken for you pressing Cancel. There was no error shown, and that app quietly stopped offering one-click updates until you asked for administrator access again from its row menu.

The green "running" dot notices every app now. macOS never announces some apps opening or closing, and their dot stayed wrong until something unrelated refreshed it.

"Update All" no longer flashes in and out during a refresh, and an app you update mid-refresh is not reset to offering the update it already installed.

A check that fails as you press Update says so, instead of being filed as "nothing to do".

Under the hood. Release dates written in unusual formats are read correctly, the address check on release-notes pages covers every equivalent spelling, and routine bookkeeping no longer touches the disk each time any app on your Mac opens or quits.

Docker's mark now describes Docker's interface instead of its daemon. The row said "native"; Docker Desktop is an Electron app. The mark is read from the app's bundle, and Docker's bundle is a wrapper: the program it names is a background service written in Go, it carries no framework of its own, and the actual window-drawing app sits one level inside it. Everything was being read correctly out of the wrong file. DuoUpdater now looks in the nested app when — and only when — the outer one brings nothing itself and contains exactly one such app that proves what it is built with, so a helper process shipped alongside a real interface still cannot lend its identity to its host. Docker reads as Electron 42.5.0, and of the hundred and forty-six apps in the list on the machine this was written on, it is the only row that changes.

CleanShot X's release notes are readable again — including 5.0's. CleanShot shipped its biggest release in years and rebuilt its changelog page along with it: the date moved above the version number, two new layers appeared around it, and a feature release now puts an introduction and two video links between the version and its list of changes. The reader DuoUpdater used stopped matching any of it. What made this worse than an empty pane is that it did not look empty: the notes it had already saved for the previous release stayed on screen under the new version's heading, so the pane said "5.0" over 4.8.10's changes and nothing anywhere said otherwise.

Notes saved before a release was published are now re-read instead of trusted forever. That is the other half of the same story. DuoUpdater knew 5.0 existed six minutes before CleanShot published what was in it, saved the page as it stood, and filed it under 5.0 — and from then on every check found something already saved and never looked again. Saved notes are now confirmed against the vendor once per session before they are treated as final, so a version whose notes arrive late fills itself in on the next visit rather than staying wrong.

Anything you open from the menu bar now opens on the first click. Choosing Changelog on a row — or Settings, or the release log — did nothing at all the first time, and worked from the second click on. Reopening the menu made every attempt the first one again, so for people who open the menu, click once, and expect a window, it never worked at all. The first click inside the menu was being spent bringing DuoUpdater to the front rather than doing what it was aimed at. The window it opens is also held in front now: it was being ordered up and pushed back down again in the same moment, which is the other way this looked like a click that did nothing.

An app that leaves its own name blank now gets one anyway. Eudic (欧路词典) sat in the list with an icon, a version, and nothing at all where the name goes. Its bundle does declare a display name — and leaves it empty, because the real names live in the app's translations — and DuoUpdater treated that empty answer as the answer instead of asking the next question. It now falls through to the app's other name, and to the name of the app file after that, so a row is never nameless. One app in a hundred and fifty here was affected; the point is that the information was already there and was being skipped.

Relaunch now tells you when the app refused to quit, instead of looking like a click that did nothing. If an app has a window waiting for you — a save prompt, a sign-in sheet, some dialog — macOS will not let it quit, and there is nothing this side can do about that: it is the app's window. DuoUpdater used to spin for thirty seconds and then put the same button back with no explanation, which reads exactly like a button that is broken, so people press it again. The row now says what happened and points you at the app to deal with that window. Nothing was changed and the new version is already installed, so the relaunch really is all that is left — and if you simply quit the app yourself in the next ten minutes, DuoUpdater notices and brings it back on the new version without you clicking anything.

欧路词典's release notes are one release again, not sixteen years of them. The app publishes its entire history — every version back to 2.5.0 — inside the notes for whichever release is newest, so the detail pane showed all of it under the heading "26.9.0" and you had to scroll past a decade to find what had just changed. Each version now gets its own entry in the list, the way every other app's does.

The mark that says what an app is built with now needs proof for Tauri, not a resemblance. Tauri leaves nothing in a bundle to find — no framework, no folder of its own — so that one mark was worked out from how the app was packaged plus the fact that it links Apple's web view. Longbridge matches all of that and is not Tauri: it draws its own windows with the same renderer Zed uses, and embeds a web view for one corner of its interface. DuoUpdater now reads Tauri's own fingerprint out of the binary before claiming it, so an app is called Tauri when it is one — and Longbridge reads as the native Mac app it is.

CapCut no longer turns red because ByteDance's servers had a bad half-second. The endpoint DuoUpdater asks for CapCut's version answers with a success code and then, about one time in fifty, an error object instead of the answer — an internal timeout inside ByteDance's own infrastructure, roughly 390 bytes where 436 kilobytes were expected. Nothing on this side could tell that apart from CapCut having changed the shape of its answer, so the row said the check had failed, which reads as "this is broken and someone has to fix it" for something that fixes itself on the next try. That specific shape is now recognised for what it is: the request is retried immediately, and it is only ever reported as a real problem if it keeps happening for five days.

Every app in the list now says what it is built with. A row for an App Store app has always carried the store's badge, and every other row looked alike — a Sparkle app, an Electron app and a native one were indistinguishable. Each name now carries the technology's own mark — click it for a word and a sentence, or hover for the tooltip: Electron, Tauri, Flutter, Qt, Java, Chromium, Mac Catalyst, an iPhone app on Apple silicon, or a native Mac app — with the runtime's version where that can be read as a fact (Electron 42.4.1, Qt 6.2, the Chromium an app embeds, the Tauri it was built with). It is read from the bundle itself — the framework a packager had to ship, the runtime a launcher needs, the libraries the binary links — so it is a fact about what is installed rather than a guess from the app's name. Where a long name leaves no room, the symbol steps aside rather than pushing the name onto a second line: the name is the row. Turn the whole thing off in Settings → General.

LibreOffice no longer claims a downgrade to the version you already have. Its download index lists three-part versions (26.8.0) while the installed copy reports four (26.8.0.3), and filling the missing part with a zero made the installed copy look newer — so the row offered a muted "the vendor is behind you" note about an app that was exactly current. A source that publishes fewer parts than the app reports is now read as describing the same release, not an older one; a genuine rollback still says so.

A setting added by an update now points at itself once. A new preference that lands in the middle of the Settings window is a preference nobody finds. The menu bar's gear carries a blue dot after an update that added one, the page it lives on carries one in the sidebar, and the control itself carries one until you have looked at it. Only for people who actually updated into it — a fresh install is not greeted with dots on features that are simply part of the app it just met.

The Electron-manifest apps added last release can actually be updated now. 0.3.75 taught DuoUpdater to read the file those apps carry inside themselves, and it did read it — it worked out the new version, the right download for your Mac, and the checksum to verify it against, and then had nowhere to send any of that. The install button never appeared, and duo install explained the refusal with a reason that was not the real one. The install path is connected this release. In practice you may see no difference at all: this reader sits behind every hand-written rule, so it only speaks for an app nothing else covers — which today, on the machine this was built on, is none of them. It matters for the app you install tomorrow that nobody has written a rule for.

A download that would take you off a native build is now refused. Apple silicon can still run Intel apps through translation, which meant an Intel-only download passed the "will this run on your Mac?" check and installed cleanly — leaving you on a translated copy of an app that had been running natively, quietly, and with every future update doing the same thing again. Every in-place install now compares what you have against what arrived and refuses that swap. Going the other way, or from a universal build down to an Apple-silicon one, is normal and still allowed.

When it cannot prove which architecture a download is, it now declines rather than guesses. Some vendors publish an Apple-silicon build alongside their default one, and the only way to tell the default is the Intel build is to notice the other exists. If that second check does not come back cleanly — the vendor's server refuses it, the connection drops, or the two disagree about which version they are — DuoUpdater no longer treats that silence as an answer. It tells you the version and offers no install, which is the honest outcome.

A broken rule can no longer disappear from Diagnostics. For apps covered both by a hand-written rule and by the new manifest reader — which is most of them, deliberately — a failure of the rule was being cancelled out by the reader succeeding straight afterwards, so an app whose rule had actually broken went on reading as healthy. The two are recorded separately now. The manifest reader also reports its own failures for the first time; three apps on the development machine turned out to be pointing at addresses that have been returning "not found" for some time, which nothing would previously have said out loud.

Rows that can see an update but cannot install one now offer the same thing in both places. The menu bar showed a button labelled "Open" that opened the Finder, and the window showed nothing at all for the identical row.

Apps built on Electron are now recognised without anyone writing them down first. A great many Mac apps ship a small file inside themselves saying where their updates live. Until now DuoUpdater only knew the ones someone had hand-written a rule for — every other one sat in your list with no version next to it and no way to tell you a new build had shipped. It now reads that file directly, the same way it has always read Sparkle's, so an app like that is covered the day you install it rather than the day someone gets around to it.

Apps that already had a hand-written rule are untouched. The new reader sits behind them, so it can only fill a gap — never take over something that was already working.

Two details decide whether the download it offers is the right one, and both were settled by checking real apps rather than assuming. Some vendors mark an Intel build as their "primary" download even while publishing an Apple-silicon one beside it; others give the Apple-silicon build a filename that looks no different from the Intel one, so nothing about the name gives it away. DuoUpdater picks by architecture, and where it cannot be sure a download will run on your Mac it tells you the version and declines to offer the install — rather than handing you something that installs cleanly and then won't open.

If you run an app's beta, release-candidate or nightly build, DuoUpdater was quietly watching the wrong track. It works out which track you are on by finding your build in the vendor's own release list — but when a prerelease keeps the same public version number as the stable release it came from, which is the normal thing for a prerelease to do, it was matching the stable entry instead. The effect was silent: nothing wrong ever appeared on screen, you simply never heard about the next build on your own track, and the release notes you were shown belonged to the stable line. Found by installing the actual prerelease builds of Supacode and TypeWhisper and watching what happened; both now follow the track they are really on.

CapCut's beta showed a version you don't have. The row read "9.3.4545 → 9.4.0-beta6" while CapCut itself, Finder and every other updater called your copy 9.4.0-beta5. Some apps put their real version in a different field than most, and DuoUpdater was reading the tidier-looking one on your side of the arrow while reading the real one on the vendor's side. The update it offered was always the right one — only the label was wrong. Both halves of the row now come from the same place, for CapCut and for the seven other apps built this way.

MacWhisper, GitHub Copilot for Xcode, TypeWhisper and OpenUsage show their release notes now. All four had an empty notes panel: their update feeds carry no notes at all, which was not noticed until each one was checked against what the vendor actually publishes. DuoUpdater now reads MacWhisper's own release-notes page, Copilot's changelog file, TypeWhisper's changelog site, and points OpenUsage at its release list.

Helium updates through its own update service now, which brings its beta channel and much smaller downloads. It was being tracked through its public release list, which only ever shows stable builds — so anyone on Helium's beta was being handed the stable one. Its own service also publishes patches, so a routine update is roughly 40 MB instead of a 124 MB re-download. Verified against both a stable and a beta build, and the download is checked against the signing key inside your installed copy before anything is replaced.

Firefox and Thunderbird's beta, developer and nightly releases are tracked properly now — until today not one of those five tracks had ever reported a single update. They were being read from Mozilla's public version file, which publishes only the version you are shown, and installing a beta strips the "b5" off it: a Firefox beta calls itself 155.0 for the whole cycle, so the only question being asked was "is 155.0b5 newer than 155.0?", and the answer is no. Nightly was worse — Mozilla ships one every day and every one of them is called 157.0a1, so a four-week cycle produced exactly nothing. DuoUpdater now asks Mozilla's own update service, the same address Firefox's built-in updater uses and that the app names inside itself, and compares the build identifier both sides carry. Checked against the real downloads for all five tracks before shipping: the identifier that service reports is byte-for-byte the one inside the app you have; a beta one build behind now shows its update, and so does a nightly built earlier the same day. The stable and ESR releases were never affected and are untouched. One limit worth stating plainly: that service publishes no release date, so for these five tracks the release log still records when DuoUpdater first saw a build rather than when Mozilla shipped it.

Wispr Flow, AionUi and Devin update with one click now, instead of just telling you a new version exists. All three could already see their updates; none could apply one, on the stated grounds that the vendor ships separate Intel and Apple-silicon builds and DuoUpdater had no way to pick. Measured rather than assumed, that reason did not hold: the endpoints these three are read from are the Apple-silicon ones already, and DuoUpdater runs on Apple silicon only — there was never a choice to be made. Each download was fetched and checked before being wired up: the right app inside, signed by the same developer as the copy you have, notarized by Apple, and AionUi's verified against the checksum its own manifest publishes. Then each was installed for real, over an older copy, and came back on the new version.

Grok Bot is tracked now — new versions, and one-click updates. xAI's desktop app is built and signed by Anysphere, the company behind Cursor, and it updates through Cursor's own release service — which is why none of the usual routes answered for it: no Sparkle feed, no App Store listing, no public release repository, and a Homebrew cask that hands updating back to the app itself. DuoUpdater reads the vendor's own version endpoint instead. Two other endpoints were available and both were left alone for stated reasons: the one behind the download button on x.ai publishes no version number at all, and the one the app's own updater uses answers with an empty body once you are already current — a silence that would be indistinguishable from a broken endpoint. The install was checked before being wired up: the disk image carries the real app, signed by the same developer as the copy you have and notarized by Apple. One thing it deliberately does not offer is release notes. xAI publishes none for this app, and the only changelog it does publish belongs to a different product.

Comet and Msty Studio update with one click too, and a check now watches for apps that could and don't. These were the last two of the batch that could see updates but not apply them, and both turned out to need nothing new — only a closer look at what the vendor actually sends. Comet's download link is signed and expires in an hour, so the update button points at Perplexity's own gateway and the link is minted at the moment you click rather than hours earlier when the check ran. Msty publishes four downloads in one file with the Intel one listed first, and the fingerprint used to verify the download was being taken from that first entry; it is now tied to the Apple-silicon build it actually fetches, which usually also catches a release published between the check and the click, rather than installing it silently. Both were downloaded and put through the real install gates before shipping. The wider lesson had nothing to do with either app: nothing in DuoUpdater could notice an app left detection-only for a reason that had stopped being true, so the nightly recipe sweep now says when an app it merely watches is already handing us an installer.

QQ音乐 is tracked now — new versions, one-click updates, and its release notes in the window. None of the usual routes answered for it. It ships no Sparkle feed and no Sparkle framework at all; its Homebrew cask hands updating back to the app itself; and the update endpoint its own binary names replies with 200 and an empty body to everything we can ask it — a silence indistinguishable from a broken endpoint, so it was left alone. What DuoUpdater reads instead is the data file the vendor's own download page is built from: that page ships as an empty shell and fills itself in from that file, which is also the only place the release notes exist anywhere — there is no blog, no appcast and no per-version page. So the notes are read from it directly and rendered in DuoUpdater's own window, and the update installs the notarized disk image named in the same answer. One trap worth recording: that file still carries a second, six-year-old Mac record beside the current one, so both rules key on the versioned filename of the Mac download rather than on the word "Mac". And one limit worth stating plainly: the file publishes the version you are shown and never the build number underneath it, so a re-cut of a version that keeps its name is invisible here. It cannot invent an update that isn't there — it just cannot see that one kind.

TimeMachineEditor is tracked now — new versions, and one-click updates. None of the usual routes answered for it: no Sparkle feed (the app carries neither a feed address nor the framework that would use one), no App Store listing, no public release repository, and a Homebrew cask that hands updating back to the app itself. What it does have is a small vendor site whose single download link states the version in its own text — the same page, read the same way, that Homebrew's own version check relies on, so this is the surface the vendor intends rather than a guess. The update installs as the vendor's own installer package rather than as a plain app swap, and that is deliberate: the download also places a background scheduler, a command-line tool and a launch daemon outside the app itself, so replacing just the app would leave a new copy sitting beside a stale scheduler with nothing to notice the mismatch.

Little Snitch is tracked now, on both its stable and its nightly releases. Object Development publishes a version file that its own updater falls back on — the same one Homebrew reads — and DuoUpdater reads it directly, because Little Snitch ships no Sparkle feed and its cask hands updating back to the app. The two releases share one identity, which normally makes them impossible to tell apart; this one gives itself away in the version string, where a nightly build writes the word out in full ("6.5 nightly (7301)") while the stable build reads as a plain "6.4.1". Worth stating what this deliberately does not do: it will not install the update for you. Little Snitch runs a system extension and a privileged background service alongside the app, and whether swapping the app on its own leaves those intact has not been verified on a real machine — so DuoUpdater tells you the new version is out and sends you to the vendor's own download, rather than guessing with a network filter.

Carbon Copy Cloner is tracked now, and it will not try to move you between major versions. Bombich keeps three generations alive at the same time — CCC 5, 6 and 7 — all reporting the same identity to the system, which makes "is there something newer?" a trickier question than it looks: 7.1.6 genuinely does sort above 6.1.13, but crossing between them is a separate purchase, and CCC 7 needs a newer macOS than a CCC 5 machine may even be running. Answering by version number alone would have told every CCC 5 and 6 owner that a free update was waiting, forever, and it would have been wrong every time. So DuoUpdater reads each generation from its own address and only ever offers the next release within the generation you actually have. Beta releases are picked up too, for anyone who has turned those on inside CCC. Detection only for now — CCC installs a privileged helper beside the app, which is a larger claim than the one-click updates already here — and the reason nothing could see these updates before is worth recording: the app's own update feed answers every request successfully, with an entirely empty page.

An update your Mac cannot actually run is no longer downloaded and installed. Every app bundle states the oldest macOS it will launch on, and some vendors also state the newest — "this build is not for an OS that new", which is how an app that has not caught up with macOS 27 says so. DuoUpdater read neither. For apps tracked through a Sparkle feed the floor was already honoured, but that is a minority: of the apps on a typical Mac, the ones read from a vendor's own endpoint or from GitHub releases are the larger half, and a GitHub release publishes no macOS requirement anywhere at all. So an app that had moved on to a newer macOS than yours could be offered, downloaded in full, swapped in — and then not open. DuoUpdater now reads the requirement out of the downloaded app itself, right beside the existing check that it is built for your processor, and refuses the swap rather than replacing a working copy with one that will not start. The vendor-stated upper bound is honoured too, where a feed publishes one, so a build the developer has marked as not-for-your-macOS is not offered in the first place. Three limits worth stating plainly. Reading the requirement out of the download means the download has already happened — this prevents a broken install, not the traffic, because for most apps there is nowhere earlier to ask. An app whose newest version your Mac cannot run will keep showing that update and keep declining to install it; the refusal now names the macOS version it wants, but DuoUpdater does not yet remember the answer and stop offering it. And the check covers the routes where DuoUpdater swaps the app bundle in itself — an installer package hands the file to macOS, which enforces the package's own requirements, and Homebrew and the App Store pick their own builds; apps that came from the iPhone and iPad section of the App Store are skipped deliberately, since the version they state is an iOS one and comparing it against macOS would be worse than not checking. One thing it does not yet say out loud: where a developer has marked a build as not-for-your-macOS, that update is simply not offered, and the app reads as up to date rather than explaining why. For an older Mac that resolves itself the day you upgrade macOS. For a Mac that is too new it does not, and a better answer than silence is owed there.

百度网盘 is tracked now — new versions, one-click updates, and its release notes in the window. None of the usual routes were open. It publishes no Sparkle feed; the Homebrew cask cannot speak for a copy that was installed by hand; and the update manifest its own bundle names has gone dead — that file, its arm64 twin and the directory holding all of it answer 404. So DuoUpdater reads the endpoint the vendor's own download page is built from, and installs the notarized arm64 disk image named in that same answer. Its release notes arrive the same way: the "版本更新" page has a Mac版 tab, but the page itself ships empty and fills in from an API, so DuoUpdater reads that API directly and renders the last forty releases in its own window. One limit worth stating plainly: that feed publishes the version you are shown and never the build number underneath it, so a re-cut of a version that keeps its name is invisible here. It cannot invent an update that isn't there — it just cannot see that one kind.

A check that failed now says it failed, instead of showing a blank. An app whose version only a vendor's own endpoint can answer for had one way of saying "no answer": an empty dash, which is also what an app nothing covers looks like. So a vendor's endpoint timing out, or moving, read exactly like "DuoUpdater does not track this app" — a permanent-looking verdict, with no Retry offered and nothing in the "apps could not be checked" count. Those are now a failed check, which is what they are: a Retry button, a place in the count, and the reason named. Deliberately not everything: a condition your Mac can simply do nothing about — no recipe for the release track you are on, an endpoint that needs an identity this machine doesn't have — stays the quiet dash it always was, and a check that has failed three rounds running still steps out of the banner rather than pinning it forever. An app that JetBrains Toolbox installs also keeps its "open Toolbox" button when the version read behind it fails, since opening Toolbox was the answer either way.

The "relaunch to apply it" reminder now goes away once you have relaunched. When an app's own updater downloaded a build in the background, DuoUpdater told you so and offered a Relaunch button on the notification. Taking it worked — the app came back on the new build and said "Now running 1.0." — but the reminder it replaced stayed in Notification Center underneath, still asking you to relaunch something you had just relaunched. DuoUpdater was watching for the reminder to stop being relevant, and the moment it checked was the one moment that had already been accounted for. It now takes the reminder down whenever there is no relaunch outstanding, which also clears one left behind by an app that applied its own update while DuoUpdater was not running.

Apps that ship many builds under one version number are handled properly now — everywhere. A Mac app carries two version strings: the one it shows you ("1.0") and a build number that actually counts up. Most apps move both. Some move only the build: Amp shipped ten builds in a single day, every one of them called 1.0; Surge has shipped four separate releases as 6.9.0; JetBrains' preview builds do the same. DuoUpdater decided "has this changed?" by comparing the shown version in about a dozen places, and for those apps that comparison can only ever answer "no" — or, where it asked "are these the same?", "yes" — whatever had really happened. What follows is what that broke. It is one mistake, found because Amp made it visible.

Relaunch no longer appears to hang for three minutes and then report a failure that did not happen. Clicking Relaunch on an app whose own updater had a build waiting would spin for three minutes and then say it had failed — while the update had in fact been applied within a second or two and the app had already reopened on the new build. DuoUpdater was watching the shown version for a change that was never going to come. Measured on Amp: 189 seconds of spinner for a swap that took under one. It now settles in well under a second, and a real failure is still reported as one.

Relaunch no longer offers you a build that is already out of date. When an app's updater had downloaded one build and the developer had since published another, DuoUpdater still offered Relaunch — so you relaunched and were immediately a build behind, which is exactly what that check was written to prevent. Those rows now offer Update instead, which fetches the current build.

Skipping a version no longer silences an app forever. This is the one worth knowing about. "Skip this version" is meant to decline one release and let the next through. It recorded only the shown version, so for an app that keeps one version name across builds, skipping once declined every future release — permanently, surviving restarts, with nothing on screen to say the app had gone quiet. A skip now records the build it declined. One consequence of the repair: a version skipped by an earlier DuoUpdater is offered to you once more, because the old record cannot say which build it meant. Skipping it again records it properly.

Rollback comes back for updates that looked like they changed nothing. The workbench hides a rollback that would do nothing — and for these apps every rollback looked like it would do nothing, so the row vanished after a genuine update while a complete backup sat on disk with no way to reach it.

The reminder to relaunch is announced once per build again, and survives the developer shipping another one. 0.3.69 replaced a reminder that repeated every five minutes with one that speaks once per staged build — but identified the build by its name, so for these apps it announced the first and then went silent for every one after. That is fixed. Separately: if an app refuses to quit because it has unsaved work, DuoUpdater keeps a note to reopen it once you have answered. That note used to be thrown away if the developer published another build while you were deciding, leaving the app closed after the swap with nothing to reopen it. It now follows the new build instead.

A failed App Store update is no longer quietly recorded as a success, and a downloaded installer package is no longer treated as the one currently on offer when it is an older build. Both came from the same comparison. Release history also counted ten builds of an app as one release; it counts them separately from now on, though history already recorded cannot be recovered.

The row itself says which build a relaunch will apply. It read "1.0 → 1.0" — a line naming no difference at all. It reads "1.0 (129) → 1.0 (130)" now, and only when the build is the thing that changed; where the version names already differ, the line is unchanged. The same correction reaches the Relaunch tooltips, the notification, the note explaining why an install was deferred, and duo install's refusal.

微信输入法 and 豆包输入法 can be updated in one click again — and they are updated the way they update themselves. One-click for input methods was withdrawn in 0.3.25 the day it shipped, after someone's input-method settings went missing. What was wrong with it was the shape of the install: it replaced the whole app, the way a first-time installer does. An input method is registered with macOS by the location of its app, and both of these apps update themselves without touching that location — they keep the app and exchange what is inside it. DuoUpdater now does the same thing, so the registered app comes through an update as the same app, and a failure at any point leaves the copy you were running exactly where it was. Neither one asks for your password any more, either.

Before either of them is updated, everything they have learned is copied first. Your dictionary, your settings and your account state do not live inside the app, so the rollback copy DuoUpdater already kept could not speak for them. They are now snapshotted before every input-method update and put back with the rollback, and the copy is close to free — 578 MB of one of them takes a tenth of a second and almost no disk. This is a safety net rather than a repair: what an app decides to do with your data the next time it starts is the app's own code running, and nothing can stand in front of that. Now there is something to go back to.

An update no longer quietly takes away an app's ability to update itself. When DuoUpdater needed an administrator to replace an app, it restored who owned the app but not the permissions the app was installed with — and a download normally unpacks with narrower permissions than an installer sets. For both input methods that is the difference between their own updater being able to finish its next update and being unable to clean up after itself. The permissions an app was installed with are now carried onto the copy that replaces it.

DuoUpdater stops asking you to relaunch the same app every five minutes. An app that updates itself parks the new version on disk and waits for you to quit it. DuoUpdater noticed that on every check and posted a reminder each time, so a build you had decided not to relaunch yet went on nagging you for as long as you left it. Each build is now announced once: relaunch it, skip it, or ignore the app and it stays quiet, and the next build announces itself normally.

An app waiting to be relaunched counts as an update, and an ignored one no longer counts at all. A new version already sitting on disk is an update — it just downloaded early — so it is now counted in the badge and in the “N updates available” line rather than in a separate tally beside them. Ignored apps went the other way: one you had ignored could light the badge while its row showed a muted “Ignored” tag and no button to press, so the number pointed at something you could not act on. Ignore and skip now mean the same thing on all three surfaces — the reminder, the badge and the list.

搜狗输入法 is tracked now — by asking its own updater rather than reading its website. Sogou's changelog page publishes three-segment version numbers where the installed copy carries four, so comparing against it would have meant trimming the real version down first. Its own update check answers in the bundle's own numbering instead. DuoUpdater asks that endpoint the way the app does and reads the release out of it, all four segments, so a respin that changes only the last one is visible. This one is detection only: Sogou's updater does a great deal more than exchange the app — it re-registers a QuickLook generator, moves your data to a new location and force-quits the input method — so the update itself is left to it. One thing worth knowing, and it is Sogou's doing rather than ours: their endpoint answers according to the macOS version asking, and a Mac on macOS 28 asking for itself is handed a build from 2023. DuoUpdater does not ask for itself, so the version you are shown is the current one on every Mac.

Release notes that a later fix would have got right are re-read, instead of staying wrong for good. Notes are cached per version, on the reasoning that a released version's notes never change. That is true of the notes and not of what DuoUpdater manages to extract from them, so an app whose notes came out garbled was stuck that way for that version no matter how many parsing fixes shipped afterwards. Every cached entry now records which generation of the extraction logic wrote it, and one written by an older generation is fetched again. Both caches do this — app changelogs, and Homebrew formula notes.

A check that fails at somebody's CDN is asked once more before it counts as broken. 502, 503 and 504 all mean an intermediary could not reach the server behind it: the request never arrived, so the same request a moment later routinely works. Headlamp's check died on exactly that — GitHub answered 504 with no rate-limit header at all. Those three now get one retry, after eight tenths of a second. Deliberately only those three: a 500 is the server itself failing and repeating it mostly reproduces it, and retrying a rate limit spends the budget it is complaining about.

A Homebrew formula's release notes follow the version they belong to. Notes were remembered per formula and never per version, so once a formula's notes had loaded, every later look at it was served that first version's notes for the rest of the session — including after a newer version appeared. Reading them also used to occupy the queue the rest of the formula list waited on, so a slow brew info could stall the list around it.

Right-click an app in the workbench's sidebar to open it. The same Open the menu-bar rows have always offered, in the window where you are more likely to want it.

Discarding a downloaded installer takes the error it left behind with it. Throwing a staged package away already put the row back to Update, but a red failure from the attempt you had just called off stayed sitting on it — and nothing would ever have cleared it, since an error is only retired once a row has gone up to date, which a row still offering an update never does. It goes with the download now.

duo, the optional command-line companion, stops describing itself inaccurately. --timeout is gone — it was accepted, documented, and read by nothing. --budget is documented for the first time, and the one message that mentions it now says a real number of minutes rather than always "15". --max-calls claimed a default of 20 when it is 6. A number flag handed something that is not a number is now an error: duo verify --max-concurrency 1x used to be ignored and swept with the default of four, which is the wrong way round for someone deliberately slowing a sweep down. And duo verify counts the requests it really made: a feed that answers 502 and succeeds on the retry no longer reports a clean ok while having quietly asked twice. -h works everywhere --help does.

Four more apps are tracked, two more release channels, and every one of them updates in place. CapCut, Canva, and WorkBuddy on both of the sites it ships from — the international one and the Chinese one run independent release trains, so an install is only ever offered the version and the notes belonging to its own site. Termius and VSCodium gain their Beta and Insiders channels alongside the stable builds already covered. CapCut's second track is the awkward one: it is chosen by a switch inside CapCut itself and appears nowhere in the version number, so which track you are on is read from the copy on disk rather than guessed from the build you happen to be running. Worth knowing if you tick that box — DuoUpdater will offer you the newest beta as soon as it exists, which can be ahead of the point CapCut's own staged rollout would have reached you.

Android Studio's preview channels no longer offer you a build older than the one you have. Google lists its releases in the order they were published rather than by version, and DuoUpdater searched that list three separate times — once for the version, once for the date, once for the download — so a release candidate published after a newer canary could supply the version from one entry and the file from another. On 26 August the Canary channel answered with 2026.1.4 RC 2 while 2026.2.1 Canary 2 had already shipped. Each of those answers now has to come from the same entry.

A re-cut release no longer installs the copy it was meant to replace. When a project rebuilds a release without changing its version, both files stay under the one tag — KeePassXC ships KeePassXC-2.7.11-1-arm64.dmg next to the original KeePassXC-2.7.11-arm64.dmg — and DuoUpdater took whichever was listed first. That order is alphabetical, which happened to put the rebuild first this time and would have put it last the next time. Nothing about it was visible: the version on the row was correct, only the file behind it was wrong. The right one is now chosen by what the filename itself says.

Nightly and snapshot builds are recognised as the pre-release builds they are. VLC's nightly, the KeePassXC snapshot and Freelens nightly all install under the stable app's name and bundle identifier, so their version string is the only thing that gives them away — and it was not being read. They counted as stable installs, which is how a nightly ends up being offered an ordinary release to overwrite itself with. DB Browser for SQLite's nightly is read from the app's own filename for the same reason, and there the problem was live but hidden: its frozen version sorts above the current stable one only until stable catches up, at which point every nightly install would have been quietly handed a stable build.

Relaunching an app reports what actually happened. A few apps ship a second app inside their own bundle — Surge keeps its Dashboard there — and when only that inner app needed restarting, DuoUpdater said the outer app had been relaunched when nothing had. It now reports the inner app's own outcome, and no longer raises a relaunch notification for a relaunch that never happened.

An update that has already downloaded says so, and can be put back. A row holding an installer package that had finished downloading showed a blue Install button and nothing else — nothing said the download was already done, or that the button reopens the installer rather than starting the update over, and there was no way to change your mind. That is now on the row itself rather than hidden in a tooltip, and Discard Downloaded Installer in the row's right-click menu throws the download away and puts the row back to Update. Discarding one while an install of the same app was already running could also throw away a package that had just finished downloading; it no longer can.

Release-notes buttons that pointed at retired pages open live ones again. Microsoft renamed the Edge enterprise release-notes pages and Termius moved its changelog, so three Edge channels and Termius were sending you to a page that no longer existed. Nothing had ever checked whether those pages were still there, which is why they could rot indefinitely with everything else looking healthy — they are now checked on a schedule. Edge's Dev channel gets no button at all: Microsoft stopped publishing notes for it, and every page that still exists belongs to a different release train.

Raycast is followed onto version 2, and only on the Macs that can run it. Raycast 2 needs macOS Tahoe and Apple silicon. A Mac that does not meet that stays on the version 1 train and is no longer told about a release it could never have installed. The two trains also keep their own release notes now, so a version 1 install reads version 1's history rather than version 2's.

Release notes are rendered in DuoUpdater's own window for more apps. BetterDisplay on all three of its tracks, Shotbase, and WorkBuddy on both sites. BetterDisplay's notes no longer end in the raw markup of its download button, and no longer repeat its contributor list on every single release.

duo, the optional command-line companion, refuses a command line it does not understand. A mistyped flag used to be ignored, and an ignored flag reads as one you never passed — so duo verify --githubb quietly checked every recipe instead of the one you asked for. Unknown flags, a flag left without its value, and stray arguments are now errors that name what the command actually accepts, and --help works after any command.

BetterDisplay's pre-release and internal builds are now offered to the people who asked for them. BetterDisplay carries all three of its release trains in a single update feed and picks between them with two switches in its own settings. DuoUpdater could only guess your train from the build you happened to be running, which says nothing about a switch you turned on but have not yet acted on — so someone with both switches on, sitting on a stable build, was told they were up to date while BetterDisplay's own updater was offering a version four releases ahead. Your actual choice is read from BetterDisplay now, including the case where turning on internal builds keeps the ordinary pre-releases coming too. Its Apple-silicon-only preview builds are deliberately left out, so an Intel Mac is never offered one it cannot run.

Switching an app's release channel twice in a row no longer strands the row on the old answer. Flipping a channel switch sends DuoUpdater to re-check that app, and a second flip while the first check was still running used to be dropped — the row went on offering a beta to someone who had just turned betas off, until something unrelated happened to trigger another check. The newer flip now takes over from the older one, and a check that is interrupted partway leaves the apps it never reached marked for the next pass instead of recording them as handled. Rows also enter their checking state sooner after a flip, so there is less time in which the answer on screen is one your switch has already invalidated.

An app that lives inside another app is now closed and reopened along with it. A few apps ship a second, complete app inside their own bundle — Surge keeps its Dashboard there. macOS treats that as a separate app, so nothing closed it when an update was applied to the app around it: it carried on running the version that had just been replaced, out of a copy that was no longer where it thought it was, and from there it could not talk to the app it belongs to. Those are now closed with the app they live in and reopened after it, and whichever window you were actually working in is the one that comes back in front. Only apps you could have opened yourself are treated this way; the invisible helper processes an app runs for itself are left alone.

Rollback points that had quietly stopped being taken are being taken again. An app may lock one of its own files so that nothing can delete it, and the copy kept for rollback inherited that lock — so it could never be replaced by the next one, and a single interrupted attempt could leave behind a copy nothing was able to clear. After that, every update of that app went ahead with no way back, and said so in one line that gave no reason. One app on the developer's own Mac had been in that state for two days. Copies kept for rollback no longer carry the lock, the installed app is left exactly as its developer set it up, and a copy that has got stuck can no longer block the one meant to replace it.

An update that worked no longer reports that it failed. macOS can put a new version in place and then fail while removing the one it displaced. That was taken at face value: DuoUpdater would say an app had not been updated when it had, or that there was no rollback point while a complete one was sitting in the backup store — and in the first case it sent you to grant a permission you had already granted and that could not have helped either way. What actually happened on disk is now checked before anything is reported.

Version lines no longer repeat digits the version number already contains. When an app is waiting to be relaunched, the row shows the version you are running and the one a relaunch will land. Both sides carried a build number, which is what makes the difference legible when a developer ships several builds under one version name — and pure noise when the versions already differ. Chrome's line spent its width printing "151.0.7922.174 (7922.17…" and ran out before the digits that actually changed. The build numbers now appear only when they are the thing that changed.

One word now for reopening an app to finish its update. An app whose own updater had already put the new version in place asked you to "Restart" it; one that keeps the new version aside until you quit asked you to "Relaunch" it. That difference was real behind the scenes and made none to you: the same click either way, and the same outcome if you never click, since the update lands the next time you quit the app regardless. It reads Relaunch throughout now — including the notifications, the tooltips and the setting that does it for you — which is also the word Chrome, Claude and most apps that update themselves put in front of you. Only English ever had two words for this; German, Japanese, Russian and Chinese have always used one, and Spanish and French move onto the wording they were already using elsewhere.

An update that needs an administrator now asks for one, instead of failing and blaming a permission. Whether a swap needed a password was decided by looking at the folder an app sits in rather than at the app itself. Anything macOS installed as root — every App Store app, and any app an installer package laid down — passed that test, took the route that needs no password, and could not possibly finish: removing the old version requires write access to the directories inside it. macOS reports that refusal with the same code it uses for a denied App Management permission, so the failure arrived as a request to grant App Management, which could never help, because the obstacle was file ownership. On an ordinary Mac that was every App Store app and a handful of others besides, and it was administrator accounts it hit — standard accounts were already being routed correctly. Those updates now take the route that works, and the app keeps the owner it had rather than quietly becoming yours.

App Store apps now get a rollback point like everything else. Backups skipped them, on the reasoning that the store can always fetch a previous build back. It cannot — the App Store offers only an app's current version — which left the store as the one route that applied an update with no way to undo it. Those apps are now backed up before an update like any other, and because the copy is made by cloning it costs almost no disk space until the update actually replaces the original. Restoring one says the thing that is specific to the store: the update reappears in the Updates list straight away, and is re-applied on its own if automatic app updates are switched on. And when a store update was never going to be applied — an iPhone app running on a Mac, a title not sold in your region — no rollback point is taken any more, so a row can no longer offer to roll back to the version it is already running.

Longbridge Desktop is now tracked, on both of its release trains. Stable and Preview each get version detection, one-click installation of the official Apple silicon build, and release notes rendered in DuoUpdater's own window, illustrations included.

WhatsApp's release notes now appear while its App Store check is still running. The App Store page shown in that window had been filed under a name the lookup could never match, so the notes pane sat empty instead of showing it.

The menu header carries more in less room. DuoUpdater's own version now sits beside its name, and clicking it opens its release notes. "Update All" has moved onto its own line, where a translated label has room to be read in full, and the actions along the bottom are icons. The banner that used to announce DuoUpdater updating itself is gone — the sparkle beside the version lights up instead, and stays lit until you have read what changed. A status line too long for the menu now ends in an ellipsis rather than stretching it.

Settings no longer cuts short the update choices it is offering. The two menus that decide how an update is applied were clipping their own labels in several languages. The wording is shorter now, and they move onto a second line where the words still need it.

What's New says when each version shipped. Every version in the rail now carries its publication date.

Architecture-specific updates now choose the build this Mac can actually run. A few apps publish the same version twice in one Sparkle feed — once for Apple silicon and once for Intel — and DuoUpdater used to break that tie by whichever download address happened to sort first. It now reads the feed's hardware requirement and, where a vendor leaves that blank, the architecture in the filename. The native build wins consistently, and a build this Mac cannot launch is not offered. GitHub releases get the same treatment, without calling a perfectly healthy recipe broken just because its newest artifact targets another architecture.

ChatGPT update checks now follow the rollout track attached to the account. OpenAI sometimes holds business and enterprise accounts on an earlier desktop build while a new one reaches consumer accounts first. DuoUpdater used to omit the account's plan from that check, which silently selected the cautious track for everyone: it could say the installed copy was somehow ahead while ChatGPT itself was already downloading a newer build, or offer a build the app's own updater would replace again. It now sends the plan label from the ChatGPT/Codex sign-in state with the same update request the app makes. If that label is unavailable it keeps the cautious behavior; credentials themselves are never put into the request or diagnostics.

A relaunch that macOS never answers can no longer wedge every later update. Launch Services occasionally accepts a request to reopen an updated app and then never calls back. The row stayed on “Relaunching…” forever, its Restart button remained disabled, and DuoUpdater's own update waited behind it. A launch that has not answered after a minute is now released as failed, so the row recovers and the rest of the updater keeps working.

Release Log stays populated when its scrollbar is dragged quickly. The old lazy stack could be outrun by a long jump, briefly leaving a blank window while rows were created around the new position. The log now uses a recycling list that can jump directly to the destination. The refresh control also keeps the same footprint while changing between its arrow and spinner, so the bottom row no longer twitches when a check starts.

Download Traffic now marks updates that used a binary patch. New downloads record the route that actually completed — not merely whether a patch was offered — and carry a Delta badge in their history. The unmistakably smaller patch downloads from 0.3.62 are recognised too, even though they were recorded before the traffic ledger had a route field.

Updates now download only what changed, when the developer publishes it that way. Some apps ship a small patch alongside each release — enough to turn the version you have into the new one, without fetching the whole thing again. DuoUpdater used to ignore those and download the full package every time. It now takes the patch when one matches the exact build you're on. ChatGPT's last update came to 1.9 MB instead of 605 MB; Docker's to 87 MB instead of 582 MB. The result is the identical application either way — same signature, same bytes, verified against the full download before this shipped. When no patch fits what you have, or one fails to apply, the full download happens as before, so nothing can fail to install because of this.

DuoUpdater no longer downloads an update an app is already downloading itself. Many apps update themselves as well, and both of us reaching for the same 600 MB file at the same time cost you that file twice. DuoUpdater now notices a download in progress and leaves it alone, saying so on the row rather than doing nothing silently. If that download turns out to be abandoned, it stops counting after ten minutes so nothing stays blocked.

An update an app has already prepared is no longer overwritten. Apps that update themselves often download in the background and then wait for you to quit them before swapping the new version in. Installing over one of those looked like it worked and then came undone the moment you quit the app — and where the app's own pending version was older than what DuoUpdater had just installed, you ended up further behind than when you started. Those updates are now left to finish, whichever version they carry.

DuoUpdater's own updates got smaller too. Its releases now ship the same kind of patch, so updating from a recent version fetches a few hundred kilobytes instead of eleven megabytes.

Explanations left behind by an update now clear themselves. When DuoUpdater hands an app over to its own updater, the row says so — "brought it to the front so its own updater applies the update". That sentence used to stay there for good: the only thing that ever removed it was starting another update on the same app, so it was still sitting under the row long after the update had landed, describing something that finished hours ago. It now goes as soon as the app is up to date. A warning that an update was applied without a rollback point is deliberately left alone, because that one describes the update that already happened and only starts to matter once it is over.

An update that was refused now explains itself in your language. When something else is already installing — DuoUpdater working through a batch, or duo in a terminal — the row tells you so. That message was English only, in an otherwise translated window, and it ended with a process number: useful in a terminal, and nothing you can act on in a menu. It is now translated, and says what to do rather than who has the lock. The command-line tool still prints the process number, where you can do something with it.

The release-pattern line, and its clock, now follow your locale. Release Log → Patterns summed everything up as "Most often ships Friday, around 6 PM". Languages that inflect the names of days cannot say that with the day dropped into the middle of the sentence, and got the wrong form of the word — Russian read "Чаще всего выходит пятница" where it needs "по пятницам". It is now a label: "Peak: Friday, around 6 PM", which is correct everywhere. The time goes with it — if your Mac writes clock times on a 24-hour dial, so does this, on the chart's axis too.

Settings search now understands the words on your screen. The search field above the settings sidebar matched a list of extra terms that was written in English and never translated, so "rollback" reached General while "Zurücksetzen" and "回滚" reached nothing. Those terms now exist in every language DuoUpdater speaks. The English ones still work in every language too, since the documentation they come from is in English.

Turning on "Show all" no longer makes the menu hesitate. The full list of every app you have was measured in its entirety each time it appeared — on a Mac with 127 apps that came to roughly a second of work, nearly all of it spent laying out rows far below the ones you can see. It was paid on every toggle, not just the first. Only the rows actually on screen are built now.

An explanation left over from a failed update no longer outlives the failure. When an update couldn't be applied — because something else was already installing, say — the row said why, in red. Nothing ever took that line away: once the update did land and the row went back to a tick, the old explanation was still sitting underneath it, and it stayed there through every re-check until DuoUpdater was restarted. It now goes as soon as the app is up to date. Reasons belonging to updates that are still waiting are left alone, so one you haven't read yet can't be wiped by a check running in the background.

The "Update All" button no longer changes size with the length of the list. With only a few updates pending it was drawn a size smaller than it should have been, with eighty points of empty space beside it, and jumped back to its proper size whenever the list grew. It now stays the size it is meant to be.

Download Traffic now says which build an update moved to, not just which version. Plenty of apps ship several builds under one version name — Surge put four separate releases out as "6.9.0" — so those rows read "6.9.0 → 6.9.0" and told you nothing. They now read "6.9.0 (12028) → 6.9.0 (12030)", and only when the version name alone isn't enough; where the version already changed, the build number would just be noise and is left out.

The build recorded is the one that actually landed, read off the app itself once the update is in place. Not the number the developer's update feed advertised — feeds do misreport, and this way it also works for the places that publish no build number at all: GitHub, Homebrew and the App Store. An update still waiting on macOS's installer window is never guessed at; nothing is recorded for it until it's real.

A download that changed nothing is now marked as such. Occasionally an update fetches and installs the build that was already on your Mac — a version-number mismatch on the developer's side, a mirror serving what you already have. That is real bandwidth spent for no result, and the traffic window is where you would want to see it. Those rows now carry a "no change" tag. Downloads recorded before this release don't have build numbers to compare, so they are left alone rather than guessed at: unknown is not the same as unchanged.

DuoUpdater now shows you what keeping your apps up to date has actually cost in downloads. It has been counting, to the byte, every update it fetched for you — but the count had nowhere to appear, so the number sat in a file nobody could read. There is now a Download Traffic window, opened from the chart button at the bottom of the menu, with this month's figure printed next to that button so the most common question is answered without opening anything. Inside: the total, the last three months side by side with the change between them, a breakdown of where the bytes came from, and every app ranked by how much it has cost — click one to see each update it took, which version it went from and to, and how big that download was.

The total is honest about what it cannot see. Homebrew, the App Store, and apps that update through their own built-in updater fetch their own bytes, and DuoUpdater never handles them — so the figure is a floor, not a full accounting. That has always been true; what changed is that the window says so permanently, rather than only on the empty screen you see before anything has been recorded.

Apps you have since renamed or deleted keep their history. Traffic is recorded against where an app lives on disk, which is what lets two channels of the same app — Android Studio Canary and Beta, say — stay apart instead of being added together. The cost is that renaming an app leaves its past under a name that no longer exists. When OpenAI renamed Codex to ChatGPT, that split 30 GB of downloads across two entries that looked like duplicates. Those entries are now grouped and dimmed at the bottom of the list, under a heading that says what they are. Nothing is thrown away, and the total still includes them.

An app you installed from the App Store is no longer offered the developer's own download. Plenty of apps are published in two places at once — in the store, and as a direct download from the developer's own site — under the same identity but as genuinely different builds. The direct download usually runs ahead, because it doesn't wait for store review. DuoUpdater checks the store first for a store-installed app, but when that check failed for any reason — a dropped connection, a storefront that didn't answer — it quietly moved on to the developer's site and offered you whatever was there. WhatsApp showed this as "26.32.75 → 26.33.19": a real version, from the wrong place. Taking it would have replaced your store copy with one the App Store could never update again. Store-installed apps are now checked against the store and nowhere else.

Switching an app's update channel inside that app now registers straight away. Some apps let you choose between their normal releases and their beta ones — Surge, Tailscale, Fork, OrbStack, IINA, Alfred and others — and DuoUpdater follows whichever you picked so it never offers you a build you didn't ask for. It used to notice the change only when you quit the app or opened one of DuoUpdater's windows. Neither covers what people actually do: turn the setting off, leave the app running, and glance at the menu bar. Worse, apps save that setting to disk when it suits them rather than the moment you click — Surge took five minutes here — so even quitting could be read too early. DuoUpdater now watches for the setting itself changing and re-checks that one app within a second or two. Switching Surge back to normal releases now clears the beta version from its row immediately, instead of leaving it there for up to an hour.

A check that couldn't reach anything no longer looks like a clean bill of health. When every source failed — no network, or a proxy quietly refusing connections while the Wi-Fi icon still says everything is fine — the apps that failed were hidden and the panel said "127 apps · up to date". That is the same screen you get when everything genuinely is up to date, which made a failed check indistinguishable from a successful one. The panel now says how many apps it couldn't reach, shows what went wrong, and offers to try those again — just those, leaving everything it did manage to check alone.

A helper macOS wouldn't switch on no longer sends you to reset your whole Mac. App Store updates install through a small background item macOS asks you to approve once. When that approval was refused, DuoUpdater had exactly one explanation for it: the system's record of the item is damaged, and repairing it takes a Terminal command that clears the background-item approvals of every app on your Mac. That is one cause among several, and macOS never says which one applies — the ordinary one is that the switch is simply off. The message now leads with that instead: turn DuoUpdater on under "Allow in the Background" in Login Items & Extensions, and DuoUpdater opens the pane for you. The Terminal reset is still written down, as the fallback for when DuoUpdater isn't listed there or switching it on changes nothing. The message also stopped running off the edge of its card.

Two Settings options read on one line again. The two options under Install routing — how App Store updates install, and what happens to apps that ship their own updater — used to put their name to the left of the menu, the way options do everywhere else on macOS. Adding six languages moved both names above their menus instead, because a German or Russian option can be long enough to run off the end of the line, and the safe layout was applied to every language at once. Each row now decides for itself: the name stays beside the menu as long as the menu leaves room for it, and only moves above when the text genuinely needs the width. In English, Japanese and Chinese both rows are back on one line; in German and French they stay stacked, which is the only way they fit without being cut off.

DuoUpdater now speaks Russian, Simplified Chinese, Japanese, German, French and Spanish. It follows whatever language your Mac is set to, and switches with it — there is nothing to turn on. If you would rather read it in a language your Mac isn't set to, macOS can do that per app: System Settings ▸ General ▸ Language & Region ▸ Applications. Everything the app writes itself is translated: the menu-bar panel, every Settings page, the setup window, the notifications, the alerts, and the small print under each option. Counts are handled the way each language actually handles them rather than by bolting an "s" onto the end, which matters most in Russian, where "1 update", "2 updates" and "5 updates" take three different endings — and in Chinese and Japanese, where they take none.

Two things stay in English on purpose. Release notes are the vendor's own words, so they arrive in whatever language the vendor wrote them in — translating them would mean rewriting what a developer said about their own release. And the duo command-line tool stays English, the way command-line tools generally are.

An App Store app that was open when you updated it comes back again. Last release drew a line in the wrong place. It was fixing something real — an update that failed, or that you cancelled, could bring an app back to life minutes after you had closed it yourself — but it decided whether to reopen by asking whether someone had answered a quit prompt. Almost no App Store update shows one: the prompt only appears on a route DuoUpdater stopped using a while ago. So from 0.3.53, updating an App Store app that was running closed it and left it closed, with nothing on screen to say why. The question it asks now is whether the update actually landed, which is the thing that decides whether a quit is coming at all. This is verified against a real App Store update rather than reasoned about: the store terminates your app to replace its files and never brings it back — despite its own prompt promising that it will.

DuoUpdater now tells you when App Store is waiting on you. The store cannot replace an app while it is open, so it puts up a "cannot be open during installation" prompt and waits — with no time limit, holding the update and a download slot for as long as it takes. That prompt is a small panel inside App Store's own window, which may be sitting on some entirely unrelated page; App Store bounces its Dock icon a few times and gives up; and DuoUpdater lives in the menu bar with no Dock icon to bounce. An update could sit there indefinitely with nothing anywhere telling you a click was needed — one took four and a half minutes here, ending only because someone thought to look. The row now says what is being waited for, from the moment the update starts.

The dot that marks an app as open no longer lags behind. macOS does not reliably announce that an app has launched — some apps never trigger it at all, while quitting is always announced. DuoUpdater listened only for those announcements, so a row could insist an app was closed while its window sat in front of you, and only correct itself when some unrelated app happened to open or quit. It now re-derives which apps are running whenever it rescans, which includes the moment you open the menu.

Two things a review caught before you did. The warning that an update was applied without a rollback point was being erased before it could be read: restarting an app cleared the whole note, and restarting after an update is the default. It is now retracted only by whoever wrote it. And Update All could refuse to restart an app — telling you its own updater had a version staged and waiting — when nothing was staged at all: it compared what was on disk now against a build number recorded before the install, so an app whose version name had not changed looked like a conflict.

An app that already downloaded its own update is no longer asked to download it again. Plenty of apps fetch their next version quietly in the background and hold it until you next quit them — that's the "relaunch to update" state you see inside Claude, TablePlus and others. DuoUpdater has always recognised that state in Electron apps and offered you Relaunch instead of an Update, because the bytes are already on your disk. Apps built on Sparkle, which is most of the rest, were invisible to it: TablePlus sat there with a 133 MB download and an unpacked 382 MB copy of 26.9.11 in its cache, while its row offered to fetch 26.9.11 for you all over again. Those apps are now recognised too — the row offers Relaunch, and nothing is downloaded twice.

One thing it deliberately won't do is offer Relaunch for an older build. An app can be holding a version behind the one you have, which happens when a vendor releases to some people before others and DuoUpdater installed the newer one first. Relaunching there would quietly move you backwards, so the row doesn't offer it — and DuoUpdater won't restart the app for you either, because that restart is the exact signal the app's own installer is waiting for.

Three ways an update could go wrong that a review caught before you did. An App Store update that failed, or that you cancelled, could bring the app back to life minutes later — you would quit it yourself and it would reopen, because DuoUpdater had noted "this app may need reopening" before anything had actually closed it. It now only reopens an app when a quit was genuinely asked for. Separately, the check added last release to stop an app's own updater undoing ours compared only the version name: a vendor that ships several builds under one version number slipped straight past it, which is the same failure it was written to prevent. It now compares the build number too. And an app's own updater is no longer assumed to be waiting just because a downloaded copy is sitting in its cache — Sparkle leaves those behind for ten days after an interrupted install, which could have left a Restart button that did nothing but explain itself.

Same changes as 0.3.51, reissued so it can actually reach you. 0.3.51 went out carrying the same internal build number as 0.3.50. That number, not the one in the version name, is what an update check compares — so anyone already running 0.3.50 was told they were up to date and never offered it. This release carries the changes below under a build number that is properly newer. If you are reading this on 0.3.51, nothing about the app changed between the two.

An update that kept coming back. ChatGPT would offer a new version, install it, restart — and a minute later the same update was waiting again. The install was never the problem: the new version really did land on disk. What happened next is that ChatGPT's own updater put a different one back. There are two lists involved, and DuoUpdater was reading the wrong one. The first is everything the vendor has published; the second is what the vendor is actually handing out to your Mac today, which can be an earlier build while a release is still rolling out. DuoUpdater was reading the published list — the same address ChatGPT itself is configured with, which is what made it look right — and offering you a build the vendor was still holding back. ChatGPT had meanwhile downloaded the build it was being offered and parked it, waiting for the app to close. Restarting to apply our update is what closed it. DuoUpdater now asks the same question ChatGPT's own updater asks, so the two agree on what the current version is.

Restarting an app no longer applies somebody else's update. That collision isn't unique to ChatGPT. A lot of apps download their next version quietly and install it the instant you quit them — the "relaunch to update" state you've seen in Claude, TablePlus and others. They'll wait hours for that quit; one was observed holding on for nearly seven. When DuoUpdater restarts an app to put its own update into effect, that restart is exactly the signal they're waiting for, and theirs runs second. Before restarting, DuoUpdater now checks whether anything is waiting, and what version it holds. If it's the same version — which is the common case, and harmless — nothing changes. If it's a different one, the app isn't restarted: the row says what's waiting and leaves the choice to you, rather than quietly swapping in a build you didn't pick.

An App Store update no longer leaves your app closed. Updating an app you have open through the App Store means closing it — the store's own installer quits it to replace the bundle, and doesn't open it again afterwards. DuoUpdater knew to reopen it, but only if you'd answered its own "quit to finish updating" prompt. Answer the identical prompt in the App Store window instead, or take an update that raises no prompt at all, and nothing remembered that your app had been open. It updated correctly, said "Updated ✓", and your app was simply gone — with no restart offered, because by then there was no running app to restart. What decides now is whether the app was running when the update started, which is the thing that was actually true, rather than which window you happened to click in.

豆包输入法 now gets checked, and shows its release notes. It was in the list — DuoUpdater looks inside /Library/Input Methods — but nothing anywhere knew where to ask about it, so the row sat blank forever and read like "nothing to do". It now reads the same endpoint the vendor's own download button reads, and compares the same build number the vendor versions by — so even a re-release that keeps the version name unchanged shows up, rather than passing as the version you already have. The release notes come from the feed the input method's own updater polls, laid out as a proper list rather than a link to a page that doesn't exist. As with 微信输入法, DuoUpdater will tell you a new version is out but will not install it for you: an input method is registered with the system by its installer, not merely copied into place, and quietly swapping the bundle is how you lose your personal dictionary.

Zed's release notes come back instead of an error. DuoUpdater asks GitHub about a lot of apps, and GitHub lets an unidentified caller ask only sixty questions an hour from one network — a budget every GitHub-hosted app on your machine shares. Zed's notes were the ones losing that race, and the panel showed a failure rather than the notes. Those requests now carry the GitHub token you saved in Settings ▸ GitHub, or the one the gh command-line tool already holds if you use it, which lifts the limit far out of the way. Without a token nothing gets worse: Zed's notes now come from a single, smaller source that covers both its channels, where before each one fetched its own web page.

Notion's release notes are about the app you have. They were being read from Notion's product announcements — the page that introduces features as they launch. Those posts are titled by feature, not by version, so nothing on that page ever lined up with the version number on Notion's row, and the notes for the update you were being offered were never there to find. They now come from Notion's own "What's new" page for the Mac and Windows app, where the versions are the ones you can actually compare against.

Some release notes were quietly incomplete, and no longer are. A release's final line could go missing from ChatWise's notes. Two of Postman's releases were cut off mid-sentence — both at the exact point where the text contained a quotation mark. Six apps' notes are now read from the same data the vendor's own site is built from, rather than picked out of the finished page, which is both sturdier and how those two ended up whole. HBuilderX is the one trade-off: its notes now come from the official release document, which covers the HBuilderX editor itself and not the bundled uni-app and uniCloud module logs, so its entries are shorter than before — the same releases, in the vendor's own words, minus the parts about other products.

Release notes that arrive inside an app's update feed are laid out properly. A good number of apps ship their notes as a small piece of a web page tucked inside the feed their updater reads. DuoUpdater used to hand that straight to the system's HTML renderer, which produced a serif font nothing else in the app uses, bullets indented into the margin, and — the part that mattered — a long list could be cut in half, with everything after the cut silently absent. Those notes are now read into the same list layout the rest of the panel uses: right font, right indentation, and nothing dropped. Where the markup is too tangled to be sure of, DuoUpdater leaves it to the old renderer rather than risk showing you part of a list as if it were all of it. TablePro benefits most — its notes had stopped being found at all.

Four more apps show their release notes properly, instead of an embedded web page. Alcove, Docker, Kiro and Waku each publish their notes somewhere a program can read them — Alcove and Docker on their own sites, Kiro as a feed, Waku on GitHub — but DuoUpdater was showing you the web page instead, which meant the vendor's fonts, the vendor's navigation, and no way to move between versions. They now read as proper entries in the list, the same as everything else. Kiro's feed covers three separate products; only the notes for the app you have installed are shown.

A release whose notes are written as sentences no longer vanishes. Some releases don't have a bulleted list of changes — they have a line or two of prose, sometimes only "no public-facing changes in this release". DuoUpdater understood lists and nothing else, so those releases were skipped entirely: if you happened to be running exactly that version, the panel had no entry for it at all, as though your own build had never shipped. They're kept now. Where the notes are laid out in a way that still can't be read cleanly — a table, mostly — the page is shown as before rather than half-converted into something misleading.

Antigravity IDE gets checked. It's a separate app from Antigravity, with its own version, and it was in your list but nothing ever checked it — the row just sat there with no version to compare against and no way to find out. It's checked now. DuoUpdater won't install it for you; it will tell you when a new version is out and where to get it.

DuoUpdater tells you when it updated itself, and what changed. It installs its own updates quietly, on purpose — it waits until you're away from the machine and swaps itself without asking, because a tool that interrupts you to talk about itself is getting in the way of the work it's supposed to protect. The cost was that you'd end up on a new version you never agreed to and never saw the notes for: every other app in your list has a "what changed" panel, and the one app that changed under you in silence was this one. Now the menu says so once, and opens its release notes. It's shown until you read it rather than for a set time — the update this is for is the one that landed while you were asleep. A fresh install doesn't get told it was updated, because it wasn't, and going back to an older build on purpose doesn't either.

And you can read those notes any time. The ✨ button at the bottom of the menu opens every release DuoUpdater has ever shipped, with the one you're running marked in the list.

Switching an app's update channel in that app is noticed straight away. Some apps let you choose between their stable and pre-release builds in their own settings — Tailscale, Fork, Surge, OrbStack, TablePlus, CleanShot, IINA, Alfred, DuoPaste. That choice lives in the app's own preferences, where nothing tells DuoUpdater it has changed, so switching from a beta track back to stable left the row still comparing you against the beta — and still offering it — until the next scheduled check, up to an hour later. DuoUpdater now re-reads that choice when one of those apps opens or quits, and when you come back to its own window, and re-checks just the app that changed.

Tailscale's Release Candidate track is one of the choices DuoUpdater understands. Tailscale publishes three: stable, release candidate, and unstable. Only two were known here, so a Mac opted into release candidates was quietly checked against stable instead, and reported up to date whenever a candidate build was newer than the stable one. All three are now checked against the track you actually chose.

Confirming a quit late no longer leaves the app updated but closed. Some apps guard their own quit with a dialog — Claude, for one, asks about an active conversation — and that dialog could land in the middle of a Relaunch. DuoUpdater rightly refuses to sit there while you decide (and it still won't force the quit past your unsaved work), but once it stepped aside it also stopped listening. Answer the dialog a minute later and the quit went through, the app's own updater swapped in the new version — and then nothing happened: some updaters deliberately don't reopen the app after installing, DuoUpdater was no longer watching, and you were left staring at an app that had simply closed. It now leaves a note for itself when it steps aside: if you do confirm that quit within the next few minutes, it waits for the update to finish landing and then brings the app back, in front if that's where it was. The note expires after a few minutes, so a quit hours later is just you closing the app, and stays that way.

The same late answer now works for a plain Restart. A save prompt could block the Restart button (and the automatic restart that follows a one-click Update) in exactly the same way, with a worse ending: the new version was already on disk, so when you finally dismissed that prompt and the app closed, DuoUpdater's next look around decided there was nothing left to restart and quietly dropped the badge too — an app closed, an update half-applied, and no trace that anything had been asked for. Answering a save prompt within a few minutes now finishes the restart it belonged to, and the app comes back on the new version.

App Store updates tell you they're waiting, and don't leave the app closed if you answer late. When the App Store finishes downloading an update for an app you have open, it asks for that app to be closed before it can install — and DuoUpdater waits, indefinitely and on purpose, for you to say when. Until now that request lived only inside the menu: if you never opened it, an update sat downloaded-but-not-installed all night, and background checks waited with it. It now also arrives as a notification with a Relaunch button, so you can answer it without hunting for the row. And if your app puts up a save prompt of its own after you tap Relaunch, answering it minutes later no longer strands the app closed — the update lands and the app is reopened, the same as if it had quit right away.

The list holds still while you are clicking down it. Every finished update used to re-sort the list on the spot: the app that just landed left the pending group, or picked up a Restart badge and jumped to the top, and everything below it slid up a row — under the pointer of anyone working down a list of Update buttons. Click the top one, go for the next, and the next one had moved. Now the order is held from the first click until the whole round is done: an app that finishes shows its confirmation in place, nothing else moves, and the list settles once at the end, when the finished apps drop away as they always did.

One less version number on a row that needs a restart. An app with an update waiting and an earlier update still needing a restart tried to print all three versions on one line — installed, available, and the older one still running. On four-part versions like Chrome's it did not fit, and the line was cut off exactly where the digits started to differ, so the part left visible said nothing at all. The line now sticks to the comparison that matters — what you have and what is offered — and the running version moved to the row's tooltip, where it fits.

No more Dock icon. DuoUpdater is a menu-bar app — everything it does starts from the icon up there — but it also held a Dock slot, and the only thing that slot ever did was open the same window the menu bar opens. It now runs from the menu bar alone. If you would rather keep the Dock icon, Settings ▸ General ▸ "Hide the Dock icon" turns it back on, and with it the badge that shows the number of pending updates; hidden, that count lives on the menu-bar icon instead.

An app you ignored is no longer checked at all. Ignoring an app hid its row, but every check still asked its vendor after it — one network request per app per round, spent on an answer nothing would ever be said about. On an unauthenticated GitHub budget of sixty requests an hour, those were requests taken from the apps you do watch. Ignoring now means not asking, which is what "hide an app from update checks" always claimed. Naming an app on the command line still checks it, and so does asking for hidden rows, since both are you asking about that app specifically. Skipping a version is unchanged and still checked — whether the version on offer is still the one you skipped can only be known by asking. And un-ignoring re-checks that app on the spot, instead of leaving the row blank until the next round comes due.

An app you ignored stops notifying you. An app that updates itself leaves a "Relaunch to apply it" reminder in Notification Center and repeats it every few minutes until you act on it. That reminder never consulted the ignore list, so an app you had ignored went on sending it — hidden in DuoUpdater's own list, still arriving every five minutes, with nothing on screen to explain where it was coming from. Ignoring an app now silences those reminders and clears any already waiting in Notification Center. Skipping a version does the same for that version.

A download no longer squeezes the app's name or its version off the row. While an app was downloading, the progress bar and its percentage claimed enough of the row that a long name wrapped onto a second line, a long date-style version was clipped at both ends to something unreadable, and at 100% the percentage itself broke across two lines. The row now measures what the name and the version actually need and fits the progress readout into what's left: the bar gives way to a compact ring, and the percentage stays. Nothing is given up until there is genuinely no room for it.

An update no longer looks like it fired twice. With one update pending, installing it briefly emptied the list: the row dropped out the moment the new version reached the disk, the "Everything is up to date" placeholder took its place and jumped the window's height, and then the row reappeared saying "Relaunching...". Nothing was actually wrong underneath — the app still had to be restarted to run the new code — but it read as though something had happened twice. The row now stays where it is from the click through to the relaunch. The step that used to announce "Done" while the app was still being restarted says "Installed" instead; "finished" is left for the confirmation at the end, where it belongs.

The workbench sidebar follows the arrow keys again. Holding an arrow key walked the selection off the edge of the list and left it there, moving through apps that were never drawn and not catching up when the keys stopped. The selected app stays in view.

Moving through that sidebar is quicker. Every keypress was re-deriving, once per app per row, a fact about the whole list that had not changed — on a machine with 124 apps that came to about fifteen thousand redundant filesystem-path lookups per keystroke. It is derived once now. Fast key repeat can still outrun the list; there is more to do here.

An App Store update says "Update", not "Get". When the background helper has not been approved there is no way to install an App Store update in place, so the row hands off to the App Store app instead — but the button for that read "Get", which is what the store says about an app you do not own yet. Every row that reaches it is an app you already have, with an update waiting. It says "Update" now, and when the helper is what is missing the tooltip says so, since approving it in Settings is what turns those updates into one click.

The action column lines up. A checkmark or a small badge at the end of a row was centred in its slot while a wide button sat flush against the row's edge, so the right-hand column read as ragged — and visibly out of line with the Homebrew row pinned below it. Everything ends on the same edge now.

WeType (微信输入法) now reports the version you actually have. Its version was being read off the name of the vendor's installer file, which turns out to carry the installer's version rather than the app's — the installer is a small downloader that fetches the real app separately, and the two numbers drift apart. DuoUpdater now reads the same manifest the vendor's own installer reads, so the version matches your copy and new releases show up when they ship. WeType still has to be updated with the vendor's installer rather than in place: replacing the bundle skips the input-method registration step and was found to lose settings.

A beta build can never arrive on the stable channel. The fix in 0.3.44 looks further back through an app's releases when the newest one has no Mac build attached. That wider search could also see the developer's beta and release-candidate builds, which the normal check never shows you — so an app that happened to publish a release without its Mac download could have offered you a beta. Nothing had actually hit this, and now nothing can: the wider search only ever considers finished releases.

Notion's release notes are readable again. Notion restyled its releases page and DuoUpdater could no longer pick the posts out of it, so the notes fell back to showing the raw web page. They render as proper entries again.

PureMac's updates are visible again. The developer publishes a separate command-line tool from the same place as the app, and its release was being read as if it were the app — as version 1.0.0, which looks older than what you have installed, so the app reported itself up to date and every real update stayed hidden. It now reads only the app's own releases.

An update that only ever existed for phones no longer sits in your list. Some apps share one version number across Mac, Windows, Linux and mobile, and sometimes a release goes out to the phones alone — the version number moves, but no Mac build is ever made. DuoUpdater was reading that as a Mac update, which left an update you could never install and that never went away. It now looks for the Mac download itself rather than trusting the version number, so those releases are correctly ignored. LocalSend was the app affected; its row now reads as up to date, which it is.

LocalSend installed from its own website updates in one click. Now that the right release is identified, its Mac disk image can be installed in place like the rest.

An App Store copy is never replaced with a build from elsewhere. A few apps are published both on the Mac App Store and as a download from their developer, under the same identity — LocalSend is one. Those are genuinely different builds, and the App Store's copy has to keep updating through the App Store. DuoUpdater now leaves those copies to the store instead of ever offering the developer's build over them.

Updates that the wider ecosystem never picks up get flagged for us. Our nightly recipe sweep could only tell whether an app's version could still be read, not whether the answer made sense for a Mac — which is why the LocalSend problem had to be spotted by hand. It now also compares against Homebrew, and raises a flag when we are reporting a version that nobody else has packaged long after it was published. Nothing about this is visible on your Mac; it is how this class of mistake gets caught by machine next time.

Package updates are read more thoroughly before they are opened. The check added in 0.3.41 asks an installer package where it installs, and refuses one that will not say. It was reading only the summary the package publishes about itself; it now also reads the package's own file list, which is what the installer actually follows. That means a package that keeps quiet in its summary is still understood instead of turned away. Every app that updates this way was re-checked against its real installer, and none of them changed.

A download link that stays broken now gets noticed. When a vendor's server has a bad minute, DuoUpdater waits it out rather than crying wolf — but that was letting a download link that had been broken for good slip by unremarked, because it looked the same as a bad minute on any single check. It now tells the difference: brief trouble is still ignored, trouble that lasts is flagged and fixed. Nothing changes on your Mac; this is about broken apps getting repaired sooner instead of quietly staying broken.

Release notes can't be pulled down to an insecure page. A vendor's notes page is loaded over a secure connection, but nothing stopped that page from redirecting itself to an insecure one. Now it can't.

Updating OneNote no longer installs the whole of Microsoft Office. OneNote's update was being fetched from Microsoft's combined Office installer, which puts Word, Excel, PowerPoint, Outlook and OneDrive on your Mac along with it. If you keep OneNote on its own, that was five applications you never asked for. It now downloads Microsoft's standalone OneNote update, which installs OneNote and nothing else.

Package updates are checked more strictly before they are opened. An update that arrives as a macOS installer package now has to say where it installs, and has to name the app you are updating. A package that will not say is refused rather than let through. Every app that updates this way — Office, Edge, Teams, OneDrive, Tailscale, ToDesk, AweSun and the rest — was checked against its real installer first, so this should never fire on a normal update.

Telegram's one-click update stops vanishing when Telegram's servers hiccup. Working out where to download Telegram from needs one extra request to their servers, and those servers were intermittently refusing it — a few minutes at a time, then fine again. When that happened the update was still detected, but the Update button quietly disappeared for that check and you had to go to the website yourself. DuoUpdater now retries before giving up, so a brief hiccup no longer costs you the one-click install.

Package installers are checked against the app they claim to update. Some updates arrive as a macOS installer package, which runs with administrator rights the moment you confirm it. Until now the only check was that the package came from the same developer as the app being updated — which would have let any package from that developer through, not just the right one. DuoUpdater now also reads where the package says it will install and refuses it if that is not the app you are updating. Nothing changes for a normal update; this only ever fires on a package that does not belong.

Release notes only open over a secure connection. The notes pane loads a vendor's own page for some apps, and one of those pages was still being fetched over plain http. Those pages now have to be https, and a handful of other malformed addresses are refused outright rather than loaded.

Fewer apps can go silently missing after a vendor renumbers. Zotero's jump to 10.0 in 0.3.37 exposed a whole class of this: an app disappears from the update list, with no error, because the vendor changed how many numbers are in its version. Twenty-one apps — among them VS Code, Discord, Obsidian, Figma, WhatsApp and GIMP — no longer depend on that staying the same. The rest were checked and deliberately left alone, because for those a looser check would risk reading the wrong number off the page.

The download percentage no longer spills out of its row. While an update downloaded, the number next to the progress bar sat too far right — clipped by the edge of the list instead of lining up with everything else in the column — and there was more empty space between the bar and the number than there needed to be. Both are fixed; the percentage still holds a fixed width, so the row stays steady as it counts up to 100%.

Zotero 10 shows up as an update again. Zotero numbered its new major release 10.0 — two numbers where every previous release had three — and DuoUpdater's check for it quietly stopped recognising the version. Nothing looked wrong: no error, no failed check, Zotero simply never appeared in the list, so anyone still on 9.0.6 was never offered the upgrade. It is recognised again, and the one-click install is unchanged.

A package update that leaves the old copy running now offers to restart it. Some updates install as a package handed to macOS's own installer, and when that finished it left the previous copy still running the old version — with no prompt, so you had to notice and quit it yourself. DuoUpdater now spots that and offers a Restart, the same as it already does for other kinds of update. It only offers one when a copy that was open before the install is genuinely still running the old code — if the installer (or you) already relaunched the app, or it wasn't open, nothing is shown.

WeChat DevTools (微信开发者工具) is checked for updates now — Stable, RC and Nightly each on their own track. It used to sit there as "unknown": since version 2.02 the app reports Electron's stock identity on disk, calling itself version 36.6.0, and all three channels look identical from the outside. DuoUpdater now reads the real version and channel out of the app's own configuration, so whichever track you installed is the only one you are offered — a Nightly install is never handed a Stable build, or the other way round. Updates install in one click, and the release notes for the exact build show up in the window.

App Store updates stop breaking every time DuoUpdater updates itself. Replacing the app left the background helper from the previous copy running, and macOS then never started the new one — so the helper looked switched on while every App Store update failed talking to a copy that no longer existed. Only a restart cleared it. The helper now steps aside when it has been idle for a minute, which means a replaced app heals itself by the next update. For a Mac already in that state there is a Restart Helper button in Settings → Diagnostics and on the update that failed; it asks for an administrator password and takes effect immediately, no restart.

Diagnostics can tell you whether the helper actually works. "Enabled" only ever meant "switched on", and the difference between that and "answering" showed up as a failed update. A Check button now says which one you have, in those words.

Backups: choose what to delete. "Clean Up Now" only removed backups belonging to apps you had uninstalled, so on most Macs it deleted nothing and said nothing while the size stayed put. It now opens a list — every backup with its app icon, the update it would roll back, its size and date — with everything selected and anything you want to keep unselectable. The button says how much the selection frees. Backups whose records had gone missing were previously counted in the total but impossible to see or remove; they are listed too, marked unusable.

A silent self-update no longer leaves DuoUpdater sitting in front of you. When it applied its own update in the background, macOS brought back the windows that had been open — and bringing a window back also brings the app forward, so an update nobody asked for landed on top of whatever was being worked on and stayed there. DuoUpdater now notes which application was in front before it replaces itself, and gives the front back to it on the way in. Windows still return exactly as they were.

Docker updated to the version it said it would. Docker publishes its releases in an order that puts an older one first, and DuoUpdater read the newest version from that list while taking the download link from the top of it — so it fetched 574 MB, kept a backup, installed 4.86.0 over the 4.86.0 already there, and went on offering 4.87.0. Downloads are now matched to the version each entry declares for itself, whatever order a vendor lists them in.

An update that changed nothing is no longer reported as done. The check that runs immediately after installing already knew Docker hadn't moved; it was overruled by a success message and an "Updated ✓". When an update we applied ourselves leaves the app exactly as it was, that is now shown as the failure it is, naming what was installed and what is still on disk.

Silent self-updates no longer wait for an empty screen. With the switch turned on, DuoUpdater would hold its own update back while any of its windows were open — which, for a window people leave open, meant waiting until the app was quit. Open windows no longer delay it (macOS brings them back after the restart), while a DuoUpdater you are actually using still does: it waits for the keyboard and mouse to go quiet before restarting itself.

Installing DuoUpdater's own updates silently now actually is silent. The switch introduced in 0.3.30 downloaded the new version in the background and then still asked before applying it — the one thing it was meant to spare you. It now applies the update itself, at a moment when doing so interrupts nothing: no check or install running, nothing waiting to be relaunched, no DuoUpdater window open, and you working in another app. It restarts itself there, without a prompt. Until such a moment arrives it simply waits, and if none ever comes the update is still applied when you quit — so the wait can delay a version, never lose one. Leaving the switch off is unchanged: you are asked, as before.

An App Store update no longer blames you for a permission you already gave. When DuoUpdater is replaced while it is running — by its own update, or by a rebuild during development — the previous copy of its background helper keeps holding the slot the system reserves for it, while macOS still reports the helper as switched on. Every App Store update then failed with a red line telling you to go turn it on in Login Items, where you would find it already on, and the button offered beside that message quietly did nothing. That state is now recognised and named for what it is, along with the one thing that clears it. DuoUpdater no longer tries to repair it by re-registering the helper: that was measured to switch the background item off and leave it unable to be switched back on.

DuoUpdater can now update itself without asking. Settings → Updates has a new switch for installing DuoUpdater's own updates in the background, taking effect the next time it restarts. It stays off unless you turn it on, and the prompt-and-wait behaviour is unchanged for everyone who leaves it alone.

Its own updates are noticed within the hour. DuoUpdater checked for its own new versions once a day, so a release could sit unseen for most of a day. It now checks hourly.

Eight more AI desktop apps are tracked. OpenCode Desktop and OpenChamber follow their GitHub releases; Wispr Flow, Granola, Comet, Windsurf, AionUi and Msty are read from their vendors' own version endpoints.

Installer packages stay the same package from verification to macOS Installer. DuoUpdater now seals the selected installer before closing or replacing any existing Installer window, checks it again immediately before opening, and refuses the hand-off if another local process changed the file in between. This preserves Sparkle's signature guarantee all the way to the package you see in Installer without making the menu-bar UI pause while large packages are checked.

Multi-installer disk images handle more real-world package names without guessing. Versioned beta, release-candidate, Apple Silicon, and universal package names are recognized when they identify one unique product, while similarly named helpers and sibling products remain excluded. Older bundle-style macOS installer packages are supported by the same integrity checks.

Failed installer downloads are cleaned up immediately. A bad signature, unreadable disk image, cancelled download, or rejected package no longer leaves a full installer sitting in temporary storage until the next day's cleanup.

Signed Sparkle updates that arrive as installer packages now work. A few apps publish a perfectly valid, cryptographically signed .pkg instead of an app archive. DuoUpdater offered those updates, downloaded them, and then tried to unpack the package as though it were a zip — an update that could never finish. They now go to macOS's own Installer, after DuoUpdater verifies both the Sparkle signature on the download and the installer identity inside it.

A disk image containing several installers is no longer allowed to make a guess. Some vendors put a main installer, helpers, and sibling products in one image. Matching on a fragment of the filename could pick a helper simply because its name contained the app's name. DuoUpdater now opens a package only when it is the sole choice or can be identified uniquely; otherwise it stops and leaves the decision to you instead of presenting the wrong installer.

The App Store helper is more tightly scoped to your login session. The privileged helper now takes the account identity directly from macOS's authenticated XPC connection and refuses a request whose claimed user does not match. Normal App Store updates behave exactly as before; the change closes off a signed client from redirecting the helper into another user's session.

Release notes show their formatting instead of its punctuation. Notes that come from a project's GitHub release were rendered exactly as written — **bold** with the asterisks, links as [text](url). Bold is now bold and links are links. This affected every app whose updates come from GitHub, which is most of the open-source ones.

Arrow-keying down the app list no longer crawls. Holding an arrow key felt like moving one row at a time through mud, and long release notes made it worse. Three things were doing it: a permission check on every app in the list ran again for every row drawn; the notes for whichever app you passed through were re-parsed on each keypress; and a long set of notes — one project's runs to 54,000 characters — was laid out in a single pass, which froze the window for about two seconds. The check is now computed once per list, parsed notes are kept, and long notes are laid out only as far as you have scrolled. Worst measured stall went from ~2.1 s to under 0.6 s, and what remains is the deliberate pause before the detail pane catches up rather than a freeze.

Input methods are never updated by replacing the app, and 微信输入法 (WeType)'s one-click from 0.3.25 is withdrawn. Settings were lost on a Mac during the work that added it. What we can show is that the copy in the protected system folder was never actually replaced by DuoUpdater — but an older copy of the input method was installed and launched elsewhere on that machine while testing, inside the window where the settings were rewritten. Nothing here is proven, and an input method's dictionary is not something to test a theory on: WeType now reports its version and sends you to the vendor's installer, which registers the input source with the system — a step that replacing the app bundle skips, and the likely reason that Mac then appeared twice in WeType's own device list.

The refusal is not specific to WeType: DuoUpdater no longer offers a one-click for anything installed as an input method, whichever vendor it comes from. Those apps still report their versions and link out.

Three more apps now report their updates, and all three install with one click. Hidden Bar, XQuartz and EasyFind were sitting in the list as a grey "unknown".

Hidden Bar is the interesting one: it ships with an update feed configured, so from the outside it looked like it was already covered. The feed answers, and is well-formed, and contains no releases at all — which is indistinguishable from a healthy feed until you look inside it. Its version now comes from its release tags instead. EasyFind ships no updater at all, so a copy installed from the vendor's site had no way to learn about new versions.

XQuartz installs through the system installer rather than by replacing the app, because it is not just an app: it lays down a whole X11 stack, and swapping only the app bundle would leave the rest at the old version. macOS asks for the administrator password itself, as it does for any package.

Thirteen more apps now report their updates, twelve of them with one click. GIMP, MongoDB Compass, Meld, Emacs, Tor Browser, Zotero, GrandPerspective, TigerVNC, qBittorrent, Opera, LibreOffice, pgAdmin 4 and Telegram Desktop were all sitting in the list as a grey "unknown" — installed, with nothing to say about them. Each was worked out by downloading the vendor's actual build and reading its identity out of it, so a one-click only appears where the download is signed by the same developer as the copy you already have. The exception is qBittorrent: its own build isn't signed by an identified developer at all, so it reports its version and sends you to the project's page.

Opera, LibreOffice and pgAdmin 4 nearly joined that exception. All three publish nothing but a directory listing, and listings sort alphabetically — "100" comes before "99" — so the newest release is not the first one on the page. Reading the version was never the problem; building a download link was, because the obvious way to build one would have picked whichever release happened to be listed first. They now download the release that was actually compared. That is the one mistake a signature check cannot catch for you: an older build of the right app, signed perfectly.

1Password and Inkscape now install with one click too, and both were previously written off. 1Password's official download turns out to be a small installer program rather than the app — signed and notarised by 1Password, so every safety check passes it, and installing it would have replaced your password manager with its own installer. DuoUpdater now fetches the package that installer itself downloads. Inkscape's download page hands out its file through a one-time link that changes with every release; the same file also sits at a plain, predictable address, which is what gets used.

微信输入法 (WeType) now installs with one click. It lives in a folder only an administrator can write to, which is why it used to only report its version. It now goes through the same administrator prompt as any other app in a protected location.

Release notes for Opera, Inkscape and 1Password. Opera publishes one page per major version, Inkscape one wiki page per release, and 1Password a feed — all three now render as proper change lists in the app instead of a link out. 1Password's version and its notes both come from that feed now, which is a published interface, rather than from scraping the page beside it.

Discord's version check works again. Discord moved its downloads to a different server and the check was still looking at the old address, so DuoUpdater quietly reported "no version" for Discord Stable while everything else kept working. It now keys off the part of the address that names the release channel, which is the part that actually has to be right.

Fifteen more apps now report their updates, and all but one install with one click. Rancher Desktop, Cherry Studio, RedisInsight, Upscayl, WailBrew, Wave Terminal, Lens, Termius, Unity Hub, iStat Menus, Inkscape, Google Gemini, Antigravity, AnyDesk and Kiro were all showing as a grey "unknown" — installed, with nothing to say about them. Each one was worked out by reading the vendor's own build rather than trusting a download page: the version now comes from wherever that app's own updater looks, and a one-click only appears where the download is signed by the same developer as the copy you already have. Three of them (Google Gemini, Antigravity, Kiro) publish nothing a download page can be scraped for; their real update services answer the same questions their own updaters ask, so that is what DuoUpdater asks too.

AnyDesk in particular was written off and shouldn't have been. Its download and changelog pages both refuse anything that isn't a person with a browser, so an earlier sweep concluded the app was unreachable. The plain-text changelog on the same server answers fine — and it is what AnyDesk's own Homebrew entry has always read.

Updating an app in a location that needs an administrator password now asks, once. Most apps live in /Applications, which you can write to; a few — input methods, for one — live where only an administrator can. Those used to show an Update button that could never work. Now the button asks for the password, and if you dismiss that prompt DuoUpdater takes the hint: the row switches to Open and stops asking on every release. "Ask for administrator access again" in the row's right-click menu brings the button back. The choice is remembered for that copy of the app specifically, so declining for one install doesn't silence another.

An up-to-date Xcode beta no longer claims the vendor is behind it. Under "Show all", a row whose vendor has fallen behind what you have installed shows a muted note saying so — you're ahead, nothing to do. Xcode was getting that note while sitting on exactly the build Apple was offering: it publishes a build number plus a human label ("27.0 beta 5"), and comparing that label against the plain "27.0" the bundle reports made a release look newer than its own beta. The note now settles on the build whenever both sides have one, so the same release is recognised as the same release however it is labelled. A vendor that has genuinely fallen behind is still called out.

44 more apps now report their updates. Apps that publish on GitHub but ship no update feed of their own used to sit in the list as a grey "unknown" — DuoUpdater could see them installed and had nothing to say about them. Bruno, UTM, kitty, KeePassXC, Godot, Bitwarden, VSCodium, draw.io, Podman Desktop, Anki, Raspberry Pi Imager, LuLu, MarkEdit, Clash Verge, Freelens, Tabby, Espanso, Moonlight, SwiftBar, Sequel Ace, balenaEtcher, DB Browser for SQLite, OpenLens, Headlamp, OpenMTP, Goose, Caffeine, noTunes, KeepingYouAwake, MiddleClick and a dozen more now show a real version, and 36 of them install with one click like any other app. Which ones was decided by downloading each vendor's actual build and reading the identity out of it, so a one-click only appears where the download is signed by the same developer as the copy you already have. Seven — Alacritty, Flameshot, MarkText, darktable, OWASP ZAP, BlueBubbles and Wine — publish builds Apple hasn't notarised, so those report their version and send you to the vendor rather than installing anything. LocalSend is report-only for a different reason, corrected here after this version shipped: its builds are notarised, but its newest release attaches no macOS download at all, so there is nothing to install until the project starts publishing one again.

Apps that already carry a Sparkle feed needed nothing: they were checked as part of this sweep and were already working, which is why names like Rectangle, Maccy, iTerm2 and Telegram aren't in the list above.

An update that your Mac couldn't run is now refused rather than installed. Where a developer publishes one download per processor, DuoUpdater picks between them by the file's name — and names are not always honest: three of the apps above ship an Apple silicon build under a name that says nothing about it, or says the opposite. Before an app is replaced, its new version is now checked against the processor in your Mac, read out of the program itself instead of its name. If it can't run here, the update stops and your working copy is left exactly as it was. Nothing about a normal update changes; this is the case that used to end with an app that no longer opened.

An unanswered permission prompt no longer leaves the update check hanging. To tell a TestFlight build apart from an App Store one, DuoUpdater reads TestFlight's own database, and macOS keeps that behind the "access data from other apps" permission. Until that was answered the read didn't fail — it waited, indefinitely, for a prompt that might be sitting behind another window or might never be answered at all, and the scan behind it simply never finished. Nothing timed out and nothing said why. It now waits a few seconds and then carries on without TestFlight's side of the story; the only thing missing in the meantime is whether those particular apps came from TestFlight, and it sorts itself out on the next scan once the permission is granted.

Homebrew updates work behind a proxy. If your Mac reaches the internet through a proxy, upgrading brew packages failed with curl: (28) Failed to connect while every other update went through fine. Homebrew shells out to curl, which — unlike the rest of DuoUpdater's networking — doesn't read the proxy you configured in System Settings; it only reads proxy environment variables, and an app launched from the Dock or at login has none. DuoUpdater now passes your system proxy settings down to Homebrew itself. Nothing changes on a machine with no proxy configured, and a proxy you've already exported in your own shell still wins.

Claude's updates now show up while they're still rolling out. Anthropic releases Claude in stages: a build goes to a fraction of Macs at a time, and the public download page only catches up at the end. DuoUpdater was reading that public page, so for the whole of a rollout — most of a day, in the case of 1.30096.5 — it told you Claude was up to date while Claude itself had already quietly downloaded the new version and was waiting for a relaunch. It now also asks the same endpoint Claude's own updater asks, which answers for your Mac specifically, and offers whichever of the two is further ahead. Nothing about which build you're offered has changed: it is either the public release or the one your Mac was already allocated. Claude also gains real publication times, so its releases now appear in the Release Log with the moment Anthropic shipped them rather than an estimate.

A superseded package update no longer leaves its window sitting there. Updates that go through macOS's own installer — Microsoft Office, AweSun, ToDesk — open an Installer window and then wait for you. If you left one open and a newer release came along, installing that one opened a second window, and they stacked up. The older window is now closed once its replacement is ready, and its download cleaned up with it. A window that's mid-install, or asking for your password, is left strictly alone.

duo says which copy is which when two apps share a name. Naming an app that is installed twice — two Xcode betas, say — printed both candidates as a bare "Xcode" and told you to name one exactly, which matches both again. The listing now carries each copy's version, and when the matches genuinely share a name it asks for the path instead of repeating advice that cannot work.

"Open download page" no longer downloads a file. On apps whose page DuoUpdater knows — ToDesk and UU Remote among them — that button handed your browser the installer package instead of opening anything: the link it used was the same one the updater downloads from, so clicking it started a download you didn't ask for. The page and the package are now kept apart, and the button opens the vendor's actual download page. Where a source only ever publishes a package and no page at all — a bare Sparkle feed — there is now no button rather than one that downloads something.

The app list responds to the arrow keys again. Opening the workbench window left the keyboard focus nowhere in particular, so ↑ and ↓ did nothing until you clicked a row — and after clicking into the release notes on the right, or switching to another app and back, they stopped working again. The list now takes the keyboard when it opens and takes it back at the points it used to lose it. Typing in the search box is untouched: a search you've started keeps the caret.

The Brew section starts collapsed. Casks and command-line formulae are a side channel for most people, and having that tree open by default pushed your actual apps up the sidebar every time the window opened. It now starts closed and remembers however you leave it.

Xcode betas and release candidates are now detected, and two copies can be told apart. Xcode was a grey "v27.0" with no update information at all, and if you keep more than one build around — a current beta beside the previous one — they were indistinguishable: same name, same version, same icon. Each row now reads its real build, so an update shows as "27.0 beta 1 (27A5194q) → 27.0 beta 5 (27A5237l)". Which track a copy belongs to is worked out from Apple's published builds rather than guessed from what you named the folder, and you'll only ever be pointed at something at least as finished as what you have — a beta can be superseded by a beta, a release candidate or the finished release, never the other way round. Updating still means going to Apple: the downloads need you signed in with your Apple ID, so the row links to Apple's download page and its release notes.

Cursor's release notes are shown properly instead of an embedded web page. Cursor writes its changelog as dated posts rather than numbered releases, so the notes pane fell back to loading the website. Each post is now shown as its own entry — its date, its headline and its changes — the same as every other app with readable notes.

duo check now shows what changed when the version number doesn't. Updates that keep the same version and only move the build — Surge, the JetBrains previews — printed as "6.9.0 → 6.9.0" on the command line, which was accurate and told you nothing. It now shows the builds, matching what the menu bar has always shown.

Apps that ship their own updater are now updated directly by default. These are the ones like Chrome, VS Code, Cursor and the Electron apps — and because they are also the apps you tend to leave running all day, the old default of stepping aside while they were open meant they were almost never updated at all: the row offered to open the app and left the rest to you. DuoUpdater now downloads the vendor's own installer and applies it whether or not the app is running, then quits and relaunches it so the new version takes effect. Anything that installs background components alongside the app — Tailscale, Office — ships a package that macOS's own installer handles, so those pieces are still put in place properly. If you would rather nothing was touched while an app is open, Settings → General → Self-updating apps still has the old behaviour, and changing it back does not affect anything already installed.

An app whose developer changed its update signing key can be updated again. Apps that update through Sparkle sign each release with a key, and the copy you already have carries the matching public key to check it against. If a developer generates a new key and ships it without a hand-over release signed by the old one, that check fails for everybody — the app's own updater is just as stuck as DuoUpdater was, and the update sits there refusing to install with a signature error. DuoUpdater now recognises that specific situation: when the new download carries a different key of its own and the release was signed with it, it stops trusting the signature and falls back to the same checks it uses for apps that publish no signature at all — the download must be validly signed by Apple's developer certificates, and by the same developer as the app it is replacing, for the same app. A download that fails any of that is still refused, as is a bad signature that isn't explained by a key change. Mirage Beacon 1.3.0 was the first to hit this.

Update All no longer says a running app is finished before its restart. When an update had already replaced an app on disk but Update All was still busy with other installers, the row briefly showed a green checkmark and disappeared even though the old version was still running. The app now stays visible with its running and installed versions, explains that it is waiting for the batch restart, and offers Restart now. The completion checkmark appears only when the update is actually in effect.

Apps installed by a .pkg can now be rolled back. DuoUpdater keeps a copy of the previous version before it updates an app, so a bad update can be undone. Apps that install through macOS's own installer — Microsoft Office, AweSun, ToDesk and the like — never got that copy: the rollback was skipped for them entirely, so the one kind of update DuoUpdater can't watch land was also the one you couldn't back out of. They're now backed up like everything else.

Rollback no longer refuses apps that write inside their own bundle. Some apps keep working files in amongst their own program files — ToDesk stores its settings database and logs there, and doing so breaks the seal Apple puts on an app. DuoUpdater checked that seal before restoring a backup, so for those apps it declared a perfectly good backup damaged and refused to put it back. It now checks the copy against a fingerprint taken when the copy was made, which is the thing that actually matters: that what's being restored is exactly what was saved. Tampering with a stored backup is still caught, and still refused.

When a backup isn't possible, it says so instead of failing quietly. A few apps keep program files that your account simply can't read — EasyConnect is one — and no copy of those can be made. Rather than attempting it and reporting a failure part-way through an update, DuoUpdater now checks first, tells you which file is in the way, and updates anyway; you just don't get a rollback point for that one app.

The same app no longer appears several times over. Some apps leave a dated copy of themselves behind every time they update — DuoPaste, for one, parks a DuoPaste.backup-20260716-183428.app next to the real thing on each self-update. Those copies are complete, working app bundles as far as anything on disk can tell, so DuoUpdater listed each one as its own app: three identical DuoPaste rows, each offering the same update. Worse, taking one of those offers would have installed the new version into the backup, leaving the app you actually use untouched and creating another stray copy. Backup and duplicate bundles are now recognised for what they are and left out of the list, as are exact clones of an app found in two places. Genuinely separate installs that happen to share an identity — Firefox alongside Firefox Beta, two versions of Android Studio kept side by side — still each get their own row.

The list now reliably notices apps appearing and disappearing. DuoUpdater watches your Applications folders so that an app updating itself in the background, or one you drag to the Trash, is reflected within a few seconds. That watch could quietly stop working — nothing crashed, nothing was reported, it simply stopped hearing about changes, and the list then went stale until you reopened the window. It's now rebuilt periodically and after your Mac wakes from sleep, with a fresh scan each time, so a watch that dies recovers on its own instead of staying dead for the rest of the session.

An app that gets restarted after an update no longer jumps in front of what you're doing. When DuoUpdater updates an app that's currently running, it quits and reopens it so the new version actually takes effect. Reopening it also pulled it to the front — so an update to something sitting quietly in the background could drop a window on top of the thing you were typing into. The app's position now survives the restart: whatever was in front comes back in front, and whatever was in the background comes back in the background, still there and still updated, just not in your way. The same applies to apps DuoUpdater reopens after an App Store update. Apps that weren't running at all are, as before, updated on disk and left closed — updating an app never starts it up.

Fixes AweSun's update failing with "the server returned HTTP 404". DuoUpdater worked out where to download AweSun's installer by building the filename itself from the version number. Oray then renamed the file — the same 16.6.0 build, one letter's difference — and every attempt at the update hit a dead link. It now takes the filename straight from Oray's own download listing rather than guessing at it, so a future rename won't break it again.

An installer you've already downloaded no longer downloads again. Some apps update through an installer package that macOS opens for you to confirm. If you closed that window without finishing — or quit DuoUpdater and came back — the row went back to offering "Update", and taking it fetched the whole package a second time. ToDesk's is 375 MB. The download was on your disk the entire time; nothing was pointing at it. Those rows now offer "Install" instead, which just re-opens the file you already have. If the installer window is still open it comes forward rather than opening a second one, and the offer stands until either the download is gone or a newer version comes out — at which point the old package would be the wrong one, so the row goes back to a normal "Update".

Homebrew packages that aren't apps now show up. DuoUpdater tracked outdated Homebrew formulae, and left casks alone on the grounds that a cask installs an app, which already gets its own row. That holds right up until a cask installs no app — a command-line tool like codex, a font, a driver. Those had no row anywhere: nothing for the app list to find, and not a formula either. codex sat three versions behind without a word. They're now part of the Homebrew panel, which reads "packages" rather than "formulae" to match. Casks that do install an app are still managed per-app exactly as before, and apps that update themselves are still left to their own updater.

"Update All" now includes apps that install from a package. A handful of apps — ToDesk and AweSun among them — ship their update as an installer package rather than something DuoUpdater can swap into place on its own. Those were quietly left out of "Update All" and had to be updated one row at a time; if such an app was the only other update pending, the button disappeared altogether rather than acting on just one. They're now part of the batch, and they run at the very end: everything that updates unattended finishes first, so nothing opens a window or asks for your admin password until the rest is already done. One caveat worth knowing — DuoUpdater can't tell when macOS's installer has finished, so if two package updates come up in the same batch, both installer windows open one after the other rather than waiting in line.

Fixes updates going unnoticed for days at a time. DuoUpdater re-uses the answers it gets from each app's version feed so it isn't re-downloading the same file every few minutes. The problem was how long it trusted a stored answer: when a vendor's server doesn't say how long its reply stays valid, macOS guesses — and it guesses longer the longer that feed has gone unchanged. So the very feeds that had been quiet for a while were exactly the ones DuoUpdater stopped re-reading, and a new release could sit there for days with the app still reporting "up to date" and no error to show for it. Every version check now always asks the server whether anything changed, while still skipping the download when nothing has. OrbStack 2.2.2 is the release that surfaced this; the same blind spot applied to most apps checked directly against their vendor, including Chrome, Cursor, Claude, ChatGPT, Warp, Spotify, and Visual Studio Code.

Homebrew-managed apps no longer get stuck at the version they were on when DuoUpdater started. The catalog DuoUpdater reads to learn the latest version of a Homebrew app was loaded once per launch and never refreshed, which is invisible if you quit the app daily and wrong if you leave it running for weeks. It now refreshes periodically. As a bonus, machines with no Homebrew casks installed no longer download that 5 MB catalog at all.

Uses less memory and does less work in the background. This release is entirely under the hood — nothing about what DuoUpdater does has changed, only what it costs to leave running. Every update it downloaded used to leave a small amount of memory behind that was never reclaimed; harmless once, but it adds up over the weeks a menu-bar app tends to stay open. Separately, while the main window was open DuoUpdater re-read every installed app from disk every 15 seconds and started a system process each time to see what was running — that now happens every three minutes, since the filesystem watcher already notices a real change the moment it happens. Recording the release history after each check also used to save its file once per app rather than once per check, and release notes could be fetched more than once when the same page was already on its way in.

No more beachball while an app is relaunching. Clicking Update on another app while one was being quit and relaunched could freeze DuoUpdater for a moment — the spinning rainbow cursor, an unresponsive window, a click that seemingly did nothing. Relaunching an app now happens in the background instead of on the interface, so the rest of the list stays live and clickable throughout. The same freeze could show up when opening an app from a row's right-click menu, or when handing an update off to an app's own updater; both are fixed too.

Backups from uninstalled apps are now cleaned up automatically. DuoUpdater keeps one backup of an app's previous version so an update can be rolled back. Backups for apps you've since uninstalled or moved were never reclaimed and could quietly pile up gigabytes of disk space over time. They're now deleted automatically during the regular update check. Settings shows how much space backups are currently using, with a toggle to turn off the automatic cleanup and a "Clean Up Now" button to run it on demand.

JetBrains Toolbox apps no longer show a stuck or incorrect "update available." Version checks for Toolbox-managed apps (IntelliJ, Android Studio, Fleet, Air, and others) now always ask live rather than sometimes falling back to a local cache that could never actually report a new version — it fixes both a status that lingered after Toolbox had already installed the update, and one that never appeared in the first place.

Claude Desktop's release notes are now shown in DuoUpdater. Update entries for Claude Desktop now include Anthropic's own per-version changelog instead of a generic notice.

Update All now also relaunches apps that were only waiting on a restart. If an app had already downloaded its update and just needed a relaunch to finish — Claude, for instance — clicking Update All used to skip it, leaving a stray "Relaunch" button behind. It now relaunches those too, in the same pass, whenever automatic restart-after-update is on.

App Store updates recover from a receipt hiccup instead of just failing. Occasionally a Mac App Store update downloads in full but the very last install step trips over a "receipt" error — a transient App Store glitch that a second attempt usually clears. DuoUpdater now retries once automatically. If it still doesn't take, the row offers an "Open App Store" button to finish the update from the App Store's Updates page, instead of leaving a raw error on screen.

ToDesk update detection fixed. A change to ToDesk's download page stopped DuoUpdater from reading its latest Mac version, so ToDesk updates went unnoticed. Detection now reads the version reliably again.

App Store updates no longer show a scary error for an app that's already up to date. If the Mac App Store had quietly updated an app in the background — TestFlight, say — DuoUpdater's row could go stale and, on Update, try to reinstall the version that was already there. macOS's installer rejects that with an alarming red "The upgrade failed", even though nothing was actually wrong. DuoUpdater now confirms an App Store app really is behind before reinstalling, and treats a no-op reinstall as "already up to date" — settling the row quietly instead of showing an error.

App Store updates now ride out network hiccups. A brief connection drop mid-update — a flaky link, or a proxy resetting the connection — used to fail an App Store update outright with a "could not connect to the server" error. Those updates now retry automatically a few times before giving up, so a momentary blip no longer strands an update that a second attempt lands cleanly. Clicking Update again after a failure also clears the old error immediately, instead of leaving it on screen next to the spinner.

Apps that update themselves clear from the list faster. When an app like Chrome finishes updating itself in the background while an App Store update is running, its "update available" row now clears promptly — it no longer lingers until the rest of the queue finishes.

One-click updates for four more apps. HBuilderX, JetBrains Toolbox, and Microsoft Edge's Beta and Dev channels now update in place with a single click, instead of only telling you that an update exists. HBuilderX also now reads its version straight from DCloud's own release feed, so it picks up new builds sooner and more reliably.

Self-updating apps stay in their own lane. A running app that ships its own Sparkle updater is now handed off to that updater — the same courtesy DuoUpdater already gave other self-updating apps — instead of being replaced underneath it, unless you've chosen "Always replace" in Settings.

Fixes

  • The running-app dot and "Relaunch" badge no longer briefly lose track of an app right after an in-place update, when macOS keeps its process pinned to the temporary swap location for a moment.
  • When you restart an app yourself after it updated in the background, the "Relaunch" badge now clears the moment the app comes back up — instead of lingering until the next background check.

Fixes

  • When you restart an app yourself after it updated in the background, the "Relaunch" badge now clears the moment the app comes back up — instead of lingering until the next background check.

See when your apps actually ship. DuoUpdater now keeps a Release Log: a running timeline of every release the apps you track put out, each stamped with its publish time. Open it from the clock icon at the bottom of the popover.

Release-habit heatmap. A new Patterns view charts releases by weekday and hour, so you can see when an app tends to ship — pick any single app for its own pattern and version history, or view all of them together. History is backfilled from each app's update feed, so the heatmap is useful right away instead of starting empty.

Honest about what it can't time. Apps that publish an exact release date (Sparkle, GitHub, Alcove) are timed to the minute. Apps that only expose a version number get a clearly-marked "≈" estimated window — bounded by when DuoUpdater last saw the old version and first saw the new one — and never skew the heatmap.

Fixes

  • ToDesk update checks no longer report an older grayscale build; they now track the version actually offered for download.

Passwordless App Store updates. Updating Mac App Store apps no longer interrupts you for your password every time. DuoUpdater now installs a small, signed privileged helper (one-time approval) and bundles mas, so App Store updates apply directly in the background.

Cleaner "Restart to finish" lines. When an app updates itself on disk while it's still running, the pending-restart line now shows the real marketing version on both sides — e.g. 1.8.x (build) → 1.9.0 (build) instead of a bare build number on the left.

Fixes

  • Fixed a build issue on Xcode 26.5 (changelog extractor name collision).

Apps that update themselves now clear correctly. If an app updated through its own updater (for example, Chrome via "About Chrome") while DuoUpdater was busy installing other updates, it could keep showing a stale "update available" row long after it was already current. DuoUpdater now re-checks the moment the installs finish, so the row clears right away instead of lingering.

"Update All" shows the whole queue. Every app in an "Update All" run now shows a "Queued" state immediately, instead of leaving the ones further down the list looking idle with a clickable Update button. Clicking Update on an app that's already queued can no longer start a second install of it.