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>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-11-02 23:32:10 +00:00
1 parent 6c59cd2d33
commit 515847fb96
2 files changed
+14 -4

No files matched your search

+4 -3
View File
@@ -85,7 +85,7 @@ const LEAD_LABEL = new RegExp(`^(?:[^\\p{L}\\p{N}\\s]\\s*|\\d+[.)]\\s*)*(?:\\w[\
const STATED_BLOCKS = /(?<![A-Za-z0-9])Blocks\**\s*:?\s*\**\s*(yes|no)(?![A-Za-z0-9])/gi;
/** A prose reader's `Blocks` field as it can lower a count (GST-1031): its colon required, so prose ("blocks no other writer") never does, and a JSON finding (no Blocks field in its schema) never has one. */
const BLOCKS_FIELD = /(?<![A-Za-z0-9])Blocks\**\s*:\s*\**\s*(yes|no)(?![A-Za-z0-9-])/gi;
export const statesNoBlocks = (raw: string) => { 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 = /(?<![A-Za-z])(?:except|but|other than|apart from|aside from|besides|unless|save for)(?![A-Za-z])/i;
const PATH_LOCUS = /(?:[\w.-]+\/)+[\w.-]+:\d+|[\w.-]+\.\w+:\d+/;
const DENIED_OR_FIXED = new RegExp(`(?:(?<![A-Za-z])(?:nothing|none|no|neither|zero)(?:\\s+(?:of\\s+)?(?:these|them|this|it|other|else|further|new|remaining|the\\s+\\w+|\\w+\\s+findings?|findings?|issues?))?\\s+(?:else\\s+)?(?:(?:is|are|was|were|would\\s+be|at)\\s+)?|(?<![A-Za-z])(?:cover(?:s|ed)?|fix(?:es|ed)?|address(?:es|ed)?|resolve[sd]?|close[sd]?)\\s+(?:the|that|this)\\s+)[*\`]*(?:${LABELS})(?![A-Za-z0-9])`, 'gi');
const LABEL_LIST = `[*\`]*(?:${LABELS})[*\`]*(?:\\s*(?:,|/|\\bor\\b|\\band\\b|\\bnor\\b)\\s*[*\`]*(?:${LABELS})[*\`]*)*(?![A-Za-z0-9])`;
const DENIED_OR_FIXED = new RegExp(`(?:(?<![A-Za-z])(?:nothing|none|no|neither|zero)(?:\\s+(?:of\\s+)?(?:these|them|this|it|other|else|further|new|remaining|the\\s+\\w+|\\w+\\s+findings?|findings?|issues?))?\\s+(?:else\\s+)?(?:(?:is|are|was|were|would\\s+be|at)\\s+)?${LABEL_LIST}(?=\\s*(?:issues?|findings?|problems?|regressions?|bugs?|defects?|blockers?|concerns?|items?|severity|impact|remains?|to\\s+report|here|found|introduced|[.;:,!?)—–-]|$))|(?<![A-Za-z])(?:cover(?:s|ed)?|fix(?:es|ed)?|address(?:es|ed)?|resolve[sd]?|close[sd]?)\\s+(?:the|that|this)\\s+${LABEL_LIST}\\s+(?:fix|finding|issue|bug|defect|blocker)(?:e?s)?(?![A-Za-z]))`, 'gi');
export const summaryCandidate = (t: string) => 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;
}
+10 -1
View File
@@ -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']);