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.
Problem
git-hotspots(bin/checkup.sh§19) emits awarneven when the churn axis carries no information — e.g. a snapshot import, a shallow/--depth 1clone, 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-densityandchange-couplingbothskip/report "no data" on a single-commit tree because their >=3-shared-commit noise filter cannot be met.git-hotspotshas no analogous guard, so it is the odd one out — it fireswarnwhere the other two honestly abstain.Root cause
The diagonal test keys off the 80th-percentile churn cutoff:
When the churn distribution is degenerate (all values equal — the single-commit case gives
churn == 1for every joined file),quantile(0.8)collapses to that common value and.churn >= $churnQ80is true for every row. The diagonal condition reduces to.maxCcn >= $ccnQ80alone, so every high-CCN file is classified as a churn×complexity hotspot and the check reportswarnwith a long list of"1 changes × CCN N"findings — i.e. the complexity ranking wearing a churn label.The existing
JOINED_COUNT < 10guard (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
skiphonestly when it fails (never a falsewarn, and arguably not a falsepasseither):churnQ80 == churnMin), the quintile split is meaningless ->skipwith 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."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
git-hotspots: skipwith an explained reason, not awarn.bug-fix-densityandchange-couplingalready phrase their no-data skips.Pointers
bin/checkup.sh§19git-hotspots—$churnQ80computation and the diagonal.churn >= $churnQ80test; the existingJOINED_COUNT < 10skip is the natural insertion point for the variance guard.bug-fix-density/change-couplingsections — the >=3-shared-commit noise filter is the precedent to mirror.