fix(robomp): keep label failures best effort

This commit is contained in:
oldschoola
2026-06-16 01:54:14 -07:00
parent f2015a0a21
commit 75fc25c9ba
2 changed files with 67 additions and 0 deletions
+14
View File
@@ -143,6 +143,10 @@ def _issue_needs_info(bindings: ToolBindings) -> bool:
return row is not None and row.state == "needs_info"
def _optional_label_error(exc: Exception) -> str:
return f"{type(exc).__name__}: {exc}"
def _remove_needs_info_label(bindings: ToolBindings) -> bool:
try:
_run_coro(
@@ -154,6 +158,12 @@ def _remove_needs_info_label(bindings: ToolBindings) -> bool:
return True
log.warning("needs-info label cleanup failed", extra={"issue": bindings.issue_key, "err": str(exc)})
return False
except Exception as exc: # noqa: BLE001 - best-effort optional label cleanup
log.warning(
"needs-info label cleanup failed",
extra={"issue": bindings.issue_key, "err": _optional_label_error(exc)},
)
return False
return True
@@ -934,6 +944,10 @@ def _build_mark_unable(bindings: ToolBindings) -> HostTool[Any, Any]:
# info-request comment, so label setup must not block resumption.
log.warning("needs-info label failed", extra={"issue": bindings.issue_key, "err": str(exc)})
result["label_error"] = f"{exc.status} {exc.message}"
except Exception as exc: # noqa: BLE001 - best-effort optional label setup
error = _optional_label_error(exc)
log.warning("needs-info label failed", extra={"issue": bindings.issue_key, "err": error})
result["label_error"] = error
bindings.db.set_issue_state(bindings.issue_key, "needs_info")
_audit(bindings, "mark_unable_to_reproduce", args, result=result)
return f"posted needs-info comment id={comment.id}"
+53
View File
@@ -437,6 +437,31 @@ def test_repro_record_advances_needs_info_when_label_is_missing(db: Database, tm
assert issue and issue.state == "reproducing"
def test_repro_record_advances_needs_info_when_cleanup_transport_fails(db: Database, tmp_path: Path) -> None:
def handler(request: httpx.Request) -> httpx.Response:
raise httpx.ConnectError("connection dropped", request=request)
bindings, loop, t = _bindings(db, tmp_path, httpx.MockTransport(handler))
db.set_issue_state(bindings.issue_key, "needs_info")
try:
tool = next(x for x in build(bindings) if x.name == "repro_record")
result = tool.execute(
{
"title": "panic on empty input",
"command": "bun test foo.test.ts",
"output": "Error: boom",
"exit_code": 1,
},
_ctx(),
)
finally:
_stop_loop(loop, t)
assert result == "recorded"
issue = db.get_issue(bindings.issue_key)
assert issue and issue.state == "reproducing"
def test_repro_record_chowns_to_slot_when_root(db: Database, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
chowns: list[tuple[Path, int, int]] = []
monkeypatch.setattr(host_tools, "_slot_permissions_active", lambda slot_uid: slot_uid is not None)
@@ -519,6 +544,34 @@ def test_mark_unable_keeps_needs_info_when_label_is_missing(db: Database, tmp_pa
assert issue and issue.state == "needs_info"
def test_mark_unable_keeps_needs_info_when_label_transport_fails(db: Database, tmp_path: Path) -> None:
comments = 0
def handler(request: httpx.Request) -> httpx.Response:
nonlocal comments
if request.url.path.endswith("/labels"):
raise httpx.ConnectError("connection dropped", request=request)
comments += 1
return httpx.Response(201, json={"id": 321, "user": {"login": "robomp-bot"}, "body": "x", "created_at": "t"})
bindings, loop, t = _bindings(db, tmp_path, httpx.MockTransport(handler))
try:
tool = next(x for x in build(bindings) if x.name == "mark_unable_to_reproduce")
tool.execute({"diagnosis": "needed exact version", "info_needed": "post bun --version"}, _ctx())
finally:
_stop_loop(loop, t)
assert comments == 1
issue = db.get_issue(bindings.issue_key)
assert issue and issue.state == "needs_info"
row = db._conn.execute(
"SELECT result_json FROM tool_calls WHERE tool='mark_unable_to_reproduce' ORDER BY id DESC LIMIT 1"
).fetchone()
assert row is not None
result = json.loads(row["result_json"])
assert "ConnectError" in result["label_error"]
def test_abort_task_signals_controller_and_abandons_without_comment(db: Database, tmp_path: Path) -> None:
# Any HTTP call is a regression: abort_task MUST NOT touch GitHub.
def handler(request: httpx.Request) -> httpx.Response: