mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(grok): unbreak workspace-cwd agents, free trapped agents, stop self-PR review
Three bugs surfaced by the first live Grok lifecycle run: - Dev/QA/doc agents crash-looped at startup with ModuleNotFoundError on roboco.llm.providers. The entrypoint ran the opencode-config render from the agent's workspace-clone cwd, whose own roboco/ dir shadows /app on the sys.path front; a branch without the grok code lacks the providers package. Render from /app so the installed package always resolves (the render has no cwd dependency — writes global, reads ROBOCO_MCP_CONFIG). - A budget/loop halt blocked EVERY tool, including i_am_idle, unclaim, and i_am_blocked, so a halted agent could neither continue nor stop and flailed — one billed model turn per blocked retry. The before-gate now always lets the release verbs through so a halted agent can exit cleanly. - The inbound reviewer ingested the org's OWN PRs (authored by the repo-owner account), which can't take a REQUEST_CHANGES review (GitHub 422) and get re-reviewed every poll. The normalizer flags author_is_owner and ingestion skips them — the reviewer reviews only PRs the org did not author. External/contributor PRs are unaffected. Tests: owner-authored PR flagged + skipped; normalize shape covers the new field. Gate green on the changed modules (ruff/mypy/xenon + 48 tests).
This commit is contained in:
@@ -196,3 +196,24 @@ async def test_skip_internal_when_disabled(monkeypatch: pytest.MonkeyPatch) -> N
|
||||
)
|
||||
assert ok is False
|
||||
svc.ingest_external_pr.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_skip_owner_authored_pr(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
# The org's own account opened the PR → self-review, never ingest (even with
|
||||
# both review modes on). The reviewer reviews PRs the org did NOT author.
|
||||
monkeypatch.setattr(orch_mod.settings, "external_pr_enabled", True)
|
||||
monkeypatch.setattr(orch_mod.settings, "internal_pr_enabled", True)
|
||||
svc = _svc()
|
||||
pr = {
|
||||
"number": 213,
|
||||
"is_fork": False,
|
||||
"author_is_owner": True,
|
||||
"author_association": "OWNER",
|
||||
"head_ref": "feat/grok-provider-seam",
|
||||
}
|
||||
ok = await _orch()._ingest_pr_if_reviewable(
|
||||
svc, SimpleNamespace(id=uuid4()), pr, uuid4(), set()
|
||||
)
|
||||
assert ok is False
|
||||
svc.ingest_external_pr.assert_not_awaited()
|
||||
|
||||
@@ -94,11 +94,31 @@ async def test_list_open_prs_normalizes_and_flags_fork() -> None:
|
||||
"head_sha": "deadbeef",
|
||||
"is_fork": True,
|
||||
"user_login": "corey",
|
||||
"author_is_owner": False,
|
||||
"author_association": "CONTRIBUTOR",
|
||||
}
|
||||
# Same-repo head → not a fork.
|
||||
assert internal["is_fork"] is False
|
||||
assert internal["author_association"] == "MEMBER"
|
||||
# A contributor (not the repo owner) is not flagged as the owner.
|
||||
assert internal["author_is_owner"] is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_open_prs_flags_owner_authored_pr() -> None:
|
||||
"""A PR opened by the repo-owner account is flagged author_is_owner."""
|
||||
svc = _service()
|
||||
payload = [
|
||||
# Author login == repo owner ("acme") → the org's own PR.
|
||||
_pr(number=9, head_full="acme/repo", login="acme", assoc="OWNER"),
|
||||
]
|
||||
client = _client(_resp(200, json_payload=payload))
|
||||
with (
|
||||
_patch_project(),
|
||||
patch("roboco.services.git.httpx.AsyncClient", return_value=client),
|
||||
):
|
||||
out = await svc.list_open_prs("roboco")
|
||||
assert out[0]["author_is_owner"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user