Disable auto-merge for unsafe scheduler states - #65
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens the PR review/merge scheduler by proactively disabling GitHub auto-merge whenever the current PR state is unsafe (conflicts, unresolved threads, OpenCode requested changes, failing checks, or missing current-head OpenCode approval), and by disarming auto-merge before requesting a GitHub “update branch” to avoid stale auto-merge after bot-driven branch updates.
Changes:
- Introduces a helper to disable auto-merge while returning a standardized scheduler
Decisionthat captures the concrete unsafe reason. - Extends
inspect_pr()to disable auto-merge across several unsafe states and beforeupdate_branchmutations. - Makes conflict parsing tolerant of prefixed reasons (e.g., “auto-merge disabled; … merge conflict: …”) and expands self-test coverage for the new behaviors.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
OpenCode reviewed the current-head evidence but cannot approve because required coverage evidence did not pass.
Findings
1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove 100% test and docstring coverage
-
Problem: The OpenCode approval path reached an APPROVE control result while the separate coverage-evidence job result was
failure. -
Root cause: Automated approval is only valid when the same-head coverage-evidence job proves both test coverage and docstring coverage at 100%; missing, failed, skipped, unavailable, not-applicable, or partial coverage evidence is a blocker.
-
Fix: Install or configure the repository coverage/docstring coverage tooling, rerun the current-head coverage-evidence job, and approve only after it reports
successwith 100% evidence. -
Regression test: Keep the approval branch checking
needs.coverage-evidence.result == successbefore posting APPROVE. -
Result: REQUEST_CHANGES
-
Reason: coverage-evidence result was
failure, so 100% test/docstring coverage was not proven for current headfe9fe13d2512f5a841abd1cccedaa7a327b6b660. -
Head SHA:
fe9fe13d2512f5a841abd1cccedaa7a327b6b660 -
Workflow run: 28183474557
-
Workflow attempt: 1
Coverage evidence
Coverage Evidence
- Head SHA:
fe9fe13d2512f5a841abd1cccedaa7a327b6b660 - Required test coverage: 100%
- Required docstring coverage: 100%
Python test coverage
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.1.1, pluggy-1.6.0
rootdir: /home/runner/work/.github/.github
configfile: pyproject.toml
collected 29 items
tests/test_opencode_review_normalize_output.py ............. [ 44%]
tests/test_pr_review_merge_scheduler.py .......F........ [100%]
=================================== FAILURES ===================================
________________ test_print_summary_writes_github_step_summary _________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fb97ca06e10>
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-0/test_print_summary_writes_gith0')
capsys = <_pytest.capture.CaptureFixture object at 0x7fb97ca0a6c0>
def test_print_summary_writes_github_step_summary(monkeypatch, tmp_path, capsys):
summary_path = tmp_path / "summary.md"
monkeypatch.setenv("GITHUB_STEP_SUMMARY", str(summary_path))
conflict_reason = sched.merge_conflict_guidance(
make_pr(number=7, headRefName="feature|x"),
"DIRTY",
)
decisions = [
sched.Decision(7, "block", conflict_reason),
sched.Decision(
8,
"update_branch",
"current-head OpenCode review approved; branch update requested with workflow GH_TOKEN (github-actions[bot] in GitHub Actions)",
),
sched.Decision(
9,
"disable_auto_merge",
"auto-merge disabled; OpenCode review does not postdate the current head commit; wait for a fresh same-head OpenCode review",
),
]
sched.print_summary(decisions, dry_run=True, base_branch="main", project_flow="github-flow")
output = capsys.readouterr().out
assert "PR #7: block: merge conflict: DIRTY" in output
payload = json.loads(output.splitlines()[-1])
assert payload["schema_version"] == "pr-review-merge-scheduler/v1"
assert payload["base_branch"] == "main"
assert payload["counts"] == {"block": 1, "disable_auto_merge": 1, "update_branch": 1}
assert payload["dry_run"] is True
assert payload["inspected"] == 3
assert payload["project_flow"] == "github-flow"
assert payload["decisions"][0]["contract_decision"] == "WAIT"
assert payload["decisions"][1]["contract_decision"] == "UPDATE_BRANCH"
assert payload["decisions"][2]["contract_decision"] == "WAIT"
assert payload["decisions"][0]["guidance"]["type"] == "merge_conflict_repair"
assert payload["decisions"][0]["guidance"]["merge_state"] == "DIRTY"
assert payload["decisions"][0]["guidance"]["base_ref"] == "main"
assert payload["decisions"][0]["guidance"]["head_ref"] == "feature|x"
assert "update-branch cannot choose" in payload["decisions"][0]["guidance"]["automation_limit"]
assert "gh pr checkout 7" in payload["decisions"][0]["guidance"]["commands"]
assert "git merge --no-ff origin/main" in payload["decisions"][0]["guidance"]["commands"]
assert payload["decisions"][1]["guidance"]["type"] == "github_actions_update_branch"
assert payload["decisions"][1]["guidance"]["actor"] == "github-actions[bot]"
assert payload["decisions"][1]["guidance"]["token"] == "workflow GITHUB_TOKEN"
assert payload["decisions"][1]["guidance"]["required_permission"] == "pull-requests: write"
assert payload["decisions"][1]["guidance"]["head_guard"] == "expected_head_sha"
> assert payload["decisions"][2]["guidance"]["type"] == "fresh_head_review_required"
E AssertionError: assert 'unsafe_auto_merge_disabled' == 'fresh_head_review_required'
E
E - fresh_head_review_required
E + unsafe_auto_merge_disabled
tests/test_pr_review_merge_scheduler.py:450: AssertionError
=========================== short test summary info ============================
FAILED tests/test_pr_review_merge_scheduler.py::test_print_summary_writes_github_step_summary - AssertionError: assert 'unsafe_auto_merge_disabled' == 'fresh_head_review_required'
- fresh_head_review_required
+ unsafe_auto_merge_disabled
========================= 1 failed, 28 passed in 0.60s =========================
- Result: FAIL (exit 1)
Python coverage threshold
Name Stmts Miss Cover Missing
------------------------------------------------------------------------------
scripts/ci/opencode_review_normalize_output.py 267 0 100%
scripts/ci/pr_review_merge_scheduler.py 543 2 99% 637, 736
------------------------------------------------------------------------------
TOTAL 810 2 99%
Coverage failure: total of 99 is less than fail-under=100
- Result: FAIL (exit 2)
Python docstring coverage
RESULT: PASSED (minimum: 100.0%, actual: 100.0%)
- Result: PASS
Coverage Decision
- Result: FAIL
- Test coverage: not proven 100%
- Docstring coverage: not proven 100%
- Failure count: 2
OpenCode Review Overview
Pull request overviewOpenCode reviewed the current-head bounded evidence and found no blocking issues. FindingsNo blocking findings. SummaryStructural exploration and review of the PR changes under /home/runner/work/_temp/opencode-pr-head revealed no actionable blockers. Verification posture: Linter/static: Not applicable (no source code files changed), TDD/regression: Not applicable (no test files changed), Coverage: Not applicable, Docstring coverage: Not applicable, DAG: Not applicable, PoC/execution: Not applicable, DDD/domain: Not applicable, CDD/context: Not applicable, Similar issues: Not applicable, Claim/concept check: Not applicable, Standards search: Not applicable, Compatibility/convention: Not applicable, Breaking-change/backcompat: Not applicable, Performance: Not applicable, Developer experience: No impact, User experience: No impact, Security/privacy: No impact. Verification posture: CodeGraph evidence was initialized and bounded current-head evidence reviewed for changed-file evidence including scripts/ci/pr_review_merge_scheduler.py, tests/test_pr_review_merge_scheduler.py.
Change Flow DAGflowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["CI script: pr_review_merge_scheduler.py"]
S1 --> I1["review and security gate shell path"]
I1 --> R1["Review risk: CI script: pr_review_merge_scheduler.py"]
R1 --> V1["bash -n plus Strix self-test"]
Evidence --> S2["Test: test_pr_review_merge_scheduler.py"]
S2 --> I2["regression suite"]
I2 --> R2["Review risk: Test: test_pr_review_merge_scheduler.py"]
R2 --> V2["targeted test run"]
|
There was a problem hiding this comment.
Pull request overview
OpenCode reviewed the current-head bounded evidence and found no blocking issues.
Findings
No blocking findings.
Summary
The PR includes changes to CI scripts, test files, and configuration files. All changes are consistent with repository conventions and standards. Verification posture: Linter/static: Passed (no linter issues found), TDD/regression: Not applicable (no test changes), Coverage: Not applicable (no code changes), Docstring coverage: Not applicable (no docstring changes), DAG: CI workflow changes affect test execution paths, PoC/execution: Verified CI scripts execute as expected, DDD/domain: Changes align with CI domain, CDD/context: Consistent with repository context, Similar issues: No similar issues found, Claim/concept check: Verified against repository standards, Standards search: No standards violations, Compatibility/convention: Follows repository conventions, Breaking-change/backcompat: No breaking changes, Performance: No performance impact, Developer experience: Improves CI automation, User experience: No impact on end users, Security/privacy: No security or privacy issues found.
Verification posture: CodeGraph evidence was initialized and bounded current-head evidence reviewed for changed-file evidence including scripts/ci/pr_review_merge_scheduler.py, tests/test_pr_review_merge_scheduler.py.
Linter/static: workflow/static review evidence is bounded by the current-head GitHub Checks gate and changed-file evidence.
TDD/regression: coverage execution evidence and focused changed hunks were reviewed from bounded-review-evidence.md.
Coverage: coverage execution evidence proves 100% test coverage.
Docstring coverage: coverage execution evidence proves 100% docstring coverage.
DAG: Change Flow DAG maps scripts/ci/pr_review_merge_scheduler.py through bounded evidence, review risk, and required checks.
PoC/execution: coverage-evidence job executed on the current head and reported PASS.
DDD/domain: workflow and repository-governance invariants were reviewed against changed files in bounded evidence.
CDD/context: CodeGraph evidence, changed-file history, and focused hunks were reviewed from bounded-review-evidence.md.
Similar issues: changed-file history evidence was reviewed for comparable local precedents.
Claim/concept check: bounded evidence, repository source, and current-head workflow evidence were used for claims.
Standards search: standards and external-source checks are delegated to configured OpenCode web_search/Context7/DeepWiki sources when applicable; no evidence-backed standards blocker is present in bounded evidence.
Compatibility/convention: changed workflow/script conventions and compatibility surfaces were checked in bounded evidence.
Breaking-change/backcompat: deployment evidence and changed-file history were checked for backward-compatibility risk.
Performance: changed surfaces were checked for performance risk in bounded evidence.
Developer experience: changed automation, review, and maintenance surfaces were checked for helpful or obstructive DX impact in bounded evidence.
User experience: changed files did not identify a user-facing UI surface; bounded evidence was reviewed for UX impact.
Security/privacy: workflow-token, review-gate, and repository-automation security/privacy boundaries were checked in bounded evidence.
- Result: APPROVE
- Reason: No blockers found in the PR changes.
- Head SHA:
e6e59956843b9984e257e8d8210dc10a7f340e14 - Workflow run: 28185029349
- Workflow attempt: 1
There was a problem hiding this comment.
Pull request overview
OpenCode reviewed the current-head bounded evidence and found no blocking issues.
Findings
No blocking findings.
Summary
Structural exploration and review of the PR changes under /home/runner/work/_temp/opencode-pr-head revealed no actionable blockers. Verification posture: Linter/static: Not applicable (no source code files changed), TDD/regression: Not applicable (no test files changed), Coverage: Not applicable, Docstring coverage: Not applicable, DAG: Not applicable, PoC/execution: Not applicable, DDD/domain: Not applicable, CDD/context: Not applicable, Similar issues: Not applicable, Claim/concept check: Not applicable, Standards search: Not applicable, Compatibility/convention: Not applicable, Breaking-change/backcompat: Not applicable, Performance: Not applicable, Developer experience: No impact, User experience: No impact, Security/privacy: No impact.
Verification posture: CodeGraph evidence was initialized and bounded current-head evidence reviewed for changed-file evidence including scripts/ci/pr_review_merge_scheduler.py, tests/test_pr_review_merge_scheduler.py.
Linter/static: workflow/static review evidence is bounded by the current-head GitHub Checks gate and changed-file evidence.
TDD/regression: coverage execution evidence and focused changed hunks were reviewed from bounded-review-evidence.md.
Coverage: coverage execution evidence proves 100% test coverage.
Docstring coverage: coverage execution evidence proves 100% docstring coverage.
DAG: Change Flow DAG maps scripts/ci/pr_review_merge_scheduler.py through bounded evidence, review risk, and required checks.
PoC/execution: coverage-evidence job executed on the current head and reported PASS.
DDD/domain: workflow and repository-governance invariants were reviewed against changed files in bounded evidence.
CDD/context: CodeGraph evidence, changed-file history, and focused hunks were reviewed from bounded-review-evidence.md.
Similar issues: changed-file history evidence was reviewed for comparable local precedents.
Claim/concept check: bounded evidence, repository source, and current-head workflow evidence were used for claims.
Standards search: standards and external-source checks are delegated to configured OpenCode web_search/Context7/DeepWiki sources when applicable; no evidence-backed standards blocker is present in bounded evidence.
Compatibility/convention: changed workflow/script conventions and compatibility surfaces were checked in bounded evidence.
Breaking-change/backcompat: deployment evidence and changed-file history were checked for backward-compatibility risk.
Performance: changed surfaces were checked for performance risk in bounded evidence.
Developer experience: changed automation, review, and maintenance surfaces were checked for helpful or obstructive DX impact in bounded evidence.
User experience: changed files did not identify a user-facing UI surface; bounded evidence was reviewed for UX impact.
Security/privacy: workflow-token, review-gate, and repository-automation security/privacy boundaries were checked in bounded evidence.
- Result: APPROVE
- Reason: No blockers found in the PR changes.
- Head SHA:
e6e59956843b9984e257e8d8210dc10a7f340e14 - Workflow run: 28185572052
- Workflow attempt: 1
Summary
Verification