2114ccd278
CI / Lint & Test (Python 3.13) (push) Failing after 2s
CI / Lint & Test (Python 3.14) (push) Failing after 1s
CI / Lint & Test (Python 3.12) (push) Failing after 2s
CI / DCO Check (push) Has been skipped
Scorecard supply-chain security / Scorecard analysis (push) Failing after 2s
6.5 KiB
6.5 KiB
Production Code Bugs Found & Fixed
Covers three phases: 6/23 (API pool refactor) + 6/24-25 (test architecture) + 6/26 (upstream merge + review hardening) All discovered by tests or test-driven audits
🔴 Production Code Bugs (15)
6/23 — Discovered During API Pool Refactor
| # | Location | Bug | Symptom | Fix | Discovery Method |
|---|---|---|---|---|---|
| B1 | api_pool.py:snapshot() |
Deadlock — self._lock is not reentrant. snapshot() calls self.active_requests property while holding the lock → property internally acquires the same lock again |
Process hangs | Read fields directly within the locked region, do not call property | Integration test |
| B2 | api_pool.py:_capacity_summary() |
Deadlock — Same as above. acquire() calls self.total_capacity property while holding the lock |
Same as above | Same as above | Integration test |
| B3 | api_pool.py:PooledChatModel._ainvoke_with_retry() |
Async event loop blocking — acquire() synchronously blocks on Condition.wait(), asyncio event loop stalls |
Concurrent performance degradation | Added try_acquire() non-blocking fast path |
Integration test |
| B4 | api_pool.py:record_retry_success() |
Counting error — Increments on retry attempt, not retry success | Report data is misleading | Moved to after llm.invoke() succeeds, inside if attempt > 0 condition |
Code review |
| B5 | api_pool.py:set_api_pool(None) |
Does not restore original function — After calling set_api_pool(None), the patched wrapper remains in memory |
Subsequent calls still use the old path | Save _original_get_chat_model, restore when None |
Integration test |
| B6 | runner.py:Patch 6 |
Pydantic alias dependency — Only sets kwargs["timeout"], relying on Pydantic v2 alias to cover the canonical name |
May break on upstream Pydantic version upgrade | Set both kwargs["timeout"] + kwargs["request_timeout"] |
Audit discovery |
| B7 | runner.py:cleanup_result() |
Unreachable code — shutil.rmtree(ignore_errors=True) never raises, subprocess rm -rf fallback never executes |
Dead code | Removed fallback branch + unused import | Code review |
| B8 | runner.py:Patch 2/3 |
Overly broad exception handling — except (json.JSONDecodeError, Exception) makes JSONDecodeError redundant under Exception, and masks the difference between Pydantic validation errors and JSON parse errors |
Masks real bugs | Split into separate except json.JSONDecodeError (LLM output quality issue) and except Exception (upstream schema change), with logs distinguishing "invalid JSON" vs "schema validation failed" |
Code review |
| B9 | batch_scan.py:main() |
Report delay — with ThreadPoolExecutor calls shutdown(wait=True) on exit, waiting for stuck worker threads. Timed-out skipped skills are still running, blocking report output |
Report waits 80-100s | Changed to executor.shutdown(wait=False), do not wait for dead threads |
Integration test |
6/24-25 — Discovered During Test Architecture Audit
| # | Location | Bug | Symptom | Fix | Discovery Method |
|---|---|---|---|---|---|
| B10 | runner.py:_apply_patches() |
Nested premature restore — _patches_active: bool flag. Inner __exit__ removes patches that the outer block is still using |
Patches silently deactivate | Changed to _patches_depth: int nesting counter |
Code review + nesting test |
| B11 | test_runner_patches.py:TestSetupFunction.tearDownClass |
Infinite loop — from runner import _patches_depth copies the int value. while _patches_depth > 0: reads the local copy, which is never 0 |
Test process hangs permanently | Changed to import runner as _r; while _r._patches_depth > 0: |
Random-order test |
| B12 | test_runner_patches.py:test_setup_applies_patches |
False assertion — assertIsNot(init, LLMAnalyzerBase.__init__ if False else True) is always True |
Test always passes, cannot detect patch failure | Changed to save orig_init reference then assertIsNot(init, orig_init) |
Audit discovery |
| B13 | runner.py:_check_signature() |
Does not detect parameter kind — Only checks parameter name existence, not whether it is keyword-only. If upstream changes to def __init__(self, *, base_prompt, model), the check still passes |
Patch may crash on newer Python 3 versions | Added KEYWORD_ONLY detection, raises RuntimeError when found |
Audit discovery |
| B14 | runner.py:_original_chatopenai_init |
Capture timing depends on import order — Captured when _apply_patches() runs. If another module pre-modifies ChatOpenAI.__init__, the wrong version is captured |
Test environment may be incorrect | Moved to module load time (captured on import runner.py) |
Audit discovery |
| B15 | test_runner_patches.py:Patch 4/5 |
Missing functional verification — Only checks that method references are replaced, does not verify that the replacement actually appends JSON instructions | Patch 4/5 failure is undetectable | Added 2 functional tests: assertIn("Respond with ONLY a JSON object", prompt) |
Mutation testing |
6/26 — Discovered During Upstream Merge + Reviewer Response
| # | Location | Bug | Symptom | Fix | Discovery Method |
|---|---|---|---|---|---|
| B16 | runner.py:set_api_pool() |
Pool bypass: graph path — Only patched llm_utils.get_chat_model. llm_analyzer_base imports via from ... import, creating a local reference. Graph analyzers (95% LLM calls) called the unpatched local reference. snapshot()['rate_limits_hit'] always 0. |
Pool appears wired but graph path bypasses it entirely | Added _llm_analyzer_base.get_chat_model = _pooled_get_chat_model; test_pool_wiring.py now verifies LLMAnalyzerBase._llm is PooledChatModel |
PR re-review after upstream merge |
🟡 Test Code Bugs (3)
| # | Location | Bug | Fix |
|---|---|---|---|
| T1 | test_api_pool.py:test_exponential_backoff_values |
Tests the math formula min(30*2^(n-1), 300), not the pool's actual release(success=False) behavior |
Changed to go through the real release path |
| T2 | test_api_pool.py:_make_key() |
Dead code — defined but never called | Removed |
| T3 | test_gap_fill.py:_VALID_FINDING |
Module-level mutable dict — shared state risk | Changed to _valid_finding(**overrides) factory function |
📊 Statistics
| Category | Count |
|---|---|
| Production code bugs (fixed) | 16 |
| Test code bugs (fixed) | 3 |
| Known blind spots (accepted) | 4 (Q13, Q16, Q17, Q18) |
| Mutation MISSED (not production bugs) | 9 |