mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[a1f52d13] Rebuild evidence-assembly timeout fix on current main (#796)
* [a1f52d13] feat(gateway): re-apply evidence-assembly timeout fix on current main
Rebuilds the bounded-timeout fix for claim_review/evidence()/roboco_git_diff
that PR #765 carried as contaminated pre-Board-Programs file copies. The
rebase restored the current-main baseline; this commit re-applies ONLY the
validated timeout-fix hunks on top of that baseline:
- config.py: evidence_assembly_timeout_seconds (90.0s) and
conventions_validator_timeout_seconds (45s) Settings fields
- envelope.py: Envelope.gateway_timeout classmethod for structured
timeout errors naming the stalled component
- git.py: GitService.diff_and_files (single resolution, concurrent
diff+--name-only subprocesses), settings-backed conventions-validator
timeout, and changed_files passthrough to conventions_check_for_task
- qa.py: claim_review bounded-timeout wrapper around
_build_qa_claim_evidence, split into _qa_git_and_conventions and
_qa_db_reads concurrent segments (pass_review signature and gate
untouched)
- content_actions.py: evidence() bounded wrapper with _evidence_git_and_fetch
and _evidence_db_reads concurrent split (all other methods untouched)
- git routes: GET /diff bounded asyncio.wait_for guard with 504 on trip
The pre-existing methods (sync_env_branch, open_sync_pr, pass_review with
criteria_verified gate, all propose_*/request_render methods) are preserved
and untouched. The test files already exist on current main.
* [a1f52d13] fix(git-routes): wrap workspace resolution in bounded timeout for GET /diff
The bounded asyncio.wait_for must cover the workspace resolution step too,
not just the diff+stat subprocesses — the integration test makes
get_workspace slow (not _run_git), so the timeout guard needs to enclose
the entire resolve-and-diff path to return a structured 504 on trip.
* [a1f52d13] test: update mock setups from git.diff/list_changed_files to diff_and_files
Tests that exercise claim_review and evidence() paths need their git
mocks to return a tuple from diff_and_files instead of separate diff
and list_changed_files return values, matching the new combined accessor
introduced by the evidence-assembly timeout fix.
* [a1f52d13] test: configure diff_and_files mock in lifecycle parity test _make_deps
The spec-parity test test_claim_review_matches_spec[qa-awaiting_qa] failed
with ValueError at qa.py:330 because _make_deps set git=AsyncMock() without
configuring diff_and_files.return_value, so the await resolved to a MagicMock
instead of a (diff_summary, files_changed) 2-tuple. Adds the same
return_value = ("", []) configuration that commit 014a0d03 applied to the
other 4 test files.
* [a1f52d13] docs(evidence-assembly-timeout): correct GET /diff guard scope and _qa_git_and_conventions timing field
---------
Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
This commit is contained in:
co-authored by
Backend Developer 2
Backend Documenter
parent
89254f796c
commit
ce89a04d26
@@ -41,10 +41,12 @@ def _over_cap_project() -> MagicMock:
|
||||
|
||||
|
||||
def _make_deps(task_svc: AsyncMock, **overrides: Any) -> ChoreographerDeps:
|
||||
git = AsyncMock()
|
||||
git.diff_and_files.return_value = ("", [])
|
||||
base: dict[str, Any] = {
|
||||
"task": task_svc,
|
||||
"work_session": AsyncMock(),
|
||||
"git": AsyncMock(),
|
||||
"git": git,
|
||||
"a2a": AsyncMock(),
|
||||
"journal": AsyncMock(),
|
||||
"audit": AsyncMock(),
|
||||
|
||||
@@ -37,7 +37,7 @@ def _make_deps(**overrides: AsyncMock) -> ContentActionsDeps:
|
||||
else:
|
||||
git = AsyncMock()
|
||||
git.commit.return_value = {"sha": "abc12345"}
|
||||
git.diff.return_value = ""
|
||||
git.diff_and_files.return_value = ("", [])
|
||||
|
||||
a2a = overrides.get("a2a", AsyncMock())
|
||||
journal = overrides.get("journal", AsyncMock())
|
||||
@@ -270,7 +270,7 @@ async def test_evidence_blocks_when_not_assignee() -> None:
|
||||
task_svc = AsyncMock()
|
||||
task_svc.get.return_value = task_obj
|
||||
git_svc = AsyncMock()
|
||||
git_svc.diff.return_value = ""
|
||||
git_svc.diff_and_files.return_value = ("", [])
|
||||
workspace_svc = AsyncMock()
|
||||
deps = _make_deps(task=task_svc, git=git_svc, workspace=workspace_svc)
|
||||
ca = ContentActions(deps)
|
||||
@@ -279,7 +279,7 @@ async def test_evidence_blocks_when_not_assignee() -> None:
|
||||
body = env.as_dict()
|
||||
assert body["error"] == "not_authorized"
|
||||
workspace_svc.fetch_branch_for_inspection.assert_not_awaited()
|
||||
git_svc.diff.assert_not_awaited()
|
||||
git_svc.diff_and_files.assert_not_awaited()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -410,7 +410,7 @@ async def test_evidence_unassigned_task_allows_inspection() -> None:
|
||||
task_svc = AsyncMock()
|
||||
task_svc.get.return_value = task_obj
|
||||
git_svc = AsyncMock()
|
||||
git_svc.diff.return_value = "diff content"
|
||||
git_svc.diff_and_files.return_value = ("diff content", [])
|
||||
workspace_svc = AsyncMock()
|
||||
deps = _make_deps(task=task_svc, git=git_svc, workspace=workspace_svc)
|
||||
ca = ContentActions(deps)
|
||||
@@ -446,8 +446,7 @@ async def test_evidence_allows_dependency_inspection() -> None:
|
||||
task_svc.get.return_value = target
|
||||
task_svc.list_assigned_for_agent.return_value = [callers_task]
|
||||
git_svc = AsyncMock()
|
||||
git_svc.diff.return_value = ""
|
||||
git_svc.list_changed_files.return_value = []
|
||||
git_svc.diff_and_files.return_value = ("", [])
|
||||
workspace_svc = AsyncMock()
|
||||
deps = _make_deps(task=task_svc, git=git_svc, workspace=workspace_svc)
|
||||
ca = ContentActions(deps)
|
||||
|
||||
@@ -7,7 +7,7 @@ Bug:
|
||||
commit's delta, even when GitHub showed a multi-commit change set.
|
||||
|
||||
Fix:
|
||||
Pull files via ``git.list_changed_files(branch_name=...)`` (no base
|
||||
Pull files via ``git.diff_and_files(branch_name=...)`` (no base
|
||||
→ full diff vs parent branch). Pull diff with ``base=None`` so the
|
||||
full PR diff comes through. Both use git as the authoritative source
|
||||
instead of the legacy ``work_session.files_modified`` field, which
|
||||
@@ -65,8 +65,10 @@ async def test_evidence_populates_files_changed_from_git() -> None:
|
||||
task_svc = AsyncMock()
|
||||
task_svc.get.return_value = _task_with_pr(task_id, commits=["abc", "def"])
|
||||
git_svc = AsyncMock()
|
||||
git_svc.diff.return_value = "diff --git a/README.md b/README.md\n+added line\n"
|
||||
git_svc.list_changed_files.return_value = ["README.md", "docs/guide.md"]
|
||||
git_svc.diff_and_files.return_value = (
|
||||
"diff --git a/README.md b/README.md\n+added line\n",
|
||||
["README.md", "docs/guide.md"],
|
||||
)
|
||||
workspace_svc = AsyncMock()
|
||||
evidence_repo = AsyncMock()
|
||||
evidence_repo.journal_highlights_for_task.return_value = []
|
||||
@@ -80,12 +82,12 @@ async def test_evidence_populates_files_changed_from_git() -> None:
|
||||
assert body["error"] is None
|
||||
assert body["evidence"]["files_changed"] == ["README.md", "docs/guide.md"]
|
||||
assert "diff --git" in body["evidence"]["pr_diff_summary"]
|
||||
git_svc.list_changed_files.assert_awaited_once()
|
||||
git_svc.diff_and_files.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evidence_uses_full_pr_diff_not_head_minus_one() -> None:
|
||||
"""git.diff must be called with base=None (full PR diff vs parent),
|
||||
"""diff_and_files must be called with base=None (full PR diff vs parent),
|
||||
not base='HEAD~1' (only the last commit)."""
|
||||
agent_id = uuid4()
|
||||
task_id = uuid4()
|
||||
@@ -94,8 +96,7 @@ async def test_evidence_uses_full_pr_diff_not_head_minus_one() -> None:
|
||||
# task.commits was non-empty, masking earlier commits' changes.
|
||||
task_svc.get.return_value = _task_with_pr(task_id, commits=["sha1", "sha2", "sha3"])
|
||||
git_svc = AsyncMock()
|
||||
git_svc.diff.return_value = "full diff"
|
||||
git_svc.list_changed_files.return_value = []
|
||||
git_svc.diff_and_files.return_value = ("full diff", [])
|
||||
workspace_svc = AsyncMock()
|
||||
evidence_repo = AsyncMock()
|
||||
evidence_repo.journal_highlights_for_task.return_value = []
|
||||
@@ -105,13 +106,13 @@ async def test_evidence_uses_full_pr_diff_not_head_minus_one() -> None:
|
||||
)
|
||||
await ca.evidence(agent_id=agent_id, task_id=task_id)
|
||||
|
||||
git_svc.diff.assert_awaited_once()
|
||||
call_kwargs = git_svc.diff.await_args.kwargs
|
||||
git_svc.diff_and_files.assert_awaited_once()
|
||||
call_kwargs = git_svc.diff_and_files.await_args.kwargs
|
||||
# Pre-fix bug: kwargs['base'] would be 'HEAD~1' for any multi-commit
|
||||
# branch. Post-fix: base is omitted (or explicitly None).
|
||||
base = call_kwargs.get("base")
|
||||
assert base in (None, ""), (
|
||||
f"git.diff must use full-PR diff (base=None), got base={base!r}"
|
||||
f"diff_and_files must use full-PR diff (base=None), got base={base!r}"
|
||||
)
|
||||
assert call_kwargs.get("branch_name") == "feature/backend/abc12345--def67890"
|
||||
|
||||
@@ -125,8 +126,7 @@ async def test_evidence_populates_journal_highlights() -> None:
|
||||
task_svc = AsyncMock()
|
||||
task_svc.get.return_value = _task_with_pr(task_id, commits=["abc"])
|
||||
git_svc = AsyncMock()
|
||||
git_svc.diff.return_value = ""
|
||||
git_svc.list_changed_files.return_value = []
|
||||
git_svc.diff_and_files.return_value = ("", [])
|
||||
workspace_svc = AsyncMock()
|
||||
evidence_repo = AsyncMock()
|
||||
highlights = [
|
||||
@@ -179,5 +179,4 @@ async def test_evidence_no_branch_skips_git_calls() -> None:
|
||||
assert body["error"] is None
|
||||
assert body["evidence"]["files_changed"] == []
|
||||
assert body["evidence"]["pr_diff_summary"] == ""
|
||||
git_svc.diff.assert_not_awaited()
|
||||
git_svc.list_changed_files.assert_not_awaited()
|
||||
git_svc.diff_and_files.assert_not_awaited()
|
||||
|
||||
@@ -107,8 +107,7 @@ async def test_claim_review_evidence_carries_prior_findings(
|
||||
task_svc.list_paused_for_agent.return_value = []
|
||||
task_svc.qa_claim.return_value = t_claimed
|
||||
git_svc = AsyncMock()
|
||||
git_svc.diff.return_value = ""
|
||||
git_svc.list_changed_files.return_value = []
|
||||
git_svc.diff_and_files.return_value = ("", [])
|
||||
deps = _make_deps(task=task_svc, git=git_svc)
|
||||
c = Choreographer(deps)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user