diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 19ea58003f..1861f36620 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -482,7 +482,7 @@ jobs: .[] | select( (.user.login // "" | ascii_downcase) as $user - | $user == "opencode-agent" or $user == "opencode-agent[bot]" + | $user == "opencode-agent" or $user == "opencode-agent[bot]" or $user == "github-actions[bot]" ) | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") @@ -505,7 +505,7 @@ jobs: end ')" if [ -z "$verdict" ]; then - echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. The dispatch workflow will rerun this failed job after publishing an authenticated exact-head verdict." + echo "::error::No APPROVED or CHANGES_REQUESTED from an authorized OpenCode formal-review publisher on the current head. The dispatch workflow will rerun this failed job after publishing an authenticated exact-head verdict." exit 1 fi echo "Current-head OpenCode verdict: ${verdict}." diff --git a/CHANGELOG.md b/CHANGELOG.md index 55a2a2f211..b86ceb1a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ ### CodeQL scan dispatch matrix serialisation - Serialised the dispatched CodeQL matrix with `toJSON()` in `codeql-scan-dispatch.yml`. `codeql-pr.yml` sends `client_payload.matrix` as an array and the handler assigned it straight into `env:`, where a value must be a scalar, so GitHub rejected the step with "A sequence was not expected" and the dispatched scan never ran -- 0 successes against 136 failures since the handler was added in #1776. The validate step already consumes the value through `jq`, so JSON text is the shape it was written for and no consumer changes. Added a string contract test, because neither `yaml.safe_load` nor `actionlint` 1.7.12 flags this: it is an Actions template rule, so only GitHub's own validator rejects it and no local gate catches the class. +### Required OpenCode formal-review publisher alignment + +- **Accept `github-actions[bot]` in the required OpenCode check's own verdict lookup.** `scripts/ci/opencode_review_receipt_gate.py`'s `FORMAL_AUTHORS` allowlist already accepted `github-actions[bot]` as a formal reviewer, but the required check's own jq verdict-matching in `opencode-review.yml` only recognized `opencode-agent`/`opencode-agent[bot]` — so the receipt gate could wake a run for a `github-actions[bot]` review that the required job's own admission logic would never actually recognize. Fixed the inconsistency; no other behavior change. ### Contextual-orchestrator pin refresh diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 1cc9e20313..4788d58522 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -2649,6 +2649,15 @@ Higgins, S. S., Crepalde, N., & Fernandes, L. (2021). Segmented multiplexity: A **Follow-up.** If the organization later solves free+ZDR routing robustly enough to deliberately widen required-review CI to `orchestrator/auto` (e.g. once a spend ceiling and reviewer-visible cost evidence exist for that path), the change is exactly one `case` arm plus the corresponding assertions in `test_sidecar_pins_the_pool_to_free_for_github_actions` — this entry is the record of *why* it was narrowed, not a permanent prohibition. + +### OPENCODE-EVENT-DRIVEN-REQUIRED-WAKE-2026-09-02 (reconciled 2026-09-05, scope narrowed) +- Gap, as originally scoped: Required OpenCode verdict admission occupied a hosted runner while waiting; an intermediate repair then introduced fixed dispatch retry/sleep/transport allocations (`12`, `5s`, `30s`) without a governing model or standard; the required check's own verdict lookup was also inconsistent with `scripts/ci/opencode_review_receipt_gate.py`'s `FORMAL_AUTHORS` allowlist. +- Causal owner: `ContextualWisdomLab/.github` required review and merge-control workflows. +- **2026-09-05 reconciliation: two of this PR's three proposed changes are now superseded by separately-merged, later work; only the allowlist fix survives.** `#1830` ("release required runner after dispatch," merged 2026-09-04) independently rewrote `opencode-review.yml`'s fail-closed step to one live PR read plus one Reviews read with immediate fail-closed, achieving the runner-release goal a different way, and its own `tests/test_opencode_required_rerun_capacity.py` still expects `opencode-review-dispatch.yml`'s wake step to keep its original 12-attempt loop and `.head_sha`-based run matching — this PR's proposed single-lookup/`pull_requests[]`-matching replacement for that step directly contradicts that already-tested, already-shipping design, so it was dropped rather than pushed through over a live test disagreement. Separately, `#1840` ("stop required-check completion fanout," merged 2026-09-04) deliberately removed every `workflow_run:` listener from `pr-review-merge-scheduler.yml` in favor of GitHub's native auto-merge; this PR's proposed `reconcile-opencode-required-verdict` `workflow_run: completed` handler would reintroduce exactly the mechanism `#1840` retired, so it was also dropped. Both reverted pieces' dedicated test files (`test_opencode_event_driven_required_wake.py`, `test_opencode_formal_verdict_authority_contract.py`, `test_opencode_required_verdict_reconciliation_contract.py`, `test_opencode_required_verdict_runner_release.py`) were removed with them rather than left testing dead code. +- Repair proposed on this PR branch: the required check's verdict lookup in `opencode-review.yml` accepts `github-actions[bot]` alongside `opencode-agent`/`opencode-agent[bot]`, matching the receipt gate's own allowlist. Nothing else changes. +- Verification contract: `tests/test_opencode_required_verdict_regression.py` directly executes the production jq filter and requires an exact-head `github-actions[bot]` `APPROVED` verdict to be admitted, while its existing negative actor and stale/fallback cases remain fail-closed. Fresh hosted checks on the unchanged final head remain authoritative. +- Status: Proposed on this PR branch and not yet protected-main authority. Integrate only through ordinary protection after fresh exact-head checks and review are terminal-clean; the other two proposed changes are superseded and not pursued further. + ## 2026-09-02 org-queue-sweep investigation: historical conclusion superseded by PR #1821 **Current status (2026-09-04).** The conclusion below was invalidated by live queue evidence. PR #1821 removed the organization-wide Actions-run inventory and cancellation block from `org-queue-sweep` and merged as `11bb6a7871f4d95ab8a3eab616b4264d02327010`. Native per-PR concurrency and the current-head coalescer now own stale-run cancellation; the scheduled sweep retains only missed review, merge, and branch-update recovery. Focused ownership contracts passed 78 tests before merge. This preserves the event-gap recovery described below without paying the repository-wide run-listing and cancellation API cost. diff --git a/tests/test_opencode_required_verdict_regression.py b/tests/test_opencode_required_verdict_regression.py index f29b97a663..00504c883f 100644 --- a/tests/test_opencode_required_verdict_regression.py +++ b/tests/test_opencode_required_verdict_regression.py @@ -154,6 +154,17 @@ def test_runtime_required_verdict_ignores_later_nonformal_current_head_comment( ) == state +@pytest.mark.parametrize("state", ("APPROVED", "CHANGES_REQUESTED")) +def test_runtime_required_verdict_accepts_github_actions_formal_publisher( + state: str, +) -> None: + """The canonical workflow actor can publish either exact-head formal verdict.""" + body = "deterministic fallback approval" if state == "CHANGES_REQUESTED" else "" + workflow_actor = review(state=state, body=body) + workflow_actor["user"] = {"login": "github-actions[bot]"} + assert runtime_verdict([workflow_actor]) == state + + def test_runtime_required_verdict_rejects_other_actor() -> None: """A non-OpenCode formal review cannot satisfy the runtime filter.""" human = review(state="APPROVED")