Skip to content

feat(automation): route trusted review-agent mentions - #787

Open
seonghobae wants to merge 93 commits into
mainfrom
feat/review-agent-mention-router-main
Open

feat(automation): route trusted review-agent mentions#787
seonghobae wants to merge 93 commits into
mainfrom
feat/review-agent-mention-router-main

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Purpose

Provide a central, bounded comment-invocation surface for the existing Noema and OpenCode review planes without copying privileged implementation into product repositories.

Product behavior

  • route trusted @cwl-noema-review and review-only @opencode-agent comments;
  • bind every invocation to repository, PR number, exact head SHA, base branch, source comment ID, and requested agent;
  • use deterministic per-agent invocation keys and central wrapper workflow-run records as the durable idempotency ledger;
  • retry only missing agents after partial completion; completed, failed, queued, and in-progress exact-key wrapper runs suppress duplicate forwarding;
  • treat target-repository acknowledgement comments and reactions as UX signals only, never durable dispatch authority;
  • allow only comments from OWNER, MEMBER, or COLLABORATOR identities and reject bot-authored requests;
  • scan sibling repositories every five minutes because organization required workflows do not propagate issue_comment events into their default branches;
  • count only newly queued agent work against the bounded dispatch budget, so rejected, duplicate, already-ledgered, and other no-op requests cannot starve newer mentions;
  • allowlist exact OpenCode target repositories;
  • forward through wrappers with explicit enable_auto_merge=false, update_branches=false, and merge_mode=disabled controls;
  • prohibit approval, merge, release, branch update, and branch-protection mutation;
  • keep the router schedule-only for organization sweep execution; no branch-selected workflow_dispatch entrypoint remains;
  • use fixed ubuntu-24.04 runners and immutable actions/checkout v7.0.1 pins;
  • scope contents: write only to jobs and wrapper workflows that call GitHub's create-repository-dispatch endpoint, while workflow defaults remain read-only and the existing review-agent credential chain remains unchanged;
  • enforce permanent Python 3.14 quality gates with 100% production statement, branch, and public-docstring coverage; and
  • record operations, rollback, claim boundaries, and APA 7 primary-source references.

Reliability and security repairs

Review-driven regressions now prove the complete distributed idempotency boundary:

  • downstream Noema and OpenCode wrappers consume the router's exact wrapper event types and enforce the deterministic invocation key;
  • a partially completed source request does not redispatch completed agent work;
  • acknowledgement or reaction failure cannot become dispatch authority;
  • sibling sweeps do not trust target-repository comments as receipts;
  • already-ledgered historical requests do not consume the new-work dispatch cap;
  • Bash base-branch validation uses supported positive character checks plus an explicit leading-hyphen rejection;
  • local and sibling dispatch paths preserve exact-current-head and review-only controls;
  • a manually selected branch cannot invoke the organization sweep; and
  • missing cross-repository credentials or unavailable token exchange ultimately fail the sweep before comment inventory or dispatch is processed.

Exact-head verification

Current exact head: 8aa7101fdb6a9993e1699639362d6612f6cc42c5.
Current protected base: f070c504c1cb06891b800d7ab0cf6ac7d3cf8eae.

Successful on this exact head:

  • Agent Mention Router Quality CI 31063991508;
  • CodeQL PR 31063991507;
  • Python Security 31063991539;
  • SAST Semgrep 31063991515;
  • Security Scan 31063991514;
  • Secret Scan 31063991505;
  • OSV-Scanner PR 31063991708;
  • Scorecard PR 31063991525;
  • SBOM Generation 31063991502;
  • CodeRabbit combined status; and
  • zero unresolved inline review threads.

The predecessor-head OpenCode request-changes review for ff02c877db5fbe2863f46b9639362d6612f6cc42c5 does not exist; the dismissed stale review was anchored to ff02c877db5fbe2863f46b963b1d5c27b5acae37 and its failed coverage run. It was dismissed only after the current exact head passed the complete focused and security gates. A new exact-current-head CodeRabbit, OpenCode, Noema, and qualifying non-author verdict remains required.

Integration boundary

The router is a control-plane invocation surface only. Existing Noema and OpenCode workflows retain reviewer identity, credential resolution, model policy, findings, approvals, and merge policy. Wrapper workflows forward each deterministic exact-key invocation at most once to those authoritative planes. Standalone product repositories require no copied privileged implementation.

Merge acceptance

Do not merge until a qualifying independent non-author approval is anchored to exact head 8aa7101fdb6a9993e1699639362d6612f6cc42c5 and branch protection permits merge without administrative bypass. Prior-head reviews, author comments, stale requested-changes verdicts, successful checks without a current verdict, rate-limit notices, target-repository acknowledgements, and reactions are not approval evidence.

Supersedes the unique mention-router slice of closed predecessor #757; no predecessor approval or check is reused.

Summary by CodeRabbit

  • 새 기능

    • PR 댓글의 신뢰된 에이전트 멘션을 자동 감지하고 리뷰 작업으로 전달합니다.
    • 조직 내 최근 멘션을 주기적으로 검색해 누락된 요청을 처리합니다.
    • 중복 요청을 식별해 한 번만 전달하며, 처리 결과를 댓글에 안내합니다.
    • Noema 및 제한된 OpenCode 리뷰 요청을 지원하고 자동 병합은 방지합니다.
  • 품질 개선

    • 입력 검증, 권한 경계, 인증 실패, 재시도 및 중복 방지 동작을 강화했습니다.
    • 관련 자동화에 대한 포괄적인 검증과 품질 게이트를 추가했습니다.
  • 문서

    • 리뷰 에이전트 호출 방법, 운영 한도, 활성화 및 롤백 절차를 문서화했습니다.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6227a6e7-9d93-4b93-ae91-0ff9a2b80cf7

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf29e5 and cb1733a.

📒 Files selected for processing (2)
  • .github/workflows/cleanup-accidental-placeholder-issues-v2.yml
  • .github/workflows/finalize-pr787-payload-binding-v3.yml
📝 Walkthrough

Walkthrough

PR 댓글과 5분 주기 스윕에서 신뢰된 에이전트 멘션을 검증합니다. 결정론적 invocation key와 durable leader를 사용해 중복 dispatch를 억제합니다. Noema와 OpenCode downstream workflow를 연결하고 품질 검증과 운영 문서를 추가합니다.

Changes

에이전트 멘션 라우팅

Layer / File(s) Summary
라우터 검증 및 dispatch
.github/workflows/agent-mention-router.yml, scripts/ci/agent_mention_router.py, tests/test_agent_mention_router.py, tests/test_agent_mention_receipt_authority.py
신뢰된 댓글과 PR 메타데이터를 검증합니다. 허용된 멘션을 분류하고 invocation key 기반으로 중앙 dispatch를 수행합니다.
조직 댓글 스윕 및 실행 연결
.github/workflows/agent-mention-router.yml, scripts/ci/agent_mention_sweep.py, tests/test_agent_mention_sweep.py
조직 또는 설치 저장소의 최근 열린 PR과 댓글을 조회합니다. 유효한 요청을 dispatch 한도 내에서 처리합니다.
Downstream 리더 선출 및 전달
.github/workflows/agent-mention-noema-dispatch.yml, .github/workflows/agent-mention-opencode-dispatch.yml, tests/test_agent_mention_downstream_idempotency.py, tests/test_agent_mention_idempotency.py
canonical payload digest와 invocation key를 검증합니다. 동일 요청의 가장 오래된 실행만 Noema 또는 merge-scheduler로 전달합니다.
품질 게이트 및 운영 계약
.github/workflows/agent-mention-router-quality-ci.yml, tests/test_agent_mention_workflow_contract.py, CHANGELOG.md, docs/automation/review-agent-comment-invocation.md, tests/test_pr_review_fix_scheduler_coverage.py
고정 실행 환경, 100% 브랜치 커버리지, 문서화 검사, 컴파일 검사와 workflow 계약을 추가합니다. 운영 절차와 rollback 범위를 문서화합니다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PRComment
  participant AgentMentionRouter
  participant DownstreamWorkflow
  participant ReviewWorkflow
  PRComment->>AgentMentionRouter: trusted agent mention
  AgentMentionRouter->>AgentMentionRouter: validate payload and invocation key
  AgentMentionRouter->>DownstreamWorkflow: repository dispatch
  DownstreamWorkflow->>DownstreamWorkflow: elect oldest workflow run
  DownstreamWorkflow->>ReviewWorkflow: authoritative review dispatch
Loading

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 신뢰된 리뷰 에이전트 멘션을 라우팅하는 PR의 주요 변경사항을 간결하고 정확하게 요약합니다.
Docstring Coverage ✅ Passed Docstring coverage is 93.55% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/review-agent-mention-router-main

Comment @coderabbitai help to get the list of available commands.

@seonghobae
seonghobae marked this pull request as ready for review August 5, 2026 13:16

Copy link
Copy Markdown
Contributor Author

@opencode-agent @cwl-noema-review @coderabbitai review

Review exact head cf4400f7f8fe673f1f89c467a6629bd92dcaae2c against protected main f070c504c1cb06891b800d7ab0cf6ac7d3cf8eae. Exact-head Agent Mention Router Quality CI and all direct Python Security, Security Scan, CodeQL, Semgrep, Secret Scan, OSV, Scorecard, and SBOM workflows are successful. Verify trusted association handling, exact-head/base dispatch binding, idempotent receipts, bounded organization sweep, OpenCode target allowlisting, explicit review-only merge controls, immutable action pins, credential scope, permanent 100% statement/branch/docstring evidence, absence of approval/merge/release/protection mutation, and no temporary write workflow. Submit a formal current-head approval only if independent branch-protection requirements are satisfied; otherwise provide source-backed actionable findings. Do not reuse predecessor #757 evidence.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

@seonghobae: I will review pull request #787 at the specified current head. I will use only current-head evidence. I will check the requested control-plane and safety boundaries before I make an approval decision.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae
seonghobae enabled auto-merge (squash) August 5, 2026 13:26
@seonghobae
seonghobae marked this pull request as draft August 5, 2026 13:35
auto-merge was automatically disabled August 5, 2026 13:35

Pull request was converted to draft

@seonghobae
seonghobae marked this pull request as ready for review August 5, 2026 13:35
@seonghobae
seonghobae enabled auto-merge (squash) August 5, 2026 13:36

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Exact-head reliability finding (valid, blocking the claimed receipt-based idempotency): dispatch_request() writes its only durable receipt after dispatching Noema, dispatching OpenCode, adding a reaction, and then posting the acknowledgement. If any later call fails after an earlier repository dispatch succeeds—for example, Noema succeeds and OpenCode or the reaction/comment call fails—the source comment remains unreceipted. The five-minute sweep retries it and dispatches the already-successful agent again. The current Noema repository-dispatch concurrency key is PR-scoped with cancel-in-progress: true, so this failure window can cancel/restart a legitimate review rather than merely producing a harmless duplicate.

Please fix test-first with fault injection at every external-call boundary. The durable protocol must track completion per requested agent and allow retrying only missing work. Because there is still an unavoidable crash window between a successful GitHub repository-dispatch response and recording that success, the downstream Noema/OpenCode entrypoints should also enforce idempotency using a deterministic key containing at least target_repository, PR number, exact head SHA, requested agent, and source_comment_id. Receipt/reaction failure must not cause completed agent work to be redispatched, and a partially completed request must not be treated as fully processed. Update the operator record and claim boundary accordingly; do not weaken the current exact-head or review-only controls.

@opencode-agent
opencode-agent Bot disabled auto-merge August 5, 2026 13:52

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Exact-head blocking defect in the sibling-repository sweep: processed_comment_ids() accepts receipt markers only when the comment author is exactly github-actions[bot], but the scheduled sweep posts target-repository acknowledgements through TARGET_REPOSITORY_TOKEN (PR_REVIEW_MERGE_TOKEN, OPENCODE_APPROVE_TOKEN, or an OpenCode installation token). Those comments are authored by the PAT user or GitHub App bot—not by github-actions[bot]. Consequently, the next five-minute sweep does not recognize its own sibling-repository receipt and redispatches the same mention repeatedly for the entire lookback window (subject only to each run's dispatch cap). This also amplifies the partial-failure idempotency defect from my preceding review.

Please add a failing end-to-end sweep regression using realistic PAT and installation-bot acknowledgement identities, then bind receipt recognition to the authenticated target-token identity (or another durable, non-spoofable central receipt protocol) rather than a hard-coded local-workflow actor. Do not broadly trust arbitrary bot/user markers: a repository workflow or collaborator must not be able to forge a receipt and suppress a required review. Verify local github-actions[bot], cross-repository PAT, and installation-token paths, repeated sweep runs, mixed legitimate/forged markers, token rotation, and partial agent completion. The current head should not merge while the advertised organization sweep can create an unbounded redispatch loop.

@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 cf4400f7f8fe673f1f89c467a6629bd92dcaae2c.

  • Head SHA: cf4400f7f8fe673f1f89c467a6629bd92dcaae2c

  • Workflow run: 31012190358

  • 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 (2 files)"]
  S1 --> I1["GitHub Actions review job"]
  I1 --> R1["Review risk: Workflow (2 files)"]
  R1 --> V1["actionlint plus required checks"]
  Evidence --> S2["Changed file: CHANGELOG.md"]
  S2 --> I2["repository behavior"]
  I2 --> R2["Review risk: Changed file: CHANGELOG.md"]
  R2 --> V2["required checks"]
  Evidence --> S3["Docs: review-agent-comment-invocation.md"]
  S3 --> I3["operator or user guidance"]
  I3 --> R3["Review risk: Docs: review-agent-comment-invocation.md"]
  R3 --> V3["docs review"]
  Evidence --> S4["CI script (2 files)"]
  S4 --> I4["review and security gate shell path"]
  I4 --> R4["Review risk: CI script (2 files)"]
  R4 --> V4["bash -n plus Strix self-test"]
  Evidence --> S5["Test (3 files)"]
  S5 --> I5["regression suite"]
  I5 --> R5["Review risk: Test (3 files)"]
  R5 --> V5["targeted test run"]
Loading

@seonghobae
seonghobae dismissed opencode-agent[bot]’s stale review August 6, 2026 06:31

Stale-head OpenCode coverage blocker resolved on exact head 3bef8ad. Agent Mention Router Quality CI run 31077236939 passed 45 tests with 100% statement coverage, 100% branch coverage, and 100% public-docstring coverage. The current head also removes all transient PR-specific branch writers and binds both wrappers to the canonical payload digest. A fresh exact-head independent verdict remains required; this dismissal does not count as approval.

@seonghobae
seonghobae marked this pull request as draft August 6, 2026 06:35
@seonghobae
seonghobae marked this pull request as ready for review August 6, 2026 06:35
@seonghobae
seonghobae enabled auto-merge (squash) August 6, 2026 06:36

Copy link
Copy Markdown
Contributor Author

@opencode-agent @cwl-noema-review @coderabbitai review

Please review exact head 92c83f44c75cae42e90dc6984dc89c696b6c3db9. The Noema payload now binds base_branch, the focused quality gate and all direct exact-head security/supply-chain workflows except the cancelled Python Security rerun have succeeded, and every inline thread is resolved or outdated. Re-evaluate this exact head only; merge remains subject to a fresh successful Python Security run, repository protection, and a qualifying independent approval.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/.github"
base="f070c504c1cb06891b800d7ab0cf6ac7d3cf8eae"
head="92c83f44c75cae42e90dc6984dc89c696b6c3db9"

printf '%s\n' '== Revision verification =='
printf 'checkout HEAD: '; git rev-parse HEAD
printf 'requested HEAD: '; git rev-parse "${head}^{commit}"
printf 'base: '; git rev-parse "${base}^{commit}"
printf 'merge base: '; git merge-base HEAD "$base"
printf '%s\n' '== Changed files =='
git diff --name-status "${base}..${head}"

printf '%s\n' '== Relevant files =='
fd -t f . .github/workflows scripts/ci tests docs 2>/dev/null |
  rg '(agent[-_]mention|review-agent-comment-invocation|pr_review_fix_scheduler)' || true

printf '%s\n' '== Router key and payload construction =='
ast-grep outline scripts/ci/agent_mention_router.py --items all 2>/dev/null || true
rg -n -C 15 \
  'def agent_invocation_key|def noema_payload|def opencode_payload|base_branch|invocation_key' \
  scripts/ci/agent_mention_router.py

printf '%s\n' '== Wrapper validation, forwarding, and controls =='
for file in \
  .github/workflows/agent-mention-noema-dispatch.yml \
  .github/workflows/agent-mention-opencode-dispatch.yml \
  .github/workflows/agent-mention-router.yml \
  .github/workflows/agent-mention-router-quality-ci.yml
do
  if [ -f "$file" ]; then
    printf '\n-- %s --\n' "$file"
    cat -n "$file"
  fi
done

printf '%s\n' '== Invocation regression tests =='
fd -t f . tests 2>/dev/null |
  rg 'agent_mention' |
  while IFS= read -r file; do
    printf '\n-- %s --\n' "$file"
    rg -n -C 8 \
      '(invocation.key|base.branch|sha256|digest|wrapper|payload|ledger|partial|budget|acknowledg)' \
      "$file" || true
  done

printf '%s\n' '== Exact-head check and review state =='
gh pr view 787 --repo "$repo" \
  --json headRefOid,baseRefOid,state,isDraft,mergeStateStatus,reviewDecision,reviews,statusCheckRollup \
  --jq '{headRefOid,baseRefOid,state,isDraft,mergeStateStatus,reviewDecision,reviews:[.reviews[]|{author:.author.login,state,commitId:.commit.oid,submittedAt}],checks:[.statusCheckRollup[]|{name,status,conclusion,headSha}]}'

printf '%s\n' '== Failed, cancelled, or incomplete exact-head checks =='
gh api "repos/${repo}/commits/${head}/check-runs?per_page=100" \
  --jq '.check_runs[] | select(.conclusion != "success" and .conclusion != "skipped" and .conclusion != "neutral") | {id,name,status,conclusion,details_url,output}'

printf '%s\n' '== Branch protection =='
gh api "repos/${repo}/branches/main/protection" \
  --jq '{required_pull_request_reviews,required_status_checks,enforce_admins,required_conversation_resolution}' || true

printf '%s\n' '== Unresolved current review threads =='
gh api graphql -F owner="ContextualWisdomLab" -F name=".github" -F number=787 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          isOutdated
          path
          line
          comments(first: 10) { nodes { author { login } body } }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and .isOutdated == false) | {path,line,comments:[.comments.nodes[]|{author:.author.login,body}]}'

Length of output: 78480


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Router dispatch and acknowledgement boundaries =='
sed -n '245,455p' scripts/ci/agent_mention_router.py | cat -n

printf '%s\n' '== Sweep inventory, dispatch budget, and credential source handling =='
ast-grep outline scripts/ci/agent_mention_sweep.py --items all 2>/dev/null || true
sed -n '1,460p' scripts/ci/agent_mention_sweep.py | cat -n

printf '%s\n' '== Repository-dispatch consumers and review-only controls =='
rg -n -i -C 8 \
  '(repository_dispatch:|types:\s*\[?(noema-review|merge-scheduler)|event_type:\s*"(noema-review|merge-scheduler)"|agent_invocation_key|enable_auto_merge|update_branches|merge_mode|pulls/.*/merge|releases|branch protection)' \
  .github/workflows scripts/ci

printf '%s\n' '== Full workflow-contract test =='
cat -n tests/test_agent_mention_workflow_contract.py

printf '%s\n' '== Targeted key, Noema base-branch, and sweep tests =='
rg -n -C 10 \
  '(base_branch|invocation_key|canonical|digest|partial|budget|credential|fail.closed|repository_source|workflow.run)' \
  tests/test_agent_mention_idempotency.py \
  tests/test_agent_mention_sweep.py \
  tests/test_agent_mention_downstream_idempotency.py \
  tests/test_agent_mention_receipt_authority.py

printf '%s\n' '== Mutation-capable GitHub API calls in changed automation =='
rg -n -i -C 4 \
  '(gh api .* -X (POST|PUT|PATCH|DELETE)|/dispatches|/comments|/reactions|/merge|/releases|branches/.*/protection)' \
  scripts/ci .github/workflows/agent-mention-*.yml

printf '%s\n' '== Exact-head check workflow runs and annotations for cancelled runs =='
for run_id in 31077409462 31077407485 31077816786; do
  printf '\n-- workflow run %s --\n' "$run_id"
  gh run view "$run_id" --repo ContextualWisdomLab/.github \
    --json databaseId,headSha,status,conclusion,event,workflowName,jobs \
    --jq '{databaseId,headSha,status,conclusion,event,workflowName,jobs:[.jobs[]|{name,status,conclusion,startedAt,completedAt}]}'
done

Length of output: 246652


@seonghobae I reviewed exact head 92c83f44c75cae42e90dc6984dc89c696b6c3db9 against base f070c504c1cb06891b800d7ab0cf6ac7d3cf8eae.

The prior wrapper finding is resolved.

  • Both wrappers rebuild the canonical seven-field JSON and reject an invocation-key digest mismatch.
  • The Noema payload now includes and forwards base_branch.
  • The router uses central wrapper workflow runs as the durable per-agent ledger.
  • The sweep increments its budget only when it queues new agent work.
  • The OpenCode path forwards enable_auto_merge: false, update_branches: false, and merge_mode: "disabled".
  • The inspected router and wrapper paths contain only repository-dispatch, reaction, and acknowledgement-comment writes. I found no approval, merge, release, branch-update, or branch-protection mutation in those paths.
  • The focused quality check succeeded.

I found no new source-level blocker in the reviewed mention-router boundary.

I cannot approve this exact head yet.

  • The replacement Python Security pip-audit job is still in progress.
  • CodeQL, Semgrep, and Strix jobs are also still in progress.
  • GitHub reports REVIEW_REQUIRED and BLOCKED.
  • The returned reviews contain no independent approval anchored to 92c83f44c75cae42e90dc6984dc89c696b6c3db9.
  • The branch-protection API returned HTTP 403. I could not independently verify the live protection configuration.

Wait for all exact-head required checks to complete successfully. Then obtain a qualifying independent current-head approval and verify branch protection with a credential that can read it.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Exact-head review request for 3bef8adf0f00b668a431d5113fc3081a1780a652. The current head binds Noema and OpenCode wrapper payloads to the router's canonical SHA-256 invocation key, compares the recomputed digest in constant time before durable-leader election, forwards base_branch consistently, and removes every transient PR-specific branch writer. Agent Mention Router Quality CI passed 45 tests with 100% statement/branch/public-docstring coverage; CodeQL, Semgrep, security, secret, OSV, Scorecard, and SBOM gates are successful. Python Security has been retriggered because GitHub marked a successful pip-audit attempt cancelled at the run aggregate. Review only this head against protected main; no predecessor evidence transfers.

@coderabbitai review
@cwl-noema-review
@opencode-agent

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

@seonghobae I will review pull request #787 at exact head 3bef8adf0f00b668a431d5113fc3081a1780a652.

I will use current-head evidence only. I will verify the wrapper key binding, base_branch forwarding, durable idempotency boundary, sweep controls, write permissions, review-only controls, coverage evidence, and current required-check state.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 92c83f44c75cae42e90dc6984dc89c696b6c3db9.

  • Head SHA: 92c83f44c75cae42e90dc6984dc89c696b6c3db9

  • Workflow run: 31078896274

  • 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 (4 files)"]
  S1 --> I1["GitHub Actions review job"]
  I1 --> R1["Review risk: Workflow (4 files)"]
  R1 --> V1["actionlint plus required checks"]
  Evidence --> S2["Changed file: CHANGELOG.md"]
  S2 --> I2["repository behavior"]
  I2 --> R2["Review risk: Changed file: CHANGELOG.md"]
  R2 --> V2["required checks"]
  Evidence --> S3["Docs: review-agent-comment-invocation.md"]
  S3 --> I3["operator or user guidance"]
  I3 --> R3["Review risk: Docs: review-agent-comment-invocation.md"]
  R3 --> V3["docs review"]
  Evidence --> S4["CI script (2 files)"]
  S4 --> I4["review and security gate shell path"]
  I4 --> R4["Review risk: CI script (2 files)"]
  R4 --> V4["bash -n plus Strix self-test"]
  Evidence --> S5["Test (7 files)"]
  S5 --> I5["regression suite"]
  I5 --> R5["Review risk: Test (7 files)"]
  R5 --> V5["targeted test run"]
Loading

@opencode-agent
opencode-agent Bot disabled auto-merge August 6, 2026 06:57

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Exact current head 92c83f44c75cae42e90dc6984dc89c696b6c3db9 is reduced to the durable 15-file router/runtime/test/documentation scope; every PR-specific repair workflow, trigger, and patch helper has been removed. Agent Mention Router Quality CI, Python Security, CodeQL, Security Scan, Semgrep, Secret Scan, OSV, Scorecard, SBOM, and CodeRabbit status all succeeded; all inline threads are resolved. Requesting fresh exact-head independent review without branch mutation, author approval, or merge bypass.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@opencode-agent

Review-only exact-head request for 92c83f44c75cae42e90dc6984dc89c696b6c3db9; do not update branches, approve from author identity, or merge. Validate exact-key distributed idempotency, per-agent partial completion, durable wrapper-run authority, bounded dispatch accounting, review-only mutation controls, 100% production statement/branch coverage, and public docstrings.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cwl-noema-review

Independently review exact head 92c83f44c75cae42e90dc6984dc89c696b6c3db9, focusing on invocation authority, partial-failure idempotency, sibling-sweep starvation resistance, token boundaries, and absence of branch mutation or merge authority.

@seonghobae
seonghobae enabled auto-merge (squash) August 6, 2026 07:00
@opencode-agent
opencode-agent Bot disabled auto-merge August 6, 2026 07:00

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 9

🧹 Nitpick comments (10)
tests/test_agent_mention_downstream_idempotency.py (2)

110-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

PR 번호에 고정된 임시 경로 테스트는 병합 전에 제거하십시오.

이 테스트는 PR #787 전용 파일 이름 12개의 부재만 확인합니다. 병합 이후에는 회귀를 방지하지 못하고, 영구 스위트에 PR 번호를 남깁니다. 임시 자동화가 제거되었는지 확인하는 목적은 이미 이 PR의 diff가 충족합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_agent_mention_downstream_idempotency.py` around lines 110 - 127,
Remove test_branch_contains_no_transient_pr787_repair_automation and its PR
`#787-specific` transient_paths assertions; the existing diff already verifies
removal of the temporary automation, so no permanent test should retain
hard-coded PR-specific paths.

66-86: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

이 계약 테스트는 문자열 존재만 확인합니다.

테스트 이름은 wrapper가 라우터의 정규 digest를 재계산한다고 선언합니다. 그러나 단언은 필드 이름 리터럴과 함수 이름의 포함 여부만 검사합니다. 다음 변경은 이 테스트를 통과하면서 모든 invocation을 깨뜨립니다.

  • wrapper가 "head_sha"PR_HEAD_SHA 대신 다른 환경 변수를 매핑하는 경우
  • 라우터가 정규 구조에 새 필드를 추가하고 wrapper는 추가하지 않는 경우
  • ensure_ascii 값이 한쪽에서만 바뀌는 경우

wrapper의 Python heredoc을 추출해 알려진 환경 변수로 실행하고, 결과를 라우터의 agent_invocation_key 출력과 비교하는 실행형 테스트를 추가하십시오. .github/workflows/agent-mention-opencode-dispatch.yml 라인 65-88에 남긴 중복 제거 제안과 함께 적용하면 이 테스트는 단일 구현을 검증하게 됩니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_agent_mention_downstream_idempotency.py` around lines 66 - 86,
Replace the substring-only assertions in the test covering the noema and
opencode wrappers with an executable contract test: extract and run each
wrapper’s Python heredoc using known environment values, then compare its
generated invocation digest with the router’s agent_invocation_key output.
Ensure the test detects incorrect environment-variable mappings, canonical-field
drift, and serialization-option mismatches, and reuse the shared implementation
introduced for the duplicated workflow logic.
tests/test_agent_mention_idempotency.py (1)

121-162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

base_branchactor 변형을 changed_values에 추가하십시오.

정규 구조는 7개 필드를 포함합니다. 현재 변형은 repository, pr_number, head_sha, comment_id 4개만 다룹니다. base_branchactor를 정규 구조에서 제거해도 이 테스트는 통과합니다. 그러나 wrapper의 재계산 결과와 불일치가 발생하여 모든 invocation이 거부됩니다.

💚 제안 추가
         module.MentionRequest(
             original.repository,
             original.pull_request_number,
             original.pull_request_head_sha,
+            "release/next",
+            original.comment_id,
+            original.actor,
+            original.agents,
+        ),
+        module.MentionRequest(
+            original.repository,
+            original.pull_request_number,
+            original.pull_request_head_sha,
+            original.pull_request_base_branch,
+            original.comment_id,
+            "other-maintainer",
+            original.agents,
+        ),
+        module.MentionRequest(
+            original.repository,
+            original.pull_request_number,
+            original.pull_request_head_sha,
             original.pull_request_base_branch,
             original.comment_id + 1,
             original.actor,
             original.agents,
         ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_agent_mention_idempotency.py` around lines 121 - 162, Extend the
changed_values cases in the agent_invocation_key idempotency test to include
variants with a different pull_request_base_branch and a different actor. Keep
all other MentionRequest fields identical to original and assert both variants
produce keys different from noema_key.
.github/workflows/agent-mention-opencode-dispatch.yml (1)

65-88: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

정규 digest 계산 로직이 세 곳에 중복됩니다.

동일한 canonical JSON 구조가 scripts/ci/agent_mention_router.py 라인 228-242, 이 파일 라인 71-84, .github/workflows/agent-mention-noema-dispatch.yml 라인 61-74에 존재합니다. 한 곳에서 필드 이름이나 직렬화 옵션이 바뀌면 모든 invocation이 조용히 거부됩니다.

tests/test_agent_mention_downstream_idempotency.py 라인 75-86은 문자열 포함 여부만 확인합니다. 따라서 필드 값의 드리프트는 검출되지 않습니다.

라우터의 agent_invocation_key를 재사용하는 단일 스크립트를 scripts/ci/에 추가하고, 두 wrapper가 checkout 후 그 스크립트를 호출하도록 변경하십시오. 그러면 계약이 하나의 구현으로 수렴하고, 기존 파이썬 테스트가 그 구현을 직접 검증합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/agent-mention-opencode-dispatch.yml around lines 65 - 88,
중복된 canonical digest 계산을 제거하고 `scripts/ci/agent_mention_router.py`의
`agent_invocation_key`를 재사용하는 단일 스크립트를 `scripts/ci/`에 추가하십시오.
`.github/workflows/agent-mention-opencode-dispatch.yml`과
`agent-mention-noema-dispatch.yml`의 inline Python을 제거하고 checkout 이후 해당 스크립트를
호출하도록 변경하며, 기존 필드와 JSON 직렬화 옵션을 그대로 유지해 기존 Python 테스트가 공통 구현을 직접 검증하게 하십시오.
tests/test_agent_mention_sweep.py (1)

301-338: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

실제 dispatch_request를 사용하는 스윕 테스트를 추가하십시오.

모든 스윕 오케스트레이션 테스트는 dispatch_request를 대체합니다. 따라서 스윕과 라우터의 결합 동작은 검증되지 않습니다. 특히 허용 목록 밖 OpenCode 요청이 스윕 반복마다 확인 코멘트를 다시 게시하는 동작이 이 스위트에서 드러나지 않습니다. 해당 결함은 scripts/ci/agent_mention_router.py 라인 386-435에 기록했습니다.

가짜 GitHubClient 두 개를 사용해 sweep를 두 번 연속 실행하고, 두 번째 실행에서 대상 저장소 mutation 호출이 발생하지 않는지 단언하는 테스트를 추가하십시오.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_agent_mention_sweep.py` around lines 301 - 338, Extend the sweep
orchestration tests to use the real dispatch_request instead of replacing it,
with two fake GitHubClient instances and consecutive sweep calls. Configure an
OpenCode request outside the allowlist, then assert the first run performs the
expected confirmation mutation while the second run performs no
target-repository mutation, covering the sweep/router integration and preventing
repeated confirmation comments.
.github/workflows/agent-mention-router.yml (2)

144-148: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

앱 토큰을 스텝 출력 대신 $GITHUB_ENV로 전달하십시오.

GITHUB_OUTPUT에 기록한 값은 워크플로 실행 컨텍스트에 저장되고 같은 잡의 모든 후속 스텝에서 참조할 수 있습니다. 이 토큰은 같은 잡의 단일 스텝(라인 158)만 사용합니다. 노출 범위를 줄이려면 토큰 값은 $GITHUB_ENV로 전달하고, 출력에는 가용성 플래그만 남기십시오.

🔒 제안 변경
           echo "::add-mask::$app_token"
-          {
-            echo "available=true"
-            echo "token=$app_token"
-          } >>"$GITHUB_OUTPUT"
+          echo "available=true" >>"$GITHUB_OUTPUT"
+          echo "SWEEP_APP_TOKEN=$app_token" >>"$GITHUB_ENV"

라인 158도 함께 변경해야 합니다.

-          TARGET_REPOSITORY_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.sweep_app_token.outputs.token }}
+          TARGET_REPOSITORY_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || env.SWEEP_APP_TOKEN }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/agent-mention-router.yml around lines 144 - 148, Update
the token handoff in the app-token step to write only the availability flag to
GITHUB_OUTPUT and pass the masked app_token through GITHUB_ENV instead. Update
the single consuming step at the token-use location around line 158 to read the
token from the environment rather than step outputs, while preserving the
existing availability check.

113-137: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

외부 HTTP 호출에 타임아웃을 추가하십시오.

curl 호출에는 타임아웃 옵션이 없습니다. OIDC 엔드포인트 또는 OpenCode API가 응답하지 않으면 스텝이 잡의 15분 예산을 모두 소비합니다. 5분 주기 스케줄에서는 후속 실행이 지연됩니다. --max-time을 지정하십시오.

♻️ 제안 변경
-            curl -fsS \
+            curl -fsS --max-time 20 \
               -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
               "${request_url}${separator}audience=${OIDC_AUDIENCE}"
-            curl -fsS \
+            curl -fsS --max-time 30 \
               -X POST \
               -H "Authorization: Bearer ${oidc_token}" \
               "${OPENCODE_API_BASE_URL}/exchange_github_app_token"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/agent-mention-router.yml around lines 113 - 137, 두 외부 HTTP
호출인 OIDC 토큰 요청과 exchange_github_app_token 요청의 curl 옵션에 --max-time 제한을 추가하십시오. 두
요청이 무기한 대기하지 않도록 동일한 적절한 타임아웃 값을 사용하고, 기존 오류 처리 및 mark_unavailable 흐름은 유지하십시오.
scripts/ci/agent_mention_router.py (2)

186-196: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

저장소 허용 목록 비교를 대소문자 무시로 정규화하는 것을 검토하십시오.

GitHub 저장소 전체 이름은 대소문자를 구분하지 않습니다. 현재 eligible_agents(라인 211)는 정확한 문자열 일치를 사용합니다. OPENCODE_REPOSITORY_DISPATCH_TARGETS 항목의 대소문자가 실제 저장소와 다르면 OpenCode 요청이 조용히 거부되고 사용자에게는 "Rejected" 메시지만 표시됩니다. 허용 목록과 비교 값을 모두 casefold()로 정규화하면 이 설정 오류가 사라집니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/agent_mention_router.py` around lines 186 - 196, Normalize
repository names with casefold() when building the allowlist and when comparing
entries in eligible_agents. Keep validation behavior unchanged, but ensure
differently cased GitHub repository names match instead of being rejected.

80-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

gh api 실패 시 stderr를 노출하십시오.

capture_output=Truecheck=True를 함께 사용하면 CalledProcessError 메시지에 반환 코드만 남습니다. gh 가 출력한 오류 본문은 로그에 나타나지 않습니다. 이 라우터는 무인 자동화로 동작하므로 진단 정보가 필요합니다.

♻️ 제안 변경
         completed = subprocess.run(
             command,
             input=None if input_payload is None else json.dumps(input_payload),
             text=True,
             capture_output=True,
-            check=True,
+            check=False,
             env=environment,
         )
+        if completed.returncode != 0:
+            raise RuntimeError(
+                f"gh api failed with exit code {completed.returncode}: "
+                f"{completed.stderr.strip()}"
+            )
         output = completed.stdout.strip()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/agent_mention_router.py` around lines 80 - 89, Update the
subprocess execution flow around subprocess.run so gh API failures expose the
captured stderr content in the router’s logs or propagated error. Catch
subprocess.CalledProcessError, include its stderr in the diagnostic message, and
preserve failure propagation; keep the existing successful stdout parsing
unchanged.
tests/test_agent_mention_router.py (1)

148-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

downstream 문자 집합을 위반하는 actor 케이스를 추가하십시오.

현재 파라미터는 빈 actor만 검증합니다. wrapper 워크플로는 REQUESTED_BY^[A-Za-z0-9-]+$를 요구합니다. 이 계약을 테스트로 고정하면 라우터와 wrapper의 검증 불일치가 회귀하지 않습니다. scripts/ci/agent_mention_router.py 라인 168-171에 남긴 지적과 함께 적용하십시오.

💚 제안 추가
         (("comment", "user", "login"), "", "actor"),
+        (("comment", "user", "login"), "bad login", "actor"),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_agent_mention_router.py` around lines 148 - 172, Extend
test_parse_event_rejects_malformed_trusted_requests with an actor value
containing characters outside the downstream REQUESTED_BY contract, such as an
underscore or other non-alphanumeric/non-hyphen character, while retaining the
existing empty-actor case. Ensure the added case expects parse_event to raise
ValueError matching “actor”.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/agent-mention-noema-dispatch.yml:
- Around line 80-99: Update the leader-election blocks in
.github/workflows/agent-mention-noema-dispatch.yml#L80-L99 and
.github/workflows/agent-mention-opencode-dispatch.yml#L90-L104 to retry the
workflow-run listing with a short backoff when GITHUB_RUN_ID is absent, then
treat the current run as leader if it remains absent after retries instead of
exiting with failure. Keep duplicate suppression when a lower durable run ID is
found; consider extracting the identical logic into a shared composite action.

In @.github/workflows/agent-mention-router-quality-ci.yml:
- Around line 6-14: Update the workflow trigger paths in the quality CI
configuration to include docs/automation/review-agent-comment-invocation.md.
Replace the explicitly listed pytest files with the full-suite command python -m
coverage run -m pytest -q, while preserving the existing include configuration
limiting coverage measurement to the two CI scripts.
- Around line 46-50: Update
.github/workflows/agent-mention-router-quality-ci.yml lines 46-50 to fetch the
commits needed for PR base/head and push before/head comparisons, then update
the git diff --check invocation at line 96 to use the event-specific comparison
range. Remove the final diff whitespace-check claim from
docs/automation/review-agent-comment-invocation.md line 58 until the actual
changed-range check is implemented.

In `@scripts/ci/agent_mention_router.py`:
- Around line 291-315: Limit the workflow-run request in
scripts/ci/agent_mention_router.py:291-315 by adding the lookback-window created
filter, and query the agent marker set only once per sweep run for reuse across
agents. In scripts/ci/agent_mention_sweep.py:137-156, preserve the
updated-descending ordering, stop repository traversal when an item is older
than the cutoff, and fetch results lazily one page at a time instead of using
full pagination.
- Around line 386-435: Update scripts/ci/agent_mention_router.py:386-435 in
dispatch_request to make rejected-only requests idempotent, using
conversation_comments and processed_comment_ids suppression or another durable
rejection signal so reactions and acknowledgement comments are not repeated
across sweeps and rejected requests do not consume dispatch budget. Update
tests/test_agent_mention_router.py:264-271 to reflect the new rejected-request
behavior and assert that processing the same request twice causes
target-repository mutations only once.
- Around line 168-171: Update the actor validation in the router’s
request-validation flow to require the same character set as the downstream
wrappers: one or more ASCII letters, digits, or hyphens. Reject missing or
invalid actor values before dispatching, while preserving the existing
ValueError behavior for invalid input.

In `@scripts/ci/agent_mention_sweep.py`:
- Around line 249-274: Update the sweep flow around list_recent_pull_requests,
build_requests_for_pull_request, and dispatch_request to isolate exceptions per
repository or candidate, log each failure, and continue processing remaining
candidates. Track the number of failed candidates and determine the sweep’s
return/exit status from that failure count while preserving the existing
dispatch-limit behavior.

In `@tests/test_agent_mention_router.py`:
- Around line 264-271: Update the test around module.dispatch_request to expect
no confirmation comment when no agent is dispatched and the request is only
rejected; remove or revise the assertion checking “Rejected `@opencode-agent`” in
target.calls, while preserving the assertions that central.calls remains empty
and dispatch returns ().

In `@tests/test_pr_review_fix_scheduler_coverage.py`:
- Around line 11-20: Rename the import_without_script_directory parameters
globals and locals to module_globals and module_locals to avoid shadowing Python
built-ins, and pass the renamed parameters to real_import in the same positional
order.

---

Nitpick comments:
In @.github/workflows/agent-mention-opencode-dispatch.yml:
- Around line 65-88: 중복된 canonical digest 계산을 제거하고
`scripts/ci/agent_mention_router.py`의 `agent_invocation_key`를 재사용하는 단일 스크립트를
`scripts/ci/`에 추가하십시오. `.github/workflows/agent-mention-opencode-dispatch.yml`과
`agent-mention-noema-dispatch.yml`의 inline Python을 제거하고 checkout 이후 해당 스크립트를
호출하도록 변경하며, 기존 필드와 JSON 직렬화 옵션을 그대로 유지해 기존 Python 테스트가 공통 구현을 직접 검증하게 하십시오.

In @.github/workflows/agent-mention-router.yml:
- Around line 144-148: Update the token handoff in the app-token step to write
only the availability flag to GITHUB_OUTPUT and pass the masked app_token
through GITHUB_ENV instead. Update the single consuming step at the token-use
location around line 158 to read the token from the environment rather than step
outputs, while preserving the existing availability check.
- Around line 113-137: 두 외부 HTTP 호출인 OIDC 토큰 요청과 exchange_github_app_token 요청의
curl 옵션에 --max-time 제한을 추가하십시오. 두 요청이 무기한 대기하지 않도록 동일한 적절한 타임아웃 값을 사용하고, 기존 오류
처리 및 mark_unavailable 흐름은 유지하십시오.

In `@scripts/ci/agent_mention_router.py`:
- Around line 186-196: Normalize repository names with casefold() when building
the allowlist and when comparing entries in eligible_agents. Keep validation
behavior unchanged, but ensure differently cased GitHub repository names match
instead of being rejected.
- Around line 80-89: Update the subprocess execution flow around subprocess.run
so gh API failures expose the captured stderr content in the router’s logs or
propagated error. Catch subprocess.CalledProcessError, include its stderr in the
diagnostic message, and preserve failure propagation; keep the existing
successful stdout parsing unchanged.

In `@tests/test_agent_mention_downstream_idempotency.py`:
- Around line 110-127: Remove
test_branch_contains_no_transient_pr787_repair_automation and its PR
`#787-specific` transient_paths assertions; the existing diff already verifies
removal of the temporary automation, so no permanent test should retain
hard-coded PR-specific paths.
- Around line 66-86: Replace the substring-only assertions in the test covering
the noema and opencode wrappers with an executable contract test: extract and
run each wrapper’s Python heredoc using known environment values, then compare
its generated invocation digest with the router’s agent_invocation_key output.
Ensure the test detects incorrect environment-variable mappings, canonical-field
drift, and serialization-option mismatches, and reuse the shared implementation
introduced for the duplicated workflow logic.

In `@tests/test_agent_mention_idempotency.py`:
- Around line 121-162: Extend the changed_values cases in the
agent_invocation_key idempotency test to include variants with a different
pull_request_base_branch and a different actor. Keep all other MentionRequest
fields identical to original and assert both variants produce keys different
from noema_key.

In `@tests/test_agent_mention_router.py`:
- Around line 148-172: Extend
test_parse_event_rejects_malformed_trusted_requests with an actor value
containing characters outside the downstream REQUESTED_BY contract, such as an
underscore or other non-alphanumeric/non-hyphen character, while retaining the
existing empty-actor case. Ensure the added case expects parse_event to raise
ValueError matching “actor”.

In `@tests/test_agent_mention_sweep.py`:
- Around line 301-338: Extend the sweep orchestration tests to use the real
dispatch_request instead of replacing it, with two fake GitHubClient instances
and consecutive sweep calls. Configure an OpenCode request outside the
allowlist, then assert the first run performs the expected confirmation mutation
while the second run performs no target-repository mutation, covering the
sweep/router integration and preventing repeated confirmation comments.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 97644a4c-fc07-401f-903d-6a61df11b1b3

📥 Commits

Reviewing files that changed from the base of the PR and between f070c50 and 1cf29e5.

📒 Files selected for processing (15)
  • .github/workflows/agent-mention-noema-dispatch.yml
  • .github/workflows/agent-mention-opencode-dispatch.yml
  • .github/workflows/agent-mention-router-quality-ci.yml
  • .github/workflows/agent-mention-router.yml
  • CHANGELOG.md
  • docs/automation/review-agent-comment-invocation.md
  • scripts/ci/agent_mention_router.py
  • scripts/ci/agent_mention_sweep.py
  • tests/test_agent_mention_downstream_idempotency.py
  • tests/test_agent_mention_idempotency.py
  • tests/test_agent_mention_receipt_authority.py
  • tests/test_agent_mention_router.py
  • tests/test_agent_mention_sweep.py
  • tests/test_agent_mention_workflow_contract.py
  • tests/test_pr_review_fix_scheduler_coverage.py

Comment on lines +80 to +99
marker="[cwl-agent-invocation:${INVOCATION_KEY}]"
leader_id="$(
gh api --paginate --slurp \
"repos/${GITHUB_REPOSITORY}/actions/workflows/agent-mention-noema-dispatch.yml/runs?event=repository_dispatch&per_page=100" \
| jq -r --arg marker "$marker" '
[.[].workflow_runs[]
| select((.display_title // "") | contains($marker))
| .id]
| min // empty
'
)"
if [ -z "$leader_id" ]; then
echo "::error::Could not establish the durable Noema invocation leader."
exit 1
fi
if [ "$leader_id" != "$GITHUB_RUN_ID" ]; then
echo "forward=false" >>"$GITHUB_OUTPUT"
echo "Duplicate exact-key invocation suppressed by durable workflow-run identity."
exit 0
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

두 wrapper의 리더 선출이 실행 목록 API의 지연에 취약합니다. 동일한 블록이 두 파일에 복사되어 있습니다. 공통 근본 원인은 조회 결과에 자신의 GITHUB_RUN_ID가 없을 때 exit 1로 실패하는 처리입니다. Actions 실행 목록 API는 즉시 일관적이지 않으므로, 방금 생성된 실행이 목록에 나타나지 않을 수 있습니다. 그리고 scripts/ci/agent_mention_router.pydispatched_agents는 실패한 실행도 dispatch 완료로 간주하므로, 재시도가 억제되고 리뷰 요청이 영구히 사라집니다.

  • .github/workflows/agent-mention-noema-dispatch.yml#L80-L99: 목록에 GITHUB_RUN_ID가 없으면 짧은 백오프로 재조회하고, 재조회 후에도 없으면 자신을 리더로 취급하도록 변경하십시오.
  • .github/workflows/agent-mention-opencode-dispatch.yml#L90-L104: 동일한 재조회와 자기 리더 처리 로직을 적용하십시오. 두 파일이 같은 로직을 공유하므로 composite action으로 추출하는 방안을 함께 검토하십시오.
📍 Affects 2 files
  • .github/workflows/agent-mention-noema-dispatch.yml#L80-L99 (this comment)
  • .github/workflows/agent-mention-opencode-dispatch.yml#L90-L104
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/agent-mention-noema-dispatch.yml around lines 80 - 99,
Update the leader-election blocks in
.github/workflows/agent-mention-noema-dispatch.yml#L80-L99 and
.github/workflows/agent-mention-opencode-dispatch.yml#L90-L104 to retry the
workflow-run listing with a short backoff when GITHUB_RUN_ID is absent, then
treat the current run as leader if it remains absent after retries instead of
exiting with failure. Keep duplicate suppression when a lower durable run ID is
found; consider extracting the identical logic into a shared composite action.

Comment on lines +6 to +14
paths:
- ".github/workflows/agent-mention-router.yml"
- ".github/workflows/agent-mention-router-quality-ci.yml"
- ".github/workflows/agent-mention-noema-dispatch.yml"
- ".github/workflows/agent-mention-opencode-dispatch.yml"
- "scripts/ci/agent_mention_router.py"
- "scripts/ci/agent_mention_sweep.py"
- "tests/test_agent_mention_*.py"
- "requirements-opencode-review-ci-hashes.txt"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

전체 테스트 스위트를 실행하도록 품질 게이트를 수정하세요.

현재 pathsdocs/automation/review-agent-comment-invocation.md가 없으므로 해당 거버넌스 문서만 변경하면 이 게이트가 실행되지 않습니다. 게이트가 실행되어도 지정된 여섯 테스트만 실행합니다.

문서 경로를 트리거에 추가하고, 명시적 테스트 파일 목록 없이 python -m coverage run -m pytest -q를 실행하세요. include 설정은 두 CI 스크립트의 100% 측정 범위를 계속 제한합니다.

As per coding guidelines, “Run the full test suite when editing contract-tested workflows, configuration, or governance prose; tests assert exact strings and structures in those files.”

Also applies to: 76-82

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/agent-mention-router-quality-ci.yml around lines 6 - 14,
Update the workflow trigger paths in the quality CI configuration to include
docs/automation/review-agent-comment-invocation.md. Replace the explicitly
listed pytest files with the full-suite command python -m coverage run -m pytest
-q, while preserving the existing include configuration limiting coverage
measurement to the two CI scripts.

Source: Coding guidelines

Comment on lines +46 to +50
- name: Checkout exact head
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

PR 변경 범위를 대상으로 공백 검사를 수행하세요.

인자 없는 git diff --check는 작업 트리와 인덱스의 차이만 검사합니다. 이 작업은 깨끗한 checkout 후에 실행되므로 PR 또는 push 변경을 검사하지 않습니다. Git은 두 커밋 또는 A...B 형식으로 명시한 비교 범위를 지원합니다. (git-scm.com)

  • .github/workflows/agent-mention-router-quality-ci.yml#L46-L50: PR base/head와 push before/head를 비교할 수 있도록 필요한 커밋을 fetch하세요.
  • .github/workflows/agent-mention-router-quality-ci.yml#L96-L96: 이벤트별 변경 범위를 git diff --check에 전달하세요.
  • docs/automation/review-agent-comment-invocation.md#L58-L58: 실제 변경 범위 검사가 구현되기 전에는 최종 diff 공백 검사 주장을 제거하세요.
📍 Affects 2 files
  • .github/workflows/agent-mention-router-quality-ci.yml#L46-L50 (this comment)
  • .github/workflows/agent-mention-router-quality-ci.yml#L96-L96
  • docs/automation/review-agent-comment-invocation.md#L58-L58
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/agent-mention-router-quality-ci.yml around lines 46 - 50,
Update .github/workflows/agent-mention-router-quality-ci.yml lines 46-50 to
fetch the commits needed for PR base/head and push before/head comparisons, then
update the git diff --check invocation at line 96 to use the event-specific
comparison range. Remove the final diff whitespace-check claim from
docs/automation/review-agent-comment-invocation.md line 58 until the actual
changed-range check is implemented.

Comment on lines +168 to +171
if not BASE_BRANCH_RE.fullmatch(base_branch):
raise ValueError("pull request base branch is missing or invalid")
if not actor:
raise ValueError("comment actor is missing")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

actor 값을 downstream wrapper와 동일한 문자 집합으로 검증하십시오.

라우터는 actor가 비어 있지 않은지만 확인합니다. .github/workflows/agent-mention-noema-dispatch.yml 라인 50과 .github/workflows/agent-mention-opencode-dispatch.yml 라인 55는 REQUESTED_BY^[A-Za-z0-9-]+$를 요구합니다. actor가 이 집합을 벗어나면 라우터는 dispatch를 보내고 대상 PR에 확인 코멘트를 남기지만, wrapper는 exit 1로 실패합니다. 그리고 중앙 run 원장에는 해당 invocation key의 실행 기록이 남으므로 재시도가 억제됩니다. 결과는 조용한 유실입니다. 생산자 측에서 fail-closed로 검증하십시오.

🐛 제안 수정
 BASE_BRANCH_RE = re.compile(r"^(?!-)[A-Za-z0-9._/-]+$")
+ACTOR_RE = re.compile(r"^[A-Za-z0-9-]+$")
-    if not actor:
-        raise ValueError("comment actor is missing")
+    if not ACTOR_RE.fullmatch(actor):
+        raise ValueError("comment actor is missing or invalid")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/agent_mention_router.py` around lines 168 - 171, Update the actor
validation in the router’s request-validation flow to require the same character
set as the downstream wrappers: one or more ASCII letters, digits, or hyphens.
Reject missing or invalid actor values before dispatching, while preserving the
existing ValueError behavior for invalid input.

Comment on lines +291 to +315
response = dispatch_client.request(
[
endpoint,
"-X",
"GET",
"-f",
"event=repository_dispatch",
"-f",
"per_page=100",
"--paginate",
"--slurp",
]
)
marker = agent_invocation_marker(request, agent)
for run in _workflow_run_records(response):
run_id = run.get("id")
if (
isinstance(run_id, int)
and run_id > 0
and run.get("event") == "repository_dispatch"
and marker in str(run.get("display_title") or "")
):
observed.add(agent)
break
return frozenset(observed)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

5분 주기 스윕이 서버 측 시간 필터 없이 전체 페이지네이션을 수행합니다. 두 지점은 같은 근본 원인을 공유합니다. --paginate --slurp는 반복 이전에 모든 페이지를 가져오고, 조회 범위를 lookback 창으로 제한하지 않습니다. .github/workflows/agent-mention-router.yml 라인 7의 5분 주기와 기본 lookback 168시간이 이 비용을 반복 발생시킵니다. 결과적으로 REST 시간당 5,000회 한도를 소진할 수 있고, 같은 토큰을 사용하는 다른 자동화까지 중단됩니다.

  • scripts/ci/agent_mention_router.py#L291-L315: 실행 목록 질의에 created 파라미터로 lookback 창을 지정하십시오. 그리고 스윕 1회 실행 동안 에이전트별 marker 집합을 한 번만 조회해 재사용하십시오.
  • scripts/ci/agent_mention_sweep.py#L137-L156: 결과가 sort=updated&direction=desc로 정렬되므로, cutoff 이전 항목을 만나면 해당 저장소의 순회를 중단하고 페이지 단위로 지연 조회하십시오.
📍 Affects 2 files
  • scripts/ci/agent_mention_router.py#L291-L315 (this comment)
  • scripts/ci/agent_mention_sweep.py#L137-L156
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/agent_mention_router.py` around lines 291 - 315, Limit the
workflow-run request in scripts/ci/agent_mention_router.py:291-315 by adding the
lookback-window created filter, and query the agent marker set only once per
sweep run for reuse across agents. In scripts/ci/agent_mention_sweep.py:137-156,
preserve the updated-descending ordering, stop repository traversal when an item
is older than the cutoff, and fetch results lazily one page at a time instead of
using full pagination.

Comment on lines +386 to +435
existing = dispatched_agents(request, dispatch_client, dispatchable)
missing = tuple(agent for agent in dispatchable if agent not in existing)
handles = tuple(f"@{agent}" for agent in missing)
if not missing and not rejected:
return ()

dispatch_endpoint = f"repos/{CENTRAL_AUTOMATION_REPOSITORY}/dispatches"
if "cwl-noema-review" in missing:
dispatch_client.request(
[dispatch_endpoint, "-X", "POST"],
input_payload=noema_payload(request),
)
if "opencode-agent" in missing:
dispatch_client.request(
[dispatch_endpoint, "-X", "POST"],
input_payload=opencode_payload(request),
)

target_api = f"repos/{request.repository}"
target_client.request(
[f"{target_api}/issues/comments/{request.comment_id}/reactions", "-X", "POST"],
input_payload={"content": "eyes"},
)
status_parts: list[str] = []
if handles:
status_parts.append(f"Queued {' and '.join(handles)}")
existing_handles = tuple(
f"@{agent}" for agent in dispatchable if agent in existing
)
if existing_handles:
status_parts.append(
f"Already queued {' and '.join(existing_handles)} on this exact request"
)
if rejected:
rejected_handles = " and ".join(f"@{agent}" for agent in rejected)
status_parts.append(
f"Rejected {rejected_handles}: repository is absent from "
"OPENCODE_REPOSITORY_DISPATCH_TARGETS"
)
acknowledgement = (
f"{receipt_marker(request.comment_id)}\n"
f"{' ; '.join(status_parts)} for PR #{request.pull_request_number} at head "
f"`{request.pull_request_head_sha}`. Central exact-key workflow runs are "
"the durable dispatch ledger; existing review workflows remain "
"authoritative for the final verdict and failure evidence."
)
target_client.request(
[f"{target_api}/issues/{request.pull_request_number}/comments", "-X", "POST"],
input_payload={"body": acknowledgement},
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

거부된 요청에는 멱등성 신호가 없어 스윕마다 확인 코멘트가 반복 생성됩니다. 근본 원인은 dispatch_request가 dispatch 성공만 durable 원장으로 추적하고, 거부 상태는 추적하지 않는 설계입니다. 허용 목록 밖 @opencode-agent 멘션은 매 스윕 실행마다 동일한 요청으로 재구성되고, 반응과 "Rejected" 코멘트를 다시 게시합니다. 반환값이 비므로 max_dispatches 예산도 이 반복을 제한하지 않습니다.

  • scripts/ci/agent_mention_router.py#L386-L435: 거부 전용 경로에서 반복 게시를 중단하십시오. 통지를 한 번만 유지하려면 스윕이 conversation_comments를 전달하여 processed_comment_ids 억제를 활성화하거나, 거부 사실을 중앙에 기록하는 durable 신호를 도입하십시오.
  • tests/test_agent_mention_router.py#L264-L271: 거부 전용 요청이 확인 코멘트를 게시한다는 단언을 새 동작에 맞게 갱신하고, 동일 요청을 두 번 처리해도 대상 저장소 mutation이 한 번만 발생하는지 확인하는 단언을 추가하십시오.
📍 Affects 2 files
  • scripts/ci/agent_mention_router.py#L386-L435 (this comment)
  • tests/test_agent_mention_router.py#L264-L271
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/agent_mention_router.py` around lines 386 - 435, Update
scripts/ci/agent_mention_router.py:386-435 in dispatch_request to make
rejected-only requests idempotent, using conversation_comments and
processed_comment_ids suppression or another durable rejection signal so
reactions and acknowledgement comments are not repeated across sweeps and
rejected requests do not consume dispatch budget. Update
tests/test_agent_mention_router.py:264-271 to reflect the new rejected-request
behavior and assert that processing the same request twice causes
target-repository mutations only once.

Comment on lines +249 to +274
for issue in list_recent_pull_requests(
target_client,
organization=organization,
repository_source=repository_source,
since=since,
):
for request in build_requests_for_pull_request(
target_client,
issue=issue,
since=since,
):
queued_agents = dispatch_request(
request,
target_client=target_client,
dispatch_client=dispatch_client,
opencode_allowlist=opencode_allowlist,
dry_run=dry_run,
)
if not queued_agents:
continue
dispatched += 1
if dispatched >= max_dispatches:
print(f"Agent mention sweep reached dispatch limit {max_dispatches}.")
return dispatched
print(f"Agent mention sweep completed with {dispatched} dispatch(es).")
return dispatched

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

저장소 단위 오류 격리가 없습니다.

sweep는 후보 순회 전체를 단일 예외 경계로 실행합니다. list_recent_comments, build_requests_for_pull_request, dispatch_request 중 하나가 예외를 발생시키면 남은 모든 저장소의 처리가 중단됩니다. 이 경로는 조직 전체 저장소를 대상으로 하고, 대상 저장소별 접근 권한은 스윕 도중에도 달라질 수 있습니다. 예를 들어 gh api가 403 또는 404를 반환하면 GitHubClient.requestCalledProcessError를 발생시킵니다.

그 결과 저장소 하나의 실패가 5분 주기마다 반복해서 전체 스윕을 차단합니다. 후보 단위로 예외를 포착하고, 오류를 기록한 뒤 다음 후보로 진행하십시오. 종료 코드는 실패 건수에 따라 결정하십시오.

♻️ 제안 방향
     for issue in list_recent_pull_requests(
         target_client,
         organization=organization,
         repository_source=repository_source,
         since=since,
     ):
-        for request in build_requests_for_pull_request(
-            target_client,
-            issue=issue,
-            since=since,
-        ):
+        try:
+            candidates = build_requests_for_pull_request(
+                target_client,
+                issue=issue,
+                since=since,
+            )
+        except Exception as error:  # noqa: BLE001
+            print(f"::warning::Skipped {issue['repository']}#{issue['number']}: {error}")
+            continue
+        for request in candidates:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/agent_mention_sweep.py` around lines 249 - 274, Update the sweep
flow around list_recent_pull_requests, build_requests_for_pull_request, and
dispatch_request to isolate exceptions per repository or candidate, log each
failure, and continue processing remaining candidates. Track the number of
failed candidates and determine the sweep’s return/exit status from that failure
count while preserving the existing dispatch-limit behavior.

Comment on lines +264 to +271
assert module.dispatch_request(
request,
target_client=target,
dispatch_client=central,
opencode_allowlist=frozenset(),
) == ()
assert central.calls == []
assert "Rejected @opencode-agent" in target.calls[-1][1]["body"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

이 기대값은 반복 코멘트 결함을 고정합니다.

테스트는 dispatch할 에이전트가 없고 거부만 있는 경우에도 확인 코멘트가 게시된다고 단언합니다. 이 동작이 스윕에서 반복 게시를 유발합니다. 근본 원인과 수정 위치는 scripts/ci/agent_mention_router.py 라인 386-435에 기록했습니다. 라우터를 수정할 때 이 단언도 함께 갱신하십시오.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_agent_mention_router.py` around lines 264 - 271, Update the test
around module.dispatch_request to expect no confirmation comment when no agent
is dispatched and the request is only rejected; remove or revise the assertion
checking “Rejected `@opencode-agent`” in target.calls, while preserving the
assertions that central.calls remains empty and dispatch returns ().

Comment on lines +11 to +20
def import_without_script_directory(
name,
globals=None,
locals=None,
fromlist=(),
level=0,
):
if name == "pr_review_merge_scheduler":
raise ModuleNotFoundError(name)
return real_import(name, globals, locals, fromlist, level)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Ruff A002 오류를 제거하세요.

globalslocals가 Python 내장 이름을 가립니다. 매개변수를 module_globalsmodule_locals로 변경하고 real_import 호출에 같은 순서로 전달하세요.

수정 예시
 def import_without_script_directory(
     name,
-    globals=None,
-    locals=None,
+    module_globals=None,
+    module_locals=None,
     fromlist=(),
     level=0,
 ):
     if name == "pr_review_merge_scheduler":
         raise ModuleNotFoundError(name)
-    return real_import(name, globals, locals, fromlist, level)
+    return real_import(name, module_globals, module_locals, fromlist, level)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def import_without_script_directory(
name,
globals=None,
locals=None,
fromlist=(),
level=0,
):
if name == "pr_review_merge_scheduler":
raise ModuleNotFoundError(name)
return real_import(name, globals, locals, fromlist, level)
def import_without_script_directory(
name,
module_globals=None,
module_locals=None,
fromlist=(),
level=0,
):
if name == "pr_review_merge_scheduler":
raise ModuleNotFoundError(name)
return real_import(name, module_globals, module_locals, fromlist, level)
🧰 Tools
🪛 Ruff (0.16.1)

[error] 13-13: Function argument globals is shadowing a Python builtin

(A002)


[error] 14-14: Function argument locals is shadowing a Python builtin

(A002)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_pr_review_fix_scheduler_coverage.py` around lines 11 - 20, Rename
the import_without_script_directory parameters globals and locals to
module_globals and module_locals to avoid shadowing Python built-ins, and pass
the renamed parameters to real_import in the same positional order.

Source: Linters/SAST tools

@seonghobae
seonghobae enabled auto-merge (squash) August 6, 2026 09:22
@opencode-agent
opencode-agent Bot disabled auto-merge August 6, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants