From f27fda0f4531561164c5596e7fd55440aadf9e15 Mon Sep 17 00:00:00 2001 From: Randalix Date: Fri, 23 Oct 2026 17:06:45 +0200 Subject: [PATCH] test(edit-history): the live conversation wins even when older; tighter wiring guard Review round 3 (all low): - the "written last" test left solo newer than conv-1 for the rest of the file, so the live test passed by mtime alone. solo's mtime is reset in a finally, and the live test now runs with the live transcript older than the stored one (a /resume into an older conversation before it writes). - the server wiring guard matches the remote branch of the ternary as one expression, so swapped branches fail it. - note on whole-second `-nt` (BusyBox, bash 3.2) at the lookup. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/remote-files.ts | 2 ++ test/edit-history-sync.test.ts | 2 +- test/remote-edit-sources.test.ts | 23 +++++++++++++++-------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/remote-files.ts b/src/remote-files.ts index 14ba227b..aadad078 100644 --- a/src/remote-files.ts +++ b/src/remote-files.ts @@ -542,6 +542,8 @@ export function buildRemoteClaudeTranscriptLocateCommand(ids: readonly string[], 'best=', ...claudeConfigDirLines(), // newest : `best` becomes that id's transcript if it is newer than the current best. + // BusyBox's (and bash 3.2's) `-nt` compares whole seconds: a tie keeps the earlier id, the + // stored conversation, until the next write; only without a live id. 'newest() {', ' for f in "$cfg"/projects/*/"$1".jsonl; do', ' [ -f "$f" ] || continue', diff --git a/test/edit-history-sync.test.ts b/test/edit-history-sync.test.ts index 38c4f19a..d1755e1a 100644 --- a/test/edit-history-sync.test.ts +++ b/test/edit-history-sync.test.ts @@ -739,7 +739,7 @@ describe('reads only on request (wiring guard)', () => { const start = server.indexOf('new EditHistorySync({'); const lookup = server.slice(start, server.indexOf('readSources:', start)); expect(lookup).toMatch( - /remoteLocateClaudeTranscript\(\s*session\.remote,\s*remoteTranscriptIds\(session, storedTranscriptId\)/ + /session\.remote\s*\?\s*remoteLocateClaudeTranscript\(\s*session\.remote,\s*remoteTranscriptIds\(session, storedTranscriptId\)/ ); expect(lookup).toMatch(/resolveClaudeTranscript\(\s*this\.transcriptWatchers\.get\(session\.id\)\?\.getPath\(\)/); }); diff --git a/test/remote-edit-sources.test.ts b/test/remote-edit-sources.test.ts index 62fa5621..753140d2 100644 --- a/test/remote-edit-sources.test.ts +++ b/test/remote-edit-sources.test.ts @@ -108,13 +108,18 @@ describe('the transcript lookup on a real shell', () => { it('takes the most recently written transcript of all ids, not the first id that has one', () => { // conv-1 lies in old/ (1000 s) and new/ (2000 s); solo is older than both, then newest. const solo = join(projects(), 'old', 'solo.jsonl'); - utimesSync(solo, 1_500, 1_500); - expect(locate(['conv-1', 'solo'])).toBe(join(projects(), 'new', 'conv-1.jsonl')); - expect(locate(['solo', 'conv-1'])).toBe(join(projects(), 'new', 'conv-1.jsonl')); - expect(locate(['missing', 'solo'])).toBe(solo); - // A respawn resumed the launch conversation after a /clear: the one written last wins. - utimesSync(solo, 3_000, 3_000); - expect(locate(['conv-1', 'solo'])).toBe(solo); + try { + utimesSync(solo, 1_500, 1_500); + expect(locate(['conv-1', 'solo'])).toBe(join(projects(), 'new', 'conv-1.jsonl')); + expect(locate(['solo', 'conv-1'])).toBe(join(projects(), 'new', 'conv-1.jsonl')); + expect(locate(['missing', 'solo'])).toBe(solo); + // A respawn resumed the launch conversation after a /clear: the one written last wins. + utimesSync(solo, 3_000, 3_000); + expect(locate(['conv-1', 'solo'])).toBe(solo); + } finally { + // Older than conv-1, so the live tests below cannot pass by mtime alone. + utimesSync(solo, 1_500, 1_500); + } }); it('exits 3 when no id has a transcript, and drops ids that are not plain tokens', () => { @@ -166,7 +171,9 @@ describe('the transcript lookup on a real shell', () => { const locateIn = (p: typeof pane | undefined) => locate(['conv-1'], p, { PATH: `${bin}:${process.env.PATH}` }); const session = (body: string) => writeFileSync(join(home, '.claude', 'sessions', '300.json'), body); - it('finds the live conversation of a grandchild claude before the stored ids', () => { + it('finds the live conversation of a grandchild claude before the stored ids, even an older one', () => { + // solo (1500 s) is older than conv-1 (2000 s): a /resume into an older conversation + // that has not written yet still wins. session('{"pid":300,"sessionId":"solo","cwd":"/w","kind":"interactive"}'); expect(locateIn(pane)).toBe(join(projects(), 'old', 'solo.jsonl')); });