Proof audit, pass 1 — [#32646](https://git.cafe/pingdotgg/t3code/pull/32646)

Reviewed head [ddeaa04345](https://github.com/pingdotgg/t3code/commit/ddeaa04345eedbf54177bf8c46462ff07ae6f4b9), branch `fix/issue-32637`, against main at merge-base [0a9abddaee](https://github.com/pingdotgg/t3code/commit/0a9abddaee00ff1495e77a03dd4fafa73c3cd4cb). Review checkout: `/home/user/wt/pr-32646`. Read-only audit: no source edits, pushes, comments, installations, runtime captures or test execution. Completed within the 45-minute budget; no scope was truncated for time.

**Findings**

1. **Proof is insufficient at the reviewed head.** The only displayed runtime transcript covers F01's two-name bash title-trap case at [cc9837ce85](https://github.com/pingdotgg/t3code/commit/cc9837ce85), using `trap - DEBUG 2>/dev/null;`. The reviewed command is `trap - DEBUG 2>/dev/null || true;`. The old capture demonstrates the original failure and an earlier correction; it does not establish current-head compatibility, especially when the trap reset fails under `set -e` (F08/F10). The prose claims other shell runs, but supplies no per-fixture commands, profiles, outputs or statuses for them.
2. **The displayed driver cannot be reused unchanged.** It strips only `trap - DEBUG 2>/dev/null; `. That prefix does not match the reviewed head. Both its “before” and “after” calls would therefore run the fixed command. Replace this comparison with commands generated from the pinned main and head sources, or use the exact current prefix and independently verify the stripped command equals main for every shell/names pair. Do not strip anything on the Nushell path.
3. **The regression test is relevant but narrower than a login-shell capture.** [The new test](https://github.com/pingdotgg/t3code/blob/ddeaa04345eedbf54177bf8c46462ff07ae6f4b9/packages/shared/src/shell.test.ts#L121) installs the reported terminal-title DEBUG trap in a synthetic `BASH_ENV`, requests `XDG_DATA_HOME` and `PATH`, omits unset XDG and expects clean PATH. Its exec injection deliberately changes `-ilc` to `-c`; it tests command/parser behavior without host startup interference. A production-argument runtime transcript is still needed. No test run or before/after regression result was independently executed in this matrix-only pass. The PR's reported passing test counts are claims, not newly verified results.
4. **Revision provenance differs between forge metadata and Git.** `cafe pr view` and `cafe pr status` reported head [9838027d75](https://github.com/pingdotgg/t3code/commit/9838027d7525b67ce2e49602011871821d840615), with older reviews/checks. `git ls-remote origin` reported `fix/issue-32637` at the requested [ddeaa04345](https://github.com/pingdotgg/t3code/commit/ddeaa04345eedbf54177bf8c46462ff07ae6f4b9) and `main` at [0a9abddaee](https://github.com/pingdotgg/t3code/commit/0a9abddaee00ff1495e77a03dd4fafa73c3cd4cb). The clean checkout matches the requested head. Older forge checks/reviews are not current-head proof. GitCafe and GitHub PR numbers were resolved from `data.resource.github.number`: both are 32646.

No new source defect is established by this pass. These findings concern missing or stale proof, not an assertion that unsupported-shell fallback fails.

**Code paths and consumers**

- [The changed builder](https://github.com/pingdotgg/t3code/blob/ddeaa04345eedbf54177bf8c46462ff07ae6f4b9/packages/shared/src/shell.ts#L211) computes `basename(shell).toLowerCase().startsWith("nu")`. Every other basename takes the same new prefix: bash, zsh, ksh, dash, mksh, fish, csh, tcsh and sh. No separate POSIX/fish/csh compatibility branch exists. Absolute paths and mixed-case basenames affect classification only through that expression. Names such as `nu`, `Nu`, `nu.exe`, and any other basename starting with `nu` all select the unchanged Nu branch; aliases with other names do not.
- For each name the builder validates `/^[A-Z0-9_]+$/` and throws before launching on failure. A valid singleton emits one marker/value/end block. Multiple names emit blocks in input order; repeated names repeat blocks. Valid names can start with digits. Non-Nu capture uses `printf`, `printenv NAME || true`, `printf`. Unset and empty exports are omitted by the parser; nonempty/multiline/whitespace values are preserved. No quoting or validation rule changed.
- Nushell has nested choices for missing/null values, a configured `ENV_CONVERSIONS.<name>.to_string`, default PATH list joining/home expansion, default PATH scalar, and non-PATH default values. These expressions are all unchanged, including the optional-field checks and list test.
- Direct `buildEnvironmentCaptureCommand(shell, [])` now yields just the non-Nu prefix (Nu yields the empty string). Both production callers short-circuit empty lists before invoking it, so this changed direct-helper result is unreachable through current discovery.
- [Shared reader](https://github.com/pingdotgg/t3code/blob/ddeaa04345eedbf54177bf8c46462ff07ae6f4b9/packages/shared/src/shell.ts#L299): empty names return `{}`; otherwise launches `shell -ilc command`, timeout 5000 ms, SIGKILL, detached, stdin/stderr ignored, stdout piped. It takes the first start marker and following end marker per requested name. Absent markers/empty values are omitted. Spawn, nonzero exit and timeout throw. The reader/parser are unchanged.
- [Server caller](https://github.com/pingdotgg/t3code/blob/ddeaa04345eedbf54177bf8c46462ff07ae6f4b9/apps/server/src/os-jank.ts#L21): `fixPath` -> `hydratePosixPath` -> shared reader, names exactly `["PATH", "__MISE_DIFF", "JAVA_HOME"]`. It tries deduplicated configured/user/fallback shells until PATH is returned; exceptions warn and advance. Darwin may fall back to launchctl; inherited PATH is merged. Only Linux/Darwin take this path, and `shellEnvironmentPrepared` bypasses it. The server's current list does not include XDG_DATA_HOME.
- [Desktop caller](https://github.com/pingdotgg/t3code/blob/ddeaa04345eedbf54177bf8c46462ff07ae6f4b9/apps/desktop/src/shell/DesktopShellEnvironment.ts#L330): `installPosixEnvironment` -> local `readLoginShellEnvironment` -> builder, with its own equivalent marker parser and async process spawner. It also launches `-ilc`, ignores stderr, and requires exit 0; command failure/timeout yields empty output and candidate fallback. Cache refresh also reaches discovery. Its exact 24-name input is below. A clean shared-layer transcript proves the changed command once; do not add duplicate GUI captures or wrapper tests for this change.
- Windows bypasses the changed builder in both consumers. Server uses shared PowerShell discovery/repair; desktop uses its own PowerShell builder. Both try `pwsh.exe`, then `powershell.exe`, using profile/no-profile paths as appropriate. Empty-name, failure and platform/prepared bypasses are unchanged. macOS launchctl and unsupported platforms also bypass this command. No provider adapter, mobile render path, RPC contract, remote transport or permission branch changed; remote use runs the same environment-local server discovery.

Desktop names in order: `PATH`, `DBUS_SESSION_BUS_ADDRESS`, `DISPLAY`, `LANG`, `LC_ALL`, `LC_CTYPE`, `SSH_AUTH_SOCK`, `HOMEBREW_PREFIX`, `HOMEBREW_CELLAR`, `HOMEBREW_REPOSITORY`, `XDG_CONFIG_HOME`, `XDG_CURRENT_DESKTOP`, `XDG_DATA_HOME`, `XDG_RUNTIME_DIR`, `XDG_SESSION_DESKTOP`, `XDG_SESSION_TYPE`, `WAYLAND_DISPLAY`, `SSL_CERT_FILE`, `SSL_CERT_DIR`, `NODE_EXTRA_CA_CERTS`, `T3CODE_TELEMETRY_ENABLED`, `T3CODE_GITHUB_APP_CLIENT_ID`, `__MISE_DIFF`, `JAVA_HOME`.

**Inventory**

`command -v bash zsh ksh dash mksh fish csh tcsh nu` finds only bash and dash. Individually confirmed:

| Shell | Result on this VM |
| --- | --- |
| bash | `/usr/bin/bash` |
| zsh | Not installed |
| ksh | Not installed |
| dash | `/usr/bin/dash` |
| mksh | Not installed |
| fish | Not installed |
| csh | Not installed |
| tcsh | Not installed |
| nu | Not installed |

Node is v26.12.1. Disk check returned 108 GB available. No shell installation was attempted. A future bash-as-sh fixture can use a scratch symlink to the installed bash; do not substitute this VM's `/bin/sh` without checking its target. A future `ksh` or `csh` capture must record the actual implementation/version; an alias to mksh or tcsh does not establish ksh93 or BSD csh behavior.

**Fixture matrix**

Each runtime row means one before/after transcript with the same synthetic HOME, profile contents, environment and shell invocation. “Required” identifies the minimum capture set for the stated shell-compatibility scope. Missing shells require another authorized environment or a narrower supported/claimed scope; they are not silently treated as passing. Shared input variations are folded into F01/F02 instead of multiplying every shell by every names/value shape.

| ID / named fixture | Seed and requested data | Diff impact / minimum proof | Current status and stale coverage |
| --- | --- | --- | --- |
| F01 — bash title DEBUG, unset XDG | Synthetic `.bash_profile` sources `.bashrc`; export sentinel PATH, mise and Java values; install exact reported `trap 'echo -ne "\033]0;${BASH_COMMAND}\007"' DEBUG`; unset XDG_DATA_HOME. Run report pair `[XDG_DATA_HOME, PATH]`, server triple, and desktop 24-name list on the same fixture. | Affected. Required. Main shows marker/title contamination; head returns only seeded exports, clean PATH, absent XDG, exit 0. Trap itself may print startup noise before it is cleared; check parsed values, not silence. | Runnable here; current capture missing. Old transcript covers only the two-name variant at the older prefix; server/desktop name-list variants are absent. New test covers the two-name command with `-c`/BASH_ENV, not login flags. |
| F02 — clean bash, errexit and value shapes | Synthetic `.bash_profile`, no DEBUG trap, `set -e`. Export PATH, nonempty XDG_DATA_HOME, EMPTY_VAR='', CUSTOM_VAR containing leading/trailing spaces and an embedded newline; unset MISSING_VAR. Run singleton `[PATH]`, then `[MISSING_VAR, PATH, EMPTY_VAR, CUSTOM_VAR, XDG_DATA_HOME]`. | Affected. Required normal-case control. Both revisions exit 0, preserve nonempty values and whitespace, omit unset/empty. No-trap behavior of DEBUG-capable shells is represented here; a full cross-product is unnecessary. | Runnable here; missing. PR prose claims clean-shell runs, but no fixture transcript. |
| F03 — bash invoked as sh | Scratch `bin/sh` symlink to bash; `.profile` and an explicit scratch ENV startup file, sentinel PATH, XDG unset, bash DEBUG title hook; log that the intended profile/hook actually loaded. Keep real `-ilc`. | Affected. Required to substantiate the PR's explicit bash-as-sh compatibility claim. Expect main contamination/head clean if the DEBUG hook is active. If the mode does not install a DEBUG hook, record it as a clean-mode control and do not claim a trap reproduction. | Runnable here after scratch seeding; missing. Prose claim only. |
| F04 — zsh DEBUG trap | Synthetic HOME with ZDOTDIR pointing there; `.zshrc` installs a DEBUG trap that prints the pending command (`$ZSH_DEBUG_CMD`) into a title escape; sentinel PATH, XDG unset. `.zprofile` may source common exports. | Affected. Required. Establish hook fired; main contaminated/head clean, exit 0. | Cannot run here: zsh absent. Prose claim only. |
| F05 — zsh TRAPDEBUG function | Same synthetic zsh home/data as F04, replace hook with `TRAPDEBUG()` printing `$ZSH_DEBUG_CMD` in a title escape; function returns success. | Affected. Required separate hook mechanism. Main contaminated/head clean; verifies `trap - DEBUG` also removes this form. | Cannot run here: zsh absent. Prose claim only. |
| F06 — ksh93 DEBUG trap | Synthetic `.profile` plus explicit ENV file if needed; native ksh93 DEBUG hook echoes its pending command (for example `${.sh.command}`) with markers visible; sentinel PATH, XDG unset. Record implementation/version and hook-loaded evidence. | Affected. Required. Main contaminated/head clean, exit 0. Do not count mksh as ksh93. | Cannot run here: ksh absent. Prose claim only. |
| F07 — dash rejects DEBUG, errexit off | Synthetic `.profile`, sentinel PATH, XDG unset, no DEBUG trap and no `set -e`; explicit scratch ENV if interactive startup needs it. | Affected. Required rejecting-shell control. Main/head must both return clean PATH and exit 0. Record trap-reset rejection/diagnostics separately from parsed stdout. | Runnable here; missing. Prose claim only. |
| F08 — dash rejects DEBUG, errexit on | Same F07 seed with `set -e` in the loaded startup file. Keep production `-ilc`, including interactive mode. | Affected. Required for the new `\|\| true`. Both main and head must still complete cleanly. The older unguarded prefix is not evidence for this head. | Runnable here; missing. PR asserts success, but displayed transcript contains no dash run. |
| F09 — mksh rejects DEBUG, errexit off | Synthetic `.profile`, scratch ENV/`.mkshrc`, sentinel PATH, XDG unset, no `set -e`. | Affected. Required second interpreter's rejection control; main/head clean, exit 0. | Cannot run here: mksh absent. Prose claim only. |
| F10 — mksh rejects DEBUG, errexit on | Same F09 seed with `set -e` in the loaded startup file. | Affected. Required guard compatibility; main/head clean, exit 0 despite unsupported trap reset. | Cannot run here: mksh absent. No displayed transcript or explicit per-fixture `set -e` result. |
| F11 — fish without DEBUG facility | Synthetic HOME, XDG_CONFIG_HOME under it, `.config/fish/config.fish` using native `set -gx` to seed PATH and a normal export; XDG_DATA_HOME erased; no fabricated POSIX trap in startup. | Affected. Required native parser/trap compatibility: new prefix must not prevent clean values or change exit status. Record actual trap command behavior rather than assuming POSIX semantics. | Cannot run here: fish absent. Prose claim only. |
| F12 — BSD csh without trap builtin | Synthetic `.cshrc` and `.login`, native `setenv PATH` and nonempty export, XDG_DATA_HOME unset. Record actual implementation. | Affected. Required native `2>`/`\|\|`/unknown-command compatibility: main/head clean values, exit 0. Stderr is normally ignored; use diagnostics without treating stderr-only output as value corruption. | Cannot run here: csh absent. Prose claim only. |
| F13 — tcsh production option rejection | Synthetic `.tcshrc`/`.login` with sentinel export. Execute exact `-ilc`; do not “fix” flags to make this capture pass. | Prefix changes, but may never execute. Required negative control for the PR's tcsh statement: both revisions reject options identically. If this tcsh accepts `-ilc`, instead require clean equivalent main/head results. | Cannot run here: tcsh absent. Prose says pre-existing rejection; no actual statuses/diagnostics shown. |
| F14 — Nushell exclusion and value branches | Names include PATH, ordinary export and missing/empty values. Compare command strings for basename variants `nu`/`Nu`/`nu.exe`; branch cases: null, conversion closure, PATH list with `~` and relative/empty entries, scalar PATH, ordinary value. A future optional live seed uses scratch `env.nu`/`config.nu` and explicit config flags. | Unaffected. Byte-identical source/command equivalence suffices; no mandatory new live capture. Existing opt-in Nu test covers list PATH, multiline, unset/empty; it does not exercise every conversion branch. | Source verified unchanged in complete diff; live unavailable (nu absent), explicitly not claimed. No old live transcript. |
| F15 — empty names | Call reader with `[]` and exec spy; also inspect direct builder output for non-Nu and Nu. Desktop has its own empty-list guard. No profile needed. | Direct non-Nu builder output affected; both production guards unaffected, returning `{}` without spawn. No runtime capture required. | Source verified. Direct builder now returns prefix-only; not a reachable discovery regression. |
| F16 — validation, order and duplicates | Valid names: PATH, __MISE_DIFF, JAVA_HOME, numeric/underscore names; singleton/multi already in F01/F02; duplicates repeat blocks. Invalid names: empty string, lowercase, punctuation/injection. No profile for invalid inputs. | Unaffected validation and assembly; invalid input throws before exec; names remain ordered. No extra shell capture required. | Source verified. Runtime value states represented by F02; no need to multiply shell fixtures. |
| F17 — Windows bypass | Server shared PowerShell builder and desktop local PowerShell builder; profile/no-profile, pwsh/powershell fallback, empty-list and failure variants. | Unaffected, changed helper never called by production Windows path. No Windows capture required for this diff. | Source verified; no Windows runtime claimed on this Linux VM. |
| F18 — parser/process/fallback boundaries | Missing start/end markers, empty output, spawn/nonzero/timeout; server candidate loop and Darwin launchctl; desktop exit-status rejection/candidate fallback; prepared/platform/cache paths. | Unchanged. No independent new captures required; F07–F13 record compatibility failures that could reach these branches. Cache refresh uses the same builder. | Source traced. Existing unit files were inspected, not rerun. No screenshot/UI requirement. |

**Seeding and capture contract for the next pass**

All future driver scripts, per-row homes, profile files, shell symlinks, source snapshots and raw logs must remain under `/tmp/lead-32646-r1/proof-audit-1/`. Never use or change the developer's HOME/dotfiles, live T3 data, or global shell configuration. This pass created the directory, read-only PR JSON snapshot, and this report only.

Use a Node driver launched from `apps/server`, importing `readEnvironmentFromLoginShell` and `buildEnvironmentCaptureCommand` from the reviewed shared module. Since the script itself lives in scratch, use an absolute module import or feed it to Node as module stdin from `apps/server`; a bare workspace package import from a `/tmp` script can otherwise fail module resolution.

For each row, set a minimal child environment containing synthetic HOME, PATH `/usr/bin:/bin`, TERM `dumb`, and only the required shell-config selectors (ENV/ZDOTDIR/XDG_CONFIG_HOME). Use native profile syntax. Do not spread the host environment. Seed PATH to a distinctive scratch directory plus `/usr/bin:/bin`, mise to a distinctive synthetic string, and JAVA_HOME to a scratch path. If profiles need files/directories, create them inside that row's scratch home. Quote every seeded value with the shell's own syntax.

Use a recorded profile/hook-loaded indicator, kept out of captured stdout markers, to verify system startup files did not change HOME, PATH or prevent the intended hook from loading. Record final seeded exports and flags, not just intended contents. Bash login uses `.bash_profile` (source `.bashrc` explicitly); bash-as-sh/dash/ksh/mksh use `.profile` and, where needed, the explicit ENV file; zsh uses ZDOTDIR and `.zshrc`; fish uses its config directory; csh/tcsh use the corresponding rc/login files. Real login captures retain the reader's exact `-ilc` flags. Replacing them with `-c` plus BASH_ENV is valid for the unit regression only, not proof of the login path.

Generate main's and head's commands from the two pinned Git sources. The only changed runtime function is the builder; the shared reader/parser is identical. The driver may therefore invoke the reviewed `readEnvironmentFromLoginShell` twice with an injected exec wrapper that supplies the separately generated main or head command to the same shell, preserving `args[0]` and all supplied exec options and adding only the synthetic environment. Extract/load the pure builder with its marker helpers from the pinned base snapshot in scratch, or assert the exact prefix removal reproduces that builder. A label saying “main” without this equality check is insufficient.

For every invocation log: fixture ID; full revisions; real shell path/implementation/version; synthetic HOME; exact profile file contents; names list; both generated commands as JSON; startup/hook-loaded evidence; raw stdout (JSON escaped); parsed result; process status or error. Production ignores stderr: retain that behavior for the proof call. If diagnostics are needed for a rejected trap/flags, label a separate diagnostic invocation with stderr piped; record it without conflating it with the proof call. Compare JSON so ESC/BEL/control bytes and multiline whitespace are visible. Report thrown failures for F13 rather than dropping the fixture.

Minimum new runtime set: F01–F13, collapsing each shell's unchanged name/value permutations as shown. F01 has the report/server/desktop input variants; F02 has singleton and mixed-state multi-name variants. They can share one driver and one transcript artifact with clearly labeled records, but each record must include main and reviewed-head output. Run the focused new bash regression against both revisions, without editing this checkout (a scratch-derived baseline runner/loader is acceptable); show fail on main and pass at head. Existing broader tests can supplement this evidence, not substitute for rejected-trap/errexit runtime rows. F14–F18 need no new runtime capture because the relevant branches are unchanged or guarded off. macOS bash 3.2 and a real provider update are outside this minimum set and must remain explicitly unclaimed unless separately tested. No UI screenshots apply.

**Status**

Covered: complete two-file diff, changed builder and all its conditionals, both production consumers, name/value shapes, startup/trap/errexit conditions, Windows/Nu exclusions, existing regression mechanism, stale transcript/driver, revision provenance and VM shell inventory. `git diff --check` passed. No production behavior or test execution is newly claimed.

FRICTION:
forge | GitCafe PR metadata/checks point to an older head than origin and the requested checkout | revision-bound sync/metadata at the Git branch head would avoid stale evidence attribution.
infra | zsh, ksh, mksh, fish, csh, tcsh and nu are absent on this VM | a prepared shell-matrix VM or authorized shell-specific environment would enable future captures; Nu live is waived for this unchanged branch.

VERDICT: INSUFFICIENT F01–F13 current-head before/after runtime rows and independently verified main-fail/head-pass bash regression; F01 old transcript is stale and its before-driver no longer strips the current prefix.
