From 9a40c35944994cd890a4013fdd2b8e2aecb22fdc Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 2 Mar 2026 22:18:47 -0800 Subject: [PATCH 1/2] Fix cohesion detail payload type (follow-up to 385a7527) Follow-up to 385a7527f5df921622ae8987012ae94993e945b3. Keep make_finding/show signature behavior unchanged while fixing the source of the mismatch: tree-sitter responsibility cohesion now emits dict detail, with tests asserting the new fields. --- desloppify/app/commands/show/formatting.py | 2 -- desloppify/languages/_framework/treesitter/phases.py | 6 +++++- desloppify/tests/lang/common/test_phase_builders.py | 2 ++ pyproject.toml | 5 +++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/desloppify/app/commands/show/formatting.py b/desloppify/app/commands/show/formatting.py index cc159add..d866b998 100644 --- a/desloppify/app/commands/show/formatting.py +++ b/desloppify/app/commands/show/formatting.py @@ -60,8 +60,6 @@ def format_detail(detail: dict) -> list[str]: def suppressed_match_estimate(pattern: str, hidden_by_detector: dict[str, int]) -> int: """Estimate hidden-match count for a show pattern using detector-level noise totals.""" - if not isinstance(pattern, str) or not isinstance(hidden_by_detector, dict): - return 0 detector = pattern.split("::", 1)[0] return int(hidden_by_detector.get(detector, 0)) diff --git a/desloppify/languages/_framework/treesitter/phases.py b/desloppify/languages/_framework/treesitter/phases.py index ef4d9267..2cec2f80 100644 --- a/desloppify/languages/_framework/treesitter/phases.py +++ b/desloppify/languages/_framework/treesitter/phases.py @@ -80,7 +80,11 @@ def run(path, lang): f"{e['component_count']} disconnected function clusters " f"({e['function_count']} functions) — likely mixed responsibilities" ), - detail=f"Clusters: {families}", + detail={ + "cluster_count": e["component_count"], + "family": families, + "families": e["families"], + }, )) if entries: potentials["responsibility_cohesion"] = len(entries) diff --git a/desloppify/tests/lang/common/test_phase_builders.py b/desloppify/tests/lang/common/test_phase_builders.py index 61e90c4d..17d4cef9 100644 --- a/desloppify/tests/lang/common/test_phase_builders.py +++ b/desloppify/tests/lang/common/test_phase_builders.py @@ -244,6 +244,8 @@ def test_make_cohesion_phase_run_with_entries(): assert potentials["responsibility_cohesion"] == 1 assert findings[0]["detector"] == "responsibility_cohesion" assert "disconnected function clusters" in findings[0]["summary"] + assert findings[0]["detail"]["cluster_count"] == 4 + assert findings[0]["detail"]["family"] == "network, database, ui, auth" def test_make_unused_imports_phase_run_with_entries(): diff --git a/pyproject.toml b/pyproject.toml index 311dda26..e7ceb85b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ python_version = "3.11" warn_unused_configs = true warn_redundant_casts = true warn_unreachable = true +check_untyped_defs = true show_error_codes = true strict_optional = true ignore_missing_imports = true @@ -97,8 +98,12 @@ files = [ "desloppify/app/commands/scan/scan_reporting_presentation.py", "desloppify/app/commands/scan/scan_reporting_subjective.py", "desloppify/app/commands/scan/scan_workflow.py", + "desloppify/app/commands/show/formatting.py", + "desloppify/app/commands/show/render.py", "desloppify/app/cli_support/parser.py", "desloppify/app/cli_support/parser_groups.py", + "desloppify/engine/_state/filtering.py", "desloppify/engine/concerns.py", + "desloppify/languages/_framework/treesitter/phases.py", "desloppify/languages/_framework/generic.py", ] From f6378c59b94e10eeeb325dcec8e87a7108993ced Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 2 Mar 2026 22:58:52 -0800 Subject: [PATCH 2/2] Handle legacy string finding detail in show output --- desloppify/app/commands/show/formatting.py | 9 +++++++-- desloppify/app/commands/show/render.py | 3 ++- desloppify/tests/commands/test_cmd_show.py | 7 +++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/desloppify/app/commands/show/formatting.py b/desloppify/app/commands/show/formatting.py index d866b998..500fb2f9 100644 --- a/desloppify/app/commands/show/formatting.py +++ b/desloppify/app/commands/show/formatting.py @@ -31,8 +31,13 @@ ] -def format_detail(detail: dict) -> list[str]: - """Build display parts from a finding's detail dict.""" +def format_detail(detail: object) -> list[str]: + """Build display parts from a finding's detail payload.""" + if isinstance(detail, str): + return [f"detail: {detail}"] if detail else [] + if not isinstance(detail, dict): + return [] + parts = [] for key, label, formatter in DETAIL_DISPLAY: value = detail.get(key) diff --git a/desloppify/app/commands/show/render.py b/desloppify/app/commands/show/render.py index 3d7ebe8d..03fcb8a9 100644 --- a/desloppify/app/commands/show/render.py +++ b/desloppify/app/commands/show/render.py @@ -60,7 +60,8 @@ def _print_single_finding(finding: dict, *, show_code: bool) -> None: if detail_parts: print(colorize(f" {' · '.join(detail_parts)}", "dim")) if show_code: - detail = finding.get("detail", {}) + detail_raw = finding.get("detail", {}) + detail = detail_raw if isinstance(detail_raw, dict) else {} target_line = ( detail.get("line") or (detail.get("lines", [None]) or [None])[0] ) diff --git a/desloppify/tests/commands/test_cmd_show.py b/desloppify/tests/commands/test_cmd_show.py index 496d7183..9705dbd9 100644 --- a/desloppify/tests/commands/test_cmd_show.py +++ b/desloppify/tests/commands/test_cmd_show.py @@ -118,6 +118,13 @@ def test_outliers_truncated(self): out_part = [p for p in parts if p.startswith("outliers:")][0] assert "f" not in out_part # Only first 5 + def test_string_detail_is_rendered(self): + parts = format_detail("Clusters: alpha, beta") + assert parts == ["detail: Clusters: alpha, beta"] + + def test_non_dict_non_string_detail_is_ignored(self): + assert format_detail(123) == [] + # --------------------------------------------------------------------------- # build_show_payload