Skip to content

feat: implement findings quality improvements (#84, #85, #112) - #130

Merged
AndresL230 merged 19 commits into
mainfrom
claude/superpowers-plugins-skills-1Yaij
May 28, 2026
Merged

feat: implement findings quality improvements (#84, #85, #112)#130
AndresL230 merged 19 commits into
mainfrom
claude/superpowers-plugins-skills-1Yaij

Conversation

@AndresL230

@AndresL230 AndresL230 commented May 28, 2026

Copy link
Copy Markdown
Contributor

Closes #84
Closes #85
Closes #112

Summary

Implements three coupled findings quality improvements in a single PR:

Key Changes

#112: Comment-Word Guard Leak Fix

  • Added stripComments() helper in local-waste-detector.ts to remove line/block comments before testing guard regexes
  • Guard signals (CACHE_GUARD, BATCH_GUARD, etc.) now test against comment-stripped window text, preventing false suppressions from prose mentions like // TODO: batch these later
  • All other signals continue using raw window text
  • // line-comment rule uses a (?<[:/]) lookbehind so https:// URLs aren't mangled
  • Added regression tests to verify comment-only suppressions no longer kill real findings

#85: Severity Derivation + Cost Impact

  • Exported FREQUENCY_CLASS_MULTIPLIERS from simulator as single source of truth
  • Added riskScore field to LocalWasteFinding interface; emitted from all detectors (local, Python, AST-based)
  • Implemented deriveSeverity() function: hybrid model combining structural floor (riskScore thresholds) with cost amplifier (confidence × costImpactUsd)
    • Structural floor preserves C1's calibrated precision and keeps free-endpoint risks visible
    • Cost amplifier can only escalate severity, never drop below structural tier (benchmark-safe)
  • Implemented computeCostImpact(): endpoint monthlyCost amplified by frequency-class multiplier
  • Applied derived severity at all Suggestion construction sites: scan-results.ts, scan-publishing-handler.ts, chat-handler.ts
  • Added confidence filter control to ResultsPage UI (always visible, even for single-type scans)

#84: Finding Deduplication + Source Tracking

  • Added sources?: string[] and costImpactUsd?: number | null fields to Suggestion type
  • Implemented collapseSuggestions() function: merges findings by type + file + endpoint/line-bucket
    • Unions sources from all detections
    • Takes max confidence
    • Recomputes severity from merged signals
    • Prefers AI description over local-rule when available
  • Applied deduplication at all Suggestion construction sites (incl. the sidebar's scan-publishing-handler.ts path)
  • Added "detected by N sources" badge to ResultsPage UI (shown when multiple sources detected same issue)
  • Mirrored new fields onto FindingNode for graph/export parity

Implementation Details

  • Severity model: Math.max(structuralTier, costTier) where structural tier is based on riskScore (≥5→high, ≥3→medium, <3→low) and cost tier is based on confidence × costImpactUsd (≥100→high, ≥10→medium, <10→low). Never demotes — only escalates above the C1-calibrated structural floor.
  • Cost heuristic: Reuses existing endpoint monthlyCost from LOCAL_PRICING × FREQUENCY_CLASS_MULTIPLIERS; costImpactUsd is internal (drives severity + ordering) and never rendered to users.
  • Dedup key: type | file | endpointId (or 5-line bucket when no endpoint); findings on same endpoint always collapse regardless of line distance.
  • Source ranking: AI > remote > local-rule (used for description selection in collapsed findings).
  • Benchmark safety: Severity reassignment never adds or drops a finding; per-type precision gate unaffected.

Tests

  • Added src/test/scan-results.test.ts: unit tests for deriveSeverity() (incl. exact threshold boundaries), computeCostImpact(), collapseSuggestions(), buildRemoteScanResults aggressive-suggestion labeling
  • Added src/test/chat-handler-merge.test.ts: AI finding merge + collapse
  • Extended src/test/local-waste-detector.test.ts: [Findings] Tighten CACHE_GUARD / BATCH_GUARD bare-word leak #112 regression cases (comment-word suppressions, https:// URL preservation, explicitGuard comment leak)
  • All tests use plain node:assert/strict compiled by tsc and run via npm run test:scanner

Gates

  • npm run build clean (extension + webview)
  • npm run test:scanner — FAIL count 0 (full suite)
  • npm run benchmark against the 7-fixture v1 corpus — Δ +0.00pp on all five metrics (detection P/R, provider attribution, finding P/R); every per-type finding precision held at 100%

Pending manual verification

The two webview UI bits build cleanly and typecheck, but require interactive F5 verification:

Annotated as [~] in docs/accuracy/findings.md rather than [x].

Tracked follow-ups (out of scope for this PR)

  • Pre-existing scope === "internal" guard divergence between the two buildAggressiveSuggestions copies (scan-results.ts lacks the guard scan-publishing-handler.ts has)
  • Dead SourceBadge component + duplicated badge inline-style in ResultsPage.tsx — extract a shared secondary-badge
  • Minor stripComments edge cases: protocol-relative URLs //cdn... and # inside string literals (under-suppress only — never over-suppress; benchmark-clean)
  • Intelligence-layer FindingNode.severity still reflects raw structural severity, not the cost-amplified value (intentional per plan; flag if a follow-up wants ranking alignment with the sidebar)

https://claude.ai/code/session_01DPQ8sonp1j85mKqDQefc8T

Summary by CodeRabbit

Release Notes

  • New Features

    • Added minimum confidence filter to Issues tab for filtering low-confidence findings
    • Added "detected by N sources" badge showing how many sources identified each issue
    • Enhanced finding deduplication to merge results from multiple sources instead of dropping overlaps
  • Bug Fixes

    • Improved guard detection accuracy by excluding guard keywords found only in code comments
  • Documentation

    • Updated Wave 1 findings quality implementation plan and progress tracking
  • Tests

    • Expanded test coverage for scanner and intelligence components

[[Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/recost-dev/extension/pull/130?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

claude added 19 commits May 28, 2026 00:42
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements Wave 1 of the findings quality initiative: it adds structural risk scoring to all detectors, introduces a hybrid severity derivation model combining risk floors with confidence-weighted cost escalation, centralizes suggestion deduplication with source tracking, fixes guard detection to ignore comment text, and adds UI filtering/badging for confidence thresholds and cross-detector consensus.

Changes

Findings Quality & Severity Derivation

Layer / File(s) Summary
Type Extensions for Provenance & Cost Tracking
src/analysis/types.ts, src/intelligence/types.ts, webview/src/types.ts, src/simulator/engine.ts
Suggestion and FindingNode interfaces gain sources?: string[] and costImpactUsd?: number | null fields to track detection provenance and cost impact; FREQUENCY_CLASS_MULTIPLIERS is exported from simulator to enable cost heuristics.
Detector Risk Scoring & Comment-Aware Guard Detection
src/scanner/local-waste-detector.ts, src/ast/waste/batch-detector.ts, src/ast/waste/cache-detector.ts, src/ast/waste/concurrency-detector.ts, src/scanner/python-waste-detector.ts
All detectors now emit riskScore on LocalWasteFinding objects from their internal scoring logic; local-waste-detector introduces stripComments() to prevent guard words in comments from suppressing findings (cache/batch/concurrency/retry/idempotency guards must be in actual code).
Severity & Cost Derivation Primitives
src/scan-results.ts
Adds computeCostImpact (baseline monthly cost × frequency class multiplier), SeveritySignals interface (riskScore + confidence + costImpactUsd), deriveSeverity (hybrid model with structural riskScore floor and confidence-weighted cost escalation), SEVERITY_TO_RISK_SCORE mapping, and collapseSuggestions (dedupes by type+file+location, unions sources, takes max confidence, prefers AI description, recomputes severity).
Suggestion Construction & Severity/Cost Integration
src/intelligence/builder.ts, src/webview/chat-handler.ts, src/webview/scan-publishing-handler.ts, src/scan-results.ts
All suggestion builders now compute costImpactUsd via frequency heuristics, derive severity from riskScore+confidence+cost signals, populate sources: ["local-rule" | "ai" | "remote"], and apply collapseSuggestions at builder exit points; aggressive suggestions, local waste findings, and AI mappings all wire through the unified pipeline.
Confidence Filtering & Source Provenance Badge
webview/src/components/ResultsPage.tsx
ResultsPage adds minConfidence state and "Minimum confidence" dropdown (any/≥40%/≥60%/≥80%), filtering suggestions by confidence threshold; SuggestionCard renders "detected by N sources" badge when suggestion has multiple provenance entries.
Tests: Risk Scoring, Derivation, Deduplication & Guard Safety
src/test/scan-results.test.ts, src/test/local-waste-detector.test.ts, src/test/chat-handler-merge.test.ts, src/intelligence/__tests__/*.test.ts, package.json
Add unit tests for cost/severity derivation with structural floors and confidence escalation; validate collapseSuggestions merging; verify comment-stripping prevents false suppression while real code guards work; update all existing test fixtures with riskScore; expand test:scanner script to include all new test suites.
Implementation Plan & Progress Documentation
docs/superpowers/plans/2026-05-28-wave1-findings-quality.md, docs/accuracy/findings.md, docs/superpowers/plans/PROGRESS.md
Comprehensive Wave 1 plan detailing Group A (comment-aware guard tightening), Group B (risk propagation + cost/severity derivation), Group C (centralized deduplication), and verification gates; document C2/C3 landed outcomes with hybrid severity model, dedupe key shape, and source-badge behavior; mark Wave 1 code-complete with pending EDH verification for confidence filter and sources badge.

Sequence Diagram

sequenceDiagram
  participant Detector as Detectors
  participant Builder as Builders<br/>(local/remote)
  participant Collapse as collapseSuggestions
  participant UI as UI<br/>(Filter & Badge)
  Detector->>Detector: compute score → riskScore
  Detector->>Builder: emit LocalWasteFinding
  Builder->>Builder: costImpactUsd = compute(baseline, frequency)
  Builder->>Builder: severity = deriveSeverity(signals)
  Builder->>Builder: sources = ["local-rule"]
  Builder->>Collapse: merged suggestions
  Collapse->>Collapse: dedupe by type+file+endpoint
  Collapse->>Collapse: sources = union(sources)
  Collapse->>Collapse: confidence = max(confidence)
  Collapse->>Collapse: recompute severity
  Collapse->>UI: collapsed suggestions
  UI->>UI: filter by minConfidence
  UI->>UI: badge: "detected by N"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • recost-dev/extension#108: Both PRs modify cache detection paths; #108 tightens Python cache classification and URL-aware bucketing while this PR adds riskScore propagation to the same detectors.
  • recost-dev/extension#110: Both PRs extend AST waste detectors; #110 tightens batch/concurrency detection logic while this PR adds riskScore/severity signals to those same emitted findings.
  • recost-dev/extension#87: Retrieved PR refactored AI suggestion merging in chat-handler; this PR further changes that same merge flow to use collapseSuggestions() with new severity/cost/sources handling.

🐰 Findings now speak truth with confidence,
Deduped and scored with diligence,
Sources united, severity refined,
Guard words in comments left behind,
Wave One complete, quality's designed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: implementing findings quality improvements across three coupled issues (#84, #85, #112). It is concise, specific, and accurately reflects the primary objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/superpowers-plugins-skills-1Yaij

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
webview/src/components/ResultsPage.tsx (1)

689-757: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing empty state for filtered results.

When all suggestions are filtered out by the confidence threshold, the UI renders blank content with no explanation. The empty state check on line 689 tests suggestions.length, not visibleSuggestions.length, so users who set a high confidence threshold might see an empty panel and assume the UI is broken.

📋 Proposed fix to add filtered empty state
             ) : (
               <>
                 <div style={{ padding: "5px 12px", borderBottom: "1px solid var(--vscode-panel-border)", display: "flex", alignItems: "center", gap: "6px" }}>
                   ...
                 </div>
+                {visibleSuggestions.length === 0 ? (
+                  <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "40px 16px", gap: "8px", color: "var(--vscode-descriptionForeground)" }}>
+                    <span className="codicon codicon-filter" style={{ fontSize: "24px" }} />
+                    <span>No issues match the current filters</span>
+                    <span style={{ fontSize: "10px", opacity: 0.6 }}>Try adjusting the confidence or type filter</span>
+                  </div>
+                ) : (
                 {(() => {
                   const paidIssues = visibleSuggestions.filter((s) => s.pricingClass === "paid");
                   ...
                 })()}
+                )}
               </>
             )}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview/src/components/ResultsPage.tsx` around lines 689 - 757, The
empty-state check uses suggestions.length instead of the filtered list, so
change the top conditional in ResultsPage (the ternary that currently tests
suggestions.length) to test visibleSuggestions.length and render a clear "No
issues match filters" empty state when visibleSuggestions is 0; also update any
UI counts shown in the type/select header (e.g., the "All
({suggestions.length})" option and presentTypes counts) to reflect
visibleSuggestions where appropriate so the counts match the filtered view and
the PricingSection(s) receive the same filtered suggestion arrays
(paidIssues/freeIssues/unknownIssues).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/test/local-waste-detector.test.ts`:
- Around line 119-133: The test titled "`#112`: bare 'cleanup'/'guard' word in a
comment does not suppress" currently only includes a comment with "cleanup", so
update the test input in the run block to also include a comment containing the
bare word "guard" (e.g., add "// guard this endpoint later" or replace the
single comment line with two comment lines "// cleanup..." and "// guard...") so
the detectLocalWasteFindingsInText("src/items.ts", text) assertion exercises
both comment tokens; no changes to detection logic needed—only modify the test
text in this test case.

---

Outside diff comments:
In `@webview/src/components/ResultsPage.tsx`:
- Around line 689-757: The empty-state check uses suggestions.length instead of
the filtered list, so change the top conditional in ResultsPage (the ternary
that currently tests suggestions.length) to test visibleSuggestions.length and
render a clear "No issues match filters" empty state when visibleSuggestions is
0; also update any UI counts shown in the type/select header (e.g., the "All
({suggestions.length})" option and presentTypes counts) to reflect
visibleSuggestions where appropriate so the counts match the filtered view and
the PricingSection(s) receive the same filtered suggestion arrays
(paidIssues/freeIssues/unknownIssues).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 842ba2e0-0465-4a6c-a640-c617c4f9b82d

📥 Commits

Reviewing files that changed from the base of the PR and between 493c325 and 23821fe.

📒 Files selected for processing (26)
  • docs/accuracy/findings.md
  • docs/superpowers/plans/2026-05-28-wave1-findings-quality.md
  • docs/superpowers/plans/PROGRESS.md
  • package.json
  • src/analysis/types.ts
  • src/ast/waste/batch-detector.ts
  • src/ast/waste/cache-detector.ts
  • src/ast/waste/concurrency-detector.ts
  • src/intelligence/__tests__/builder.test.ts
  • src/intelligence/__tests__/clusters.test.ts
  • src/intelligence/__tests__/compression.test.ts
  • src/intelligence/__tests__/export.test.ts
  • src/intelligence/__tests__/scorer.test.ts
  • src/intelligence/builder.ts
  • src/intelligence/types.ts
  • src/scan-results.ts
  • src/scanner/local-waste-detector.ts
  • src/scanner/python-waste-detector.ts
  • src/simulator/engine.ts
  • src/test/chat-handler-merge.test.ts
  • src/test/local-waste-detector.test.ts
  • src/test/scan-results.test.ts
  • src/webview/chat-handler.ts
  • src/webview/scan-publishing-handler.ts
  • webview/src/components/ResultsPage.tsx
  • webview/src/types.ts

Comment on lines +119 to +133
run("#112: bare 'cleanup'/'guard' word in a comment does not suppress", () => {
const text = [
"// cleanup this endpoint later",
"export async function loadItems(ids) {",
" const out = [];",
" for (const id of ids) {",
" out.push(await fetch(`https://api.example.com/items/${id}`));",
" }",
" return out;",
"}",
].join("\n");
const findings = detectLocalWasteFindingsInText("src/items.ts", text);
assert.ok(
findings.some((f) => f.type === "n_plus_one" || f.type === "cache"),
"comment-only 'cleanup' must not suppress findings; expected n_plus_one or cache finding"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Cover the 'guard' comment-token case explicitly.

Line 121 only exercises cleanup; it never places guard in comment text, so the test does not validate the full case described in its title.

Proposed test-input fix
-    "// cleanup this endpoint later",
+    "// cleanup this endpoint later; add guard next",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run("#112: bare 'cleanup'/'guard' word in a comment does not suppress", () => {
const text = [
"// cleanup this endpoint later",
"export async function loadItems(ids) {",
" const out = [];",
" for (const id of ids) {",
" out.push(await fetch(`https://api.example.com/items/${id}`));",
" }",
" return out;",
"}",
].join("\n");
const findings = detectLocalWasteFindingsInText("src/items.ts", text);
assert.ok(
findings.some((f) => f.type === "n_plus_one" || f.type === "cache"),
"comment-only 'cleanup' must not suppress findings; expected n_plus_one or cache finding"
run("`#112`: bare 'cleanup'/'guard' word in a comment does not suppress", () => {
const text = [
"// cleanup this endpoint later; add guard next",
"export async function loadItems(ids) {",
" const out = [];",
" for (const id of ids) {",
" out.push(await fetch(`https://api.example.com/items/${id}`));",
" }",
" return out;",
"}",
].join("\n");
const findings = detectLocalWasteFindingsInText("src/items.ts", text);
assert.ok(
findings.some((f) => f.type === "n_plus_one" || f.type === "cache"),
"comment-only 'cleanup' must not suppress findings; expected n_plus_one or cache finding"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/local-waste-detector.test.ts` around lines 119 - 133, The test
titled "`#112`: bare 'cleanup'/'guard' word in a comment does not suppress"
currently only includes a comment with "cleanup", so update the test input in
the run block to also include a comment containing the bare word "guard" (e.g.,
add "// guard this endpoint later" or replace the single comment line with two
comment lines "// cleanup..." and "// guard...") so the
detectLocalWasteFindingsInText("src/items.ts", text) assertion exercises both
comment tokens; no changes to detection logic needed—only modify the test text
in this test case.

@AndresL230
AndresL230 merged commit 60a5efc into main May 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants