Skip to content

fix(scheduler): isolate central Actions inventory quota - #1231

Draft
seonghobae wants to merge 23 commits into
mainfrom
fix/scheduler-actions-read-token
Draft

fix(scheduler): isolate central Actions inventory quota#1231
seonghobae wants to merge 23 commits into
mainfrom
fix/scheduler-actions-read-token

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Current status — Proposed / decomposition required

This historical branch remains open so no valid delta is lost. It is intentionally Draft because exact head 99a1a481871f26ee1793338fe401d6373d85281e is 23 commits ahead and 36 commits behind protected main@c9052e607e5f3cc76e73207e7786b21500721b79, is conflicted, and combines 17 files from several independent responsibilities. Earlier test counts, approvals, and required-workflow receipts belong to predecessor heads and are not current admission evidence.

Verified successor carryover

The following bounded responsibilities now have current-base RED→GREEN successors:

  • #2002 — host-scoped Actions inventory/cancellation credentials; RED 8cc62ce…, GREEN 7bf3451a…
  • #2003 — prove selected mutation token differs from workflow github.token and preserve recorded guidance; RED ebcc6715…, GREEN e2204eeb…
  • #2004 — restore Python Security and Runtime Quality coverage for stacked PRs; RED 890bac2f…, GREEN 14f7c85c…
  • #2005 — skip non-authoritative target Actions inventory for centralized review; RED 08a16caa…, GREEN a42ab003…
  • #2006 — reject Draft PRs at direct-merge and auto-merge mutation boundaries; RED 2d140a84…, GREEN 5abc0a02…
  • #2007 — case-insensitive owned-head and compare-ref identity; RED 4fb514db…, GREEN 75e9d67c…

All successors are Draft/Proposed and preserve normal fast-forward lineage. #2004 through #2007 each generated all five hosted workflow runs, including stacked Python Security and Runtime Quality, but those runs remain queued and are not GREEN evidence. #1231 is not retired or closed.

Remaining delta ledger

The following historical responsibilities still require current-main comparison and, where still valid, independent RED→GREEN successors or a non-destructive semantic restack:

  • configured repository-dispatch run-name deduplication is verified complete on protected main: #1983 merged as 74224b2091701a754b2fd1a539833c7a6d50b79e; current main@c9052e607e5f3cc76e73207e7786b21500721b79 is 3 commits ahead / 0 behind that commit, and the production matcher plus OpenCode/Strix rendered-run-name regressions are present. No duplicate successor is required.
  • actionlint/shfmt policy, queue: max compatibility, workflow provisioning, and license-boundary changes
  • SBOM attestation, Rust GPU adapter, NVIDIA fallback, and sweep completion-order changes

The live unresolved thread on dynamic cancel-in-progress beside queue: max remains substantive and intentionally unresolved. The former #1213 dependency is historical and cannot be used as current merge authority.

No reviewer, required check, scanner, permission, ruleset, or cross-repository mutation authority is weakened.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

스케줄러가 실행 호스트별 credential을 선택하고 Actions 실행을 조회하거나 취소합니다. 중앙 dispatch는 정확한 제목으로 중복 실행을 판별하고 비권위 대상 실행 정리를 생략합니다. 병합 mutation은 draft PR을 거부합니다. 워크플로 린터와 Strix fallback 동작도 갱신합니다.

Changes

워크플로와 스케줄러 강화

Layer / File(s) Summary
신뢰된 워크플로 린팅
scripts/ci/lint_github_workflows.rb, tests/test_lint_github_workflows.py, .github/workflows/pr-review-autofix.yml, docs/doctoring/actionlint-modern-schema-and-shellcheck.md
Ruby 린터가 YAML 구조와 concurrency.queue: max를 검증합니다. actionlint와 파일 기반 ShellCheck를 분리 실행하고, 오류와 잘못된 출력을 fail-closed로 처리합니다.
스케줄러 자격 증명과 실행 호스트 인벤토리
scripts/ci/pr_review_merge_scheduler.py, .github/workflows/pr-review-merge-scheduler.yml, .github/workflows/opencode-review-dispatch.yml, tests/test_pr_review_merge_scheduler.py, tests/test_opencode_agent_contract.py, docs/doctoring/fork-head-review-dispatch.md
실행 호스트별 credential을 선택합니다. 실제 선택 토큰과 workflow token을 검증합니다. 기록된 credential 거부 사유를 guidance와 요약에 사용합니다.
디스패치 중복과 병합 방어
scripts/ci/pr_review_merge_scheduler.py, tests/test_pr_review_merge_scheduler.py, docs/doctoring/fork-head-review-dispatch.md, CHANGELOG.md
정확한 repository·PR·head SHA 제목을 workflow name 필터보다 먼저 비교합니다. 중앙 dispatch에서는 대상 저장소의 old-head 정리를 생략합니다. draft PR의 enable_auto_mergemerge_pr 호출을 사전에 차단합니다.
워크플로 실행과 Strix 지원 변경
.github/workflows/opencode-review-dispatch.yml, .github/workflows/exact-artifact-sbom-attestation.yml, scripts/ci/strix_quick_gate.sh, tests/*, docs/doctoring/strix-nvidia-nim-not-found-fallback.md, CHANGELOG.md
대형 shell 블록과 파일 목록 처리를 조정합니다. openai-direct/* fallback을 LiteLLM용 openai/*로 변환합니다. Strix 재시도·스캔 범위·증거 처리·sweep counter·workflow context 변경을 기록하고 검증합니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: 🟡 Moderate · up to 53b53

The current changes can still allow workflows with unsupported concurrency settings, introduce a dependency that conflicts with repository licensing policy, and cause the Strix gate to miss Python files during analysis; these issues should be fixed or explicitly accepted before merging.

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler
  participant CentralDispatch
  participant TargetRepository
  participant MergeMutation
  Scheduler->>CentralDispatch: 정확한 dispatch 제목과 head SHA로 실행 확인
  CentralDispatch-->>Scheduler: 일치하는 실행 반환
  Scheduler->>TargetRepository: 중앙 dispatch이면 old-head 실행 정리 생략
  Scheduler->>MergeMutation: 병합 또는 자동 병합 요청
  MergeMutation-->>Scheduler: draft PR이면 RuntimeError 반환
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 75 functions across 10 files. (7 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 중앙 Actions inventory quota를 격리하는 scheduler 수정이라는 PR의 주요 목적을 정확히 요약합니다. 간결하고 구체적입니다.
Full details: Docstring Coverage

Explanation

Docstring coverage is 72.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 75 functions across 10 files. (7 skipped: 7 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/scheduler-actions-read-token

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

…-read-token

# Conflicts:
#	CHANGELOG.md
#	tests/test_pr_review_autofix_nvidia_nim_contract.py
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

* fix(ci): lint modern Actions schemas safely

* fix(ci): preserve multiline workflow diagnostics

* test(ci): bound Ruby runtime coverage

* fix(ci): preserve deterministic workflow evidence
github-advanced-security[bot]

This comment was marked as resolved.

github-advanced-security[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@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 7b16617af04431a43f8f7528b8ac7db345e404a7.
  • 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 (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 (3 files)"]
  S3 --> I3["operator or user guidance"]
  I3 --> R3["Review risk: Docs (3 files)"]
  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 (8 files)"]
  S5 --> I5["regression suite"]
  I5 --> R5["Review risk: Test (8 files)"]
  R5 --> V5["targeted test run"]
Loading

…-read-token

# Conflicts:
#	.github/workflows/opencode-review-dispatch.yml
#	CHANGELOG.md
#	docs/doctoring/strix-nvidia-nim-not-found-fallback.md
#	tests/test_pr_review_autofix_nvidia_nim_contract.py
#	tests/test_strix_nvidia_nim_not_found_fallback.py

Copy link
Copy Markdown
Contributor Author

Resolved the stale merge conflict against current main (base was 885f2cd, main tip 1d8e872).

5 conflicting files, all mechanical/superseded-content — no unresolved product decision:

  • .github/workflows/opencode-review-dispatch.yml: this PR only added a shellcheck disable=SC2016 explanatory comment above the Tauri-npm-build bash -c line; main independently changed the command itself from bare npm run build to corepack npm run build (the pnpm/Corepack-boundary hardening already in CHANGELOG.md's Unreleased section). Kept the PR's comment with main's corepack npm run build command — complementary, not competing.
  • CHANGELOG.md: pure ## [Unreleased] / ### Fixed list-append conflict — kept both this PR's two entries and every entry main added since the branch point.
  • docs/doctoring/strix-nvidia-nim-not-found-fallback.md: this PR's unique contribution here (rewording the fallback chain to "→ configured direct OpenAI candidate" and a paragraph delegating the full cross-provider credential handoff to ContextualWisdomLab/.github#1213) is now fully superseded — #1213 itself is closed ("superseded by fix(strix): make Azure and cross-provider fallbacks executable #1263 ... moved unchanged to protected main"), and main's current doc already describes the live, more advanced chain (NVIDIA catalog → OpenRouter's dynamic free router → direct OpenAI, dated 2026-08-25) with no mention of a pending #1213 dependency. Took main's version for both hunks and dropped the now-stale #1213-blocker paragraph the 3-way merge had carried forward unflagged; the result is byte-identical to main's copy of this file.
  • tests/test_strix_nvidia_nim_not_found_fallback.py: same story at the code level — main has since migrated Strix off direct NVIDIA-catalog model resolution entirely onto the contextual-orchestrator/orchestrator/free gateway (confirmed: this PR's diff never touches scripts/ci/strix_quick_gate.sh, and that file's fallback logic already matched main with zero conflict). Kept main's test_workflow_routes_all_scans_through_contextual_orchestrator in place of the PR's stale test_workflow_uses_available_free_first_nvidia_plan (whose body asserted on NVIDIA-model wiring main already removed from strix.yml).
  • tests/test_pr_review_autofix_nvidia_nim_contract.py: REVIEW_DISPATCH_BLOB_SHA pins the exact git blob hash of opencode-review-dispatch.yml as a byte-for-byte trust-boundary check. Since the merge changed that file's content (see above), neither side's recorded hash was correct — recomputed git hash-object on the merged file (ea13d12a3771f7bb2550736185730f61ae0b766d) and used that.

Test evidence (worktree, head ae36220a):

  • PYTHONPATH=. python3 -m pytest tests/test_pr_review_merge_scheduler.py tests/test_strix_nvidia_nim_not_found_fallback.py tests/test_pr_review_autofix_nvidia_nim_contract.py tests/test_opencode_agent_contract.py tests/test_exact_artifact_sbom_attestation_contract.py tests/test_opencode_rust_coverage_toolchain_contract.py tests/test_pr_review_conflict_scope.py -q → 276 passed, 1 skipped
  • PYTHONPATH=. python3 -m pytest tests -q (full suite) → 1918 passed, 1 skipped, 21 subtests
  • coverage run -m pytest tests -q && coverage report --include="scripts/ci/*" --show-missing → 100% statements/branches across all of scripts/ci/, including pr_review_merge_scheduler.py (1777 stmts / 732 branches, 100%)
  • interrogate -v scripts/ci/pr_review_merge_scheduler.py and repo-wide interrogate → 100% docstring coverage
  • ruby -c scripts/ci/lint_github_workflows.rb → Syntax OK
  • YAML-parsed all 4 touched workflow files → OK
  • No .sh files are touched by this PR's own diff, so no bash -n targets applied

Pushed as merge commit ae36220a onto fix/scheduler-actions-read-token (no rebase/force-push). Ready for fresh exact-head review.


Generated by Claude Code

@devin-ai-integration devin-ai-integration 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.

Devin Review found 6 new potential issues.

Devin Review

Comment thread scripts/ci/pr_review_merge_scheduler.py Outdated
Comment thread scripts/ci/lint_github_workflows.rb
Comment on lines +31 to +40
def validate_concurrency_queue!(path, label, concurrency)
return unless concurrency.is_a?(Hash) && concurrency.key?("queue")
unless concurrency["queue"] == "max"
raise WorkflowLintError,
"#{path}: #{label} concurrency queue must be exactly max, got #{concurrency['queue'].inspect}"
end
return unless concurrency["cancel-in-progress"] == true

raise WorkflowLintError,
"#{path}: #{label} concurrency queue max requires cancel-in-progress to be false or absent"

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.

🔍 Dynamic cancellation policy remains ambiguous

validate_concurrency_queue! accepts expression-valued cancellation beside queue: max. Define whether dynamic expressions require rejection or proof of false before relying on this compatibility gate.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread scripts/ci/lint_github_workflows.rb
Comment thread scripts/ci/pr_review_merge_scheduler.py Outdated
Comment thread .github/workflows/exact-artifact-sbom-attestation.yml
… shell detection

Devin findings on PR #1231:

1. scripts/ci/pr_review_merge_scheduler.py: same_repository_head and
   compare_ref_for_pr_head compared headRepository.nameWithOwner to the
   configured target repo with exact case-sensitive equality, even though
   GitHub repository identity is case-insensitive and a sibling
   stale-run-cancellation path in this same file already case-folds.
   A same-repository PR whose GitHub-reported canonical name differed only
   in case from the configured target lost branch updates and automated
   merge eligibility. Fixed both to casefold consistently with the
   existing normalization idiom used elsewhere in the file. Added a
   regression covering same_repository_head, compare_ref_for_pr_head, and
   the end-to-end inspect_pr update_branch decision with a
   differently-cased headRepository.

2. scripts/ci/lint_github_workflows.rb: effective_shell ignored job-level
   `container:`, which defaults a step's shell to sh (not bash) per
   GitHub Actions semantics when no shell is configured anywhere in the
   resolution chain — so Bash-only syntax in a container job's default
   shell could pass validation as if it were bash. shellcheck_dialect also
   only recognized the bare names "bash"/"sh", skipping absolute-path
   custom shell templates (e.g. "/bin/bash --noprofile --norc -eo
   pipefail {0}", "/usr/bin/sh {0}") entirely rather than classifying
   them. Fixed by defaulting containerized jobs' shell to sh, and by
   classifying shellcheck_dialect off the executable's basename so
   absolute paths resolve the same as bare names. Added regression tests
   for both: a container job with no explicit shell (asserts shfmt is
   invoked with the posix dialect) and absolute-path bash/sh templates
   (asserts shfmt is invoked at all, with the correct dialect).

All four changes verified against the pre-fix code: each new/extended
test fails on the unpatched function and passes after the fix.

Evidence: PYTHONPATH=. python3 -m pytest tests -q -> 1920 passed, 1
skipped, 21 subtests passed; coverage 100% statements/branches on
scripts/ci; interrogate 100% docstrings; ruby -c clean; git diff --check
clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPmJErfkcHer4UVEgrQxUX

Copy link
Copy Markdown
Contributor Author

Devin finding "Repository casing blocks valid automation" — confirmed and fixed

Verified against the exact PR head (ae36220ae1519e678ddbd99de87a00e7072f3544): same_repository_head (scripts/ci/pr_review_merge_scheduler.py:1905) and compare_ref_for_pr_head (:963) both compared headRepository.nameWithOwner to the configured target repo with exact ==, while a sibling check in the same file (the stale-run-cancellation gate, repository_dispatch_target(repo).casefold() == repo.casefold() at line 2439) already case-folds. GitHub repository identity is case-insensitive, so a same-repository PR whose GitHub-reported canonical name differed only in case from the configured target would be misclassified as cross-repository — losing branch updates and merge eligibility.

Fix: both functions now casefold() both sides, matching the existing normalization idiom already used elsewhere in this file (repo.casefold() at lines 629 and 2439). same_repository_head also guards the None-headRepository case explicitly via bool(head_repo) and ... (kept as a single expression, not a branching if, to avoid an uncovered branch under the 100%-branch-coverage gate).

Regression test (tests/test_pr_review_merge_scheduler.py): extended test_inspect_pr_blocks_and_waits_for_policy_states with a PR whose headRepository.nameWithOwner is "Owner/Repo" against target "owner/repo", asserting same_repository_head, can_update_pr_head, compare_ref_for_pr_head (bare ref, not owner-prefixed), and the end-to-end inspect_pr decision (update_branch, branch-update actually invoked). Verified it fails against the pre-fix exact-== code (AssertionError: assert False on same_repository_head) and passes against the fix.

Note: I found the identical case-sensitive-== pattern in same_repository_head inside scripts/ci/pr_review_fix_scheduler.py and scripts/ci/pr_auto_rebase.py, but neither file is in this PR's diff, so I left them alone rather than expanding scope — flagging for a follow-up PR/issue if that's wanted.

Devin finding "Supported shells escape correct validation" — confirmed and fixed

Verified against scripts/ci/lint_github_workflows.rb: effective_shell never consulted job-level container:, so a container job with no explicit shell: anywhere in the resolution chain defaulted to bash in the linter, even though GitHub Actions actually runs such a step under sh. Separately, shellcheck_dialect only recognized the bare names "bash"/"sh" (or those names followed by a space), so an absolute-path custom shell template (e.g. /bin/bash --noprofile --norc -eo pipefail {0}, /usr/bin/sh {0}) matched neither branch and was silently skipped — no shell-syntax validation at all for that step.

Fix: effective_shell now defaults containerized jobs (job has a container: key) to sh instead of bash when nothing else in the chain sets a shell. shellcheck_dialect now takes the first whitespace token of the shell string, resolves it to its File.basename, and classifies on that — so "bash", "/bin/bash", "/bin/bash --noprofile --norc -eo pipefail {0}", "sh", and "/usr/bin/sh {0}" all classify correctly.

Regression tests (tests/test_lint_github_workflows.py, added, following the existing subprocess-driven harness pattern with faked actionlint/shfmt):

  • test_linter_treats_unshelled_container_job_step_as_posix_sh — a container job step with no explicit shell asserts shfmt is invoked with ["-ln", "posix", "-tojson"], and a sibling non-container job step still gets bash.
  • test_linter_classifies_absolute_path_shell_templates — absolute-path bash/sh templates each produce a shfmt invocation (previously: zero, since the step was skipped entirely) with the correct dialect.

Both verified to fail against the pre-fix functions (posix-vs-bash mismatch; 0 shfmt records instead of 2) and pass against the fix.

Other unresolved Devin threads on this PR (not in scope, no action taken)

  • "Dynamic cancellation policy remains ambiguous" (lint_github_workflows.rb:40) — a design question about validate_concurrency_queue!'s handling of expression-valued cancel-in-progress, not a concrete bug; left for human/maintainer judgment.
  • Three "Info" threads (mutation token proof, checksum inventory, queue diagnostic exemption) are informational confirmations, not findings requiring a fix.

Validation evidence

  • ruby -c scripts/ci/lint_github_workflows.rb — clean
  • PYTHONPATH=. python3 -m pytest tests -q — 1920 passed, 1 skipped, 21 subtests passed
  • coverage run -m pytest tests -q && coverage report — 100% statements, 100% branches on scripts/ci (pr_review_merge_scheduler.py: 1777/1777 statements, 732/732 branches)
  • interrogate — 100% docstring coverage
  • git diff --check — clean

Pushed to fix/scheduler-actions-read-token (this PR's actual head branch — the branch name given in the task, fix/central-actions-inventory-quota-isolation, does not match the PR's real head ref) at b297581a.


_Generated by Claude Code


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

opencode-review failure on b297581a (job 99272876036) is the standard fail-closed wait-state, not a defect: No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head — the async OpenCode dispatch hasn't posted a verdict for this exact head yet. No action needed beyond waiting for the next dispatch cycle.


Generated by Claude Code

@seonghobae seonghobae added area: ci-cd CI, GitHub Actions, checks, release, or supply chain bug Something isn't working priority: high High-priority or P1 work status: blocked Blocked by conflict, dependency, or required prerequisite type: bug Defect or incorrect behavior labels Sep 2, 2026 — with ChatGPT Codex Connector

Copy link
Copy Markdown
Contributor Author

Merge-conflict triage: genuine architectural divergence (pr_review_merge_scheduler.py facade/core split), not attempted

Checked this PR's diff (git diff --name-only origin/main...origin/fix/scheduler-actions-read-token) before attempting the standard merge repair for its dirty mergeable_state. It edits scripts/ci/pr_review_merge_scheduler.py directly — the same file this session found in #1271 (see that PR's comment) to be a pre-split, thousands-of-lines monolith on the PR side versus a 241-line facade re-exporting from the new scripts/ci/pr_review_merge_scheduler_core.py on current main (landed via #1803, after this PR was opened). A text-level git merge cannot reconcile those two shapes of the same file; resolving it correctly means re-deriving this PR's credential-routing/mutation-authorization logic against pr_review_merge_scheduler_core.py as it stands today, not a mechanical conflict resolution — so per this loop's "don't guess on real divergence" standard, no merge was attempted and nothing was pushed.

This PR's own description also records an unresolved external stack dependency (ContextualWisdomLab/.github#1213 "must land on protected main before this PR's required Strix run can use the corrected trusted gate") that is independent of the file-split issue above and would need to be re-checked against current main regardless.

Recommended path: re-derive this PR's Actions-inventory-credential-routing fix directly against current main/pr_review_merge_scheduler_core.py (and re-verify whether #1213's dependency still applies) rather than resolving the conflict in place.


Generated by Claude Code

seonghobae pushed a commit that referenced this pull request Sep 4, 2026
…test-bug fix

Follow-up to this same PR's original entry: this round's continued PR sweep
found #1065 and #1681 conflicting on strix.yml/noema_review_gate.py (same
pattern as the 7 PRs already documented), plus #1271 and #1231 conflicting on
scripts/ci/pr_review_merge_scheduler.py -- confirming the #1803 facade/core
split is now also an active collision surface (4,074-line monolith on each
PR's branch vs. a 241-line facade + separately-evolving core file on main).
Evidence-based comments were left on all 4; no guessed resolution was pushed.

Also records one genuine pre-existing (not merge-caused) test bug found and
fixed while merge-repairing #1655: a jq trailing-newline off-by-one in a new
E2E test, and a coverage gap in opencode_review_normalize_output.py's new
needs-info wrapper (branches only exercised via subprocess, invisible to
coverage.py). Both fixes are test-only, pushed as part of #1655 itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
main replaced the 4063-line scheduler monolith this branch was built on with
a 241-line facade plus a 6222-line core, so all four scheduler conflicts had
ours 656/1322/611/359 lines against theirs 2/17/2/15. That is the #1009 shape,
but it is relocatable here, not a re-implementation: all 16 functions the
branch's hunks anchor on still exist in pr_review_merge_scheduler_core.py.

Method: take main's facade, then replay the branch's own diff against the core
module (git apply --3way with the path rewritten). 15 of 17 hunks landed
cleanly. The two that did not were placement collisions, resolved by applying
the branch's intent at core's existing sites instead of at the branch's:
- core already owns active_workflow_runs (23 references vs the branch's 4) and
  had grown server-side filters and memoization. Kept core's function and
  rerouted its single call to run_github_actions_for_repository(repo, args),
  which is what the branch's version of that function did.
- core calls cancel_stale_pr_runs at a different point in inspect_pr than the
  branch did. Wrapped core's call site with the branch's
  repository_dispatch_target(repo).casefold() == repo.casefold() guard.

Other files:
- .github/workflows/pr-review-merge-scheduler.yml: main deleted the entire
  684-line org-queue-sweep job and pins its absence
  (assert_file_not_contains 'org-queue-sweep'). Took main's side. Provenance
  checked: org-queue-sweep is 4x at the merge base and 4x on the branch, 0x on
  main, and the branch's own change to this file is only +2 lines, so nothing
  the branch authored is lost with the job. One of those two lines
  (SCHEDULER_WORKFLOW_TOKEN) belonged to org-queue-sweep; the other had already
  auto-merged into the job main kept.
- tests/test_opencode_agent_contract.py: the branch pinned
  count('SCHEDULER_WORKFLOW_TOKEN: ...') == 2. One of those two occurrences was
  org-queue-sweep's, so the merged tree has 1. Changed the pin to 1 with the
  reason in a comment.
- tests/test_pr_review_autofix_nvidia_nim_contract.py: REVIEW_DISPATCH_BLOB_SHA
  pins git hash-object on opencode-review-dispatch.yml. Both sides are correct
  for their own tree and both wrong for this merge (branch ea13d12, main
  26e8555). Recomputed from the merged file: d7f7c18.
- tests/test_pr_review_merge_scheduler.py: kept both sides (main's 337 lines and
  the branch's 32). The conflict boundary falls between complete top-level
  functions.
- CHANGELOG.md: kept both entries.

Evidence:
- uvx ruff check --select F821 scripts/ci tests: All checks passed
- full suite, branch head b297581 (unmerged): 1920 passed, 0 failed
- full suite, this merge:                      2919 passed, 0 failed
- PR's own oracle (test_lint_github_workflows.py, test_pr_review_merge_
  scheduler.py, test_pr_review_autofix_nvidia_nim_contract.py): 372 passed
- coverage: TOTAL 100%; pr_review_merge_scheduler_core.py 2557/2557 100%.
  With fail_under = 100 this is the independent proof that both relocated
  edits are reached, not merged-in-but-dead.
- negative control: deleting the SCHEDULER_WORKFLOW_TOKEN workflow line makes
  test_merge_scheduler_uses_escalating_mutation_credentials fail by name
- interrogate: PASSED (minimum 100.0%)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@seonghobae

Copy link
Copy Markdown
Contributor Author

main 병합 완료 (99a1a481) — 리뷰어가 알아야 할 3가지

main이 이 브랜치의 기반인 4063줄 스케줄러 모놀리스를 241줄 facade + 6222줄 core로 교체해서, 스케줄러 충돌 4블록이 ours 656/1322/611/359줄 vs theirs 2/17/2/15줄이었습니다. #1009과 같은 형태지만 재구현이 아니라 재배치였습니다 — 브랜치 헝크가 딛는 함수 16개가 pr_review_merge_scheduler_core.py에 전부 살아 있습니다.

방법: facade는 main 것을 취하고, 브랜치 자신의 diff를 경로만 core로 바꿔 git apply --3way로 재생했습니다. 17개 헝크 중 15개가 그대로 안착했고, 나머지 2개는 의미 충돌이 아니라 배치 충돌이라 core의 기존 자리에 브랜치 의도만 적용했습니다.

  • core는 이미 active_workflow_runs를 갖고 있고(참조 23회 vs 브랜치 4회) 서버사이드 필터와 메모이제이션까지 붙었습니다. core 함수를 유지하고 그 안의 호출 하나만 run_github_actions_for_repository(repo, args)로 돌렸습니다 — 브랜치 판본이 하던 그대로입니다.
  • core는 cancel_stale_pr_runsinspect_pr의 다른 지점에서 호출합니다. 그 호출부를 브랜치의 repository_dispatch_target(repo).casefold() == repo.casefold() 가드로 감쌌습니다.

org-queue-sweep 684줄이 사라집니다 — 이 PR의 손실은 아닙니다

main이 그 잡을 통째로 삭제했고 부재를 계약으로 고정합니다(assert_file_not_contains "org-queue-sweep"). 출처를 확인했습니다: merge-base 4회 / 이 브랜치 4회 / main 0회이고, 이 브랜치가 이 워크플로에 한 변경은 +2줄뿐입니다. 그 2줄 중 하나(SCHEDULER_WORKFLOW_TOKEN)가 org-queue-sweep 소속이었고, 나머지 하나는 main이 남긴 잡으로 이미 자동 병합돼 있었습니다.

REVIEW_DISPATCH_BLOB_SHA — 양쪽 다 틀렸습니다

이 핀은 git hash-object .github/workflows/opencode-review-dispatch.yml입니다. 브랜치는 ea13d12a…, main은 26e85559…를 주장하는데 둘 다 각자 트리에서만 맞고 병합본에는 틀립니다. 병합 파일에서 재계산한 d7f7c18d9fc520e3ae91d58d808ccca9d8e57771로 넣었습니다.

③ 계약 카운트 2 → 1

test_merge_scheduler_uses_escalating_mutation_credentialsSCHEDULER_WORKFLOW_TOKEN 2회를 고정하는데, 그중 하나가 org-queue-sweep 것이었습니다. 1로 바꾸고 이유를 주석에 남겼습니다. 네거티브 컨트롤로 탐지력을 확인했습니다 — env 줄을 지우면 이 테스트가 이름을 찍고 실패합니다.

검증

  • uvx ruff check --select F821 — All checks passed
  • 전체 스위트, 병합 전 브랜치 헤드 b297581a: 1920 passed / 0 failed
  • 전체 스위트, 이 병합: 2919 passed / 0 failed
  • PR 고유 오라클(test_lint_github_workflows.py + 스케줄러/autofix 계약): 372 passed
  • coverage TOTAL 100%, pr_review_merge_scheduler_core.py 2557/2557 100%fail_under = 100이므로 이 수치 자체가 재배치한 두 편집이 죽은 코드가 아니라는 독립 증거입니다
  • interrogate PASSED (minimum 100.0%)

fast-forward 푸시, force push 없음.

🤖 Generated with Claude Code

Copy link
Copy Markdown
Contributor Author

CodeQL compatibility analysis (python) and CodeQL compatibility analysis (actions) failed on this head (99a1a481), both with "CodeQL scan dispatched. The dispatch workflow will rerun this exact failed CodeQL job after publishing its terminal verdict." (jobs 101391596142/101391596251). Confirmed via the job log: VERDICT_STATE=pending — the designed dispatch-and-poll wait state, not a defect.

Same root cause already traced today on .github#1503's identical failure: an org-wide GitHub Actions runner-queue congestion is delaying the underlying codeql-scan-dispatch.yml repository_dispatch runs across multiple PRs simultaneously, not anything in this PR's own diff. Not re-running: this shard's own RUN_ATTEMPT != 1 guard would fail it outright without an authenticated verdict already present, and the dispatch workflow is designed to rerun this exact job automatically once it publishes one. Keeping this PR watched for that.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01KPmJErfkcHer4UVEgrQxUX


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

noema-review failed on this head (99a1a481, job 101391562686) with the same class of real failure just found and reported on .github#1912: HTTPError: HTTP Error 429: Too Many Requests; caller attempts=1, duration=575.5s, phase=response_error, served_model=deepseek-ai/deepseek-v4-flash-0731.

Not this PR's diff. Corroborating evidence for the contextual-orchestrator pool-failover gap: this run's own preflight found 3 "ready" orchestrator/free routes, but all three needed escalation (attempts: 2) to become ready — nvidia_nim_sub_deepseek_ai_deepseek_v4_flash_0731, openrouter_cohere_north_mini_code_free, and openrouter_dots_studio_dots_3_note_preview_free — while nvidia_nim_deepseek_ai_deepseek_v4_pro_0813/nvidia_nim_sub_deepseek_ai_deepseek_v4_pro_0813 were rejected outright (429/TimeoutError) this time. The actual gateway request still went to the nvidia_nim deepseek-flash route and got a 429 after 575s, with no evidence it fell over to either of the two openrouter alternatives also marked ready.

Already investigating and (if confirmed) fixing this at the source in contextual-orchestrator — see the note on .github#1912 for the same root-cause writeup. Not duplicating that analysis here or re-running (same reasoning: a re-run would likely just re-hit the same gap). Keeping this PR watched.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01KPmJErfkcHer4UVEgrQxUX


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

New tooling RED: GitHub queue: max syntax vs actionlint 1.7.12 schema

Naruon release/deploy work reproduced a validator/toolchain mismatch without pushing an unsupported workflow key:

  • GitHub's current concurrency documentation supports queue: max for retained pending runs (up to 100), and documents its incompatibility with cancel-in-progress: true.
  • Existing local validator actionlint 1.7.12, given a minimal workflow through stdin using concurrency.queue: max, exits 1 with unexpected key "queue".
  • This is therefore a CI toolchain/schema compatibility RED, not a reason to suppress actionlint, delete the release-ordering policy, or push syntax that the repository's validator currently rejects.
  • Naruon fix(scheduler): rerun the Strix scan job, not a sibling job #1586 intentionally did not push queue: max; that release/deploy owner remains scoped to image digest/runtime deployment and must not duplicate validator ownership.

This PR's remaining-delta ledger already names actionlint/shfmt policy, queue: max compatibility, workflow provisioning as uncarried work, so keep the repair here (or in a verified bounded successor preserving this exact delta).

Acceptance for the toolchain owner:

  1. Reproduce the minimal stdin case with the exact currently governed actionlint binary/version.
  2. Check upstream actionlint release/schema support for GitHub's documented concurrency.queue grammar; do not assume a newer version supports it without executable proof.
  3. If upstream has support, upgrade/pin through the canonical CI-toolchain path with RED→GREEN contracts covering queue: single, queue: max, invalid queue values, and the documented queue:max + cancel-in-progress:true invalid combination.
  4. If upstream has not released support, keep release/deploy policy source Proposed/unapplied and track the upstream capability gap explicitly rather than suppressing actionlint or maintaining a local permissive fork unless a separately justified owner decision is made.
  5. Preserve PR validation semantics (queue: single + trigger-aware cancellation) and scope queue:max only to lossless release/deploy/migration intent where actual target locks, protected revision/version/digest and idempotency are separately verified.

No Naruon source, release permission, or workflow gate should be weakened to make this RED disappear.

@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 toolchain finding for 99a1a481871f26ee1793338fe401d6373d85281e: current scripts/ci/lint_github_workflows.rb passes actionlint -ignore with QUEUE_DIAGNOSTIC for concurrency.queue.

That implementation predates the now-verified upstream state and conflicts with comment 5565056754's acceptance boundary. GitHub documents queue: max, but actionlint v1.7.12 is still the latest release; upstream issue #657 and PR #654 remain open/unmerged. The local Ruby validator does not make suppressing the released validator's schema error an acceptable protected-main toolchain contract.

Preserve the other #1247/#1231 deltas, but do not land this diagnostic suppression or an unmerged upstream parser. Keep queue:max consumers Proposed/unapplied until upstream native support is merged and released; then pin the immutable released actionlint version and prove positive/negative fixtures, including rejection of queue:max + cancel-in-progress:true. The existing unresolved dynamic-cancellation finding remains valid independently.

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

Labels

area: ci-cd CI, GitHub Actions, checks, release, or supply chain bug Something isn't working priority: high High-priority or P1 work status: blocked Blocked by conflict, dependency, or required prerequisite type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants