From e6c13866a8197ff06231ab5526081963432cacfd Mon Sep 17 00:00:00 2001 From: garrytan Date: Sun, 18 Oct 2026 00:17:22 +0000 Subject: [PATCH] test(qa-evidence): pin the post-repair edit's mtime to capture 002's completion The GPT voice's /review run on 51f1e02 failed qa-evidence.test.ts:547 once: capture 003 reported no required probe remaining, so capture 002 (started before the edit) counted as post-edit. The test wrote the edit right after 002 returned, a gap of one child run (7-27 ms measured here; the voice's machine ran this test in 59 ms against 183 ms here, so a few ms there), while an ext4 mtime comes from the kernel's coarse clock and lags Date.now() by up to a tick (0.5-4.5 ms measured here). The edit's mtime is now set to 002's recorded completedAt, so 002 started strictly before the boundary and 003 starts after it on any machine. The product boundary is unchanged: tick-scale imprecision only matters when a capture starts within milliseconds before an edit, and a stricter margin would refuse a real edit && capture chain. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- test/qa-evidence.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/qa-evidence.test.ts b/test/qa-evidence.test.ts index 3707751d8..04097731d 100644 --- a/test/qa-evidence.test.ts +++ b/test/qa-evidence.test.ts @@ -535,15 +535,23 @@ test('post-repair required probes count only captures started after the last fix const [duplicate, partial] = [probe('console.log(JSON.stringify({scenario:"duplicate"}))'), probe('console.log(JSON.stringify({scenario:"partial"}))')]; const env = { ...process.env, GSTACK_QA_REQUIRED_PROBES: JSON.stringify([duplicate.join(' '), partial.join(' ')]), GSTACK_QA_REQUIRED_AFTER_EDIT: JSON.stringify({ paths: [src], since: new Date(Date.now() - 30_000).toISOString() }) }; + let last: Record = {}; const capture = (captureId: string, argv: string[]) => { const after = captureId === '001' ? [] : ['--after', String(Number(captureId) - 1).padStart(3, '0'), '--hypothesis', 'The previous scenario result shows which invariant the next probe must check.']; const result = spawnSync(process.execPath, [CLI, 'capture', f.root, captureId, '--timeout-ms', '4000', ...after, '--', ...argv], { cwd: f.root, encoding: 'utf8', timeout: 10_000, env }); expect(result.status, result.stderr).toBe(0); - return receipt(result.stdout).requiredRemaining as string[]; + last = receipt(result.stdout); + return last.requiredRemaining as string[]; }; expect(capture('001', duplicate)).toEqual([duplicate.join(' '), partial.join(' ')]); expect(capture('002', partial)).toEqual([duplicate.join(' '), partial.join(' ')]); + // The edit's mtime is pinned to capture 002's completion: a real write's mtime comes from the kernel's coarse clock, + // which lags Date.now() by up to a tick, while 002 started only one child run (a few ms on a fast machine) earlier; + // the GPT voice's run on 51f1e02 counted 002 as post-edit that way. 002 started before this instant and 003 after it. + const edit = new Date(Date.parse(String(last.completedAt))); + expect(Date.parse(String(last.startedAt))).toBeLessThan(edit.getTime()); fs.writeFileSync(path.join(src, 'worker.ts'), 'export const effect = 2;\n'); + fs.utimesSync(path.join(src, 'worker.ts'), edit, edit); expect(capture('003', duplicate)).toEqual([partial.join(' ')]); expect(capture('004', partial)).toEqual([]); const secondEdit = new Date(Date.now() + 60_000);