fix(session): repaired torn JSONL appends

Rolled partial file appends back to their pre-write size and marked malformed resumed sessions for an atomic rewrite.

Retried transient persistence failures from in-memory state and surfaced the first failure in the interactive TUI.

Fixes #8596
This commit is contained in:
roboomp
2026-08-15 00:33:48 +00:00
parent ffd53ff92a
commit 9ae089d71b
10 changed files with 233 additions and 75 deletions
@@ -92,6 +92,7 @@ describe("loadEntriesFromFileStream (Bun.JSONL parity)", () => {
expect(titleSlot?.title).toBe("Visitor");
expect(entryIds(visited)).toEqual(["s1", "m1", "m2"]);
expect((await sessionLoader.loadEntriesFromFileStream(file)).malformedRecords).toBe(1);
});
it("does not revisit entries before a malformed line spanning stream chunks", async () => {
const content = [
@@ -167,6 +168,7 @@ describe("loadEntriesFromFileStream (Bun.JSONL parity)", () => {
expect(entryTypes(stream.entries)).toEqual(["session", "message", "message"]);
const ids = messageIds(stream.entries);
expect(ids).toEqual(["m1", "m2"]); // valid entries kept in order, malformed skipped
expect(stream.malformedRecords).toBe(1);
});
it("matches parseSessionContent when there is no title slot (header is the first line)", async () => {
@@ -217,5 +219,6 @@ describe("loadEntriesFromFileStream (Bun.JSONL parity)", () => {
const stream = await sessionLoader.loadEntriesFromFileStream(missing);
expect(stream.entries).toEqual([]);
expect(stream.titleSlot).toBeUndefined();
expect(stream.malformedRecords).toBe(0);
});
});