# PR #22836 proof audit, pass 2

**Verdict: SUFFICIENT**

Head `fbe839d80b64472ecf9efbdfe06c5f06f44ea393`, verified with `git fetch` and `rev-parse origin/fix/issue-22796`. Auditor rebuild at that head on 2026-10-22.

## (a) SHAs
- `git merge-base origin/fix/issue-22796 origin/main` = `8d3b061121fe65669d5d43d011b48cd3027a4108`. This matches captures "before".
- `git diff 6c15210198 fbe839d80b --stat` shows only `apps/server/scripts/evidence/pr-22836-fork-moved-root.ts` (+401). Captures taken at `6c15210198` therefore apply to the current head.
- `summary.json` `revision` matches its label in both `final-before` (`8d3b0611…`) and `final-after` (`6c152101…`).

## (b) Per-row check against raw logs

| Row | Claim | Evidence checked | ok/gap |
|---|---|---|---|
| R1 | Moved-root fork, in-process path. Before: fails "Session … not found in project directory". After: works | `final-before/server.log` has 8× `not found in project directory`. `summary.json` moved forks: runs `failed`, provider thread `active/None`, fork transfer `pending/None`. Only the source jsonl is under slug moved-a. `final-after`: forks `completed`, `idle/ef957d10…`, `idle/5e2615b0…`, `consumed/native_fork`, "fork a ok."/"fork b ok.", and 0 matches for the error. All 3 jsonl are under slug moved-a | ok |
| R2 | Custom CLAUDE_CONFIG_DIR (worker path). Before: fail. After: pass | `vitest-r2-before.log`: FAIL "forks a session from its original directory…", `Session 1111… not found in project directory for /workspace`, 1 failed / 206 skipped. `vitest-r2-after.log`: 1 passed / 206 skipped | ok |
| R3 | Control fork works at both SHAs | Control in both summaries: forks `completed`, `consumed/native_fork`, both merges `consumed/fork_delta_context` | ok |
| R7 | Sibling forks + merge-back. Before, moved: rejected. After: accepted | Before, moved `merges[]`: both `rejected: … run … is failed; only provider-finished runs are supported.` After: both `accepted`, and the source transfers are `consumed/fork_delta_context` | ok, with a minor note |
| R11 | Source continues after the move | Moved source runs `completed×3` and "source turn 2 ok."/"source turn 3 ok." at both SHAs | ok |
| ThreadFork / ThreadMergeBack integration | Pass at head, from recorded fixtures | `vitest-integration-after.log`: 2 files passed, 6 tests passed. Correctly labelled as fixture replay | ok |
| R4, R5, R6, R9, R10 | unverified, with reasons | Each reason is stated. R6 has no behaviour change by construction. R10 is out of scope and the code is unchanged. R4 has fixture coverage at head. R5 and R9 are untested edge cases with low risk (the SDK search is unrestricted, and session ids are UUIDs) | acceptable |
| R8 | n/a: fixtures no test consumes | Consistent with the matrix | ok |

**Minor inaccuracy in R7, not outcome-changing.** The captures table lists the before/moved source's merge-back transfers as "-". The actual `summary.json` holds two merge-back transfers with status `pending` and strategy `null` on the before/moved source. The rejected merge-backs therefore left stuck pending transfer records at merge-base. That reinforces the "before is broken" claim, and the after run is clean. The table should say `pending/None ×2`.

## (c) Context
- An Environment line is present: OS, Node, Claude CLI 2.1.303, SDK version, model `claude-sonnet-4-6` through the hub, and live turns.
- A "What else changed" section is present. It covers control parity, the extra empty moved-b slug dir, the source cwd still being A at both SHAs (not a regression), the 25 merged main commits already in the merge-base, and the driver interleaving.
- Isolation is described. All server PIDs recorded in `*/server.pid` (328879, 330004, 332547, 332553) are confirmed exited.

## (d) Rebuild at head `fbe839d80b`
I ran `node apps/server/scripts/evidence/pr-22836-fork-moved-root.ts --out /tmp/ev-22836/audit2-run` with `T3CODE_HOME` unset. The driver forces an isolated `--base-dir`, `T3CODE_HOME` and `CLAUDE_CONFIG_DIR`. Live Claude `claude-sonnet-4-6` ran through the hub and the hub accepted the requests. Exit code 0.

- `summary.revision` = `fbe839d80b64472ecf9efbdfe06c5f06f44ea393`.
- **R1, moved:** root `moved-a` → `moved-b`. Both forks ran `completed`, provider threads `idle/3ae29942…` and `idle/3119f01c…`, transfers `consumed/native_fork`, replies "fork a ok."/"fork b ok.". `server.log` has 0 matches for `not found in project directory`. All three jsonl landed under slug moved-a, and moved-b is empty. **This matches the captures' after.**
- **R7:** both moved merge-backs were `accepted`, the source transfers are `consumed/fork_delta_context`, and the source turns completed. Matches.
- **R3:** the control scenario has the same shape as the captures. Matches.
- **R11:** source runs `completed×3`. Matches.
- Server PID 336490 was spawned by the driver, stopped by its `finally`, and confirmed exited. `/tmp/ev-22836/audit2-run*` was deleted afterwards.

I did not rebuild the before side. Its outcome is fully supported by `final-before` and by an earlier exploratory pair (`before-run/`) that gave the same result.

## Missing rows
None that block. Optional: a symlink probe for R9 and a whole-thread fork for R5.
