fix(ci): give the opencode-review required check a workflow_run second chance - #1494
fix(ci): give the opencode-review required check a workflow_run second chance#1494seonghobae wants to merge 1 commit into
Conversation
…d chance opencode-review.yml's opencode-review-target job (the "opencode-review" required check) evaluates synchronously inside its own pull_request_target run, seconds after push, by querying whether an opencode-agent review already exists for the current head. The real review is posted much later by the separate opencode-review-dispatch.yml repository_dispatch path, and nothing re-ran the already-failed check once it landed -- documented and left unfixed in #1485. Direction chosen: workflow_run re-entry (candidate 1 from #1485), but NOT a literal copy of noema-review.yml's own workflow_run source list. Tracing the actual execution model first: - opencode-review.yml is distributed by the org's required-workflow ruleset into every target repository (it runs "as" contextual-orchestrator, etc., not only as .github), so workflow_run re-entry has to work per repository. - opencode-review-dispatch.yml (the workflow that actually posts the review) is deliberately default-branch-only and always runs inside ContextualWisdomLab/.github -- it is NOT part of the required-workflow ruleset's distributed set. workflow_run cannot cross repositories, so a sibling repository's copy of opencode-review.yml could never observe its completion. Listening to "OpenCode Review Dispatch" (a literal mirror of noema's source list swapped in) would silently do nothing for every sibling-repo PR -- the majority of the affected PRs. - "Required PR Review Merge Scheduler" (pr-review-merge-scheduler.yml) IS ruleset-distributed into every target repository, and already reacts to pull_request_review: [submitted, dismissed] -- the exact event GitHub fires the instant the real opencode-agent review posts -- plus its own periodic sweep. Listening to its completion gives this required check a same-repository second chance without needing pull_request_review as a direct trigger (not supported by the required-workflow ruleset's trigger set) and without any cross-repository credential. Change, kept as close to the existing pull_request_target shape as possible: - on.workflow_run listens for "Required PR Review Merge Scheduler" completions (excluding cancelled runs, matching noema-review.yml's own exclusion). - required-workflow-bootstrap gains `if: github.event_name == 'pull_request_target'`; it has no meaningful inputs for a workflow_run event, and coverage-source-tree / coverage-evidence cascade-skip with it (no changes needed there). - opencode-review-target now runs standalone on a workflow_run event (needs.coverage-evidence.result == 'success' is only required on the pull_request_target path, preserving that path's exact prior behavior), resolving PR_NUMBER/HEAD_SHA from github.event.workflow_run.pull_requests[0] (GitHub-computed, not attacker-controlled) when github.event.pull_request is absent, with a new early exit for workflow_run events that carry no associated PR (the periodic-sweep case). - concurrency.group and run-name gain the same PR-number/head-SHA fallback chain for workflow_run events. Security posture, unchanged: no checkout, no secrets, no new permissions. The verdict step still only performs a read via `gh api .../pulls/{PR_NUMBER}/reviews` with the existing read-only github.token, gated by the same opencode-agent/current-head/non-fallback checks as before -- this only changes when that exact check gets a chance to re-run, never what it accepts as passing. New tests in test_opencode_required_verdict_regression.py lock in: the workflow_run trigger source and its #1485 rationale, the bootstrap job's pull_request_target-only gate, the target job's workflow_run re-entry condition, and three executable bash runs of the actual verdict step (no associated PR exits cleanly without calling gh; a workflow_run re-entry with a real review now present passes; the original pull_request_target fail-closed behavior is unchanged). Full suite: 2123 passed, 1 skipped, 21 subtests. Coverage 100% (scripts/ci), interrogate 100%. Refs #1485. Once merged, #1492 and ContextualWisdomLab/contextual-orchestrator#955/#956 (this session's PRs that hit this exact race) are expected to self-resolve on their next opencode-review re-run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Generated by Claude Code |
|
@opencode-agent please review this draft PR. Generated by Claude Code |
|
Closing as superseded: while resolving this PR's merge conflict against current Concrete proof this PR's premise is now stale: after merging Not pushing the merge I did locally. The real, still-open question this surfaces — why the actively dispatched review didn't land within 90 minutes on #1500's actual CI run — is a new, different problem from what this PR was fixing, and I'll track that separately. Generated by Claude Code |
Summary
Closes the gap documented (not fixed) in #1485:
opencode-review.yml'sopencode-review-targetjob — theopencode-reviewrequired check — evaluates synchronously inside its ownpull_request_targetrun, seconds after push, by querying whether anopencode-agentreview already exists for the current head. The real review is posted much later by the separateopencode-review-dispatch.ymlrepository_dispatchpath (an LLM review, sometimes over an hour perdocs/product-goal-directive.md), and nothing re-ran the already-failed check once it landed. Confirmed live and 100% reproducible this session on 6+ independent PRs across.githubandcontextual-orchestrator.Direction chosen, and why it isn't a literal copy of
noema-review.yml#1485recorded two candidate directions. I chose direction 1 (workflow_runre-entry) — but traced the actual execution model before wiring it, rather than mirroringnoema-review.yml's exact source-workflow list, because a literal copy would silently do nothing for most of the affected PRs:opencode-review.ymlis distributed by the org's required-workflow ruleset (18156473) into every target repository — it runs ascontextual-orchestrator,naruon, etc., not only as.github.opencode-review-dispatch.yml(the workflow that actually posts the review) is deliberately default-branch-only and always runs insideContextualWisdomLab/.github— it is not part of the required-workflow ruleset's distributed set (docs/org-required-workflow-rollout.md's active path list has 7 entries; the dispatch workflow isn't one of them).workflow_runcannot cross repositories, so a sibling repository's copy ofopencode-review.ymlcould never observe that completion. Listening to"OpenCode Review Dispatch"— the literal analog of noema's own list — would fix.github's own PRs only and leave every sibling repo (the majority of the affected PRs, includingcontextual-orchestrator#955/#956) exactly as broken as today."Required PR Review Merge Scheduler"(pr-review-merge-scheduler.yml) is ruleset-distributed into every target repository, and already reacts topull_request_review: [submitted, dismissed]— the exact event GitHub fires the instant the realopencode-agentreview posts — plus its own periodic sweep (*/15/*/30 * * * *). Listening to its completion givesopencode-reviewa same-repository second chance, without needingpull_request_reviewas a direct trigger (not in the required-workflow ruleset's supported trigger set —pull_request,pull_request_target,push,workflow_runonly) and without any cross-repository credential.I also checked whether re-running the existing failed job via the Actions Jobs API (direction 2,
POST .../actions/jobs/{id}/rerun) could work fromopencode-review-dispatch.ymldirectly. It can't: that workflow always runs as.github, and this codebase's ownscheduler_dispatch_env()already documents that the OpenCode app credential has no cross-repository Actions permission, so a cross-repository job-control call has no working credential today.workflow_runre-entry sidesteps that entirely — it needs no privileged credential, just the same read-onlygithub.tokenthe check already used.Change
Kept as close to the existing
pull_request_targetshape as possible:on.workflow_runlistens for"Required PR Review Merge Scheduler"completions (excludingcancelled, matchingnoema-review.yml's own exclusion).required-workflow-bootstrapgainsif: github.event_name == 'pull_request_target'— it has no meaningful inputs (PR number, head SHA, event action) for aworkflow_runevent;coverage-source-tree/coverage-evidencecascade-skip with it, no changes needed there.opencode-review-targetnow runs standalone on aworkflow_runevent (needs.coverage-evidence.result == 'success'is only required on thepull_request_targetpath, preserving that path's exact prior behavior byte-for-byte), resolvingPR_NUMBER/HEAD_SHAfromgithub.event.workflow_run.pull_requests[0](GitHub-computed server-side, not attacker-controlled) whengithub.event.pull_requestis absent, with a new early exit forworkflow_runevents that carry no associated PR (the periodic-sweep case — nothing to verify, not an error).concurrency.groupandrun-namegain the same PR-number/head-SHA fallback chain forworkflow_runevents.Security posture (unchanged)
No checkout, no secrets, no new permissions anywhere in this diff. The verdict step still only performs a read via
gh api .../pulls/{PR_NUMBER}/reviewswith the same read-onlygithub.token(contents: read,pull-requests: read, unchanged), gated by the exact same opencode-agent/current-head-SHA/non-fallback-body checks as before. This only changes when that exact check gets a chance to re-run — never what it accepts as passing, never what credential it uses, and it never touches PR content.Trust-boundary analysis specifically for spoofing risk:
PR_NUMBER/HEAD_SHAfor theworkflow_runpath come fromgithub.event.workflow_run.pull_requests[0], a field GitHub computes server-side from genuine branch/repo correlation — not something a PR author's branch content, title, or commits can forge.workflow_runcannot cross repositories, so a fork/PR in one repository can never cause this re-entry to fire in a different repository's copy of the workflow. The chainpr-review-merge-scheduler → opencode-review → pr-review-merge-scheduleris bounded by GitHub's own 3-levelworkflow_runchain-depth cap (no runaway loop possible).Contract tests
tests/pins exact strings/structure of this file (test_opencode_required_verdict_regression.py,test_pingora_edge_workflow_contract.py,test_required_workflow_queue_contract.py,test_opencode_agent_contract.py,test_opencode_security_boundaries.py,test_central_required_workflow_ruleset_audit.py) — read all of them before editing; none needed weakening. Extendedtest_opencode_required_verdict_regression.pywith:workflow_runtrigger source, the#1485rationale comment, the bootstrap job'spull_request_target-only gate, and the target job'sworkflow_runre-entry condition.bashruns of the actual verdict step (not a Python mirror, matching this file's existing "one executable owner" discipline): aworkflow_runevent with no associated PR exits0cleanly without ever invokinggh; aworkflow_runre-entry with a real review now present passes and prints the verdict; the originalpull_request_targetfail-closed behavior (empty reviews → exit 1) is unchanged.Verification
python3 -c "import yaml; yaml.safe_load(open('.github/workflows/opencode-review.yml'))"— OKbash -non the extracted, GitHub-expression-rendered verdict step — OKPYTHONPATH=. coverage run -m pytest tests -q && coverage report --show-missing— 2123 passed, 1 skipped, 21 subtests, coverage 100% (scripts/ci)interrogate— 100% (this fix is workflow/test-only; confirms the docstring gate wasn't disturbed)Expected first-run behavior on this PR itself
Because the fix isn't live on
mainyet when this PR's ownopencode-reviewcheck first evaluates, this PR is very likely to hit the exact same race on its own first run — that's expected, not a sign the fix is wrong (the base branch's currentopencode-review.yml, not this PR's, is what actually executes for apull_request_target-triggered required check). Once merged,opencode-review-dispatch.ymlposting the real review will triggerpr-review-merge-scheduler.yml(viapull_request_review: submitted), which will in turn re-triggeropencode-review.ymlhere on the next PR that needs it.Other PRs
#1492(.github) andcontextual-orchestrator#953,#955,#956(already documented in#1485, plus#1479/#1482/#1437/#1478) hit this exact same race this session and already carry "not this PR's fault, see #1485" comments. Once this fix is merged and the base branch picks it up, those PRs'opencode-reviewcheck should self-resolve on its next natural re-evaluation (their own nextpull_request_review/scheduler-completionworkflow_run, or a manual re-run) — I have not pushed anything to those PRs myself; they're out of scope for this change.Refs #1485.
Generated by Claude Code