Skip to content

git-hotspots warns on degenerate churn (single-commit / shallow clone) instead of skipping #126

Description

@maudlin

Problem

git-hotspots (bin/checkup.sh §19) emits a warn even when the churn axis carries no information — e.g. a snapshot import, a shallow/--depth 1 clone, or any forensic window in which every tracked file has the same commit count. In that case the "churn × complexity" signal silently degenerates into a complexity-only ranking, but is still reported as a bug-hotspot diagonal.

This is a false-signal of exactly the class checkup is otherwise careful about (ADR-0003, honest degrade). Its sibling forensic checks handle the same input correctly: bug-fix-density and change-coupling both skip/report "no data" on a single-commit tree because their >=3-shared-commit noise filter cannot be met. git-hotspots has no analogous guard, so it is the odd one out — it fires warn where the other two honestly abstain.

Root cause

The diagonal test keys off the 80th-percentile churn cutoff:

| ($rows | map(.churn) | sort | quantile(0.8)) as $churnQ80
...
| (if .churn >= $churnQ80 and .maxCcn >= $ccnQ80 then "warning"  # diagonal

When the churn distribution is degenerate (all values equal — the single-commit case gives churn == 1 for every joined file), quantile(0.8) collapses to that common value and .churn >= $churnQ80 is true for every row. The diagonal condition reduces to .maxCcn >= $ccnQ80 alone, so every high-CCN file is classified as a churn×complexity hotspot and the check reports warn with a long list of "1 changes × CCN N" findings — i.e. the complexity ranking wearing a churn label.

The existing JOINED_COUNT < 10 guard (the "quintile analysis not meaningful" skip) catches too few files, but not too little churn variance, which is the real precondition for a meaningful quintile split on the churn axis.

Suggested fix

Add a churn-variance guard alongside the existing size guard, and skip honestly when it fails (never a false warn, and arguably not a false pass either):

  • Option A (distinct-value check): if the churn column has effectively one distinct value (or churnQ80 == churnMin), the quintile split is meaningless -> skip with a clear reason, e.g. "insufficient churn variance in the window (all tracked files share the same commit count — likely a snapshot/shallow clone); hotspot ranking would collapse to complexity-only."
  • Option B (minimum-commits noise filter): mirror the trio — require >= N distinct commits touching the scanned roots in the window before computing hotspots; below that, skip. This keeps the family of git-axis checks consistent in how they degrade.

Either is a small, local change in the §19 block. The "1 changes x CCN ..." shape in the findings is itself a tell the guard could assert against.

Acceptance

  • A single-commit / uniform-churn repository yields git-hotspots: skip with an explained reason, not a warn.
  • The skip reason names the cause (insufficient churn variance / history), consistent with how bug-fix-density and change-coupling already phrase their no-data skips.
  • A repository with genuine churn variance is unaffected (regression-guard with the existing behaviour).

Pointers

  • bin/checkup.sh §19 git-hotspots — $churnQ80 computation and the diagonal .churn >= $churnQ80 test; the existing JOINED_COUNT < 10 skip is the natural insertion point for the variance guard.
  • bug-fix-density / change-coupling sections — the >=3-shared-commit noise filter is the precedent to mirror.
  • ADR-0003 (honest graceful-degrade) — the invariant this restores.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    coreCore runner / universal checksenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions