diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 74cc90ef9..14fcf2666 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -1297,6 +1297,7 @@ jobs: -type f \ -not -path '*/node_modules/*' \ -print >"$summary_list" + chmod 0444 "$summary_list" if [ ! -s "$summary_list" ]; then append "### JavaScript/TypeScript coverage threshold" diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 926ff5fe8..e9964c6d4 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -1001,6 +1001,7 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" "JavaScript/TypeScript dependencies (npm ci, lifecycle hooks disabled)" "opencode coverage evidence installs npm workspace dependencies without lifecycle hooks before JS coverage" assert_file_contains "$workflow_file" "coverage/coverage-summary.json" "opencode coverage evidence reads JS coverage summaries instead of trusting test exit codes" assert_file_contains "$workflow_file" "coverage/coverage-final.json" "opencode coverage evidence supports Vitest Istanbul final coverage files" + assert_file_contains "$workflow_file" 'chmod 0444 "$summary_list"' "opencode coverage makes the root-created summary list readable by the unprivileged sandbox user" assert_file_contains "$workflow_file" "javascript_coverage_gate.py" "opencode coverage evidence delegates changed-source measurement to the tested central gate" assert_file_contains "$workflow_file" '--base-sha "$PR_BASE_SHA"' "opencode changed-source coverage is bound to the pull request base" assert_file_contains "$workflow_file" '--head-sha "$PR_HEAD_SHA"' "opencode changed-source coverage is bound to the current pull request head" diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index f0f0839f3..b9dbb106a 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -408,7 +408,20 @@ def test_opencode_target_coverage_materializes_only_after_authorized_dispatch(): assert '--store-dir "$writable_pnpm_store_dir"' in measure_step assert "pnpm offline install" in measure_step assert "--offline" in measure_step - assert 'find "$COVERAGE_SOURCE_WORKDIR"' in measure_step + coverage_function_start = measure_step.index( + " check_javascript_coverage_thresholds() {\n" + ) + coverage_function_end = measure_step.index( + "\n }\n", coverage_function_start + ) + coverage_function = measure_step[coverage_function_start:coverage_function_end] + summary_find = coverage_function.index('find "$COVERAGE_SOURCE_WORKDIR"') + summary_find_complete = coverage_function.index( + '-print >"$summary_list"', summary_find + ) + summary_chmod = coverage_function.index('chmod 0444 "$summary_list"') + summary_argument = coverage_function.index('--summary-list "$summary_list"') + assert summary_find < summary_find_complete < summary_chmod < summary_argument assert '--repo-root "$COVERAGE_SOURCE_WORKDIR"' in measure_step assert "javascript_coverage_ran_any=1" in measure_step assert measure_step.count("check_javascript_coverage_thresholds") == 2