Conversation
d0b1478 to
1928993
Compare
|
@all-hands-bot Please review the current head and explicitly approve it if there are no blocking findings. |
|
🤖 OpenHands is reviewing this PR. Head commit: This comment was posted by an AI agent (OpenHands). |
Review of head
|
| Area | Assessment |
|---|---|
| Architecture | Clean separation of concerns. The scanner is stateless w.r.t. conversations and delegates all runtime work to the profile-backed subject agent. Idempotency key ({event_id}:{sha}) and subject key ({repo_id}:pr:{number}) are correctly composed. |
| Security | The scanner receives only the token secret name (not value) in the prompt text — the actual credential is injected by the profile runtime. test_reviewer_scanner_has_no_conversation_or_runtime_code verifies no conversation/workspace/credential code leaks into the scanner. No hardcoded secrets. |
| Correctness | PR is re-fetched after listing to get fresh metadata. Latest labeled event is checked to avoid reprocessing. The prompt instructs the agent to re-verify the head SHA before reporting and to leave the label in place if the head changed. Status reporting is keyed to the exact head SHA. |
| Backward compatibility | _build_review_prompt preserves its original defaults, so the existing main.py standalone path continues to work unchanged. |
| Tests | All 907 tests pass (24 expected skips). The 3 new delivery tests cover: labeled exact-head submission, unlabeled-PR skipping, and scanner purity (no runtime code). Fixtures and bundle-index are consistent with the manifest. |
| Bundle consistency | bundle-index.js inlines the correct worker.py, main.py, and github_client.py source; the test_bundle_files_exist_and_are_inlined test validates this. |
Minor observations (non-blocking)
- The
main.pymodule-level docstring (lines 11–14) still describes the old "script owns the repository checkout" model. Sincemain.pyis now primarily a library imported byworker.py, this docstring is slightly stale — but it does not affect runtime behavior. - The
main.py_CONFIG_TYPESdict does not includegithub_token_secret, so that key inconfig.jsonis silently ignored byload_config. This is intentional (the scanner reads it fromself.configdirectly, andrun_repositoriesresolves it from args/config), but a reader might expect it to be validated alongside the other keys.
Verdict
The code is clean, well-tested, architecturally sound, and introduces no blocking issues. Approving.
Generated by OpenHands AI on behalf of the user.
all-hands-bot
left a comment
There was a problem hiding this comment.
This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.
Summary
This PR refactors the GitHub PR reviewer automation from a single-process scanner that manages checkouts and conversations directly into a thin scanner that delegates each labeled PR head as an idempotent subject turn to a profile-backed sandboxed agent. The architectural separation is clean: the scanner never touches conversations, workspaces, runtime credentials, or result artifacts. The existing review prompt is reused via _build_review_prompt with new keyword-only parameters for workspace instructions and the token secret name.
Findings
[IMPROVEMENT] Missing per-PR error isolation in the scanner loop
File: skills/github-pr-reviewer/scripts/worker.py, line 88
The run() method iterates over labeled PRs and calls submit_subject_turn for each, but there is no try/except inside the loop. If submit_subject_turn raises (network timeout, 5xx from the subject-turn endpoint, transient DNS), the exception propagates out of run() and is caught by run_repositories at the per-repository level - which means all remaining labeled PRs in that repository are skipped for this scan cycle. The old main.py scanner handled this with per-PR error isolation in _process_repo.
This is not a data-loss issue: labels are not removed until both statuses are visible, so skipped PRs will be picked up on the next scan. But for repositories with many labeled PRs, a single transient failure can delay reviews for all subsequent PRs by one full scan interval (15 minutes by default). Wrapping the per-PR body in a try/except that logs and continues would match the robustness of the previous implementation.
Design Assessment
- Repository boundary: Correct. The scanner, prompt, manifest, and tests all belong in this extensions registry. The subject-turn API and Docker sandbox runtime are in OpenHands/automation and software-agent-sdk respectively, as noted in the PR dependencies.
- Credential handling: The
githubTokenSecretform field correctly asks for a secret name (not value), and the prompt references the env var by name (GH_TOKEN=${TOKEN_NAME}) rather than embedding the value. The scanner itself authenticates withself.tokenfor GitHub API reads but never passes the token value into the subject turn - only the name. - Idempotency: The
subject_key({repository_id}:pr:{pr_number}) andidempotency_key({label_event_id}:{sha}) are well-chosen: GitHub's immutable repository ID prevents cross-repo collisions, the label event ID prevents duplicate reviews for the same label application, and the SHA ensures a force-pushed head gets a fresh review. - Test coverage: The delivery tests exercise the real
run()code path with mocked I/O boundaries, verify prompt content, and include a static source scan ensuring the scanner has no conversation/runtime code. The test adaptation intest_automation_setup.py(switching the schema-rejection fixture fromgithub-pr-reviewertogithub-issue-to-pr) is correct because the reviewer manifest now has emptyintegrations, which would break theintegrations.github.valuerejection case. - Fixture consistency: All fixture entries are internally consistent - version 1.1.0,
python3 worker.pyentrypoint, 1800s timeout,github_token_secretin config,worker.pyin packs.
Risk Assessment
🟢 LOW - The change is well-structured with clean separation of concerns. The scanner is stateless and idempotent. The main robustness concern (per-PR error isolation) is non-blocking: failures delay rather than lose reviews. No security issues found - credential names are used throughout, never values.
Verdict
✅ Worth merging - Core logic is sound. The per-PR error isolation suggestion is an improvement, not a blocker.
Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:
- Add a
.agents/skills/custom-codereview-guide.mdfile to your branch (or edit it if one already exists) with the/codereviewtrigger and the context the reviewer is missing (e.g., "Security concerns about X do not apply here because Y"). See the customization docs for the required frontmatter format.- Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
- When your PR is merged, the guideline file goes through normal code review by repository maintainers.
Resolve with AI? Install the iterate skill in your agent and run
/iterateto automatically drive this PR through CI, review, and QA until it's merge-ready.Was this review helpful? React with 👍 or 👎 to give feedback.
|
👋 This PR needs a couple of things fixed before OpenHands can review it:
Push an update once this is addressed and this check re-runs automatically. This is an automated check - no AI was used to generate this comment. |
|
@all-hands-bot The current head addresses the per-PR failure-isolation finding and the stale module description. Please review it and submit an APPROVE review if there are no blocking findings. |
all-hands-bot
left a comment
There was a problem hiding this comment.
Re-review of head 4dbe9c8 — ✅ APPROVED
I re-reviewed the current head, focusing on the two findings from the previous review on a085b2f.
Finding 1: Per-PR failure isolation — ✅ Resolved
worker.py run() now wraps the per-PR body (PR fetch, label-event lookup, submit_subject_turn, and result logging) in a try/except Exception that prints a diagnostic and continues to the next PR. A single transient submission failure no longer propagates to run_repositories, so later labeled PRs in the same repository are still dispatched in the same scan cycle.
The error message uses candidate.get('number', '?') rather than pr["number"], which is correct: if the failure occurs during self.gh("GET", f"/pulls/{...}"), pr may not exist yet, but candidate always does.
A new test, test_reviewer_continues_after_one_submission_fails, exercises the real run() code path with two labeled PRs where the first submission raises RuntimeError and the second succeeds. It asserts submit.call_count == 2, confirming the loop continues past the failure.
Finding 2: Stale module description — ✅ Resolved
The main.py docstring (lines 11–14) was updated to describe the dual role:
This standalone script owns the repository checkout: it downloads the pull request's head commit as a tarball, hands the agent that directory as its workspace, and removes it once the review has finished. Catalog workers may instead reuse its prompt builder with their own workspace instructions.
This correctly reflects that main.py is now both a standalone script and a library whose _build_review_prompt is imported by worker.py. The stale "The agent never clones, checks out, or deletes anything" claim is removed.
Test suite
All 908 tests pass (24 expected skips), including the 4 delivery tests and the full bundle/fixture consistency suite.
Verdict
No blocking findings. Approving.
Generated by OpenHands AI on behalf of the user.
1edb0ca to
5275f21
Compare
281a012 to
da5fc74
Compare
c96290d to
1e0a258
Compare
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
da5fc74 to
187caf0
Compare
Co-authored-by: openhands <openhands@all-hands.dev>
187caf0 to
c7f530d
Compare
Why
The existing reviewer already provides the established review prompt and native GitHub review workflow. Its host-managed checkout and conversation lifecycle assume a shared filesystem and cannot cleanly fan out several exact PR heads. Review work should run inside each delegated agent's own runtime.
Summary
Keep the established reviewer and add a thin scheduled scanner that:
Changed heads remain labeled for a fresh review. The label is removed only after both statuses exist on the reviewed head. The scanner does not parse or repost JSON review artifacts.
Issue Number
Closes #568.
How to Test
uv run --group test pytest -q tests/test_github_reviewer_delivery.py tests/test_automation_setup.pyResult: 127 passed, 18 skipped.
Dependencies
Review and merge #592 first. Running from Canvas with a selected profile additionally requires OpenHands/automation#479. Profile-selected saved secrets require OpenHands/software-agent-sdk#5017; Docker-backed conversations require OpenHands/software-agent-sdk#3403.
Live evidence
The exact head reviewed Airbnb PR #87 in its own Docker conversation. It ran the repository tests and build, posted a readable native review rather than a JSON artifact, published successful exact-head
software-factory/testsandsoftware-factory/reviewstatuses, and removed the review label.