# Standalone self-update proof

Linux x64. Native Node single-executable CLI builds from the base and head, each with an isolated HOME and T3CODE_HOME and a fresh database. No live data was copied. The same terminal viewport, theme, driver and RPC were used on both sides.

The base is [543ee27fa8](https://github.com/pingdotgg/t3code/commit/543ee27fa8e890cf0f6ce9e7f6a2d53dd3b1a7af); the head is [c04f95fe23](https://github.com/pingdotgg/t3code/commit/c04f95fe23d2b5d0123e35e7726f561d95c6ef86).

Run `vp i` in each worktree, then run `build-runtimes.sh OUTPUT_DIRECTORY` from each worktree root. This produces two genuine standalone executables, with only apps/server/package.json version changed to 0.1.8 and 0.1.9 for a deterministic local update. No product logic is patched.

Keep prove-update.py and update-rpc.mjs together. Set these environment variables for each run:

- ARTIFACT_ROOT: a private scratch output directory
- SIDE: before or after
- COMMIT: the full commit under test
- OLD_BINARY and NEW_BINARY: absolute paths to that side's old-t3 and new-t3
- MODULES: that worktree's apps/server/node_modules
- DEPENDENCY_PACKAGE: that worktree's apps/server/package.json
- NODE: the Node executable path
- SERVER_PORT: an unused loopback port

Run `headless-desktop bash record.sh` to capture the terminal, or `python3 prove-update.py` for the runtime transcript. Run before and after separately. The recording uses GNOME Terminal, Xvfb, ImageMagick and ffmpeg. Each driver run creates a new timestamped output directory, keeps its private server log separate, and stops the launcher it starts.

The driver installs the two local executables under the synthetic T3 home's runtime/versions, adds install-complete markers and links ~/.local/bin/t3 to 0.1.8. It starts the real service launcher directly rather than replacing the host's installed system service. It issues a scoped credential without printing it, sends the real server.updateServerWithProgress client RPC to select the cached 0.1.9 runtime, waits for the actual service-state commit and HTTP environment version, and executes the real CLI and readlink. It then intentionally pins the CLI to 0.1.8, stops and restarts the real launcher without another update, and repeats the observations.

Before: server commits and serves 0.1.9, CLI stays at 0.1.8. After: server and CLI both select 0.1.9. The intentional pin survives the later restart on both sides.

The screenshots are identical across two captures 500 ms apart before cropping. Published images and videos use the same 1456x720 crop to remove unused desktop area. The recordings show actual live command output, not a reconstruction. Captures were inspected before upload.

Limitations: this isolates server and CLI behavior; it does not exercise the desktop Update server button, OS service installation, remote release downloading, custom install directories, additional PATH aliases, or macOS.

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