Skip to content

fix(review): 중앙 스케줄러의 대상 PR 디스패치 지원#586

Open
seonghobae wants to merge 6 commits into
mainfrom
fix/targeted-central-merge-scheduler
Open

fix(review): 중앙 스케줄러의 대상 PR 디스패치 지원#586
seonghobae wants to merge 6 commits into
mainfrom
fix/targeted-central-merge-scheduler

Conversation

@seonghobae

@seonghobae seonghobae commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

개요

대상 저장소의 스케줄러가 중앙 Required OpenCode Review를 디스패치할 교차 저장소 자격증명이 없어 대기하는 문제를 해결합니다. 중앙 .githubmerge-scheduler 디스패치가 target_repositorypr_number를 받아 한 PR만 안전하게 처리할 수 있도록 했습니다.

조직 큐 실운영에서 GitHub GraphQL 응답이 unexpected end of JSON input으로 절단되어 naruon 큐 전체가 중단된 사례도 함께 복구합니다.

운영 실패 증거: https://github.com/ContextualWisdomLab/.github/actions/runs/29730037605

변경 사항

  • ContextualWisdomLab/* 대상과 양의 PR 번호만 허용합니다.

  • GitHub API에서 열린 PR의 base 저장소·base/head SHA를 다시 읽어 라이브 메타데이터에 바인딩합니다.

  • 검증 뒤 헤드가 바뀌면 오래된 디스패치는 성공적으로 무동작합니다.

  • 중앙 github.token은 중앙 Actions 조회/디스패치에만 사용하고, 앱/PAT는 대상 PR 읽기·변경에 사용합니다.

  • 대상 저장소 Actions API에 중앙 전용 토큰으로 먼저 접근해 403이 발생하던 경로를 차단합니다.

  • 대상 base branch에서 project flow를 파생합니다.

  • GitHub GraphQL의 절단된 stderr와 성공 종료 뒤 잘린 stdout을 일시 장애로 분류해 bounded retry합니다.

  • 재시도 소진 뒤 기존 REST fallback으로 전환해 저장소 큐 전체가 중단되지 않게 합니다.

  • 중앙 repository_dispatch OpenCode의 queued/in-progress 실행만 집계해 조직 전체 active 상한을 적용합니다.

  • ACTIVE_OPENCODE_REVIEW_LIMIT 저장소 변수로 상한을 조정하며 기본값은 16입니다.

  • 상한 도달 시 OpenCode만 다음 heartbeat로 미루고 Strix·일반 CI·병합 판단은 계속합니다.

  • 잘못된 상한 값은 조용히 무제한으로 전환하지 않고 명시적 오류로 차단합니다.

  • Noema의 기존 리뷰 재사용 시 GitHub commit_id, 본문의 Head SHA, noema-review-gate head_sha를 모두 current HEAD에 바인딩합니다.

  • 본문/marker가 이전 HEAD를 가리키는 승인은 재사용하지 않아 독립 Noema 리뷰가 다시 실행될 수 있게 합니다.

검증

  • codegraph sync 및 변경 call path 확인
  • git diff --check
  • actionlint .github/workflows/pr-review-merge-scheduler.yml
  • python3 -m ruff check scripts/ci/pr_review_merge_scheduler.py tests/test_pr_review_merge_scheduler.py
  • python3 -m py_compile scripts/ci/pr_review_merge_scheduler.py
  • python3 scripts/ci/pr_review_merge_scheduler.py --self-test
  • PyYAML safe load
  • actionlint -shellcheck= -pyflakes= .github/workflows/pr-review-merge-scheduler.yml
  • python3 -m interrogate -c pyproject.toml scripts/ci — 100%
  • python3 -m pytest -q — 650 passed

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head abd8f7d6a83a827c23157335e00c3ef70d66b414.

  • Head SHA: abd8f7d6a83a827c23157335e00c3ef70d66b414

  • Workflow run: 29580222601

  • Workflow attempt: 1

Coverage evidence

Coverage Decision

  • Result: FAIL
  • Test evidence: not proven passing
  • Docstring evidence: not proven passing when configured
  • Failure count: 1

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Workflow: pr-review-merge-scheduler.yml"]
  S1 --> I1["GitHub Actions review job"]
  I1 --> R1["Review risk: Workflow: pr-review-merge-scheduler.yml"]
  R1 --> V1["actionlint plus required checks"]
  Evidence --> S2["CI script: pr_review_merge_scheduler.py"]
  S2 --> I2["review and security gate shell path"]
  I2 --> R2["Review risk: CI script: pr_review_merge_scheduler.py"]
  R2 --> V2["bash -n plus Strix self-test"]
  Evidence --> S3["Test (3 files)"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test (3 files)"]
  R3 --> V3["targeted test run"]
Loading

@opencode-agent

opencode-agent Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: f490dfef0637b88fc5f281e84bb1118746812065
  • Workflow run: 29828997559
  • Workflow attempt: 1
  • Gate result: REQUEST_CHANGES (approval step)

Pull request overview

OpenCode could not approve from deterministic current-head evidence because GitHub Checks have failed.

Findings

1. HIGH Current-head GitHub Checks - Fix failed required checks before approval

  • Problem: Failed same-head checks remain for f490dfef0637b88fc5f281e84bb1118746812065.
  • Root cause: The model-unavailable evidence fallback is allowed only when peer GitHub Checks are complete and clean.
  • Fix: Read and fix the failed check logs below, then rerun the current-head checks.
  • Regression test: Keep the model-unavailable fallback gated on an empty failed-check rollup.

Failed checks:

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Workflow: pr-review-merge-scheduler.yml"]
  S1 --> I1["GitHub Actions review job"]
  I1 --> R1["Review risk: Workflow: pr-review-merge-scheduler.yml"]
  R1 --> V1["actionlint plus required checks"]
  Evidence --> S2["CI script (2 files)"]
  S2 --> I2["review and security gate shell path"]
  I2 --> R2["Review risk: CI script (2 files)"]
  R2 --> V2["bash -n plus Strix self-test"]
  Evidence --> S3["Test (4 files)"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test (4 files)"]
  R3 --> V3["targeted test run"]
Loading

@seonghobae

Copy link
Copy Markdown
Contributor Author

현재 헤드 보강 검증 (523bea3c454928294cef68e2a213bd9b3d8d8bb7): 이전 OpenCode coverage 실패가 지적한 1838, 3756, 3759-3760, 3768-3771 분기를 각각 실행하는 회귀 테스트를 추가했습니다.

참고로 이 헤드의 Strix 29582300093도 기존 기본 브랜치 래퍼가 provider 고갈·보고서 0개인 gate exit 1을 neutral skip으로 바꾼 false-green 실행입니다. 따라서 #587의 fail-closed 부트스트랩 후 이 PR의 Strix를 새 기본 래퍼로 다시 검증해야 합니다.

@seonghobae
seonghobae dismissed opencode-agent[bot]’s stale review July 20, 2026 03:54

Superseded by current head 523bea3, which adds the missing branch regressions and proves 100% coverage (642 tests passed). A fresh current-head review is still required.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Current-HEAD 큐 backpressure 검증

  • Head SHA: f47a35ab79814acb609290dbbd3ee9bd7bef7faa
  • CodeGraph sync/explore: 변경된 dispatch 경로와 caller 영향 재확인
  • 실제 중앙 큐 집계: API 상태 창 내 active OpenCode repository_dispatch 120건(기본 상한 16 초과)
  • 동작: 상한 도달 시 capacity_reached로 기록하고 다음 scheduler heartbeat에서 재시도; Strix와 일반 CI는 제한 대상이 아님
  • python -m pytest -q: 649 passed
  • 집중 scheduler/workflow 계약 테스트: 203 passed
  • Ruff, actionlint, git diff --check: 통과
  • 이전 HEAD의 queued/in-progress 실행: 0건

현재 GitHub-hosted 체크는 기존 중앙 Actions 큐 포화로 대기 중이며, 위 변경은 새 OpenCode 디스패치가 큐를 계속 증폭시키는 저장소 로컬 원인을 차단합니다.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Current-HEAD Noema identity hardening

  • Head SHA: 1c7d00846428d36c3357d3683bba780e5313f694
  • 운영 재현: naruon#1085의 Noema 승인에서 REST commit_id는 current HEAD였지만 본문과 marker의 head_sha는 이전 HEAD를 가리킴
  • 수정: 기존 리뷰 재사용이 commit/body/marker 세 식별자를 모두 current HEAD에 묶도록 fail-closed 처리
  • 회귀: 본문 불일치, marker 불일치, 완전 일치 케이스 추가
  • 전체 테스트: 650 passed
  • 집중 테스트: 221 passed
  • Ruff, CodeGraph sync/explore, git diff --check: 통과
  • 이전 HEAD 활성 실행: 0건

@seonghobae

Copy link
Copy Markdown
Contributor Author

Naruon stale Noema approval cleanup

The new identity guard was applied to the live open-PR inventory before merge.

  • Invalid active Noema approvals dismissed: naruon#1081, #1082, #1083, #1085
  • Each dismissal was guarded by the live PR head and exact review object; the review commit and/or explicit body/marker head did not match the live head.
  • Post-cleanup audit across all open Naruon PRs: 0 active Noema reviews with commit/body/marker head mismatch.
  • These approvals can be replaced only by a new exact-current-HEAD independent Noema review after primary OpenCode evidence.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Organization-wide active Noema identity audit

This is governance cleanup only; every dismissed review requires a fresh exact-current-HEAD independent Noema review before it can contribute to the two-reviewer rule.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head 1c7d00846428d36c3357d3683bba780e5313f694.

  • Head SHA: 1c7d00846428d36c3357d3683bba780e5313f694

  • Workflow run: 29799150787

  • Workflow attempt: 1

Coverage evidence

Coverage Decision

  • Result: FAIL
  • Test evidence: not proven passing
  • Docstring evidence: not proven passing when configured
  • Failure count: 1

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Workflow: pr-review-merge-scheduler.yml"]
  S1 --> I1["GitHub Actions review job"]
  I1 --> R1["Review risk: Workflow: pr-review-merge-scheduler.yml"]
  R1 --> V1["actionlint plus required checks"]
  Evidence --> S2["CI script (2 files)"]
  S2 --> I2["review and security gate shell path"]
  I2 --> R2["Review risk: CI script (2 files)"]
  R2 --> V2["bash -n plus Strix self-test"]
  Evidence --> S3["Test (4 files)"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test (4 files)"]
  R3 --> V3["targeted test run"]
Loading

Copilot AI review requested due to automatic review settings July 21, 2026 12:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the central PR Review Merge Scheduler workflow and its supporting Python tooling to safely support targeted, cross-repository dispatch (binding to live PR metadata), add bounded retries for transient/truncated GitHub GraphQL responses, and introduce an aggregate central OpenCode active-run capacity limit to prevent organization-wide queue saturation.

Changes:

  • Add a targeted repository_dispatch path that validates target_repository/pr_number, re-fetches live PR metadata (base repo/branch + base/head SHAs), and no-ops if the head has changed.
  • Harden scheduler reliability: retry transient/truncated GraphQL failures, scope Actions API calls to avoid guaranteed 403s, and sanitize Actions step-summary Markdown rendering.
  • Strengthen Noema review reuse and transport safety: bind reuse to current HEAD via commit/body/marker, require verified actor, and enforce HTTPS + public-IP pinning for the LLM endpoint.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.github/workflows/pr-review-merge-scheduler.yml Adds targeted central dispatch preflight + env wiring for scoped Actions access and aggregate OpenCode capacity limiting.
scripts/ci/pr_review_merge_scheduler.py Implements Actions scope guard, GraphQL retry hardening, central OpenCode capacity checks, targeted expected-head no-op, and safe Markdown summary rendering.
scripts/ci/noema_review_gate.py Tightens Noema reuse to current HEAD + verified actor; replaces URL fetching with validated/pinned HTTPS transport.
tests/test_required_workflow_queue_contract.py Contract assertions for new targeted central dispatch binding behavior in the workflow.
tests/test_pr_review_merge_scheduler.py Adds tests for GraphQL retry cases, Actions scoping, central capacity behavior, targeted expected-head behavior, and Markdown sanitization.
tests/test_opencode_agent_contract.py Updates workflow contract expectations for expanded read-token selection and new env vars.
tests/test_noema_review_gate.py Adds coverage for Noema head-binding checks, pinned HTTPS transport, and stricter URL validation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 94 to 99
github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].number && format('pr-{0}', github.event.workflow_run.pull_requests[0].number) ||
github.event_name == 'workflow_call' && inputs.pr_number != '' && format('pr-{0}', inputs.pr_number) ||
github.event_name == 'workflow_call' && inputs.base_branch != '' && format('call-{0}', inputs.base_branch) ||
github.event_name == 'repository_dispatch' && github.event.client_payload.target_repository != '' && format('target-{0}-pr-{1}', github.event.client_payload.target_repository, github.event.client_payload.pr_number) ||
github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && format('pr-{0}', github.event.client_payload.pr_number) ||
github.event_name == 'repository_dispatch' && github.run_id ||

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

OpenCode could not approve from deterministic current-head evidence because GitHub Checks have failed.

Findings

1. HIGH Current-head GitHub Checks - Fix failed required checks before approval

  • Problem: Failed same-head checks remain for f490dfef0637b88fc5f281e84bb1118746812065.
  • Root cause: The model-unavailable evidence fallback is allowed only when peer GitHub Checks are complete and clean.
  • Fix: Read and fix the failed check logs below, then rerun the current-head checks.
  • Regression test: Keep the model-unavailable fallback gated on an empty failed-check rollup.

Failed checks:

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Workflow: pr-review-merge-scheduler.yml"]
  S1 --> I1["GitHub Actions review job"]
  I1 --> R1["Review risk: Workflow: pr-review-merge-scheduler.yml"]
  R1 --> V1["actionlint plus required checks"]
  Evidence --> S2["CI script (2 files)"]
  S2 --> I2["review and security gate shell path"]
  I2 --> R2["Review risk: CI script (2 files)"]
  R2 --> V2["bash -n plus Strix self-test"]
  Evidence --> S3["Test (4 files)"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test (4 files)"]
  R3 --> V3["targeted test run"]
Loading

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

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants