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
210 changes: 157 additions & 53 deletions .github/workflows/opencode-review.yml

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ on:
concurrency:
group: >-
strix-${{ github.repository }}-${{ github.event_name == 'pull_request_target' &&
format('pr-{0}', github.event.pull_request.number) || github.event.inputs.pr_number != '' &&
format('pr-{0}', github.event.inputs.pr_number) || github.ref }}
format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha) ||
github.event.inputs.pr_number != '' && github.event.inputs.pr_head_sha != '' &&
format('pr-{0}-{1}', github.event.inputs.pr_number, github.event.inputs.pr_head_sha) ||
github.event.inputs.pr_number != '' && format('pr-{0}', github.event.inputs.pr_number) || github.ref }}
# cancel-in-progress deliberately disabled: an attacker could force-push
# a benign commit to cancel an in-progress scan of a malicious commit.
# a benign commit to cancel an in-progress scan of a malicious commit. The
# head SHA in PR groups prevents stale scans from serializing newer evidence.
cancel-in-progress: false

permissions:
Expand Down Expand Up @@ -377,9 +380,9 @@ jobs:
STRIX_SOURCE_DIRS: ". backend frontend"
STRIX_REASONING_EFFORT: low
STRIX_LLM_MAX_RETRIES: 1
STRIX_TRANSIENT_RETRY_PER_MODEL: 5
STRIX_TRANSIENT_RETRY_PER_MODEL: 2
STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS: 60
STRIX_FALLBACK_MODELS: ${{ steps.gate.outputs.provider_mode == 'github_models' && 'github_models/deepseek/deepseek-r1-0528 github_models/deepseek/deepseek-v3-0324' || '' }}
STRIX_FALLBACK_MODELS: ${{ steps.gate.outputs.provider_mode == 'github_models' && 'github_models/deepseek/deepseek-v3-0324 github_models/deepseek/deepseek-r1-0528' || '' }}
STRIX_FAIL_ON_PROVIDER_SIGNAL: "1"
STRIX_VERTEX_FALLBACK_MODELS: ""
NPM_CONFIG_IGNORE_SCRIPTS: "true"
Expand Down Expand Up @@ -440,7 +443,7 @@ jobs:
publish-manual-pr-evidence-status:
name: publish-manual-pr-evidence-status
needs: strix
if: ${{ always() && github.event_name == 'workflow_dispatch' && github.event.inputs.pr_head_sha != '' }}
if: ${{ always() && !cancelled() && github.event_name == 'workflow_dispatch' && github.event.inputs.pr_head_sha != '' }}
runs-on: ubuntu-latest
permissions:
statuses: write
Expand Down
91 changes: 59 additions & 32 deletions PR_GOVERNANCE_AUDIT.md

Large diffs are not rendered by default.

32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,37 @@ because it adds noise or misleading review experience.

OpenCode judges PRs; GitHub Actions performs mechanical updates and merges.
The scheduler updates a same-repository PR branch only when the latest OpenCode
review is approved and GitHub reports the PR as behind. After that update, the
new head must pass OpenCode, Strix, required checks, and review-thread gates
again before auto-merge or `--match-head-commit` merge can proceed.
Branch updates and merges run through the workflow `GITHUB_TOKEN`, so GitHub
records those mechanical mutations as `github-actions[bot]` rather than an
OpenCode app token or a personal token.
review is approved, no current-head failed check is present, and GitHub reports
the PR as behind. After that update, the new head must pass OpenCode, Strix,
required checks, and review-thread gates again before auto-merge or
`--match-head-commit` merge can proceed.
Branch updates run through the workflow `GITHUB_TOKEN`, so GitHub records those
mechanical updates as `github-actions[bot]` rather than an OpenCode app token or
a personal token. That path uses the pull-request branch update API and should
only need `pull-requests: write`; it does not justify widening repository
`contents` permission. Merge or auto-merge is a separate mutation. When a repo
wants GitHub Actions to perform the merge itself, that repo needs an explicit
scheduler-job `contents: write` policy exception and should expect Scorecard or
token-permission policy review to notice it.
That `update_branch` path is deliberately not used for `DIRTY` or
`CONFLICTING` PRs: GitHub cannot synthesize a safe conflict resolution for the
author, so the review must give the author a repair path instead of pretending
the bot can fix it.
When GitHub reports `DIRTY` or `CONFLICTING`, the scheduler does not pretend to
fix the branch. It blocks the PR with repair guidance: merge or rebase the
latest base branch into the PR branch, resolve conflict markers in that PR
branch, rerun focused checks, and push the same branch. OpenCode comments must
include a compact command block covering `gh pr checkout`, `git fetch`, merge or
rebase, `git status --short`, resolved-file staging, normal push, and
`--force-with-lease` only for rebased branches.

OpenCode review execution is `workflow_dispatch`-only. The scheduler dispatches
same-head Strix evidence first, then dispatches OpenCode for the same PR head.
This avoids running PR-head review, CodeGraph, coverage, or PoC code from a
privileged `pull_request_target` OpenCode workflow.
Strix keeps `cancel-in-progress: false` so old evidence is not cancelled by a
force-push, but PR-scoped concurrency includes the head SHA so an obsolete scan
does not serialize newer current-head evidence.

OpenCode approval is evidence-gated. Before approval, the review summary must
name changed files, CodeGraph or structural MCP evidence, a Change Flow DAG,
Expand Down
6 changes: 3 additions & 3 deletions scripts/ci/emit_opencode_failed_check_fallback_findings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -668,8 +668,8 @@ emit_strix_provider_failure_finding() {
if grep -Eq "api\\.deepseek\\.com|401 Unauthorized|Authentication Fails|DeepseekException" "$strix_evidence_file"; then
printf -- '- Problem: Strix failed before producing vulnerability reports. The failed log reported `RateLimitError` / `Too many requests` for the primary `openai/gpt-5` attempt, then fallback attempts reached direct DeepSeek (`api.deepseek.com`) and failed with `401 Unauthorized` or `Authentication Fails`, ending with `Configured model and fallback models were unavailable`.\n'
printf -- '- Root cause: The fallback model names were not routed through the GitHub Models endpoint for this failed PR check, so a GitHub Models token was used against direct DeepSeek instead of `https://models.github.ai/inference`; no Strix Vulnerability Report window was produced.\n'
printf -- '- Fix: Do not approve from this failed scan. Keep %s:%s using the GitHub Models-qualified fallback list (`github_models/deepseek/deepseek-r1-0528 github_models/deepseek/deepseek-v3-0324`) and keep the Strix gate mapping those values to `openai/deepseek/...` for the GitHub Models API base, then rerun the failed PR Strix check.\n' "$path" "$line"
printf -- '- Suggested edit: `%s:%s` must use `STRIX_FALLBACK_MODELS: ${{ steps.gate.outputs.provider_mode == '\''github_models'\'' && '\''github_models/deepseek/deepseek-r1-0528 github_models/deepseek/deepseek-v3-0324'\'' || '\'''\'' }}` instead of unqualified `deepseek/...` values that route to `api.deepseek.com`.\n' "$path" "$line"
printf -- '- Fix: Do not approve from this failed scan. Keep %s:%s using the GitHub Models-qualified fallback list (`github_models/deepseek/deepseek-v3-0324 github_models/deepseek/deepseek-r1-0528`) and keep the Strix gate mapping those values to `openai/deepseek/...` for the GitHub Models API base, then rerun the failed PR Strix check.\n' "$path" "$line"
printf -- '- Suggested edit: `%s:%s` must use `STRIX_FALLBACK_MODELS: ${{ steps.gate.outputs.provider_mode == '\''github_models'\'' && '\''github_models/deepseek/deepseek-v3-0324 github_models/deepseek/deepseek-r1-0528'\'' || '\'''\'' }}` instead of unqualified `deepseek/...` values that route to `api.deepseek.com`.\n' "$path" "$line"
else
printf -- '- Problem: Strix failed before producing vulnerability reports. The failed log reported LLM CONNECTION FAILED, RateLimitError or Too many requests for the primary model, provider/budget output for fallback models, and Configured model and fallback models were unavailable.\n'
printf -- '- Root cause: The configured GitHub Models primary/fallback provider capacity or provider route failed for this run; no Strix Vulnerability Report window was produced, so there is no application source line to patch from this evidence.\n'
Expand Down Expand Up @@ -745,6 +745,6 @@ emit_strix_provider_failure_finding "$strix_evidence_file"
emit_strix_cancelled_without_log_finding "$strix_evidence_file"

if [ "$finding_index" -eq 0 ]; then
printf 'No source-backed failed-check fallback finding matched the available evidence; leaving the PR review unchanged so the current-head review can be rerun with better evidence.\n' >&2
printf 'No source-backed failed-check fallback finding matched the available evidence. No PR review was posted; retry after current-head failed-check logs or annotations are available, or rerun the failed check to collect them.\n' >&2
exit 1
fi
10 changes: 5 additions & 5 deletions scripts/ci/opencode_review_normalize_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@
)

NON_ACTIONABLE_FAILED_CHECK_REVIEW_PHRASES = (
"no deterministic missing-string markers",
"no deterministic missing string markers",
"strix report locations were recognized",
"use the failed-check evidence below to map",
"map each failed check to exact local source lines before approving",
"deterministic missing-string markers",
"deterministic missing string markers",
"strix report locations",
"failed-check evidence below",
"map each failed check to exact local source lines",
)

CHANGED_FILE_EVIDENCE_PATTERN = re.compile(
Expand Down
Loading