5.8 KiB
Contributing — Multilingual Batch Scanner
For developers who want to set up, test, and extend this module.
Quick Start
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
cp contrib/batch_scan/.env.example .env # edit with your API keys
Verify everything works:
python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8
Project Map
contrib/batch_scan/
├── batch_scan.py # CLI entry + ThreadPoolExecutor (start here)
├── runner.py # graph.invoke() wrapper + 7 patches + pool wiring (core)
├── gap_fill.py # GapFillAnalyzer — LLM pass for 8 uncovered rules
├── api_pool.py # ApiKeyPool — multi-key scheduler + 429 backoff
├── detection.py # Unicode script-ratio language detection
├── annotation.py # Finding language-compatibility labels
├── discovery.py # Recursive SKILL.md finder
├── reports.py # Terminal / JSON / Markdown formatters
├── CONTRIBUTING.md # this file
│
├── docs/
│ ├── README.md # user guide — all commands, test commands, reviewer index
│ ├── DESIGN.md # architecture — concurrency, patches, dual-patch mechanism
│ ├── REVIEW_RESPONSE.md # PR #100 review response
│ └── archive/ # deep dives, history, future work, pitfalls
│
└── tests/
├── test_pool_wiring.py # smoke — 3-path pool verification
├── test_monkeypatch_invasiveness.py # thread isolation, scoping (14 tests)
├── test_monkeypatch_fragility.py # guard verification, deep deps (26 tests)
├── docs/
│ ├── TEST_DESIGN.md # WHY each suite was designed
│ ├── TEST_GUIDE.md # WHAT each file covers + run commands
│ └── BUGS_FOUND.md # 16 bugs found & fixed
└── tests-pro/
├── test_api_pool.py # 45 tests — acquire/release/backoff
├── test_gap_fill.py # 41 tests — JSON parsing, prompt building
├── test_runner_patches.py # 24 tests — context manager, patches
├── test_annotation.py # 10 tests — language compatibility
├── random_numbered.py # main entry point (seed=42)
└── mutation_max.py # 30-bug injection framework
Running Tests
# All 164 tests
python contrib/batch_scan/tests/tests-pro/random_numbered.py # 120 unit (seed=42)
python contrib/batch_scan/tests/test_pool_wiring.py # 4 smoke checks
python contrib/batch_scan/tests/test_monkeypatch_invasiveness.py # 14 thematic
python contrib/batch_scan/tests/test_monkeypatch_fragility.py # 26 thematic
# Review-themed only
python -m unittest \
contrib.batch_scan.tests.test_monkeypatch_invasiveness \
contrib.batch_scan.tests.test_monkeypatch_fragility -v
python contrib/batch_scan/tests/test_pool_wiring.py
# Mutation test
python contrib/batch_scan/tests/tests-pro/mutation_max.py
# End-to-end (fixture suite)
python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8
python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8 --no-llm
Three commands catch most regressions:
python contrib/batch_scan/tests/tests-pro/random_numbered.py
python contrib/batch_scan/tests/test_pool_wiring.py
python -m contrib.batch_scan.batch_scan ./tests/fixtures/ -f terminal --workers 8
Code Conventions
Match SkillSpector upstream exactly:
- SPDX header on every
.pyfile from __future__ import annotationsas first import- Imports: stdlib → third-party →
skillspector.*→ relative (.) | Nonesyntax (notOptional[X])frozenset/Finalfor module-level constants (UPPER_SNAKE_CASE)- Private helpers:
_lower_snake_case logger = get_logger(__name__)in every module- Comments explain why, not what
- Docstrings on all public functions and classes
Commit Style
fix: wire ApiKeyPool into llm_analyzer_base graph path
feat: add multilingual batch scanner with parallel execution
docs: document dual-patch pool wiring fix
- Present-tense, imperative mood
Signed-off-bytrailer required (NVIDIA DCO)Co-authored-bytrailer for joint work
Key Design Points
Before modifying code, understand these three:
-
Dual-patch pool wiring.
set_api_pool()patches bothllm_utils.get_chat_modelANDllm_analyzer_base.get_chat_model. The latter is necessary becausellm_analyzer_baseimports viafrom ... import, creating a local reference that single-module patching misses. Seedocs/archive/PITFALLS.md. -
Instance-attribute injection (not class-attribute). Patch 1 writes
self.response_schema = Noneto instance__dict__, not class__dict__. Python MRO finds instance attributes first. This is what makes patches thread-safe. Mutating the class attribute causes cross-thread races (this killed V1). -
Guard before apply.
_verify_patch_targets()checks all 7 patch assumptions before_apply_patches()runs. If upstream changes a signature or removes a dependency, the guard raises immediately — patches fail closed, never silently.
Full architecture: docs/DESIGN.md.
All pitfalls: docs/archive/PITFALLS.md.
Where to Contribute
See docs/archive/FUTURE_WORK.md for 12 future directions with effort estimates. High-impact items:
- Checkpoint/resume (prevents data loss on large scans)
- Language detection expansion (9+ languages)
- SARIF output format
- Non-English ground-truth fixtures
Next: docs/README.md — user guide · docs/DESIGN.md — architecture · docs/REVIEW_RESPONSE.md — PR #100 review response