Skip to content

Commit dd0b96f

Browse files
seonghobaeclaude
andauthored
fix(review): name the Strix sandbox class in the failed-check finding (#1960)
`opencode-review-dispatch.yml`'s `emit_strix_provider_failure_finding` rendered one fixed finding for every `STRIX_PROVIDER_UNAVAILABLE` line, whose Root cause read "The contextual-orchestrator gateway or its discovered provider pool was unavailable for this run". #1953 had just given the Strix sandbox bootstrap failure its own second verdict token, `STRIX_SANDBOX_UNAVAILABLE`, precisely because that attribution is wrong for it: the sandbox container never reaches its Caido proxy, so the run dies before the gateway serves anything. This consumer re-applied the wrong attribution one step downstream, into the review findings and the failure census -- the misattribution #1953 fixed in the gate, surviving in the reader. The emitter now branches on the second token. A sandbox verdict gets a finding that names Strix's sandbox, states that the verdict does not name the gateway, and tells the reader not to change gateway or provider configuration on its strength. A `STRIX_PROVIDER_UNAVAILABLE` line without the token keeps its existing four lines verbatim, so the gateway class has no regression surface. No test covered this finding text at all before ("gateway or its discovered provider pool" and "provider availability blocked" both matched nothing under tests/). tests/test_opencode_dispatch_strix_sandbox_finding.py runs the production emitter, extracted from the published run block with the existing _extract_run_block harness, and pins three directions: the sandbox token, a gateway failure without it, and evidence carrying no provider-unavailable signal at all. Editing the workflow moves its blob, so REVIEW_DISPATCH_BLOB_SHA in tests/test_pr_review_autofix_nvidia_nim_contract.py is recomputed to 694c04b with git hash-object (lane peer 1 flagged this pin in advance). Gate on this tree: 2931 passed, 1 skipped, 21 subtests; coverage 100% (0 missed); interrogate 100%. Negative control on origin/main's workflow with this test file: 1 failed, 2 passed -- only the sandbox direction fails there. Refs #1953, #1935. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent c232ca0 commit dd0b96f

4 files changed

Lines changed: 117 additions & 1 deletion

File tree

.github/workflows/opencode-review-dispatch.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5636,6 +5636,24 @@ jobs:
56365636
fi
56375637
56385638
finding_index=$((finding_index + 1))
5639+
5640+
# The gate emits a second token for the class it can tell apart:
5641+
# STRIX_SANDBOX_UNAVAILABLE means Strix's own sandbox container
5642+
# never reached its Caido proxy, so the run died before the
5643+
# gateway served anything. Reporting that as "the gateway or its
5644+
# provider pool was unavailable" sends the reader to the wrong
5645+
# component -- the misattribution #1953 fixed in the gate itself,
5646+
# which survived here because this text was fixed for every
5647+
# STRIX_PROVIDER_UNAVAILABLE line.
5648+
if grep -q "STRIX_SANDBOX_UNAVAILABLE" "$strix_evidence_file"; then
5649+
printf '### %s. HIGH %s:%s - Strix sandbox bootstrap blocked current-head security evidence\n' "$finding_index" "$path" "$line"
5650+
printf -- '- Problem: Strix failed before producing vulnerability reports. The failed log reported STRIX_SANDBOX_UNAVAILABLE, which the gate emits when the run ended in Strix sandbox bootstrap after its bounded sandbox-specific retries.\n'
5651+
printf -- '- Root cause: Strix sandbox container did not reach its Caido proxy on 127.0.0.1, so the scan ended before any Vulnerability Report window was produced. This verdict names Strix sandbox, not the contextual-orchestrator gateway, and there is no application source line to patch from this evidence.\n'
5652+
printf -- '- Fix: Do not approve from this failed scan. Re-run Strix; the sandbox bootstrap is a startup race and the gate already retries it once. Do not change gateway or provider configuration on the strength of this finding.\n'
5653+
printf -- '- Regression test: Keep the gate emitting STRIX_SANDBOX_UNAVAILABLE for sandbox bootstrap failures and keep this consumer reading it, so a sandbox outage is never reported as a gateway outage.\n\n'
5654+
return 0
5655+
fi
5656+
56395657
printf '### %s. HIGH %s:%s - Contextual-orchestrator provider availability blocked current-head security evidence\n' "$finding_index" "$path" "$line"
56405658
printf -- '- Problem: Strix failed before producing vulnerability reports. The failed log reported LLM CONNECTION FAILED, RateLimitError or Too many requests, budget-limit output, gateway exhaustion, and Configured model and fallback models were unavailable.\n'
56415659
printf -- '- Root cause: The contextual-orchestrator gateway or its discovered provider pool was unavailable for this run; no Strix Vulnerability Report window was produced, so there is no application source line to patch from this evidence.\n'

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### Failed-check finding names the Strix sandbox instead of the gateway
2+
3+
- `opencode-review-dispatch.yml`'s `emit_strix_provider_failure_finding` rendered one fixed finding for every `STRIX_PROVIDER_UNAVAILABLE` line, whose Root cause read "The contextual-orchestrator gateway or its discovered provider pool was unavailable for this run". `#1953` had just given the Strix sandbox bootstrap failure its own second verdict token (`STRIX_SANDBOX_UNAVAILABLE`) precisely because that attribution is wrong for it -- the sandbox container never reaches its Caido proxy, so the run dies before the gateway serves anything -- and this consumer re-applied the wrong attribution one step downstream, into the review findings and the failure census. The emitter now branches on the second token: a sandbox verdict gets a finding that names Strix's sandbox, says the verdict does not name the gateway, and tells the reader not to change gateway or provider configuration on its strength. A `STRIX_PROVIDER_UNAVAILABLE` line without the token keeps its existing text verbatim, so the gateway class has no regression surface. No test covered this finding text at all before (`gateway or its discovered provider pool` matched nothing under `tests/`); `tests/test_opencode_dispatch_strix_sandbox_finding.py` now runs the production emitter from the published run block and pins both directions plus the no-signal case. Refs #1953, #1935.
4+
15
### Strix gate keeps a recovered transient model error from failing a completed scan
26

37
- `scripts/ci/strix_quick_gate.sh` `sanitize_known_strix_report_warnings` now also strips strix-agent's `strix.core.execution: transient model/provider error for <agent>; replaying turn (attempt n/m, backoff Ns): …` WARNING lines before the report failure-signal scan. strix-agent 1.5.3 (`strix/core/execution.py:763`) emits that line only inside its bounded transient-retry branch, immediately before the replay runs; an exhausted retry logs `agent run failed for …; marking failed` at ERROR with a traceback and exits non-zero, and both of those still fail the gate. Observed on `.github#1689` run `34013778497`: a completed 63-minute scan (`run.json` `completed`, SARIF 0 results, attempt exit 0) was failed closed as `STRIX_PROVIDER_UNAVAILABLE … exhausted` on three such warnings, and the scheduler then dispatched another same-head scan. The pattern is anchored before the exception repr so the same class keeps matching after a gateway pin advance changes the exception type; re-verify the message format on every strix-agent bump. One documented side effect: when a provider's 503 body appears only inside a retry line's exception repr, removing that line also removes the only text `has_strix_report_provider_failure_signal` would have matched in the report log, which can make `is_model_retryable_error`'s report-only branch read a genuine outage as non-retryable. The direction is fail-closed (an exhausted retry still exits non-zero with its ERROR and traceback retained), and with a contextual-orchestrator primary the verdict branch answers before that classifier is consulted, so no path today changes its outcome; if fallback-model classification is ever wanted for a non-gateway primary, read the pre-sanitize attempt copy that `preserve_attempt_log` already keeps. Tests: `tests/test_strix_recovered_transient_sanitizer.py`.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""The failed-check finding must name Strix sandbox when the gate named it.
2+
3+
`#1953` gave the Strix sandbox bootstrap failure its own verdict token,
4+
`STRIX_SANDBOX_UNAVAILABLE`, precisely because reporting it as
5+
`contextual-orchestrator/orchestrator/free exhausted` sent readers to a
6+
component the run never reached. This consumer rendered one fixed finding for
7+
every `STRIX_PROVIDER_UNAVAILABLE` line, so the corrected verdict was being
8+
re-attributed to the gateway one step downstream, and no test covered the text
9+
at all. These tests pin both directions.
10+
"""
11+
12+
from __future__ import annotations
13+
14+
import subprocess
15+
from pathlib import Path
16+
17+
from tests.test_opencode_workflow_shell_syntax import _extract_run_block
18+
19+
WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml")
20+
STEP_NAME = "Publish OpenCode review outcome"
21+
FUNCTION = "emit_strix_provider_failure_finding"
22+
23+
24+
def _emitter_source() -> str:
25+
"""Return the emitter function's shell source from the published run block."""
26+
script = _extract_run_block(WORKFLOW.read_text(encoding="utf-8"), STEP_NAME)
27+
start = script.index(f"{FUNCTION}() {{")
28+
# ``_extract_run_block`` dedents the YAML block scalar, leaving the
29+
# function body at two spaces and its closing brace on a line of its own.
30+
closing = "\n }\n"
31+
end = script.index(closing, start) + len(closing)
32+
return script[start:end]
33+
34+
35+
def _run_emitter(evidence: str, tmp_path: Path) -> str:
36+
"""Run the production emitter against one evidence file and return its finding text."""
37+
evidence_file = tmp_path / "strix-evidence.txt"
38+
evidence_file.write_text(evidence, encoding="utf-8")
39+
harness = tmp_path / "harness.sh"
40+
harness.write_text(
41+
"set -euo pipefail\n"
42+
f'strix_evidence_file="{evidence_file}"\n'
43+
f'repo_root="{tmp_path}"\n'
44+
"finding_index=0\n"
45+
f"{_emitter_source()}\n"
46+
f"{FUNCTION}\n",
47+
encoding="utf-8",
48+
)
49+
result = subprocess.run(
50+
["bash", str(harness)], capture_output=True, text=True, check=True
51+
)
52+
return result.stdout
53+
54+
55+
def test_sandbox_token_reports_the_sandbox_not_the_gateway(tmp_path: Path) -> None:
56+
"""A `STRIX_SANDBOX_UNAVAILABLE` verdict never blames the gateway or its provider pool."""
57+
finding = _run_emitter(
58+
"STRIX_PROVIDER_UNAVAILABLE: STRIX_SANDBOX_UNAVAILABLE: the last Strix "
59+
"attempt ended in the sandbox bootstrap (Caido proxy on 127.0.0.1 "
60+
"unreachable through Strix's loginAsGuest attempts) after 1 "
61+
"sandbox-specific same-model retries (budget 1); this verdict names "
62+
"Strix's sandbox, not the LLM gateway.\n",
63+
tmp_path,
64+
)
65+
66+
assert "Strix sandbox bootstrap blocked current-head security evidence" in finding
67+
assert "STRIX_SANDBOX_UNAVAILABLE" in finding
68+
assert "names Strix sandbox, not the contextual-orchestrator gateway" in finding
69+
assert "gateway or its discovered provider pool was unavailable" not in finding
70+
# The reader must not be sent to change gateway configuration.
71+
assert "Do not change gateway or provider configuration" in finding
72+
assert finding.startswith("### 1. HIGH .github/workflows/strix.yml:")
73+
74+
75+
def test_gateway_failure_keeps_its_existing_finding(tmp_path: Path) -> None:
76+
"""Without the sandbox token the previous gateway text is emitted unchanged."""
77+
finding = _run_emitter(
78+
"STRIX_PROVIDER_UNAVAILABLE: contextual-orchestrator/orchestrator/free "
79+
"exhausted; the gateway owns provider discovery and failover.\n",
80+
tmp_path,
81+
)
82+
83+
assert (
84+
"Contextual-orchestrator provider availability blocked current-head security evidence"
85+
in finding
86+
)
87+
assert "gateway or its discovered provider pool was unavailable" in finding
88+
assert "STRIX_SANDBOX_UNAVAILABLE" not in finding
89+
assert "Strix sandbox bootstrap blocked" not in finding
90+
91+
92+
def test_unrelated_evidence_emits_no_finding(tmp_path: Path) -> None:
93+
"""Evidence with no provider-unavailable signal still produces nothing."""
94+
assert _run_emitter("Strix run succeeded for model 'x' in 12s.\n", tmp_path) == ""

tests/test_pr_review_autofix_nvidia_nim_contract.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md")
1818
CHANGELOG = Path("CHANGELOG.md")
1919
REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml")
20-
REVIEW_DISPATCH_BLOB_SHA = "fd0e5ff75d9ae6ec68cc6945174dff5c9d487662"
20+
REVIEW_DISPATCH_BLOB_SHA = "d86497b3f43bebbabbb4f504eb5132cdf3b7b293"
2121

2222

2323
def _workflow_text(path: Path) -> str:

0 commit comments

Comments
 (0)