fix(opencode): allow governed free models for private repositories - #830
fix(opencode): allow governed free models for private repositories#830seonghobae wants to merge 16 commits into
Conversation
Add an immutable trusted-base opt-in for private repositories classified as public-equivalent, preserve the existing model-pool implementation byte-for-byte behind a policy wrapper, and isolate every OpenCode subprocess to its selected provider credential.
|
Warning Review limit reached
Next review available in: 1 minute Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughPrivate 저장소의 익명 OpenCode 무료 모델 사용 정책을 추가했습니다. 정책은 신뢰된 base 커밋에서만 활성화됩니다. 모델 풀을 별도 구현으로 위임하고, 공급자별 자격 증명 격리와 fail-closed 계약 테스트를 추가했습니다. ChangesOpenCode 거버넌스 및 실행 제어
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Wrapper as run_opencode_review_model_pool.sh
participant Policy as opencode_private_free_model_policy.py
participant Guard as opencode_provider_guard.sh
participant Pool as run_opencode_review_model_pool_impl.sh
participant OpenCode as OpenCode
Wrapper->>Policy: base/head 커밋으로 정책 평가
Policy-->>Wrapper: 무료 모델 사용 허용 또는 거부
Wrapper->>Guard: OpenCode 실행 wrapper 설치
Wrapper->>Pool: 후보 목록과 실행 환경 전달
Pool->>Guard: 선택된 모델 실행 요청
Guard->>OpenCode: 정리된 자격 증명 환경으로 실행
OpenCode-->>Pool: 모델 출력과 세션 결과 반환
Pool-->>Wrapper: 성공, 재시도 또는 exhausted 상태 기록
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Expand the governed private pool to every anonymous candidate already configured by the central workflow and retain the established fail-closed source contract while delegating runtime behavior to the unchanged implementation.
Keep the established central source-level contract visible at the stable entrypoint and fail closed on a truncated delegated implementation in full workflow materializations.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
tests/test_opencode_private_free_model_runner_contract.py (1)
206-221: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win이 테스트는 두 개의 독립된 차단 이유를 동시에 만족합니다.
base_has_policy=False이므로 정책 평가가 이미 거부됩니다. 동시에 후보 목록에opencode-free/glm-5-free가 있어candidate_list_contains_anonymous_free_model이 조기 반환합니다. 따라서 "기존 free 풀은 재정렬하지 않는다"는 계약이 단독으로 검증되지 않습니다.base_has_policy=True로 바꾸면 조기 반환 경로만 검증합니다.💚 테스트 강화 제안
source, base_sha, head_sha = create_source_repository( tmp_path, - base_has_policy=False, + base_has_policy=True, head_changes_policy=False, )🤖 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_opencode_private_free_model_runner_contract.py` around lines 206 - 221, Update test_existing_public_free_pool_is_not_reordered_or_duplicated to set base_has_policy=True while keeping head_changes_policy=False, so the policy gate passes and the test isolates the existing public free-pool ordering behavior without triggering the anonymous free-model early return.scripts/ci/run_opencode_review_model_pool.sh (2)
169-170: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
trap을install_provider_guard앞에 등록하십시오.현재
trap cleanup_provider_guard EXIT INT TERM은install_provider_guard다음 줄에 있습니다.mktemp -d성공 후cp또는chmod가 실패하면set -e가 스크립트를 종료합니다. 그 시점에는 trap이 아직 없으므로 임시 디렉터리가 남습니다. trap을 먼저 등록하면 모든 실패 경로에서 정리가 실행됩니다.♻️ 순서 변경 제안
-install_provider_guard trap cleanup_provider_guard EXIT INT TERM +install_provider_guard🤖 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/run_opencode_review_model_pool.sh` around lines 169 - 170, Register the cleanup trap before calling install_provider_guard so cleanup_provider_guard handles failures during temporary-directory setup, including cp or chmod errors under set -e.
77-103: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value후보 목록 확장 시 glob 확장을 차단하십시오.
for candidate in ${OPENCODE_MODEL_CANDIDATES:-}는 인용을 생략하여 단어 분리를 의도합니다. 그러나 파일명 확장도 함께 활성화됩니다. 워크플로가*,?,[를 포함한 후보 문자열을 전달하면 후보 이름이 현재 디렉터리 파일명으로 치환될 수 있습니다. 두 함수를set -f/set +f로 감싸거나,read -r -a로 배열을 만들면 확장이 차단됩니다.🛡️ 제안
candidate_list_contains_anonymous_free_model() { - local candidate - for candidate in ${OPENCODE_MODEL_CANDIDATES:-}; do + local candidate + local -a candidates + read -r -a candidates <<<"${OPENCODE_MODEL_CANDIDATES:-}" + for candidate in "${candidates[@]}"; do case "$candidate" in opencode-free/*) return 0 ;; esac done return 1 } prepend_unique_anonymous_free_candidates() { local combined="" local candidate - for candidate in $anonymous_free_candidates ${OPENCODE_MODEL_CANDIDATES:-}; do + local -a candidates + read -r -a candidates <<<"$anonymous_free_candidates ${OPENCODE_MODEL_CANDIDATES:-}" + for candidate in "${candidates[@]}"; do case " $combined " in🤖 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/run_opencode_review_model_pool.sh` around lines 77 - 103, Disable pathname expansion while iterating over OPENCODE_MODEL_CANDIDATES in candidate_list_contains_anonymous_free_model and prepend_unique_anonymous_free_candidates, preserving intentional whitespace-based word splitting. Restore the caller’s globbing state after each function completes, including early returns, or use a read-based array approach that prevents glob expansion without changing candidate parsing.scripts/ci/opencode_private_free_model_policy.py (1)
176-177: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueblob SHA 검증에 커밋 SHA 패턴을 재사용합니다.
COMMIT_SHA_PATTERN은 40자 16진수만 허용합니다. SHA-256 오브젝트 포맷 저장소에서git ls-tree는 64자 SHA를 반환합니다. 그 경우 정책 평가는 상태 2로 실패합니다. 현재 GitHub 호스팅 저장소는 SHA-1이므로 즉시 영향은 없습니다. 별도의 오브젝트 ID 패턴(40 또는 64자)을 사용하면 향후 마이그레이션에서 안전합니다.🤖 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/opencode_private_free_model_policy.py` around lines 176 - 177, Update the validation around entry.object_sha in the policy evaluation flow to use a dedicated object ID pattern that accepts valid 40- or 64-character hexadecimal SHAs, rather than COMMIT_SHA_PATTERN. Keep the existing PolicyEvaluationError and invalid-SHA handling unchanged.scripts/ci/run_opencode_review_model_pool_impl.sh (1)
42-51: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value
normalize_opencode_output은 호출 컨텍스트의 errexit 비활성화에 의존합니다.
set -euo pipefail이 활성 상태입니다. Line 44의opencode_review_approve_gate.sh가 0이 아닌 상태로 끝나면, 조건 컨텍스트 밖에서는 errexit이 발동하여 Line 46의rc=$?와 Line 50의rm -f "$probe"가 실행되지 않습니다. 현재 유일한 호출 지점인 Line 536은if !조건이므로 동작합니다. 향후 다른 위치에서 호출하면 임시 파일이 남고 폴백이 중단됩니다. 명시적으로 상태를 잡으면 호출 위치와 무관하게 안전합니다.♻️ 제안
if python3 "$GITHUB_WORKSPACE/scripts/ci/opencode_review_normalize_output.py" \ "$HEAD_SHA" "$RUN_ID" "$RUN_ATTEMPT" "$probe"; then - bash "$GITHUB_WORKSPACE/scripts/ci/opencode_review_approve_gate.sh" \ - "$HEAD_SHA" "$RUN_ID" "$RUN_ATTEMPT" "$probe" >/dev/null - rc=$? + rc=0 + bash "$GITHUB_WORKSPACE/scripts/ci/opencode_review_approve_gate.sh" \ + "$HEAD_SHA" "$RUN_ID" "$RUN_ATTEMPT" "$probe" >/dev/null || rc=$? else rc=1 fi🤖 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/run_opencode_review_model_pool_impl.sh` around lines 42 - 51, Update the normalize_opencode_output flow around opencode_review_approve_gate.sh so its nonzero status is captured explicitly without relying on an outer if or ! condition to suppress errexit. Ensure rc is assigned before cleanup, rm -f "$probe" always runs, and the function returns the captured status for callers regardless of invocation context.tests/test_opencode_private_free_model_policy_1.py (1)
17-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win세 테스트 파일이 동일한 97줄 헤더를 복제합니다. 공유 헬퍼 모듈이 없어 모듈 로더,
run,git,commit_all,write_policy,repositoryfixture,evaluate가 세 번 정의되었습니다. 정책 검사기 인터페이스가 바뀌면 세 곳을 모두 수정해야 합니다.tests/conftest.py또는 전용 헬퍼 모듈로 추출하십시오.
tests/test_opencode_private_free_model_policy_1.py#L17-L113: 헬퍼와 fixture를 공유 모듈로 옮기고 import로 대체하십시오.tests/test_opencode_private_free_model_policy_2.py#L17-L113: 동일한 공유 모듈을 import하도록 바꾸십시오.tests/test_opencode_private_free_model_policy_3.py#L17-L113: 동일한 공유 모듈을 import하도록 바꾸십시오.참고: 세 파일 모두
sys.modules["opencode_private_free_model_policy"]에 서로 다른 모듈 객체를 등록합니다. 공유 모듈로 통합하면 이 중복 등록도 사라집니다.🤖 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_opencode_private_free_model_policy_1.py` around lines 17 - 113, Extract the duplicated module loader, run, git, commit_all, write_policy, repository fixture, and evaluate helpers into one shared test helper module. Update tests/test_opencode_private_free_model_policy_1.py#L17-L113, tests/test_opencode_private_free_model_policy_2.py#L17-L113, and tests/test_opencode_private_free_model_policy_3.py#L17-L113 to import the shared helpers and remove their local definitions, including separate sys.modules registrations for opencode_private_free_model_policy.
🤖 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 `@docs/doctoring/opencode-private-free-model-policy.md`:
- Around line 63-74: 문서의 모델 목록에서 1번, 3번, 12번 항목의 잘린 `-fre` 접미사를 `-free`로 수정해
`anonymous_free_candidates` 및 `EXPECTED_FREE_CANDIDATES`와 이름을 일치시키세요.
In `@scripts/ci/opencode_private_free_model_policy.py`:
- Around line 218-219: Update the validation around EXPECTED_POLICY to compare
JSON values with strict type sensitivity, so boolean true is not accepted as
numeric 1 and vice versa. Preserve the exact canonical declaration requirement
for every field, including schema_version and allow_private_free_models, while
retaining the existing PolicyDenied behavior for mismatches.
In `@scripts/ci/opencode_provider_guard.sh`:
- Around line 16-24: Update the argument scan around previous_argument and
model_candidate to recognize both “--model candidate” and “--model=candidate”
forms. Track occurrences explicitly and reject duplicate --model values before
provider credential removal, while preserving the existing candidate validation
and single-model behavior.
In `@scripts/ci/run_opencode_review_model_pool_impl.sh`:
- Line 463: Validate OPENCODE_FATAL_ERROR_POLL_SECONDS through the existing
env_integer_or_default helper when assigning fatal_poll_seconds, preserving the
default of 5 for unset or non-integer values. Ensure the validated value is used
by the sleep call in the kill -0 polling loop.
---
Nitpick comments:
In `@scripts/ci/opencode_private_free_model_policy.py`:
- Around line 176-177: Update the validation around entry.object_sha in the
policy evaluation flow to use a dedicated object ID pattern that accepts valid
40- or 64-character hexadecimal SHAs, rather than COMMIT_SHA_PATTERN. Keep the
existing PolicyEvaluationError and invalid-SHA handling unchanged.
In `@scripts/ci/run_opencode_review_model_pool_impl.sh`:
- Around line 42-51: Update the normalize_opencode_output flow around
opencode_review_approve_gate.sh so its nonzero status is captured explicitly
without relying on an outer if or ! condition to suppress errexit. Ensure rc is
assigned before cleanup, rm -f "$probe" always runs, and the function returns
the captured status for callers regardless of invocation context.
In `@scripts/ci/run_opencode_review_model_pool.sh`:
- Around line 169-170: Register the cleanup trap before calling
install_provider_guard so cleanup_provider_guard handles failures during
temporary-directory setup, including cp or chmod errors under set -e.
- Around line 77-103: Disable pathname expansion while iterating over
OPENCODE_MODEL_CANDIDATES in candidate_list_contains_anonymous_free_model and
prepend_unique_anonymous_free_candidates, preserving intentional
whitespace-based word splitting. Restore the caller’s globbing state after each
function completes, including early returns, or use a read-based array approach
that prevents glob expansion without changing candidate parsing.
In `@tests/test_opencode_private_free_model_policy_1.py`:
- Around line 17-113: Extract the duplicated module loader, run, git,
commit_all, write_policy, repository fixture, and evaluate helpers into one
shared test helper module. Update
tests/test_opencode_private_free_model_policy_1.py#L17-L113,
tests/test_opencode_private_free_model_policy_2.py#L17-L113, and
tests/test_opencode_private_free_model_policy_3.py#L17-L113 to import the shared
helpers and remove their local definitions, including separate sys.modules
registrations for opencode_private_free_model_policy.
In `@tests/test_opencode_private_free_model_runner_contract.py`:
- Around line 206-221: Update
test_existing_public_free_pool_is_not_reordered_or_duplicated to set
base_has_policy=True while keeping head_changes_policy=False, so the policy gate
passes and the test isolates the existing public free-pool ordering behavior
without triggering the anonymous free-model early return.
🪄 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: 4ef09089-44f5-44d9-997c-fbf050bce76d
📒 Files selected for processing (13)
CHANGELOG.mddocs/doctoring/opencode-private-free-model-policy.mddocs/examples/opencode-private-free-models.jsonscripts/ci/opencode_private_free_model_policy.pyscripts/ci/opencode_provider_guard.shscripts/ci/run_opencode_review_model_pool.shscripts/ci/run_opencode_review_model_pool_impl.shtests/test_opencode_delegated_runner_contract.pytests/test_opencode_private_free_model_policy_1.pytests/test_opencode_private_free_model_policy_2.pytests/test_opencode_private_free_model_policy_3.pytests/test_opencode_private_free_model_runner_contract.pytests/test_opencode_provider_guard.py
Expose every established central shell-gate marker at the stable model-pool entrypoint and verify the delegated implementation before any model process starts.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@tests/test_opencode_private_free_model_runner_contract.py`:
- Around line 275-280: In test_wrapper_preserves_every_quick_gate_runner_marker,
correct the undefined wrapper_tex reference in the marker assertion loop to use
the existing wrapper_text variable read from WRAPPER.
- Line 116: Update the test fixture’s candidate-selection logic to use
OPENCODE_MODEL_CANDIDATES exclusively, remove the unused
OPENCODE_MODD_CANDIDATES fallback, and capture/assert that the first candidate
is selected because the fake opencode does not validate --model. Also correct
the undefined wrapper_tex reference to wrapper_text.
🪄 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: 42094baf-07a5-4706-8dcb-044a8071e605
📒 Files selected for processing (2)
scripts/ci/run_opencode_review_model_pool.shtests/test_opencode_private_free_model_runner_contract.py
🚧 Files skipped from review as they are similar to previous changes (1)
- scripts/ci/run_opencode_review_model_pool.sh
Correct the exact-head regression test so the stable wrapper marker contract is evaluated without truncating the final identifier.
Restore the previously verified exact runner-contract test blob while retaining the live quick-gate assertions in the central shell gate itself.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@tests/test_opencode_private_free_model_runner_contract.py`:
- Line 237: 손상된 테스트 코드를 복구해 `for script in (...)` 구문이 `WRAPPER`와
`PROVIDER_GUARD`를 순회하도록 수정하고, 각 스크립트에 대해 Bash `-n` 구문 검증을 수행하게 하십시오. 변경 후 전체 테스트
스위트를 실행해 검증하십시오.
🪄 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: 0581ad70-cdc7-4c1b-90cf-b3c384e52202
📒 Files selected for processing (1)
tests/test_opencode_private_free_model_runner_contract.py
|
@opencode-agent address Exact-head bounded GREEN repair for The current branch already contains RED contracts in
Do not alter model selection, credentials, review semantics, timeout policy beyond this validation, or any unrelated file. Run the focused runner contract and the repository-authoritative exact-head suite before committing. Do not merge or synthesize approval. |
|
@opencode-agent address Exact-head bounded GREEN repair for Current-head Strix Changed Path Quality CI run Two production defects are directly evidenced in the delegated stable implementation:
Make only these minimal production repairs plus any strictly necessary test/doc wording alignment. Do not weaken the fail-first assertions, do not change provider eligibility, credentials, wrapper policy, model order, secrets, workflows, or branch protection, and do not create temporary/self-modifying workflows. Run the focused runner contract first, then the complete repository tests, |
|
@opencode-agent address Same unchanged exact head
Do not alter candidate ordering or any other model identifier. Validate the documentation against |
|
@opencode-agent address Repair only the exact current head Exact-head Strix run
Do not change the trusted-base private/free-model policy, provider credential isolation, model list/order, reviewer identities or credentials, NVIDIA NIM behavior, branch protection, tests, or unrelated model-pool semantics. Do not add a temporary/write-capable repair workflow. Run the focused private-free-model runner contract first, then the complete central suite and Strix exact-head gate. Keep the branch unmerged; any new head requires fresh exact-head security/review evidence. |
|
@opencode-agent address Repair only the exact current-head deterministic/review blockers on PR #830. Live head is Exact-head Strix Changed Path Quality CI run Make the smallest repair, limited to the delegated implementation, its focused regression only if needed to cover zero, and the doctoring list:
Do not modify wrapper governance, provider credential isolation, candidate ordering, workflows, permissions, CHANGELOG, unrelated tests, or any other file. Run the focused runner contract, relevant delegated-runner tests, Bash syntax, and doc/model-name consistency before committing, then let normal exact-head CI/security/Strix and review gates rerun. Do not mark Ready/merge/release, resolve unrelated threads, or synthesize approval. |
|
Perform a read-only formal review of exact current head RCA focus: the former blanket private-repository exclusion conflated repository visibility with data classification. Verify that the replacement is operationally realistic and fail-closed: anonymous All exact-head GitHub Actions checks currently report success and all inline review threads are resolved/outdated. Review the current source and bounded evidence independently. Submit a formal |
|
Operational closure is tracked in #833. Code-level merge is not considered sufficient: after protected integration, the scheduled central loop must execute one opted-in private |
|
@cwl-noema-review Please perform independent review-only evaluation of exact current head |
|
@coderabbitai review Please perform a fresh full review of unchanged exact head |
|
|
|
@coderabbitai review The prior exact-head request was rate-limited and its stated cooldown has elapsed. Please now perform one fresh full review of unchanged head |
|
|
|
@coderabbitai review Exact-current-head full review request for |
|
|
|
@coderabbitai review The reported organization review cooldown has now elapsed. Re-requesting one full review for unchanged exact head |
|
|
|
@coderabbitai review Please perform one fresh full review of exact head |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Live PR identity =='
gh api repos/ContextualWisdomLab/.github/pulls/830 \
--jq '{number, state, base: .base.sha, head: .head.sha, updated_at}'
printf '%s\n' '== Local commit identities =='
git rev-parse HEAD
git rev-parse 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba^{commit}
git rev-parse 9a9b3e061599c28905aed793fc803123a2615205^{commit}
printf '%s\n' '== Changed files =='
git diff --name-status 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba 9a9b3e061599c28905aed793fc803123a2615205
printf '%s\n' '== Relevant file outlines =='
ast-grep outline scripts/ci/opencode_private_free_model_policy.py --items all
ast-grep outline tests/test_opencode_provider_guard.py --items all
ast-grep outline tests/test_opencode_private_free_model_runner_contract.py --items all
printf '%s\n' '== Relevant symbols and credential references =='
rg -n -C 3 --glob 'scripts/ci/*.sh' --glob 'scripts/ci/*.py' \
'anonymous_free_candidates|evaluate_policy|require_policy_unchanged|policy_blob_entry|normalize_opencode_output|run_one_model_attempt|OPENCODE_FATAL_ERROR_POLL_SECONDS|OPENCODE_REAL_BIN|GITHUB_TOKEN|ACTIONS_ID_TOKEN|ACTIONS_RUNTIME|OPENCODE_' \
scripts/ciLength of output: 137696 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Policy checker =='
sed -n '1,290p' scripts/ci/opencode_private_free_model_policy.py
printf '%s\n' '== Wrapper =='
sed -n '1,260p' scripts/ci/run_opencode_review_model_pool.sh
printf '%s\n' '== Provider guard =='
sed -n '1,180p' scripts/ci/opencode_provider_guard.sh
printf '%s\n' '== Delegated runner: normalization and model execution =='
sed -n '1,70p;449,545p;760,815p' scripts/ci/run_opencode_review_model_pool_impl.sh
printf '%s\n' '== Focused runner contract =='
sed -n '1,310p' tests/test_opencode_private_free_model_runner_contract.py
printf '%s\n' '== Provider-guard contract =='
sed -n '1,240p' tests/test_opencode_provider_guard.py
printf '%s\n' '== Find workflow model-pool invocation =='
rg -n -C 18 --glob '*.yml' --glob '*.yaml' \
'Run OpenCode PR Review model pool|OPENCODE_MODEL_CANDIDATES|is_private|opencode-free|run_opencode_review_model_pool' \
.github
printf '%s\n' '== Policy tests: self-enablement and canonical policy =='
rg -n -C 12 --glob 'test_opencode_private_free_model_policy_*.py' \
'self|unchanged|canonical|VALID_POLICY|duplicate|symlink|missing' testsLength of output: 136034 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Direct OpenCode invocation sites =='
rg -n -C 4 -P '\bopencode\s+(run|export)\b|\bOPENCODE_REAL_BIN\b|install_provider_guard\b' \
scripts/ci/run_opencode_review_model_pool.sh \
scripts/ci/run_opencode_review_model_pool_impl.sh \
scripts/ci/opencode_provider_guard.sh \
.github/workflows/opencode-review-dispatch.yml
printf '%s\n' '== Complete model-pool environment and execution block =='
sed -n '4276,4395p' .github/workflows/opencode-review-dispatch.yml
printf '%s\n' '== Credential-like names present in changed scripts =='
rg -n -o --glob 'opencode_private_free_model_policy.py' --glob 'opencode_provider_guard.sh' \
--glob 'run_opencode_review_model_pool*.sh' \
'[A-Z][A-Z0-9_]*(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|ACTIONS_[A-Z0-9_]+)' scripts/ci \
| sort -u
printf '%s\n' '== Changed documentation candidate IDs =='
rg -n -C 1 'opencode-free/' docs/doctoring/opencode-private-free-model-policy.md
printf '%s\n' '== Current PR checks =='
gh pr checks 830 --repo ContextualWisdomLab/.github || true
printf '%s\n' '== Final live PR identity =='
gh api repos/ContextualWisdomLab/.github/pulls/830 \
--jq '{base: .base.sha, head: .head.sha, state, updated_at}'Length of output: 29726 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Failed-check diagnostic job environment =='
sed -n '4925,4985p' .github/workflows/opencode-review-dispatch.yml
printf '%s\n' '== Failed-check diagnostic model selection and OpenCode calls =='
sed -n '6220,6375p' .github/workflows/opencode-review-dispatch.yml
printf '%s\n' '== Diagnostic model variable definitions =='
rg -n -C 8 -P '(^|[[:space:]])MODEL=|MODEL:|model_candidate|failed-check diagnosis|diagnos' \
.github/workflows/opencode-review-dispatch.yml | tail -n 260
printf '%s\n' '== All direct OpenCode execution commands in the workflow =='
rg -n -C 5 -P '\bopencode\s+(run|export)\b' .github/workflows/opencode-review-dispatch.ymlLength of output: 37474
P1 — Guard the failed-check diagnosis subprocessThe provider guard protects the model-pool subprocesses. It does not protect the direct failed-check diagnosis subprocess. The job environment provides The selected model is Route this diagnostic path through the same provider-credential guard, or apply an equivalent complete environment scrub before both commands. Keep the selected-provider-only rule for The PR head and base remained the requested commits during this review. I did not mutate the branch or infer approval from the passing checks. You are interacting with an AI system. |
Problem
The central OpenCode review workflow currently prepends NVIDIA NIM and anonymous
opencode-free/*candidates only when GitHub reports the target repository as public. A private repository therefore skips the free pool even when its tracked source is intentionally public-equivalent and contains no confidential data.Repository visibility is not a sufficient data-classification signal, but the absence of configured Actions secrets is not sufficient either. Source, fixtures, history, and generated review evidence can still be confidential.
Solution
Add a fail-closed trusted-base policy at
.github/opencode-private-free-models.json.Require the exact canonical declaration:
{ "schema_version": 1, "allow_private_free_models": true, "repository_data_classification": "public_equivalent", "external_model_data_use_accepted": true }Read the declaration from the exact immutable PR base commit.
Reject a PR that adds, removes, renames, chmods, or modifies its own policy; the opt-in takes effect only after the policy reaches the protected base branch and is used by a later PR.
Prepend all twelve anonymous free candidates already supported by the current central generated OpenCode configuration, then retain the existing keyed fallback pool.
Preserve the existing model-pool implementation byte-for-byte as
run_opencode_review_model_pool_impl.sh; the original entrypoint becomes a small governance and credential-isolation wrapper while retaining the established static fail-closed source contract.Validate the delegated stable implementation's established contract before invoking any model in a full central workflow materialization.
Scope every OpenCode subprocess to its selected provider credential. Anonymous free models, export commands, and unknown provider prefixes receive no provider key, GitHub token, Actions OIDC credential, or Actions runtime/cache/results credential.
Add an operator runbook, an example policy, CHANGELOG entries, and complete regression coverage.
Security properties
The policy checker accepts only a regular non-executable
100644blob at the fixed path, strict UTF-8, at most 4,096 bytes, exact fields and values, and JSON without duplicate keys. It accepts only full 40-character base/head SHAs, ignores user/system Git configuration, disables hooks and filesystem monitors, and fails closed on missing, invalid, changed, or unreadable policy state.The declaration means the repository owner accepts external free-model processing for tracked repository content classified as
public_equivalent; it does not claim that secret scanning can prove the absence of every confidential fact. Secret Protection, push protection, generic/custom patterns, and CODEOWNERS remain defense-in-depth controls.Verification
Local focused verification:
The skipped test only checks that the twelve governed candidates are present in the full central workflow; the focused local fixture intentionally excludes that large workflow. All twelve candidates are present in current
main, and the repository's existingtest_strix_quick_gate.shindependently enforces the stable entrypoint markers on the exact hosted head.Migration note
This focused change supersedes the overlapping private/free-model routing slice in draft PR #760. Any future rebase or decomposition of #760 must preserve this trusted-base opt-in and provider-scoped credential boundary rather than restoring the blanket private-repository exclusion.
Sources