AntaresScanner.usedheuristic is set as a side effect of scanning one item (scanfile → scanwithheuristic). scanstagedfiles() and scandirectory() both rely on that loop side effect, so the flag stays at its constructor default False in two whole-run cases:
Solution written to ~/solution.md. Summary below.
scan_directory() misses used_heuristic on zero-item / early-return pathsAntaresScanner.used_heuristic is set as a side effect of scanning one item (scan_file → _scan_with_heuristic). scan_staged_files() and scan_directory() both rely on that loop side effect, so the flag stays at its constructor default False in two whole-run cases:
-d some_file.py, missing path)..py files — os.walk runs without ever calling scan_file.gitreins/cli.py (cmd_security_scan) then skips the heuristic disclosure and prints the bare Antares: clean — no findings in <target>. Tests missed it because they only covered scan_file/scan_staged_files.
engine/antares.py) def scan_directory(self, directory: str) -> list[AntaresFinding]:
findings: list[AntaresFinding] = []
if not os.path.isabs(directory):
directory = os.path.join(self.workdir, directory)
if not os.path.isdir(directory):
+ if not self._use_ml:
+ self.used_heuristic = True
return findings
...
findings.extend(self.scan_file(full))
+ if not self._use_ml:
+ self.used_heuristic = True
return findings
Plus 2 regression tests (zero-.py dir, dir with findings) in tests/test_antares.py, and the README/CONTRIBUTING count bump 2448 → 2450 enforced by scripts/check_docs_drift.py.
7048e69, editable venv)gitreins security-scan -d /tmp/pyless-dir → Antares: clean (no disclosure, exit 0).Antares: heuristic mode (no ML stack installed) — keyword fallback, NOT a full scan on the zero-.py dir, the not-a-directory path, and the findings path (exit 1 preserved).55 passed in tests/test_antares.py (2 new).pytest --collect-only -q → 2450 tests collected; check_docs_drift.py → OK.2436 passed, 14 skipped (= 2450).A per-item flag set inside a loop is false for zero-item iterations and early returns. Enumerate every entry path (file / staged / directory, including empty and early-return), set the whole-run default in each entry function, and drive the CLI disclosure from the flag — not the findings count.
# Evidence - Problem class: python-scanner-mode-flag-missed-on-directory-scan-path - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-25T13:41:51.969Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A scanner that gained a mode-disclosure flag (used_heuristic, set by scan_file and scan_staged_files) still printed the bare clean line 'Antares: clean' through one CLI entry path: the -d/--directory path calls scan_directory(), which loops calling scan_file() (setting the flag per file) but (a) returns early on not-a-directory without setting the flag, and (b) walks a directory containing zero .py files without ever calling scan_file, so the flag stays False and the CLI printed no mode line. Root cause pattern: a per-item flag set inside a loop is false for ZERO-ITEM iterations and for early-return paths \u2014 any caller that summarizes flag state after the loop must set the default flag state for the whole-run mode, not rely on per-item side effects. Fix: set the flag at the end of scan_directory() (and on the not-a-directory early return) whenever the run mode is the fallback, exactly like scan_staged_files already did. General rule: when adding an output-honesty flag to a scanner with multiple entry points (file/staged/directory), enumerate EVERY entry path including zero-item and early-return ones, and drive the CLI disclosure from the flag, not from findings count. Regression tests: one for a directory with no .py files, one for a directory with findings.", "environment": "gitreins Python package (engine/ + gitreins/ CLI), pytest suite, editable venv install, guard runs check_docs_drift + pytest in diff mode", "language": "python", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "python-scanner-mode-flag-missed-on-directory-scan-path", "provider": "openrouter", "solved_at": "2026-09-25T13:41:51.970Z", "version": "main 5692cc1"}Solution written to ~/solution.md. Summary below.
scan_directory() misses used_heuristic on zero-item / early-return pathsAntaresScanner.used_heuristic is set as a side effect of scanning one item (scan_file → _scan_with_heuristic). scan_staged_files() and scan_directory() both rely on that loop side effect, so the flag stays at its constructor default False in two whole-run cases:
-d some_file.py, missing path)..py files — os.walk runs without ever calling scan_file.gitreins/cli.py (cmd_security_scan) then skips the heuristic disclosure and prints the bare Antares: clean — no findings in <target>. Tests missed it because they only covered scan_file/scan_staged_files.
engine/antares.py) def scan_directory(self, directory: str) -> list[AntaresFinding]:
findings: list[AntaresFinding] = []
if not os.path.isabs(directory):
directory = os.path.join(self.workdir, directory)
if not os.path.isdir(directory):
+ if not self._use_ml:
+ self.used_heuristic = True
return findings
...
findings.extend(self.scan_file(full))
+ if not self._use_ml:
+ self.used_heuristic = True
return findings
Plus 2 regression tests (zero-.py dir, dir with findings) in tests/test_antares.py, and the README/CONTRIBUTING count bump 2448 → 2450 enforced by scripts/check_docs_drift.py.
7048e69, editable venv)gitreins security-scan -d /tmp/pyless-dir → Antares: clean (no disclosure, exit 0).Antares: heuristic mode (no ML stack installed) — keyword fallback, NOT a full scan on the zero-.py dir, the not-a-directory path, and the findings path (exit 1 preserved).55 passed in tests/test_antares.py (2 new).pytest --collect-only -q → 2450 tests collected; check_docs_drift.py → OK.2436 passed, 14 skipped (= 2450).A per-item flag set inside a loop is false for zero-item iterations and early returns. Enumerate every entry path (file / staged / directory, including empty and early-return), set the whole-run default in each entry function, and drive the CLI disclosure from the flag — not the findings count.
# Evidence - Problem class: python-scanner-mode-flag-missed-on-directory-scan-path - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-25T13:41:51.969Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A scanner that gained a mode-disclosure flag (used_heuristic, set by scan_file and scan_staged_files) still printed the bare clean line 'Antares: clean' through one CLI entry path: the -d/--directory path calls scan_directory(), which loops calling scan_file() (setting the flag per file) but (a) returns early on not-a-directory without setting the flag, and (b) walks a directory containing zero .py files without ever calling scan_file, so the flag stays False and the CLI printed no mode line. Root cause pattern: a per-item flag set inside a loop is false for ZERO-ITEM iterations and for early-return paths \u2014 any caller that summarizes flag state after the loop must set the default flag state for the whole-run mode, not rely on per-item side effects. Fix: set the flag at the end of scan_directory() (and on the not-a-directory early return) whenever the run mode is the fallback, exactly like scan_staged_files already did. General rule: when adding an output-honesty flag to a scanner with multiple entry points (file/staged/directory), enumerate EVERY entry path including zero-item and early-return ones, and drive the CLI disclosure from the flag, not from findings count. Regression tests: one for a directory with no .py files, one for a directory with findings.", "environment": "gitreins Python package (engine/ + gitreins/ CLI), pytest suite, editable venv install, guard runs check_docs_drift + pytest in diff mode", "language": "python", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "python-scanner-mode-flag-missed-on-directory-scan-path", "provider": "openrouter", "solved_at": "2026-09-25T13:41:51.970Z", "version": "main 5692cc1"}