Skip to content

fix(opencode-review): vendor Cargo deps offline for the coverage sandbox - #2223

Merged
seonghobae merged 1 commit into
mainfrom
seonghobae/fix-1907-fallback-cause
Sep 16, 2026
Merged

seonghobae merged 1 commit into
mainfrom
seonghobae/fix-1907-fallback-cause

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #2222 (merged): the fallback review now posts REQUEST_CHANGES instead of a bare
COMMENT, so PRs no longer hang forever with no formal verdict — but per fast-mlsirm#1907 that
still means every PR that hits the fallback path cannot merge. This PR root-causes why the
fallback triggers so often and fixes the dominant cause at its owner (this repo's coverage
sandbox), without weakening or bypassing the review gate.

Root-cause evidence

Collected the opencode-agent reviews on the last 10 dispatch runs across recent fast-mlsirm PRs
(#1868, #1870, #1876, #1882, #1883, #1884, #1887, #1888, #1890, #1892 — the full available window
before the #2222 fix landed and reviews stopped falling back). Classified each fallback from the
live check-run logs (repos/ContextualWisdomLab/.github/actions/jobs/{id}/logs):

Cause Count Share
Coverage-gate computation failure (sandbox infra, not a real regression) 9/10 90%
Pre-existing failed peer GitHub Check (CodeQL) blocking the fallback path 1/10 10%

All 9 "coverage-gate computation failure" cases trace to the same systemic cause: fast-mlsirm
is a maturin/PyO3 project (build-backend = "maturin", module-name = "fast_mlsirm._core"), and
the sandboxed coverage-measurement container runs docker run --network=none.

  • 8/9 failed Python test collection itself with ImportError: cannot import name '_core' from partially initialized module 'fast_mlsirm' (410-411 collection errors), because nothing in
    the generic Python coverage path (safe_pytest_command.py) builds the compiled extension before
    running pytest. This produced a bogus RESULT: FAILED (minimum: 100.0%, actual: 69.7%) — not a
    real measurement of the PR's test coverage.
  • 1/9 (run 34922561683, PR fix(ci): close the admission-controller coverage/docstring gap on main #1883, job 104362547255) failed cargo llvm-cov directly:
    warning: spurious network error ... Could not resolve host: index.crates.io.

Both are the same root gap: Python (materialize_base_python_requirements.py) and JavaScript
(materialize_base_javascript_packages.py) already vendor their base-pinned dependency closures
on the runner, before the network-isolated container exists. Rust/Cargo had no equivalent, so
any Rust-touching coverage run — directly via cargo llvm-cov, or indirectly via a compiled
Python extension — was structurally guaranteed to fail regardless of PR content.

Fix

Adds scripts/ci/materialize_base_rust_dependencies.py, mirroring the existing Python/JS
materializers' trust model:

  • Reads only the validated base commit's Cargo manifests via git show (never the pull
    request's) — the same base-only trust boundary as the Python/JS materializers.
  • Runs cargo vendor while the runner still has network, using Cargo's own built-in per-package
    checksum verification (every Cargo.lock entry carries a checksum), so no separate hash-pin
    parser is needed.
  • Fails closed (does not silently vendor a guess) on any topology beyond a single Cargo workspace
    or a single standalone crate — the same restraint the uv-workspace case already takes in
    materialize_base_python_requirements.py.

Wired into opencode-review-dispatch.yml: materialized alongside the existing Python/JS steps,
COPY'd into the trusted coverage image, and copied into the sandboxed CARGO_HOME right before
any cargo/pytest command runs — so cargo llvm-cov and any Cargo build now resolve dependencies
from the offline vendor directory instead of index.crates.io.

This directly fixes the 1/9 cargo llvm-cov network-resolution failure. It also lays the
required infrastructure for the dominant 8/9 _core ImportError case, but does not complete
that fix: building the extension needs maturin (or setuptools-rust) present in the sandbox's
trusted Python toolchain, and maturin is not currently in
requirements-opencode-review-ci-hashes.txt. Regenerating that hash-pinned lock changes the
resolved/hashed dependency set of the entire org's OpenCode review toolchain image, not just
fast-mlsirm's — I deliberately left that as explicit follow-up rather than landing an unverified
--upgrade regeneration in this PR. Once vendored Rust deps are baked in (this PR) and maturin
is added to the trusted requirements (follow-up), the fix is: detect a build-backend = "maturin"
project via its pyproject.toml and run a fixed pip install --no-build-isolation --no-deps --no-index -e . before the discovered pytest command.

Second finding (issue comment): close-empty required check never produced

Traced separately, not fixed by this PR — it is not a code change:

close-empty is a required status-check context in fast-mlsirm's classic branch protection
(required_status_checks.contexts). close-empty-pr.yml (the workflow that used to produce it)
was deleted in 6fb2a1cf3 (2026-09-04, "ci(scheduler): consolidate empty PR cleanup"), which
correctly removed close-empty-pr.yml from ruleset 18156473's required-workflow list and moved
the functionality into pr_review_merge_scheduler_core.py's scan-pr-queue decision logic — but
fast-mlsirm's own classic branch protection still lists the literal context name close-empty,
which nothing has produced since. GitHub reports an unproduced required context as permanently
"Expected — waiting for status to be reported," blocking merges with zero failing required
checks
(confirmed live: PR #1863, state=BLOCKED, missing required: ['close-empty']).

This is a live branch-protection settings mutation on fast-mlsirm (remove close-empty from
required_status_checks.contexts), not a git-trackable file — I did not make this change
unilaterally since it's a cross-repo, hard-to-reverse infra action. Flagging it here for an
admin/owner to apply; removing a permanently-unsatisfiable orphaned context does not weaken any
review gate (opencode-review, coverage-evidence, etc. all remain required).

Test plan

  • 23 new focused tests for materialize_base_rust_dependencies.py — 100% line+branch
    coverage, 100% docstring coverage (coverage run -m pytest tests/test_materialize_base_rust_dependencies.py,
    interrogate scripts/ci)
  • One test proves a locked base dependency vendors and builds fully offline
    (cargo build --offline against only the emitted vendor config)
  • One test proves a PR-added dependency absent from the base lock is never vendored
    (preserves the base-only trust boundary)
  • Full local suite: coverage run -m pytest tests → 3134 passed, 3 skipped, 100% coverage /
    100% docstrings on scripts/ci (two pre-existing, unrelated coverage gaps in
    noema_review_document.py/noema_review_gate.py are untouched by this diff)
  • tests/test_*shell_syntax*, tests/test_*workflow_contract* — 95 passed
  • Updated the REVIEW_DISPATCH_BLOB_SHA pin in test_pr_review_autofix_nvidia_nim_contract.py
    to match the new workflow blob
  • python3 -c "import yaml; yaml.safe_load(open('.github/workflows/opencode-review-dispatch.yml'))"
    valid YAML
  • Not run: the actual sandboxed docker build/docker run --network=none coverage pipeline
    end-to-end against a live fast-mlsirm PR (needs org CI; this PR's own opencode-review run is
    the first live exercise of the wiring)

No self-approval; not merging this PR myself.

Refs ContextualWisdomLab/fast-mlsirm#1907

🤖 Generated with Claude Code

https://claude.ai/code/session_013tbpBhMXjXEUcz5oWZFKTH

Summary by CodeRabbit

  • 개선 사항

    • 커버리지 실행 시 기준 커밋의 Rust 의존성을 오프라인 환경에서도 사용할 수 있도록 준비합니다.
    • Rust 및 PyO3/maturin 기반 Python 커버리지가 사전 준비된 의존성을 활용해 더 일관되게 실행됩니다.
    • 샌드박스 환경에서도 Cargo 설정이 자동으로 적용됩니다.
    • Rust 코드가 없는 프로젝트는 불필요한 의존성 준비 없이 처리됩니다.
  • 테스트

    • 의존성 준비, 오프라인 빌드, 잘못된 구성 및 오류 상황에 대한 검증을 확대했습니다.

Root-caused the dominant opencode-review fallback cause on fast-mlsirm: of
the last 10 fallback reviews (PRs #1868-#1892), 9 carried "Coverage gate:
failure" and every one of those 9 traced to the same systemic sandbox
limitation, not a real regression in the reviewed PR.

8/9 failed Python test collection with `ImportError: cannot import name
'_core' from partially initialized module 'fast_mlsirm'` (410-411 errors),
because fast-mlsirm is a maturin/PyO3 project and nothing in the generic
Python coverage path (safe_pytest_command.py) builds the compiled
extension before running pytest. 1/9 failed `cargo llvm-cov` directly with
`Could not resolve host: index.crates.io`, because the coverage-measurement
container runs `docker run --network=none` and Rust/Cargo had no offline
dependency path -- unlike Python (materialize_base_python_requirements.py)
and JavaScript (materialize_base_javascript_packages.py), which already
vendor their base-pinned dependency closures on the runner, before the
network-isolated container exists.

Adds materialize_base_rust_dependencies.py, mirroring that same trust
model: it reads only the validated base commit's Cargo manifests (never
the pull request's), runs `cargo vendor` while the runner still has
network, and writes a `[source.crates-io] replace-with` config plus the
vendored crates for the Docker build to bake in. Wires it into
opencode-review-dispatch.yml: materialized alongside the existing
Python/JS steps, COPY'd into the trusted image, and copied into the
sandboxed CARGO_HOME right before any cargo/pytest command runs.

This directly fixes the `cargo llvm-cov` network-resolution failure (the
1/9 case). It also lays the required infrastructure for the dominant 8/9
`_core` ImportError case, but does not complete that fix: fast-mlsirm's
`pyproject.toml` needs `maturin` to build offline, and `maturin` is not in
requirements-opencode-review-ci-hashes.txt. Regenerating that hash-pinned
lock (`uv pip compile --upgrade --generate-hashes ...`) changes the
resolved/hashed dependency set of the entire org's OpenCode review
toolchain image, not just fast-mlsirm's; that is deliberately left as
follow-up work for a session that can validate the regenerated lock
end-to-end, rather than landing it unverified in this PR.

Regression tests: 23 new tests for the materializer (100% line+branch
coverage, 100% docstring coverage), including one that proves a locked
base dependency vendors and builds fully offline (`cargo build --offline`
against only the vendored config), and one that proves a PR-added
dependency absent from the base lock is never vendored. Updates the
REVIEW_DISPATCH_BLOB_SHA pin in test_pr_review_autofix_nvidia_nim_contract.py
to match the new workflow blob. Full local suite: 3134 passed, 3 skipped;
100% coverage and 100% docstrings on scripts/ci (two pre-existing,
unrelated gaps in noema_review_document.py/noema_review_gate.py are not
touched by this change).

Refs ContextualWisdomLab/fast-mlsirm#1907

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

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d0eb26da-e7fb-4f19-b55f-8f5611211c64

📥 Commits

Reviewing files that changed from the base of the PR and between 38d9bfc and efc35f7.

📒 Files selected for processing (4)
  • .github/workflows/opencode-review-dispatch.yml
  • scripts/ci/materialize_base_rust_dependencies.py
  • tests/test_materialize_base_rust_dependencies.py
  • tests/test_pr_review_autofix_nvidia_nim_contract.py

📝 Walkthrough

Walkthrough

베이스 커밋의 Cargo 의존성을 오프라인으로 벤더링하는 Python 도구를 추가했습니다. 생성된 vendor 디렉터리와 Cargo 구성을 커버리지 Docker 이미지와 샌드박스에 연결했습니다. 입력 검증, 오류 처리, CLI 동작을 테스트했습니다.

Changes

Rust 의존성 벤더링

Layer / File(s) Summary
베이스 Cargo 의존성 생성
scripts/ci/materialize_base_rust_dependencies.py
베이스 SHA의 추적된 Cargo.tomlCargo.lock을 확인합니다. 단일 워크스페이스 또는 독립 크레이트의 의존성을 cargo vendor --versioned-dirs로 생성합니다. cargo-config.tomlmanifest.json을 기록합니다.
벤더링 검증 및 오류 처리
tests/test_materialize_base_rust_dependencies.py
Rust 프로젝트 부재, 베이스 커밋 기준 의존성 범위, 잘못된 Git 입력, 심볼릭 링크, 모호한 프로젝트 구조, Cargo 실패, CLI 결과를 검증합니다.
커버리지 워크플로 통합
.github/workflows/opencode-review-dispatch.yml, tests/test_pr_review_autofix_nvidia_nim_contract.py
생성된 의존성을 Docker 이미지에 복사합니다. 비어 있지 않은 Cargo 구성을 샌드박스의 CARGO_HOME에 복사합니다. 워크플로 blob SHA 검증값을 갱신합니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant GitBaseCommit
  participant MaterializeBaseRustDependencies
  participant CargoVendor
  participant CoverageDockerImage
  participant SandboxCARGO_HOME
  GitBaseCommit->>MaterializeBaseRustDependencies: Cargo.toml 및 Cargo.lock 조회
  MaterializeBaseRustDependencies->>CargoVendor: 베이스 트리로 cargo vendor 실행
  CargoVendor-->>MaterializeBaseRustDependencies: vendor 디렉터리 및 Cargo 구성 생성
  MaterializeBaseRustDependencies->>CoverageDockerImage: /opt/base-rust-dependencies 복사
  CoverageDockerImage->>SandboxCARGO_HOME: Cargo 구성 복사
Loading
✨ 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 seonghobae/fix-1907-fallback-cause

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.

@seonghobae
seonghobae merged commit 346b46d into main Sep 16, 2026
6 of 19 checks passed
@seonghobae
seonghobae deleted the seonghobae/fix-1907-fallback-cause branch September 16, 2026 17:01
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.

1 participant