Skip to content
Merged
16 changes: 14 additions & 2 deletions scripts/ci/opencode_review_normalize_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,13 +669,25 @@ def valid_control(
}


def extract_dicts(obj: Any) -> list[Any]:
"""Recursively extract all dictionaries from a JSON-like object."""
results = []
if isinstance(obj, dict):
results.append(obj)
for v in obj.values():
results.extend(extract_dicts(v))
elif isinstance(obj, list):
for item in obj:
results.extend(extract_dicts(item))
return results

def iter_json_objects(text: str) -> list[Any]:
"""Extract JSON objects from raw OpenCode output that may include prose."""
decoder = json.JSONDecoder()
values: list[Any] = []

try:
values.append(json.loads(text))
return extract_dicts(json.loads(text))
except json.JSONDecodeError:
# OpenCode exports may contain prose around the JSON control object.
pass
Expand All @@ -693,7 +705,7 @@ def iter_json_objects(text: str) -> list[Any]:
continue
try:
value, new_index = decoder.raw_decode(text, index)
values.append(value)
values.extend(extract_dicts(value))
# ⚡ Bolt: Advance index to avoid O(N^2) redundant parsing of nested JSON blocks
index = new_index
continue
Expand Down
8 changes: 4 additions & 4 deletions scripts/ci/strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,10 @@ evaluate_pull_request_findings() {
continue
fi
found_any_vuln_file=1
if vulnerability_file_is_retryable_model_inconsistency "$vuln_file"; then
found_retryable_model_inconsistency=1
continue
fi
rank="$(extract_first_severity_rank "$vuln_file")"
if [ "$rank" -lt 0 ]; then
PR_FINDINGS_DECISION="block_unmapped"
Expand All @@ -1890,10 +1894,6 @@ evaluate_pull_request_findings() {
if [ "$rank" -lt "$threshold_rank" ]; then
continue
fi
if vulnerability_file_is_retryable_model_inconsistency "$vuln_file"; then
found_retryable_model_inconsistency=1
continue
fi
mapfile -t vulnerability_locations < <(extract_vulnerability_locations "$vuln_file")
if [ "${#vulnerability_locations[@]}" -eq 0 ]; then
PR_FINDINGS_DECISION="block_unmapped"
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 @@ -998,7 +998,7 @@ assert_pr_review_merge_scheduler_uses_github_actions_bot_token() {
assert_file_contains "$scheduler_file" "same-head OpenCode dispatched" "scheduler records review dispatch after completed security evidence"
assert_file_contains "$workflow_file" "--pr-number" "scheduler scopes required-workflow PR events to the current pull request"
assert_file_contains "$workflow_file" "--review-workflow \"Required OpenCode Review\"" "scheduler dispatches the canonical required OpenCode Review workflow"
assert_file_contains "$readme_file" "github-actions[bot]" "README documents that mechanical branch updates and merges are attributed to GitHub Actions bot"
assert_file_contains "$readme_file" "PR_REVIEW_MERGE_TOKEN" "README documents that mechanical branch updates and merges use the central mutation credential"
assert_file_contains "$fix_workflow_file" 'workflow_call:' "fix scheduler can run as the central reusable autofix-dispatch workflow"
assert_file_contains "$fix_workflow_file" 'repository: ContextualWisdomLab/.github' "fix scheduler checks out the canonical implementation instead of relying on repo-local scheduler code"
assert_file_contains "$fix_workflow_file" 'GH_TOKEN: ${{ github.token }}' "fix scheduler uses the caller repository workflow token for dispatch markers"
Expand Down
7 changes: 4 additions & 3 deletions tests/test_opencode_review_normalize_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,10 +679,11 @@ def raise_for_evidence(path, *args, **kwargs):


def test_iter_json_objects_extracts_raw_and_embedded_json():
assert norm.iter_json_objects('{"a": 1}') == [{"a": 1}, {"a": 1}]
assert norm.iter_json_objects('{"a": 1}') == [{"a": 1}]
assert norm.iter_json_objects('prefix {"b": 2} suffix') == [{"b": 2}]
assert norm.iter_json_objects('prefix {"outer": {"inner": 1}} suffix') == [
{"outer": {"inner": 1}}
assert norm.iter_json_objects('prefix {"wrapper": {"control": true}} suffix') == [
{"wrapper": {"control": True}},
{"control": True},
]
assert norm.iter_json_objects("prefix { } suffix") == [{}]
assert norm.iter_json_objects("prefix {not json}") == []
Expand Down