You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(review): bound in-flight conversations across scheduled scans #689
The scheduled GitHub PR reviewer limits new conversations per scan, but does not cap conversations that remain active across scans. On the OSS Agent Canvas VM, scans accumulated enough Docker runtimes to cause repeated container OOM kills, one host-wide OOM, 159 GB of retained runtime data, and eventual loss of guest networking.
Acceptance criteria
The reviewer counts its nonterminal dispatched conversations before starting more.
A configurable global in-flight limit bounds active review conversations across scheduled scans.
Completed, errored, stale, and missing conversations release capacity.
Existing per-scan ordering, deduplication, and max-new-per-run behavior remain unchanged.
Tests cover capacity across multiple scans and capacity recovery.
OpenHands AI triage
The following comments and acceptance criteria were added by the OpenHands AI agent.
Triage
The scheduled reviewer already bounds how many conversations a single scan starts (ReviewIntake / max_new_per_run, #659), but it does not bound conversations that remain nonterminal across scans. Repeated scans therefore stack Docker runtimes until the OSS Agent Canvas VM exhausts memory. This is bounded scheduling work in the existing reviewer automation, so no further design decision is needed.
Scope is skills/github-pr-reviewer/scripts/worker.py, its rendered config.json, and the agent-driven setup path, reusing the machinery #654/#659 already added: the KV-backed AgentConversationDispatcher (per-subject records under agent-conversation-{conversation_id}), the exact-head check gate, _finish_completed_review, run_repositories, and the agent server's per-conversation execution status. The count is the reviewer's own dispatched conversations across all configured repositories, not a new queue or runtime layer.
Counting is reviewer scheduling behavior, so it stays in the reviewer worker rather than in the shared agent_conversation.py dispatcher that github-issue-to-pr, gitlab-issue-to-mr, and github-issue-triage also import.
Non-goals: no repository-specific policy, VM tuning, or deployment control; no second automation record; no new secret, token scope, or GitHub permission; no change to the review prompt, verdict parsing, maintainer handoff, secret selection, per-scan ordering or deduplication, the trigger-label scan, or the explicit review_requested event path.
Desired Behavior
A scheduled scan determines how many of its dispatched review conversations are still nonterminal before it starts new ones, and starts no more than a configurable global in-flight limit allows. Capacity is released as conversations complete, error, go stale, or disappear, so later scans resume starting reviews. Existing per-scan ordering, deduplication, and max_new_per_run behavior are unchanged.
Acceptance Criteria
Before starting any new review conversation, a scheduled scan counts the reviewer's own nonterminal dispatched conversations across all configured repositories (not per repository), and starts only as many new ones as remain below the configured global in-flight limit; when the count is already at or above the limit it starts none.
The global in-flight limit is separate from max_new_per_run: both apply to a scheduled scan, max_new_per_run still bounds new starts per scan, and the number started is the smaller of the two remainders.
Capacity is released when a conversation is completed, errored, stale, or missing: a conversation whose execution status is terminal (finished, idle, error, or stuck), one whose reviewed head is no longer the pull request's current head, one abandoned past the existing maximum-active-age bound, and one the agent server reports as missing (404) are all not counted, so a later scheduled scan can start again.
An unreadable conversation status (a status lookup that raises) does not allow the limit to be exceeded: the scan treats the unknown conversation as occupying capacity and reports it.
Reaching the limit does not cut the scan short: the remaining eligible candidates still get their exact-head check evaluation and waiting/blocked dispositions, completed reviews are still reconciled, and maintainer handoff still runs.
A dispatch that raises is reported and consumes no capacity, and does not abort the run or the other repositories.
Existing behavior is unchanged: eligible-candidate ordering, per-(request event, head SHA) deduplication so a repeated scan creates no second conversation or native review, the trigger-label scan, and the explicit review_requested event path. The global in-flight limit bounds only the scheduled scan's new conversations.
The limit is configurable through the reviewer's rendered config.json and the agent-driven setup substitutions alongside max_new_per_run; an unset value applies a default documented in SKILL.md and README.md; a non-positive, non-integer, or boolean value fails the run at config load, matching max_new_per_run validation.
No new secret, token scope, or GitHub permission is required, and no secret value appears in run logs, comments, or the PR.
Focused automated tests driving the shipped reviewer entrypoint through the catalog-bundle test helper cover: (a) capacity persisting across scans, where a first scan fills the limit and a second scan with those conversations still nonterminal starts none; (b) capacity recovery, where once those conversations are terminal, stale, or missing a later scan starts the next eligible conversation; (c) the limit being shared across two repositories; and (d) config validation rejecting a non-positive or boolean value.
SKILL.md and README.md document the global in-flight limit, its config key, its default, and how it composes with max_new_per_run; the catalog entry and bundle versions are bumped and generated catalogs regenerated so python scripts/sync_extensions.py --check and npm run build report no drift.
Where the OSS Agent Canvas deployment is reachable, the PR's evidence links the scheduled run's conversations showing the in-flight count was not exceeded; if it is not reachable, the PR states that explicitly instead of claiming live validation.
Problem
The scheduled GitHub PR reviewer limits new conversations per scan, but does not cap conversations that remain active across scans. On the OSS Agent Canvas VM, scans accumulated enough Docker runtimes to cause repeated container OOM kills, one host-wide OOM, 159 GB of retained runtime data, and eventual loss of guest networking.
Acceptance criteria
OpenHands AI triage
The following comments and acceptance criteria were added by the OpenHands AI agent.
Triage
The scheduled reviewer already bounds how many conversations a single scan starts (
ReviewIntake/max_new_per_run, #659), but it does not bound conversations that remain nonterminal across scans. Repeated scans therefore stack Docker runtimes until the OSS Agent Canvas VM exhausts memory. This is bounded scheduling work in the existing reviewer automation, so no further design decision is needed.Scope is
skills/github-pr-reviewer/scripts/worker.py, its renderedconfig.json, and the agent-driven setup path, reusing the machinery #654/#659 already added: the KV-backedAgentConversationDispatcher(per-subject records underagent-conversation-{conversation_id}), the exact-head check gate,_finish_completed_review,run_repositories, and the agent server's per-conversation execution status. The count is the reviewer's own dispatched conversations across all configured repositories, not a new queue or runtime layer.Counting is reviewer scheduling behavior, so it stays in the reviewer worker rather than in the shared
agent_conversation.pydispatcher thatgithub-issue-to-pr,gitlab-issue-to-mr, andgithub-issue-triagealso import.Non-goals: no repository-specific policy, VM tuning, or deployment control; no second automation record; no new secret, token scope, or GitHub permission; no change to the review prompt, verdict parsing, maintainer handoff, secret selection, per-scan ordering or deduplication, the trigger-label scan, or the explicit
review_requestedevent path.Desired Behavior
A scheduled scan determines how many of its dispatched review conversations are still nonterminal before it starts new ones, and starts no more than a configurable global in-flight limit allows. Capacity is released as conversations complete, error, go stale, or disappear, so later scans resume starting reviews. Existing per-scan ordering, deduplication, and
max_new_per_runbehavior are unchanged.Acceptance Criteria
max_new_per_run: both apply to a scheduled scan,max_new_per_runstill bounds new starts per scan, and the number started is the smaller of the two remainders.finished,idle,error, orstuck), one whose reviewed head is no longer the pull request's current head, one abandoned past the existing maximum-active-age bound, and one the agent server reports as missing (404) are all not counted, so a later scheduled scan can start again.review_requestedevent path. The global in-flight limit bounds only the scheduled scan's new conversations.config.jsonand the agent-driven setup substitutions alongsidemax_new_per_run; an unset value applies a default documented inSKILL.mdandREADME.md; a non-positive, non-integer, or boolean value fails the run at config load, matchingmax_new_per_runvalidation.SKILL.mdandREADME.mddocument the global in-flight limit, its config key, its default, and how it composes withmax_new_per_run; the catalog entry and bundle versions are bumped and generated catalogs regenerated sopython scripts/sync_extensions.py --checkandnpm run buildreport no drift.