From 841beea45cb25ba51f29fa45b7e272938d19b80a Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Thu, 30 Apr 2026 12:14:45 -0400 Subject: [PATCH] fix: handle dotted reserved snapshot names --- scripts/loop-status.js | 7 ++++++- tests/scripts/loop-status.test.js | 19 +++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/scripts/loop-status.js b/scripts/loop-status.js index 6c7128b1..16c0d698 100644 --- a/scripts/loop-status.js +++ b/scripts/loop-status.js @@ -626,7 +626,12 @@ function sanitizeSnapshotName(value, fallback = 'session') { return sanitized; } if (sanitized && isWindowsReservedBasename(sanitized)) { - return `${sanitized}-${hashString(raw).slice(0, 8)}`; + const firstDotIndex = sanitized.indexOf('.'); + const hashSuffix = hashString(raw).slice(0, 8); + if (firstDotIndex === -1) { + return `${sanitized}-${hashSuffix}`; + } + return `${sanitized.slice(0, firstDotIndex)}-${hashSuffix}${sanitized.slice(firstDotIndex)}`; } const prefix = sanitized ? sanitized.slice(0, 48).replace(/[._-]+$/g, '') : fallback; diff --git a/tests/scripts/loop-status.test.js b/tests/scripts/loop-status.test.js index a3da4b89..63cb1c6a 100644 --- a/tests/scripts/loop-status.test.js +++ b/tests/scripts/loop-status.test.js @@ -642,6 +642,9 @@ function runTests() { writeTranscript(homeDir, '-Users-affoon-project-windows-name', 'con.jsonl', [ assistantMessage('2026-04-30T09:55:00.000Z', 'con', 'Loop checkpoint.'), ]); + writeTranscript(homeDir, '-Users-affoon-project-windows-name', 'con-txt.jsonl', [ + assistantMessage('2026-04-30T09:56:00.000Z', 'con.txt', 'Loop checkpoint.'), + ]); const result = run([ '--home', @@ -657,13 +660,17 @@ function runTests() { const indexPath = path.join(snapshotDir, 'index.json'); const indexPayload = JSON.parse(fs.readFileSync(indexPath, 'utf8')); - const snapshotName = path.basename(indexPayload.sessions[0].snapshotPath); - assert.strictEqual(indexPayload.sessions[0].sessionId, 'con'); - assert.notStrictEqual(snapshotName.toLowerCase(), 'con.json'); + assert.strictEqual(indexPayload.sessions.length, 2); - const snapshotPayload = JSON.parse(fs.readFileSync(indexPayload.sessions[0].snapshotPath, 'utf8')); - assert.strictEqual(snapshotPayload.schemaVersion, 'ecc.loop-status.session.v1'); - assert.strictEqual(snapshotPayload.session.sessionId, 'con'); + for (const sessionIndex of indexPayload.sessions) { + const snapshotName = path.basename(sessionIndex.snapshotPath); + assert.notStrictEqual(snapshotName.toLowerCase(), `${sessionIndex.sessionId}.json`); + assert.ok(!/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(snapshotName.split('.')[0])); + + const snapshotPayload = JSON.parse(fs.readFileSync(sessionIndex.snapshotPath, 'utf8')); + assert.strictEqual(snapshotPayload.schemaVersion, 'ecc.loop-status.session.v1'); + assert.strictEqual(snapshotPayload.session.sessionId, sessionIndex.sessionId); + } } finally { fs.rmSync(homeDir, { recursive: true, force: true }); fs.rmSync(snapshotDir, { recursive: true, force: true });