Desktop update evidence

Both revisions were independently built from their own git worktree with:
T3CODE_DEV_SEED=0 vp run dist:desktop:artifact --platform linux --target AppImage --arch x64 --output-dir <artifact-directory> --mock-updates --verbose
Base: [9cd7a04797](https://git.cafe/pingdotgg/t3code/commit/9cd7a04797184c92a8f636ae1e918f42c139d850)
Head: [6ccaad0a1f](https://git.cafe/pingdotgg/t3code/commit/6ccaad0a1fad37e68eea2a637dd246e3c353fa38)
No source edits, state injection, fake dialogs, or unit tests were used.

Environment for every capture: Linux x86_64, packaged T3 Code 0.1.8 AppImage,
Electron 44.10.1; headless-desktop Xvfb 1920x1080 at scale 1, Openbox,
light appearance, maximized app. Fresh isolated server and client state on each
side. Finish onboarding with project import unchecked; no projects or threads.

Start the controlled update source:
  python3 feed.py <head-AppImage> 31357
It advertises a synthetic stable 0.1.9, serves the real head AppImage with its
correct SHA-512, and throttles transfer to 4 MB/s. This verifies the real updater
and native dialogs, not the public release CDN or installation of a newer binary.
Do not install this fixture: its embedded version remains 0.1.8.

Start each build:
  EVIDENCE_INSPECT_PORT=9331 headless-desktop python3 launch.py <head-AppImage> <new-head-run-directory> 31357
  EVIDENCE_INSPECT_PORT=9332 headless-desktop python3 launch.py <base-AppImage> <new-base-run-directory> 31357
Each output directory must be new. Complete onboarding without importing history,
then maximize the app. A fresh synthetic keyring may need a disposable password.

Run the same menu driver on each side:
  python3 record-check.py <base-run-directory> 9332 before-available
  python3 record-check.py <head-run-directory> 9331 after-available
The frameless Linux titlebar does not expose the native application menu.
inspect.mjs calls Menu.getApplicationMenu().popup() to open that EXISTING menu;
then xdotool moves through Help and clicks Check for Updates. It does not invoke
the handler, replace services, or set updater state. The real handler, real
updater, HTTP requests, downloads, and native message boxes run unchanged.

Observed: base closes the menu without feedback; head opens Update available,
showing 0.1.9, current version 0.1.8, Download and Later.
The first base take was rejected because onboarding was still visible. Only the
subsequent clean-home-screen take is evidence.

After that first divergence: base has no Download dialog action, so start its
download using the existing bottom-left sidebar update button. On head, click
Download in the new native dialog. Then run the SAME record-check.py interaction
on each side with labels before-downloading and after-downloading. Base gives no
menu dialog; head reports that 0.1.9 is already downloading, with OK.
After the actual downloads finish, dismiss head's OK and run the SAME driver with
labels before-ready and after-ready. Base gives no menu dialog; head offers
Restart/Later and warns that running tasks will be interrupted. Do not Restart.
These downstream comparisons have equivalent updater states but the download
entry points differ because the original bug prevents the menu path on base.

Every final still was accepted only after two full-frame pixel buffers captured
500 ms apart were identical. Each interaction is also recorded in an MP4.
The driver creates a new timestamped capture directory for each run.
All published stills and recordings were visually inspected before upload.

Not covered: macOS/Windows native menu placement; public release discovery/CDN;
actual Restart/install and interruption of running tasks. No real agents ran.

AI-assisted; verified with the tests and tools listed above.
