mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(git): reviewer reads must prefer origin over a diverged local ref (#690)
* fix(git): reviewer reads must prefer origin over a diverged local ref _resolve_head_ref (GitService.diff/list_changed_files/read_file_at_branch) kept local priority on ANY divergence from origin, real or rewritten. A reviewer's clone parked on pre-rebase history after the branch's routine force-push sync stayed frozen there across every subsequent review round, while origin held every fix commit — QA repeatedly bounced work that had already landed. Every caller here is a reader, never the branch's own author mid-write, so origin now wins whenever it carries anything the local ref lacks; local keeps priority only when it strictly contains origin (unpushed commits, or equal). The read-only git MCP surface (roboco_git_log) hit the same staleness through a separate path: /api/git/log resolved the requested branch as a bare name straight off whatever the caller's own clone had on disk, with no fetch at all. It now routes through the same fixed _resolve_head_ref. * test(e2e): give the armed flow-verb timeout real headroom The armed value is also verb-2's entire execution budget (claim + every claim guard + set_plan + start + tracing gate), which grows as guards land; 1s flaked on loaded CI runners while passing locally. The cancel-and-release semantics only need the timeout far below the hang. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -266,6 +266,37 @@ async def test_log_with_branch_success(git_client: dict) -> None:
|
||||
assert [c["author"] for c in commits] == ["me", "you"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_log_resolves_through_head_ref_not_bare_branch(
|
||||
git_client: dict,
|
||||
) -> None:
|
||||
"""The route must route the requested branch through
|
||||
``_resolve_head_ref`` (fetch + prefer origin) instead of handing git the
|
||||
bare branch name straight off whatever this clone happens to have on
|
||||
disk — this clone is the CALLER's own, never the branch owner's, and a
|
||||
left-over local ref from an earlier inspection can be pinned stale
|
||||
(live 2026-07-24: a QA clone read a commit 5 review rounds old)."""
|
||||
log_result = MagicMock()
|
||||
log_result.returncode = 0
|
||||
log_result.stdout = ""
|
||||
with patch("roboco.api.routes.git.get_git_service") as mock_get:
|
||||
svc = AsyncMock()
|
||||
svc.get_workspace = AsyncMock(return_value="/tmp/ws")
|
||||
svc._token_for_branch = AsyncMock(return_value="tok")
|
||||
svc._resolve_head_ref = AsyncMock(return_value="origin/feature/x")
|
||||
svc._run_git = AsyncMock(return_value=log_result)
|
||||
mock_get.return_value = svc
|
||||
response = await git_client["client"].get(
|
||||
f"/api/git/log?project_slug={git_client['project'].slug}&branch=feature/x",
|
||||
headers=_HDR,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
svc._resolve_head_ref.assert_awaited_once_with("/tmp/ws", "feature/x", token="tok")
|
||||
svc._run_git.assert_awaited_once()
|
||||
logged_args = svc._run_git.await_args.args[1]
|
||||
assert logged_args[-1] == "origin/feature/x"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_log_no_branch_fetches_current(git_client: dict) -> None:
|
||||
log_result = MagicMock()
|
||||
|
||||
Reference in New Issue
Block a user