Repository navigation
fix(agent-manager): do not inherit a merged PR on a reused branch name - #14145
Conversation
Agent Manager derives a worktree branch from the session title, so a repeated title recreates the branch name of an already merged PR. gh's finder, and the batched lookup that mirrors it, return that merged PR by head ref name, so the new worktree showed the old PR, and merge or review actions failed with "The pull request is no longer open." Keep a merged or closed PR only when the checkout contains it: its head is the local HEAD, or its head is reachable from HEAD while the merge into the base is not. Open and draft PRs are unaffected and need no git calls. This also covers the legacy gh pr view path, which had the same attribution. Adds mergeCommit to the batched query fields and to gh pr view --json, so no extra request is needed.
Code Review SummaryStatus: 3 Issues Found | Recommendation: Address before merge Overview
Fix these issues in Kilo Cloud Issue Details (click to expand)SUGGESTION
Files Reviewed (1 file in this commit, 2 re-verified)
Notes: fork hygiene is fine (all changes are in Previous Review Summaries (3 snapshots, latest commit 76194f1)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 76194f1)Status: 4 Issues Found | Recommendation: Address before merge Overview
Fix these issues in Kilo Cloud Issue Details (click to expand)SUGGESTION
Files Reviewed (1 file in this commit, 3 re-verified)
Previous review (commit a6edb7d)Status: 4 Issues Found | Recommendation: Address before merge Overview
Fix these issues in Kilo Cloud The follow-up commit Issue Details (click to expand)SUGGESTION
Files Reviewed (2 in this commit, 2 re-verified)
Notes: fork hygiene is fine (all changes are in Previous review (commit c1195f0)Status: 3 Issues Found | Recommendation: Address before merge Overview
Fix these issues in Kilo Cloud The core guard is correct and well tested: open/draft PRs short-circuit with no git work, squash-merged and recreated-branch cases are dropped, and legitimate PR-head checkouts (including Issue Details (click to expand)SUGGESTION
Files Reviewed (7 files)
Notes: fork hygiene is fine (all changes are in Reviewed by deepseek-v4.1-flash · Input: 59.1K · Output: 9K · Cached: 273.8K Review guidance: REVIEW.md from base branch |
…hip guard `merge-base --is-ancestor` is reflexive, so the guard no longer reads HEAD to compare it with the PR head. A commit git cannot resolve is absent from the local object store, so it cannot be in HEAD's history either.
Removing `git rev-parse HEAD` made "the PR head is HEAD" a case of "the PR head is an ancestor", so the two stub tests asserted the same path. Keep one and assert the exact git calls. The reflexivity the guard now depends on cannot be modelled by the stub, so check it against a real repository together with the ancestor and merge-commit cases.
`execFileSync` without `windowsHide` flashes a cmd.exe window on Windows, as the sibling git fixture in base-update.test.ts already avoids.
What Problem This Solves
A new Agent Manager worktree can display an old merged pull request that it never belonged to, and merge or review actions on that PR fail with
The pull request is no longer open.Agent Manager derives a worktree branch from the session title, so a repeated title recreates the branch name of an already merged PR.
gh's finder, and the batched lookup that mirrors it, resolve a PR by head ref name and return the newest merged or closed PR of that name. The worktree therefore inherited a merged PR whose commits are not in its history.Concretely, PR #12726 (
fix(cli): speed up local recall searches, merged 2026-08-03) used branchoptimize-kilo-local-recall-performance. Running the same task title again created a worktree with the identical branch name, and the card showed #12726.Why This Change Was Made
The branch name is the only key GitHub has, so the lookup itself is correct. The mistake is attributing a non-open PR to a checkout that does not contain it. A merged or closed PR is now kept only when the checkout can contain it:
HEADequals the PR headHEADand the merge commit is not, which covers a merged PR with local commits on topIt is dropped when the PR head is not reachable from
HEAD(squash or rebase merges, closed PRs, a recreated branch name), or when the PR's merge commit is reachable (the branch was cut from the base after the merge).The same guard runs for both the batched result and the legacy
gh pr viewresult, because both had the same attribution. The batched GraphQL query gains one scalar field (mergeCommit { oid }) andgh pr view --jsongains the same field, so there is no extra request, no extra rate-limit cost of note, and no lookup at worktree creation. For open PRs the guard adds no git work at all.Branch name deduplication at creation was deliberately not extended.
WorktreeManager.availableBranchalready suffixes names that exist inrefs/headsor as a worktree directory, but the old branch was deleted locally and on the remote, so only GitHub still knew the name. Aghcheck at creation would add a network round trip and a dependency to worktree creation, and would only cover that one collision source.User Impact
A worktree that only reuses a merged PR branch name no longer shows that PR. Worktrees that genuinely sit on a merged PR keep the merged badge, including PR review checkouts and PRs checked out through
refs/pull/N/head, and open PRs are unaffected.Within a running extension session, a card that already shows the wrong PR keeps it until the next reload (
retainPRStatusintentionally keeps a known PR when an empty result arrives for the same branch). New worktrees are correct immediately.Evidence
Agent-executed:
bun testonam-pr-utils,am-pr-status-bridge,am-pr-batch,am-pr-seed,agent-manager-arch,ghfrompackages/kilo-vscode: 321 pass, 0 fail. New coverage: 7 unit cases for the guard,mergeCommitparsing, and a batched full-sync case for the recreated branch name.bun run lintandbun run knipinpackages/kilo-vscode: clean.parsePRResultoptional OID fields were extracted into a helper to stay under the complexity limit.optimize-kilo-local-recall-performance, HEAD2d9c6eda1b7), with the guard fed realgh pr viewoutput and realgithistory: fix(cli): speed up local recall searches #12726 (squash merge, head1fb15ce) is rejected, and PR docs(kilo-docs): consolidate older docs-sync rollovers #14135 (merge commit PR whose merge commit is the worktree HEAD) is rejected.bun run typecheckinpackages/kilo-vscodereports 216 errors from a stale SDK/dependency build in this worktree (KiloClient.v2, missing@opencode-ai/core/kilocode/zero-id). The same count and the same errors occur without this change, and none are in the touched files.How to Test
Agent-executed manual verification, using the real worktree and live GitHub data (no writes to the repository):
gh pr view <branch> --json state,headRefOid,mergeCommitfor a branch name that matches an old merged PR. It returns the merged PR.git merge-base --is-ancestor <pr head> HEADin that worktree. It exits non-zero, so the PR is not in the checkout.Reviewer steps: