From 94f61b8dc428fa61631a542fcd4c9e39383ea248 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 13 Sep 2026 22:58:33 +0900 Subject: [PATCH 1/8] test(autofix): reproduce Required OpenCode coverage RCA deadlock --- ...cheduler_required_opencode_coverage_rca.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py diff --git a/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py b/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py new file mode 100644 index 0000000000..d197276a81 --- /dev/null +++ b/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py @@ -0,0 +1,82 @@ +"""Regression contracts for Required OpenCode pre-review coverage RCA routing.""" + +from scripts.ci import pr_review_fix_scheduler as fix + + +def _required_opencode_check( + *, + name: str, + conclusion: str | None, + status: str = "COMPLETED", + created_at: str = "2026-09-13T00:00:00Z", +) -> dict: + """Build one Required OpenCode Review check-run fixture.""" + return { + "__typename": "CheckRun", + "name": name, + "status": status, + "conclusion": conclusion, + "checkSuite": { + "createdAt": created_at, + "workflowRun": {"workflow": {"name": "Required OpenCode Review"}}, + }, + } + + +def _pr_with_checks(*checks: dict) -> dict: + """Build the minimal clean same-head PR context required by RCA classification.""" + return { + "number": 2079, + "isDraft": False, + "baseRefName": "main", + "baseRefOid": "b" * 40, + "headRefName": "feature", + "headRefOid": "a" * 40, + "headRepository": {"nameWithOwner": "owner/repo"}, + "mergeStateStatus": "CLEAN", + "reviews": {"nodes": []}, + "reviewThreads": {"nodes": []}, + "statusCheckRollup": {"contexts": {"nodes": list(checks)}}, + } + + +def test_required_opencode_coverage_failure_routes_to_rca_without_review() -> None: + """A failed source-backed coverage gate must reach RCA before model review exists.""" + pr = _pr_with_checks( + _required_opencode_check(name="coverage-evidence", conclusion="FAILURE") + ) + + assert fix.current_head_failed_checks(pr) == ("coverage-evidence",) + assert fix.needs_rca_repair(pr) == ( + True, + ("current-head failed check(s) require RCA: coverage-evidence",), + ) + + +def test_required_opencode_orchestrator_failure_stays_nonrecursive() -> None: + """The OpenCode orchestration status must not recursively dispatch its own repair.""" + pr = _pr_with_checks( + _required_opencode_check(name="opencode-review", conclusion="FAILURE") + ) + + assert fix.current_head_failed_checks(pr) == () + assert fix.needs_rca_repair(pr) == (False, ()) + + +def test_newer_required_opencode_coverage_success_supersedes_stale_failure() -> None: + """A newer successful coverage attempt must retire the older failure evidence.""" + pr = _pr_with_checks( + _required_opencode_check( + name="coverage-evidence", + conclusion="FAILURE", + created_at="2026-09-13T00:00:00Z", + ), + _required_opencode_check( + name="coverage-evidence", + conclusion="SUCCESS", + created_at="2026-09-13T00:05:00Z", + ), + ) + + assert fix.current_head_failed_checks(pr) == () + assert fix.needs_rca_repair(pr) == (False, ()) From 67dfa048446c96d99e3db2ebacdc02d16ed6294e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 13 Sep 2026 23:51:58 +0900 Subject: [PATCH 2/8] test(autofix): cover pending Required OpenCode coverage attempts --- ...cheduler_required_opencode_coverage_rca.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py b/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py index d197276a81..1c43a1f8a7 100644 --- a/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py +++ b/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py @@ -63,6 +63,26 @@ def test_required_opencode_orchestrator_failure_stays_nonrecursive() -> None: assert fix.needs_rca_repair(pr) == (False, ()) +def test_pending_required_opencode_coverage_attempt_supersedes_stale_failure() -> None: + """A pending coverage rerun must retire stale failure evidence until it is terminal.""" + pr = _pr_with_checks( + _required_opencode_check( + name="coverage-evidence", + conclusion="FAILURE", + created_at="2026-09-13T00:00:00Z", + ), + _required_opencode_check( + name="coverage-evidence", + conclusion=None, + status="IN_PROGRESS", + created_at="2026-09-13T00:05:00Z", + ), + ) + + assert fix.current_head_failed_checks(pr) == () + assert fix.needs_rca_repair(pr) == (False, ()) + + def test_newer_required_opencode_coverage_success_supersedes_stale_failure() -> None: """A newer successful coverage attempt must retire the older failure evidence.""" pr = _pr_with_checks( From 7cda25c8b903108051e0d1d78e49d2b692a94a4a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 13 Sep 2026 23:54:03 +0900 Subject: [PATCH 3/8] fix(autofix): admit source-backed pre-review coverage RCA --- scripts/ci/pr_review_fix_scheduler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/ci/pr_review_fix_scheduler.py b/scripts/ci/pr_review_fix_scheduler.py index bc2868c5a4..4fe59527ff 100755 --- a/scripts/ci/pr_review_fix_scheduler.py +++ b/scripts/ci/pr_review_fix_scheduler.py @@ -99,6 +99,7 @@ REST_UNKNOWN_GITHUB_ACTIONS_WORKFLOW, } ) +RCA_SOURCE_BACKED_PRE_REVIEW_CHECK_NAMES = frozenset({"coverage-evidence"}) FAILED_CHECK_CONCLUSIONS = frozenset( {"FAILURE", "STARTUP_FAILURE", "TIMED_OUT"} ) @@ -285,7 +286,10 @@ def current_head_failed_checks(pr: dict[str, Any]) -> tuple[str, ...]: conclusion = str(node.get("conclusion") or "").upper() if ( name not in RCA_IGNORED_CHECK_NAMES - and workflow_name not in RCA_IGNORED_WORKFLOW_NAMES + and ( + workflow_name not in RCA_IGNORED_WORKFLOW_NAMES + or name in RCA_SOURCE_BACKED_PRE_REVIEW_CHECK_NAMES + ) and conclusion in FAILED_CHECK_CONCLUSIONS ): failed.append(name or "unnamed check") From d01316990c9410165736ef09f0ab9979241078e8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 13 Sep 2026 23:58:19 +0900 Subject: [PATCH 4/8] test(autofix): bind coverage RCA to authoritative workflow --- ...cheduler_required_opencode_coverage_rca.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py b/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py index 1c43a1f8a7..df931f72b5 100644 --- a/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py +++ b/tests/test_pr_review_fix_scheduler_required_opencode_coverage_rca.py @@ -9,8 +9,9 @@ def _required_opencode_check( conclusion: str | None, status: str = "COMPLETED", created_at: str = "2026-09-13T00:00:00Z", + workflow_name: str = "Required OpenCode Review", ) -> dict: - """Build one Required OpenCode Review check-run fixture.""" + """Build one OpenCode-family check-run fixture with explicit workflow provenance.""" return { "__typename": "CheckRun", "name": name, @@ -18,7 +19,7 @@ def _required_opencode_check( "conclusion": conclusion, "checkSuite": { "createdAt": created_at, - "workflowRun": {"workflow": {"name": "Required OpenCode Review"}}, + "workflowRun": {"workflow": {"name": workflow_name}}, }, } @@ -53,6 +54,20 @@ def test_required_opencode_coverage_failure_routes_to_rca_without_review() -> No ) +def test_other_opencode_workflow_coverage_name_stays_excluded() -> None: + """Coverage RCA admission must bind the exact authoritative workflow and check name.""" + pr = _pr_with_checks( + _required_opencode_check( + name="coverage-evidence", + conclusion="FAILURE", + workflow_name="OpenCode Review", + ) + ) + + assert fix.current_head_failed_checks(pr) == () + assert fix.needs_rca_repair(pr) == (False, ()) + + def test_required_opencode_orchestrator_failure_stays_nonrecursive() -> None: """The OpenCode orchestration status must not recursively dispatch its own repair.""" pr = _pr_with_checks( From d3f1d0264912e74b897fdf2a0a6085100de4e40a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 13 Sep 2026 23:59:41 +0900 Subject: [PATCH 5/8] fix(autofix): bind coverage RCA to workflow provenance --- scripts/ci/pr_review_fix_scheduler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/ci/pr_review_fix_scheduler.py b/scripts/ci/pr_review_fix_scheduler.py index 4fe59527ff..c1e71c4ae3 100755 --- a/scripts/ci/pr_review_fix_scheduler.py +++ b/scripts/ci/pr_review_fix_scheduler.py @@ -99,7 +99,9 @@ REST_UNKNOWN_GITHUB_ACTIONS_WORKFLOW, } ) -RCA_SOURCE_BACKED_PRE_REVIEW_CHECK_NAMES = frozenset({"coverage-evidence"}) +RCA_SOURCE_BACKED_PRE_REVIEW_CHECKS = frozenset( + {("Required OpenCode Review", "coverage-evidence")} +) FAILED_CHECK_CONCLUSIONS = frozenset( {"FAILURE", "STARTUP_FAILURE", "TIMED_OUT"} ) @@ -288,7 +290,7 @@ def current_head_failed_checks(pr: dict[str, Any]) -> tuple[str, ...]: name not in RCA_IGNORED_CHECK_NAMES and ( workflow_name not in RCA_IGNORED_WORKFLOW_NAMES - or name in RCA_SOURCE_BACKED_PRE_REVIEW_CHECK_NAMES + or (workflow_name, name) in RCA_SOURCE_BACKED_PRE_REVIEW_CHECKS ) and conclusion in FAILED_CHECK_CONCLUSIONS ): From 88b516ec77ccbd253696091d706630f77798e727 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 14 Sep 2026 03:48:14 +0900 Subject: [PATCH 6/8] test(review): pin full-suite Noema dependency closure --- ...t_review_repair_full_suite_dependencies.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/test_review_repair_full_suite_dependencies.py diff --git a/tests/test_review_repair_full_suite_dependencies.py b/tests/test_review_repair_full_suite_dependencies.py new file mode 100644 index 0000000000..cf2f5146cc --- /dev/null +++ b/tests/test_review_repair_full_suite_dependencies.py @@ -0,0 +1,19 @@ +"""Regression contracts for review-repair full-suite dependency closure.""" + +from pathlib import Path + + +WORKFLOW = Path(".github/workflows/agent-review-runtime-quality-ci.yml") + + +def test_review_repair_full_suite_installs_noema_document_dependency() -> None: + """Full-suite review-repair collection must provision Noema's XML reader.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + install_step = workflow.split( + "- name: Install exact Noema document dependencies", + maxsplit=1, + )[1].split("- name: Verify Noema token-lifetime contracts", maxsplit=1)[0] + + assert "steps.affected_suites.outputs.noema == 'true'" in install_step + assert "steps.affected_suites.outputs.review_repair == 'true'" in install_step + assert "requirements-noema-document-ci-hashes.txt" in install_step From efc2479eb250da83af7def831c94093683d43817 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 14 Sep 2026 03:49:22 +0900 Subject: [PATCH 7/8] fix(review): provision full-suite Noema dependency --- .github/workflows/agent-review-runtime-quality-ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/agent-review-runtime-quality-ci.yml b/.github/workflows/agent-review-runtime-quality-ci.yml index 3976424bfb..f1a1215d39 100644 --- a/.github/workflows/agent-review-runtime-quality-ci.yml +++ b/.github/workflows/agent-review-runtime-quality-ci.yml @@ -60,6 +60,7 @@ on: - "tests/test_github_hourly_conflict_repair.py" - "tests/test_hourly_scheduler_runtime_budget.py" - "tests/test_hourly_autofix_context_quality_gate.py" + - "tests/test_review_repair_full_suite_dependencies.py" - "tests/test_pr_review_conflict_scope.py" - "tests/test_pr_review_conflict_scope_control_files.py" - "tests/test_pr_review_conflict_scope_git_executable.py" @@ -255,6 +256,7 @@ jobs: tests/test_github_hourly_conflict_repair.py|\ tests/test_hourly_scheduler_runtime_budget.py|\ tests/test_hourly_autofix_context_quality_gate.py|\ + tests/test_review_repair_full_suite_dependencies.py|\ tests/test_pr_review_conflict_scope.py|\ tests/test_pr_review_conflict_scope_control_files.py|\ tests/test_pr_review_conflict_scope_git_executable.py|\ @@ -346,7 +348,7 @@ jobs: -r requirements-opencode-review-ci-hashes.txt - name: Install exact Noema document dependencies - if: steps.affected_suites.outputs.noema == 'true' + if: steps.affected_suites.outputs.noema == 'true' || steps.affected_suites.outputs.review_repair == 'true' run: >- python -m pip install --disable-pip-version-check --require-hashes --no-deps -r requirements-noema-document-ci-hashes.txt @@ -446,6 +448,7 @@ jobs: tests/test_hourly_scheduler_runtime_budget.py \ tests/test_pr_review_conflict_scope_control_files.py \ tests/test_hourly_autofix_context_quality_gate.py \ + tests/test_review_repair_full_suite_dependencies.py \ tests/test_pr_review_conflict_scope_git_executable.py \ tests/test_pr_review_conflict_scope_ignored_paths.py \ tests/test_pr_review_conflict_scope_symlink_targets.py \ @@ -510,7 +513,7 @@ jobs: --include=scripts/ci/verify_exact_artifact_sbom_handoff.py \ --show-missing \ --fail-under=100 - python -m interrogate --fail-under=100 \ + python -m interrogate --fail-under 100 \ scripts/ci/verify_exact_artifact_sbom_handoff.py python -m compileall -q \ scripts/ci/verify_exact_artifact_sbom_handoff.py \ @@ -525,4 +528,4 @@ jobs: python -m pytest -q tests/test_agent_review_runtime_quality_consolidation.py python -m compileall -q tests/test_agent_review_runtime_quality_consolidation.py git diff --check "${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}" - git diff --exit-code + git diff --exit-code \ No newline at end of file From ae0f2f57f1d2abda7bb2e7ac9ce3bf8f1cac6f39 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 14 Sep 2026 03:50:35 +0900 Subject: [PATCH 8/8] chore(review): preserve unrelated quality syntax --- .github/workflows/agent-review-runtime-quality-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/agent-review-runtime-quality-ci.yml b/.github/workflows/agent-review-runtime-quality-ci.yml index f1a1215d39..23e3b80789 100644 --- a/.github/workflows/agent-review-runtime-quality-ci.yml +++ b/.github/workflows/agent-review-runtime-quality-ci.yml @@ -513,7 +513,7 @@ jobs: --include=scripts/ci/verify_exact_artifact_sbom_handoff.py \ --show-missing \ --fail-under=100 - python -m interrogate --fail-under 100 \ + python -m interrogate --fail-under=100 \ scripts/ci/verify_exact_artifact_sbom_handoff.py python -m compileall -q \ scripts/ci/verify_exact_artifact_sbom_handoff.py \ @@ -528,4 +528,4 @@ jobs: python -m pytest -q tests/test_agent_review_runtime_quality_consolidation.py python -m compileall -q tests/test_agent_review_runtime_quality_consolidation.py git diff --check "${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}" - git diff --exit-code \ No newline at end of file + git diff --exit-code