diff --git a/pilot/ai_review.py b/pilot/ai_review.py index 3575a8a..073da0e 100644 --- a/pilot/ai_review.py +++ b/pilot/ai_review.py @@ -68,10 +68,11 @@ _SHA_MARKER_RE = re.compile(r"") AI_REVIEW_LABEL = "AI-REVIEW" # Opt-in label for the token-usage block. Read at render time — see pr_has_label. AI_USAGE_LABEL = "AI-USAGE" -SEVERITIES = ("critical", "high", "medium", "low") +SEVERITIES = ("critical", "high", "medium", "low", "trivial", "info") # Severity rank — higher = more severe. Used by `apply_repo_config` to drop -# findings below `severity_threshold`. Critical=3, high=2, medium=1, low=0. -SEVERITY_RANK = {"low": 0, "medium": 1, "high": 2, "critical": 3} +# findings below `severity_threshold`. critical=4, high=3, medium=2, low=1, +# trivial=0, info=-1. +SEVERITY_RANK = {"info": -1, "trivial": 0, "low": 1, "medium": 2, "high": 3, "critical": 4} REPO_CONFIG_FILE = ".pr-review.json" # Style → (default max_findings, default severity_threshold). Strict is @@ -957,6 +958,7 @@ _SEVERITY_EMOJI = { "high": "🔴", "medium": "🟡", "low": "🔵", + "trivial": "⚪", "info": "⚪", "nit": "⚪", } diff --git a/tests/pilot/test_ai_review.py b/tests/pilot/test_ai_review.py index 334a0b7..3ab2231 100644 --- a/tests/pilot/test_ai_review.py +++ b/tests/pilot/test_ai_review.py @@ -11,9 +11,11 @@ sys.path.insert(0, os.path.join(ROOT, "pilot")) import ai_review # noqa: E402 from ai_review import ( # noqa: E402 + _SEVERITY_EMOJI, _balanced_json_substring, _extract_first_json_object, _last_balanced_json, + _normalize_finding, _render_collapsible_usage, build_user_prompt, compute_attribution, @@ -28,6 +30,8 @@ from ai_review import ( # noqa: E402 parse_text_blocks, prior_review_bodies, reviewed_shas, + SEVERITIES, + SEVERITY_RANK, split_findings, summary_bullets, truncate_diff, @@ -1692,3 +1696,53 @@ def test_inline_comment_body_humanized_tokens(): body = inline_comment_body(f) assert "🪙 ~17,303 (17.3K) tok" in body + +# --------------------------------------------------------------------------- +# Severity levels — Task 4 (add trivial + info) +# --------------------------------------------------------------------------- + + +def test_severities_includes_trivial_and_info(): + assert "trivial" in SEVERITIES + assert "info" in SEVERITIES + + +def test_severity_rank_orders_new_levels(): + assert SEVERITY_RANK["info"] < SEVERITY_RANK["trivial"] < SEVERITY_RANK["low"] + + +def test_threshold_medium_keeps_low_below_trivial_below_info(): + # medium+ threshold: + # medium (rank 2) → kept + # low (rank 1) → DROPPED + # trivial (rank 0) → DROPPED + # info (rank -1) → DROPPED + cfg = {"style": "lenient", "severity_threshold": "medium"} + findings = [ + {"severity": "info", "path": "a", "line": 1, "problem": "", "fix": "", "suggestion": "", "reference": ""}, + {"severity": "trivial", "path": "b", "line": 1, "problem": "", "fix": "", "suggestion": "", "reference": ""}, + {"severity": "low", "path": "c", "line": 1, "problem": "", "fix": "", "suggestion": "", "reference": ""}, + {"severity": "medium", "path": "d", "line": 1, "problem": "", "fix": "", "suggestion": "", "reference": ""}, + ] + kept, dropped = ai_review.apply_repo_config(findings, cfg, changed_paths=["x.py"]) + sev_kept = [f["severity"] for f in kept] + sev_dropped = [f["severity"] for f in dropped] + assert "info" in sev_dropped + assert "trivial" in sev_dropped + assert "low" in sev_dropped + assert "medium" in sev_kept + # and nothing else + assert len(kept) == 1 + + +def test_unknown_severity_still_normalizes_to_medium(): + # Backward compat + n = _normalize_finding({"severity": "emergency", "path": "x", "line": 1, "problem": "p"}) + assert n["severity"] == "medium" + + +def test_emoji_for_trivial_and_info_is_neutral(): + # The plan's emoji table maps trivial/info to ⚪ + assert _SEVERITY_EMOJI["trivial"] == "⚪" + assert _SEVERITY_EMOJI["info"] == "⚪" +