Skip to content

fix(opencode): allow governed free models for private repositories - #830

Open
seonghobae wants to merge 2 commits into
mainfrom
fix/opencode-private-free-opt-in-20260808
Open

fix(opencode): allow governed free models for private repositories#830
seonghobae wants to merge 2 commits into
mainfrom
fix/opencode-private-free-opt-in-20260808

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Problem and RCA

The central OpenCode review path historically used repository visibility as the proxy for anonymous/free-model eligibility. That is too coarse: a private repository can be intentionally public-equivalent, while absence of Actions secrets does not prove that tracked source, history, comments, fixtures, or generated review evidence are non-confidential.

A fresh exact-head review also exposed three distinct fail-closed defects in the first implementation: a private caller could bypass the immutable-base policy by pre-populating opencode-free/*; git ls-tree -z output was reconstructed rather than requiring the real terminating NUL; and the static free alias list had drifted from the current OpenCode Zen zero-cost catalog. These are source defects and are being repaired on this branch rather than classified as reviewer-capacity or governance blockers.

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 and reject a head that adds, removes, renames, chmods, or modifies its own policy. The opt-in takes effect only for a later PR after normal protected-base integration.

  • Never treat preconfigured opencode-free/* text as authorization. Private or unverified callers have every anonymous candidate removed before policy evaluation; only the immutable base policy may re-enable them.

  • Preserve public behavior only from positive visibility evidence: an explicit trusted OPENCODE_REPOSITORY_IS_PRIVATE=false, or a credential-free successful Git read from a strictly validated public ContextualWisdomLab origin. Ambiguous, private, auth-required, malformed, or unavailable visibility evidence fails closed to the policy path.

  • Synchronize the governed anonymous pool to the currently documented OpenCode Zen zero-cost aliases: nemotron-3-ultra-free, deepseek-v4-flash-free, north-mini-code-free, laguna-s-2.1-free, ling-3.0-flash-free, big-pickle, and mimo-v2.5-free. Stale opencode-free/* aliases are filtered before provider execution.

  • Scope every OpenCode subprocess to its selected provider credential. Anonymous/free and export execution receives no GitHub token, Actions OIDC/runtime/cache/results credential, NVIDIA/OpenAI/OpenRouter/OpenCode key, or unrelated provider secret.

  • Recognize both long and short OpenCode model selectors (--model, --model=, -m, -m=), reject duplicate/missing selectors, and stop parsing at --.

  • Require the trusted policy tree lookup to contain exactly one real NUL-terminated git ls-tree -z record; truncated or extra records fail closed.

  • Validate integer model-pool runtime/retry/cycle controls before Bash arithmetic or timeout consumption, with reviewed safe defaults.

Current governed free catalog

The wrapper allowlist is intentionally narrower than the generated provider configuration. The current primary Zen documentation identifies these seven zero-cost aliases:

  1. opencode-free/nemotron-3-ultra-free
  2. opencode-free/deepseek-v4-flash-free
  3. opencode-free/north-mini-code-free
  4. opencode-free/laguna-s-2.1-free
  5. opencode-free/ling-3.0-flash-free
  6. opencode-free/big-pickle
  7. opencode-free/mimo-v2.5-free

Aliases previously labelled free for Hy3, MiniMax M3, GLM 5, Kimi K2.5, and Qwen3.6 Plus are not in the current documented zero-cost list and are no longer admitted by the wrapper. Catalog changes require a separately reviewable source change; the opencode-free/* prefix alone is never trusted as pricing evidence.

Security and governance properties

The canonical 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 proves absence of confidential facts. Secret Protection, push protection, generic/custom patterns, and CODEOWNERS remain defense in depth.

The policy checker accepts only a regular non-executable 100644 blob at the fixed path, strict UTF-8, at most 4,096 bytes, exact field types/values, and JSON without duplicate keys. It accepts only full 40-character base/head SHAs, ignores user/system Git configuration, disables hooks/filesystem monitors, and fails closed on missing, invalid, changed, malformed-tree, or unreadable policy state.

Automated reviewer verdicts remain separate from the repository's qualifying counted independent human approval requirement. Reviewer rate limits or missing counted approval are governance/capacity evidence, not source defects and must not trigger speculative source patches.

Test-first repair after current-head review

The review-triggered repair was implemented test-first:

  • fail-closed private preconfigured-free and unknown-visibility contracts;
  • current official free-catalog filtering contract;
  • real ls-tree -z NUL termination and extra-record rejection;
  • -m/-m= aliases, -- terminator, duplicate/missing model selector, and executable-boundary tests;
  • integer runtime-control contracts for the delegated model pool.

Every new head invalidates predecessor-head checks/reviews. Current exact-head machine evidence is still regenerating after these repairs; queued/in-progress checks are not acceptance.

Operational acceptance

Code-level checks are necessary but not sufficient. Issue #833 remains the post-merge operational contract: separately merge the canonical policy into an authoritatively classified private low-risk canary, use a later PR to prove inherited-base activation, verify an actual opencode-free/* selection and zero credential exposure, run a private negative control without policy, preserve keyed fallback/exhaustion fail-closed behavior, and demonstrate or deterministically rehearse rollback. If no private repository can be authoritatively classified as public-equivalent, keep the feature inactive rather than inventing eligibility.

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, catalog filtering, visibility fail-closed path, and provider-scoped credential boundary rather than restoring blanket private exclusion or trusting candidate text.

Sources

Summary by CodeRabbit

  • 새로운 기능

    • 비공개 저장소에서 익명 무료 모델을 사용할 수 있는 엄격한 정책 검증을 추가했습니다.
    • 공개 동등 데이터 선언과 외부 모델 데이터 사용 동의가 확인된 경우에만 무료 모델을 활성화합니다.
    • 무료 모델 후보를 검증된 목록으로 제한하고, 오래되거나 알 수 없는 모델은 제외합니다.
    • 모델 제공자별 자격 증명을 격리하고, 익명·알 수 없는 모델에는 민감한 자격 증명을 전달하지 않습니다.
  • 문서

    • 비공개 무료 모델 사용 조건과 운영 절차를 문서화했습니다.
  • 테스트

    • 정책 검증, 모델 선택, 자격 증명 보호 및 실패 시 안전한 차단 동작을 검증하는 테스트를 추가했습니다.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Private 저장소의 익명 OpenCode 무료 모델 사용을 base 커밋 정책으로 제한했습니다. 모델 풀을 별도 구현으로 위임하고, 공급자별 자격 증명 격리, 실행 제어, fail-closed 테스트를 추가했습니다.

Changes

OpenCode 거버넌스 및 실행 제어

Layer / File(s) Summary
Private 무료 모델 정책 검증
scripts/ci/opencode_private_free_model_policy.py, docs/doctoring/..., docs/examples/..., CHANGELOG.md, tests/test_opencode_private_free_model_policy_*.py
Base 커밋의 고정 정책 blob만 평가합니다. 정책 변경, 잘못된 SHA와 Git tree, 손상된 JSON, 중복 키, 잘못된 UTF-8 및 canonical 값은 거부합니다.
모델 풀 위임 및 무료 후보 제어
scripts/ci/run_opencode_review_model_pool.sh, tests/test_opencode_private_free_model_runner_contract.py, tests/test_opencode_delegated_runner_contract.py
Wrapper가 구현 계약과 정책 검사기를 확인한 뒤 sibling 구현을 실행합니다. 검증된 base 정책이 있을 때만 익명 무료 후보를 중복 없이 추가합니다.
공급자 자격 증명 격리
scripts/ci/opencode_provider_guard.sh, tests/test_opencode_provider_guard.py
선택된 공급자의 자격 증명만 OpenCode에 전달합니다. GitHub, Actions OIDC 및 미선택 공급자 키를 제거합니다. 익명 모델, export, 알 수 없는 공급자는 자격 증명 없이 실행합니다.
위임된 모델 풀 실행
scripts/ci/run_opencode_review_model_pool_impl.sh
모델 출력 검증, 승인 게이트, 공급자 오류 분류, 재시도, 백오프, 후보 격리, 실행 시간·시도·예산 제한 및 exhausted 상태 기록을 구현합니다.

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

Possibly related issues

Possibly related PRs

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 상태 기록
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 private repository에서 governed free model을 허용하는 PR의 주요 변경 사항을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/opencode-private-free-opt-in-20260808

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 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

trapinstall_provider_guard 앞에 등록하십시오.

현재 trap cleanup_provider_guard EXIT INT TERMinstall_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 value

blob 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, repository fixture, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6eb06cd and 3362860.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • docs/doctoring/opencode-private-free-model-policy.md
  • docs/examples/opencode-private-free-models.json
  • scripts/ci/opencode_private_free_model_policy.py
  • scripts/ci/opencode_provider_guard.sh
  • scripts/ci/run_opencode_review_model_pool.sh
  • scripts/ci/run_opencode_review_model_pool_impl.sh
  • tests/test_opencode_delegated_runner_contract.py
  • tests/test_opencode_private_free_model_policy_1.py
  • tests/test_opencode_private_free_model_policy_2.py
  • tests/test_opencode_private_free_model_policy_3.py
  • tests/test_opencode_private_free_model_runner_contract.py
  • tests/test_opencode_provider_guard.py

Comment thread docs/doctoring/opencode-private-free-model-policy.md Outdated
Comment thread scripts/ci/opencode_private_free_model_policy.py Outdated
Comment thread scripts/ci/opencode_provider_guard.sh
Comment thread scripts/ci/run_opencode_review_model_pool_impl.sh Outdated
@seonghobae
seonghobae enabled auto-merge (squash) August 8, 2026 09:59
@opencode-agent
opencode-agent Bot disabled auto-merge August 8, 2026 10:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3362860 and 23eb9f7.

📒 Files selected for processing (2)
  • scripts/ci/run_opencode_review_model_pool.sh
  • tests/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

Comment thread tests/test_opencode_private_free_model_runner_contract.py Outdated
Comment thread tests/test_opencode_private_free_model_runner_contract.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 23eb9f7 and 58de30c.

📒 Files selected for processing (1)
  • tests/test_opencode_private_free_model_runner_contract.py

Comment thread tests/test_opencode_private_free_model_runner_contract.py Outdated

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Exact-head bounded GREEN repair for e427cfb14dfc8ff0e678b25ea44662b3be3f74b3; target implementation blob scripts/ci/run_opencode_review_model_pool_impl.sh = 986982e9af3e65cf468f993d8e858d9e1edfc5c1. Abort without writing if either identity moved.

The current branch already contains RED contracts in tests/test_opencode_private_free_model_runner_contract.py for two still-valid CodeRabbit findings. Make only the minimal implementation changes needed to satisfy them:

  1. Assign fatal_poll_seconds through the existing env_integer_or_default OPENCODE_FATAL_ERROR_POLL_SECONDS 5 helper so malformed environment input cannot make sleep fail inside the set +e polling loop and busy-spin the runner.
  2. In normalize_opencode_output, capture a nonzero opencode_review_approve_gate.sh status explicitly (for example rc=0; ... || rc=$?) so cleanup of the temporary probe always executes regardless of invocation context; return the captured status unchanged.

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.

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Exact-head bounded GREEN repair for e427cfb14dfc8ff0e678b25ea44662b3be3f74b3; scripts/ci/run_opencode_review_model_pool_impl.sh blob is 986982e9af3e65cf468f993d8e858d9e1edfc5c1. Do not write if either identity has moved.

Current-head Strix Changed Path Quality CI run 31253529097, job 93093433574, has one deterministic failure after the rest of the suite: 1 failed, 1026 passed, 16 subtests passed. The fail-first contract is tests/test_opencode_private_free_model_runner_contract.py::test_delegated_runner_validates_poll_interval_and_cleans_normalization_probe.

Two production defects are directly evidenced in the delegated stable implementation:

  1. run_one_model_attempt() currently uses fatal_poll_seconds="${OPENCODE_FATAL_ERROR_POLL_SECONDS:-5}". Invalid/empty/non-numeric user configuration can reach sleep or create a zero-second busy loop. Use the existing trusted helper exactly as the RED contract requires:
    fatal_poll_seconds="$(env_integer_or_default OPENCODE_FATAL_ERROR_POLL_SECONDS 5)".

  2. normalize_opencode_output() calls the approval gate under set -e and assigns rc=$? on the following line. A nonzero approval-gate result can therefore terminate before rm -f "$probe", leaking the temporary normalization probe. Preserve cleanup by initializing/capturing status with an explicit non-errexit conditional, matching the existing RED contract: the gate invocation must end with >/dev/null || rc=$?, then remove the probe and return the captured status. Do not turn a gate failure into success.

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, bash scripts/ci/test_strix_quick_gate.sh, Bash syntax, Python compile/docstring/coverage gates, and exact-head security gates. Commit normally to this existing branch; do not merge or synthesize approval.

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Same unchanged exact head e427cfb14dfc8ff0e678b25ea44662b3be3f74b3: include the remaining unresolved current-head CodeRabbit documentation finding in the same bounded repair. In docs/doctoring/opencode-private-free-model-policy.md, correct only the three truncated candidate IDs so the operator runbook matches the executable/test contracts exactly:

  • opencode-free/nemotron-3-ultra-freopencode-free/nemotron-3-ultra-free
  • opencode-free/north-mini-code-freopencode-free/north-mini-code-free
  • opencode-free/qwen3.6-plus-freopencode-free/qwen3.6-plus-free

Do not alter candidate ordering or any other model identifier. Validate the documentation against anonymous_free_candidates and EXPECTED_FREE_CANDIDATES as part of the same exact-head run. Resolve the review thread only after the committed text is verified.

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Repair only the exact current head e427cfb14dfc8ff0e678b25ea44662b3be3f74b3 on protected base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba. Immediately before any write, refetch the PR head/base and scripts/ci/run_opencode_review_model_pool_impl.sh; the exact current implementation blob is 986982e9af3e65cf468f993d8e858d9e1edfc5c1. If any identity moved, do not write and re-plan from the new state.

Exact-head Strix run 31253529097, job 93093433574, passed 1,026 tests and failed only test_delegated_runner_validates_poll_interval_and_cleans_normalization_probe. The permanent test already exposes two real fail-closed defects in the delegated runner; keep the test unchanged and make only these minimal production fixes:

  1. Replace the unvalidated fatal_poll_seconds="${OPENCODE_FATAL_ERROR_POLL_SECONDS:-5}" with fatal_poll_seconds="$(env_integer_or_default OPENCODE_FATAL_ERROR_POLL_SECONDS 5)", preventing malformed/empty hostile configuration from reaching sleep or creating a busy/failing poll loop.
  2. In normalize_opencode_output, preserve cleanup when opencode_review_approve_gate.sh returns nonzero under set -e by using the existing test-required form "$HEAD_SHA" "$RUN_ID" "$RUN_ATTEMPT" "$probe" >/dev/null || rc=$? (initialize/retain rc so rm -f "$probe" always executes before returning the gate status).

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.

seonghobae commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

@opencode-agent address

Repair only the exact current-head deterministic/review blockers on PR #830. Live head is e427cfb14dfc8ff0e678b25ea44662b3be3f74b3, protected base is 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba, scripts/ci/run_opencode_review_model_pool_impl.sh blob is 986982e9af3e65cf468f993d8e858d9e1edfc5c1, and docs/doctoring/opencode-private-free-model-policy.md blob is 59693a35485f70f414961db2922d9a499331d2ae. Abort without writing if any head/base/target-blob identity moves.

Exact-head Strix Changed Path Quality CI run 31253529097, job 93093433574, completed 1,026 tests plus 16 subtests with exactly one reported failure at tests/test_opencode_private_free_model_runner_contract.py::test_delegated_runner_validates_poll_interval_and_cleans_normalization_probe. Two current CodeRabbit threads on this same head are also valid: the fatal poll interval is unvalidated, and doctoring truncates three governed model IDs.

Make the smallest repair, limited to the delegated implementation, its focused regression only if needed to cover zero, and the doctoring list:

  1. In run_one_model_attempt, replace raw ${OPENCODE_FATAL_ERROR_POLL_SECONDS:-5} consumption with fatal_poll_seconds="$(env_integer_or_default OPENCODE_FATAL_ERROR_POLL_SECONDS 5)", then fail safe from zero as well ([ "$fatal_poll_seconds" -gt 0 ] || fatal_poll_seconds=5) so malformed, negative, or zero configuration cannot create a no-delay watcher loop. Preserve the existing sleep call and default 5 seconds. If the current regression does not cover zero, add one bounded focused assertion/test before the production change; do not weaken the existing RED assertion.
  2. In normalize_opencode_output, capture approval-gate rejection without relying on caller if ! context: set rc=0 before invoking opencode_review_approve_gate.sh, run the gate as ... "$probe" >/dev/null || rc=$?, then keep unconditional rm -f "$probe" and return "$rc". Do not convert normalization failure or gate rejection into success.
  3. In docs/doctoring/opencode-private-free-model-policy.md, correct only the three truncated governed IDs: nemotron-3-ultra-frenemotron-3-ultra-free, north-mini-code-frenorth-mini-code-free, and qwen3.6-plus-freqwen3.6-plus-free.

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.

Copy link
Copy Markdown
Contributor Author

@opencode-agent

Perform a read-only formal review of exact current head 9a9b3e061599c28905aed793fc803123a2615205 against protected base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba for PR #830. Re-fetch both identities before review and do not write, address, merge, or synthesize approval if either moved.

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 opencode-free/* candidates activate only from an unchanged trusted-base canonical public_equivalent declaration; a PR cannot self-enable; anonymous/free and export subprocesses receive no provider secrets, GitHub token, Actions OIDC/runtime/cache/results credentials, or unrelated provider keys; private repositories without the trusted-base opt-in remain on the private-safe keyed path.

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 APPROVE only if affirmative source-backed evidence supports the contract; otherwise submit source-anchored REQUEST_CHANGES. Do not treat secret-scan success alone as proof that repository content is public-equivalent.

Copy link
Copy Markdown
Contributor Author

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 public_equivalent canary and one private negative control, verify provider/GitHub/Actions credential absence in the anonymous subprocess, preserve keyed fallback and review/merge gates, and demonstrate rollback or a deterministic rollback rehearsal. Auto-merge is enabled for this unchanged head, but it remains gated by a qualifying independent formal approval and normal repository protection.

Copy link
Copy Markdown
Contributor Author

@cwl-noema-review
@opencode-agent

Please perform independent review-only evaluation of exact current head 9a9b3e061599c28905aed793fc803123a2615205 against protected base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba. Do not update the branch, merge, enable auto-merge, or weaken any gate. Re-read the exact head before publishing a formal review; if the head moved, abort this request as stale. Focus on the trusted-base public_equivalent opt-in, provider-credential isolation, fail-closed private-repository behavior, and the latest poll/probe cleanup fixes. A qualifying approval must be a real non-author formal review on this exact head; status-only/comment-only output is not approval.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please perform a fresh full review of unchanged exact head 9a9b3e061599c28905aed793fc803123a2615205. Earlier rate-limited and predecessor-head review evidence is not acceptance; verify the final base-to-head diff and report only current valid findings.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

@seonghobae I will perform a fresh full review of PR #830. I will evaluate the current base-to-head diff and report only findings that apply to the current head.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 4 minutes.

Copy link
Copy Markdown
Contributor Author

@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 9a9b3e061599c28905aed793fc803123a2615205 against base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba. Do not treat predecessor-head findings as current; report only still-valid base-to-head findings.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

@seonghobae I will perform a fresh full review of PR #830. I will evaluate the current base-to-head diff and report only findings that apply to the current head.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 34 minutes.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Exact-current-head full review request for 9a9b3e061599c28905aed793fc803123a2615205. The previous organization rate-limit window reported at 2026-08-08 22:14 KST has elapsed. Please review the complete base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba → head diff. Treat the trusted-base private/free-model policy, provider-scoped credential isolation, self-enablement denial, negative-control path, and fail-closed exhaustion behavior as security-critical. Do not infer approval from successful checks alone.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

@seonghobae I will perform a fresh full review of PR #830. I will verify the current base-to-head diff and report only findings that apply to the current head. I will examine trusted-base opt-in validation, credential isolation, self-enablement denial, negative-control behavior, and fail-closed exhaustion behavior.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 4 minutes.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

The reported organization review cooldown has now elapsed. Re-requesting one full review for unchanged exact head 9a9b3e061599c28905aed793fc803123a2615205 against base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba. Please review the complete diff, with particular attention to trusted-base-only opt-in, PR self-enablement denial, private negative-control behavior, provider-scoped credential isolation, and fail-closed exhaustion. Do not infer approval from green CI alone.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

@seonghobae I will perform a fresh full review of PR #830. I will review the complete base-to-head diff and report only findings that apply to the current head. I will not infer approval from successful CI checks.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 1 minute.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please perform one fresh full review of exact head 9a9b3e061599c28905aed793fc803123a2615205 against protected base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba. The previous organization cooldown has elapsed. Treat this as review-only evidence: do not mutate the branch, and do not treat advisory review as the counted independent human approval required by branch protection. Focus on the trusted-base public_equivalent opt-in, prohibition on PR self-enablement, and anonymous/free subprocess credential isolation. If review capacity is still unavailable, report the capacity condition once; no retry is required from this comment.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

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/ci

Length 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' tests

Length 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.yml

Length of output: 37474


@seonghobae REQUEST_CHANGES

P1 — Guard the failed-check diagnosis subprocess

The provider guard protects the model-pool subprocesses. It does not protect the direct failed-check diagnosis subprocess.

The job environment provides STRIX_GITHUB_MODELS_TOKEN, OPENAI_API_KEY, NVIDIA_API_KEY, and NVIDIA_NIM_API_KEY. It also inherits Actions runtime, cache, and results credentials. At Line 6332, the workflow starts opencode run directly. At Line 6347, it starts opencode export directly. These commands remove only GitHub tokens and the two OIDC variables. They do not remove unrelated provider credentials or Actions runtime credentials.

The selected model is github-models/deepseek/deepseek-v3-0324. This process must receive only STRIX_GITHUB_MODELS_TOKEN. It must not receive the OpenAI, NVIDIA, or Actions runtime credential set.

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 run and the zero-provider-credential rule for export.

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.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please perform one fresh full review of exact current head 9a9b3e061599c28905aed793fc803123a2615205 against protected base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba. The prior organization cooldown has elapsed. Revalidate the trusted-base private/free-model opt-in, PR self-enablement refusal, provider-scoped credential isolation, fail-closed negative path, and current tests/docs. Do not treat rate-limit/provider capacity as a source defect, and do not infer counted human approval from automated review.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba'
head='9a9b3e061599c28905aed793fc803123a2615205'
repo='ContextualWisdomLab/.github'

printf '%s\n' '== Live PR identity =='
gh api "repos/${repo}/pulls/830" \
  --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

printf '%s\n' '== Local commit identity =='
git rev-parse "${base}^{commit}"
git rev-parse "${head}^{commit}"
git rev-parse HEAD

printf '%s\n' '== Changed paths =='
git diff --name-status "$base" "$head"

printf '%s\n' '== Complete changed-file diff statistics =='
git diff --stat "$base" "$head"

printf '%s\n' '== OpenCode direct execution sites and guard installation =='
rg -n -C 8 -P '\bopencode\s+(run|export)\b|OPENCODE_REAL_BIN|install_provider_guard|opencode_provider_guard' \
  scripts/ci .github/workflows

printf '%s\n' '== Credential variables in changed execution paths =='
rg -n -C 4 -P 'GITHUB_TOKEN|GH_TOKEN|ACTIONS_(ID_TOKEN|RUNTIME|CACHE|RESULTS)|STRIX_GITHUB_MODELS_TOKEN|OPENAI_API_KEY|NVIDIA(_NIM)?_API_KEY|OPENCODE_' \
  scripts/ci/opencode_provider_guard.sh \
  scripts/ci/run_opencode_review_model_pool.sh \
  scripts/ci/run_opencode_review_model_pool_impl.sh \
  .github/workflows/opencode-review-dispatch.yml

printf '%s\n' '== Policy checker structure =='
ast-grep outline scripts/ci/opencode_private_free_model_policy.py --items all

printf '%s\n' '== Changed policy and runner contracts =='
ast-grep outline tests/test_opencode_private_free_model_policy_1.py --items all
ast-grep outline tests/test_opencode_private_free_model_policy_2.py --items all
ast-grep outline tests/test_opencode_private_free_model_policy_3.py --items all
ast-grep outline tests/test_opencode_private_free_model_runner_contract.py --items all
ast-grep outline tests/test_opencode_provider_guard.py --items all

Length of output: 248306


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Trusted-base policy checker =='
sed -n '1,280p' scripts/ci/opencode_private_free_model_policy.py

printf '%s\n' '== Wrapper policy activation and guard setup =='
sed -n '80,195p' scripts/ci/run_opencode_review_model_pool.sh

printf '%s\n' '== Provider credential guard =='
sed -n '1,140p' scripts/ci/opencode_provider_guard.sh

printf '%s\n' '== Delegated runner: normalization and invocation =='
sed -n '430,540p' scripts/ci/run_opencode_review_model_pool_impl.sh
rg -n -A35 -B10 '^normalize_opencode_output\(\)' scripts/ci/run_opencode_review_model_pool_impl.sh

printf '%s\n' '== Failed-check diagnosis environment and complete function =='
sed -n '4928,4985p' .github/workflows/opencode-review-dispatch.yml
sed -n '6260,6370p' .github/workflows/opencode-review-dispatch.yml

printf '%s\n' '== Documentation candidate list =='
rg -n -C 2 'opencode-free/' docs/doctoring/opencode-private-free-model-policy.md

printf '%s\n' '== Current test assertions for policy, guard, and diagnosis =='
rg -n -C 5 -P 'diagnos|ACTIONS_RUNTIME|ACTIONS_CACHE|ACTIONS_RESULTS|STRIX_GITHUB_MODELS_TOKEN|OPENCODE_FATAL_ERROR_POLL_SECONDS|normalize_opencode_output|policy.*head|head.*policy' \
  tests/test_opencode_private_free_model_policy_*.py \
  tests/test_opencode_private_free_model_runner_contract.py \
  tests/test_opencode_provider_guard.py

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 '{state, base: .base.sha, head: .head.sha, updated_at}'

Length of output: 61114


@seonghobae REQUEST_CHANGES

P1 — Apply credential isolation to failed-check diagnosis

The failed-check diagnosis subprocess bypasses scripts/ci/opencode_provider_guard.sh.

The job environment provides STRIX_GITHUB_MODELS_TOKEN, OPENAI_API_KEY, NVIDIA_API_KEY, and NVIDIA_NIM_API_KEY. The selected model is github-models/deepseek/deepseek-v3-0324. At Line 6332, the workflow runs opencode run directly. At Line 6347, it runs opencode export directly.

The local env -u commands remove GitHub and OIDC credentials. They do not remove OPENAI_API_KEY, NVIDIA_API_KEY, NVIDIA_NIM_API_KEY, ACTIONS_RUNTIME_TOKEN, ACTIONS_CACHE_URL, ACTIONS_RESULTS_URL, or ACTIONS_RUNTIME_URL. The diagnosis subprocess can therefore access unrelated provider and Actions credentials.

Route both commands through the provider guard, or apply the same complete scrub in this path. The run subprocess must retain only STRIX_GITHUB_MODELS_TOKEN. The export subprocess must retain no provider credential.

The trusted-base canonical policy check, self-enablement denial, private negative path, poll interval validation, normalization-probe cleanup, and documentation candidate IDs are correct on the reviewed head. The live base and head remained unchanged during this review. Passing checks do not remove this source-level credential-isolation defect.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 7 minutes.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Fresh full-diff review request for unchanged exact head 9a9b3e061599c28905aed793fc803123a2615205 over protected base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba. The prior organization review-capacity cooldown reported on this PR has elapsed; this is a single retry, not a source-change trigger. Please review the full current diff, with particular attention to trusted-base-only private free-model opt-in, PR self-enablement refusal, and provider/GitHub/Actions credential isolation. Do not treat reviewer-capacity history or missing counted human approval as source findings.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

@seonghobae I will perform a fresh full review of PR #830. I will verify the current base-to-head diff and report only findings that apply to the current head. I will focus on trusted-base-only opt-in, PR self-enablement refusal, credential isolation, and fail-closed behavior.

✅ Action performed

Full review finished.

opencode-agent[bot]
opencode-agent Bot previously approved these changes Aug 10, 2026

@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 reviewed the current-head bounded evidence and found no blocking issues.

Findings

No blocking findings.

Summary

Approval sufficiency: PASS. Changed-file evidence inspected: CHANGELOG.md, docs/doctoring/opencode-private-free-model-policy.md (focused current-head hunks), tests/test_opencode_provider_guard.py and tests/test_opencode_model_pool_runner.py (CodeGraph verbatim current-head source). PR #830 fix(opencode): allow governed free models for private repositories implements the base-only public_equivalent policy (head cannot self-enable external processing), fail-closed visibility and policy validation (40-char SHAs, exactly one 100644 blob via git ls-tree -z, 4096-byte cap, strict UTF-8 without duplicate keys, unchanged trusted-base re-enable path), provider-scoped child credential isolation, and integer runtime/retry/cycle/export validation; every described behavior has a dedicated test file and the full suite passed. Verification posture: repo-native python3 -m pytest tests passed per Coverage execution evidence (Result PASS); coverage gate python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing --fail-under=100 and docstring gate python3 -m interrogate --fail-under=100 --verbose . passed or were advisory per Coverage execution evidence. Linter/static: no failed GitHub Checks at head c001fcb (failed-check-evidence.md: none present); hadolint/bandit/pip_audit/trivy contracts declared with no reported violations. TDD/regression: 7 new/updated test files (test_opencode_provider_guard.py, test_opencode_private_free_model_policy_1/2/3.py, test_opencode_private_free_model_runner_contract.py, test_opencode_delegated_runner_contract.py, test_opencode_model_pool_cycle_sleep_contract.py, plus updates to test_opencode_agent_contract.py and test_opencode_model_pool_runner.py) cover credential absence, fail-closed policy rejection, bounded backoff, positive cycle delay, and log redaction. Coverage: supported repository test suites passed per Coverage execution evidence (Result PASS). Docstring coverage: configured repository docstring gates passed or advisory per Coverage execution evidence. DAG: flowchart (base-to-head changed flow) A['CHANGELOG.md + docs policy']-->B['opencode_private_free_model_policy.py (fail-closed, base-only)']-->C['opencode_provider_guard.sh (credential scope)']-->D['run_opencode_review_model_pool.sh -> run_opencode_review_model_pool_impl.sh (visibility gate)']-->E['tests/* contracts -> coverage/docstring gates']; main risk = private data egress to external free models, mitigated by base-only policy, credential stripping, and fail-closed visibility; verification path = python3 -m pytest tests. PoC/execution: Coverage execution evidence Result PASS (test suites + docstring gates); no browser-tool receipts claimed (non-web CI control plane; no OPENCODE_EXECUTION_RECEIPT browser lines present). DDD/domain: policy eligibility, visibility, credential, and catalog boundaries are explicit domain objects mirrored between docs/doctoring record and scripts. CDD/context: context preserved in docs/doctoring/opencode-private-free-model-policy.md decision record and the CHANGELOG Security section. Similar issues: no unresolved non-outdated threads at current head (Other unresolved review thread evidence: none); historical CodeRabbit findings reconciled - truncated candidate IDs fixed at 1ef9e3a and the current doc lists all seven aliases; stale predecessor-head OpenCode REQUEST_CHANGES reviews tied to failed coverage evidence were dismissed and current-head coverage PASS supersedes them. Claim/concept check: doc claims (base-only policy, no PR self-enablement, credential stripping, fail-closed ls-tree -z single-record parsing, strict JSON without duplicate keys) are consistent with test history and CodeGraph; the external zero-cost OpenCode Zen catalog alias list is a documented runtime-dependent decision that could not be independently verified offline (source limitation, not a repository fact). Standards search: git ls-tree -z single-record parsing, OIDC/Actions credential stripping, and duplicate-key JSON rejection follow documented fail-closed practice; no external official source was reachable (no network). Compatibility/convention: new names are multi-word snake_case (opencode_private_free_model_policy.py, opencode_provider_guard.sh, run_opencode_review_model_pool_impl.sh, DuplicateJsonKey, schema_version, allow_private_free_models, repository_data_classification, external_model_data_use_accepted); no single-word or reserved identifiers introduced; no sequential-id exposure (no DB/API surfaces). Breaking-change/backcompat: wrapper entry run_opencode_review_model_pool.sh retained with logic extracted to run_opencode_review_model_pool_impl.sh; keyed/private fallback pool left unchanged on every denial per docs; delegated-runner and quick-gate contracts preserved by tests. Implementation completeness: full 141-line guard, 269-line policy checker, and 814-line impl script; no placeholder bodies or TODO-only branches observed in inlined hunks and CodeGraph sources. Performance: integer controls validated before shell arithmetic or timeout consumption; bounded exponential backoff (1s retry test) and positive cycle-delay contract proven by tests; CodeGraph blast radius shows RUNNER (wrapper) exercised by 3 test callers. Developer experience: CI/review-comment and operator runbook surface judged via the 7-step operating procedure (including the private negative control), bounded provider-failure metadata with credential redaction, and CHANGELOG entries. User experience: non-web CLI/CI/log surface; provider failures expose only a fixed class and bounded byte counts (tests/test_opencode_model_pool_runner.py verbatim tests). Visual/DOM: no web UI changed; non-web interaction surface (CLI/logs/docs/workflow output) reviewed instead. Accessibility/i18n: no UI surface changed; no i18n impact. Supply-chain/license: no dependency, lockfile, or license changes in this PR; security contracts (npm audit, pip_audit, bandit, trivy) declared with no reported violations. Packaging: unpackaged_source_surfaces is empty; scripts execute under existing repo CI contracts (pyproject requires-python >=3.10; workflows pinned Python 3.10-3.14). Security/privacy: fail-closed visibility probe (private/malformed/timeout -> anonymous candidates removed), base-only policy preventing PR self-enablement, provider-scoped child env stripping GH/Actions/OIDC/runtime/provider credentials for opencode-free, exports, and unknown provider prefixes (test_opencode_provider_guard.py), log redaction of bearer/api/GitHub tokens, strict ls-tree/blob/JSON validation; no secrets or sequential identifiers in the diff.

Approval sufficiency: bounded evidence supplied affirmative approval evidence for changed files, coverage/docstring posture, risk surfaces, and current-head verification; approval is not based merely on the absence of known blockers.
Verification posture: CodeGraph evidence was initialized and bounded current-head evidence reviewed for changed-file evidence including CHANGELOG.md, docs/doctoring/opencode-private-free-model-policy.md, docs/examples/opencode-private-free-models.json, scripts/ci/opencode_private_free_model_policy.py, scripts/ci/opencode_provider_guard.sh, and 12 more.
Linter/static: workflow/static review evidence is bounded by the current-head GitHub Checks gate and changed-file evidence.
TDD/regression: coverage execution evidence and focused changed hunks were reviewed from bounded-review-evidence.md.
Coverage: coverage execution evidence reports supported repository test suites passed.
Docstring coverage: coverage execution evidence reports configured repository docstring gates passed or docstring coverage was advisory.
DAG: CodeGraph/source-backed behavior map connects CHANGELOG.md to the affected review, runtime, or workflow path and required checks.
PoC/execution: coverage-evidence job executed on the current head and reported PASS.
DDD/domain: workflow and repository-governance invariants were reviewed against changed files in bounded evidence.
CDD/context: CodeGraph evidence, changed-file history, and focused hunks were reviewed from bounded-review-evidence.md.
Similar issues: changed-file history evidence was reviewed for comparable local precedents.
Claim/concept check: bounded evidence, repository source, current-head workflow evidence, and, where numeric, scientific, statistical, or literature-backed claims are affected, original-paper/formula evidence and parameter-recovery expectations were used for claims.
Standards search: standards and external-source claims require trusted bounded source evidence prepared outside the isolated model process; no evidence-backed standards blocker is present in bounded evidence.
Compatibility/convention: changed workflow/script conventions, object naming, and reserved-word safety for schema/API/config/code surfaces were checked in bounded evidence.
Breaking-change/backcompat: deployment evidence and changed-file history were checked for backward-compatibility risk.
Performance: changed surfaces were checked for performance risk in bounded evidence.
Developer experience: changed automation, review, test, setup, and maintenance surfaces were checked for helpful or obstructive DX impact in bounded evidence.
User experience: connected user, operator, API, CLI, documentation, review-comment, status-check, rendering, and workflow-reader behavior was checked for contradictions against code, docs, and tests in bounded evidence.
Visual/DOM: deterministic repair does not infer browser runtime execution; source-backed DOM/UI evidence and trusted workflow receipts were reviewed when present, and non-web surfaces used API/CLI/log/docs/workflow evidence instead.
Accessibility/i18n: accessibility, localization, and human-readable text surfaces were checked where UI, CLI, API message, docs, logs, or review text changed.
Supply-chain/license: dependency, package, model, container, and external-tool changes were checked in bounded evidence.
Packaging: package, build, test, lint, and security contracts were checked in bounded evidence.
Security/privacy: workflow-token, review-gate, and repository-automation security/privacy boundaries were checked in bounded evidence.

Adversarial validation

{"status":"passed","probes":[{"path":"tests/test_opencode_provider_guard.py","line":258,"hypothesis":"An anonymous opencode-free/* child process inherits GitHub, Actions OIDC/runtime, or provider credentials and leaks them to an external endpoint.","attack_or_counterexample":"Run the real scripts/ci/opencode_provider_guard.sh with a fake opencode child executable that prints its environment while all 15 credential variables (GH_TOKEN, GITHUB_TOKEN, OPENCODE_APP_TOKEN, ACTIONS_ID_TOKEN_REQUEST_TOKEN/URL, ACTIONS_RUNTIME_TOKEN, ACTIONS_CACHE_URL, ACTIONS_RESULTS_URL, ACTIONS_RUNTIME_URL, STRIX_GITHUB_MODELS_TOKEN, OPENCODE_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY, NVIDIA_API_KEY, NVIDIA_NIM_API_KEY) are present in the parent environment.","evidence":"Coverage execution evidence reports Result PASS - supported repository test suites passed (python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing --fail-under=100); tests/test_opencode_provider_guard.py drives the real guard through the fake_opencode fixture and assert_absent credential checks (CodeGraph verbatim lines 53-92), and the observed suite pass at tests/test_opencode_provider_guard.py:258 means every credential-absence assertion in the file executed without failure; source-line-sha256=8547ae13447681027c25ecda0ad380823f11b3fba5f6fa0df8806a8d0c3068e8","outcome":"falsified"},{"path":"tests/test_opencode_model_pool_cycle_sleep_contract.py","line":35,"hypothesis":"A zero or negative model-pool cycle delay reaches Bash arithmetic or timeout, busy-looping or hanging the review runner.","attack_or_counterexample":"Configure OPENCODE_MODEL_POOL_CYCLE_SECONDS=0 (or negative) and invoke the pool wrapper; separately inject shell substitution into an integer control such as OPENCODE_BACKOFF_INITIAL_SECONDS=SECONDS[$(touch marker)].","evidence":"Coverage execution evidence reports Result PASS (supported repository test suites passed); the positive cycle-delay contract test (history 66f36dcd 'test(opencode): require positive model-pool cycle delay', file ending at line 35) passed, and the verbatim CodeGraph test at tests/test_opencode_model_pool_runner.py:409-425 (test_backoff_environment_rejects_recursive_arithmetic_injection) observed returncode != 0 with the touch-marker file never created, proving malformed integer controls are rejected before shell arithmetic at tests/test_opencode_model_pool_cycle_sleep_contract.py:35; source-line-sha256=8e9568fc6efade940af5280a458f1608eda24cc4c0b03c2b2ef1903c698ed931","outcome":"falsified"},{"path":"scripts/ci/opencode_private_free_model_policy.py","line":269,"hypothesis":"A pull-request head can authorize its own external free-model processing, or malformed/truncated/type-confused policy tree records are accepted by the policy checker.","attack_or_counterexample":"Head adds, removes, renames, chmods, or modifies .github/opencode-private-free-models.json; or the git ls-tree -z response contains truncated or extra records, a non-100644 blob, over-4096-byte content, or JSON with duplicate keys at the fixed path.","evidence":"Coverage execution evidence reports Result PASS (supported repository test suites passed); tests/test_opencode_private_free_model_policy_1.py (history 654c6f06 'reject malformed policy tree records' and dda2e682 'reject type-confused free-model policy', 251 lines) and the runner-contract test history 3405ffaa 'fail closed on private free-pool bypass' passed, so the base-only, exactly-one-100644-blob, strict-UTF-8/JSON validation anchored at scripts/ci/opencode_private_free_model_policy.py:269 rejected the counterexample without enabling the anonymous pool; source-line-sha256=cc79ea1de8f5cf7c4f02e7b43c494b1ae782416b138c209cd6f7b5ff347fc48c","outcome":"falsified"}],"residual_risk":"Direct file reads of /home/runner/work/_temp/opencode-pr-head were blocked by the sandbox permission policy, so script-level conclusions rest on the trusted inlined hunks, CodeGraph verbatim sources, and Coverage/check evidence rather than full-file reads; the external OpenCode Zen zero-cost catalog alias list could not be independently verified offline and remains a documented, runtime-dependent decision whose provider availability continues to be handled as ordinary bounded fallback evidence; no sequential-id, DB, or API surfaces were introduced by this PR."}
  • Result: APPROVE
  • Reason: Approved: the trusted-base fail-closed private free-model policy, provider-scoped credential isolation, and integer-control validation are implemented with passing repository test/coverage/docstring gates, no failed GitHub Checks, and no unresolved threads at head c001fcb.
  • Head SHA: c001fcb7979ed5ceef82f5eeaba97c184cc47d96
  • Workflow run: 31421464284
  • Workflow attempt: 1

Copy link
Copy Markdown
Contributor Author

Exact-current-head independent review request for c001fcb7979ed5ceef82f5eeaba97c184cc47d96 over protected main@6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba.

OpenCode formally approved this exact head, all nine hosted workflows are terminal-success, and all published findings are resolved. Independently review the complete governed private/free-model boundary: canonical immutable-base public_equivalent opt-in; strict type-sensitive 4 KiB regular-blob JSON; exact base/head and real NUL-terminated tree records; head cannot self-enable; private/unknown visibility strips preconfigured anonymous candidates before policy; governed seven-alias catalog only; provider-scoped child environments with GitHub/Actions/OIDC and unrelated provider secrets removed; long/short model-selector parsing with duplicate/missing/terminator rejection; glob-safe candidate handling; and validated positive retry/cycle/runtime controls. The external model catalog remains a separately reviewable operational dependency, and issue #833 still owns post-merge canary/negative-control/rollback acceptance.

Keep the exact head unchanged. Do not synthesize author approval, transfer predecessor reviews, infer eligibility from absent secrets, trust an opencode-free/* prefix as price/privacy evidence, expose private data or credentials, alter reviewer/merge authority, update the branch, merge, release, or bypass protection.

@coderabbitai review
@cwl-noema-review review
@strix review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='ContextualWisdomLab/.github'
base='6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba'
head='c001fcb7979ed5ceef82f5eeaba97c184cc47d96'

printf '%s\n' '== Live PR identity: start =='
gh api "repos/${repo}/pulls/830" --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

printf '%s\n' '== Requested commit objects =='
git rev-parse "${base}^{commit}"
git rev-parse "${head}^{commit}"

printf '%s\n' '== Changed paths =='
git diff --name-status "$base" "$head"

printf '%s\n' '== Source maps =='
ast-grep outline scripts/ci/opencode_private_free_model_policy.py --items all
ast-grep outline scripts/ci/run_opencode_review_model_pool.sh --items all
ast-grep outline scripts/ci/run_opencode_review_model_pool_impl.sh --items all
ast-grep outline scripts/ci/opencode_provider_guard.sh --items all

printf '%s\n' '== Policy checker =='
sed -n '1,300p' scripts/ci/opencode_private_free_model_policy.py

printf '%s\n' '== Wrapper eligibility and guard setup =='
sed -n '1,280p' scripts/ci/run_opencode_review_model_pool.sh

printf '%s\n' '== Provider guard =='
sed -n '1,220p' scripts/ci/opencode_provider_guard.sh

printf '%s\n' '== Implementation: controls, normalization, and execution =='
rg -n -A38 -B12 \
  'env_integer_or_default|normalize_opencode_output|run_one_model_attempt|OPENCODE_FATAL_ERROR_POLL_SECONDS|OPENCODE_POOL_CYCLE_SLEEP_SECONDS|opencode ' \
  scripts/ci/run_opencode_review_model_pool_impl.sh

printf '%s\n' '== Workflow: direct OpenCode calls and diagnosis environment =='
rg -n -C 14 -P \
  '\bopencode\s+(run|export)\b|opencode_provider_guard|STRIX_GITHUB_MODELS_TOKEN|OPENAI_API_KEY|NVIDIA(_NIM)?_API_KEY|GITHUB_TOKEN|GH_TOKEN|ACTIONS_(ID_TOKEN|RUNTIME|CACHE|RESULTS)' \
  .github/workflows/opencode-review-dispatch.yml

printf '%s\n' '== Relevant test coverage =='
rg -n -C 5 -P \
  'self.enable|self-enable|unchanged|canonical|NUL|anonymous_free_candidates|OPENCODE_REPOSITORY_IS_PRIVATE|terminator|duplicate|missing|ACTIONS_RUNTIME|diagnos|cycle_sleep|fatal_poll' \
  tests/test_opencode_private_free_model_policy_*.py \
  tests/test_opencode_private_free_model_runner_contract.py \
  tests/test_opencode_provider_guard.py

printf '%s\n' '== PR checks =='
gh pr checks 830 --repo "$repo" || true

printf '%s\n' '== Live PR identity: end =='
gh api "repos/${repo}/pulls/830" --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

Length of output: 157278


@seonghobae REQUEST_CHANGES

P1 — Apply credential isolation to failed-check diagnosis

The failed-check diagnosis path bypasses scripts/ci/opencode_provider_guard.sh.

At Line 6332, .github/workflows/opencode-review-dispatch.yml invokes opencode run directly. At Line 6347, it invokes opencode export directly. The job environment supplies STRIX_GITHUB_MODELS_TOKEN, OPENAI_API_KEY, NVIDIA_API_KEY, and NVIDIA_NIM_API_KEY.

The local env -u commands remove GitHub, OpenCode App, and OIDC request credentials. They do not remove:

  • OPENAI_API_KEY
  • NVIDIA_API_KEY
  • NVIDIA_NIM_API_KEY
  • ACTIONS_RUNTIME_TOKEN
  • ACTIONS_CACHE_URL
  • ACTIONS_RESULTS_URL
  • ACTIONS_RUNTIME_URL

The diagnosis model is github-models/deepseek/deepseek-v3-0324. Its run subprocess must retain only STRIX_GITHUB_MODELS_TOKEN. Its export subprocess must retain no provider credential. Both subprocesses must receive no GitHub or Actions credentials.

Route both commands through the provider guard, or apply an equivalent complete environment scrub. Add a regression test for this workflow path.

The requested base and head remained 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba and c001fcb7979ed5ceef82f5eeaba97c184cc47d96 before and after this review. I did not modify the branch.


🧠 Learnings used
Learnt from: seonghobae
Repo: ContextualWisdomLab/.github PR: 0
File: :0-0
Timestamp: 2026-08-05T13:44:10.007Z
Learning: For PR `#782`, only successful checks associated with the exact current head are valid merge evidence. Do not treat queued, in-progress, cancelled, predecessor-head, temporary-workflow, or administrative-bypass results as passing evidence. Before a formal approval, verify that the direct security and supply-chain workflows, the permanent hourly NVIDIA NIM contract, current-head reviews, unresolved-thread checks, and an independent approval have completed successfully.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Independent exact-current-head review requested for c001fcb7979ed5ceef82f5eeaba97c184cc47d96 against protected main@6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba.

Fresh evidence: all nine directly observed exact-head workflows are terminal-success, every inline thread is resolved, and OpenCode has submitted a formal exact-head APPROVED review with three falsified adversarial probes. The governed private-free path remains fail-closed: only an immutable protected-base public_equivalent declaration can enable anonymous external processing, stale aliases are filtered, provider credentials are scoped, malformed Git/JSON/control evidence is rejected, and a PR head cannot self-authorize.

A qualifying independent non-author reviewer must still inspect and formally approve this unchanged head. Do not update the branch, broaden eligibility, restore stale model aliases without live catalog evidence, expose credentials, synthesize author approval, or bypass protection. Post-merge operational canary #833 remains a separate acceptance requirement.

@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 3619e6ab0310c4085330e1d1016e3ef654c2f27a.
  • 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["Changed file (3 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (3 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Docs (2 files)"]
  S2 --> I2["operator or user guidance"]
  I2 --> R2["Review risk: Docs (2 files)"]
  R2 --> V2["docs review"]
  Evidence --> S3["CI script (5 files)"]
  S3 --> I3["review and security gate shell path"]
  I3 --> R3["Review risk: CI script (5 files)"]
  R3 --> V3["bash -n plus Strix self-test"]
  Evidence --> S4["Test (10 files)"]
  S4 --> I4["regression suite"]
  I4 --> R4["Review risk: Test (10 files)"]
  R4 --> V4["targeted test run"]
Loading

@opencode-agent
opencode-agent Bot disabled auto-merge August 13, 2026 10:24
@seonghobae
seonghobae enabled auto-merge (squash) August 13, 2026 17:24
@seonghobae

Copy link
Copy Markdown
Contributor Author

@opencode-agent review

Re-evaluate exact current head 74a6868002214c6c20e20109f7944bdd3eb558c4. 리뷰 확인 for the bounded-include increment; do not treat Checks wait as a blocker.

Copy link
Copy Markdown
Contributor Author

Exact-current-head read-only review request for 74a6868002214c6c20e20109f7944bdd3eb558c4 over protected main@6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba.

All ten directly observed exact-head workflows are terminal-success: Trusted uv Materializer Quality CI, Strix Changed Path Quality CI, CodeQL PR, Python Security, Security Scan, SAST Semgrep, Secret Scan, OSV-Scanner PR, Scorecard PR, and SBOM Generation. Every published inline thread is resolved. The active CHANGES_REQUESTED review cites a failed Strix run on predecessor head 3619e6ab...; it does not transfer to this unchanged green head. The earlier approval belongs to c001fcb7....

Review the unchanged current head only. Preserve immutable-base-only private opt-in, strict public-equivalent classification, exact NUL-terminated tree parsing, current governed free catalog, provider-scoped credential removal, validated model selectors/runtime controls, and fail-closed unknown visibility. Do not mutate the branch, infer eligibility from candidate text or secret absence, synthesize approval, or bypass protection.

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

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='ContextualWisdomLab/.github'
base='6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba'
head='74a6868002214c6c20e20109f7944bdd3eb558c4'

printf '%s\n' '== Live PR identity: start =='
gh api "repos/${repo}/pulls/830" --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

printf '%s\n' '== Requested commit objects =='
git rev-parse "${base}^{commit}"
git rev-parse "${head}^{commit}"

printf '%s\n' '== Base-to-head changed paths =='
git diff --name-status "$base" "$head"

printf '%s\n' '== Changed-path summary =='
git diff --stat "$base" "$head"

printf '%s\n' '== Current checks =='
gh pr checks 830 --repo "$repo" || true

printf '%s\n' '== Relevant source outlines =='
ast-grep outline scripts/ci/opencode_private_free_model_policy.py --items all
ast-grep outline scripts/ci/run_opencode_review_model_pool.sh --items all
ast-grep outline scripts/ci/run_opencode_review_model_pool_impl.sh --items all
ast-grep outline scripts/ci/opencode_provider_guard.sh --items all

printf '%s\n' '== Policy checker =='
sed -n '1,330p' scripts/ci/opencode_private_free_model_policy.py

printf '%s\n' '== Governance wrapper =='
sed -n '1,320p' scripts/ci/run_opencode_review_model_pool.sh

printf '%s\n' '== Provider guard =='
sed -n '1,260p' scripts/ci/opencode_provider_guard.sh

printf '%s\n' '== Implementation controls and OpenCode execution =='
rg -n -A55 -B14 -P \
  'env_integer_or_default|normalize_opencode_output|run_one_model_attempt|OPENCODE_(FATAL_ERROR_POLL_SECONDS|POOL_CYCLE_SLEEP_SECONDS)|\bopencode\s+(run|export)\b|OPENCODE_REAL_BIN' \
  scripts/ci/run_opencode_review_model_pool_impl.sh

printf '%s\n' '== All direct OpenCode execution sites =='
rg -n -C 12 -P '\bopencode\s+(run|export)\b|opencode_provider_guard|OPENCODE_REAL_BIN' \
  scripts/ci .github/workflows

printf '%s\n' '== Credential-bearing environment and scrubbing paths =='
rg -n -C 6 -P \
  'STRIX_GITHUB_MODELS_TOKEN|OPENAI_API_KEY|NVIDIA(_NIM)?_API_KEY|GITHUB_TOKEN|GH_TOKEN|ACTIONS_(ID_TOKEN|RUNTIME|CACHE|RESULTS)|ACTIONS_RESULTS_URL' \
  scripts/ci .github/workflows/opencode-review-dispatch.yml

printf '%s\n' '== Relevant tests and contracts =='
rg -n -C 5 -P \
  'self.enable|self-enable|unchanged|canonical|NUL|anonymous_free_candidates|OPENCODE_REPOSITORY_IS_PRIVATE|terminator|duplicate|missing|fatal_poll|cycle_sleep|diagnos|ACTIONS_RUNTIME|OPENCODE_TOTAL_RETRY_BUDGET_SECONDS' \
  tests/test_opencode_private_free_model_policy_*.py \
  tests/test_opencode_private_free_model_runner_contract.py \
  tests/test_opencode_provider_guard.py \
  tests/test_opencode_agent_contract.py

printf '%s\n' '== Candidate catalog references =='
rg -n -C 2 'opencode-free/' scripts/ci docs tests .github/workflows/opencode-review-dispatch.yml

printf '%s\n' '== Live PR identity: end =='
gh api "repos/${repo}/pulls/830" --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

Length of output: 203015


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba'
previous='c001fcb7979ed5ceef82f5eeaba97c184cc47d96'
head='74a6868002214c6c20e20109f7944bdd3eb558c4'
repo='ContextualWisdomLab/.github'

printf '%s\n' '== Increment from prior reviewed head =='
git diff --name-status "$previous" "$head"
git diff --unified=60 "$previous" "$head" -- \
  .github/workflows/opencode-review-dispatch.yml \
  scripts/ci/opencode_provider_guard.sh \
  scripts/ci/run_opencode_review_model_pool.sh \
  scripts/ci/run_opencode_review_model_pool_impl.sh \
  tests/test_opencode_provider_guard.py \
  tests/test_opencode_private_free_model_runner_contract.py \
  tests/test_opencode_agent_contract.py

printf '%s\n' '== Exact failed-check diagnosis implementation =='
sed -n '6240,6385p' .github/workflows/opencode-review-dispatch.yml

printf '%s\n' '== Exact provider guard implementation =='
cat -n scripts/ci/opencode_provider_guard.sh

printf '%s\n' '== Wrapper eligibility, candidate filtering, and delegation =='
sed -n '1,285p' scripts/ci/run_opencode_review_model_pool.sh

printf '%s\n' '== Runtime normalization and cycle/poll controls =='
sed -n '1,115p;445,555p;760,850p' scripts/ci/run_opencode_review_model_pool_impl.sh

printf '%s\n' '== Diagnosis and provider-guard regression coverage =='
rg -n -C 10 -P \
  'diagnos|failed-check|opencode_provider_guard|ACTIONS_(RUNTIME|CACHE|RESULTS)|OPENAI_API_KEY|NVIDIA(_NIM)?_API_KEY|STRIX_GITHUB_MODELS_TOKEN|--model --|-m=|terminator' \
  tests

printf '%s\n' '== Approval authority contract =='
sed -n '7315,7385p;7940,8005p' .github/workflows/opencode-review-dispatch.yml
sed -n '1,280p' scripts/ci/opencode_existing_approval_gate.py

printf '%s\n' '== Live PR identity: final =='
gh api "repos/${repo}/pulls/830" --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

Length of output: 124317


@seonghobae REQUEST_CHANGES

P1 — Isolate credentials in the failed-check diagnosis subprocesses

At Line 6330, .github/workflows/opencode-review-dispatch.yml starts the diagnosis opencode run command directly. At Line 6345, it starts opencode export directly.

These commands unset the GitHub token, OPENCODE_APP_TOKEN, and OIDC request variables. They do not unset these credentials from the job environment:

  • OPENAI_API_KEY
  • NVIDIA_API_KEY
  • NVIDIA_NIM_API_KEY
  • ACTIONS_RUNTIME_TOKEN
  • ACTIONS_CACHE_URL
  • ACTIONS_RESULTS_URL
  • ACTIONS_RUNTIME_URL

The selected diagnosis model is github-models/deepseek/deepseek-v3-0324. Its run subprocess must receive only STRIX_GITHUB_MODELS_TOKEN. Its export subprocess must receive no provider credential. Both subprocesses must receive no GitHub or Actions credentials.

Route both commands through scripts/ci/opencode_provider_guard.sh, or apply an equivalent complete environment scrub. Add a regression test for this workflow path. The existing provider-guard tests define the required isolation boundary.

The policy checker, private and unknown visibility filtering, governed catalog filtering, selector rejection, poll and cycle control validation, and normalization-probe cleanup are correct in the reviewed source.

The live PR remained on base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba and head 74a6868002214c6c20e20109f7944bdd3eb558c4 before and after review. I did not modify the branch, approve the PR, or infer approval from successful checks.

I applied the existing exact-current-head evidence requirement from the retrieved project guidance.


🧠 Learnings used
Learnt from: seonghobae
Repo: ContextualWisdomLab/.github PR: 0
File: :0-0
Timestamp: 2026-08-14T08:06:07.458Z
Learning: For ContextualWisdomLab/.github PR `#789`, a cancelled security or supply-chain check does not count as evidence. A manually re-run replacement check remains pending until it reaches terminal success on the exact current PR head. Predecessor-head check results, findings, and approvals do not transfer.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

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

Review only exact current head 74a6868002214c6c20e20109f7944bdd3eb558c4 against live protected main. All 10 directly observed current-head hosted workflows are terminal-success and every review thread is resolved. I independently rechecked the current official OpenCode Zen catalog before this request: the governed seven-candidate free allowlist on this branch still matches the current official free list (Big Pickle, DeepSeek V4 Flash Free, MiMo-V2.5 Free, Laguna S 2.1 Free, Ling-3.0-flash Free, North Mini Code Free, Nemotron 3 Ultra Free). Earlier approvals/change requests are predecessor-head evidence only. Review read-only; do not mutate source, synthesize author approval, weaken protection, or merge.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='ContextualWisdomLab/.github'
base='6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba'
head='74a6868002214c6c20e20109f7944bdd3eb558c4'

printf '%s\n' '== Live PR identity: start =='
gh api "repos/${repo}/pulls/830" \
  --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

printf '%s\n' '== Requested commits =='
git rev-parse "${base}^{commit}"
git rev-parse "${head}^{commit}"
git rev-parse HEAD

printf '%s\n' '== Changed paths =='
git diff --name-status "$base" "$head"

printf '%s\n' '== Changed paths statistics =='
git diff --stat "$base" "$head"

printf '%s\n' '== Relevant source outlines =='
ast-grep outline scripts/ci/opencode_private_free_model_policy.py --items all
ast-grep outline scripts/ci/run_opencode_review_model_pool.sh --items all
ast-grep outline scripts/ci/run_opencode_review_model_pool_impl.sh --items all
ast-grep outline scripts/ci/opencode_provider_guard.sh --items all

printf '%s\n' '== Direct OpenCode execution and credential boundaries =='
rg -n -C 10 -P \
  '\bopencode\s+(run|export)\b|opencode_provider_guard|OPENCODE_REAL_BIN|STRIX_GITHUB_MODELS_TOKEN|OPENAI_API_KEY|NVIDIA(_NIM)?_API_KEY|GITHUB_TOKEN|GH_TOKEN|ACTIONS_(ID_TOKEN|RUNTIME|CACHE|RESULTS)' \
  scripts/ci .github/workflows

printf '%s\n' '== Policy, eligibility, selector, and runtime-control definitions =='
rg -n -C 8 -P \
  'evaluate_policy|require_policy_unchanged|policy_blob_entry|anonymous_free_candidates|OPENCODE_REPOSITORY_IS_PRIVATE|env_integer_or_default|OPENCODE_(FATAL_ERROR_POLL_SECONDS|POOL_CYCLE_SLEEP_SECONDS)|normalize_opencode_output' \
  scripts/ci

printf '%s\n' '== Related regression coverage =='
rg -n -C 7 -P \
  'diagnos|ACTIONS_(RUNTIME|CACHE|RESULTS)|opencode_provider_guard|self-enable|self_enable|NUL|terminator|fatal_poll|cycle_sleep|anonymous_free_candidates' \
  tests

printf '%s\n' '== Live PR identity: end =='
gh api "repos/${repo}/pulls/830" \
  --jq '{state, base: .base.sha, head: .head.sha, updated_at}'

Length of output: 202183


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='ContextualWisdomLab/.github'
base='6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba'
head='74a6868002214c6c20e20109f7944bdd3eb558c4'

printf '%s\n' '== Live PR identity: focused inspection start =='
gh api "repos/${repo}/pulls/830" \
  --jq '{base: .base.sha, head: .head.sha, state, updated_at}'

printf '%s\n' '== Provider guard: complete implementation =='
cat -n scripts/ci/opencode_provider_guard.sh

printf '%s\n' '== Wrapper: guard installation and delegation scope =='
sed -n '1,330p' scripts/ci/run_opencode_review_model_pool.sh | cat -n

printf '%s\n' '== Failed-check diagnosis command block =='
sed -n '6260,6370p' .github/workflows/opencode-review-dispatch.yml | cat -n

printf '%s\n' '== Approval-gate job environment =='
sed -n '4928,5005p' .github/workflows/opencode-review-dispatch.yml | cat -n

printf '%s\n' '== Persistent PATH and guard references in the workflow =='
rg -n -C 4 -P \
  'GITHUB_PATH|PATH=.*opencode|install_provider_guard|opencode_provider_guard|OPENCODE_REAL_BIN' \
  .github/workflows/opencode-review-dispatch.yml \
  scripts/ci/run_opencode_review_model_pool.sh \
  scripts/ci/run_opencode_review_model_pool_impl.sh

printf '%s\n' '== Diagnosis-specific regression coverage =='
rg -n -C 8 -i \
  'failed-check diagnosis|failed_check_diagnosis|opencode export.*diagnos|diagnos.*ACTIONS_RUNTIME|diagnos.*OPENAI_API_KEY|diagnos.*provider.guard' \
  tests

printf '%s\n' '== Full test references to the diagnosis command block =='
rg -n -C 5 -P \
  'OPENCODE_RUN_TIMEOUT_SECONDS: "120"|MODEL: github-models/deepseek/deepseek-v3-0324|opencode run "\$\(cat "\$prompt_file"\)"|OPENCODE_EXPORT_TIMEOUT_SECONDS' \
  tests/test_opencode_agent_contract.py tests

printf '%s\n' '== Live PR identity: focused inspection end =='
gh api "repos/${repo}/pulls/830" \
  --jq '{base: .base.sha, head: .head.sha, state, updated_at}'

Length of output: 53866


@seonghobae REQUEST_CHANGES

P1 — Isolate the failed-check diagnosis subprocesses

At Line 6330, .github/workflows/opencode-review-dispatch.yml invokes opencode run directly. At Line 6345, it invokes opencode export directly.

The approval-gate job defines these credentials:

  • STRIX_GITHUB_MODELS_TOKEN
  • OPENAI_API_KEY
  • NVIDIA_API_KEY
  • NVIDIA_NIM_API_KEY

The local env -u commands remove GitHub, OpenCode App, and OIDC request credentials. They do not remove:

  • OPENAI_API_KEY
  • NVIDIA_API_KEY
  • NVIDIA_NIM_API_KEY
  • ACTIONS_RUNTIME_TOKEN
  • ACTIONS_CACHE_URL
  • ACTIONS_RESULTS_URL
  • ACTIONS_RUNTIME_URL

The existing scripts/ci/opencode_provider_guard.sh removes this full set. However, scripts/ci/run_opencode_review_model_pool.sh installs that guard only for its own process. The workflow does not persist the temporary guard path through GITHUB_PATH. The later diagnosis commands therefore bypass the guard.

Route both diagnosis commands through scripts/ci/opencode_provider_guard.sh, or apply an equivalent complete environment scrub. For github-models/deepseek/deepseek-v3-0324, run must retain only STRIX_GITHUB_MODELS_TOKEN. export must retain no provider credential. Both commands must receive no GitHub or Actions credentials.

Add a regression test for this workflow path. The current guard tests define the required child-environment boundary.

I verified the live PR before and after review. It remained on base 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba and head 74a6868002214c6c20e20109f7944bdd3eb558c4. I did not modify, approve, or merge the PR.

You are interacting with an AI system.

Do not replace the current model-pool implementation. Source a thin
hook that opt-in enables governed anonymous free candidates from an
unchanged trusted-base policy and wraps opencode with a provider
credential guard. Unit tests that pass candidates directly are left
alone unless PR_BASE_SHA or visibility is set.
@seonghobae
seonghobae force-pushed the fix/opencode-private-free-opt-in-20260808 branch from 74a6868 to f449a8d Compare August 16, 2026 15:58
@seonghobae
seonghobae enabled auto-merge (squash) August 16, 2026 15:58
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Current-head review of f449a8de: the trusted-base policy checker, provider-scoped credential guard, and hook-on-live-runner shape are the right control-plane cut. Do not merge this head.

The live OpenCode runner already has a trusted privacy signal (validate-pr-metadata.outputs.is_private) and never exports it as OPENCODE_REPOSITORY_IS_PRIVATE. Public free-pool eligibility then depends on a credential-free git ls-remote. A timeout or transport failure treats a public repository as private, strips governed opencode-free/* aliases, and misses the documented trusted-caller path.

The hook contract only asserts that the runner sources the hook. It does not execute public catalog filtering, private preconfigured-free removal, or eligible-base prepend. Those are the buyer-visible behaviors this PR claims.

Land #1046 instead. That successor wires the trusted visibility signal, adds hook-level contracts, and protects .github/opencode-private-free-models.json in CODEOWNERS. Do not add the policy file on this PR; a head cannot opt itself in. After the successor merges, use #833 for the private canary.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment on lines +97 to +107
repository_visibility_is_public() {
case "${OPENCODE_REPOSITORY_IS_PRIVATE:-}" in
false)
return 0
;;
true)
return 1
;;
"")
source_repository_is_public_without_credentials
return $?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The documented trusted-caller path is OPENCODE_REPOSITORY_IS_PRIVATE=false, but the live model-pool step never sets this variable. Production therefore falls through to the credential-free ls-remote probe for every public review.

Pass needs.validate-pr-metadata.outputs.is_private into the runner. Keep ls-remote only as a fallback when that trusted signal is absent. A timeout on a public ContextualWisdomLab origin must not be the only way to keep the governed free catalog.

Comment on lines +193 to +199
# Unit tests pass OPENCODE_MODEL_CANDIDATES directly. Do not strip free
# aliases unless the review workflow supplied visibility or a base SHA.
if [ -z "${OPENCODE_REPOSITORY_IS_PRIVATE:-}" ] &&
[ -z "${PR_BASE_SHA:-}" ]; then
trap cleanup_provider_guard EXIT INT TERM
install_provider_guard
return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This escape is required for local model-pool unit tests that pass OPENCODE_MODEL_CANDIDATES directly. It is safe only if every live review sets visibility or PR_BASE_SHA.

The live workflow sets PR_BASE_SHA today, so this return does not fire in production. Still lock the production contract: the runner must export trusted OPENCODE_REPOSITORY_IS_PRIVATE next to PR_BASE_SHA, and a test must fail if either signal disappears from the live env.

Comment on lines +18 to +24
assert "opencode_private_free_model_hook.sh" in runner
assert "apply_private_free_model_policy" in runner
assert "maybe_enable_private_free_models" in hook
assert "install_provider_guard" in hook
assert POLICY.is_file()
assert GUARD.is_file()
assert "run_opencode_review_model_pool_impl.sh" not in runner

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These assertions only prove the runner sources the hook. They do not execute the security properties the PR describes.

Add hook-level runs that assert:

  • public visibility keeps governed aliases and drops stale opencode-free/* names;
  • private visibility with no base policy strips every preconfigured free alias;
  • an unchanged eligible base policy prepends the governed catalog;
  • malformed visibility fails closed to the policy path.

String presence is not evidence that a private caller cannot bypass the immutable-base gate.

@opencode-agent
opencode-agent Bot disabled auto-merge August 16, 2026 16:08
@seonghobae
seonghobae enabled auto-merge (squash) August 16, 2026 16:11
Export validate-pr-metadata is_private as OPENCODE_REPOSITORY_IS_PRIVATE
next to PR_BASE_SHA so public reviews keep the governed free catalog
without a credential-free ls-remote probe. Keep ls-remote as fallback
only when that trusted signal is absent. Add hook-level visibility
contracts and pin the updated review-dispatch blob.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict

Do not merge #830 at 943b8040.

The trusted visibility export is correct: public reviews no longer depend on a credential-free ls-remote. Two source defects remain on this head.

  1. apply_private_free_model_policy still returns early when visibility and PR_BASE_SHA are both unset, so a missing production export keeps every preconfigured opencode-free/* alias.
  2. The provider guard does not unset COPILOT_GITHUB_TOKEN.

Next action

Land #1055 instead of this head. #1055 removes the unit-test exemption, strips COPILOT_GITHUB_TOKEN, and keeps local runner fixtures on an explicit OPENCODE_REPOSITORY_IS_PRIVATE=false. After that merge, keep #833 as the operational canary. Do not add .github/opencode-private-free-models.json on the checker PR.

Draft #1046 is the previous visibility-wire successor and is not the landing vehicle.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment on lines +193 to +199
# Unit tests pass OPENCODE_MODEL_CANDIDATES directly. Do not strip free
# aliases unless the review workflow supplied visibility or a base SHA.
if [ -z "${OPENCODE_REPOSITORY_IS_PRIVATE:-}" ] &&
[ -z "${PR_BASE_SHA:-}" ]; then
trap cleanup_provider_guard EXIT INT TERM
install_provider_guard
return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This early return is fail-open. When both OPENCODE_REPOSITORY_IS_PRIVATE and PR_BASE_SHA are unset, every preconfigured opencode-free/* alias stays in the pool. A missing production export therefore authorizes anonymous egress without a public probe or trusted-base policy.

Remove the exemption. Missing both signals must strip anonymous aliases. Local runner fixtures that need the public catalog should set OPENCODE_REPOSITORY_IS_PRIVATE=false.

The repair is on #1055.

env
-u GH_TOKEN
-u GITHUB_TOKEN
-u OPENCODE_APP_TOKEN

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The guard already drops GH_TOKEN and GITHUB_TOKEN, but a COPILOT_GITHUB_TOKEN in the runner environment still reaches anonymous and keyed OpenCode children. Unset it with the other GitHub tokens. #1055 does that.

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