fix(pregate): literals checks only the text after a history row's closing bracket; restore digest fixtures

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-31 05:46:18 +00:00
1 parent bcb1d7e05a
commit a0edd745a7
4 files changed
+11 -10

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
**pregate `literals` no longer flags a version-history table that the branch extended.** A test that keeps one row per past version of a constant (`[45, "<digest>"]`, then `[46, "<digest>"]`) used to fail `literals` on every bump, because the old row still holds the old value. Truther turned the check down to a warning over it, which also hid real stale tests. **pregate `literals` no longer flags a version-history table that the branch extended.** A test that keeps one row per past version of a constant (`[45, "<digest>"]`, then `[46, "<digest>"]`) used to fail `literals` on every bump, because the old row still holds the old value. Truther turned the check down to a warning over it, which also hid real stale tests.
### Fixed ### Fixed
- `lib/pregate/checks/literals.ts`: a row that starts with the old value is skipped when the same table holds a new-value row the branch added (the file at the merge base lacks it). Only the row's first field is exempt, and the scan continues to the next line holding the old value, so `expect(PROOF_CHECKS).toBe(45)` below an extended table still fails. A table the branch did not extend, a file that already held a new-value row at the merge base (read through a rename git detects), and a merge base that cannot be read still fail. - `lib/pregate/checks/literals.ts`: a row that starts with the old value is skipped when the same table holds a new-value row the branch added (the file at the merge base lacks it). Only the row itself is exempt (the old value after its closing bracket on the same line still fails), and the scan continues to the next line holding the old value, so `expect(PROOF_CHECKS).toBe(45)` below an extended table still fails. A table the branch did not extend, a file that already held a new-value row at the merge base (read through a rename git detects), and a merge base that cannot be read still fail.
- docs/pregate.md describes the history-row rule and the table shapes it recognizes. - docs/pregate.md describes the history-row rule and the table shapes it recognizes.
### Tests ### Tests
+2 -1
View File
@@ -175,7 +175,8 @@ Two stages with separate clocks, both printed:
the branch added (the file at the merge base, read through a rename git the branch added (the file at the merge base, read through a rename git
detects, has no new-value row). A table the branch did not extend, a file that already held a detects, has no new-value row). A table the branch did not extend, a file that already held a
new-value row at the merge base, a merge base that cannot be read, and every new-value row at the merge base, a merge base that cannot be read, and every
other line holding the old value still fail. Rows in object form (`{ v: 45 }`) or spread over several other line holding the old value still fail. Only the row itself is exempt:
the old value after its closing bracket on the same line still fails. Rows in object form (`{ v: 45 }`) or spread over several
lines are not recognized as history rows. lines are not recognized as history rows.
- `timing`: added or changed test lines that compare an elapsed wall-clock or - `timing`: added or changed test lines that compare an elapsed wall-clock or
+1 -1
View File
@@ -103,7 +103,7 @@ export function consumersOf(repoRoot: string, testFiles: readonly string[], chan
let extended: boolean | undefined; let extended: boolean | undefined;
const skip = (i: number) => { const skip = (i: number) => {
const line = lines[i]!; const line = lines[i]!;
if (!oldRowRe.test(line) || !tableHasNew[table[i]!] || valueRe.test(line.replace(oldRowRe, '['))) return false; if (!oldRowRe.test(line) || !tableHasNew[table[i]!] || valueRe.test(line.slice(line.indexOf(']') + 1))) return false;
if (extended === undefined) { const base = baseText?.(rel); extended = base === null || (base !== undefined && !newRowRe.test(base)); } if (extended === undefined) { const base = baseText?.(rel); extended = base === null || (base !== undefined && !newRowRe.test(base)); }
return extended; return extended;
}; };
+7 -7
View File
@@ -227,7 +227,7 @@ describe('literals (pure)', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-literals-history-')); const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-literals-history-'));
const nl = String.fromCharCode(10); const nl = String.fromCharCode(10);
const head = "import { PROOF_CHECKS } from '../src/proof-checks.ts';"; const head = "import { PROOF_CHECKS } from '../src/proof-checks.ts';";
const rows = (...ns: number[]) => ns.map(n => ` [${n}, "sha${n}"],`); const rows = (...ns: number[]) => ns.map(n => ` [${n}, "digest-${n}"],`);
const files: Record<string, [string[], string[] | null]> = { const files: Record<string, [string[], string[] | null]> = {
'test/history.test.ts': [[head, 'const versions = [', ...rows(44, 45, 46), '];', 'expect(PROOF_CHECKS).toBe(46);'], [head, 'const versions = [', ...rows(44, 45), '];', 'expect(PROOF_CHECKS).toBe(45);']], 'test/history.test.ts': [[head, 'const versions = [', ...rows(44, 45, 46), '];', 'expect(PROOF_CHECKS).toBe(46);'], [head, 'const versions = [', ...rows(44, 45), '];', 'expect(PROOF_CHECKS).toBe(45);']],
'test/newest-first.test.ts': [[head, 'const versions = [', ...rows(46, 45, 44), '];'], [head, 'const versions = [', ...rows(45, 44), '];']], 'test/newest-first.test.ts': [[head, 'const versions = [', ...rows(46, 45, 44), '];'], [head, 'const versions = [', ...rows(45, 44), '];']],
@@ -236,17 +236,17 @@ describe('literals (pure)', () => {
'test/two-tables.test.ts': [[head, 'const a = [', ...rows(44, 45), '];', 'const b = [', ...rows(45, 46), '];'], [head, 'const a = [', ...rows(44, 45), '];', 'const b = [', ...rows(45), '];']], 'test/two-tables.test.ts': [[head, 'const a = [', ...rows(44, 45), '];', 'const b = [', ...rows(45, 46), '];'], [head, 'const a = [', ...rows(44, 45), '];', 'const b = [', ...rows(45), '];']],
'test/boundary.test.ts': [[head, 'test.each([', ...rows(45, 46), '])("%d", () => {});'], [head, 'test.each([', ...rows(45, 46), '])("%d", () => {});']], 'test/boundary.test.ts': [[head, 'test.each([', ...rows(45, 46), '])("%d", () => {});'], [head, 'test.each([', ...rows(45, 46), '])("%d", () => {});']],
'test/new-file.test.ts': [[head, 'const versions = [', ...rows(45, 46), '];'], null], 'test/new-file.test.ts': [[head, 'const versions = [', ...rows(45, 46), '];'], null],
'test/same-line.test.ts': [[head, 'const versions = [', ' [45, "sha45"], expect(PROOF_CHECKS).toBe(45),', ' [46, "sha46"],', '];'], [head, 'const versions = [', ' [45, "sha45"], expect(PROOF_CHECKS).toBe(45),', '];']], 'test/same-line.test.ts': [[head, 'const versions = [', ' [45, "digest-45"], expect(PROOF_CHECKS).toBe(45),', ' [46, "digest-46"],', '];'], [head, 'const versions = [', ' [45, "digest-45"], expect(PROOF_CHECKS).toBe(45),', '];']],
}; };
for (const [rel, [now]] of Object.entries(files)) { fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true }); fs.writeFileSync(path.join(root, rel), now.join(nl) + nl); } for (const [rel, [now]] of Object.entries(files)) { fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true }); fs.writeFileSync(path.join(root, rel), now.join(nl) + nl); }
const baseText = (rel: string) => files[rel]?.[1]?.join(nl) ?? null; const baseText = (rel: string) => files[rel]?.[1]?.join(nl) ?? null;
const lineOf = (rel: string, text: string) => `${rel}:${files[rel]![0].indexOf(text) + 1}`; const lineOf = (rel: string, text: string) => `${rel}:${files[rel]![0].indexOf(text) + 1}`;
expect(consumersOf(root, Object.keys(files), { name: 'PROOF_CHECKS', old: '45', new: '46', file: 'src/proof-checks.ts' }, baseText).sort()).toEqual([ expect(consumersOf(root, Object.keys(files), { name: 'PROOF_CHECKS', old: '45', new: '46', file: 'src/proof-checks.ts' }, baseText).sort()).toEqual([
lineOf('test/boundary.test.ts', ' [45, "sha45"],'), lineOf('test/boundary.test.ts', ' [45, "digest-45"],'),
lineOf('test/same-line.test.ts', ' [45, "sha45"], expect(PROOF_CHECKS).toBe(45),'), lineOf('test/same-line.test.ts', ' [45, "digest-45"], expect(PROOF_CHECKS).toBe(45),'),
lineOf('test/stale-later.test.ts', 'expect(PROOF_CHECKS).toBe(45);'), lineOf('test/stale-later.test.ts', 'expect(PROOF_CHECKS).toBe(45);'),
lineOf('test/two-tables.test.ts', ' [45, "sha45"],'), lineOf('test/two-tables.test.ts', ' [45, "digest-45"],'),
lineOf('test/unextended.test.ts', ' [45, "sha45"],'), lineOf('test/unextended.test.ts', ' [45, "digest-45"],'),
].sort()); ].sort());
fs.writeFileSync(path.join(root, 'test/versions.test.ts'), ["import { SCHEMA } from '../src/schema.ts';", 'const history = [', " ['v3', 'a'],", ' ["v4", \'b\'],', '];', ''].join(nl)); fs.writeFileSync(path.join(root, 'test/versions.test.ts'), ["import { SCHEMA } from '../src/schema.ts';", 'const history = [', " ['v3', 'a'],", ' ["v4", \'b\'],', '];', ''].join(nl));
const strings = (rel: string) => rel === 'test/versions.test.ts' ? ["import { SCHEMA } from '../src/schema.ts';", 'const history = [', " ['v3', 'a'],", '];'].join(nl) : null; const strings = (rel: string) => rel === 'test/versions.test.ts' ? ["import { SCHEMA } from '../src/schema.ts';", 'const history = [', " ['v3', 'a'],", '];'].join(nl) : null;
@@ -406,7 +406,7 @@ describe('gstack-pregate (bin)', () => {
}); });
test('GST-344: a history table the branch extends passes literals through the merge-base read; one it does not extend fails', () => { test('GST-344: a history table the branch extends passes literals through the merge-base read; one it does not extend fails', () => {
const history = (...ns: number[]) => ["const { MAX_RETRIES } = require('../lib/retry.js');", 'const versions = [', ...ns.map(n => ` [${n}, 'sha${n}'],`), '];', ''].join('\n'); const history = (...ns: number[]) => ["const { MAX_RETRIES } = require('../lib/retry.js');", 'const versions = [', ...ns.map(n => ` [${n}, 'digest-${n}'],`), '];', ''].join('\n');
const repo = fixture({ baseFiles: { 'test/history.test.js': history(2, 3) } }); const repo = fixture({ baseFiles: { 'test/history.test.js': history(2, 3) } });
const bump = { 'lib/retry.js': "exports.MAX_RETRIES = 4;\nexports.TIMEOUT_MS = 5000;\nexports.retry = (fn) => fn();\n", 'test/retry.test.js': "const test = require('node:test');\nconst assert = require('node:assert');\nconst { MAX_RETRIES } = require('../lib/retry.js');\ntest('retries four times', () => assert.strictEqual(MAX_RETRIES, 4));\n" }; const bump = { 'lib/retry.js': "exports.MAX_RETRIES = 4;\nexports.TIMEOUT_MS = 5000;\nexports.retry = (fn) => fn();\n", 'test/retry.test.js': "const test = require('node:test');\nconst assert = require('node:assert');\nconst { MAX_RETRIES } = require('../lib/retry.js');\ntest('retries four times', () => assert.strictEqual(MAX_RETRIES, 4));\n" };
commit(repo, bump); commit(repo, bump);