From b3ddd6c74f1a1f34bf93c6ab6efd6581ef9d0d2b Mon Sep 17 00:00:00 2001 From: garrytan Date: Mon, 2 Nov 2026 13:03:41 +0000 Subject: [PATCH] =?UTF-8?q?fix(review-ledger):=20a=20field=20at=20the=20li?= =?UTF-8?q?ne's=20start=20needs=20no=20closing=20clause;=20mid-line=20fiel?= =?UTF-8?q?ds=20accept=20:=20/=20and=20=E2=86=92=20after=20the=20label=20(?= =?UTF-8?q?GST-976=20fix-check)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- lib/review-ledger.ts | 4 ++-- test/review-dispatch.test.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/review-ledger.ts b/lib/review-ledger.ts index a4001aba3..67b975376 100644 --- a/lib/review-ledger.ts +++ b/lib/review-ledger.ts @@ -72,7 +72,7 @@ const STATED_SEVERITY = new RegExp(`(?]\\s+|\\d+[.)]\\s+)?\\**\\s*Severity\\**\\s*:?|(?:[.;,|()]\\s*|\\s[—–-]\\s*)\\**\\s*Severity\\**\\s*:)\\s*\\**\\s*(${LABELS})(?![A-Za-z0-9])(?=\\**\\s*(?:$|[.;,|()*—–]|-\\s|Blocks))`, 'gi'); +const SEVERITY_FIELD = new RegExp(`(?:^(?:[-*+>]\\s+|\\d+[.)]\\s+)?\\**\\s*Severity\\**\\s*:?\\s*\\**\\s*(${LABELS})(?![A-Za-z0-9])|(?:[.;,|()]\\s*|\\s[—–-]\\s*)\\**\\s*Severity\\**\\s*:\\s*\\**\\s*(${LABELS})(?![A-Za-z0-9])(?=\\**\\s*(?:$|[.;,|()*:/→—–]|-\\s|Blocks)))`, 'gi'); /** * A label leading its line past list, quote, heading, emphasis and emoji marks (one mark per step, so a long divider stays linear) * and an optional file:line, closed by a mark or a separator, or by a space, comma or parenthesis after an all-caps word ("[P0] …", @@ -114,7 +114,7 @@ export function parseFindings(reader: string, output: string): Array<{ label: st const named = (l: string) => (role === 'adversarial-native' && l.toUpperCase() === 'FIXABLE' ? 'CRITICAL' : l); const strongestOf = (labels: Array) => labels.reduce((a, b) => (a === undefined ? b : b === undefined ? a : stronger(a, b)), undefined); const strongest = (t: string) => strongestOf([...t.matchAll(ALL_LABELS)].map(m => named(m[1]!))); - const fieldOf = (t: string) => strongestOf([...t.matchAll(SEVERITY_FIELD)].map(m => m[1]!)); + const fieldOf = (t: string) => strongestOf([...t.matchAll(SEVERITY_FIELD)].map(m => m[1] ?? m[2]!)); const critical = (t: string) => BLOCKS_YES.test(t) || (role === 'adversarial-native' && /(? { const l = LEAD_LABEL.exec(t)?.[1]; return l === undefined ? undefined : named(l); }; const labelOf = (t: string) => { diff --git a/test/review-dispatch.test.ts b/test/review-dispatch.test.ts index 2b9f19979..81b372e1c 100644 --- a/test/review-dispatch.test.ts +++ b/test/review-dispatch.test.ts @@ -688,7 +688,10 @@ describe('gstack-review-dispatch sent, collect, judge and verify: nothing change ['1. The P0 pilot table drops a row\nSeverity Low', 'Low'], ['1. The P0 pilot table drops a row\n**Severity** Low', 'Low'], ['1. Bypass x.ts:3 is CRITICAL (Severity: Low in pass 1)', 'CRITICAL'], ['Still open: x.ts:3 is CRITICAL, Severity: Low in pass 1 understated it', 'CRITICAL'], ['1. lib/x.ts:42-58 CRITICAL: drops rows. Severity: Low', 'CRITICAL'], ['1. lib/x.ts:42:7 CRITICAL: drops rows. Severity: Low', 'CRITICAL'], - ['1. (lib/x.ts:42, 50) CRITICAL: drops rows. Severity: Low', 'CRITICAL']]; + ['1. (lib/x.ts:42, 50) CRITICAL: drops rows. Severity: Low', 'CRITICAL'], + ['1. The P0 pilot table drops a row\nSeverity: Low: only the pilot', 'Low'], ['1. The P0 pilot table drops a row\nSeverity: Low because it is internal', 'Low'], + ['1. The P0 pilot table drops a row\nSeverity: Low/Medium', 'Low'], ['Severity: Low: only the P0 pilot table', 'Low'], + ['1. The P0 pilot drops a row. Severity: Low/P3', 'Low'], ['1. The P0 pilot drops a row. Severity: Low → it is internal', 'Low']]; for (const [output, label] of p2) expect([output, parseFindings('outside-voice', output).map(f => f.label)]).toEqual([output, [label]]); const t0 = performance.now(); for (const run of ['='.repeat(60), '-'.repeat(60), '|' + '-'.repeat(60) + '|', '🔴'.repeat(60), '. '.repeat(60), '-.'.repeat(60), '`'.repeat(60)])