pre-push(17.2): name every skipped check in plain words

One terminal line per distinct coverage reason (push/explain.py): hex-encoded
paths decoded, per-question sha256 tokens dropped and counted, unknown reasons
still printed; the JSON artifact keeps the raw values. A changed language with
no engine frontend and no quick rules is recorded and printed as "language not
covered: <lang> (...)", in pre-push and in `ousast scan`. The scan report
renders the eight degradations cli.py recorded but never showed, and any
reason without a sentence. The hook forwards OUSAST_COMPARISON_BASE.
Requirement 9.2.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-10-02 22:10:33 +02:00
co-authored by Claude Opus 5.5
parent bf07cd4d1d
commit f555dee640
11 changed files with 445 additions and 15 deletions
+25
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
import json
import re
from collections.abc import Sequence
from dataclasses import asdict, dataclass
from pathlib import Path
@@ -74,6 +75,30 @@ def quick_scan_findings(
return sorted(findings, key=lambda item: (_severity_sort(item.severity), -item.ranking_priority, item.path))
def uncovered_languages(
targets: Sequence[FileTarget], ruleset: Sequence[PatternRule] | None = None, *, engine: bool = False
) -> list[tuple[str, int]]:
"""Languages present in `targets` that no enabled quick rule (and, with `engine`, no engine frontend) covers.
A repository in such a language produces zero findings whatever it contains; callers name it so that
zero is never read as a clean result.
"""
from .model.partitions import FRONTENDS
rules = PATTERN_RULES if ruleset is None else ruleset
enabled = [rule for rule in rules if rule.status != "disabled"]
if any(not rule.languages for rule in enabled): # a rule without a language list applies to every file
return []
covered = {language for rule in enabled for language in rule.languages}
if engine:
covered |= set(FRONTENDS)
counts: dict[str, int] = {}
for target in targets:
if target.language not in covered and target.language != "unknown":
counts[target.language] = counts.get(target.language, 0) + 1
return sorted(counts.items())
def build_quick_hunter_prompt(target: FileTarget, source_excerpt: str) -> str:
return (
"Review this file for security-relevant issues. Return structured findings only when evidence exists.\n"