Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/opencode-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,28 @@ on:
repository_dispatch:
types: [opencode-review]

concurrency:
# Workflow-level admission, for the same reason strix.yml, noema-review.yml and
# opencode-review.yml carry theirs at this level: a job-level group is never
# evaluated while the whole run waits behind the organization job ceiling, so
# superseded dispatches for one pull request coalesce only after each of them
# has already been allocated a runner. Measured on 2026-09-06: of the five
# dispatch runs that passed `validate-pr-metadata`, four were rejected hours
# later by `opencode-review`'s privileged metadata check because the head had
# moved while they queued (runs 34002473295, 34010256951, 34015973300,
# 34016922761) -- each after `coverage-source-tree` and `coverage-evidence`
# had run. Cancelling the superseded run at creation returns that slot instead
# of spending it to discover the review's subject no longer exists.
#
# The key is the target pull request, matching the job-level group below and
# codeql-scan-dispatch.yml's workflow-level group; `github.run_id` keeps runs
# without a payload in their own groups rather than colliding.
group: >-
opencode-review-dispatch-${{
github.event.client_payload.target_repository || github.repository }}-${{
github.event.client_payload.pr_number || github.run_id }}
cancel-in-progress: true
Comment on lines +30 to +34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Stale reruns cancel current reviews

Rerunning an older dispatch enters the same PR-wide concurrency group and cancels the current-head review before metadata validation. The stale rerun then fails validation, leaving no review to publish or wake the required check.

Prompt for agents
The workflow-level concurrency group treats the newest workflow run as authoritative, but a GitHub rerun preserves the older repository_dispatch payload. Rerunning a stale dispatch for the same target repository and PR therefore cancels an in-progress current-head dispatch before validate-pr-metadata can reject the stale payload. The valid dispatch is lost and no replacement is created. Adjust workflow-level admission so cancellation is based on validated target-head evidence or otherwise ensure that canceling a current-head dispatch always enqueues exactly one replacement, while retaining pre-runner coalescing for genuinely superseded heads. Cover the stale-rerun-versus-current-dispatch case in the workflow contract tests.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


permissions:
contents: read

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### Superseded OpenCode review dispatches coalesce before they take a runner

- `opencode-review-dispatch.yml` now carries a workflow-level `concurrency` group keyed by the dispatched pull request (`opencode-review-dispatch-<target repository>-<pr number>`, `cancel-in-progress: true`), matching `codeql-scan-dispatch.yml`'s workflow-level group and the rationale already recorded in `strix.yml`, `noema-review.yml` and `opencode-review.yml`: a job-level group is never evaluated while the whole run waits behind the organization job ceiling. The workflow kept its group only on the long `opencode-review-target` job, so two dispatches for one pull request each queued for hours and each was allocated a runner before the older one could be discarded. Measured on 2026-09-06: four of the five dispatch runs that passed `validate-pr-metadata` were rejected hours later by the privileged metadata check because the head had moved while they queued (runs `34002473295`, `34010256951`, `34015973300`, `34016922761`), each after `coverage-source-tree` and `coverage-evidence` had run. The privileged check itself is unchanged -- it rejected exactly what it should; what changes is that the superseded run is now cancelled at creation instead of spending a slot to discover its subject moved.

### Strix gate names the sandbox bootstrap failure and retries it once

- `scripts/ci/strix_quick_gate.sh` gives the Caido sandbox bootstrap race (`loginAsGuest failed after 10 attempts` on `127.0.0.1:<port>`, upstream usestrix/strix#1036/#1037/#1056) its own bounded same-model retry budget, `STRIX_SANDBOX_BOOTSTRAP_RETRIES` (default 1), drawn on top of `STRIX_TRANSIENT_RETRY_PER_MODEL`. That budget is 0 in production because the gateway owns model failover, so the documented sandbox retry never ran: `argos` Strix run 34013128112 (2026-09-06) shows one attempt, `Docker image ready`, the proxy never reachable, Strix exiting after 240 s -- while the sidecar reported four ready and four deferred routes that were never called. The budget is charged in the same branch that grants the attempt, so a log matching the sandbox class together with a gateway class cannot extend the loop without charging it (caught by adversarial review of the first draft). The primary-scan verdict for that class now reads `STRIX_PROVIDER_UNAVAILABLE: STRIX_SANDBOX_UNAVAILABLE: the last Strix attempt ended in the sandbox bootstrap (...) after N sandbox-specific same-model retries (budget B); this verdict names Strix's sandbox, not the LLM gateway.` instead of `orchestrator/free exhausted`, stating only what the gate observed; the leading token is unchanged so the workflow's finding-free classification and its tests are untouched, and the second token lets the review census split sandbox outages from gateway ones (two of six recent Strix artifacts were this class). Refs #1948.
Expand Down
8 changes: 6 additions & 2 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -1815,8 +1815,12 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert "run_opencode_review_model_pool.sh" in workflow
assert "rekick_model_pool_on_exhaustion" not in workflow
assert "publish stage performs no duplicate model-catalog pass" in workflow
concurrency_contract = workflow.split("concurrency:", 1)[1].split(
"permissions:", 1
# The review job's own group, addressed by its indentation: the workflow
# also carries a workflow-level admission group (pinned in
# tests/test_required_workflow_queue_contract.py), so splitting on the
# first "concurrency:" would read that one instead of this one.
concurrency_contract = workflow.split("\n concurrency:", 1)[1].split(
"\n runs-on:", 1
)[0]
assert "needs.validate-pr-metadata.outputs.target_repository" in concurrency_contract
assert "needs.validate-pr-metadata.outputs.pr_number || github.run_id" in concurrency_contract
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pr_review_autofix_nvidia_nim_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md")
CHANGELOG = Path("CHANGELOG.md")
REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml")
REVIEW_DISPATCH_BLOB_SHA = "26e8555967171a5f3974602ac05700c27bddebf1"
REVIEW_DISPATCH_BLOB_SHA = "fd0e5ff75d9ae6ec68cc6945174dff5c9d487662"


def _workflow_text(path: Path) -> str:
Expand Down
36 changes: 36 additions & 0 deletions tests/test_required_workflow_queue_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,42 @@ def test_privileged_review_retries_use_default_branch_repository_dispatch() -> N
assert '"gh",\n "workflow",\n "run"' not in autofix_scheduler


def test_privileged_review_dispatch_coalesces_superseded_runs_before_admission() -> None:
"""A superseded dispatch must be cancelled while queued, not after it takes a runner.

``opencode-review-dispatch.yml`` carried its concurrency group only on the
long ``opencode-review-target`` job. A job-level group is not evaluated
while the whole run waits behind the organization job ceiling, so two
dispatches for one pull request each waited hours and each was allocated a
runner before the older one could be discarded. Measured on 2026-09-06:
four of the five dispatch runs that passed ``validate-pr-metadata`` were
then rejected by the privileged metadata check because the head had moved
while they queued, every one of them after ``coverage-source-tree`` and
``coverage-evidence`` had already run.

The workflow-level group is keyed by the dispatched pull request, matching
``codeql-scan-dispatch.yml``'s workflow-level group and the job-level group
this workflow keeps for the review job itself.
"""
workflow = workflow_text("opencode-review-dispatch.yml")
header = workflow.split("permissions:", 1)[0]
concurrency_contract = header.split("concurrency:", 1)[1]

assert re.search(r"(?m)^concurrency:", header)
assert "opencode-review-dispatch-" in concurrency_contract
assert (
"github.event.client_payload.target_repository || github.repository"
in concurrency_contract
)
assert (
"github.event.client_payload.pr_number || github.run_id"
in concurrency_contract
)
assert "cancel-in-progress: true" in concurrency_contract
assert "github.event.client_payload.pr_head_sha" not in concurrency_contract
assert re.search(r"(?m)^ concurrency:", workflow)


def test_required_opencode_dispatch_does_not_wait_on_merge_scheduler() -> None:
"""Dispatch review execution directly so polling cannot starve its producer."""
workflow = workflow_text("opencode-review.yml")
Expand Down
Loading