Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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`.
51 changes: 51 additions & 0 deletions docs/doctoring/opencode-nvidia-nim-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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` on
`nvidia/llama-3.3-nemotron-super-49b-v1.5`.

## 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 the field.

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 an explicit ban on `reasoningEffort` plus `{env:NVIDIA_NIM_API_KEY}`
inside `opencode.jsonc`.
- Test points: `test_opencode_uses_nvidia_nim_only` and
`test_opencode_uses_the_canonical_nvidia_nim_contract`.
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,42 @@
"""Regression contract for the repository's NVIDIA NIM OpenCode configuration."""

from __future__ import annotations

import json
from pathlib import Path


def test_opencode_uses_the_canonical_nvidia_nim_contract() -> None:
"""Require the reviewed NIM models, process credential alias, and option boundary."""
repo_root = Path(__file__).resolve().parents[3]
opencode_text = (repo_root / "opencode.jsonc").read_text(encoding="utf-8")
config = json.loads(opencode_text)

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

provider = config["provider"]["nvidia-nim"]
assert provider["options"]["baseURL"] == "https://integrate.api.nvidia.com/v1"
# Central workflows source GitHub Secret NVIDIA_NIM_API_KEY and intentionally
# expose it to this OpenCode-compatible client through the NVIDIA_API_KEY
# process alias. The repository config must consume the alias, not rename
# the organization secret contract.
assert provider["options"]["apiKey"] == "{env:NVIDIA_API_KEY}"
assert "{env:NVIDIA_NIM_API_KEY}" not in opencode_text

primary_model = provider["models"]["nvidia/llama-3.3-nemotron-super-49b-v1.5"]
assert "reasoningEffort" not in primary_model.get("options", {})
assert "meta/llama-3.3-70b-instruct" in provider["models"]

for forbidden_token in (
"github-models",
"STRIX_GITHUB_MODELS_TOKEN",
"COPILOT_GITHUB_TOKEN",
"openai/gpt-5",
"openai/o3",
"openai/o4-mini",
"models.github.ai",
):
assert forbidden_token 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)

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

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