Skip to content
Open
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
29 changes: 22 additions & 7 deletions .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1915,6 +1915,7 @@ jobs:
local changed_file="$1"
case "${GH_REPOSITORY}:${changed_file}" in
ContextualWisdomLab/.github:.github/workflows/opencode-review.yml | \
ContextualWisdomLab/.github:.github/workflows/noema-review.yml | \
ContextualWisdomLab/.github:.github/workflows/pr-review-merge-scheduler.yml | \
ContextualWisdomLab/.github:.github/workflows/strix.yml | \
ContextualWisdomLab/.github:.jules/bolt.md | \
Expand All @@ -1924,18 +1925,28 @@ jobs:
ContextualWisdomLab/.github:opencode.jsonc | \
ContextualWisdomLab/.github:scripts/ci/changed_file_syntax_gate.py | \
ContextualWisdomLab/.github:scripts/ci/javascript_coverage_gate.py | \
ContextualWisdomLab/.github:scripts/ci/materialize_pr_review_source.py | \
ContextualWisdomLab/.github:scripts/ci/noema_review_gate.py | \
ContextualWisdomLab/.github:scripts/ci/opencode_review_approve_gate.sh | \
ContextualWisdomLab/.github:scripts/ci/opencode_dispatch_status.py | \
ContextualWisdomLab/.github:scripts/ci/opencode_existing_approval_gate.py | \
ContextualWisdomLab/.github:scripts/ci/pr_head_replay_guard.py | \
ContextualWisdomLab/.github:scripts/ci/pr_review_fix_scheduler.py | \
ContextualWisdomLab/.github:scripts/ci/pr_review_merge_scheduler.py | \
ContextualWisdomLab/.github:scripts/ci/run_opencode_review_model_pool.sh | \
ContextualWisdomLab/.github:scripts/ci/opencode_review_normalize_output.py | \
ContextualWisdomLab/.github:scripts/ci/strix_quick_gate.sh | \
ContextualWisdomLab/.github:scripts/ci/validate_opencode_failed_check_review.sh | \
ContextualWisdomLab/.github:tests/test_changed_file_syntax_gate.py | \
ContextualWisdomLab/.github:tests/test_javascript_coverage_gate.py | \
ContextualWisdomLab/.github:tests/test_materialize_pr_review_source.py | \
ContextualWisdomLab/.github:tests/test_noema_review_gate.py | \
ContextualWisdomLab/.github:tests/test_opencode_agent_contract.py | \
ContextualWisdomLab/.github:tests/test_opencode_existing_approval_gate.py | \
ContextualWisdomLab/.github:tests/test_opencode_model_pool_runner.py | \
ContextualWisdomLab/.github:tests/test_opencode_security_boundaries.py | \
ContextualWisdomLab/.github:tests/test_pr_head_replay_guard.py | \
ContextualWisdomLab/.github:tests/test_pr_review_fix_scheduler.py | \
ContextualWisdomLab/.github:tests/test_pr_review_fix_scheduler_coverage.py | \
ContextualWisdomLab/.github:tests/test_pr_review_merge_scheduler.py | \
ContextualWisdomLab/.github:tests/test_required_workflow_queue_contract.py | \
Expand Down Expand Up @@ -3506,25 +3517,25 @@ jobs:
continue-on-error: true
env:
STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}
# Native OpenAI backend for the lead review model. GitHub Models
# Native OpenAI backend for the direct 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
# api.openai.com directly with the org OPENAI_API_KEY gives that
# 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 }}
SHARE: "false"
NPM_CONFIG_IGNORE_SCRIPTS: "true"
NO_COLOR: "1"
# High-sensitivity review candidates only. DeepSeek V3 has been the
# most reliable first-pass reviewer in the org queue, then the pool
# falls through to the direct GPT-5.6 Luna slot, then the full-size
# GPT-4.1 long-context endpoint and provider-specific GPT/o3 fallbacks.
# High-sensitivity review candidates only. GPT-4.1 is first because it
# was the only provider to return a parseable control artifact during
# the latest current-head exhaustion incident; DeepSeek remains the
# first fallback but cannot monopolize a full 90-minute cadence slot.
# The direct-OpenAI slot runs GPT-5.6 Luna: the newest family's
# cost-efficient tier, cheaper than the legacy gpt-5 it replaced
# ($1/$6 vs $1.25/$10 per 1M tokens) so the org OpenAI budget
# stretches further between top-ups.
OPENCODE_MODEL_CANDIDATES: "github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1"
OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-4.1 github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1"
# 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.
Expand Down Expand Up @@ -3561,6 +3572,10 @@ jobs:
# larger. Keep the exact runtime failure visible without spending a
# full medium/large cadence slot after the long-context candidate.
OPENCODE_GITHUB_GPT5_RUN_TIMEOUT_SECONDS: "45"
# DeepSeek returned no provider detail while consuming two complete
# 90-minute slots. Five minutes preserves a useful fallback window
# while allowing the remaining independent providers to run.
OPENCODE_GITHUB_DEEPSEEK_RUN_TIMEOUT_SECONDS: "300"
OPENCODE_DYNAMIC_MAX_CYCLES: "0"
CENTRAL_REVIEW_PROCESS_FALLBACK_ELIGIBLE: ${{ steps.central_review_process_fallback_scope.outputs.eligible || 'false' }}
CENTRAL_REVIEW_PROCESS_FALLBACK_SCOPE_LABEL: ${{ steps.central_review_process_fallback_scope.outputs.scope_label || 'unsupported' }}
Expand Down
75 changes: 75 additions & 0 deletions scripts/ci/opencode_review_normalize_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,80 @@ def adversarial_probe_source_receipt_error(
return ""


def repair_adversarial_probe_evidence_bindings(value: Any) -> dict[str, Any] | Any:
"""Bind trusted source receipts and locations into otherwise valid evidence.

Models sometimes place the exact changed-file path and positive line in the
structured ``path``/``line`` fields but omit the duplicate ``path:line`` text
from ``evidence``. Restore only that redundant location after the existing
single receipt already matches the sealed current-head line. Missing,
duplicate, or mismatched receipts, unsafe paths, missing changed-file
evidence, and circular or unobserved claims remain unmodified and fail closed.
"""
if not isinstance(value, dict):
return value
validation = value.get("adversarial_validation")
if not isinstance(validation, dict):
return value
probes = validation.get("probes")
if not isinstance(probes, list):
return value

changed_files = current_changed_files()
repaired_probes: list[Any] = []
changed = False
for probe in probes:
if not isinstance(probe, dict):
repaired_probes.append(probe)
continue
path = probe.get("path")
line = probe.get("line")
evidence = probe.get("evidence")
if (
not isinstance(path, str)
or path not in changed_files
or isinstance(line, bool)
or not isinstance(line, int)
or line <= 0
or not isinstance(evidence, str)
or not evidence.strip()
or adversarial_probe_location_error(path, line)
):
repaired_probes.append(probe)
continue

receipts = SOURCE_LINE_RECEIPT_RE.findall(evidence)
if len(receipts) != 1 or adversarial_probe_source_receipt_error(
evidence, path, line
):
repaired_probes.append(probe)
continue
repaired_evidence = evidence
rejection = adversarial_evidence_rejection_reason(repaired_evidence, path, line)
if rejection == "must cite the exact probe path and positive line":
repaired_evidence = f"{path}:{line} {repaired_evidence}"
elif rejection:
repaired_probes.append(probe)
continue
if adversarial_probe_source_receipt_error(
repaired_evidence, path, line
) or adversarial_evidence_rejection_reason(repaired_evidence, path, line):
repaired_probes.append(probe)
continue
if repaired_evidence == evidence:
repaired_probes.append(probe)
else:
repaired_probes.append({**probe, "evidence": repaired_evidence})
changed = True

if not changed:
return value
return {
**value,
"adversarial_validation": {**validation, "probes": repaired_probes},
}


def adversarial_validation_error(
value: Any,
*,
Expand Down Expand Up @@ -1267,6 +1341,7 @@ def reject(reason: str) -> None:
return reject("APPROVE cannot contain findings")
if result == "REQUEST_CHANGES" and not findings:
return reject("REQUEST_CHANGES requires at least one finding")
value = repair_adversarial_probe_evidence_bindings(value)
adversarial_error = adversarial_validation_error(
value.get("adversarial_validation"),
result=result,
Expand Down
5 changes: 4 additions & 1 deletion scripts/ci/run_opencode_review_model_pool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ cap_model_run_timeout() {
github-models/openai/gpt-5 | github-models/openai/gpt-5-chat)
cap_seconds="$(env_integer_or_default OPENCODE_GITHUB_GPT5_RUN_TIMEOUT_SECONDS 45)"
;;
github-models/deepseek/*)
cap_seconds="$(env_integer_or_default OPENCODE_GITHUB_DEEPSEEK_RUN_TIMEOUT_SECONDS 300)"
;;
*)
printf '%s\n' "$run_timeout_seconds"
return 0
Expand Down Expand Up @@ -548,7 +551,7 @@ main() {
uncapped_run_timeout="$OPENCODE_RUN_TIMEOUT_SECONDS"
OPENCODE_RUN_TIMEOUT_SECONDS="$(cap_model_run_timeout "$model_candidate" "$OPENCODE_RUN_TIMEOUT_SECONDS")"
if [ "$OPENCODE_RUN_TIMEOUT_SECONDS" -lt "$uncapped_run_timeout" ]; then
printf 'OpenCode %s runtime cap selected %ss instead of %ss because this installation has returned a constrained request-body limit for that endpoint.\n' \
printf 'OpenCode %s runtime cap selected %ss instead of %ss because the configured provider-specific cap is lower than the cadence timeout.\n' \
"$model_candidate" "$OPENCODE_RUN_TIMEOUT_SECONDS" "$uncapped_run_timeout"
fi
export OPENCODE_RUN_TIMEOUT_SECONDS
Expand Down
29 changes: 22 additions & 7 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():

assert candidate_pairs
assert candidate_pairs == [
["github-models", "openai/gpt-4.1"],
["github-models", "deepseek/deepseek-v3-0324"],
["openai", "gpt-5.6-luna"],
["github-models", "openai/gpt-4.1"],
["github-models", "openai/gpt-5"],
["github-models", "openai/gpt-5-chat"],
["github-models", "openai/o3"],
Expand All @@ -115,8 +115,8 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
assert direct_openai_models == ["gpt-5.6-luna"]
assert set(github_candidate_models).issubset(set(github_models))
assert github_candidate_models == [
"deepseek/deepseek-v3-0324",
"openai/gpt-4.1",
"deepseek/deepseek-v3-0324",
"openai/gpt-5",
"openai/gpt-5-chat",
"openai/o3",
Expand Down Expand Up @@ -939,6 +939,20 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
in workflow
)
assert "tests/test_opencode_agent_contract.py | \\" in workflow
for central_review_path in (
".github/workflows/noema-review.yml",
"scripts/ci/materialize_pr_review_source.py",
"scripts/ci/noema_review_gate.py",
"scripts/ci/opencode_dispatch_status.py",
"scripts/ci/opencode_existing_approval_gate.py",
"scripts/ci/pr_review_fix_scheduler.py",
"tests/test_materialize_pr_review_source.py",
"tests/test_noema_review_gate.py",
"tests/test_opencode_existing_approval_gate.py",
"tests/test_opencode_security_boundaries.py",
"tests/test_pr_review_fix_scheduler.py",
):
assert f"ContextualWisdomLab/.github:{central_review_path}" in workflow
assert (
"ContextualWisdomLab/appguardrail:scripts/ci/collect_org_security_failures.py"
in workflow
Expand Down Expand Up @@ -1090,9 +1104,9 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
in workflow
)
assert (
'OPENCODE_MODEL_CANDIDATES: "github-models/deepseek/deepseek-v3-0324 '
'OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-4.1 '
"github-models/deepseek/deepseek-v3-0324 "
"openai/gpt-5.6-luna "
"github-models/openai/gpt-4.1 "
"github-models/openai/gpt-5 "
"github-models/openai/gpt-5-chat "
"github-models/openai/o3 "
Expand All @@ -1105,6 +1119,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "11700"' in workflow
assert 'OPENCODE_POOL_STEP_TIMEOUT_SECONDS: "12000"' in workflow
assert 'OPENCODE_POOL_MAX_CYCLES: "0"' in workflow
assert 'OPENCODE_GITHUB_DEEPSEEK_RUN_TIMEOUT_SECONDS: "300"' in workflow
assert 'OPENCODE_DYNAMIC_REVIEW_CADENCE: "true"' in workflow
assert (
"OPENCODE_CHANGED_FILES_FILE: ${{ runner.temp }}/opencode-changed-files.txt"
Expand Down Expand Up @@ -1179,7 +1194,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert "while :" in model_pool_runner
assert "should_skip_model_candidate" in model_pool_runner
assert "cap_model_run_timeout" in model_pool_runner
assert "constrained request-body limit" in model_pool_runner
assert "configured provider-specific cap" in model_pool_runner
assert "run_central_adversarial_harness" not in model_pool_runner
assert "finish_pool_without_model" in model_pool_runner
assert "central-current-head-adversarial-harness" not in model_pool_runner
Expand Down Expand Up @@ -1213,9 +1228,9 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
'OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-5-nano"' not in workflow
)
assert (
'OPENCODE_MODEL_CANDIDATES: "github-models/deepseek/deepseek-v3-0324 '
'OPENCODE_MODEL_CANDIDATES: "github-models/openai/gpt-4.1 '
"github-models/deepseek/deepseek-v3-0324 "
"openai/gpt-5.6-luna "
"github-models/openai/gpt-4.1 "
"github-models/openai/gpt-5 "
"github-models/openai/gpt-5-chat "
"github-models/openai/o3 "
Expand Down
Loading
Loading