diff --git a/README.md b/README.md index 833f332..e3ebf34 100644 --- a/README.md +++ b/README.md @@ -125,9 +125,10 @@ Models sometimes report "findings" that conclude the code is fine — titled `N/ - its **title**, **failure_scenario**, or **suggestion** starts with a non-finding signal — `N/A`, `not applicable`, `placeholder`, or a separator-delimited confirmation phrase (`none — …`, `not a finding — …`, `no failure — …`, `no concrete failure scenario — …`, `no bug — …`, `no (further) action needed — …`, `no change needed — …`) - its **title** ends with a declarative confirmation — `…is correct` or `…is accurate` — or starts with a prior-finding resolution confirmation — `Prior (bot) finding(s) addressed/resolved/fixed …` -- its **failure_scenario** ends with a leaked conclusion — `…no bug` / `…no bug here` or `…analysis was wrong` +- its **title** contains a self-negating phrase followed by a separator or end-of-title — `…not actionable`, `…not a defect`, `…not a(n) (real) issue/bug/problem` +- its **failure_scenario** ends with a leaked conclusion — `…no bug` / `…no bug here`, `…not actionable` / `…not a defect` / `…not a(n) (real) issue/bug/problem`, or `…analysis was wrong` -The patterns are deliberately anchored to the start or end of a field, so real findings survive: a scenario like "No failure occurs until the third retry…" or "None of the guards catch this input" never matches. Dropped counts are logged per run (`non-finding filter applied to model output`). +The patterns are deliberately anchored (start-of-field, end-of-field, or separator-delimited) so real findings survive: a scenario like "No failure occurs until the third retry…" or "None of the guards catch this input" never matches. Dropped counts are logged per run (`non-finding filter applied to model output`). ## Unknown-file filter diff --git a/src/review/__tests__/filter-non-findings.test.ts b/src/review/__tests__/filter-non-findings.test.ts index 53fa293..f75e270 100644 --- a/src/review/__tests__/filter-non-findings.test.ts +++ b/src/review/__tests__/filter-non-findings.test.ts @@ -463,4 +463,145 @@ describe("filterNonFindings", () => { expect(result).toEqual({ findings: [finding], droppedAsNonFinding: 0 }) }) + + // --- Self-negating titles --- + + it("drops a finding whose title ends with 'not actionable'", () => { + const finding = makeFinding({ + title: "Edge-case navigation race — not actionable", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + it("drops a finding whose title contains 'not a defect'", () => { + const finding = makeFinding({ + title: "Deferred script ordering — not a defect", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + it("drops a finding whose title contains 'not a real issue'", () => { + const finding = makeFinding({ + title: "Theoretical type coercion — not a real issue", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + it("drops a finding whose title contains 'not a real bug'", () => { + const finding = makeFinding({ + title: "Config ordering sensitivity — not a real bug", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + it("drops a finding whose title contains 'not an issue' (grammatical 'an' before vowel)", () => { + const finding = makeFinding({ + title: "Config ordering sensitivity — not an issue", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + it("drops a finding whose title contains 'not a problem'", () => { + const finding = makeFinding({ + title: "Unhandled rejection in cleanup — not a problem", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + it("drops a finding whose failure_scenario ends with 'Not a defect.'", () => { + const finding = makeFinding({ + failure_scenario: + "Both scripts attach before any user navigation, so pageviews are covered. Not a defect.", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + // --- Self-negating: passthrough cases --- + + it("keeps a finding whose title contains 'actionable' without 'not'", () => { + const finding = makeFinding({ + title: "Actionable race condition in the retry loop", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [finding], droppedAsNonFinding: 0 }) + }) + + it("keeps a finding whose failure_scenario mentions 'defect' mid-sentence", () => { + const finding = makeFinding({ + failure_scenario: + "The defect causes silent data loss when the buffer overflows.", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [finding], droppedAsNonFinding: 0 }) + }) + + it("keeps a finding whose title qualifies the negation with a continuation", () => { + const finding = makeFinding({ + title: + "Record leak is not a bug on single-threaded builds but races under concurrency", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [finding], droppedAsNonFinding: 0 }) + }) + + it("keeps a finding whose title contains 'not a' outside the alternation set", () => { + const finding = makeFinding({ + title: "Return type is not a promise but callers await it", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [finding], droppedAsNonFinding: 0 }) + }) + + // --- Self-negating scenario suffixes --- + + it("drops a finding whose failure_scenario ends with 'Not actionable.'", () => { + const finding = makeFinding({ + failure_scenario: + "The boundary exists to cover this path. Not actionable.", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) + + it("drops a finding whose failure_scenario ends with 'Not a real issue.'", () => { + const finding = makeFinding({ + failure_scenario: + "After re-tracing, the check handles this correctly. Not a real issue.", + }) + + const result = filterNonFindings([finding]) + + expect(result).toEqual({ findings: [], droppedAsNonFinding: 1 }) + }) }) diff --git a/src/review/filter-non-findings.ts b/src/review/filter-non-findings.ts index b3c8122..3ad0ca4 100644 --- a/src/review/filter-non-findings.ts +++ b/src/review/filter-non-findings.ts @@ -19,6 +19,11 @@ const CONFIRMATION_PREFIX = /** Declarative confirmation titles — "…is correct" / "…is accurate" — end-anchored. */ const CONFIRMATION_TITLE_SUFFIX = /\bis\s+(?:correct|accurate)\s*\.?\s*$/i +/** Self-negating title — requires a separator or end-of-title after the + * phrase so qualified retractions survive ("not a bug on X but Y…"). */ +const SELF_NEGATING_TITLE = + /\bnot (?:actionable|a defect|an? (?:real )?(?:issue|bug|problem))\s*(?:[—–:.-]|$)/i + /** Prior-finding resolution confirmations — "Prior bot finding addressed: …" * — start-anchored with a required resolution verb, so real findings about * prior-comment handling ("Prior bot comments cap drops newest…") survive. */ @@ -29,7 +34,7 @@ const CONFIRMATION_TITLE_PREFIX = * ("…No bug here.", "…my analysis was wrong.") — self-referential verdicts * that never legitimately end a failure description. */ const CONFIRMATION_SCENARIO_SUFFIX = - /(?:\bno bug(?: here)?|\banalysis was wrong)\s*\.?\s*$/i + /(?:\bno bug(?: here)?|\bnot (?:actionable|a defect|an? (?:real )?(?:issue|bug|problem))|\banalysis was wrong)\s*\.?\s*$/i /** Start-of-field signal shared by title, failure_scenario, and suggestion: * an explicit non-finding prefix or a separator-anchored confirmation phrase. */ @@ -37,15 +42,15 @@ const hasNonFindingSignal = (text: string): boolean => { return NON_FINDING_PREFIX.test(text) || CONFIRMATION_PREFIX.test(text) } -/** Checks title, failure_scenario, and suggestion against start- and - * end-anchored patterns that signal the model reported a non-finding - * rather than a real defect. */ +/** Checks title, failure_scenario, and suggestion for patterns that + * signal the model reported a non-finding rather than a real defect. */ const isNonFinding = (finding: Finding): boolean => { const title = finding.title.trim() const failureScenario = finding.failure_scenario.trim() if (hasNonFindingSignal(title)) return true if (CONFIRMATION_TITLE_SUFFIX.test(title)) return true if (CONFIRMATION_TITLE_PREFIX.test(title)) return true + if (SELF_NEGATING_TITLE.test(title)) return true if (hasNonFindingSignal(failureScenario)) return true if (CONFIRMATION_SCENARIO_SUFFIX.test(failureScenario)) return true if (finding.suggestion && hasNonFindingSignal(finding.suggestion.trim())) {