diff --git a/.jules/bolt.md b/.jules/bolt.md index a86b7aafd..bfa6cb5cd 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -19,9 +19,6 @@ ## 2026-06-25 - Avoid N+1 API blocking in PR checks **Learning:** In backend processing scripts, synchronous iterations calling an external service, such as fetching `restMergeableState` per PR, cause N+1 API bottlenecks and stall pipeline execution linearly. This matters for PR schedulers handling multiple PRs. **Action:** Use `concurrent.futures.ThreadPoolExecutor` for independent network calls in a loop when there are multiple items, keep empty and single-item inputs on the cheaper serial path, and bound `max_workers` to avoid API rate limits. -## 2026-06-25 - Avoid N+1 API blocking in PR checks -**Learning:** In backend processing scripts, synchronous iterations calling an external service, such as fetching `restMergeableState` per PR, cause N+1 API bottlenecks and stall pipeline execution linearly. This matters for PR schedulers handling multiple PRs. -**Action:** Use `concurrent.futures.ThreadPoolExecutor` for independent network calls in a loop when there are multiple items, keep empty and single-item inputs on the cheaper serial path, and bound `max_workers` to avoid API rate limits. ## 2024-05-19 - Pre-compile Regex Patterns in Loop-called Functions **Learning:** In `scripts/ci/pr_review_merge_scheduler.py`, the `scrub_sensitive_data` function was repeatedly compiling multiple regex patterns via `re.sub` for every log line or text scrubbed. This incurs measurable overhead due to cache lookups and object recreation in tightly looped string processing. **Action:** When using multiple regex replacements inside functions that are called frequently or process large amounts of text, define and pre-compile the regex objects at the module level (e.g., `SENSITIVE_DATA_SCRUB_PATTERNS`) and iterate over them using `pattern.sub()`. @@ -43,3 +40,7 @@ ## 2026-07-09 - Avoid N+1 API blocking in SBOM aggregator **Learning:** The `collect_inventories` function in `scripts/ci/sbom_inventory_aggregator.py` was fetching SBOMs from the GitHub dependency graph synchronously for every repository in the organization. For large organizations (up to 500 repos), this N+1 network/CLI bottleneck significantly stalled the aggregation workflow. **Action:** Use `concurrent.futures.ThreadPoolExecutor` to fetch SBOMs concurrently when multiple repositories are provided, bounded by a `max_workers` limit (e.g., 10) to avoid overwhelming the CLI/API, while preserving the fast serial path for single-item inputs. + +## 2024-05-24 - [대용량 로그 스캔 시 정규표현식 실행 전 O(N) 서브스트링 검증 선행] +**Learning:** `classify_testthat_failure`에서 테스트 실패 내역이 없는 2MB 로그 파일을 대상으로 정규표현식을 실행하면 약 20ms가 소요되지만, 단순 문자열 검색은 약 1ms만 소요됩니다. 문자열 존재 여부가 정규표현식 매칭의 전제 조건일 때, 콜드 패스(Cold Path)에서 순서 최적화는 매우 큰 성능 차이를 만듭니다. +**Action:** 대용량 텍스트 입력(CI 로그 등)에서 복잡한 정규표현식을 파싱하기 전에 항상 빠른 O(N) 문자열 존재 여부 확인을 먼저 수행하십시오. diff --git a/CHANGELOG.md b/CHANGELOG.md index bf30091dd..cae024197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,3 +18,4 @@ Semantic Versioning where the repository publishes a release. - Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped. - Bound both trusted-uv quality jobs to `github.event.pull_request.head.sha` and added a permanent two-checkout regression contract so exact-head compatibility, coverage, docstring, and compilation claims cannot silently measure GitHub's generated pull-request merge revision. - Made Strix treat only a single LiteLLM provider-error line containing NVIDIA NIM context and model-catalog 404 evidence as cross-model fallback evidence, rejecting cross-line signal assembly and provider-like target source literals; moved the public default to Nemotron 3 Super 120B and added a second NVIDIA hosted candidate before GitHub Models without neutralizing reported vulnerabilities. +- Optimized large R coverage log classification by checking the required failure marker with a linear substring search before invoking the more expensive regular expression, preserving the existing classification result while reducing the no-marker cold path. diff --git a/scripts/ci/r_coverage_peer_gate.py b/scripts/ci/r_coverage_peer_gate.py index c7ef1abe7..201f15ee1 100644 --- a/scripts/ci/r_coverage_peer_gate.py +++ b/scripts/ci/r_coverage_peer_gate.py @@ -78,8 +78,13 @@ def classify_testthat_failure( if any(not PACKAGE_NAME_RE.fullmatch(name) for name in allowed_missing): return False allowed_packages.update(allowed_missing) + + # ⚡ Bolt: Fast-path rejection before running expensive regex on potentially 2MB logs + if "Error: Test failures" not in text: + return False + summaries = FAIL_SUMMARY_RE.findall(text) - if not summaries or "Error: Test failures" not in text: + if not summaries: return False failure_count = int(summaries[-1]) if failure_count <= 0: diff --git a/tests/test_r_coverage_peer_gate.py b/tests/test_r_coverage_peer_gate.py index e77a80bca..af59a2254 100644 --- a/tests/test_r_coverage_peer_gate.py +++ b/tests/test_r_coverage_peer_gate.py @@ -222,3 +222,8 @@ def test_script_entrypoint_returns_cli_status( runpy.run_path(str(script), run_name="__main__") assert raised.value.code == 1 + +def test_classify_testthat_failure_returns_false_no_summaries(): + """Ensure it handles logs missing summary matches but containing the test string.""" + text = "Error: Test failures something else missing package 'test'" + assert gate.classify_testthat_failure(text, "test") is False