diff --git a/pr-review/action.yml b/pr-review/action.yml index 85ce695..f15d2dc 100644 --- a/pr-review/action.yml +++ b/pr-review/action.yml @@ -393,7 +393,14 @@ 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 + # 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" @@ -474,9 +481,16 @@ 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 + # 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*$' || 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) @@ -575,10 +589,15 @@ 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): 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.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..e880813 --- /dev/null +++ b/pr-review/tests/marker-cases/zero-count-tally-lines.md @@ -0,0 +1,47 @@ +**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 +- 🔴 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. + +#### Nit + +**Redundant blank line** at line 42. + +**Trailing whitespace** at line 87. + +--- + +Verdict: APPROVE (no blocking issues found) + +