fix(review): a native finding's Severity and Blocks: no after prose on its line count as its fields (GST-1406)

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-05 01:59:20 +00:00
1 parent 6bdafb2cf5
commit 11283d4ef0
3 files changed
+22

No files matched your search

@@ -0,0 +1,3 @@
### Fixed
- **A native adversarial finding that says `Blocks: no` after some prose is no longer collected as CRITICAL (GST-1406).** `gstack-review-dispatch collect` honored GST-1031's rule (a FIXABLE finding with its own `Severity` and `Blocks: no` collects at that severity) only when the fields started their own line. When the reader wrote them after prose (`… so the docs are wrong. Severity: Medium. Blocks: no.`), the heading's FIXABLE won. If the line was in the heading's paragraph, the fields split off as a second record. Lead judgment can't lower a CRITICAL, so the native record stayed unresolved and /ship's Step 11.5 couldn't bind. Now such a line counts as the finding's fields. A finding with no `Blocks: no`, or with `Blocks: yes`, still collects CRITICAL.
+6
View File
@@ -222,6 +222,12 @@ export function parseFindings(reader: string, output: string, fixableRaises = tr
}
const heading = LABEL_HEADING.exec(t);
if (heading) { section = fixableRaises ? named(heading[1]!) : heading[1]!; current = null; head = ''; continue; }
if (current?.heading && !current.fields.size && fieldOf(t) !== undefined && !LEAD_LABEL.test(t) && !LIST_ITEM.test(t) && !HEADING.test(t) && !NUMBERED_HEAD.test(t) && !LOCUS.test(t)) {
current.fields.add('severity');
current.label = blocks ? 'CRITICAL' : critical(current.raw) ? stronger(current.label, label!) : strongestOf([current.lead, label])!;
current.raw += ` | ${t}`;
continue;
}
if (current?.heading && !current.fields.size && !label && !blocks && !/^#{1,6}\s/.test(t) && !NUMBERED_HEAD.test(t) && !t.startsWith('{')) { current.raw += ` | ${t}`; continue; }
if (!indented && HEADING.test(t)) { section = null; current = null; }
if (t.startsWith('{')) {
+13
View File
@@ -39,6 +39,19 @@ describe('one record per native finding at its stated severity (GST-1031 case 1,
}
expect(labels('adversarial-native', '## FIXABLE\n1. Leak in a.ts:3\n2. Race in b.ts:4. Blocks: no', true)).toEqual(['CRITICAL', 'CRITICAL']);
});
test('fields that follow body prose on their line still count as the fields (GST-1406)', () => {
// Value: protects=ruling A when the reader writes its Severity and Blocks: no after prose on the same line, in the heading's
// paragraph or after a blank line; fails_when=the heading's FIXABLE collects CRITICAL (an unlowerable blocker that stops Step
// 11.5) or the field line splits off as a second record; why_new=GSTA-275 GST-1276 v4 native pass, finding 1; seam=parseFindings
const head = '**1. Title (FIXABLE).** body in lib/x.ts.';
for (const raises of [true, false]) {
expect(labels('adversarial-native', [head, '', 'More prose. Severity: Medium. Blocks: no.'].join('\n'), raises)).toEqual(['Medium']);
expect(labels('adversarial-native', [head, '- a', '- b', '', 'More prose. Severity: Medium. Blocks: no, because a reader still reads it.'].join('\n'), raises)).toEqual(['Medium']);
expect(labels('adversarial-native', [head, 'More prose. Severity: Medium. Blocks: no.'].join('\n'), raises)).toEqual(['Medium']);
expect(labels('adversarial-native', [head, '', 'More prose. Severity: Medium.'].join('\n'), raises)).toEqual(['CRITICAL']);
expect(labels('adversarial-native', [head, '', 'More prose. Severity: Medium. Blocks: yes.'].join('\n'), raises)).toEqual(['CRITICAL']);
}
});
test('collect records the stated severity beside the one record', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gst1031-'));
const c = collect(path.join(dir, 'l.jsonl'), 'adversarial-native', fixture('gst-1031-native.txt'), path.join(dir, 'm.jsonl'));