"Logout, restart and shutdown are never held up: Tinker restores what it can within eight seconds and the journal keeps the rest." — Tinker README
The Bug in the Clever Quit
The ordinary-Quit design from the last lesson — answer .terminateCancel, restore, terminate again — has a hidden victim. When Dad logs out, restarts or shuts down, macOS asks every running app to quit by sending it a quit Apple event. An app that answers .terminateCancel to that request is not saying "one moment"; it is cancelling the logout. The first Tinker did exactly that, every time. The review pass on 2026-11-10 caught it: an actuator whose quit path could stop the Mac from restarting was holding the user hostage to its own tidiness.
Telling the Two Quits Apart
Inside applicationShouldTerminate, Tinker asks NSAppleEventManager for the Apple event currently being handled. If it is the core kAEQuitApplication event, this quit came from outside — logout, restart, shutdown, or a script that asked the app to quit. Anything else is Tinker's own Quit menu item. The two take different paths.
The External Path: Defer, Restore, Always Allow
For an external quit, Tinker returns .terminateLater. AppKit keeps the process alive and keeps the run loop turning while Tinker restores. Two things can then answer the deferred quit, and whichever comes first wins: the restore task finishing, or an eight-second Timer added to the main run loop in .common modes, so it fires even inside AppKit's nested loop and even if the restore itself is stuck. Either way the answer is reply(toApplicationShouldTerminate: true). A helper guarded by readyToTerminate makes sure it is sent exactly once. Whatever was not restored in time stays in the durable journal for the next login.
Why the Starvation Problem Doesn't Bite Here
The ordinary Quit avoided terminateLater because a restore task started from inside it could be starved. On the external path that risk is accepted on purpose and bounded: the timer does not depend on the task, so even a starved restore cannot hold the logout longer than eight seconds. The ordinary path optimises for a complete restore; the external path optimises for never blocking the person.
How It Was Tested — and What Wasn't
The GUI lab run ends by sending the app a quit Apple event addressed to its own process, so the deferred path is exercised for real, not mocked. In a debug build running as the GUI lab snapshot, Tinker writes which path it took and whether it released. The architecture is honest about the limit: a real logout is not exercised in the lab. Claims are scoped to what was run.