Skip to content

fix: stop issues --overview noise suggestions on non-noisy repos#27

Open
alerizzo wants to merge 2 commits into
mainfrom
fix/issues-overview-noise-floors
Open

fix: stop issues --overview noise suggestions on non-noisy repos#27
alerizzo wants to merge 2 commits into
mainfrom
fix/issues-overview-noise-floors

Conversation

@alerizzo

@alerizzo alerizzo commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • issues --overview was suggesting patterns to disable ("Suggested actions to reduce noise") even on repositories that aren't noisy — e.g. a repo with ~20 issues, or codacy-website (245 issues) where a long tail of tiny patterns dragged the median to 3, making a 9-issue pattern look "3× the median".
  • Adds two absolute floors, AND-gated with the existing relative rules: NOISE_MIN_TOTAL (200) suppresses the whole section unless the repo has substantial volume, and NOISE_MIN_PATTERN (100) requires a pattern to produce enough issues on its own. The total floor is deliberately kept above the per-pattern floor so it does independent work.
  • Hardens the relative rules: the ≥10% share rule now only applies with ≥8 distinct patterns (with fewer, an even split already exceeds 10% each), and the ≥3× multiple rule now measures against the median (new medianOf) instead of the mean, so a single huge pattern can't inflate the baseline and mask smaller disproportionate patterns.

Test plan

  • npm test — 464 tests pass (4 new, each isolating one floor/rule)
  • npm run build — type-checks clean
  • node dist/index.js issues gh codacy codacy-website --overview — no "Suggested actions to reduce noise" section (worst pattern is 55, below the per-pattern floor)

🤖 Generated with Claude Code

Gate the "Suggested actions to reduce noise" section behind two absolute
floors, AND-ed with the existing relative rules, so it stops firing on
repositories that aren't actually noisy:

- NOISE_MIN_TOTAL (200): suppress the whole section unless the repo has a
  substantial issue volume. Kept above the per-pattern floor so it does
  independent work rather than being subsumed by it.
- NOISE_MIN_PATTERN (100): a pattern must produce enough issues on its own.
  Without it, a long tail of tiny patterns drags the median to ~3, making a
  9-issue pattern look "3x the median" and get flagged as noise.

Also harden the relative rules: the >=10% share rule now only applies with
>=8 distinct patterns (with fewer, an even split already exceeds 10% each),
and the >=3x multiple rule measures against the median (new medianOf) instead
of the mean, so one huge pattern can't inflate the baseline and mask smaller
disproportionate patterns.

Co-Authored-By: Claude <noreply@anthropic.com>
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 duplication

Metric Results
Duplication 0

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR successfully implements absolute thresholds and median-based outlier detection to refine noise suggestions, aligning with the technical specifications. Codacy quality results are up to standards, and all required test scenarios are covered in the unit tests.

A logic refinement is suggested for the NOISE_MIN_PATTERNS_FOR_SHARE constant to better achieve the goal of suppressing suggestions in balanced environments. The current threshold of 8 allows repositories with 8-10 patterns to still flag every pattern as noisy despite a balanced distribution.

Test suggestions

  • Section is suppressed when total issues are below the 200 floor
  • Individual patterns below the 100 floor are ignored despite high relative share
  • Share rule (10%) is disabled when there are fewer than 8 patterns
  • Median-based calculation identifies secondary noisy patterns that a mean-based baseline would mask
  • medianOf correctly calculates values for both odd and even length arrays

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread src/commands/issues.ts Outdated
const NOISE_MIN_TOTAL = 200;
const NOISE_MIN_PATTERN = 100;
const NOISE_SHARE = 0.1;
const NOISE_MIN_PATTERNS_FOR_SHARE = 8;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Suggestion: To ensure that a perfectly balanced repository does not trigger noise suggestions (where every pattern is suggested for disabling), this threshold should be set to 11. With the current value of 8, a repository with 8, 9, or 10 evenly distributed patterns will still flag every pattern as 'noisy' because their individual share (12.5% down to 10%) meets or exceeds the 10% threshold. Increasing the floor to 11 ensures the 10% share rule only triggers for patterns that are actually disproportionate.

Suggested change
const NOISE_MIN_PATTERNS_FOR_SHARE = 8;
const NOISE_MIN_PATTERNS_FOR_SHARE = 11;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this is mathematically right. An even split of N patterns gives each a 1/N share, and 1/N only drops below the 10% threshold once N > 10, so at 8–10 patterns a balanced repo would flag every pattern. Bumped NOISE_MIN_PATTERNS_FOR_SHARE to 11 and added a regression test for the 10-evenly-distributed-patterns case (which would have failed at 8).

🤖 Generated by /pr-fixup command

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refines the noise reduction suggestions in the issues --overview command to prevent false positives on low-volume repositories. It introduces absolute floors for total issues (200) and per-pattern issues (100), limits the 10% share rule to repositories with at least 8 distinct patterns, and switches the multiple rule to measure against the median instead of the mean. The review feedback suggests renaming the NOISE_AVG_MULTIPLE constant to NOISE_MEDIAN_MULTIPLE in the code, tests, and documentation to accurately reflect this transition from mean to median.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/commands/issues.ts Outdated
Comment on lines 56 to 63
// - multiple: it has at least NOISE_AVG_MULTIPLE times the *median* issues-per-
// pattern. Using the median (not the mean) keeps one huge pattern from inflating
// the baseline and masking smaller-but-still-disproportionate patterns.
const NOISE_MIN_TOTAL = 200;
const NOISE_MIN_PATTERN = 100;
const NOISE_SHARE = 0.1;
const NOISE_MIN_PATTERNS_FOR_SHARE = 8;
const NOISE_AVG_MULTIPLE = 3;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since the noise suggestion logic has been updated to use the median instead of the average (mean), the constant name NOISE_AVG_MULTIPLE is now misleading. Consider renaming it to NOISE_MEDIAN_MULTIPLE to accurately reflect its purpose and improve code maintainability.

Suggested change
// - multiple: it has at least NOISE_AVG_MULTIPLE times the *median* issues-per-
// pattern. Using the median (not the mean) keeps one huge pattern from inflating
// the baseline and masking smaller-but-still-disproportionate patterns.
const NOISE_MIN_TOTAL = 200;
const NOISE_MIN_PATTERN = 100;
const NOISE_SHARE = 0.1;
const NOISE_MIN_PATTERNS_FOR_SHARE = 8;
const NOISE_AVG_MULTIPLE = 3;
// - multiple: it has at least NOISE_MEDIAN_MULTIPLE times the *median* issues-per-
// pattern. Using the median (not the mean) keeps one huge pattern from inflating
// the baseline and masking smaller-but-still-disproportionate patterns.
const NOISE_MIN_TOTAL = 200;
const NOISE_MIN_PATTERN = 100;
const NOISE_SHARE = 0.1;
const NOISE_MIN_PATTERNS_FOR_SHARE = 8;
const NOISE_MEDIAN_MULTIPLE = 3;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — the name was misleading now that it multiplies the median. Renamed NOISE_AVG_MULTIPLENOISE_MEDIAN_MULTIPLE across the declaration, its usage, and the docs.

🤖 Generated by /pr-fixup command

Comment thread src/commands/issues.ts Outdated
if (total < NOISE_MIN_TOTAL) return [];
const shareFloor = NOISE_SHARE * total;
const avgFloor = NOISE_AVG_MULTIPLE * average;
const medianFloor = NOISE_AVG_MULTIPLE * medianOf(patterns.map((p) => p.total));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the usage of the renamed constant here to use NOISE_MEDIAN_MULTIPLE instead of NOISE_AVG_MULTIPLE.

Suggested change
const medianFloor = NOISE_AVG_MULTIPLE * medianOf(patterns.map((p) => p.total));
const medianFloor = NOISE_MEDIAN_MULTIPLE * medianOf(patterns.map((p) => p.total));

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — updated the usage to NOISE_MEDIAN_MULTIPLE along with the declaration.

🤖 Generated by /pr-fixup command

Comment thread src/commands/AGENTS.md Outdated
- **Overview mode** (`-O, --overview`): seven count tables — Category, Severity, Language, Tag, Pattern, Author, False Positives
- The False Positives table relabels the API's raw bucket names via `FALSE_POSITIVE_LABELS`: `belowThreshold` → "Not a False Positive", `equalOrAboveThreshold` → "Potential False Positive" (the threshold is on FP probability, so at/above = potential FP — matching `printIssueCard`)
- **Noise suggestions**: after the tables, `detectNoisyPatterns()` flags patterns that account for ≥10% of all issues (`NOISE_SHARE`) **or** have ≥3× the average issues-per-pattern (`NOISE_AVG_MULTIPLE`), sorted by count desc. For each, `resolvePatternTool()` maps the pattern ID to its owning tool by matching `Tool.prefix` (e.g. `Bandit_B101` → prefix `Bandit_`; longest match wins) against the global tool list (`fetchAllTools()` / `ToolsService.listTools`). Resolved ones print a `> codacy pattern <tool> <patternId> --disable` line under "Suggested actions to reduce noise". Patterns whose tool can't be resolved (no/unmatched prefix) are **silently discarded**. The tools fetch only happens when noisy patterns exist; JSON output is unaffected (raw counts only)
- **Noise suggestions**: after the tables, `detectNoisyPatterns()` flags noisy patterns, sorted by count desc. A pattern must clear **two absolute floors** and show a **relative signal**: (1) the repo must have at least `NOISE_MIN_TOTAL` (200) issues total, else the whole section is suppressed — kept above `NOISE_MIN_PATTERN` so it does independent work (were they equal, any pattern clearing the per-pattern floor would already push the repo past an equal total floor, making it dead code); (2) the individual pattern must have at least `NOISE_MIN_PATTERN` (100) issues — this AND-gate is what stops a long tail of tiny patterns (which drags the median to ~3) from making a 9-issue pattern look "3× the median"; (3) *and* it must either account for ≥`NOISE_SHARE` (10%) of all issues **or** have ≥`NOISE_AVG_MULTIPLE` (3×) the **median** issues-per-pattern. The share rule only applies when there are at least `NOISE_MIN_PATTERNS_FOR_SHARE` (8) distinct patterns — with fewer patterns an even split already exceeds 10% each, so the signal is meaningless. The median (not the mean, via `medianOf()`) is deliberate: a single huge pattern would inflate a mean baseline and mask smaller-but-still-disproportionate patterns. For each noisy pattern, `resolvePatternTool()` maps the pattern ID to its owning tool by matching `Tool.prefix` (e.g. `Bandit_B101` → prefix `Bandit_`; longest match wins) against the global tool list (`fetchAllTools()` / `ToolsService.listTools`). Resolved ones print a `> codacy pattern <tool> <patternId> --disable` line under "Suggested actions to reduce noise". Patterns whose tool can't be resolved (no/unmatched prefix) are **silently discarded**. The tools fetch only happens when noisy patterns exist; JSON output is unaffected (raw counts only)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the reference to NOISE_AVG_MULTIPLE to NOISE_MEDIAN_MULTIPLE in the documentation to match the renamed constant.

Suggested change
- **Noise suggestions**: after the tables, `detectNoisyPatterns()` flags noisy patterns, sorted by count desc. A pattern must clear **two absolute floors** and show a **relative signal**: (1) the repo must have at least `NOISE_MIN_TOTAL` (200) issues total, else the whole section is suppressed — kept above `NOISE_MIN_PATTERN` so it does independent work (were they equal, any pattern clearing the per-pattern floor would already push the repo past an equal total floor, making it dead code); (2) the individual pattern must have at least `NOISE_MIN_PATTERN` (100) issues — this AND-gate is what stops a long tail of tiny patterns (which drags the median to ~3) from making a 9-issue pattern look "3× the median"; (3) *and* it must either account for ≥`NOISE_SHARE` (10%) of all issues **or** have ≥`NOISE_AVG_MULTIPLE` (3×) the **median** issues-per-pattern. The share rule only applies when there are at least `NOISE_MIN_PATTERNS_FOR_SHARE` (8) distinct patterns — with fewer patterns an even split already exceeds 10% each, so the signal is meaningless. The median (not the mean, via `medianOf()`) is deliberate: a single huge pattern would inflate a mean baseline and mask smaller-but-still-disproportionate patterns. For each noisy pattern, `resolvePatternTool()` maps the pattern ID to its owning tool by matching `Tool.prefix` (e.g. `Bandit_B101` → prefix `Bandit_`; longest match wins) against the global tool list (`fetchAllTools()` / `ToolsService.listTools`). Resolved ones print a `> codacy pattern <tool> <patternId> --disable` line under "Suggested actions to reduce noise". Patterns whose tool can't be resolved (no/unmatched prefix) are **silently discarded**. The tools fetch only happens when noisy patterns exist; JSON output is unaffected (raw counts only)
- **Noise suggestions**: after the tables, `detectNoisyPatterns()` flags noisy patterns, sorted by count desc. A pattern must clear **two absolute floors** and show a **relative signal**: (1) the repo must have at least `NOISE_MIN_TOTAL` (200) issues total, else the whole section is suppressed — kept above `NOISE_MIN_PATTERN` so it does independent work (were they equal, any pattern clearing the per-pattern floor would already push the repo past an equal total floor, making it dead code); (2) the individual pattern must have at least `NOISE_MIN_PATTERN` (100) issues — this AND-gate is what stops a long tail of tiny patterns (which drags the median to ~3) from making a 9-issue pattern look "3× the median"; (3) *and* it must either account for ≥`NOISE_SHARE` (10%) of all issues **or** have ≥`NOISE_MEDIAN_MULTIPLE` (3×) the **median** issues-per-pattern. The share rule only applies when there are at least `NOISE_MIN_PATTERNS_FOR_SHARE` (8) distinct patterns — with fewer patterns an even split already exceeds 10% each, so the signal is meaningless. The median (not the mean, via `medianOf()`) is deliberate: a single huge pattern would inflate a mean baseline and mask smaller-but-still-disproportionate patterns. For each noisy pattern, `resolvePatternTool()` maps the pattern ID to its owning tool by matching `Tool.prefix` (e.g. `Bandit_B101` → prefix `Bandit_` ; longest match wins) against the global tool list (`fetchAllTools()` / `ToolsService.listTools`). Resolved ones print a `> codacy pattern <tool> <patternId> --disable` line under "Suggested actions to reduce noise". Patterns whose tool can't be resolved (no/unmatched prefix) are **silently discarded**. The tools fetch only happens when noisy patterns exist; JSON output is unaffected (raw counts only)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — updated the constant name in AGENTS.md, and also in SPECS/commands/issues.md, the changelog, and the changeset for consistency.

🤖 Generated by /pr-fixup command

- Raise NOISE_MIN_PATTERNS_FOR_SHARE 8 -> 11 (Codacy): an even split of N
  patterns gives each 1/N share, which only drops below the 10% threshold once
  N > 10, so at 8-10 patterns a perfectly balanced repo would flag every
  pattern. Add a regression test for the 10-evenly-distributed-patterns case.
- Rename NOISE_AVG_MULTIPLE -> NOISE_MEDIAN_MULTIPLE (Gemini): the constant now
  multiplies the median, not the mean, so the old name was misleading. Renamed
  across the declaration, its usage, and all docs.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant