Routing: New-issue candidate; data-loss fix needs maintainer triage. Bounded searches for checkout/force, checkout/local commits, and discard/data-loss titles found no exact report inspected here; results are partial. #7928 concerns explaining checkout failures from uncommitted changes, not evidence that forced local PR checkout preserves work. Recheck reports before filing.
Environment and verification limits
Research date: 2026-10-03. Fetched origin/main successfully; target: 7ff2eabf56. Local research host: macOS / Darwin 25.5.0 arm64, Node v24.12.0, gh 2.94.0. Main pins Effect 4.0.0-rc.115. This lane used read-only source comparisons and existing evidence; it did not launch a server, browser, desktop app, or live forge checkout. Historical regression results below are explicitly historical, not fresh runtime reproductions on this SHA. Source fingerprints and exact verification scope are in verification.json.
Reproduction environment
Use a disposable clone of a repository with an existing GitHub PR and working local gh authentication. Let H be its head branch and R its remote head. This reproduction intentionally exercises destructive current behavior only in that disposable clone; never use a checkout containing valuable work. The lane itself did not perform a live checkout.
Exact reproduction on current main — local commits
- In the disposable clone, check out H at R and create an extra local commit L on a tracked file. Do not push L. Record H's full commit ID and
git log --oneline -3; H is now ahead of the PR.
- Keep H checked out. Register/select this clone as the project's current checkout in T3 and open the PR panel.
- Choose Check out here (server
preparePullRequestThread with mode: "local").
- Inspect H/HEAD and
git log --oneline -3 again. Main delegates to gh pr checkout <reference> --force; gh documents that flag as “Reset the existing local branch to the latest state of the pull request.” The local branch can move from L back to R, removing L from branch history. The commit may remain recoverable in the reflog; this report does not claim immediate object deletion.
Exact reproduction on current main — tracked edits
- Start a separate disposable copy with H checked out at the PR head R.
- Edit a tracked file without committing; record
git diff and the file contents. Test an unstaged edit and a staged edit separately.
- Invoke the same Check out here action. The server does not check dirty tracked files before handing off forced checkout. On a forced reset of the current head branch, tracked work can be discarded. Do not generalize this to every branch-switch case: a CLI may refuse some conflicting switches.
- Compare file contents, staged/unstaged diff, branch, and HEAD. A successful reset must not be reported as safe merely because the branch lands on the PR.
Control: a clean disposable checkout without divergent local commits should still be able to check out the PR normally after the minimal correction.
Actual versus expected
Actual: local mode always sets force: true; the GitHub adapter emits --force. No tracked-edit refusal runs before it. Local commits can disappear from the branch and tracked work is exposed to forced reset; the successful response unconditionally reports isOnPullRequestHead: true. Expected: the ordinary checkout action preserves local work. Refuse tracked staged/unstaged edits before a mutation, avoid forced checkout, and let an unforced checkout refuse a divergent branch instead of resetting it.
User impact
A routine PR action can discard edits or remove unpublished commits from the working branch. Commit recovery may require reflog knowledge; uncommitted edits may have no recovery path. Web and desktop reach the same server action. This is a forge checkout issue, independent of Codex/Claude execution adapters or remote transport.
Current-main verification
- The complete local-mode block is byte-for-byte unchanged from the reproduction baseline:
checkoutChangeRequest receives force: true before statusDetails is read.
- GitHubCli still adds
--force when that input is true, and the existing main test prepares pull request threads in local mode by checking out the PR branch explicitly expects pr checkout 64 --force.
- Read-only
gh pr checkout --help on the research host confirms the force-reset contract. Neither that help output nor a passing happy-path test alone proves live data loss.
- The historical regression fixture did fail on the older baseline, and the relevant main implementation is unchanged. Today's confirmation is source/CLI-contract based; no destructive real-gh reproduction or new browser evidence was run here.
Source: apps/server/src/git/GitManager.ts; apps/server/src/sourceControl/GitHubCli.ts; apps/server/src/git/GitManager.test.ts.
Minimal proposed fix scope — no implementation
On current main, remove the unconditional force option from local PR checkout and add a focused guard refusing tracked staged/unstaged edits before calling the provider. Test dirty refusal, preservation of a divergent same-named local branch, and successful clean checkout. Keep untracked-file policy unchanged unless a necessary Git conflict makes checkout refuse. This concern does not depend on P10 or P11. No rollback engine, git-config replay, new network/head verification, tracking contract fields, warning UI, or forced-reset workaround.
Evidence available
- Historical P12-before.log: exit 1, 16 failed / 1 passed on the earlier baseline; the dirty-checkout fixture fails, and output records forced checkout. The fake gh boundary models branch/reset behavior using real temporary Git repositories, so this is not a live gh-server reproduction.
- P12.diff contains the fixtures; independent review confirms the data-loss concern and requests the minimal P12a split. Broader branch pass counts are not proof that this minimal split already exists.
- Fresh verification.json records identical local-mode source; the CLI flag semantics were read from gh help.
- Still needed for implementation evidence: a disposable real-gh clone and web before/after screenshots showing the local commit/file contents, refusal, and survival of the original work. No such current-main media was captured in this lane.
🤖 Generated with Claude Code
Routing: New-issue candidate; data-loss fix needs maintainer triage. Bounded searches for checkout/force, checkout/local commits, and discard/data-loss titles found no exact report inspected here; results are partial. #7928 concerns explaining checkout failures from uncommitted changes, not evidence that forced local PR checkout preserves work. Recheck reports before filing.
Environment and verification limits
Research date: 2026-10-03. Fetched
origin/mainsuccessfully; target: 7ff2eabf56. Local research host: macOS / Darwin 25.5.0 arm64, Node v24.12.0, gh 2.94.0. Main pins Effect 4.0.0-rc.115. This lane used read-only source comparisons and existing evidence; it did not launch a server, browser, desktop app, or live forge checkout. Historical regression results below are explicitly historical, not fresh runtime reproductions on this SHA. Source fingerprints and exact verification scope are in verification.json.Reproduction environment
Use a disposable clone of a repository with an existing GitHub PR and working local
ghauthentication. Let H be its head branch and R its remote head. This reproduction intentionally exercises destructive current behavior only in that disposable clone; never use a checkout containing valuable work. The lane itself did not perform a live checkout.Exact reproduction on current main — local commits
git log --oneline -3; H is now ahead of the PR.preparePullRequestThreadwithmode: "local").git log --oneline -3again. Main delegates togh pr checkout <reference> --force; gh documents that flag as “Reset the existing local branch to the latest state of the pull request.” The local branch can move from L back to R, removing L from branch history. The commit may remain recoverable in the reflog; this report does not claim immediate object deletion.Exact reproduction on current main — tracked edits
git diffand the file contents. Test an unstaged edit and a staged edit separately.Control: a clean disposable checkout without divergent local commits should still be able to check out the PR normally after the minimal correction.
Actual versus expected
Actual: local mode always sets
force: true; the GitHub adapter emits--force. No tracked-edit refusal runs before it. Local commits can disappear from the branch and tracked work is exposed to forced reset; the successful response unconditionally reportsisOnPullRequestHead: true. Expected: the ordinary checkout action preserves local work. Refuse tracked staged/unstaged edits before a mutation, avoid forced checkout, and let an unforced checkout refuse a divergent branch instead of resetting it.User impact
A routine PR action can discard edits or remove unpublished commits from the working branch. Commit recovery may require reflog knowledge; uncommitted edits may have no recovery path. Web and desktop reach the same server action. This is a forge checkout issue, independent of Codex/Claude execution adapters or remote transport.
Current-main verification
checkoutChangeRequestreceivesforce: truebeforestatusDetailsis read.--forcewhen that input is true, and the existing main testprepares pull request threads in local mode by checking out the PR branchexplicitly expectspr checkout 64 --force.gh pr checkout --helpon the research host confirms the force-reset contract. Neither that help output nor a passing happy-path test alone proves live data loss.Source:
apps/server/src/git/GitManager.ts;apps/server/src/sourceControl/GitHubCli.ts;apps/server/src/git/GitManager.test.ts.Minimal proposed fix scope — no implementation
On current main, remove the unconditional force option from local PR checkout and add a focused guard refusing tracked staged/unstaged edits before calling the provider. Test dirty refusal, preservation of a divergent same-named local branch, and successful clean checkout. Keep untracked-file policy unchanged unless a necessary Git conflict makes checkout refuse. This concern does not depend on P10 or P11. No rollback engine, git-config replay, new network/head verification, tracking contract fields, warning UI, or forced-reset workaround.
Evidence available
🤖 Generated with Claude Code