Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,11 @@ This section applies to any agent (Claude, Codex, Cursor, opencode, ...) working
- Do not justify risky debug defaults as temporary shortcuts.
- Do not defer dependency review, SBOM generation, or supply-chain checks to a later phase.
- Do not defer Windows or macOS build enforcement to a later phase.

## Local OpenCode contract
- Root `opencode.jsonc` is NVIDIA NIM only: `enabled_providers` is `["nvidia-nim"]`.
- The provider binds `{env:NVIDIA_API_KEY}` to `https://integrate.api.nvidia.com/v1`. That env name is the NVIDIA/OpenCode client contract.
- The organization GitHub secret is `NVIDIA_NIM_API_KEY`. CI maps that secret onto process env `NVIDIA_API_KEY`. Do not rename the local OpenCode binding to `{env:NVIDIA_NIM_API_KEY}`.
- Never introduce `COPILOT_GITHUB_TOKEN`, `github-models`, or `STRIX_GITHUB_MODELS_TOKEN` in `opencode.jsonc`.
- Do not set OpenAI-style `reasoningEffort` on `nvidia/llama-3.3-nemotron-super-49b-v1.5`. NIM uses `chat_template_kwargs` for that model (see `docs/doctoring/opencode-nvidia-nim-contract.md`).
- Do not change existing review-agent credential names or routing. Central OpenCode Review and the PR review/merge scheduler stay in `ContextualWisdomLab/.github`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ Supporting packages:
- Lockfiles (`package-lock.json`, `uv.lock`, `Cargo.lock`) are committed and must stay in sync; GitHub Actions are SHA-pinned. Adding a direct dependency requires the admission rationale defined in `AGENTS.md` and `docs/security/dependency-policy.md`.
- CI beyond quickcheck: `gate / ci / rust-check` (Tauri cargo check on macOS) and `build-baseline` Windows/macOS amd64+arm64 native builds are merge gates, alongside CodeQL, dependency-review, sbom, bandit, trivy, secret-scan, and security-audit workflows. Do not weaken or skip them.
- Version metadata lives in `VERSION`, the root `package.json`, and `CHANGELOG.md`; release flow is tag-driven (see `docs/operations/deploy-runbook.md`).
- Local developer OpenCode is NVIDIA NIM only. The binding, secret split, leftover-token deny-list, and `reasoningEffort` ban live in `AGENTS.md` (`Local OpenCode contract`) and `docs/doctoring/opencode-nvidia-nim-contract.md`.
53 changes: 53 additions & 0 deletions docs/doctoring/opencode-nvidia-nim-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Local OpenCode NVIDIA NIM contract

Next action: keep root `opencode.jsonc` on NVIDIA NIM only. Bind `{env:NVIDIA_API_KEY}` to
`https://integrate.api.nvidia.com/v1`. Do not rename that local binding to the organization
secret name `NVIDIA_NIM_API_KEY`. Do not put OpenAI-style `reasoningEffort` anywhere in the
local OpenCode configuration.

## Why this lock exists

Local developer OpenCode is a separate trust boundary from central OpenCode Review and the
PR review/merge scheduler in `ContextualWisdomLab/.github`. Those workflows keep their own
credential names. This repository only records the local client allowlist.

NVIDIA NIM reasoning models use `chat_template_kwargs` (for example `enable_thinking`) rather
than the OpenAI `reasoning_effort` field. NVIDIA documents `reasoning_effort` as a Chat
Completions knob for GPT-OSS models on multi-LLM NIM, not for Llama 3.3 Nemotron Super 49B
v1.5 (NVIDIA, n.d.-a; NVIDIA, n.d.-b). Forwarding `reasoningEffort` from OpenCode can be
ignored or rejected. The contract therefore forbids that OpenCode option across the whole
repository config instead of checking only one model's nested `options` object.

The organization GitHub secret remains `NVIDIA_NIM_API_KEY`. CI maps that secret onto process
env `NVIDIA_API_KEY` because that is the NVIDIA/OpenCode client binding.

## Held values

- `enabled_providers`: `["nvidia-nim"]`
- default model: `nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5`
- `small_model`: `nvidia-nim/meta/llama-3.3-70b-instruct`
- `apiKey`: `{env:NVIDIA_API_KEY}`
- leftover deny-list: `github-models`, `STRIX_GITHUB_MODELS_TOKEN`, `COPILOT_GITHUB_TOKEN`,
`openai/gpt-5`, `openai/o3`, `openai/o4-mini`, `models.github.ai`

## References

NVIDIA. (n.d.-a). *Use reasoning models with NVIDIA NIM for LLMs*.
https://docs.nvidia.com/nim/large-language-models/latest/reasoning-model.html

NVIDIA. (n.d.-b). *nvidia / llama-3.3-nemotron-super-49b-v1.5*.
https://docs.api.nvidia.com/nim/reference/nvidia-llama-3_3-nemotron-super-49b-v1_5

## Security Notes

- Attack surface: local OpenCode HTTPS calls to `https://integrate.api.nvidia.com/v1` using a
process-env API key.
- Trust boundary: untrusted prompts and repository contents sent to the provider; trusted
repo-controlled provider allowlist; secret name `NVIDIA_NIM_API_KEY` stays in GitHub and is
mapped onto `NVIDIA_API_KEY` only at process start.
- Mitigations: single enabled provider, no GitHub Models or Copilot token fallback, leftover
string deny-list, and explicit whole-config bans on `reasoningEffort` and
`{env:NVIDIA_NIM_API_KEY}` inside `opencode.jsonc`.
- Test points: `test_opencode_uses_nvidia_nim_only` owns the structured provider/model/secret
contract; `test_opencode_forbids_reasoning_effort_anywhere` independently prevents the
forbidden OpenAI-style option from being pasted at any config level.
15 changes: 11 additions & 4 deletions docs/workflow/pr-review-merge-scheduler.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ workflow `GITHUB_TOKEN`, depending on which credential can perform the guarded r
The local repository may keep product CI, security, release, and build workflows. It must not restore
repo-local copies of `opencode-review.yml`, `pr-review-merge-scheduler.yml`, or their `scripts/ci` helper implementations.

Local developer OpenCode (`opencode.jsonc`) is a separate trust boundary from those central
review workflows. It uses NVIDIA NIM only, binds `{env:NVIDIA_API_KEY}` at
`https://integrate.api.nvidia.com/v1`, and must not restore GitHub Models, Copilot tokens, or
review-agent secrets. The organization secret name remains `NVIDIA_NIM_API_KEY`; CI maps that
secret onto `NVIDIA_API_KEY` for the OpenCode client.

## Behavior

- Inspect non-draft PRs targeting the repository default branch, currently `develop`.
Expand Down Expand Up @@ -44,14 +50,15 @@ repo-local copies of `opencode-review.yml`, `pr-review-merge-scheduler.yml`, or

## Security Notes

- Attack surface: organization required workflows with write access to PR comments, PR branch updates, and normal merges.
- Trust boundary touched: GitHub repository governance, PR review state, status checks, and CodeRabbit review requests.
- Attack surface: organization required workflows with write access to PR comments, PR branch updates, and normal merges. Local developer OpenCode (`opencode.jsonc`) is a separate HTTPS client to NVIDIA NIM and is not invoked by this scheduler.
- Trust boundary touched: GitHub repository governance, PR review state, status checks, and CodeRabbit review requests. The local OpenCode provider allowlist (`nvidia-nim` + `{env:NVIDIA_API_KEY}`) is recorded here so agents do not restore GitHub Models or review-agent secrets into this repository.
- Realistic threats: spammed review comments, merging a PR with unresolved conversations, merging without required checks, or hiding conflicts behind automation.
- Mitigations: central required workflow source pinning, idempotent per-head review comment marker,
explicit unresolved-thread check, retry-bounded GitHub API reads, required-check verification
through GitHub, conflict skip, guarded merge with `--match-head-commit`, and no admin bypass path.
- Remaining risk: CodeRabbit and GitHub check state can be delayed or stale; the scheduler therefore only advances eligible PRs and leaves code-fix work to agents or maintainers.
- Test points: organization ruleset inheritance, current-head OpenCode approval, unresolved review
thread count, required-check rollup, approved behind PR, approved conflict-free PR, approved dirty PR,
external failed-check classification, provider/runtime failure summary, and Strix evidence lookup
scope diagnostics.
external failed-check classification, provider/runtime failure summary, Strix evidence lookup
scope diagnostics, and the separate local `opencode.jsonc` NIM-only contract (`small_model`,
`{env:NVIDIA_API_KEY}`, no `reasoningEffort`, no GitHub Models / Copilot leftovers).
57 changes: 15 additions & 42 deletions opencode.jsonc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://opencode.ai/config.json",
"model": "github-models/openai/gpt-5",
"small_model": "github-models/deepseek/deepseek-v3-0324",
"enabled_providers": ["github-models"],
"model": "nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5",
"small_model": "nvidia-nim/meta/llama-3.3-70b-instruct",
"enabled_providers": ["nvidia-nim"],
"mcp": {
"codegraph": {
"type": "local",
Expand Down Expand Up @@ -37,56 +37,29 @@
}
},
"provider": {
"github-models": {
"nvidia-nim": {
"npm": "@ai-sdk/openai-compatible",
"name": "GitHub Models",
"name": "NVIDIA NIM",
"options": {
"baseURL": "https://models.github.ai/inference",
"apiKey": "{env:STRIX_GITHUB_MODELS_TOKEN}"
"baseURL": "https://integrate.api.nvidia.com/v1",
"apiKey": "{env:NVIDIA_API_KEY}"
},
"models": {
"openai/gpt-5": {
"name": "OpenAI GPT-5",
"nvidia/llama-3.3-nemotron-super-49b-v1.5": {
"name": "NVIDIA Llama 3.3 Nemotron Super 49B v1.5",
"tool_call": true,
"reasoning": true,
"limit": {
"context": 200000,
"output": 100000
"context": 131072,
"output": 32768
}
},
"deepseek/deepseek-r1-0528": {
"name": "DeepSeek R1 0528",
"meta/llama-3.3-70b-instruct": {
"name": "Meta Llama 3.3 70B Instruct",
"tool_call": true,
"reasoning": true,
"limit": {
"context": 128000,
"output": 4096
}
},
"deepseek/deepseek-v3-0324": {
"name": "DeepSeek V3 0324",
"tool_call": true,
"limit": {
"context": 128000,
"output": 4096
}
},
"openai/o3": {
"name": "OpenAI o3",
"tool_call": true,
"reasoning": true,
"limit": {
"context": 200000,
"output": 100000
}
},
"openai/o4-mini": {
"name": "OpenAI o4-mini",
"tool_call": true,
"reasoning": true,
"limit": {
"context": 200000,
"output": 100000
"context": 131072,
"output": 16384
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Focused regression for forbidden OpenCode reasoning options."""

from __future__ import annotations

from pathlib import Path


def test_opencode_forbids_reasoning_effort_anywhere() -> None:
"""Reject OpenAI-style reasoningEffort anywhere in the local NIM config."""
repo_root = Path(__file__).resolve().parents[3]
opencode_text = (repo_root / "opencode.jsonc").read_text(encoding="utf-8")

assert '"reasoningEffort"' not in opencode_text
38 changes: 34 additions & 4 deletions services/analysis-engine/tests/test_supply_chain_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5073,14 +5073,44 @@ def test_opencode_approval_write_failure_updates_overview_only() -> None:
assert "source-backed repository findings" in policy


def test_opencode_uses_nvidia_nim_only() -> None:
"""Ensure local OpenCode is NVIDIA NIM only and does not use GitHub Models."""
repo_root = Path(__file__).resolve().parents[3]
opencode_text = (repo_root / "opencode.jsonc").read_text(encoding="utf-8")
opencode_config = json.loads(opencode_text)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: json.loads on .jsonc breaks if comments are added

test_opencode_uses_nvidia_nim_only parses opencode.jsonc with json.loads (services/analysis-engine/tests/test_supply_chain_policy.py:5080). The .jsonc format allows comments and trailing commas; the file currently has none. Adding either later would fail parsing with a decode error rather than a clear contract assertion.

Open in Devin Review

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


assert opencode_config["model"] == "nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5"
assert opencode_config["small_model"] == "nvidia-nim/meta/llama-3.3-70b-instruct"
assert opencode_config["enabled_providers"] == ["nvidia-nim"]
assert set(opencode_config["provider"]) == {"nvidia-nim"}

nim_provider = opencode_config["provider"]["nvidia-nim"]
assert nim_provider["options"]["baseURL"] == "https://integrate.api.nvidia.com/v1"
assert nim_provider["options"]["apiKey"] == "{env:NVIDIA_API_KEY}"
assert "nvidia/llama-3.3-nemotron-super-49b-v1.5" in nim_provider["models"]
assert "meta/llama-3.3-70b-instruct" in nim_provider["models"]

primary_model = nim_provider["models"]["nvidia/llama-3.3-nemotron-super-49b-v1.5"]
assert "reasoningEffort" not in primary_model.get("options", {})
assert "{env:NVIDIA_NIM_API_KEY}" not in opencode_text
Comment on lines +5076 to +5095

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: opencode.jsonc parses as strict JSON

test_opencode_uses_nvidia_nim_only calls json.loads on opencode.jsonc. Despite the .jsonc extension the file has no comments or trailing commas, so strict parsing succeeds. json is imported and parents[3] resolves to the repo root.

Open in Devin Review

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


leftover_tokens = (
"github-models",
"STRIX_GITHUB_MODELS_TOKEN",
"COPILOT_GITHUB_TOKEN",
"openai/gpt-5",
"openai/o3",
"openai/o4-mini",
"models.github.ai",
)
for leftover in leftover_tokens:
assert leftover not in opencode_text


def test_pr_review_merge_scheduler_uses_central_mutation_credential() -> None:
"""Ensure mechanical PR queue handling uses the central mutation credential."""
repo_root = Path(__file__).resolve().parents[3]
policy = central_required_workflow_policy_text()

opencode_config = (repo_root / "opencode.jsonc").read_text(encoding="utf-8")
assert '"openai/o3"' in opencode_config
assert '"openai/o4-mini"' in opencode_config
assert_local_review_workflows_removed()
assert "selected workflow mutation" in policy
assert "credential, not by a maintainer's local `gh` session" in policy
Expand Down
Loading