# Packaged AppImage updater reproduction

This is a manual real-app proof, not a test-suite result. No provider or agent turn is involved.

- Base: [31eea883c7](https://github.com/pingdotgg/t3code/commit/31eea883c7f7849ada6eb49265cb11fc64d2ecbd), the merge-base with main.
- Head: [5cf4f63505](https://github.com/pingdotgg/t3code/commit/5cf4f63505d87afb1537b0b75c42e8c148eb73ec).
- Both initial apps were packaged as version 0.1.9 from their respective commits.
- The common update payload was fully built and packaged from head as version 0.1.10.
- Linux x64, Electron AppImage, headless-desktop/Xvfb, light theme. Recordings show a fixed 1920 by 840 display region. App window is 1100 by 800; a terminal beside it prints the driver's actual filesystem measurements.
- All app state is fresh and isolated with XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME, T3CODE_HOME, and T3CODE_DEV_SEED=0. No real user database, projects, or conversations are used.
- An unrelated local CLI path is covered by a black rectangle in screenshots and recordings.

## Build and run

Create one worktree per commit and run `vp i` in each. Install the Linux packaging prerequisites, then build each initial app with:

```
vp run dist:desktop:linux --build-version 0.1.9 --mock-updates --mock-update-server-port 19322 --output-dir <artifact-directory>
```

In head, fully build the newer payload (do not use --skip-build):

```
vp run dist:desktop:linux --build-version 0.1.10 --mock-updates --mock-update-server-port 19322 --output-dir <evidence-root>/feed
```

Serve that directory on localhost port 19322 using Python's HTTP server. The updater falls back from differential download to a full download because this local server does not implement range requests. The driver confirms the cached payload's size and SHA-512 before installation.

Under the evidence root, mount a private writable 1500 MiB tmpfs as `destination/`. Copy the chosen initial AppImage there as `t3code.AppImage`. The cache stays on the normal disk, so its device differs from the executable's device.

Run the uploaded `prove.cjs` with:

```
CHECKOUT=<checkout> EVIDENCE_ROOT=<evidence-root> SIDE=<base-or-head-label> SPACE=<low-or-adequate> headless-desktop node prove.cjs
```

The driver creates a new timestamped directory per run. It opens the actual packaged desktop, completes only the onboarding flag through the app's settings IPC, opens Settings / About, selects Stable, checks the local feed, and downloads 0.1.10 through the app's production desktop bridge. It then allocates a filler file on the destination filesystem, leaving exactly 8 MiB (low) or 600 MiB (adequate) free, and calls the production install/restart IPC. It does not replace or mock the installer.

For low-space runs it measures the installed file and cached download, closes the running app, and attempts to launch the installed filename again. For adequate-space runs it checks the installed payload hash, waits for automatic relaunch without manually launching another app, and records the relaunched window. The final adequate-space runs also use the native Settings shortcut and Settings search to reveal the app version.

Screenshots are saved only when two captures 500 ms apart are byte-identical. Video records the interaction continuously. Create a `stop` marker in a completed run directory to stop the driver's app, recorder, terminal, and window manager. Stop each run and remove its filler before resetting the installed executable for the next run.

The line about ENOSPC in the measurements checks that literal error code in the dependency log. The stock updater reports `No space left on device` through `mv` instead; the sanitized runtime transcript includes that actual message.

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