From 515847fb960ea6147c4d984a79d847ded995db73 Mon Sep 17 00:00:00 2001 From: garrytan Date: Mon, 2 Nov 2026 23:32:10 +0000 Subject: [PATCH] fix(review): a Blocks yes in any form vetoes unblocking; summary candidates need a closing noun and cover label lists (GST-1031 Step 11) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- lib/review-ledger.ts | 7 ++++--- test/review-gst1031.test.ts | 11 ++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/review-ledger.ts b/lib/review-ledger.ts index 663139421..7ace70d5b 100644 --- a/lib/review-ledger.ts +++ b/lib/review-ledger.ts @@ -85,7 +85,7 @@ const LEAD_LABEL = new RegExp(`^(?:[^\\p{L}\\p{N}\\s]\\s*|\\d+[.)]\\s*)*(?:\\w[\ const STATED_BLOCKS = /(? { if (raw.startsWith('{')) return false; const said = [...raw.matchAll(BLOCKS_FIELD)].map(m => m[1]!.toLowerCase()); return said.length > 0 && !said.includes('yes'); }; +export const statesNoBlocks = (raw: string) => { if (raw.startsWith('{') || BLOCKS_YES.test(raw)) return false; const said = [...raw.matchAll(BLOCKS_FIELD)].map(m => m[1]!.toLowerCase()); return said.length > 0 && !said.includes('yes'); }; /** * A summary candidate (GST-1031): a prose finding whose every severity word is denied ("so nothing is CRITICAL", "No CRITICAL issues") * or names a fix ("the test covers the CRITICAL fix"), with no lead label, no Severity or Blocks field, no FIXABLE or INVESTIGATE tag, @@ -95,7 +95,8 @@ export const statesNoBlocks = (raw: string) => { if (raw.startsWith('{')) return const SENTENCE = /(?<=[.!?])\s+/; const EXCEPTION = /(? ANY_LABEL.test(t) && !ANY_LABEL.test(t.replace(DENIED_OR_FIXED, ' ')) && !t.startsWith('{') && !t.startsWith('**') && !LIST_ITEM.test(t) && !HEADING.test(t) && !LEAD_LABEL.test(t) && !t.match(SEVERITY_FIELD) && !t.match(STATED_BLOCKS) && [...t.matchAll(ALL_LABELS)].every(m => !!rank(m[1])) && t.split(SENTENCE).filter(s => ANY_LABEL.test(s)).every(s => !PATH_LOCUS.test(s) && !EXCEPTION.test(s)); @@ -103,7 +104,7 @@ export function stated(raw: string): Stated | undefined { if (raw.startsWith('{')) return undefined; const severity = [...raw.matchAll(STATED_SEVERITY)].at(-1)?.[1]; const said = [...raw.matchAll(BLOCKS_FIELD)].map(m => m[1]!.toLowerCase()); - const blocks = said.includes('yes') ? 'yes' : said.at(-1); + const blocks = said.includes('yes') || (said.length && BLOCKS_YES.test(raw)) ? 'yes' : said.at(-1); return severity || blocks ? { ...(severity ? { severity } : {}), ...(blocks ? { blocks } : {}) } : undefined; } diff --git a/test/review-gst1031.test.ts b/test/review-gst1031.test.ts index 32a5a2412..c8a4b76f0 100644 --- a/test/review-gst1031.test.ts +++ b/test/review-gst1031.test.ts @@ -70,11 +70,16 @@ describe('a closing summary is a candidate lead judgment may dismiss on record ( fs.rmSync(d, { recursive: true, force: true }); } }); + test('coordinated denials are candidates', () => { + for (const line of ['No CRITICAL or HIGH findings remain.', 'Nothing CRITICAL or HIGH remains.', 'No CRITICAL/HIGH findings.', 'No CRITICAL, HIGH or MEDIUM issues.', 'Nothing at CRITICAL severity remains.', 'No CRITICAL — the fix is sound.']) + expect([line, summaryCandidate(line)]).toEqual([line, true]); + }); test('negative controls: a locus, an exception clause, a lead label, a field or a JSON finding is no candidate and keeps its label', () => { for (const line of ['No CRITICAL issues except the token leak in log.ts:3.', 'Nothing else is CRITICAL, but the retry loop never ends.', 'The token leak in log.ts:3 is CRITICAL.', 'The leak in bin/gstack-log:3 is CRITICAL.', 'CRITICAL: the retry loop never ends.', 'The retry loop never ends. Severity: High', 'Not checked. Blocks: no. CRITICAL', '{"severity":"CRITICAL","summary":"x"}', 'The retry loop in the upload handler never ends, which is CRITICAL.', 'Path traversal in the download route lets any user read arbitrary files; this is CRITICAL.', - 'The session token is written to the debug log on every request, a CRITICAL leak.', 'Nothing guards the write path the failure of which is CRITICAL']) { + 'The session token is written to the debug log on every request, a CRITICAL leak.', 'Nothing guards the write path the failure of which is CRITICAL', + 'There is no CRITICAL lock around the refund write, so two workers double-pay.', 'The patch closes the CRITICAL window only on Linux; on macOS the race remains.']) { expect([line, summaryCandidate(line)]).toEqual([line, false]); expect(parseFindings('fix-check', line).length).toBe(1); } @@ -116,6 +121,10 @@ describe('the blocker count keys on Blocks (GST-1031 case 3)', () => { fs.rmSync(d, { recursive: true, force: true }); } for (const raises of [true, false]) expect(labels('adversarial-native', '**1. Race in a.ts:3. FIXABLE.**\nIt blocks no attacker.\nSeverity: Low.', raises)).toEqual(['CRITICAL']); + const q = fs.mkdtempSync(path.join(os.tmpdir(), 'gst1031y-')); + const y = collect(path.join(q, 'l.jsonl'), 'outside-voice', 'CRITICAL: auth.ts:9 skips authentication. The previous note said `Blocks: no`. Blocks yes.\n', path.join(q, 'm.jsonl')); + expect([y.lines[0]!.stated?.blocks, ledgerBlockers(path.join(q, 'l.jsonl')).map(b => b.id)]).toEqual(['yes', ['outside-voice#1']]); + fs.rmSync(q, { recursive: true, force: true }); const d = fs.mkdtempSync(path.join(os.tmpdir(), 'gst1031j-')); collect(path.join(d, 'l.jsonl'), 'specialist', JSON.stringify({ severity: 'CRITICAL', path: 'lib/auth.ts', line: 9, summary: 'the old guard, which a reader marked `Blocks: no`, is gone so any caller gets admin' }) + '\n', path.join(d, 'm.jsonl')); expect(ledgerBlockers(path.join(d, 'l.jsonl')).map(b => b.id)).toEqual(['specialist#1']);