Skip to content
Merged
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
11 changes: 6 additions & 5 deletions .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3223,10 +3223,11 @@ jobs:
OPENCODE_EXPORT_TIMEOUT_SECONDS: "120"
OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "3600"
OPENCODE_POOL_STEP_TIMEOUT_SECONDS: "3600"
# Visit the high-sensitivity candidate catalog once. Per-provider
# failures and timeouts are logged before the publish gate evaluates
# the current-head result and fails closed when no verdict exists.
OPENCODE_POOL_MAX_CYCLES: "1"
# Keep cycling through the high-sensitivity candidate catalog until
# the retry budget or step timeout is exhausted; a single invalid
# cycle can be all provider formatting noise rather than review
# evidence.
OPENCODE_POOL_MAX_CYCLES: "0"
OPENCODE_DYNAMIC_REVIEW_CADENCE: "true"
OPENCODE_SMALL_CHANGE_FILE_THRESHOLD: "3"
OPENCODE_MEDIUM_CHANGE_FILE_THRESHOLD: "20"
Expand All @@ -3243,7 +3244,7 @@ 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"
OPENCODE_DYNAMIC_MAX_CYCLES: "1"
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' }}
OPENCODE_CENTRAL_REVIEW_PROCESS_FALLBACK_RUN_TIMEOUT_SECONDS: "600"
Expand Down
4 changes: 2 additions & 2 deletions scripts/ci/run_opencode_review_model_pool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -645,13 +645,13 @@ main() {
original_run_timeout="$(env_integer_or_default OPENCODE_LARGE_CHANGE_RUN_TIMEOUT_SECONDS 3600)"
budget_seconds="$(env_integer_or_default OPENCODE_LARGE_CHANGE_TOTAL_BUDGET_SECONDS 7200)"
fi
max_cycles="$(env_integer_or_default OPENCODE_DYNAMIC_MAX_CYCLES 1)"
max_cycles="$(env_integer_or_default OPENCODE_DYNAMIC_MAX_CYCLES 0)"
printf 'OpenCode dynamic review cadence selected %ss per attempt and %ss total budget for %s changed file(s); max-cycles=%s.\n' \
"$original_run_timeout" "$budget_seconds" "$changed_file_count" "$max_cycles"
else
original_run_timeout="$(env_integer_or_default OPENCODE_UNKNOWN_CHANGE_RUN_TIMEOUT_SECONDS 1800)"
budget_seconds="$(env_integer_or_default OPENCODE_UNKNOWN_CHANGE_TOTAL_BUDGET_SECONDS 3900)"
max_cycles="$(env_integer_or_default OPENCODE_DYNAMIC_MAX_CYCLES 1)"
max_cycles="$(env_integer_or_default OPENCODE_DYNAMIC_MAX_CYCLES 0)"
printf 'OpenCode dynamic review cadence could not read OPENCODE_CHANGED_FILES_FILE; using %ss per attempt and %ss total budget; max-cycles=%s.\n' \
"$original_run_timeout" "$budget_seconds" "$max_cycles"
fi
Expand Down
2 changes: 1 addition & 1 deletion scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() {
assert_file_contains "$workflow_file" 'continue-on-error: true' "opencode approval gate still runs after model-pool failure to publish a reason"
assert_file_contains "$workflow_file" 'OPENCODE_RUN_TIMEOUT_SECONDS: "600"' "opencode primary review advances after a bounded stalled provider attempt"
assert_file_contains "$workflow_file" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "3600"' "opencode model pool exits before the step timeout so the approval gate can publish a reason"
assert_file_contains "$workflow_file" 'OPENCODE_POOL_MAX_CYCLES: "1"' "opencode model pool visits the candidate catalog once without reoccupying the queue"
assert_file_contains "$workflow_file" 'OPENCODE_POOL_MAX_CYCLES: "0"' "opencode model pool keeps cycling until the bounded retry budget or step timeout is exhausted"
assert_file_contains "$workflow_file" "needs.coverage-evidence.result == 'success'" "opencode model pool only runs after coverage evidence passed"
assert_file_contains "$workflow_file" "id: opencode_review_model_pool" "opencode DeepSeek V3 fallback still runs after a primary model timeout or step failure when coverage evidence passed"
assert_file_contains "$workflow_file" "always()" "opencode fallback chain uses always() so failed model steps cannot skip every fallback"
Expand Down
6 changes: 3 additions & 3 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert 'OPENCODE_POOL_STEP_TIMEOUT_SECONDS: "3600"' in workflow
assert 'timeout --kill-after=30s "${OPENCODE_POOL_STEP_TIMEOUT_SECONDS:-3600}s"' in workflow
assert "OpenCode model pool exceeded the outer" in workflow
assert 'OPENCODE_POOL_MAX_CYCLES: "1"' in workflow
assert 'OPENCODE_POOL_MAX_CYCLES: "0"' in workflow
assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,280}continue-on-error: true", workflow)
assert re.search(r"Publish central OpenCode fast approval[\s\S]{0,900}timeout-minutes: 8", workflow)
assert re.search(r"Publish OpenCode review outcome[\s\S]{0,900}timeout-minutes: 10", workflow)
Expand All @@ -749,7 +749,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert 'OPENCODE_EXPORT_TIMEOUT_SECONDS: "120"' in workflow
assert 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS: "3600"' in workflow
assert 'OPENCODE_POOL_STEP_TIMEOUT_SECONDS: "3600"' in workflow
assert 'OPENCODE_POOL_MAX_CYCLES: "1"' in workflow
assert 'OPENCODE_POOL_MAX_CYCLES: "0"' in workflow
assert 'OPENCODE_DYNAMIC_REVIEW_CADENCE: "true"' in workflow
assert 'OPENCODE_CHANGED_FILES_FILE: ${{ runner.temp }}/opencode-changed-files.txt' in workflow
assert 'OPENCODE_SMALL_CHANGE_RUN_TIMEOUT_SECONDS: "600"' in workflow
Expand All @@ -761,7 +761,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert 'OPENCODE_UNKNOWN_CHANGE_RUN_TIMEOUT_SECONDS: "600"' in workflow
assert 'OPENCODE_UNKNOWN_CHANGE_TOTAL_BUDGET_SECONDS: "3600"' in workflow
assert 'OPENCODE_GITHUB_GPT5_RUN_TIMEOUT_SECONDS: "45"' in workflow
assert 'OPENCODE_DYNAMIC_MAX_CYCLES: "1"' in workflow
assert 'OPENCODE_DYNAMIC_MAX_CYCLES: "0"' in workflow
assert 'OPENCODE_BACKOFF_MAX_SECONDS: "30"' in workflow
publish_step = workflow.split(" - name: Publish OpenCode review outcome", 1)[1].split(
" - name: Run merge scheduler after approval", 1
Expand Down
24 changes: 24 additions & 0 deletions tests/test_opencode_security_boundaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ def test_sensitive_log_redaction_assignment_parser_edges_remain_auditable() -> N
"token visible": "token visible",
"token=": "token=",
"token=,": "token=,",
"9safe=value": "9safe=value",
'"token: value': f'"token: {redactor.REDACTED}',
"token: ": "token: ",
}

for source, expected in cases.items():
Expand All @@ -81,6 +84,27 @@ def test_sensitive_log_redaction_assignment_parser_edges_remain_auditable() -> N
)


def test_sensitive_log_redaction_scrubs_provider_token_shapes() -> None:
"""Provider-shaped tokens are removed even when they are not key/value assignments."""
source = "\n".join(
[
"classic ghp_" + ("A" * 24),
"fine github_pat_" + ("B" * 24),
"openai sk-" + ("C" * 24),
"slack xoxb-" + ("D" * 24),
"aws AKIA" + ("E" * 16),
]
)
cleaned = redactor.redact_text(source)

assert "ghp_" not in cleaned
assert "github_pat_" not in cleaned
assert "sk-" not in cleaned
assert "xoxb-" not in cleaned
assert "AKIA" not in cleaned
assert cleaned.count(redactor.REDACTED) == 5


def test_sensitive_log_redaction_handles_lists_empty_input_and_cli(monkeypatch: pytest.MonkeyPatch) -> None:
"""Recursive lists, empty input, and the streaming CLI share the same scrubber."""
source = '{"values":[{"ok":1,"api_key":"secret-value"},2]}\n'
Expand Down
Loading