fix: preserve loop-status output on snapshot errors

This commit is contained in:
Affaan Mustafa
2026-04-30 11:47:18 -04:00
committed by Affaan Mustafa
parent 20154ddb22
commit 7627926216
2 changed files with 58 additions and 7 deletions

View File

@@ -634,13 +634,21 @@ function atomicWriteJson(filePath, payload) {
function getSnapshotPath(outputDir, session, usedNames) { function getSnapshotPath(outputDir, session, usedNames) {
const baseName = sanitizeSnapshotName(session.sessionId); const baseName = sanitizeSnapshotName(session.sessionId);
let fileName = `${baseName}.json`; const hashSuffix = hashString(session.transcriptPath || session.sessionId).slice(0, 8);
if (usedNames.has(fileName)) { let attempt = 0;
fileName = `${baseName}-${hashString(session.transcriptPath || session.sessionId).slice(0, 8)}.json`;
} while (attempt < 1000) {
const suffix = attempt === 0 ? '' : `-${hashSuffix}${attempt === 1 ? '' : `-${attempt}`}`;
const fileName = `${baseName}${suffix}.json`;
if (!usedNames.has(fileName)) {
usedNames.add(fileName); usedNames.add(fileName);
return path.join(outputDir, fileName); return path.join(outputDir, fileName);
} }
attempt += 1;
}
throw new Error(`Could not allocate a snapshot filename for session ${session.sessionId}`);
}
function writeStatusSnapshots(payload, writeDir) { function writeStatusSnapshots(payload, writeDir) {
if (!writeDir) { if (!writeDir) {
@@ -685,6 +693,19 @@ function writeStatusSnapshots(payload, writeDir) {
}; };
} }
function tryWriteStatusSnapshots(payload, options) {
if (!options.writeDir) {
return null;
}
try {
return writeStatusSnapshots(payload, options.writeDir);
} catch (error) {
console.error(`[loop-status] WARNING: could not write status snapshots: ${error.message}`);
return null;
}
}
function sleep(ms) { function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
@@ -717,7 +738,7 @@ async function runWatch(options) {
console.log(''); console.log('');
} }
const payload = buildStatus(normalizedOptions); const payload = buildStatus(normalizedOptions);
writeStatusSnapshots(payload, normalizedOptions.writeDir); tryWriteStatusSnapshots(payload, normalizedOptions);
writeStatus(payload, normalizedOptions); writeStatus(payload, normalizedOptions);
exitCode = Math.max(exitCode, getStatusExitCode(payload)); exitCode = Math.max(exitCode, getStatusExitCode(payload));
iteration += 1; iteration += 1;
@@ -748,7 +769,7 @@ async function main() {
} }
const payload = buildStatus(options); const payload = buildStatus(options);
writeStatusSnapshots(payload, options.writeDir); tryWriteStatusSnapshots(payload, options);
writeStatus(payload, options); writeStatus(payload, options);
if (options.exitCode) { if (options.exitCode) {
process.exitCode = getStatusExitCode(payload); process.exitCode = getStatusExitCode(payload);
@@ -770,5 +791,6 @@ module.exports = {
getStatusExitCode, getStatusExitCode,
parseArgs, parseArgs,
runWatch, runWatch,
tryWriteStatusSnapshots,
writeStatusSnapshots, writeStatusSnapshots,
}; };

View File

@@ -594,6 +594,35 @@ function runTests() {
} }
})) passed++; else failed++; })) passed++; else failed++;
if (test('write-dir failures do not suppress normal stdout', () => {
const homeDir = createTempHome();
try {
const blockedPath = path.join(homeDir, 'snapshot-target-is-a-file');
fs.writeFileSync(blockedPath, 'not a directory\n', 'utf8');
writeTranscript(homeDir, '-Users-affoon-project-write-error', 'session-write-error.jsonl', [
assistantMessage('2026-04-30T09:55:00.000Z', 'session-write-error', 'Loop checkpoint.'),
]);
const result = run([
'--home',
homeDir,
'--now',
NOW,
'--json',
'--write-dir',
blockedPath,
]);
assert.strictEqual(result.code, 0, result.stderr);
const payload = parsePayload(result.stdout);
assert.strictEqual(payload.schemaVersion, 'ecc.loop-status.v1');
assert.strictEqual(payload.sessions[0].sessionId, 'session-write-error');
} finally {
fs.rmSync(homeDir, { recursive: true, force: true });
}
})) passed++; else failed++;
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0); process.exit(failed > 0 ? 1 : 0);
} }