Keeping it honest
Updating
It asks before it replaces itself, and proves four things about the download before touching the copy you are running.
Check for updates… in Settings › About & help. It asks GitHub what the latest release is and says one of three things: you are on it, there is a newer one, or the question could not be answered.
It asks by sending one request to the download address that never changes and reading the redirect GitHub answers with, which names the newest release. Not the API: the API gives anonymous callers sixty requests an hour per public address, and an office behind one router shares those sixty among every panel in it and every other tool asking GitHub without a token. That is what "GitHub is rate-limiting anonymous requests" meant when colleagues saw it and you did not.
If there is a newer one you get a button, and nothing happens until you press it.
Why it asks rather than just doing it
macOS grants Accessibility and Automation to a signing identity, so a replacement signed with the same certificate inherits the permission to drive this Mac's keyboard and windows without asking anybody anything.
An app with that permission asks before it replaces itself.
What is proved before anything is touched
Four things, and any one of them failing stops everything with nothing installed.
- The disk image passes Gatekeeper's own assessment: signed and notarized, the same verdict a stranger's Mac would reach.
- The app inside verifies as intact against its own signature.
- Its team identifier equals this copy's, so a valid certificate belonging to somebody else is refused. It is compared against the running app rather than a constant, because a constant could be edited by whoever edited the download.
- Its bundle identifier is the right one.
The download location is pinned to the project's own releases, so the answer from GitHub cannot redirect the update elsewhere.
Then the app replaces itself and comes back. The permissions survive, because the certificate is the same one.
After a Claude Code update
Everything this app knows about Claude Code was worked out by observation: hook event names, payload shapes, the files the extension leaves on disk, a URI handler. None of it is promised to stay the same.
If you have the source checked out:
./Scripts/check-contract.sh --live
It reports, it does not repair, because a wrong automatic fix would hide the breakage instead of naming it. The static tier is free and takes seconds. The live tier records a real session and costs a few tokens, and it is the one that matters: an event that stops firing raises no error, the column simply freezes on a state that is no longer true, and that is the one failure you cannot see.