mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-12 11:40:51 +01:00
train S2: merge #3867 at f017177ee9 v1.93.4.0
This commit is contained in:
commit
a426e8e66f
9 files changed
+185
-22
No files matched your search
@@ -0,0 +1 @@
|
||||
{"note":"parseFindings labels at main 82e9d7ec (GST-976): every input the review-dispatch and prose-voice-floor tests pass it, every reader fixture here, and generated Severity field shapes. Regenerate only from a frozen main.","main":"82e9d7ec7a2c222f20b04aaf5b965fa50d7b6bdb","entries":[{"source":"pinned","reader":"adversarial-native","output":"FIXABLE: race in a.ts:3\nSeverity: Medium\nBlocks: no","main":["CRITICAL"]},{"source":"pinned","reader":"adversarial-native","output":"INVESTIGATE: odd lock in b.ts:9\nSeverity: Low\nBlocks: yes","main":["CRITICAL"]},{"source":"pinned","reader":"adversarial-native","output":"INVESTIGATE: odd lock in b.ts:9\nBlocks: no","main":["INVESTIGATE"]},{"source":"pinned","reader":"adversarial-native","output":"FIXABLE: race in a.ts:3. Severity: Medium. Blocks: no","main":["CRITICAL"]},{"source":"pinned","reader":"adversarial-native","output":"1. FIXABLE: race in a.ts:3 — Severity: Low, Blocks: no","main":["CRITICAL"]},{"source":"pinned","reader":"adversarial-native","output":"- FIXABLE: race in a.ts:3\n- Severity: Medium\n- Blocks: no","main":["CRITICAL"]},{"source":"pinned","reader":"outside-voice","output":"1. Drops the last row (q.ts:7). Severity: Critical. Blocks: no","main":["Critical"]},{"source":"pinned","reader":"outside-voice","output":"1. Cache evicts sessions (c.ts:4)\n Severity: High","main":["High"]},{"source":"pinned","reader":"outside-voice","output":"1. Retry storm (r.ts:2)\n Severity: Medium\n Blocks: yes","main":["CRITICAL"]},{"source":"pinned","reader":"outside-voice","output":"1. Index drift (i.ts:5). Severity: Low. Blocks: Yes, it loses the row","main":["CRITICAL"]},{"source":"pinned","reader":"outside-voice","output":"1. Naming (n.ts:1)\n Severity: Low\n Blocks: no","main":["Low"]},{"source":"pinned","reader":"specialist/testing","output":"REVIEWED_TREE: d0dd42d6a88fbc2a6abca4f6b2dfde7e35520c5a /home/user/.capy/work/gst976/wt\nNO FINDINGS\n","main":[]},{"source":"pinned","reader":"specialist/testing","output":"REVIEWED_TREE: d0dd42d6a88fbc2a6abca4f6b2dfde7e35520c5a /home/user/.capy/work/gst976/wt\n{\"severity\":\"CRITICAL\",\"path\":\"a.ts\",\"line\":3,\"category\":\"logic\",\"summary\":\"off by one\",\"specialist\":\"testing\"}\n{\"severity\":\"INFORMATIONAL\",\"path\":\"a.ts\",\"line\":9,\"category\":\"style\",\"summary\":\"name\",\"specialist\":\"testing\"}\n","main":["CRITICAL","INFORMATIONAL"]},{"source":"pinned","reader":"outside-voice","output":"REVIEWED_TREE: d0dd42d6a88fbc2a6abca4f6b2dfde7e35520c5a /home/user/.capy/work/gst976/wt\nFindings:\n1. **High**: retry loop never backs off (q.ts:78)\n2. Low - log line is noisy\nRecommendation: Fix the retry because it DoSes the pool\n","main":["High","Low"]},{"source":"pinned","reader":"specialist/testing","output":"REVIEWED_TREE: d0dd42d6a88fbc2a6abca4f6b2dfde7e35520c5a /home/user/.capy/work/gst976/wt\n{\"severity\":\"CRITICAL\",\"path\":\"a.ts\",\"line\":3,\"category\":\"logic\",\"summary\":\"x\",\"specialist\":\"testing\"}\n","main":["CRITICAL"]},{"source":"pinned","reader":"red-team","output":"REVIEWED_TREE: d0dd42d6a88fbc2a6abca4f6b2dfde7e35520c5a /home/user/.capy/work/gst976/wt\n{\"severity\":\"CRITICAL\",\"path\":\"b.ts\",\"line\":1,\"category\":\"x\",\"summary\":\"y\",\"specialist\":\"red-team\"}\n","main":["CRITICAL"]},{"source":"pinned","reader":"specialist/testing","output":"REVIEWED_TREE: d0dd42d6a88fbc2a6abca4f6b2dfde7e35520c5a /home/user/.capy/work/gst976/wt\n{\"severity\":\"CRITICAL\",\"path\":\"a.ts\",\"line\":3,\"category\":\"logic\",\"summary\":\"x\",\"specialist\":\"testing\"}\n{\"severity\":\"INFORMATIONAL\",\"path\":\"b.ts\",\"line\":1,\"category\":\"n\",\"summary\":\"y\",\"specialist\":\"testing\"}\n","main":["CRITICAL","INFORMATIONAL"]},{"source":"pinned","reader":"r","output":"NO FINDINGS\n","main":[]},{"source":"pinned","reader":"r","output":"## High\n1. The auth check skips webhooks (a.ts:4)\n - Impact: nested evidence\n **Fix:**\n- second one\n## Notes\n- not a finding\n**Critical findings**\n- real one\n**Recommendations**\n- consider renaming foo\n","main":["High","High","Critical"]},{"source":"pinned","reader":"r","output":"1. **Unbounded retry in queue.ts:78** (FIXABLE): loops on 429\n2. `auth.ts:42` skips token expiry check — INVESTIGATE\n### CRITICAL: missing auth check in api.ts:10\n#### [P1] token not expired\n### High: authorization bypass (auth.ts:14)\n- **HIGH**: leak\n| High | race in queue.ts:4 |\nClassification: INVESTIGATE\n7. **Informational: reads every file**","main":["FIXABLE","INVESTIGATE","CRITICAL","P1","High","HIGH","High","INVESTIGATE","Informational"]},{"source":"pinned","reader":"r","output":"FIXABLE: race in a.ts:3\nINVESTIGATE: odd lock\nRecommendation: Fix the race because x\n","main":["FIXABLE","INVESTIGATE"]},{"source":"pinned","reader":"outside-voice","output":"No CRITICAL issues except the token leak in log.ts:3","main":["CRITICAL"]},{"source":"pinned","reader":"fix-check","output":"No CRITICAL issues except the token leak in lLine truncated
|
||||
@@ -4,7 +4,7 @@ import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { BriefError, checkReviewedTree, promptHash, renderBrief, sectionTexts, workspaceParagraph, workspaceStep } from '../lib/review-brief';
|
||||
import { collect, judge, parseFindings, readJsonl, type MergedLine } from '../lib/review-ledger';
|
||||
import { collect, judge, parseFindings, rank, readJsonl, type MergedLine } from '../lib/review-ledger';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const BIN = path.join(ROOT, 'bin', 'gstack-review-dispatch');
|
||||
@@ -305,8 +305,9 @@ describe('gstack-review-dispatch sent, collect, judge and verify: nothing change
|
||||
|
||||
// Value: protects=a negated severity word still collects at its label, so a reader's closing prose ("found nothing CRITICAL") is a
|
||||
// known phantom lead judgment folds and never a dropped line (GSTA-167 S1 reverts GST-766's negation drop); fails_when=any negated
|
||||
// form collects nothing or a weaker label; why_new=GST-766's drop lost blockers named in exception clauses; seam=parseFindings;
|
||||
// no_claim=that a negated line is a real finding (judge folds phantoms)
|
||||
// form collects nothing or a weaker label, except under the finding's own Severity field, which decides it (GST-976);
|
||||
// why_new=GST-766's drop lost blockers named in exception clauses; seam=parseFindings; no_claim=that a negated line is a real
|
||||
// finding (judge folds phantoms)
|
||||
test('a negated severity word still collects at its label: GEVA-92 fix-check#5 and its negated forms are known phantoms', () => {
|
||||
const fixCheck = [
|
||||
'REVIEWED_TREE: bedd511efa0fa16c9277821073c68e438dc760d2 /workspace/gstack-evals',
|
||||
@@ -332,7 +333,7 @@ describe('gstack-review-dispatch sent, collect, judge and verify: nothing change
|
||||
expect(parseFindings('r', 'No CRITICAL lock guards the write in Makefile:7\nSeverity: CRITICAL').map(f => f.label)).toEqual(['CRITICAL']);
|
||||
expect(parseFindings('r', '1. auth.ts:3 LOW: log line too verbose\nNo CRITICAL lock guards the write in Makefile:7\nSeverity: CRITICAL').map(f => [f.label, f.raw])).toEqual([['LOW', '1. auth.ts:3 LOW: log line too verbose'], ['CRITICAL', 'No CRITICAL lock guards the write in Makefile:7 | Severity: CRITICAL']]);
|
||||
expect(parseFindings('r', '1. auth.ts:3 LOW: log line too verbose\nNo CRITICAL issues remain.').map(f => f.label)).toEqual(['LOW', 'CRITICAL']);
|
||||
expect(parseFindings('outside-voice', '- a.ts:3 race. Severity: Low. Blocks: no\n Separately, nothing CRITICAL about b.ts:9, but it leaks fds.\n Severity: High').map(f => [f.label, f.raw])).toEqual([['Low', '- a.ts:3 race. Severity: Low. Blocks: no'], ['CRITICAL', 'Separately, nothing CRITICAL about b.ts:9, but it leaks fds. | Severity: High']]);
|
||||
expect(parseFindings('outside-voice', '- a.ts:3 race. Severity: Low. Blocks: no\n Separately, nothing CRITICAL about b.ts:9, but it leaks fds.\n Severity: High').map(f => [f.label, f.raw])).toEqual([['Low', '- a.ts:3 race. Severity: Low. Blocks: no'], ['High', 'Separately, nothing CRITICAL about b.ts:9, but it leaks fds. | Severity: High']]);
|
||||
expect(parseFindings('r', '1. A CRITICAL race in queue.ts:78 that no lock covers').map(f => f.label)).toEqual(['CRITICAL']);
|
||||
expect(parseFindings('r', '- A divide by zero CRITICAL bug in parse()').map(f => f.label)).toEqual(['CRITICAL']);
|
||||
expect(parseFindings('adversarial-native', 'FIXABLE: no timeout on the fetch in a.ts:3').map(f => f.label)).toEqual(['CRITICAL']);
|
||||
@@ -406,8 +407,8 @@ describe('gstack-review-dispatch sent, collect, judge and verify: nothing change
|
||||
expect(parseFindings('outside-voice', '> Blocks: yes').map(f => f.label)).toEqual(['CRITICAL']);
|
||||
});
|
||||
// Value: protects=a later finding's Severity/Blocks line never lands on the finding before it, and an explicit Severity label
|
||||
// decides its line (#3660 pass 2 Red Team); fails_when=the later blocker's text is lost and the earlier finding takes its
|
||||
// CRITICAL, or 'Severity: Low (not High)' collects as High; seam=parseFindings
|
||||
// decides its line unless a stronger label leads that line (#3660 pass 2 Red Team, GST-976); fails_when=the later blocker's text
|
||||
// is lost and the earlier finding takes its CRITICAL, or 'Severity: Low (not High)' collects as High; seam=parseFindings
|
||||
test('prose after a finding\'s field, a blank-line paragraph or an indented block starts the next finding; an explicit Severity label wins', () => {
|
||||
const raws = (o: string) => parseFindings('outside-voice', o).map(f => [f.label, f.raw]);
|
||||
const two = [['Low', 'A in a.ts:1. | Severity: Low'], ['CRITICAL', 'B in b.ts:2. | Blocks: yes']];
|
||||
@@ -603,7 +604,7 @@ describe('gstack-review-dispatch sent, collect, judge and verify: nothing change
|
||||
for (const line of ['There are no CRITICAL findings.', 'No CRITICAL or High findings to report.', 'I found nothing CRITICAL.', "So there's nothing at CRITICAL.",
|
||||
'Neither finding is CRITICAL.', 'Nothing here is CRITICAL.', 'Neither fix is CRITICAL.', 'Nothing in this diff is CRITICAL.', 'This is not CRITICAL.'])
|
||||
expect([line, parseFindings('outside-voice', line).map(f => f.label)]).toEqual([line, ['CRITICAL']]);
|
||||
expect(parseFindings('outside-voice', 'Severity: Low (not High)').map(f => f.label)).toEqual(['High']);
|
||||
expect(parseFindings('outside-voice', 'Severity: Low (not High)').map(f => f.label)).toEqual(['Low']);
|
||||
});
|
||||
|
||||
// Value: protects=item layouts collect as on main: an indented JSON finding keeps its JSON severity, an indented label heading sets
|
||||
@@ -626,6 +627,82 @@ describe('gstack-review-dispatch sent, collect, judge and verify: nothing change
|
||||
.toEqual([['CRITICAL', '{"severity":"low","summary":"race"} | this blocks the merge. Blocks: yes']]);
|
||||
});
|
||||
|
||||
// Value: protects=a finding's own Severity and Blocks fields set its collected label, so a severity word in its body never raises it
|
||||
// (gstack-evals #143: the outside voice marked six findings Blocks: no, and the "P0" in #1, #2 and #4 was the plan's phase name),
|
||||
// while a real P0 or CRITICAL in label position, a Severity: P0 field and Blocks: yes still collect as blocking; fails_when=a body
|
||||
// token outranks the finding's fields, or a label-position P0/CRITICAL or Blocks: yes collects below CRITICAL rank;
|
||||
// why_new=GST-976: #1, #2 and #4 collected as P0, judge refused the downgrade (JUDGMENT_REJECTED), verify stayed dirty and the
|
||||
// receipt needed accepted_by; seam=parseFindings, collect and judge; no_claim=that a body-only label on a line with no Severity field
|
||||
// is a real finding (it still collects at that label, a known phantom judge may fold; GSTA-167 S1 keeps body labels with no fields
|
||||
// because dropping them lost blockers)
|
||||
test('GST-976: a severity word in a finding body never outranks its Severity and Blocks fields; real P0 and CRITICAL still block', () => {
|
||||
const blocking = (label: string) => rank(label) === 4;
|
||||
// The #143 outside-voice reply, cut to each finding's title, its P0 sentence and its fields (gstack-evals is private).
|
||||
const reply = [
|
||||
'The problems below are about accounting and what the post leaves out. None of them breaks a test, production or custody.',
|
||||
'',
|
||||
'1. **The per-task cost leaves out model spend.** The P0 and bulk go or no-go will re-project cost from these figures, so they will come out too low. Severity: Medium. Blocks: no.',
|
||||
'',
|
||||
'2. **One round-2 K1 pair is not accounted for.** The post sets a precedent for P0 yield math, so that drop should be named. Severity: Low. Blocks: no.',
|
||||
'',
|
||||
'3. **The two probe populations are not reconciled.** The stage table probes 189 candidates. Severity: Low. Blocks: no.',
|
||||
'',
|
||||
"4. **The P0 impact of the shortfall is missing.** The plan's P0 (line 323) needs 6 K4 replays and 2 decoys. The post says the shortfall but not that it blocks P0. Severity: Low. Blocks: no.",
|
||||
'',
|
||||
'6. **Some wording is ambiguous.** Two examples:',
|
||||
' - "Repositories 4 (3 for build tasks)" is set against a target of "at least 5 repositories".',
|
||||
'',
|
||||
' Severity: Low. Blocks: no.',
|
||||
'',
|
||||
"Recommendation: ship after a one-line fix because finding 1's per-task cost leaves out measured model spend.",
|
||||
].join('\n');
|
||||
const d = tmp();
|
||||
const merged = path.join(d, 'merged.jsonl');
|
||||
const r = collect(path.join(d, 'l.jsonl'), 'outside-voice', reply, merged);
|
||||
expect(r.lines.map(l => [l.label, l.stated])).toEqual([['Medium', { severity: 'Medium', blocks: 'no' }], ...Array(4).fill(['Low', { severity: 'Low', blocks: 'no' }])]);
|
||||
expect(r.lines.filter(l => blocking(l.label)).map(l => l.id)).toEqual([]);
|
||||
const judged = path.join(d, 'judged.jsonl');
|
||||
fs.writeFileSync(judged, r.lines.map(l => JSON.stringify({ id: l.id, severity: l.stated!.severity })).join('\n') + '\n');
|
||||
expect(judge(readJsonl<MergedLine>(merged), readJsonl(judged))).toMatchObject({ ok: true, downgraded: [] });
|
||||
const real: Array<[string, string]> = [['[P0] Token leak in log.ts:3', 'P0'], ['P0: token leak in log.ts:3', 'P0'], ['1. Token leak in log.ts:3\n Severity: P0', 'P0'],
|
||||
['1. The P0 pilot drops the last row (q.ts:7). Severity: P0. Blocks: no', 'P0'], ['**Critical**\n- Token leak in log.ts:3', 'Critical'], ['## P0\n- Token leak in log.ts:3', 'P0'],
|
||||
['### CRITICAL: missing auth check in api.ts:10', 'CRITICAL'], ['1. The P0 pilot loses rows on retry (r.ts:2). Severity: Low. Blocks: yes', 'CRITICAL'],
|
||||
['1. The P0 pilot loses rows on retry (r.ts:2)\n Severity: Medium\n Blocks: yes', 'CRITICAL']];
|
||||
for (const [output, label] of real) {
|
||||
const got = parseFindings('outside-voice', output).map(f => f.label);
|
||||
expect([output, got, got.map(blocking)]).toEqual([output, [label], [true]]);
|
||||
}
|
||||
expect(parseFindings('outside-voice', '1. The P0 pilot table drops a row (t.md:3)').map(f => f.label)).toEqual(['P0']);
|
||||
// Review of this fix (gst976-p1): a locus-first or emoji-led label, an inline field the next field line follows, a second field on
|
||||
// the line, and prose that only mentions a severity never lower a blocker the reader labeled.
|
||||
const kept: Array<[string, string]> = [['1. api.ts:10 CRITICAL: missing auth. Severity: Low', 'CRITICAL'], ['1. api.ts:10 CRITICAL: missing auth\n Severity: Low', 'CRITICAL'],
|
||||
['1. 🔴 CRITICAL: missing auth. Severity: Low', 'CRITICAL'], ['1. Token leak in auth.ts:3 lets anyone read sessions. Severity: Critical\n\nSeverity: Low', 'Critical'],
|
||||
['- auth.ts:3 token leak. Severity: Low (pre-exploit), Severity: Critical (once exposed)', 'Critical'],
|
||||
['- Missing auth on DELETE /users lets any user wipe accounts (CRITICAL); the old ticket filed it at severity low.', 'CRITICAL'],
|
||||
['- api.ts:10 lets any user wipe accounts. Earlier triage said Severity: Low; it is CRITICAL.', 'CRITICAL']];
|
||||
for (const [output, label] of kept) expect([output, parseFindings('outside-voice', output).map(f => f.label)]).toEqual([output, [label]]);
|
||||
// gst976-p2: an all-caps lead before a space, comma or parenthesis; a field after a locus's parenthesis or with no colon at the
|
||||
// line's start; and a long run of marks before a label stays linear.
|
||||
const p2: Array<[string, string]> = [['1. CRITICAL missing auth. Severity: Low', 'CRITICAL'], ['1. CRITICAL, missing auth on DELETE. Severity: Low', 'CRITICAL'],
|
||||
['1. CRITICAL (api.ts:10) missing auth. Severity: Low', 'CRITICAL'], ['1. The P0 pilot table drops a row (t.md:3) Severity: Low', 'Low'],
|
||||
['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. 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', 'P0'], ['Severity: Low: only the P0 pilot table', 'Low'],
|
||||
['1. The P0 pilot drops a row. Severity: Low/P3', 'P0'], ['1. drops rows\nSeverity: High/Critical', 'Critical'],
|
||||
['Severity: Medium/CRITICAL', 'CRITICAL'], ['1. x.ts:3 drops prod rows. Severity: Low/CRITICAL', 'CRITICAL'],
|
||||
['1. x.ts:3 is CRITICAL (Severity: Low/Medium in pass 1)', 'CRITICAL'], ['1. x.ts:3 is CRITICAL (Severity: Low: pass 1)', 'CRITICAL'],
|
||||
['Still open: x.ts:3 is P0, Severity: Low: pass 1 understated it', 'P0']];
|
||||
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)])
|
||||
for (const tail of [' P0 pilot table', ' High-risk', ' CRITICAL findings follow', ' a.ts:3 P0 pilot']) parseFindings('outside-voice', run + tail + '. Severity: Low');
|
||||
parseFindings('outside-voice', '-'.repeat(16000) + ' P0 pilot table. Severity: Low');
|
||||
expect(performance.now() - t0).toBeLessThan(1000);
|
||||
});
|
||||
|
||||
// Value: protects=a non-review the same dispatch later answers with a review is voided (counted, VERIFY clean), and a failed outside
|
||||
// voice recorded with --unavailable reads UNAVAILABLE (missing coverage, never a review) without blocking, while --unavailable is
|
||||
// refused for any other reader; fails_when=one outside-voice timeout or one refusal makes VERIFY dirty for good, or --unavailable
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { parseFindings, rank } from '../lib/review-ledger';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const LABELS = 'CRITICAL|INFORMATIONAL|Critical|Informational|High|HIGH|Medium|MEDIUM|Low|LOW|P[0-3]|FIXABLE|INVESTIGATE';
|
||||
const CHAIN = `(?:${LABELS})(?:\\s*[/→]\\s*\\**\\s*(?:${LABELS}))*`;
|
||||
/** A finding's own Severity field as GST-976 defines it: at a line's start (colon optional), or after a separator with its colon and a closed clause. */
|
||||
const FIELD = new RegExp(`(?:(?:^|\\| )(?:[-*+>]\\s+|\\d+[.)]\\s+)?\\**\\s*Severity\\**\\s*:?\\s*\\**\\s*(${CHAIN})(?![A-Za-z0-9])|(?:[.;,|()]\\s*|\\s[—–-]\\s*)\\**\\s*Severity\\**\\s*:\\s*\\**\\s*(${CHAIN})(?![A-Za-z0-9])(?=\\**\\s*(?:$|[.;,|()*—–]|-\\s|Blocks)))`, 'gi');
|
||||
const LEAD = new RegExp(`(?:^|\\| )[^\\p{L}\\p{N}]*(?:\\d+[.)]\\s*[^\\p{L}\\p{N}]*)?(?:\\w[\\w./-]*:\\d+(?:[-–:,]\\s*\\d+)*\\S*\\s+[^\\p{L}\\p{N}]*)?(?:${LABELS})(?![a-z])`, 'u');
|
||||
const HEADING = new RegExp(`^(?:#{1,6}\\s*|\\*\\*)(?:${LABELS})\\b`, 'm');
|
||||
const LABEL = new RegExp(`${LABELS}`, 'gi');
|
||||
|
||||
describe('parseFindings is never weaker than main (GST-976 invariant)', () => {
|
||||
// Value: protects=no input the suite pins, no reader fixture and no Severity field shape collects below the label main (82e9d7ec)
|
||||
// collected, unless the finding's own Severity field states exactly one label, and such a finding collects at that label unless a
|
||||
// leading label, a label heading, Blocks: yes or the native voice's FIXABLE raises it; fails_when=a field chain (High/Critical) keeps
|
||||
// its weaker label (b3ddd6c) or a field whose clause runs on stops counting and a body P0 raises the finding (f3810d4);
|
||||
// why_new=three GST-976 review rounds each found a shape the row tables missed; seam=parseFindings; no_claim=that main's label was
|
||||
// right (it is the floor), or coverage of stored private reader outputs (checked outside the repo)
|
||||
test('every pinned input, reader fixture and generated field shape', () => {
|
||||
const { entries, shapes } = JSON.parse(fs.readFileSync(path.join(ROOT, 'test/fixtures/review-ledger/main-labels.json'), 'utf8'));
|
||||
const { bodies, fields: tails, joins, readers, generated } = shapes;
|
||||
for (const [b, f, j, r, main] of generated) entries.push({ reader: readers[r], output: bodies[b] + joins[j] + tails[f], main });
|
||||
const bad: string[] = [];
|
||||
let findings = 0;
|
||||
for (const e of entries as Array<{ reader: string; output?: string; fixture?: string; main: string[] }>) {
|
||||
const output = e.output ?? fs.readFileSync(path.join(ROOT, 'test/fixtures', e.fixture!), 'utf8');
|
||||
const got = parseFindings(e.reader, output);
|
||||
if (!e.main.length) continue;
|
||||
if (got.length !== e.main.length) { bad.push(`count ${got.length}/${e.main.length} ${JSON.stringify(output.slice(0, 80))}`); continue; }
|
||||
got.forEach((f, i) => {
|
||||
findings++;
|
||||
const fields = [...f.raw.matchAll(FIELD)].map(m => (m[1] ?? m[2])!);
|
||||
const single = fields.length === 1 && fields[0]!.match(LABEL)!.length === 1 ? fields[0]!.match(LABEL)![0] : undefined;
|
||||
const raised = /Blocks:?\**:?\s*\**\s*yes/i.test(f.raw) || (e.reader.startsWith('adversarial-native') && /FIXABLE/.test(f.raw)) || LEAD.test(f.raw) || HEADING.test(output);
|
||||
const ok = single === undefined ? (rank(f.label) ?? 0) >= (rank(e.main[i]) ?? 0)
|
||||
: (rank(f.label) ?? 0) >= (rank(single) ?? 0) && (raised || (rank(f.label) ?? 0) === (rank(single) ?? 0));
|
||||
if (!ok) bad.push(`${e.reader} main=${e.main[i]} got=${f.label} field=${single ?? '-'} ${JSON.stringify(f.raw.slice(0, 100))}`);
|
||||
});
|
||||
}
|
||||
expect(bad).toEqual([]);
|
||||
expect(findings).toBeGreaterThan(1000);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user