import { describe, expect, it, vi } from "vitest"; import { createSessionWorkflowJournal, type SessionEntryLike, } from "../journal.ts"; import type { WorkflowSpec } from "../spec.ts"; const spec = (name = "wf"): WorkflowSpec => ({ name, phases: [{ id: "p", kind: "single", step: { summary: "hi", prompt: "hi" } }], }); describe("createSessionWorkflowJournal", () => { function sessionStore() { const entries: SessionEntryLike[] = []; const appendEntry = vi.fn((customType: string, data: unknown) => { entries.push({ type: "custom", customType, data }); }); const open = ( cwd = "/work", workflow = "wf", args: Record = { x: 1 }, ) => createSessionWorkflowJournal({ cwd, spec: spec(workflow), args, entries, appendEntry, }); return { appendEntry, entries, open }; } it("persists unfinished entries in Pi session custom entries", async () => { const store = sessionStore(); const first = store.open(); await first.set("step-a", { ok: true, value: "cached" }); expect(store.appendEntry).toHaveBeenCalledWith( "ultra-journal", expect.objectContaining({ kind: "step", key: "step-a" }), ); expect(await store.open().get("step-a")).toEqual({ ok: true, value: "cached", }); }); it("persists child identity beside the stable workflow and step IDs", () => { const store = sessionStore(); const first = store.open(); const session = { id: "child-id", path: "/sessions/child.jsonl" }; first.setSession("step-a", session); expect(store.open().id).toBe(first.id); expect(store.open().getSession("step-a")).toEqual(session); expect(store.appendEntry).toHaveBeenCalledWith( "ultra-journal", expect.objectContaining({ id: first.id, kind: "session", key: "step-a", session, }), ); }); it("starts fresh after the session journal is completed", async () => { const store = sessionStore(); const journal = store.open(); await journal.set("step-a", { ok: true, value: "cached" }); journal.setSession("step-a", { id: "child-id", path: "/sessions/child.jsonl", }); await journal.complete?.(); expect(store.appendEntry).toHaveBeenLastCalledWith( "ultra-journal", expect.objectContaining({ kind: "complete" }), ); expect(await store.open().get("step-a")).toBeUndefined(); expect(store.open().getSession("step-a")).toBeUndefined(); }); it("separates session journals by workflow name, cwd, and args", async () => { const store = sessionStore(); await store.open().set("step", { ok: true, value: "a" }); expect( await store.open("/work", "wf", { x: 2 }).get("step"), ).toBeUndefined(); expect(await store.open("/other").get("step")).toBeUndefined(); expect(await store.open("/work", "other").get("step")).toBeUndefined(); }); it("does not reuse results or child sessions after effective model routing changes", async () => { const store = sessionStore(); const open = (model: string, thinkingLevel: string) => createSessionWorkflowJournal({ cwd: "/work", spec: spec(), entries: store.entries, appendEntry: store.appendEntry, modelRouting: [{ model, thinkingLevel }], }); const original = open("openai/first", "low"); await original.set("step", { ok: true, value: "old result" }); original.setSession("step", { id: "old-child", path: "/sessions/old.jsonl", }); expect(await open("openai/first", "low").get("step")).toEqual({ ok: true, value: "old result", }); for (const changed of [ open("openai/second", "low"), open("openai/first", "high"), ]) { expect(changed.id).not.toBe(original.id); expect(await changed.get("step")).toBeUndefined(); expect(changed.getSession("step")).toBeUndefined(); } }); it("ignores unrelated and malformed session entries", async () => { const store = sessionStore(); store.entries.push( { type: "custom", customType: "other", data: { kind: "step" } }, { type: "custom", customType: "ultra-journal", data: null }, ); expect(await store.open().get("step")).toBeUndefined(); }); });