Skip to content

agentHost: stop a failed git probe from stalling Agent Merge - #331792

Merged
Benjamin Christopher Simmonds (benibenj) merged 2 commits into
mainfrom
benibenj/agents/agent-merge-issue-investigation
Aug 20, 2026
Merged

agentHost: stop a failed git probe from stalling Agent Merge#331792
Benjamin Christopher Simmonds (benibenj) merged 2 commits into
mainfrom
benibenj/agents/agent-merge-issue-investigation

Conversation

@benibenj

Copy link
Copy Markdown
Contributor

Fixes a class of bug where a session with Agent Merge enabled sits idle forever without ever binding to its pull request, so review comments and CI failures never reach it.

Found while investigating a real session (copilotcli:/7535d422-…, PR #331760). Agent Merge was enabled and running — it had injected its autonomous configuration and the host was keeping the session resident for it:

[AgentService] Skipping idle eviction for a session held by Agent Merge: copilotcli:/7535d422-…

…but every evaluation died on the first check, exactly backstopInterval apart:

10:57:06 [AgentMergeController] Evaluation started: session=copilotcli:/7535d422-…
10:57:06 [AgentMergeController] Waiting for a current branch: session=copilotcli:/7535d422-…
11:07:06 [AgentMergeController] Evaluation started: session=copilotcli:/7535d422-…
11:07:06 [AgentMergeController] Waiting for a current branch: session=copilotcli:/7535d422-…

Its agentMerge.controller state had no target, confirming it never got far enough to bind the PR — so it never subscribed to PR events, and a review comment and a failing check both went unnoticed.

Root cause

The session's persisted git state had lost its branch, while the worktree itself was perfectly healthy:

// session_metadata['agentHost.git']
{ "baseBranchName": "main" }   // no branchName, no hasGitHubRemote, no uncommittedChanges

_computeSessionGitState runs its probes independently and fills fields best-effort. parseGitStatusV2 always yields uncommittedChanges: 0 for any non-empty output, so its absence means git status itself returned undefined. The repository root is cached, so the function still returned an object — just a branch-less one. _setSessionGitState replaces persisted git state wholesale, so that object overwrote the good branch.

The failure left no trace: _runGit only logged when stderr was non-empty, and a timeout kill produces none.

Why it never recovered

Three separate paths that should have repaired it did not:

  1. AgentMergeController._evaluate calls attachSessionGitHubPullRequest (which begins with refreshSessionGitState), but only after the if (!branchName) return bail — so its own repair path was unreachable.
  2. The lazy refresh on subscribe only fired when git state was entirely undefined; a partial state counted as "already computed" and masked it.
  3. The file-monitor refresh needs watch interest plus an actual file change. A session held resident by Agent Merge alone — no client watching, no edits landing — gets neither.

Changes

  • agentHostGitService — return undefined when the status probe fails, so callers keep the state they already had instead of persisting a misleading one.
  • agentMergeController — refresh git state before giving up on the branch. This also recovers sessions already stuck in this state, which matters because the bad state is persisted.
  • agentService — treat a branch-less state as missing in the subscribe-time refresh.
  • agentHostGitService — log git failures that produce no stderr (warn for timeouts/signals, trace for the --quiet probes' expected non-zero exits), so this can't fail invisibly again.

Validation

agentMergeController.test.ts gains a regression test that reproduces the stall. Reverting just the controller change makes it fail the same way the real session did:

1) AgentMergeController
     recovers a session whose persisted git state lost its branch:
   Error: Timeout of 5000ms exceeded.

The git-probe change is covered by a new integration test, and was verified end to end against the compiled service: with the fix the degraded probe returns undefined and logs Not reporting session git state because git status failed; without it, it returns {} — the same shape as the corruption observed on disk.

Full agentHost unit suite: 5840 passing. The 11 failures are pre-existing and unrelated (sessionPermissions, agentHostDebugLogs, agentHostRequestService, copilotAgent, localAgentHostMetadata).

A session with Agent Merge enabled could sit idle forever without ever
binding to its pull request, so review comments and CI failures never
reached it.

`_computeSessionGitState` treats every probe as independent and populates
fields best-effort. When `git status` failed -- most often a timeout under
load, which writes nothing to stderr and so logged nothing at all -- it
still returned an object, just without a branch. `_setSessionGitState`
replaces persisted git state wholesale, so that object overwrote the good
branch with `{"baseBranchName":"main"}`.

Nothing then repaired it. `AgentMergeController._evaluate` bails on a
missing branch before it reaches the refresh that would recompute it, and
the lazy refresh on subscribe only fires when git state is entirely
absent, so a partial state masked it. For a session held resident by Agent
Merge alone -- no client watching, no edits landing -- neither of the
remaining refresh triggers fires either, leaving it to re-read the same
stale state on the 10 minute backstop indefinitely.

- Return `undefined` from `_computeSessionGitState` when the status probe
  fails, so callers keep the state they already had.
- Refresh git state in `_evaluate` before giving up on the branch, which
  also recovers sessions already holding a branch-less state.
- Treat a branch-less state as missing in the subscribe-time refresh.
- Log git failures that produce no stderr, so a timed-out probe is no
  longer invisible.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings August 20, 2026 13:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Prevents Agent Merge from stalling indefinitely when persisted session git state loses its branch (e.g., after a failed git status probe), by avoiding persistence of misleading partial git state and adding a repair path that refreshes git state when a branch is missing.

Changes:

  • Add a recovery path in AgentMergeController that refreshes session git state when no branch is available.
  • Make AgentHostGitService return undefined (instead of partial state) when git status fails, and improve logging for silent failures/timeouts.
  • Add/extend tests to cover the “probe fails → no git state persisted” behavior and the Agent Merge recovery behavior.
Show a summary per file
File Description
src/vs/platform/agentHost/test/node/agentMergeController.test.ts Adds a regression test ensuring Agent Merge recovers when persisted git state is missing a branch.
src/vs/platform/agentHost/test/node/agentHostGitService.integrationTest.ts Adds an integration test verifying that a failed status probe yields no git state (prevents persisting partial state).
src/vs/platform/agentHost/node/agentService.ts Treats “git state with no branchName” as missing to trigger lazy git-state repair for watchers.
src/vs/platform/agentHost/node/agentMergeController.ts Adds branch resolution + repair (_resolveCurrentBranch) to unblock Agent Merge when branch is missing.
src/vs/platform/agentHost/node/agentHostGitService.ts Avoids returning/persisting partial git state when git status fails; improves failure logging in _runGit.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread src/vs/platform/agentHost/node/agentMergeController.ts
Comment thread src/vs/platform/agentHost/node/agentService.ts Outdated
Addresses PR review feedback: keying the repair off a missing `branchName`
alone also matched a detached HEAD, which reports no branch by design.
Those sessions would have refreshed git state on every evaluation --
a periodic git call and log noise that could never produce a branch.

`parseGitStatusV2` already recognises `(detached)`; it now reports that as
`isDetachedHead` so the distinction survives into persisted session git
state, and a shared `needsSessionGitStateRefresh` predicate keeps the
Agent Merge and subscribe-time call sites in agreement about which states
are worth recomputing.

The controller additionally caps the repair at one attempt per runtime, so
any other checkout that cannot report a branch costs a single git call
rather than one per backstop, and logs a warning when a refresh still
yields no branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benibenj
Benjamin Christopher Simmonds (benibenj) merged commit 56a267c into main Aug 20, 2026
44 of 45 checks passed
@benibenj
Benjamin Christopher Simmonds (benibenj) deleted the benibenj/agents/agent-merge-issue-investigation branch August 20, 2026 20:12
@vs-code-engineering vs-code-engineering Bot added this to the 1.135.0 milestone Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants