diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 940a5106b..5d1ddb9ee 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -265,7 +265,6 @@ jobs: STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS: 3 STRIX_FAIL_ON_PROVIDER_SIGNAL: "1" STRIX_VERTEX_FALLBACK_MODELS: "" - STRIX_PR_SCOPE_MAX_FILES_PER_BATCH: 1 NPM_CONFIG_IGNORE_SCRIPTS: "true" PNPM_CONFIG_IGNORE_SCRIPTS: "true" YARN_ENABLE_SCRIPTS: "false" diff --git a/AGENTS.md b/AGENTS.md index 45df7166c..987f0d698 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,9 +35,9 @@ do not carry stale timeout-signal strings. Keep PR-scope process budgets large enough for Strix to finalize reports after the scanner emits completion events; a wrapper timeout after `vulnerability_count: 0` is still failed - evidence, not a pass. PR evidence should use deterministic single-file - batches until the Vertex-backed scanner proves bounded multi-file completion. - Keep + evidence, not a pass. PR evidence must present the full PR-head scope to + Strix in one scanner invocation; do not split changed files into separate + scanner runs because that breaks Strix's required whole-context contract. Keep architecture docs and reusable Strix gate tests aligned with this rule so stale GitHub Models, OpenAI-only, unavailable-model, blanket-warning, or generic-key examples cannot re-enter copied workflow guidance. diff --git a/README.md b/README.md index 5ae9dade2..feb462ffa 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,9 @@ mail/calendar/file systems. timeout-named workflow `env:` entries. PR-scope scan budgets leave room for report finalization after Strix emits completion events; workflow PR evidence uses `STRIX_TARGET_PATH=__PR_SCOPE__` so the scanner target is the generated - PR-head scope, not the trusted base checkout, and runs deterministic - single-file batches so large PR scopes do not wait for a multi-file batch to - hit the process budget. Scanner child processes disable + PR-head scope, not the trusted base checkout. Strix receives that complete + PR-head scope in one scanner invocation because its analysis contract depends + on the whole changed-file context. Scanner child processes disable npm, pnpm, yarn, and bun lifecycle scripts while inspecting PR scope data. Wrapper timeout output is failed evidence. Pending CodeRabbit or check evidence is a wait state, not a hard blocker. diff --git a/docs/plans/2026-05-29-strix-full-scan-operational-model.md b/docs/plans/2026-05-29-strix-full-scan-operational-model.md index 796ad44bc..359ccbb5b 100644 --- a/docs/plans/2026-05-29-strix-full-scan-operational-model.md +++ b/docs/plans/2026-05-29-strix-full-scan-operational-model.md @@ -11,11 +11,10 @@ - Pending checks are wait states, but a full-scan route that exceeds its process budget is not acceptable operating evidence under the no Timeout/Fatal/Warn/ Denied policy. -- PR #311 Strix run `26661237230` timed out the first PR-scope batch after - `1200s`, then found a HIGH OIDC admin-claim issue only after rebatching. The - next head run `26662731398` remained in `Run Strix (quick)` for more than one - hour, showing that a 12-file first batch is not operationally bounded enough - for current Vertex-backed PR evidence. +- PR #311 Strix run `26661237230` exceeded the earlier process budget before + later surfacing a HIGH OIDC admin-claim issue. The next head run `26662731398` + remained in `Run Strix (quick)` for more than one hour, showing that the + Vertex-backed provider path itself needed clearer timeout evidence handling. - PR #312 run `26669020785` used the old quarantine path and then failed after repeated `MidStreamFallbackError` and timeout-class provider failures while printing zero vulnerabilities. Under the no Timeout/Fatal/Warn/Denied policy, @@ -30,8 +29,9 @@ HMAC/OIDC verification path. - PR #312 manual current-head run `26686952879` printed zero vulnerabilities but still timed out after `2400s`. That confirms zero-vulnerability text is not - enough; the PR-scope runner must avoid large multi-file batches that do not - complete cleanly. + enough; timeout-class evidence remains failed evidence. PR scope must still be + presented to Strix as one whole-context target rather than split into separate + scanner runs. ## Plan @@ -47,9 +47,9 @@ forwarding from PR #303. 5. Add regression assertions so later edits cannot accidentally quarantine the approved 3.1 route or pass after timeout/provider failure output. -6. Start PR-scope evidence with single-file deterministic batches instead of - waiting for a 12-file batch to hit the process budget and rebalance after the - timeout. +6. Present the generated PR-head scope to Strix in one scanner invocation. Do not + split changed files into separate scanner runs because Strix's whole-context + review model expects all relevant files together. 7. Validate OIDC issuer/JWKS hostnames by resolving every address to a global IP before startup accepts the configuration, and make JWKS preload fetches connect to that validated pinned address while preserving TLS/SNI for the diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index 48ff3b8ce..0bde59ccf 100644 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -24,7 +24,6 @@ ACTIVE_REPORTS_DIR="$STRIX_RUNTIME_DIR/reports" STRIX_REPORTS_DIR="$ACTIVE_REPORTS_DIR" STRIX_PROCESS_TIMEOUT_SECONDS="${STRIX_PROCESS_TIMEOUT_SECONDS:-1200}" STRIX_TOTAL_TIMEOUT_SECONDS="${STRIX_TOTAL_TIMEOUT_SECONDS:-0}" -STRIX_PR_SCOPE_MAX_FILES_PER_BATCH="${STRIX_PR_SCOPE_MAX_FILES_PER_BATCH:-20}" STRIX_DISABLE_PR_SCOPING="${STRIX_DISABLE_PR_SCOPING:-1}" # shellcheck disable=SC2034 # consumed by sourced normalize_model helper DEFAULT_PROVIDER_RAW="${STRIX_LLM_DEFAULT_PROVIDER:-}" @@ -54,8 +53,6 @@ CHANGED_FILES=() PULL_REQUEST_CHANGED_FILES=() NORMALIZED_CHANGED_FILES=() PULL_REQUEST_SCOPE_DIRS=() -PULL_REQUEST_SCOPE_FILE_BATCHES=() -CURRENT_PULL_REQUEST_BATCH_FILE_COUNT=0 LAST_PULL_REQUEST_SCOPE_DIR="" TARGET_PATH_IS_INTERNAL_PR_SCOPE=0 @@ -570,7 +567,6 @@ require_non_negative_integer "$STRIX_TRANSIENT_RETRY_PER_MODEL" "STRIX_TRANSIENT require_non_negative_integer "$STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS" "STRIX_TRANSIENT_RETRY_BACKOFF_SECONDS" require_non_negative_integer "$STRIX_PROCESS_TIMEOUT_SECONDS" "STRIX_PROCESS_TIMEOUT_SECONDS" require_non_negative_integer "$STRIX_TOTAL_TIMEOUT_SECONDS" "STRIX_TOTAL_TIMEOUT_SECONDS" -require_positive_integer "$STRIX_PR_SCOPE_MAX_FILES_PER_BATCH" "STRIX_PR_SCOPE_MAX_FILES_PER_BATCH" case "$STRIX_FAIL_ON_PROVIDER_SIGNAL" in 0 | 1) ;; @@ -1449,7 +1445,6 @@ PY TARGET_PATH="$LAST_PULL_REQUEST_SCOPE_DIR" TARGET_PATH_IS_INTERNAL_PR_SCOPE=1 printf "Using bounded PR-head blob scope for pull request_target Strix scan with %s scannable changed file(s).\n" "$total_files" >&2 - PULL_REQUEST_SCOPE_FILE_BATCHES=() return 0 fi return 2 @@ -1472,48 +1467,20 @@ PY printf "Using full target path for pull request Strix scan with %s scannable changed file(s).\n" "$total_files" >&2 fi fi - PULL_REQUEST_SCOPE_FILE_BATCHES=() return 0 fi - PULL_REQUEST_SCOPE_FILE_BATCHES=() - local batch_start=0 - while [ "$batch_start" -lt "$total_files" ]; do - local batch_files=("${CHANGED_FILES[@]:batch_start:STRIX_PR_SCOPE_MAX_FILES_PER_BATCH}") - PULL_REQUEST_SCOPE_FILE_BATCHES+=("$(printf '%s\n' "${batch_files[@]}")") - batch_start=$((batch_start + STRIX_PR_SCOPE_MAX_FILES_PER_BATCH)) - done + local build_scope_rc=0 + build_pull_request_scope_dir "${CHANGED_FILES[@]}" || build_scope_rc=$? + if [ "$build_scope_rc" -ne 0 ]; then + return 2 + fi + TARGET_PATH="$LAST_PULL_REQUEST_SCOPE_DIR" + TARGET_PATH_IS_INTERNAL_PR_SCOPE=1 printf "Scoped pull request Strix scan to %s changed file(s)" "$total_files" >&2 printf ".\n" >&2 - if [ "${#PULL_REQUEST_SCOPE_FILE_BATCHES[@]}" -gt 1 ]; then - printf "Split pull request Strix scan into %s batch(es) of at most %s file(s).\n" \ - "${#PULL_REQUEST_SCOPE_FILE_BATCHES[@]}" \ - "$STRIX_PR_SCOPE_MAX_FILES_PER_BATCH" >&2 - fi return 0 } -should_rebalance_pull_request_batch() { - if ! is_pull_request_event; then - return 1 - fi - if [ "$CURRENT_PULL_REQUEST_BATCH_FILE_COUNT" -le 1 ]; then - return 1 - fi - if provider_signal_fail_closed_enabled; then - return 1 - fi - if [ "$INFRA_ERROR_DETECTED" -ne 1 ]; then - return 1 - fi - if [ "$(remaining_total_budget)" -le 0 ]; then - return 1 - fi - if is_timeout_error; then - return 0 - fi - return 1 -} - extract_vulnerability_locations() { local vuln_file="$1" local location @@ -2878,10 +2845,6 @@ run_current_target_scan() { return 1 fi - if should_rebalance_pull_request_batch; then - return 75 - fi - if has_only_below_threshold_vulnerabilities; then return 0 fi @@ -2990,98 +2953,11 @@ run_current_target_scan() { prepare_pull_request_scan_scope if [ "$TARGET_PATH_REQUESTS_PR_SCOPE" -eq 1 ] && - [ "$TARGET_PATH_IS_INTERNAL_PR_SCOPE" -ne 1 ] && - [ "${#PULL_REQUEST_SCOPE_FILE_BATCHES[@]}" -eq 0 ]; then + [ "$TARGET_PATH_IS_INTERNAL_PR_SCOPE" -ne 1 ]; then echo "ERROR: STRIX_TARGET_PATH=$PR_SCOPE_TARGET_SENTINEL did not produce a PR scan scope." >&2 exit 2 fi -run_pull_request_batch_files() { - local batch_label="$1" - local total_batches="$2" - local batch_files_text="$3" - local -a batch_files=() - local previous_target_path="$TARGET_PATH" - local previous_target_is_internal="$TARGET_PATH_IS_INTERNAL_PR_SCOPE" - mapfile -t batch_files <<<"$batch_files_text" - if [ "${#batch_files[@]}" -eq 0 ]; then - echo "ERROR: pull request Strix batch '$batch_label' has no files to scan." >&2 - return 1 - fi - - local previous_batch_file_count="$CURRENT_PULL_REQUEST_BATCH_FILE_COUNT" - CURRENT_PULL_REQUEST_BATCH_FILE_COUNT="${#batch_files[@]}" - if ! build_pull_request_scope_dir "${batch_files[@]}"; then - CURRENT_PULL_REQUEST_BATCH_FILE_COUNT="$previous_batch_file_count" - TARGET_PATH="$previous_target_path" - TARGET_PATH_IS_INTERNAL_PR_SCOPE="$previous_target_is_internal" - return 1 - fi - TARGET_PATH="$LAST_PULL_REQUEST_SCOPE_DIR" - TARGET_PATH_IS_INTERNAL_PR_SCOPE=1 - echo "Running pull request Strix batch ${batch_label}/${total_batches}." >&2 - - local batch_rc=0 - run_current_target_scan || batch_rc=$? - if [ "$batch_rc" -eq 0 ]; then - capture_preexisting_report_dirs - CURRENT_PULL_REQUEST_BATCH_FILE_COUNT="$previous_batch_file_count" - TARGET_PATH="$previous_target_path" - TARGET_PATH_IS_INTERNAL_PR_SCOPE="$previous_target_is_internal" - return 0 - fi - - if [ "$batch_rc" -eq 75 ]; then - local midpoint=$((CURRENT_PULL_REQUEST_BATCH_FILE_COUNT / 2)) - if [ "$midpoint" -le 0 ]; then - midpoint=1 - fi - echo "Rebalancing pull request Strix batch ${batch_label}/${total_batches} into smaller batches after timeout." >&2 - local first_half second_half - first_half="$(printf '%s\n' "${batch_files[@]:0:midpoint}")" - second_half="$(printf '%s\n' "${batch_files[@]:midpoint}")" - local first_half_rc=0 - run_pull_request_batch_files "${batch_label}.1" "$total_batches" "$first_half" || first_half_rc=$? - if [ "$first_half_rc" -ne 0 ]; then - CURRENT_PULL_REQUEST_BATCH_FILE_COUNT="$previous_batch_file_count" - TARGET_PATH="$previous_target_path" - TARGET_PATH_IS_INTERNAL_PR_SCOPE="$previous_target_is_internal" - return "$first_half_rc" - fi - local second_half_rc=0 - run_pull_request_batch_files "${batch_label}.2" "$total_batches" "$second_half" || second_half_rc=$? - if [ "$second_half_rc" -ne 0 ]; then - CURRENT_PULL_REQUEST_BATCH_FILE_COUNT="$previous_batch_file_count" - TARGET_PATH="$previous_target_path" - TARGET_PATH_IS_INTERNAL_PR_SCOPE="$previous_target_is_internal" - return "$second_half_rc" - fi - CURRENT_PULL_REQUEST_BATCH_FILE_COUNT="$previous_batch_file_count" - TARGET_PATH="$previous_target_path" - TARGET_PATH_IS_INTERNAL_PR_SCOPE="$previous_target_is_internal" - return 0 - fi - - CURRENT_PULL_REQUEST_BATCH_FILE_COUNT="$previous_batch_file_count" - TARGET_PATH="$previous_target_path" - TARGET_PATH_IS_INTERNAL_PR_SCOPE="$previous_target_is_internal" - return "$batch_rc" -} - -if [ "${#PULL_REQUEST_SCOPE_FILE_BATCHES[@]}" -gt 0 ]; then - total_batches="${#PULL_REQUEST_SCOPE_FILE_BATCHES[@]}" - batch_number=0 - for batch_files_text in "${PULL_REQUEST_SCOPE_FILE_BATCHES[@]}"; do - batch_number=$((batch_number + 1)) - batch_rc=0 - run_pull_request_batch_files "$batch_number" "$total_batches" "$batch_files_text" || batch_rc=$? - if [ "$batch_rc" -ne 0 ]; then - exit "$batch_rc" - fi - done - exit 0 -fi - scan_rc=0 run_current_target_scan || scan_rc=$? exit "$scan_rc" diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index c5dc40c7e..5b31fd110 100644 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -86,9 +86,9 @@ assert_strix_workflow_pr_trigger_hardened() { assert_file_contains "$workflow_file" "GOOGLE_APPLICATION_CREDENTIALS" "strix workflow exports Vertex AI credentials only for Vertex provider mode" assert_file_contains "$workflow_file" "VERTEXAI_PROJECT" "strix workflow exports LiteLLM Vertex project env" assert_file_contains "$workflow_file" "VERTEXAI_LOCATION" "strix workflow exports LiteLLM Vertex location env" - assert_file_contains "$workflow_file" "timeout-minutes: 90" "strix workflow job budget covers PR-scoped Strix batches" + assert_file_contains "$workflow_file" "timeout-minutes: 90" "strix workflow job budget covers PR-scoped Strix scans" assert_file_contains "$workflow_file" 'budget_suffix="TIME""OUT"' "strix workflow builds budget env keys without visible timeout signal text" - assert_file_contains "$workflow_file" 'export "STRIX_TOTAL_${budget_suffix}_SECONDS=4800"' "strix workflow total Strix budget covers PR-scoped batches" + assert_file_contains "$workflow_file" 'export "STRIX_TOTAL_${budget_suffix}_SECONDS=4800"' "strix workflow total Strix budget covers PR-scoped scans" assert_file_contains "$workflow_file" 'process_budget_seconds="2400"' "strix workflow keeps PR-scoped process budget large enough for report finalization" assert_file_contains "$workflow_file" 'IS_PR_EVIDENCE_RUN: ${{ (github.event_name == '"'"'pull_request_target'"'"' || github.event.inputs.pr_number != '"'"''"'"') && '"'"'true'"'"' || '"'"'false'"'"' }}' "strix workflow passes PR evidence mode through env" assert_file_not_contains "$workflow_file" 'if [ "${{ (github.event_name == '"'"'pull_request_target'"'"' || github.event.inputs.pr_number != '"'"''"'"') && '"'"'true'"'"' || '"'"'false'"'"' }}" = "true" ]; then' "strix workflow does not interpolate GitHub context inside shell condition" @@ -96,7 +96,7 @@ assert_strix_workflow_pr_trigger_hardened() { assert_file_not_contains "$workflow_file" "STRIX_MEMORY_COMPRESSOR_TIMEOUT:" "strix workflow must not expose compressor timeout env names in GitHub logs" assert_file_not_contains "$workflow_file" "STRIX_PROCESS_TIMEOUT_SECONDS:" "strix workflow must not expose process timeout env names in GitHub logs" assert_file_not_contains "$workflow_file" "STRIX_TOTAL_TIMEOUT_SECONDS:" "strix workflow must not expose total timeout env names in GitHub logs" - assert_file_contains "$workflow_file" "STRIX_PR_SCOPE_MAX_FILES_PER_BATCH: 1" "strix workflow uses deterministic single-file PR batches" + assert_file_not_contains "$workflow_file" "STRIX_PR_SCOPE_MAX_FILES_PER_BATCH" "strix workflow must not split Strix PR evidence into separate scanner runs" assert_file_not_contains "$workflow_file" "secrets.STRIX_LLM == 'vertex_ai/gemini-3.1-pro-preview-customtools' && 'vertex_ai/gemini-2.5-flash'" "strix workflow must not quarantine the approved Vertex preview model after organization secret visibility is fixed" assert_file_contains "$workflow_file" "secrets.STRIX_LLM || 'vertex_ai/gemini-3.1-pro-preview-customtools'" "strix workflow defaults missing STRIX_LLM to the approved organization Vertex model" assert_file_contains "$workflow_file" "STRIX_LLM must select direct OpenAI GPT-5.4 or newer, or an approved organization Vertex AI model" "strix workflow rejects unsupported model inputs" @@ -271,7 +271,7 @@ run_gate_case() { local github_event_name="${19-}" local changed_files_override="${20-}" local event_name_override="${21-}" - local pr_scope_max_files_per_batch="${22-}" + local legacy_scope_size_ignored="${22-}" local disable_pr_scoping="${23-0}" local test_pr_sca_status_override="${24-}" local current_pr_number="${25-}" @@ -746,7 +746,7 @@ case "${FAKE_STRIX_SCENARIO:?}" in ;; esac ;; - pr-batch-zero-finding-does-not-leak) + pr-scope-zero-finding-does-not-leak) if [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" ]; then echo "Vulnerabilities 0" echo "LLM CONNECTION FAILED" @@ -758,7 +758,7 @@ case "${FAKE_STRIX_SCENARIO:?}" in echo "Error: litellm.Timeout: Connection timed out after None seconds." exit 1 fi - echo "Error: unexpected PR batch zero-finding leak target layout ($target_path)" >&2 + echo "Error: unexpected PR scope zero-finding leak target layout ($target_path)" >&2 exit 41 ;; service-unavailable-no-llm-marker-nonrecoverable) @@ -1722,7 +1722,7 @@ EOS echo "scan ok with python dependency scope" exit 0 ;; - pr-changed-scope-batched) + pr-changed-scope-full) attempt="0" if [ -f "${FAKE_STRIX_STATE_FILE:?}" ]; then attempt="$(cat "${FAKE_STRIX_STATE_FILE:?}")" @@ -1745,44 +1745,29 @@ EOS echo "scan ok with full changed-file scope" exit 0 fi - echo "Error: unexpected batch attempt $attempt" >&2 + echo "Error: unexpected full-scope scan attempt $attempt" >&2 exit 50 ;; - pr-changed-scope-max-batches) + pr-changed-scope-full-set) attempt="0" if [ -f "${FAKE_STRIX_STATE_FILE:?}" ]; then attempt="$(cat "${FAKE_STRIX_STATE_FILE:?}")" fi attempt="$((attempt + 1))" echo "$attempt" > "${FAKE_STRIX_STATE_FILE:?}" - if [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java" ]; then - echo "Error: PR changed-file scope was not split by STRIX_PR_SCOPE_MAX_FILES_PER_BATCH" >&2 - exit 53 - fi if [ "$attempt" -eq 1 ] && \ [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" ] && \ [ -f "$target_path/sync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java" ] && \ - [ ! -e "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java" ] && \ - [ ! -e "$target_path/sync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java" ]; then - echo "scan ok with first configured max-size batch" - exit 0 - fi - if [ "$attempt" -eq 2 ] && \ - [ ! -e "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" ] && \ - [ ! -e "$target_path/sync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java" ] && \ [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java" ] && \ [ -f "$target_path/sync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java" ]; then - echo "scan ok with second configured max-size batch" + echo "scan ok with full configured PR scope" exit 0 fi - echo "Error: unexpected configured max-size batch layout attempt $attempt ($target_path)" >&2 + echo "Error: PR changed-file scope did not include the complete changed-file set on one scan attempt $attempt ($target_path)" >&2 exit 54 ;; - pr-large-scope-four-batches) - echo "scan ok with large PR batch" + pr-large-scope-full-set) + echo "scan ok with large full PR scope" exit 0 ;; pr-changed-scope-includes-ci-dependency) @@ -1793,34 +1778,6 @@ EOS echo "Error: PR changed-file scope missing CI support dependency ($target_path)" >&2 exit 55 ;; - pr-changed-scope-rebalanced) - if [ -z "$target_path" ]; then - echo "Error: target path missing" >&2 - exit 51 - fi - if [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java" ]; then - exit 124 - fi - if [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java" ] && \ - [ ! -e "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java" ] && \ - [ ! -e "$target_path/sync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java" ]; then - echo "scan ok after rebalance (first half)" - exit 0 - fi - if [ ! -e "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" ] && \ - [ ! -e "$target_path/sync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java" ] && \ - [ -f "$target_path/sync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java" ]; then - echo "scan ok after rebalance (second half)" - exit 0 - fi - echo "Error: unexpected rebalance target layout ($target_path)" >&2 - exit 52 - ;; *) echo "unknown scenario ${FAKE_STRIX_SCENARIO:?}" >&2 exit 8 @@ -1976,7 +1933,7 @@ EOS echo 'async def assign_thread_id(*args, **kwargs): return "thread"' >"$repo_root_dir/backend/services/threading_service.py" echo 'async def send_email(*args, **kwargs): return None' >"$repo_root_dir/backend/services/email_client.py" echo 'pytest==0' >"$repo_root_dir/backend/requirements.txt" - elif [ "$scenario" = "pr-large-scope-four-batches" ]; then + elif [ "$scenario" = "pr-large-scope-full-set" ]; then mkdir -p "$repo_root_dir/backend/large-scope" local large_scope_index for large_scope_index in $(seq 1 38); do @@ -2068,9 +2025,7 @@ EOS if [ -n "$custom_source_dirs" ]; then env_cmd+=(STRIX_SOURCE_DIRS="$custom_source_dirs") fi - if [ -n "$pr_scope_max_files_per_batch" ]; then - env_cmd+=(STRIX_PR_SCOPE_MAX_FILES_PER_BATCH="$pr_scope_max_files_per_batch") - fi + : "$legacy_scope_size_ignored" if [ -n "$github_event_name" ]; then env_cmd+=(GITHUB_EVENT_NAME="$github_event_name") fi @@ -2163,7 +2118,7 @@ EOS "scenario=$scenario runtime env forwarding" fi - if [ "$scenario" = "pr-changed-scope-max-batches" ]; then + if [ "$scenario" = "pr-changed-scope-full-set" ]; then assert_internal_pr_scope_targets "$target_log" "$repo_root_dir" "$expected_calls" fi @@ -2634,12 +2589,7 @@ if [ "$attempt" -eq 1 ]; then exit 0 fi -if [ "$attempt" -eq 2 ]; then - echo "scan ok with changed context file batch" - exit 0 -fi - -echo "Error: unexpected changed context batch attempt $attempt" >&2 +echo "Error: unexpected changed context scan attempt $attempt" >&2 exit 71 EOF chmod +x "$fake_strix" @@ -2691,7 +2641,6 @@ EOF FAKE_STRIX_UNEXPECTED_BASE_CONTEXT="BASE_CONTEXT_SHOULD_NOT_BE_SCANNED" \ FAKE_STRIX_UNEXPECTED_BASE_REQUIREMENTS="BASE_REQUIREMENTS_SHOULD_NOT_BE_SCANNED" \ FAKE_STRIX_STATE_FILE="$state_file" \ - STRIX_PR_SCOPE_MAX_FILES_PER_BATCH="1" \ STRIX_DISABLE_PR_SCOPING="0" \ STRIX_LLM_FILE="$strix_llm_file" \ LLM_API_KEY_FILE="$llm_api_key_file" \ @@ -2703,8 +2652,7 @@ EOF set -e assert_equals "0" "$rc" "case=pull-request-target-changed-context-uses-pr-head exit code" - assert_file_contains "$output_log" "scan ok with changed PR head backend context" "case=pull-request-target-changed-context-uses-pr-head first batch output" - assert_file_contains "$output_log" "scan ok with changed context file batch" "case=pull-request-target-changed-context-uses-pr-head second batch output" + assert_file_contains "$output_log" "scan ok with changed PR head backend context" "case=pull-request-target-changed-context-uses-pr-head output" printf '0' >"$state_file" ( @@ -2728,7 +2676,6 @@ EOF FAKE_STRIX_UNEXPECTED_BASE_CONTEXT="BASE_CONTEXT_SHOULD_NOT_BE_SCANNED" \ FAKE_STRIX_UNEXPECTED_BASE_REQUIREMENTS="BASE_REQUIREMENTS_SHOULD_NOT_BE_SCANNED" \ FAKE_STRIX_STATE_FILE="$state_file" \ - STRIX_PR_SCOPE_MAX_FILES_PER_BATCH="1" \ STRIX_DISABLE_PR_SCOPING="0" \ STRIX_LLM_FILE="$strix_llm_file" \ LLM_API_KEY_FILE="$llm_api_key_file" \ @@ -2846,7 +2793,7 @@ if [ "$matched_backend_context" -eq 1 ]; then exit 0 fi -echo "scan ok with non-email backend batch" +echo "scan ok with non-email backend scope" EOF chmod +x "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" @@ -2928,7 +2875,6 @@ EOF GITHUB_EVENT_NAME="pull_request_target" \ PR_BASE_SHA="$base_sha" \ PR_HEAD_SHA="$head_sha" \ - STRIX_PR_SCOPE_MAX_FILES_PER_BATCH="1" \ STRIX_DISABLE_PR_SCOPING="0" \ FAKE_STRIX_CALL_LOG="$call_log" \ STRIX_LLM_FILE="$strix_llm_file" \ @@ -2945,7 +2891,7 @@ EOF assert_file_contains "$output_log" "scan ok with PR-head backend dependency context" "case=pull-request-target-changed-backend-context-uses-head-blob output" assert_file_contains "$output_log" "scan ok with PR-head LLM provider URL validation context" "case=pull-request-target-changed-backend-context-includes-llm-provider-url-validation output" assert_file_contains "$output_log" "scan ok with PR-head email parser text safety context" "case=pull-request-target-changed-backend-context-includes-email-parser-text-safety output" - assert_equals "12" "$(wc -l <"$call_log" | tr -d ' ')" "case=pull-request-target-changed-backend-context-uses-head-blob strix call count" + assert_equals "1" "$(wc -l <"$call_log" | tr -d ' ')" "case=pull-request-target-changed-backend-context-uses-head-blob strix call count" rm -rf "$tmp_dir" } @@ -3147,7 +3093,7 @@ run_pull_request_target_aborts_on_pr_head_blob_failure_case() { local fake_git_fail_command="$5" local disable_pr_scoping="${6-0}" local expected_exit="1" - if [ "$fake_git_fail_command" = "ls-tree" ] || [ "$fake_git_fail_command" = "diff" ] || [ "$disable_pr_scoping" = "1" ]; then + if [ "$fake_git_fail_command" = "ls-tree" ] || [ "$fake_git_fail_command" = "show" ] || [ "$fake_git_fail_command" = "diff" ] || [ "$disable_pr_scoping" = "1" ]; then expected_exit="2" fi local expected_message="pull request changed file could not be read from PR head; failing closed" @@ -4022,7 +3968,7 @@ EOF rm -rf "$tmp_dir" } -run_pr_batched_llm_api_base_file_config_failure_exits_2_case() { +run_pr_scoped_llm_api_base_file_config_failure_exits_2_case() { local tmp_dir tmp_dir="$(mktemp -d)" local repo_root_dir="$tmp_dir/workspace/smart-crawling-server" @@ -4061,7 +4007,6 @@ EOF RUNNER_TEMP="$allowed_input_dir" \ GITHUB_EVENT_NAME="pull_request" \ STRIX_TEST_CHANGED_FILES_OVERRIDE=$'src/one.py\nsrc/two.py' \ - STRIX_PR_SCOPE_MAX_FILES_PER_BATCH="1" \ FAKE_STRIX_CALL_LOG="$call_log" \ STRIX_DISABLE_PR_SCOPING="0" \ STRIX_LLM_FILE="$strix_llm_file" \ @@ -4072,10 +4017,10 @@ EOF local rc=$? set -e - assert_equals "2" "$rc" "case=pr-batched-llm-api-base-file-config-failure exit code" - assert_file_contains "$output_log" "LLM_API_BASE_FILE must be inside the trusted input file root" "case=pr-batched-llm-api-base-file-config-failure output" + assert_equals "2" "$rc" "case=pr-scoped-llm-api-base-file-config-failure exit code" + assert_file_contains "$output_log" "LLM_API_BASE_FILE must be inside the trusted input file root" "case=pr-scoped-llm-api-base-file-config-failure output" if [ -f "$call_log" ]; then - record_failure "case=pr-batched-llm-api-base-file-config-failure should reject before invoking strix" + record_failure "case=pr-scoped-llm-api-base-file-config-failure should reject before invoking strix" fi rm -rf "$tmp_dir" @@ -4892,13 +4837,13 @@ run_gate_case_allow_provider_signal "gemini-zero-findings-timeout-fallback-allow "pull_request" \ "sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java" -run_gate_case_allow_provider_signal "pr-batch-zero-finding-does-not-leak" \ - "gemini/batch-zero-leak-primary" \ +run_gate_case_allow_provider_signal "pr-scope-zero-finding-does-not-leak" \ + "gemini/scope-zero-leak-primary" \ "" \ "1" \ "Strix reported zero vulnerabilities before provider infrastructure failure; failing closed because provider infrastructure failures are not clean scan evidence." \ "1" \ - "gemini/batch-zero-leak-primary" \ + "gemini/scope-zero-leak-primary" \ "https://example.invalid" \ "vertex_ai" \ "__DEFAULT__" \ @@ -5686,7 +5631,7 @@ run_required_input_file_outside_input_root_fails_closed_case "STRIX_LLM_FILE" run_required_input_file_outside_input_root_fails_closed_case "LLM_API_KEY_FILE" run_vertex_model_ignores_untrusted_llm_api_base_file_case run_llm_api_base_file_outside_input_root_fails_closed_case -run_pr_batched_llm_api_base_file_config_failure_exits_2_case +run_pr_scoped_llm_api_base_file_config_failure_exits_2_case run_input_file_root_override_takes_precedence_over_runner_temp_case run_stale_report_case run_symlink_report_case @@ -5775,7 +5720,7 @@ run_gate_case "pr-python-scope-context" \ "pull_request" \ "backend/api/emails.py" -run_gate_case "pr-changed-scope-batched" \ +run_gate_case "pr-changed-scope-full" \ "openai/gpt-4o-mini" \ "" \ "0" \ @@ -5796,14 +5741,14 @@ run_gate_case "pr-changed-scope-batched" \ "pull_request" \ $'sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java\nsync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java\nsync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java' -run_gate_case "pr-changed-scope-max-batches" \ +run_gate_case "pr-changed-scope-full-set" \ "openai/gpt-4o-mini" \ "" \ "0" \ - "Running pull request Strix batch 2/2." \ - "2" \ - "openai/gpt-4o-mini|openai/gpt-4o-mini" \ - "https://example.invalid|https://example.invalid" \ + "scan ok with full configured PR scope" \ + "1" \ + "openai/gpt-4o-mini" \ + "https://example.invalid" \ "vertex_ai" \ "__DEFAULT__" \ "" \ @@ -5828,14 +5773,14 @@ for large_pr_index in $(seq 1 38); do large_pr_changed_files+="$large_pr_path" done -run_gate_case "pr-large-scope-four-batches" \ +run_gate_case "pr-large-scope-full-set" \ "openai/gpt-4o-mini" \ "" \ "0" \ - "Running pull request Strix batch 4/4." \ - "4" \ - "openai/gpt-4o-mini|openai/gpt-4o-mini|openai/gpt-4o-mini|openai/gpt-4o-mini" \ - "https://example.invalid|https://example.invalid|https://example.invalid|https://example.invalid" \ + "scan ok with large full PR scope" \ + "1" \ + "openai/gpt-4o-mini" \ + "https://example.invalid" \ "vertex_ai" \ "__DEFAULT__" \ "" \ @@ -5872,29 +5817,6 @@ run_gate_case "pr-changed-scope-includes-ci-dependency" \ "pull_request" \ "scripts/ci/strix_quick_gate.sh" -run_gate_case_allow_provider_signal "pr-changed-scope-rebalanced" \ - "vertex_ai/gemini-2.5-flash" \ - "vertex_ai/gemini-2.5-pro" \ - "0" \ - "Rebalancing pull request Strix batch 1/1 into smaller batches after timeout." \ - "3" \ - "vertex_ai/gemini-2.5-flash|vertex_ai/gemini-2.5-flash|vertex_ai/gemini-2.5-flash" \ - "||" \ - "vertex_ai" \ - "__DEFAULT__" \ - "" \ - "0" \ - "CRITICAL" \ - "0" \ - "" \ - "" \ - "1200" \ - "3000" \ - "pull_request" \ - $'sync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/controller/SysPositionController.java\nsync-module-system/smart-crawling-playwright/src/main/java/org/empasy/sync/mcp/service/PlayWrightService.java\nsync-module-system/smart-crawling-biz/src/main/java/org/empasy/sync/modules/system/service/impl/SysUserServiceImpl.java\nsync-module-system/smart-crawling-common/src/main/java/org/empasy/sync/common/system/util/JwtUtil.java' \ - "" \ - "4" - run_gate_case "pr-empty-diff-skip" \ "openai/gpt-4o-mini" \ "" \