Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6406cfd
fix(review): repair verified adversarial line bindings
seonghobae Jul 14, 2026
1fda560
fix(review): derive trusted probe receipts
seonghobae Jul 15, 2026
9d7774c
refactor(review): isolate trusted receipt repair
seonghobae Jul 15, 2026
c86c4b4
test(review): restore normalizer coverage gate
seonghobae Jul 15, 2026
196c5a4
fix(review): bind only verified probe locations
seonghobae Jul 15, 2026
534e578
fix(review): add Saju CalDAV offline Python dependencies
seonghobae Jul 19, 2026
3b2255b
fix(review): ignore cancelled scheduler REST checks
seonghobae Jul 20, 2026
55219f0
fix(review): bind probes to verified evidence lines
seonghobae Jul 20, 2026
90f4c51
test: cover receipt binding rejection edges
seonghobae Jul 20, 2026
631cdc8
fix(review): try GPT-4.1 first
seonghobae Jul 20, 2026
f802182
docs(review): align candidate-order guidance
seonghobae Jul 20, 2026
871db61
fix(review): exclude GPT-4.1 approval candidate
seonghobae Jul 20, 2026
447e062
fix(review): restore evidence-backed GPT-4.1 lead
seonghobae Jul 20, 2026
54538d1
fix(review): close trusted log and symlink boundaries
seonghobae Jul 21, 2026
826a628
fix(review): strip git history from coverage artifacts
seonghobae Jul 21, 2026
039c3c1
fix(review): keep Git history out of coverage artifacts
seonghobae Jul 21, 2026
6b129f8
fix(review): fail closed on artifact validation
seonghobae Jul 21, 2026
2fbfdf6
fix(review): preserve replay guard history
seonghobae Jul 21, 2026
71528f6
fix(review): close cross-job source and log boundaries
seonghobae Jul 21, 2026
9672e01
synthetic PR 604 + PR 563 verification
seonghobae Jul 21, 2026
df7be37
fix(review): bound DeepSeek fallback runtime
seonghobae Jul 21, 2026
28c71a9
test(review): clarify missing dependency locks
seonghobae Jul 21, 2026
533accd
test(review): scope dependency hash assertions
seonghobae Jul 21, 2026
6daf661
synthetic current central sequence verification
seonghobae Jul 22, 2026
8cc232e
fix(security): patch Strix pyasn1 DoS
seonghobae Jul 22, 2026
4d8a00d
test(review): harden dependency lock contract
seonghobae Jul 22, 2026
a63ac72
test(review): normalize dependency lock parsing
seonghobae Jul 22, 2026
5b9a8b1
Merge remote-tracking branch 'origin/main' into agent/prep-central-se…
seonghobae Jul 26, 2026
358db7f
fix(review): reject contradictory probe citations
seonghobae Jul 26, 2026
039f409
fix(review): fail closed on trust-boundary scans
seonghobae Jul 26, 2026
4f8981a
fix(review): preserve primary model review budget
seonghobae Jul 26, 2026
0e15a23
fix(review): stop when no models are runnable
seonghobae Jul 26, 2026
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
323 changes: 271 additions & 52 deletions .github/workflows/opencode-review.yml

Large diffs are not rendered by default.

296 changes: 296 additions & 0 deletions requirements-opencode-review-ci-hashes.txt

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions requirements-opencode-review-ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,12 @@ interrogate==1.7.0
pytest==9.1.1
pytest-cov==7.1.0
uv==0.11.25

# Trusted runtime set required to execute ContextualWisdomLab/saju-caldav tests
# inside the networkless OpenCode coverage sandbox. Keep these exact pins aligned
# with that repository's reviewed lockfile; PR-controlled manifests remain inert.
bcrypt==5.0.0
fastapi==0.139.2
httpx==0.28.1
icalendar==7.2.0
korean-lunar-calendar==0.4.0
1 change: 1 addition & 0 deletions requirements-strix-ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ google-cloud-aiplatform==1.133.0
protobuf<7.0.0
cryptography==49.0.0
python-multipart==0.0.31
# CVE-2026-59885 and CVE-2026-59886: ASN.1 decoder DoS; fixed in 0.6.4
pyasn1==0.6.4
156 changes: 156 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,161 @@ def adversarial_probe_source_receipt_error(
return ""


def evidence_cites_probe_path_at_any_line(evidence: str, path: str) -> bool:
"""Return whether evidence already cites any positive line for the probe path."""
escaped_path = rf"(?<![A-Za-z0-9_./-]){re.escape(path)}"
return (
re.search(
rf"{escaped_path}(?::|#L|\s+line\s+)[1-9][0-9]*\b",
evidence,
re.IGNORECASE,
)
is not None
)


def receipt_verified_evidence_location(
evidence: str,
changed_files: frozenset[str],
) -> tuple[str, int] | None:
"""Return one evidence citation uniquely bound to its trusted source receipt.

A model can place a valid changed-file ``path:line`` citation and its exact
source-line receipt in ``evidence`` while copying a different location into
the redundant structured fields. Consider only safe current-head changed
files cited by the evidence, recompute every cited line receipt from the
sealed source tree, and return a location only when exactly one distinct
citation matches the single model receipt. Ambiguous or unverified evidence
remains unrepairable and fails closed.
"""
receipts = SOURCE_LINE_RECEIPT_RE.findall(evidence)
if len(receipts) != 1:
return None
receipt = receipts[0].casefold()
matches: set[tuple[str, int]] = set()
for path in changed_files:
escaped_path = rf"(?<![A-Za-z0-9_./-]){re.escape(path)}"
citation_re = re.compile(
rf"{escaped_path}(?::|#L|\s+line\s+)([1-9][0-9]*)\b",
re.IGNORECASE,
)
for citation in citation_re.finditer(evidence):
line = int(citation.group(1))
if adversarial_probe_location_error(path, line):
continue
digest = adversarial_probe_source_line_digest(path, line)
if digest is not None and digest.casefold() == receipt:
matches.add((path, line))
if len(matches) != 1:
return None
return next(iter(matches))


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``, or cite and receipt-bind one changed source line in
``evidence`` while copying a different valid changed-file location into the
redundant structured fields. Restore only a missing citation or rebind the
structured location when exactly one cited line matches the existing single
receipt in the sealed current-head tree. Missing, duplicate, ambiguous, 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:
repaired_probes.append(probe)
continue
repaired_evidence = evidence
repaired_path = path
repaired_line = line
rejection = adversarial_evidence_rejection_reason(repaired_evidence, path, line)
if rejection == "must cite the exact probe path and positive line":
if not adversarial_probe_source_receipt_error(evidence, path, line):
if evidence_cites_probe_path_at_any_line(evidence, path):
repaired_probes.append(probe)
continue
repaired_evidence = f"{path}:{line} {repaired_evidence}"
else:
rebound_location = receipt_verified_evidence_location(
evidence,
changed_files,
)
if rebound_location is None:
repaired_probes.append(probe)
continue
repaired_path, repaired_line = rebound_location
elif rejection:
repaired_probes.append(probe)
continue
if adversarial_probe_source_receipt_error(
repaired_evidence, repaired_path, repaired_line
) or adversarial_evidence_rejection_reason(
repaired_evidence,
repaired_path,
repaired_line,
):
repaired_probes.append(probe)
continue
if (
repaired_evidence == evidence
and repaired_path == path
and repaired_line == line
):
repaired_probes.append(probe)
else:
repaired_probes.append(
{
**probe,
"path": repaired_path,
"line": repaired_line,
"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 +1422,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
17 changes: 12 additions & 5 deletions scripts/ci/run_opencode_review_model_pool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,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/deepseek-r1 | github-models/deepseek/deepseek-r1-0528)
cap_seconds="$(env_integer_or_default OPENCODE_GITHUB_DEEPSEEK_R1_RUN_TIMEOUT_SECONDS 300)"
;;
*)
printf '%s\n' "$run_timeout_seconds"
return 0
Expand Down Expand Up @@ -547,9 +550,6 @@ main() {
fi
fi
deadline=0
if [ "$budget_seconds" -gt 0 ]; then
deadline=$((SECONDS + budget_seconds))
fi
: >"$OPENCODE_OUTPUT_FILE"
cd "$OPENCODE_REVIEW_WORKDIR"
read -r -a model_candidates <<<"${OPENCODE_MODEL_CANDIDATES:-}"
Expand All @@ -573,6 +573,7 @@ main() {
continue
fi
if should_skip_model_candidate "$model_candidate"; then
dead_candidate_reasons[$model_candidate]="not runnable with the credentials available to this job"
continue
fi
assert_reasoning_effort_for_candidate "$model_candidate"
Expand All @@ -582,6 +583,12 @@ main() {
opencode_json_file="${candidate_output_file}.jsonl"
opencode_export_file="${candidate_output_file}.session.json"
write_prompt "$model_candidate" "$prompt_file"
# The retry budget measures provider attempts. Trusted local prompt
# preparation can be slower on a busy runner and must not exhaust the
# budget before the first provider is invoked.
if [ "$deadline" -eq 0 ] && [ "$budget_seconds" -gt 0 ]; then
deadline=$((SECONDS + budget_seconds))
fi
for attempt in $(seq 1 "$attempts"); do
now="$SECONDS"
if [ "$deadline" -gt 0 ] && [ "$now" -ge "$deadline" ]; then
Expand Down Expand Up @@ -610,7 +617,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 Expand Up @@ -665,7 +672,7 @@ main() {
fi
done
if [ "$alive_candidates" -eq 0 ]; then
printf 'Every OpenCode model candidate is marked failed for this run; ending the pool without further provider spend.\n'
printf 'No runnable OpenCode model candidates remain for this run; ending the pool without further provider spend or idle cycles.\n'
if finish_pool_without_model; then
exit 0
fi
Expand Down
50 changes: 48 additions & 2 deletions scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,28 @@ assert_file_not_contains() {
fi
}

assert_file_not_matches() {
local file_path="$1"
local pattern="$2"
local message="$3"
local grep_status

if [ ! -f "$file_path" ]; then
record_failure "$message (missing file '$file_path')"
print_assertion_source "$file_path"
return
fi
if grep -Eq -- "$pattern" "$file_path"; then
record_failure "$message (unexpected pattern '$pattern')"
else
grep_status=$?
if [ "$grep_status" -ne 1 ]; then
record_failure "$message (grep failed with exit $grep_status for pattern '$pattern')"
print_assertion_source "$file_path"
fi
fi
}

seal_opencode_test_artifacts() {
local runner_temp="$1"
local head_sha="$2"
Expand Down Expand Up @@ -602,8 +624,12 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() {
assert_file_contains "$workflow_file" 'locked_version" != "4.0.4"' "opencode review verifies both nested installed and locked picomatch evidence"
assert_file_contains "$workflow_file" '"$CODEGRAPH_BIN" explore' "opencode review precomputes structural evidence outside the model process"
assert_file_contains "$workflow_file" '"$CODEGRAPH_BIN" --version' "opencode review logs the exact trusted CodeGraph version"
assert_file_contains "$workflow_file" 'cat "$codegraph_status" >&2' "opencode review exposes CodeGraph status failures in the job log"
assert_file_contains "$workflow_file" 'cat "$codegraph_raw" >&2' "opencode review exposes CodeGraph exploration failures in the job log"
assert_file_not_contains "$workflow_file" 'cat "$codegraph_status" >&2' "opencode review does not replay PR-derived CodeGraph status bytes as workflow commands"
assert_file_not_contains "$workflow_file" 'cat "$codegraph_raw" >&2' "opencode review does not replay PR-derived CodeGraph exploration bytes as workflow commands"
assert_file_not_matches "$workflow_file" '^[[:space:]]*cat "\$CODEGRAPH_EVIDENCE_FILE"[[:space:]]*$' "opencode review does not replay assembled PR-derived CodeGraph evidence into the command channel"
assert_file_contains "$workflow_file" 'CodeGraph status command failed; captured %s bytes without replaying PR-derived log content.' "opencode review reports bounded CodeGraph status failure metadata"
assert_file_contains "$workflow_file" 'CodeGraph exploration command failed; captured %s bytes without replaying PR-derived log content.' "opencode review reports bounded CodeGraph exploration failure metadata"
assert_file_contains "$workflow_file" 'Captured bounded CodeGraph evidence (%s bytes) without replaying PR-derived log content.' "opencode review reports bounded CodeGraph success metadata"
assert_file_not_contains "$workflow_file" "serve --mcp" "opencode review must not fetch or launch CodeGraph again for MCP"
assert_file_not_contains "$workflow_file" "https://mcp.deepwiki.com/mcp" "opencode review does not expose remote MCP to the model"
assert_file_not_contains "$workflow_file" "@upstash/context7-mcp@3.1.0" "opencode review does not install Context7 at runtime"
Expand Down Expand Up @@ -1024,6 +1050,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() {
assert_file_contains "$workflow_file" '(.workflow // "") == "CodeQL"' "opencode approval can distinguish CodeQL dynamic setup checks"
assert_file_contains "$workflow_file" '((.isRequired // false) | not) and (.workflow // "") == "CodeQL"' "opencode approval ignores non-required cancelled CodeQL checks without source evidence"
assert_file_contains "$workflow_file" '(.name // "") == "scan-pr-queue" and ((.workflow // "") == "PR Review Merge Scheduler" or (.workflow // "") == "Required PR Review Merge Scheduler")' "opencode approval ignores cancelled scheduler queue replacement checks without source evidence"
assert_file_not_contains "$workflow_file" 'select(((.conclusion // "" | ascii_downcase) == "cancelled" and (.name // "") == "scan-pr-queue") | not)' "opencode approval keeps ownership-ambiguous cancelled scan-pr-queue checks blocking in the workflow-less REST fallback"
assert_file_contains "$workflow_file" 'grep -Fq -- "Strix Security Scan/strix:" "$rollup_file"' "opencode approval avoids duplicate supplemental Strix workflow-run blockers when statusCheckRollup already has the Strix check"
assert_file_contains "$workflow_file" 'current_head_manual_strix_success_status()' "opencode approval can identify same-head manual Strix success status evidence"
assert_file_contains "$workflow_file" 'manual_run_line="$(latest_current_head_manual_strix_run || true)"' "opencode approval falls back to same-head manual Strix check-run success when commit status publication is unavailable"
Expand Down Expand Up @@ -5637,11 +5664,30 @@ run_gate_case_allow_provider_signal() {
run_gate_case_with_provider_signal_mode "0" "$@"
}

test_assert_file_not_matches_fails_closed() {
local fixture_file

fixture_file="$(mktemp)"
printf 'safe fixture\n' >"$fixture_file"
if ! (
FAILURES=0
assert_file_not_matches "$fixture_file" "[" "invalid regex must fail closed"
assert_file_not_matches "${fixture_file}.missing" "safe" "missing file must fail closed"
[ "$FAILURES" -eq 2 ]
) >/dev/null 2>&1; then
record_failure "assert_file_not_matches must record missing-file and grep failures"
fi
rm -f "$fixture_file"
}

run_filtered_gate_case_if_requested() {
case "${STRIX_TEST_CASE_FILTER:-}" in
"")
return 0
;;
assert-file-not-matches-errors-fail-closed)
test_assert_file_not_matches_fails_closed
;;
success)
run_gate_case "success" \
"vertex_ai/ready-primary" \
Expand Down
Loading
Loading