From 6179604ef76e57e84793458f819178faf1b48689 Mon Sep 17 00:00:00 2001 From: Claude Auto-Fix Date: Fri, 15 May 2026 14:07:28 -0400 Subject: [PATCH 1/2] fix(pr-review): drop zero-count summary lines in severity pre-filter (#269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a third alternation to the resolution-narration pre-filter that drops any line whose severity token is followed by ": ". This prevents `- 🔴 Critical (BLOCKING): 0` and similar findings-tally summary lines from being miscounted as load-bearing severity hits. Updates all three callsites (authoritative quality-gate, synthesis fallback, shadow gate) to stay in sync per the in-file comment. Adds a marker-case fixture using the claude-configs#564 reproducer. Note: lib/severity-regex.sh contains only severity-token patterns (not the pre-filter); no change needed at that file. Fixes #269 Refs glitchwerks/claude-configs#564 --- pr-review/action.yml | 24 ++++++++--- .../zero-count-tally-lines.expected | 1 + .../marker-cases/zero-count-tally-lines.md | 40 +++++++++++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 pr-review/tests/marker-cases/zero-count-tally-lines.expected create mode 100644 pr-review/tests/marker-cases/zero-count-tally-lines.md diff --git a/pr-review/action.yml b/pr-review/action.yml index 85ce695..24437ee 100644 --- a/pr-review/action.yml +++ b/pr-review/action.yml @@ -393,7 +393,12 @@ runs: # end-of-line (\s*$) so it only matches the persona's actual terminal suffix # and does not false-positive-suppress findings that mention FIXED mid-line. # Without this pre-filter, the gate blocks PRs whose findings are all fixed. - BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) + # Third branch (issue #269, reproducer glitchwerks/claude-configs#564): drop + # lines whose severity token is followed by `: ` at end of line. + # These are findings-tally summary lines (e.g. `- 🔴 Critical (BLOCKING): 0`) + # emitted by the persona as a count report, not as individual findings. + # A non-zero summary is also not itself a finding; per-item callouts are. + BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|:\s*[0-9]+\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) echo "Quality gate scan: $BLOCKER_HITS marker(s) matched in latest sticky comment" @@ -474,9 +479,14 @@ runs: # rather than on the same line as the severity token. Pattern anchored to # end-of-line (\s*$) to avoid false-positive suppression of findings that # mention FIXED/ADDRESSED/RESOLVED bold mid-line (#258 review feedback). + # Third branch (issue #269, reproducer glitchwerks/claude-configs#564): drop + # lines whose severity token is followed by `: ` at end of line. + # These are findings-tally summary lines (e.g. `- 🔴 Critical (BLOCKING): 0`) + # emitted by the persona as a count report, not as individual findings. + # A non-zero summary is also not itself a finding; per-item callouts are. # Same pre-filter applies in the authoritative and shadow gate steps; # all three callsites must stay in sync. - OPEN_BODY=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$' || true) + OPEN_BODY=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|:\s*[0-9]+\s*$' || true) CRITICAL=$(printf '%s' "$OPEN_BODY" | grep -oE "$SEVERITY_CRITICAL_RE" | wc -l || true) HIGH=$(printf '%s' "$OPEN_BODY" | grep -oE "$SEVERITY_HIGH_RE" | wc -l || true) MEDIUM=$(printf '%s' "$OPEN_BODY" | grep -oE "$SEVERITY_MEDIUM_RE" | wc -l || true) @@ -575,10 +585,12 @@ runs: # on incremental reviews don't inflate the prose hit count. Expanded # in #257 to also cover **FIXED**/**ADDRESSED**/**RESOLVED** suffixes # (bold markdown). Anchored to end-of-line (\s*$) so only the persona's - # terminal suffix is suppressed, not mid-line occurrences (#258). Must stay - # in sync with the authoritative gate step and the synthesis pre-filter - # (#248, #257, #258). - BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) + # terminal suffix is suppressed, not mid-line occurrences (#258). Third + # branch (issue #269): also drop findings-tally summary lines ending in + # `: ` (e.g. `- 🔴 Critical (BLOCKING): 0`). Must stay in sync + # with the authoritative gate step and the synthesis pre-filter + # (#248, #257, #258, #269). + BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|:\s*[0-9]+\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) if [ "$BLOCKER_HITS" -gt "0" ]; then PROSE_RESULT="blocking" else diff --git a/pr-review/tests/marker-cases/zero-count-tally-lines.expected b/pr-review/tests/marker-cases/zero-count-tally-lines.expected new file mode 100644 index 0000000..2516dfd --- /dev/null +++ b/pr-review/tests/marker-cases/zero-count-tally-lines.expected @@ -0,0 +1 @@ +clean|0|0|1|2 diff --git a/pr-review/tests/marker-cases/zero-count-tally-lines.md b/pr-review/tests/marker-cases/zero-count-tally-lines.md new file mode 100644 index 0000000..b1ae078 --- /dev/null +++ b/pr-review/tests/marker-cases/zero-count-tally-lines.md @@ -0,0 +1,40 @@ +**Claude finished @cbeaulieu-gt's task in 52s** —— [View job](https://github.com/glitchwerks/github-actions/actions/runs/99999000564) + +--- +### PR Review Complete + +I've reviewed this pull request and found no blocking issues. + +### Findings + +**Findings:** +- 🔴 Critical (BLOCKING): 0 +- 🟡 High-Priority (MAJOR): 0 +- 🟢 Medium: 1 +- Nit: 2 + +#### 🟢 Medium + +**Minor style inconsistency** — variable naming convention differs from the rest of the module. + +#### Nit + +**Redundant blank line** at line 42. + +**Trailing whitespace** at line 87. + +--- + +Verdict: APPROVE (no blocking issues found) + + From ab80d22852b1a69869d1b14bdad837e5eb96ff2c Mon Sep 17 00:00:00 2001 From: Claude Auto-Fix Date: Fri, 15 May 2026 15:00:17 -0400 Subject: [PATCH 2/2] fix(pr-review): require parenthesized severity-class in zero-count drop (#269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first attempt at the pre-filter was too broad — `:\s*[0-9]+\s*$` dropped all colon-digit-suffix lines, including legitimate per-finding tally entries like `- 🟢 Medium: 1`. Replace with a pattern that requires the parenthesized `(BLOCKING|MAJOR|MEDIUM|LOW)` class marker the persona emits as the structural signature of a summary line. Adds non-zero-summary and finding-tally fixture lines so a future regression in either direction is caught. Refs #269 Refs glitchwerks/claude-configs#564 --- pr-review/action.yml | 37 +++++++++++-------- .../marker-cases/zero-count-tally-lines.md | 7 ++++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/pr-review/action.yml b/pr-review/action.yml index 24437ee..f15d2dc 100644 --- a/pr-review/action.yml +++ b/pr-review/action.yml @@ -394,11 +394,13 @@ runs: # and does not false-positive-suppress findings that mention FIXED mid-line. # Without this pre-filter, the gate blocks PRs whose findings are all fixed. # Third branch (issue #269, reproducer glitchwerks/claude-configs#564): drop - # lines whose severity token is followed by `: ` at end of line. - # These are findings-tally summary lines (e.g. `- 🔴 Critical (BLOCKING): 0`) - # emitted by the persona as a count report, not as individual findings. - # A non-zero summary is also not itself a finding; per-item callouts are. - BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|:\s*[0-9]+\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) + # persona-emitted summary-tally lines of the form `- 🔴 Critical (BLOCKING): 0`. + # The parenthesized severity-class marker `(BLOCKING|MAJOR|MEDIUM|LOW)` is the + # structural signature — legitimate per-finding callouts like `- 🟢 Medium: 1` + # don't carry the parenthesized class. Pattern is intentionally count-agnostic: + # a summary line is never itself a finding regardless of whether its count is + # zero or non-zero. Issue: #269. + BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|\((BLOCKING|MAJOR|MEDIUM|LOW)\):\s*[0-9]+\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) echo "Quality gate scan: $BLOCKER_HITS marker(s) matched in latest sticky comment" @@ -480,13 +482,15 @@ runs: # end-of-line (\s*$) to avoid false-positive suppression of findings that # mention FIXED/ADDRESSED/RESOLVED bold mid-line (#258 review feedback). # Third branch (issue #269, reproducer glitchwerks/claude-configs#564): drop - # lines whose severity token is followed by `: ` at end of line. - # These are findings-tally summary lines (e.g. `- 🔴 Critical (BLOCKING): 0`) - # emitted by the persona as a count report, not as individual findings. - # A non-zero summary is also not itself a finding; per-item callouts are. + # persona-emitted summary-tally lines of the form `- 🔴 Critical (BLOCKING): 0`. + # The parenthesized severity-class marker `(BLOCKING|MAJOR|MEDIUM|LOW)` is the + # structural signature — legitimate per-finding callouts like `- 🟢 Medium: 1` + # don't carry the parenthesized class. Pattern is intentionally count-agnostic: + # a summary line is never itself a finding regardless of whether its count is + # zero or non-zero. Issue: #269. # Same pre-filter applies in the authoritative and shadow gate steps; # all three callsites must stay in sync. - OPEN_BODY=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|:\s*[0-9]+\s*$' || true) + OPEN_BODY=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|\((BLOCKING|MAJOR|MEDIUM|LOW)\):\s*[0-9]+\s*$' || true) CRITICAL=$(printf '%s' "$OPEN_BODY" | grep -oE "$SEVERITY_CRITICAL_RE" | wc -l || true) HIGH=$(printf '%s' "$OPEN_BODY" | grep -oE "$SEVERITY_HIGH_RE" | wc -l || true) MEDIUM=$(printf '%s' "$OPEN_BODY" | grep -oE "$SEVERITY_MEDIUM_RE" | wc -l || true) @@ -586,11 +590,14 @@ runs: # in #257 to also cover **FIXED**/**ADDRESSED**/**RESOLVED** suffixes # (bold markdown). Anchored to end-of-line (\s*$) so only the persona's # terminal suffix is suppressed, not mid-line occurrences (#258). Third - # branch (issue #269): also drop findings-tally summary lines ending in - # `: ` (e.g. `- 🔴 Critical (BLOCKING): 0`). Must stay in sync - # with the authoritative gate step and the synthesis pre-filter - # (#248, #257, #258, #269). - BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|:\s*[0-9]+\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) + # branch (issue #269): drop persona-emitted summary-tally lines of the form + # `- 🔴 Critical (BLOCKING): 0` — the parenthesized severity-class marker + # `(BLOCKING|MAJOR|MEDIUM|LOW)` is the structural signature. Legitimate + # per-finding callouts like `- 🟢 Medium: 1` don't carry the parenthesized + # class. Reproducer: glitchwerks/claude-configs#564. Issue: #269. + # Must stay in sync with the authoritative gate step and the synthesis + # pre-filter (#248, #257, #258, #269). + BLOCKER_HITS=$(printf '%s' "$BODY" | grep -vE '✅|\*\*(FIXED|ADDRESSED|RESOLVED)\*\*\s*$|\((BLOCKING|MAJOR|MEDIUM|LOW)\):\s*[0-9]+\s*$' | grep -E -c "$SEVERITY_BLOCKER_RE" || true) if [ "$BLOCKER_HITS" -gt "0" ]; then PROSE_RESULT="blocking" else diff --git a/pr-review/tests/marker-cases/zero-count-tally-lines.md b/pr-review/tests/marker-cases/zero-count-tally-lines.md index b1ae078..e880813 100644 --- a/pr-review/tests/marker-cases/zero-count-tally-lines.md +++ b/pr-review/tests/marker-cases/zero-count-tally-lines.md @@ -10,9 +10,16 @@ I've reviewed this pull request and found no blocking issues. **Findings:** - 🔴 Critical (BLOCKING): 0 - 🟡 High-Priority (MAJOR): 0 +- 🔴 Critical (BLOCKING): 5 - 🟢 Medium: 1 - Nit: 2 +The three lines above the finding-tally entries are persona-emitted summary-tally +lines (#564 reproducer). They carry the parenthesized severity-class marker and +MUST be dropped by the pre-filter regardless of count (zero or non-zero). +The `- 🟢 Medium: 1` and `- Nit: 2` lines are legitimate per-finding tally +entries — no parenthesized class — and MUST be kept. + #### 🟢 Medium **Minor style inconsistency** — variable naming convention differs from the rest of the module.