Skip to content
Closed
95 changes: 17 additions & 78 deletions .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,6 @@ jobs:
needs: [coverage-evidence]
if: >-
always()
&& needs.coverage-evidence.result != 'cancelled'
&& (
github.event_name == 'workflow_dispatch'
|| (
Expand Down Expand Up @@ -2007,7 +2006,7 @@ jobs:
"$schema": "https://opencode.ai/config.json",
"model": "github-models/deepseek/deepseek-r1-0528",
"small_model": "github-models/deepseek/deepseek-v3-0324",
"enabled_providers": ["openai", "github-models"],
"enabled_providers": ["github-models"],
"lsp": true,
"mcp": {
"codegraph": {
Expand Down Expand Up @@ -2133,50 +2132,6 @@ jobs:
}
},
"provider": {
"openai": {
"npm": "@ai-sdk/openai",
"name": "OpenAI (direct)",
"options": {
"baseURL": "https://api.openai.com/v1",
"apiKey": "{env:OPENAI_API_KEY}"
},
"models": {
"gpt-5": {
"name": "OpenAI GPT-5 (direct)",
"tool_call": true,
"reasoning": true,
"options": {
"reasoningEffort": "high"
},
"variants": {
"high": {
"reasoningEffort": "high"
}
},
"limit": {
"context": 400000,
"output": 128000
}
},
"gpt-5-mini": {
"name": "OpenAI GPT-5 Mini (direct)",
"tool_call": true,
"reasoning": true,
"options": {
"reasoningEffort": "high"
},
"variants": {
"high": {
"reasoningEffort": "high"
}
},
"limit": {
"context": 400000,
"output": 128000
}
}
}
},
"github-models": {
"npm": "@ai-sdk/openai-compatible",
"name": "GitHub Models",
Expand Down Expand Up @@ -2384,44 +2339,31 @@ jobs:
env:
STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
# Native OpenAI backend for the lead review model. GitHub Models
# rate-limits every request and caps bodies at ~4000 tokens, so the
# rate-starved shared pool never returned a verdict; hitting
# api.openai.com directly with the org OPENAI_API_KEY gives the lead
# model a working, un-throttled backend. Resolves {env:OPENAI_API_KEY}
# in the opencode.jsonc "openai" provider block.
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
USE_GITHUB_TOKEN: "true"
SHARE: "false"
NPM_CONFIG_IGNORE_SCRIPTS: "true"
NO_COLOR: "1"
# Lead with the NATIVE OpenAI backend (openai/gpt-5-mini, openai/gpt-5
# via api.openai.com with the org OPENAI_API_KEY). GitHub Models
# rate-limited ("Too many requests") and 4000-token-capped
# (413 tokens_limit_reached) EVERY model in the shared pool, so the
# reviewer never produced a verdict and every run hung to the 350-min
# timeout — a 100% org-wide failure. The native provider is not subject
# to those limits, so it can actually complete and approve. The
# existing github-models entries stay as fallbacks (tried only if the
# native key is missing or the direct call fails).
# github-models ordering rationale (unchanged): contract-reliable mini
# reasoning models first, high-quota non-reasoning models next, and the
# rate-starved github-models flagships (gpt-5/o3, 8-12 req/day) last so
# a throttled/hung leader always falls back instead of eating the step.
OPENCODE_MODEL_CANDIDATES: "openai/gpt-5-mini openai/gpt-5 github-models/openai/o4-mini github-models/openai/o3-mini github-models/openai/gpt-5-mini github-models/openai/gpt-5-nano github-models/openai/gpt-5-chat github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1 github-models/deepseek/deepseek-v3-0324 github-models/mistral-ai/mistral-medium-2505 github-models/meta/llama-4-maverick-17b-128e-instruct-fp8 github-models/meta/llama-4-scout-17b-16e-instruct github-models/openai/o3 github-models/openai/gpt-5"
# Ordered contract-reliability first, then quota, with the rate-starved
# flagships last. The mini reasoning models (o4-mini, o3-mini, gpt-5-
# mini/nano/chat) reliably emit the strict review contract — every
# required label and only source-backed findings — so they lead. The
# high-quota non-reasoning models (deepseek-v3, mistral, llama-4) emit
# bare or hallucinated reviews the publish/approve gates reject, so
# they are fallbacks only. gpt-5/o3 ("Reasoning" tier, 8-12 req/day)
# stay last: first-placing them stalled every review until timeout
# because a rate-limited/hung flagship never fell back.
OPENCODE_MODEL_CANDIDATES: "github-models/openai/o4-mini github-models/openai/o3-mini github-models/openai/gpt-5-mini github-models/openai/gpt-5-nano github-models/openai/gpt-5-chat github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1 github-models/deepseek/deepseek-v3-0324 github-models/mistral-ai/mistral-medium-2505 github-models/meta/llama-4-maverick-17b-128e-instruct-fp8 github-models/meta/llama-4-scout-17b-16e-instruct github-models/openai/o3 github-models/openai/gpt-5"
# One attempt per model, then fall through to the next model. Retrying
# the SAME model 5x let a rate-limited/hung leader consume the whole
# step, so the pool never reached a healthy fallback model.
OPENCODE_MODEL_ATTEMPTS: "1"
# 15 min per model — enough for a bounded review attempt, but short
# enough that a hung provider yields to the next candidate before it
# freezes the review queue.
OPENCODE_RUN_TIMEOUT_SECONDS: "900"
# 90 min per model — generous for a deep tool-using review, but bounded
# so a rate-limited model yields to the next one instead of eating the
# 350-min step. (20400s = 340min gave one model the entire budget with
# no fallback; 600s was too short for a proper review.)
OPENCODE_RUN_TIMEOUT_SECONDS: "5400"
OPENCODE_EXPORT_TIMEOUT_SECONDS: "120"
# Bound provider/model-pool outages before the 350-min job timeout. A
# zero budget disables the script deadline and caused org-wide hangs.
OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "2700"
OPENCODE_POOL_MAX_CYCLES: "1"
OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "0"
OPENCODE_BACKOFF_INITIAL_SECONDS: "30"
OPENCODE_BACKOFF_MAX_SECONDS: "30"
OPENCODE_FIRST_ATTEMPT_AGENT: ci-review
Expand Down Expand Up @@ -2790,9 +2732,6 @@ jobs:
CHECK_LOOKUP_GH_TOKEN: ${{ github.token }}
GH_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }}
STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
# Exposed so the "openai" provider in opencode.jsonc resolves during the
# failed-check diagnosis opencode run that shares this config.
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENCODE_APP_TOKEN: ${{ steps.opencode_app_token.outputs.token }}
OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md
OPENCODE_FAILED_CHECK_EVIDENCE_FILE: ${{ runner.temp }}/opencode-failed-check-evidence.md
Expand Down
56 changes: 1 addition & 55 deletions .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,8 @@ name: Strix Security Scan
on:
push:
branches: [main, develop, master]
# Skip scans for changes that touch ONLY non-executable documentation and
# image assets. A change whose entire diff is these paths has no source,
# build, config, or workflow logic for a code security scanner to analyze,
# so skipping it loses no coverage while freeing shared runner capacity.
# Conservative by design: only file EXTENSIONS/paths that can never contain
# executable logic are listed (no source, no *.txt, no *.svg, no CODEOWNERS,
# no build scripts). A diff touching even one non-listed file still scans.
# The weekly full-tree schedule below re-scans protected branches with no
# path filter, backstopping every path.
paths-ignore:
- '**/*.md'
- '**/*.markdown'
- '**/*.rst'
- '**/*.png'
- '**/*.jpg'
- '**/*.jpeg'
- '**/*.gif'
- '**/*.webp'
- '**/*.bmp'
- '**/*.ico'
- 'LICENSE'
- 'LICENSE.*'
- 'COPYING'
- '.github/ISSUE_TEMPLATE/**'
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review, closed]
# Same conservative doc/image-only skip for PR scans. GitHub evaluates these
# path filters against the PR's full base..head diff, so a PR is skipped only
# when EVERY changed file is a non-executable doc/image asset; any code,
# config, build, or workflow change still triggers the scan. The head.sha
# concurrency design (below) is unchanged. For PRs the merge scheduler
# manages, same-head Strix evidence is still forced at merge time via
# workflow_dispatch (which paths-ignore does not affect), so merged code
# never loses evidence.
paths-ignore:
- '**/*.md'
- '**/*.markdown'
- '**/*.rst'
- '**/*.png'
- '**/*.jpg'
- '**/*.jpeg'
- '**/*.gif'
- '**/*.webp'
- '**/*.bmp'
- '**/*.ico'
- 'LICENSE'
- 'LICENSE.*'
- 'COPYING'
- '.github/ISSUE_TEMPLATE/**'
schedule:
# Weekly scan on protected branches (Mondays at 03:00 UTC).
- cron: '0 3 * * 1'
Expand Down Expand Up @@ -108,14 +61,7 @@ jobs:

strix:
if: github.event_name != 'pull_request_target' || github.event.action != 'closed'
# The scan itself is hard-bounded to 30 min (the "Run Strix (quick)" step
# has timeout-minutes: 30 and exports STRIX_TOTAL_TIMEOUT_SECONDS=1800), and
# every other step is quick or self-bounded (self-test is 2 min). A healthy
# run finishes well under 50 min. The 120 cap only ever bit HUNG runs (e.g.
# a network stall in pip/git with no per-step timeout); 60 min still clears
# the realistic worst case with margin while freeing a stuck runner in half
# the time. Fail-closed: hitting the cap fails the run, never passes it.
timeout-minutes: 60
timeout-minutes: 120
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
Expand Down
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
## 2026-07-02 - Credential Masking Security Hole in Subprocess Environments
**Learning:** Found a critical missing credential masking pattern in `scripts/ci/noema_review_gate.py`'s `scrub_sensitive_data` which didn't mask `Authorization: Basic` or `Proxy-Authorization: Basic` tokens unlike its analogous helper in `scripts/ci/pr_review_merge_scheduler.py`. This leaves exception messages and logs vulnerable to exposing sensitive credentials when HTTP operations fail.
**Action:** When implementing credential masking functions that sanitize tracebacks and log messages, ensure the masking scope includes all relevant headers, particularly `Authorization` and `Proxy-Authorization`. Ensure parity across masking helpers across CI scripts to prevent blind spots.
## 2026-07-07 - Pre-compile Regex Patterns in Python Scripts Embedded in Bash
**Learning:** Found an anti-pattern in `scripts/ci/validate_opencode_failed_check_review.sh` where a Python script injected via HEREDOC repeatedly recompiled regex patterns using `re.sub` and `re.match` within tight loops for cleaning and parsing CI logs. This caused measurable overhead.
**Action:** Always pre-compile regex patterns at the module level using `re.compile()` in Python scripts, even those embedded inside Bash scripts via HEREDOCs, to prevent redundant compilations when processing large inputs.
3 changes: 3 additions & 0 deletions scripts/ci/noema_review_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ def call_llm(repo: str, number: int, pr: dict[str, Any], diff: str, truncated: b
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_multicast or ip.is_unspecified:
raise ValueError("URL cannot target internal IP addresses")

if not (api_url.startswith("http://") or api_url.startswith("https://")):
raise ValueError(f"NOEMA_LLM_API_URL must start with http:// or https:// to prevent SSRF vulnerabilities, got: {api_url}")

prompt = {
"role": "user",
"content": "\n".join(
Expand Down
28 changes: 1 addition & 27 deletions scripts/ci/run_opencode_review_model_pool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,6 @@ is_context_overflow_failure() {
grep -Eiq 'ContextOverflowError|tokens_limit_reached|Request body too large|context window' "$opencode_json_file"
}

is_direct_openai_candidate() {
case "$1" in
openai/*) return 0 ;;
*) return 1 ;;
esac
}

should_skip_model_candidate() {
local model_candidate="$1"

if is_direct_openai_candidate "$model_candidate" && [ -z "${OPENAI_API_KEY:-}" ]; then
printf 'Skipping OpenCode %s because OPENAI_API_KEY is not configured; falling back to the next provider-qualified candidate.\n' "$model_candidate"
return 0
fi
return 1
}

run_one_model_attempt() {
local model_candidate="$1"
local attempt="$2"
Expand Down Expand Up @@ -186,13 +169,12 @@ run_one_model_attempt() {

main() {
local attempts budget_seconds deadline now remaining model_candidate attempt safe_model prompt_file candidate_output_file
local opencode_json_file opencode_export_file agent retry_sleep original_run_timeout run_status cycle_sleep cycle max_cycles
local opencode_json_file opencode_export_file agent retry_sleep original_run_timeout run_status cycle_sleep cycle
local -a model_candidates

attempts="${OPENCODE_MODEL_ATTEMPTS:-3}"
original_run_timeout="${OPENCODE_RUN_TIMEOUT_SECONDS:-900}"
budget_seconds="${OPENCODE_TOTAL_RETRY_BUDGET_SECONDS:-18000}"
max_cycles="${OPENCODE_POOL_MAX_CYCLES:-0}"
deadline=0
if [ "$budget_seconds" -gt 0 ]; then
deadline=$((SECONDS + budget_seconds))
Expand All @@ -210,9 +192,6 @@ main() {
while :; do
printf 'Starting OpenCode model pool cycle %s.\n' "$cycle"
for model_candidate in "${model_candidates[@]}"; do
if should_skip_model_candidate "$model_candidate"; then
continue
fi
assert_reasoning_effort_for_candidate "$model_candidate"
safe_model="${model_candidate//\//-}"
prompt_file="${RUNNER_TEMP}/opencode-review-${safe_model}-prompt.md"
Expand Down Expand Up @@ -264,11 +243,6 @@ main() {
done

printf 'OpenCode completed a full model-candidate cycle without a valid control conclusion; continuing until a model succeeds or the GitHub Actions job timeout is reached.\n'
if [ "$max_cycles" -gt 0 ] && [ "$cycle" -ge "$max_cycles" ]; then
printf 'OpenCode model pool reached configured max cycle count %s without a valid control conclusion.\n' "$max_cycles"
record_review_model ""
exit 1
fi
cycle_sleep="${OPENCODE_POOL_CYCLE_SLEEP_SECONDS:-60}"
if [ "$deadline" -gt 0 ] && [ $((SECONDS + cycle_sleep)) -gt "$deadline" ]; then
cycle_sleep=$((deadline - SECONDS))
Expand Down
Loading
Loading