# Packaged Linux AppImage restart reproduction

Issue [#16629](https://git.cafe/pingdotgg/t3code/issues/16629), proposed fix [#15681](https://github.com/pingdotgg/t3code/pull/15681). Reproduction only, 24 October 2026, fs-40. No source/test changes, push, merge, forge comments, or PR watching.

## Result

Current main [d53873b6a4](https://git.cafe/pingdotgg/t3code/commit/d53873b6a4799e87afd63c8d288ef0089f5a3748) fails a real FUSE-mounted AppImage settings restart. Two fresh-profile, untraced trials began with a painted, loaded welcome window and `local-only`, invoked the requested renderer bridge, and did not obtain a usable replacement within 45 seconds. The old process exited 0 and its mount disappeared. The replacement main processes died with SIGBUS; replacement zygotes also had SIGTRAP/SIGBUS exits.

The historical exact ICU error string was NOT established on this current-main build. A setup trial also saw replacement-main SIGTRAP, but the inspector initially delayed exit and that trial is excluded from the primary result. Attaching strace in a separate diagnostic run changed the race: replacement exec returned ENOENT for the old mount path, exit 127. This supports the disappearing-resource-path failure, not a claim that the historical ICU stack was reproduced exactly.

The same current-main AppImage extracted to a persistent directory restarted successfully. The replacement had a visible welcome window and `network-accessible` after the stable check. This is a control, not the packaged result.

Exact PR head [59eb93faab](https://github.com/pingdotgg/t3code/commit/59eb93faabc6d89864d5663935ee0d6bf7ada00f) passed as a real packaged AppImage. PID 1803052 on `/tmp/.mount_pr.AppdcIAjl` was replaced by PID 1804241 on `/tmp/.mount_pr.AppppgEok`. The replacement opened `t3code://app/#/welcome`, rendered visible onboarding content, returned `mode: network-accessible`, and remained stable for the five-second follow-up. The old mount disappeared while the new mount remained until owned-process cleanup.

Both extracted-main and PR controls reported `endpointUrl: null` and `advertisedHost: null`. This run proves desktop restart and saved exposure mode, not remote reachability or an advertised endpoint.

## Environment and artifacts

Ubuntu 24.04.5 LTS x86_64, kernel 6.1.102, Xvfb 1920x1080x24 / X11, session D-Bus and unlocked GNOME keyring via `headless-desktop`. Flags: `--no-sandbox --disable-gpu --inspect=127.0.0.1:<unused-port>`.

Current main built as local unsigned version 0.1.2, Electron 44.9.3. AppImage SHA-256: `e050afa0152756bdf6fe046ebb7faebe801580320455acb3d0be93c87f418481`.

PR head built as local unsigned version 0.1.0, Electron 44.4.5. AppImage SHA-256: `bc4d81cb68d29d4780c0002faf00f5f3660e7c765e0b42bd1133c5231fc64312`.

The PR head is its older exact commit, not the fix rebased onto current main. Different bundled Electron versions limit the A/B comparison. The copied test AppImages matched the final build outputs byte-for-byte.

## Exact build commands and outcomes

From `/home/ubuntu/t3code`:

```sh
git remote get-url origin
# https://git.cafe/pingdotgg/t3code.git
git fetch origin main
git worktree add --detach /home/ubuntu/wt/issue-16629 origin/main
cd /home/ubuntu/wt/issue-16629
vp install
PATH="$HOME/.cargo/bin:$PATH" vp run dist:desktop:linux --verbose
```

Main install and artifact build exited 0. No prebuilt historical release was used.

Routine prerequisite installation on this VM:

```sh
sudo apt-get update
sudo apt-get install -y cargo rustc build-essential libsecret-1-dev pkg-config imagemagick squashfs-tools libfuse2
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/issue-16629-repro/rustup.sh
sh /tmp/issue-16629-repro/rustup.sh -y --profile minimal --default-toolchain stable
sudo apt-get install -y fuse3
```

Ubuntu's Rust 1.75 was too old for the repository's Rust 2024 manifests; stable installed Rust 1.99.1. The pinned electron-builder AppImage toolset was downloaded automatically, including its own mksquashfs/runtime, so standalone appimagetool was unnecessary. `fuse3` supplied the missing fusermount3 helper. Prerequisites remain installed. Free space never approached the 5 GB stop threshold; the lowest observed value was 27 GB.

PR checkout/build commands in the same assigned worktree:

```sh
# The named branch was absent on origin. Fetching the requested public SHA succeeded.
git fetch https://github.com/pingdotgg/t3code.git 59eb93faabc6d89864d5663935ee0d6bf7ada00f
git checkout --detach 59eb93faabc6d89864d5663935ee0d6bf7ada00f
vp install
PATH="$HOME/.cargo/bin:$PATH" vp run dist:desktop:linux --verbose
```

Install passed. The stock PR artifact build failed at `vp install --prod` with `ERR_PNPM_UNUSED_PATCH: node-pty@1.2.0-beta.15`. The stage resolves the manifest's version range afresh. A temporary package-manager hook outside the repository pinned the staged root `t3code` dependency to the exact node-pty version in the PR lockfile, preserving its existing patch. No repository file or compiled implementation was edited. The hook is included in the transcript bundle.

```sh
PATH="$HOME/.cargo/bin:$PATH"   PNPM_CONFIG_PNPMFILE=/tmp/issue-16629-repro/pin-locked-node-pty.cjs   vp run dist:desktop:linux --skip-build --verbose
```

This packaging run exited 0. `--skip-build` reused the successful desktop/server/web bundles produced from the exact PR head by the prior stock command. The first hook attempt using `npm_config_pnpmfile` was ignored by pnpm 11 and failed with the same unused-patch error; `PNPM_CONFIG_PNPMFILE` was recognized and the pin was logged.

## Exact runtime procedure

The included `supervise.py`, `inside.py`, and `drive.mjs` are scratch harnesses outside the repo. The supervisor is a Linux child subreaper: it records descendants by ancestry, executable changes, wait statuses and mountinfo changes, then stops only its owned descendants. Core output was disabled to avoid large dumps. The headless wrapper received isolated XDG paths before starting D-Bus/keyring; the desktop received the same paths plus isolated T3CODE_HOME. No real T3 data was opened for writing.

```sh
python3 /tmp/issue-16629-repro/supervise.py /tmp/issue-16629-repro main-confirmed /tmp/issue-16629-repro/main.AppImage
python3 /tmp/issue-16629-repro/supervise.py /tmp/issue-16629-repro main-repeat /tmp/issue-16629-repro/main.AppImage
mkdir -p /tmp/issue-16629-repro/extracted
cd /tmp/issue-16629-repro/extracted
../main.AppImage --appimage-extract
python3 /tmp/issue-16629-repro/supervise.py /tmp/issue-16629-repro main-extracted /tmp/issue-16629-repro/extracted/squashfs-root/AppRun
python3 /tmp/issue-16629-repro/supervise.py /tmp/issue-16629-repro pr /tmp/issue-16629-repro/pr.AppImage
```

The effective app invocation for each run was:

```sh
env -u APPIMAGE -u APPDIR -u ELECTRON_RUN_AS_NODE -u NODE_OPTIONS   T3CODE_HOME="$trial/t3-home"   XDG_CONFIG_HOME="$trial/config" XDG_DATA_HOME="$trial/data"   XDG_CACHE_HOME="$trial/cache" XDG_STATE_HOME="$trial/state"   XDG_RUNTIME_DIR="$trial/runtime"   T3CODE_DISABLE_AUTO_UPDATE=true T3CODE_PORT="$unused_backend_port"   "$artifact" --no-sandbox --disable-gpu --inspect=127.0.0.1:"$unused_inspector_port"
```

Launch JSON files record actual paths/ports. Main-repeat backend/inspector: 58793/32981. PR: 50433/53523. Each port was selected by a temporary loopback bind to port 0, and startup succeeded without a collision.

The loopback main-process inspector used the loaded BrowserWindow's webContents to read renderer state and invoke:

```js
window.desktopBridge.setServerExposureMode('network-accessible')
```

The driver first required readyState complete, nonempty visible text, a nonloading window, and local-only mode. It disconnected the inspector after the setting call so it did not hold the outgoing process alive. The replacement had to have a different PID, complete/painted visible UI, and network-accessible mode. It captured screenshots and an Xvfb recording. The transcript records the setting result, process identity, executable/mount paths, window URL, mode, and post-restart state.

## Diagnostic limits and discarded setup attempts

The initial missing-fusermount attempt failed before Electron, exit 127. An early harness attempt triggered after document load but before UI paint and held the inspector through exit; it was discarded and corrected. The primary two main runs used visible loaded UI and disconnected after the action. A launch-under-strace attempt could not use setuid fusermount3 (`Operation not permitted`), so the separate trace attached with sudo only after mounting. That attach trial showed old-mount exec ENOENT and exit 127. It did not establish the ICU diagnostic. A process logger initially emitted synthetic zero wait statuses when waitpid returned 0; the primary repeat and PR trials used the corrected logger, and only actual nonzero fatal statuses from the first main trial are relied on.

No Settings/Connections clicks or confirmation dialog were exercised. No Fedora/KDE/native Wayland, sandbox-enabled launcher, automatic update, .deb installation/update, provider turn recovery, cross-device reachability, web/mobile clients, Windows or macOS were verified. Automatic provider capability discovery ran during normal desktop startup; no user work turn was requested. No tests were modified or run.

## Runtime transcript excerpts

### main-confirmed

```json
{"elapsed": 3.079, "kind": "before", "value": {"pid": 1788244, "execPath": "/tmp/.mount_main.AfcCDlM/t3code", "appImage": "/tmp/issue-16629-repro/main.AppImage", "appDir": "/tmp/.mount_main.AfcCDlM", "argv": ["/tmp/.mount_main.AfcCDlM/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:44675"], "electron": "44.9.3", "windows": [{"url": "t3code://app/#/welcome", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "local-only", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 523}}}
{"elapsed": 5.701, "kind": "action", "value": "window.desktopBridge.setServerExposureMode('network-accessible')"}
{"elapsed": 5.71, "kind": "setting_result", "value": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}}
{"elapsed": 52.351, "kind": "after_no_usable_replacement", "value": {"last": null}}
```

### main-repeat

```json
{"elapsed": 5.248, "kind": "before", "value": {"pid": 1799346, "execPath": "/tmp/.mount_main.ALOAkdg/t3code", "appImage": "/tmp/issue-16629-repro/main.AppImage", "appDir": "/tmp/.mount_main.ALOAkdg", "argv": ["/tmp/.mount_main.ALOAkdg/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:32981"], "electron": "44.9.3", "windows": [{"url": "t3code://app/#/welcome", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "local-only", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 523}}}
{"elapsed": 7.846, "kind": "action", "value": "window.desktopBridge.setServerExposureMode('network-accessible')"}
{"elapsed": 7.857, "kind": "setting_result", "value": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}}
{"elapsed": 54.477, "kind": "after_no_usable_replacement", "value": {"last": null}}
```

### main-extracted

```json
{"elapsed": 4.106, "kind": "before", "value": {"pid": 1793345, "execPath": "/tmp/issue-16629-repro/extracted/squashfs-root/t3code", "argv": ["/tmp/issue-16629-repro/extracted/squashfs-root/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:51285"], "electron": "44.9.3", "windows": [{"url": "t3code://app/#/welcome", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "local-only", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 523}}}
{"elapsed": 6.699, "kind": "action", "value": "window.desktopBridge.setServerExposureMode('network-accessible')"}
{"elapsed": 6.714, "kind": "setting_result", "value": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}}
{"elapsed": 12.324, "kind": "after", "value": {"pid": 1794300, "execPath": "/tmp/issue-16629-repro/extracted/squashfs-root/t3code", "argv": ["/tmp/issue-16629-repro/extracted/squashfs-root/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:51285"], "electron": "44.9.3", "windows": [{"url": "t3code://app/", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 0}}}
{"elapsed": 17.458, "kind": "after_stable", "value": {"pid": 1794300, "execPath": "/tmp/issue-16629-repro/extracted/squashfs-root/t3code", "argv": ["/tmp/issue-16629-repro/extracted/squashfs-root/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:51285"], "electron": "44.9.3", "windows": [{"url": "t3code://app/#/welcome", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 523}}}
```

### pr

```json
{"elapsed": 18.719, "kind": "before", "value": {"pid": 1803052, "execPath": "/tmp/.mount_pr.AppdcIAjl/t3code", "appImage": "/tmp/issue-16629-repro/pr.AppImage", "appDir": "/tmp/.mount_pr.AppdcIAjl", "argv": ["/tmp/.mount_pr.AppdcIAjl/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:53523"], "electron": "44.4.5", "windows": [{"url": "t3code://app/#/welcome", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "local-only", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 144}}}
{"elapsed": 21.445, "kind": "action", "value": "window.desktopBridge.setServerExposureMode('network-accessible')"}
{"elapsed": 21.453, "kind": "setting_result", "value": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}}
{"elapsed": 42.167, "kind": "after", "value": {"pid": 1804241, "execPath": "/tmp/.mount_pr.AppppgEok/t3code", "appImage": "/tmp/issue-16629-repro/pr.AppImage", "appDir": "/tmp/.mount_pr.AppppgEok", "argv": ["/tmp/.mount_pr.AppppgEok/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:53523"], "electron": "44.4.5", "windows": [{"url": "t3code://app/#/welcome", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 523}}}
{"elapsed": 47.307, "kind": "after_stable", "value": {"pid": 1804241, "execPath": "/tmp/.mount_pr.AppppgEok/t3code", "appImage": "/tmp/issue-16629-repro/pr.AppImage", "appDir": "/tmp/.mount_pr.AppppgEok", "argv": ["/tmp/.mount_pr.AppppgEok/t3code", "--no-sandbox", "--disable-gpu", "--inspect=127.0.0.1:53523"], "electron": "44.4.5", "windows": [{"url": "t3code://app/#/welcome", "loading": false, "destroyed": false}], "renderer": {"title": "T3 Code (Beta)", "ready": "complete", "exposure": {"mode": "network-accessible", "endpointUrl": null, "advertisedHost": null, "tailscaleServeEnabled": false, "tailscaleServePort": 443}, "visibleTextCharacters": 523}}}
```

## Evidence

Before: current main, visible window disappears and does not return.

![Before: current-main packaged restart fails](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/e479875dcaa1bee4/before-main-restart-fails.mp4)

After: exact PR head, replacement window returns.

![After: packaged PR restart returns](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/c198b33524e85781/after-pr-restart-returns.mp4)

![PR replacement window after stable check](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b4ccb68dd93e41c9/pr-after-usable-window.png)

## FRICTION

FRICTION: Missing Rust and fusermount3 were repaired with routine prerequisites; electron-builder supplied its AppImage tools.

FRICTION: Stock PR packaging hit unused node-pty patch resolution; a scratch PNPM_CONFIG_PNPMFILE hook pinned the staged dependency to its lockfile version. The tested PR artifact includes this documented packaging workaround.

FRICTION: Inspector attachment can delay Electron shutdown, and ptrace changes the AppImage mount/exec race. Primary recordings use the corrected untraced harness.

FRICTION: Current main reproduced fatal mount-lifetime restart failure, not a verified match of the historical ICU error text/stack. Main and PR bundle different Electron versions.

## Cleanup

All eight trial supervisors ended with `final_owned: []`. Only captured owned descendants were stopped. The assigned `/home/ubuntu/wt/issue-16629` worktree was removed after both successful builds completed. Root status remains only the pre-existing `?? app.json`. Known artifact-builder temporary directories are absent. The temporary Browser panel tab was closed. Disk free after removal: 31 GB. The remaining `/tmp/issue-16629-repro` harness, profiles, binaries and logs are removed after the evidence upload and before final handoff. Prerequisite tools and normal package-manager caches remain installed. No public issue/PR action was taken; the existing PR is linked to this thread and is not watched.
