fix(opencode-review): vendor Cargo deps offline for the coverage sandbox - #2223
Merged
Merged
Conversation
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
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthrough베이스 커밋의 Cargo 의존성을 오프라인으로 벤더링하는 Python 도구를 추가했습니다. 생성된 vendor 디렉터리와 Cargo 구성을 커버리지 Docker 이미지와 샌드박스에 연결했습니다. 입력 검증, 오류 처리, CLI 동작을 테스트했습니다. ChangesRust 의존성 벤더링
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 구성 복사
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
This was referenced Sep 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #2222 (merged): the fallback review now posts
REQUEST_CHANGESinstead of a bareCOMMENT, so PRs no longer hang forever with no formal verdict — but per fast-mlsirm#1907 thatstill 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):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"), andthe sandboxed coverage-measurement container runs
docker run --network=none.ImportError: cannot import name '_core' from partially initialized module 'fast_mlsirm'(410-411 collection errors), because nothing inthe generic Python coverage path (
safe_pytest_command.py) builds the compiled extension beforerunning pytest. This produced a bogus
RESULT: FAILED (minimum: 100.0%, actual: 69.7%)— not areal measurement of the PR's test coverage.
34922561683, PR fix(ci): close the admission-controller coverage/docstring gap on main #1883, job104362547255) failedcargo llvm-covdirectly: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 closureson 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 compiledPython extension — was structurally guaranteed to fail regardless of PR content.
Fix
Adds
scripts/ci/materialize_base_rust_dependencies.py, mirroring the existing Python/JSmaterializers' trust model:
git show(never the pullrequest's) — the same base-only trust boundary as the Python/JS materializers.
cargo vendorwhile the runner still has network, using Cargo's own built-in per-packagechecksum verification (every
Cargo.lockentry carries achecksum), so no separate hash-pinparser is needed.
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 sandboxedCARGO_HOMEright beforeany
cargo/pytest command runs — socargo llvm-covand any Cargo build now resolve dependenciesfrom the offline vendor directory instead of
index.crates.io.This directly fixes the 1/9
cargo llvm-covnetwork-resolution failure. It also lays therequired infrastructure for the dominant 8/9
_coreImportError case, but does not completethat fix: building the extension needs
maturin(orsetuptools-rust) present in the sandbox'strusted Python toolchain, and
maturinis not currently inrequirements-opencode-review-ci-hashes.txt. Regenerating that hash-pinned lock changes theresolved/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
--upgraderegeneration in this PR. Once vendored Rust deps are baked in (this PR) andmaturinis added to the trusted requirements (follow-up), the fix is: detect a
build-backend = "maturin"project via its
pyproject.tomland run a fixedpip install --no-build-isolation --no-deps --no-index -e .before the discovered pytest command.Second finding (issue comment):
close-emptyrequired check never producedTraced separately, not fixed by this PR — it is not a code change:
close-emptyis 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"), whichcorrectly removed
close-empty-pr.ymlfrom ruleset18156473's required-workflow list and movedthe functionality into
pr_review_merge_scheduler_core.py'sscan-pr-queuedecision logic — butfast-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-emptyfromrequired_status_checks.contexts), not a git-trackable file — I did not make this changeunilaterally 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
materialize_base_rust_dependencies.py— 100% line+branchcoverage, 100% docstring coverage (
coverage run -m pytest tests/test_materialize_base_rust_dependencies.py,interrogate scripts/ci)(
cargo build --offlineagainst only the emitted vendor config)(preserves the base-only trust boundary)
coverage run -m pytest tests→ 3134 passed, 3 skipped, 100% coverage /100% docstrings on
scripts/ci(two pre-existing, unrelated coverage gaps innoema_review_document.py/noema_review_gate.pyare untouched by this diff)tests/test_*shell_syntax*,tests/test_*workflow_contract*— 95 passedREVIEW_DISPATCH_BLOB_SHApin intest_pr_review_autofix_nvidia_nim_contract.pyto match the new workflow blob
python3 -c "import yaml; yaml.safe_load(open('.github/workflows/opencode-review-dispatch.yml'))"—valid YAML
docker build/docker run --network=nonecoverage pipelineend-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
개선 사항
테스트