Skip to content

fix(opencode): model-unavailable fallback hides a multi-week review outage behind a green check #2334

Closed as not planned

Description

@seonghobae

Problem

OpenCode review produced no model verdict at all between 2026-08-27 and 2026-09-21, and the opencode-review check stayed green for much of that window. The outage was invisible in check status because the model-unavailable evidence fallback concludes success while reporting model=none.

This is an observability defect, not an authorization one. See "Not a merge-authorization hole" below.

Reproduction evidence

Run 34931908846, job opencode-review, head 91be6442 — the run that was cited as the last successful OpenCode review:

CONTEXTUAL_ORCHESTRATOR_BASE_URL: http://127.0.0.1:18080
Configured OpenCode model pool: candidates=1 attempts=1 max-cycles=1
OpenCode contextual-orchestrator/orchestrator/free attempt 1/1 failed with exit 1.
OpenCode provider failure metadata: class=provider-error json-bytes=836 stderr-bytes=0; provider-controlled content suppressed.
OpenCode completed a full model-candidate cycle without a valid control conclusion.
OpenCode model pool exhausted before producing a valid control conclusion.
  OPENCODE_MODEL_POOL_OUTCOME: exhausted
OpenCode model-pool outcome=exhausted model=none; publish stage performs no duplicate model-catalog pass.
##[notice]Current-head model-unavailable evidence fallback candidate: scope=central OpenCode/Strix review-process changed_count=1 ...

The job concluded success. Every run in that window also reported provider secrets present: 5 of 5 and a gateway whose own preflight returned status: ready, so nothing in the check surface suggested an outage.

The underlying request failure is fixed in #2333 (the OpenCode provider baseURL omitted the gateway's served /v1 prefix, so the gateway answered route_not_found whose message is the bare string not found). That fix does not address this: the next provider-side outage will go green the same way.

Where it happens

  • .github/workflows/opencode-review-dispatch.yml:6954 — publish_blockers_after_model_unavailable() runs whenever COVERAGE_EVIDENCE_RESULT is success, publishes the fallback notice, and lets the job conclude success.
  • The only signals that a model never ran are an ::notice:: line and OPENCODE_MODEL_POOL_OUTCOME: exhausted inside the log. Neither surfaces in check status, and neither is counted anywhere across runs.

Not a merge-authorization hole

Worth stating explicitly so the fix does not get aimed at the wrong gate. The fallback never grants approval:

  • scripts/ci/opencode_existing_approval_gate.py:22 lists "model-unavailable evidence fallback" in FALLBACK_MARKERS and rejects such a body at line 137.
  • scripts/ci/opencode_review_receipt_gate.py:41 does the same via FALLBACK_APPROVAL_MARKERS (line 134).
  • merge_approval_block_reason in scripts/ci/pr_review_merge_scheduler_core.py independently requires reviewDecision == APPROVED plus a non-author formal approval on the exact current head.

So merges stayed correctly blocked. What broke is detection: a three-week model outage looked like a healthy pipeline.

Suggested direction

Make the fallback path observable rather than tightening approval gates:

  • Emit a distinct, greppable terminal marker when a check concludes through the fallback with model=none, separate from ordinary success.
  • Track consecutive fallback conclusions per repository and raise an alert past a small threshold — one fallback is a transient provider blip, twenty in a row is an outage.
  • Consider surfacing the fallback state in the check's output summary so it is visible without opening the run log.

Related but distinct: #2112 (suppressed provider failure envelope discards the cause — which is why the not found body was never visible here) and #626 (fallback allowlist drift).

🤖 Generated with Claude Code

https://claude.ai/code/session_01JMBfufaYsYU8hviiA75Xb3

Activity

  1. seonghobae commented on Sep 21, 2026

    @seonghobae
    ContributorAuthor

    Duplicate of #2335, which has the more precise diagnosis. Closing this in its favour; #2336 is the fix.

    One claim in this issue was wrong, and #2335 is right. I wrote that opencode_review_receipt_gate.py already keeps a fallback out of formal-receipt status. It does so only for APPROVED:

    if state == "APPROVED" and any(
        marker in body.casefold() for marker in FALLBACK_APPROVAL_MARKERS
    ):
        return False, "fallback approval is not a substantive formal review"

    The fallback posts CHANGES_REQUESTED, which this branch never sees, so it did count as a formal receipt and suppressed the scheduler wake that would have retried a real review. #2335 found that asymmetry; I missed it. (opencode_existing_approval_gate.py does filter fallback markers in any state, but that gate only ever judges approvals, so it was never the hole.)

    The conclusion both issues share stands: merges were not opened by this — the CHANGES_REQUESTED decision and the independent-approval requirement still blocked them. The damage was detection.

    Two details from here that #2335 does not carry, kept so they aren't lost:

    • Run 34931908846, job opencode-review, head 91be6442 — attempt 1/1 failed with exit 1, OPENCODE_MODEL_POOL_OUTCOME: exhausted, model=none, job concluded success. The same run reported provider secrets present: 5 of 5 and a gateway preflight of status: ready, which is why this never looked like an outage.
    • The publishing site is .github/workflows/opencode-review-dispatch.yml:6954, publish_blockers_after_model_unavailable() — it runs whenever COVERAGE_EVIDENCE_RESULT is success.

    The suggestion here to alert on consecutive fallback conclusions is not covered by #2336 and is worth keeping as follow-up: #2336 makes a single outage visible as a red check, but a run-over-run count is what distinguishes a transient provider blip from a three-week outage.

  2. seonghobae commented on Sep 21, 2026

    @seonghobae
    ContributorAuthor

    Superseded by #2335 (diagnosis) and #2336 (fix).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions