# Live evidence for pingdotgg/t3code#23539: `vcs.switchRef` checkout timeout removed

All repositories are **SYNTHETIC** fixtures built by `mkrepo.sh`, a fresh one per row and side under `/tmp/ev-23539/repos/<row>-<side>`.

- BEFORE = `8d3b061121fe65669d5d43d011b48cd3027a4108` (main, checkout `timeoutMs: 10_000`), server on 127.0.0.1:47391, base dir `/tmp/ev-23539/home-before`
- AFTER = `f77bfb4c8a51f1bdfda110eebba5e42defc60a64` (PR head, `timeoutMs: null`), server on 127.0.0.1:47392, base dir `/tmp/ev-23539/home-after`
- Servers: `node apps/server/src/bin.ts serve --base-dir <dir> --port <p> --host 127.0.0.1`. Both start empty; no project registration was needed (switchRef resolves the VCS driver from `cwd`).
- Auth: `bin.ts auth session issue --base-dir <dir> --token-only` gives a bearer token. `POST /api/auth/websocket-ticket` then returns `{ticket}`, and the client connects to `ws://127.0.0.1:<p>/ws?orchestrationProtocol=2&wsTicket=<ticket>`. **Note:** without `orchestrationProtocol=2` the server answers HTTP 426.
- Driver: hand-written Effect RPC JSON frames (`{"_tag":"Request","id":"1","tag":"vcs.switchRef","payload":{cwd,refName},"headers":[]}`) using Node 24's built-in WebSocket.
- Driver: [switchref-driver.mjs](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1b5f14b1be99f1ce/switchref-driver.mjs), sha256 `6b2961242dbf306bb9f699c7572178d5d3f17f6668f8170d42574aeaf8d35a65`
- Fixtures: [mkrepo.sh](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b879ead95c14be62/mkrepo.sh), sha256 `a4282cb87749baf77ec0a03a71030956db7cdce3123ac0d6986297c0ccf0cd3c`
- Host: Linux 6.1, git 2.43.0, node v24.22.1. Run on 2026-10-22, 12:43 to 12:46 UTC.

| Row (SYNTHETIC) | Side | SHA | RPC wall | Result | HEAD after | `status --porcelain` after |
|---|---|---|---|---|---|---|
| R1 slow smudge (`sleep 15; cat`), old→new | BEFORE | 8d3b061121 | 10152 ms | Fail `GitCommandError` "Git command timed out." | `old` | ` M a.txt` / `?? .gitattributes` (half-applied) |
| R1 | AFTER | f77bfb4c8a | 15138 ms | Success `{refName:"new"}` | `new` | clean |
| R2 fast, main→feature | BEFORE | 8d3b061121 | 159 ms | Success `{refName:"feature"}` | `feature` | clean |
| R2 | AFTER | f77bfb4c8a | 148 ms | Success `{refName:"feature"}` | `feature` | clean |
| R3 conflict (dirty a.txt), main→feature | BEFORE | 8d3b061121 | 137 ms | Fail `GitCommandError` exit 1 "git checkout failed", stderrLength 168 | `main` | ` M a.txt` (untouched) |
| R3 | AFTER | f77bfb4c8a | 100 ms | Fail, identical payload | `main` | ` M a.txt` (untouched) |
| R4 hang (`sleep 600`), old→new | BEFORE | 8d3b061121 | 10127 ms | Fail "Git command timed out."; git+filter killed by t+13s | `old` | ` M a.txt` / `?? .gitattributes` |
| R4, socket closed at 60 s | AFTER | f77bfb4c8a | pending at 60007 ms (no Exit frame) | Client closed WS. git, sh filter and `sleep` all gone ≤3 s after close | `old` | ` M a.txt` / `?? .gitattributes` (half-applied) |
| R4 follow-up, new WS, same repo (filter set to `cat`) | AFTER | f77bfb4c8a | 72 ms | Fail exit 1 "git checkout failed": local changes to a.txt and untracked .gitattributes would be overwritten | `old` | unchanged |
| R4 follow-up, new WS, fresh fast repo | AFTER | f77bfb4c8a | 113 ms | Success `{refName:"feature"}` | `feature` | clean |

## Findings

1. The PR fixes the reported case. A checkout that legitimately takes longer than 10 s (R1) now completes cleanly instead of being killed at 10 s with a half-applied tree.
2. **R4 disconnect.** With no timeout, a hung checkout stays pending indefinitely while the client is connected: the filter pid was still alive at t+55 s. Closing the WebSocket interrupts the RPC and the server kills the whole subtree (`git checkout` → `sh` filter → `sleep`) within 3 s, so **no orphaned process**. The server and checkout lease are not stuck: a new connection switches another repo in 113 ms, and the same repo's lease was free for the follow-up RPC (it returned at once). **However**, interrupting on disconnect leaves the same half-applied tree that the old timeout did (` M a.txt`, `?? .gitattributes`, HEAD still `old`), and every later switchRef on that repo fails until manual cleanup. Removing the timeout narrows this failure mode to client disconnects; it does not remove it.
3. The R3 error payload carries only `stderrLength`, not git's stderr text, on both sides (no change in this PR). The same message from a direct `git checkout` is quoted in the R4 follow-up row.
4. No `index.lock` was left behind in any killed checkout (R1-before, R4-before, R4-after).

## Rerun one row (e.g. R1 AFTER)

```sh
export PATH="$HOME/.bun/bin:$HOME/.local/bin:$HOME/.local/share/vite-plus/bin:$PATH"; for v in $(env | grep '^T3CODE_' | cut -d= -f1); do unset $v; done
E=/tmp/ev-23539; WT=/home/ubuntu/wt/pr-23539   # BEFORE: worktree at 8d3b061121, port 47391, home-before
mkdir -p $E/home-after $E/repos $E/driver   # place switchref-driver.mjs and mkrepo.sh (links above) in $E/driver
(cd $WT && node apps/server/src/bin.ts serve --base-dir $E/home-after --port 47392 --host 127.0.0.1 > $E/server-after.log 2>&1) & echo "subshell $!"
# get the node pid from: ss -H -ltnp | grep :47392  (confirm /proc/<pid>/cwd == $WT)
(cd $WT && node apps/server/src/bin.ts auth session issue --base-dir $E/home-after --token-only > $E/token-after)
rm -rf $E/repos/r1-after; bash $E/driver/mkrepo.sh r1 after
(cd $WT && node $E/driver/switchref-driver.mjs 47392 $E/token-after $E/repos/r1-after new)
git -C $E/repos/r1-after rev-parse --abbrev-ref HEAD; git -C $E/repos/r1-after status --porcelain
kill <node pid from ss>
```
R4: add `--close-after-ms 60000` and watch `ps -p $(cat /tmp/ev-23539/r4-after.pid)` before and after the close.

## Raw logs

### r1-before
```json
{
  "port": 47391,
  "cwd": "/tmp/ev-23539/repos/r1-before",
  "refName": "new",
  "wallMs": 10152,
  "outcome": "Failure",
  "exit": {
    "_tag": "Failure",
    "cause": [
      {
        "_tag": "Fail",
        "error": {
          "_tag": "GitCommandError",
          "operation": "GitVcsDriver.switchRef.checkout",
          "command": "git",
          "cwd": "/tmp/ev-23539/repos/r1-before",
          "argumentCount": 3,
          "detail": "Git command timed out."
        }
      }
    ]
  }
}
```
```
HEAD: old
status:
 M a.txt
?? .gitattributes
```

### r1-after
```json
{
  "port": 47392,
  "cwd": "/tmp/ev-23539/repos/r1-after",
  "refName": "new",
  "wallMs": 15138,
  "outcome": "Success",
  "exit": {
    "_tag": "Success",
    "value": {
      "refName": "new"
    }
  }
}
```
```
HEAD: new
status:
```

### r2-before
```json
{
  "port": 47391,
  "cwd": "/tmp/ev-23539/repos/r2-before",
  "refName": "feature",
  "wallMs": 159,
  "outcome": "Success",
  "exit": {
    "_tag": "Success",
    "value": {
      "refName": "feature"
    }
  }
}
```

### r2-after
```json
{
  "port": 47392,
  "cwd": "/tmp/ev-23539/repos/r2-after",
  "refName": "feature",
  "wallMs": 148,
  "outcome": "Success",
  "exit": {
    "_tag": "Success",
    "value": {
      "refName": "feature"
    }
  }
}
```

### r3-before
```json
{
  "port": 47391,
  "cwd": "/tmp/ev-23539/repos/r3-before",
  "refName": "feature",
  "wallMs": 137,
  "outcome": "Failure",
  "exit": {
    "_tag": "Failure",
    "cause": [
      {
        "_tag": "Fail",
        "error": {
          "_tag": "GitCommandError",
          "operation": "GitVcsDriver.switchRef.checkout",
          "command": "git",
          "cwd": "/tmp/ev-23539/repos/r3-before",
          "argumentCount": 3,
          "exitCode": 1,
          "stdoutLength": 0,
          "stderrLength": 168,
          "detail": "git checkout failed"
        }
      }
    ]
  }
}
```
```
HEAD: main
status:
 M a.txt
```

### r3-after
```json
{
  "port": 47392,
  "cwd": "/tmp/ev-23539/repos/r3-after",
  "refName": "feature",
  "wallMs": 100,
  "outcome": "Failure",
  "exit": {
    "_tag": "Failure",
    "cause": [
      {
        "_tag": "Fail",
        "error": {
          "_tag": "GitCommandError",
          "operation": "GitVcsDriver.switchRef.checkout",
          "command": "git",
          "cwd": "/tmp/ev-23539/repos/r3-after",
          "argumentCount": 3,
          "exitCode": 1,
          "stdoutLength": 0,
          "stderrLength": 168,
          "detail": "git checkout failed"
        }
      }
    ]
  }
}
```
```
HEAD: main
status:
 M a.txt
```

### r4-before
```json
{
  "port": 47391,
  "cwd": "/tmp/ev-23539/repos/r4-before",
  "refName": "new",
  "wallMs": 10127,
  "outcome": "Failure",
  "exit": {
    "_tag": "Failure",
    "cause": [
      {
        "_tag": "Fail",
        "error": {
          "_tag": "GitCommandError",
          "operation": "GitVcsDriver.switchRef.checkout",
          "command": "git",
          "cwd": "/tmp/ev-23539/repos/r4-before",
          "argumentCount": 3,
          "detail": "Git command timed out."
        }
      }
    ]
  }
}
```
```
HEAD: old
status:
 M a.txt
?? .gitattributes
ls: cannot access '/tmp/ev-23539/repos/r4-before/.git/index.lock': No such file or directory
```

### r4-after
```json
{
  "port": 47392,
  "cwd": "/tmp/ev-23539/repos/r4-after",
  "refName": "new",
  "pendingAtClose": true,
  "closedAfterMs": 60007,
  "closedAtEpochMs": 1792673111000
}
```
```
HEAD: old
status:
 M a.txt
?? .gitattributes
ls: cannot access '/tmp/ev-23539/repos/r4-after/.git/index.lock': No such file or directory
/tmp/ev-23539/repos/r4-after: old [ M a.txt;?? .gitattributes;]
/tmp/ev-23539/repos/r4-after-fast: feature []
```

### r4-after-second-same-repo
```json
{
  "port": 47392,
  "cwd": "/tmp/ev-23539/repos/r4-after",
  "refName": "new",
  "wallMs": 72,
  "outcome": "Failure",
  "exit": {
    "_tag": "Failure",
    "cause": [
      {
        "_tag": "Fail",
        "error": {
          "_tag": "GitCommandError",
          "operation": "GitVcsDriver.switchRef.checkout",
          "command": "git",
          "cwd": "/tmp/ev-23539/repos/r4-after",
          "argumentCount": 3,
          "exitCode": 1,
          "stdoutLength": 0,
          "stderrLength": 323,
          "detail": "git checkout failed"
        }
      }
    ]
  }
}
```

### r4-after-second-fast
```json
{
  "port": 47392,
  "cwd": "/tmp/ev-23539/repos/r4-after-fast",
  "refName": "feature",
  "wallMs": 113,
  "outcome": "Success",
  "exit": {
    "_tag": "Success",
    "value": {
      "refName": "feature"
    }
  }
}
```

### r4 process timeline
```
driver-after pid 1960398
driver-before pid 1960401
--- t+3s @ 12:44:13.872
before filter pid 1960441: 1960441 1960438       00:02 /bin/sh -c echo $$ > /tmp/ev-23539/r4-before.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-before.pid; sleep 600; cat
after filter pid 1960445: 1960445 1960443       00:02 /bin/sh -c echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat
  proc 1960438 ppid=1958364 cwd=/tmp/ev-23539/repos/r4-before: git checkout new -- 
  proc 1960441 ppid=1960438 cwd=/tmp/ev-23539/repos/r4-before: /bin/sh -c echo $$ > /tmp/ev-23539/r4-before.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-before.pid; sleep 600; cat 
  proc 1960442 ppid=1960441 cwd=/tmp/ev-23539/repos/r4-before: sleep 600 
  proc 1960443 ppid=1958366 cwd=/tmp/ev-23539/repos/r4-after: git checkout new -- 
  proc 1960445 ppid=1960443 cwd=/tmp/ev-23539/repos/r4-after: /bin/sh -c echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat 
  proc 1960446 ppid=1960445 cwd=/tmp/ev-23539/repos/r4-after: sleep 600 
--- t+13s @ 12:44:24.408
before filter pid 1960441: GONE
after filter pid 1960445: 1960445 1960443       00:13 /bin/sh -c echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat
  proc 1960443 ppid=1958366 cwd=/tmp/ev-23539/repos/r4-after: git checkout new -- 
  proc 1960445 ppid=1960443 cwd=/tmp/ev-23539/repos/r4-after: /bin/sh -c echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat 
  proc 1960446 ppid=1960445 cwd=/tmp/ev-23539/repos/r4-after: sleep 600 
--- t+55s @ 12:45:06.864
before filter pid 1960441: GONE
after filter pid 1960445: 1960445 1960443       00:55 /bin/sh -c echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat
  proc 1960443 ppid=1958366 cwd=/tmp/ev-23539/repos/r4-after: git checkout new -- 
  proc 1960445 ppid=1960443 cwd=/tmp/ev-23539/repos/r4-after: /bin/sh -c echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat echo $$ > /tmp/ev-23539/r4-after.pid; sleep 600; cat 
  proc 1960446 ppid=1960445 cwd=/tmp/ev-23539/repos/r4-after: sleep 600 
driver-after alive at t+55: 1960398
socket closed (driver exited) @ 12:45:11
--- close+3s @ 12:45:14.123
before filter pid 1960441: GONE
after filter pid 1960445: GONE
--- close+10s @ 12:45:21.534
before filter pid 1960441: GONE
after filter pid 1960445: GONE
```

### r2 git state
```
r2-before HEAD: feature status: []
r2-after HEAD: feature status: []
```
