import { CheckpointRef, ProjectId, ThreadId, TurnId } from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import { describe, expect, it } from "vitest"; import { ProjectionSnapshotQuery, type ProjectionThreadCheckpointContext,} from "../../orchestration/Services/ProjectionSnapshotQuery.ts";import { checkpointRefForThreadTurn } from "../Utils.ts";import { CheckpointDiffQueryLive } from "./CheckpointDiffQuery.ts";import { CheckpointStore, type CheckpointStoreShape } from "../Services/CheckpointStore.ts";import { CheckpointDiffQuery } from "../Services/CheckpointDiffQuery.ts"; function makeThreadCheckpointContext(input: { readonly projectId: ProjectId; readonly threadId: ThreadId; readonly workspaceRoot: string; readonly worktreePath: string | null; readonly checkpointTurnCount: number; readonly checkpointRef: CheckpointRef;}): ProjectionThreadCheckpointContext { return { threadId: input.threadId, projectId: input.projectId, workspaceRoot: input.workspaceRoot, worktreePath: input.worktreePath, checkpoints: [ { turnId: TurnId.make("turn-1"), checkpointTurnCount: input.checkpointTurnCount, checkpointRef: input.checkpointRef, status: "ready", files: [], assistantMessageId: null, completedAt: "2026-01-01T00:00:00.000Z", }, ], };} describe("CheckpointDiffQueryLive", () => { it("uses the narrow full-thread context lookup for all-turns diffs", async () => { const projectId = ProjectId.make("project-full-thread"); const threadId = ThreadId.make("thread-full-thread"); const toCheckpointRef = checkpointRefForThreadTurn(threadId, 4); let getThreadCheckpointContextCalls = 0; let getFullThreadDiffContextCalls = 0; const diffCheckpointsCalls: Array<{ readonly fromCheckpointRef: CheckpointRef; readonly toCheckpointRef: CheckpointRef; readonly cwd: string; readonly ignoreWhitespace: boolean; }> = []; const checkpointStore: CheckpointStoreShape = { isGitRepository: () => Effect.succeed(true), captureCheckpoint: () => Effect.void, hasCheckpointRef: () => Effect.succeed(true), restoreCheckpoint: () => Effect.succeed(true), diffCheckpoints: ({ fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace }) => Effect.sync(() => { diffCheckpointsCalls.push({ fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace, }); return "full thread diff patch"; }), deleteCheckpointRefs: () => Effect.void, }; const layer = CheckpointDiffQueryLive.pipe( Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)), Layer.provideMerge( Layer.succeed(ProjectionSnapshotQuery, { getCommandReadModel: () => Effect.die("CheckpointDiffQuery should not request the command read model"), getSnapshot: () => Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"), getShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"), getArchivedShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request archived shell snapshots"), getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), getProjectShellById: () => Effect.succeed(Option.none()), getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), getThreadCheckpointContext: () => Effect.sync(() => { getThreadCheckpointContextCalls += 1; return Option.none(); }), getFullThreadDiffContext: () => Effect.sync(() => { getFullThreadDiffContextCalls += 1; return Option.some({ threadId, projectId, workspaceRoot: "/tmp/workspace", worktreePath: "/tmp/worktree", latestCheckpointTurnCount: 4, toCheckpointRef, }); }), getThreadShellById: () => Effect.succeed(Option.none()), getThreadDetailById: () => Effect.succeed(Option.none()), }), ), ); const result = await Effect.runPromise( Effect.gen(function* () { const query = yield* CheckpointDiffQuery; return yield* query.getFullThreadDiff({ threadId, toTurnCount: 4, ignoreWhitespace: true, }); }).pipe(Effect.provide(layer)), ); expect(getThreadCheckpointContextCalls).toBe(0); expect(getFullThreadDiffContextCalls).toBe(1); expect(diffCheckpointsCalls).toEqual([ { cwd: "/tmp/worktree", fromCheckpointRef: checkpointRefForThreadTurn(threadId, 0), toCheckpointRef, ignoreWhitespace: true, }, ]); expect(result).toEqual({ threadId, fromTurnCount: 0, toTurnCount: 4, diff: "full thread diff patch", }); }); it("computes diffs using canonical turn-0 checkpoint refs", async () => { const projectId = ProjectId.make("project-1"); const threadId = ThreadId.make("thread-1"); const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1); const diffCheckpointsCalls: Array<{ readonly fromCheckpointRef: CheckpointRef; readonly toCheckpointRef: CheckpointRef; readonly cwd: string; readonly ignoreWhitespace: boolean; }> = []; const threadCheckpointContext = makeThreadCheckpointContext({ projectId, threadId, workspaceRoot: "/tmp/workspace", worktreePath: null, checkpointTurnCount: 1, checkpointRef: toCheckpointRef, }); const checkpointStore: CheckpointStoreShape = { isGitRepository: () => Effect.succeed(true), captureCheckpoint: () => Effect.void, hasCheckpointRef: () => Effect.succeed(true), restoreCheckpoint: () => Effect.succeed(true), diffCheckpoints: ({ fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace }) => Effect.sync(() => { diffCheckpointsCalls.push({ fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace, }); return "diff patch"; }), deleteCheckpointRefs: () => Effect.void, }; const layer = CheckpointDiffQueryLive.pipe( Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)), Layer.provideMerge( Layer.succeed(ProjectionSnapshotQuery, { getCommandReadModel: () => Effect.die("CheckpointDiffQuery should not request the command read model"), getSnapshot: () => Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"), getShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"), getArchivedShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request archived shell snapshots"), getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), getProjectShellById: () => Effect.succeed(Option.none()), getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), getFullThreadDiffContext: () => Effect.die("unused"), getThreadShellById: () => Effect.succeed(Option.none()), getThreadDetailById: () => Effect.succeed(Option.none()), }), ), ); const result = await Effect.runPromise( Effect.gen(function* () { const query = yield* CheckpointDiffQuery; return yield* query.getTurnDiff({ threadId, fromTurnCount: 0, toTurnCount: 1, ignoreWhitespace: true, }); }).pipe(Effect.provide(layer)), ); const expectedFromRef = checkpointRefForThreadTurn(threadId, 0); expect(diffCheckpointsCalls).toEqual([ { cwd: "/tmp/workspace", fromCheckpointRef: expectedFromRef, toCheckpointRef, ignoreWhitespace: true, }, ]); expect(result).toEqual({ threadId, fromTurnCount: 0, toTurnCount: 1, diff: "diff patch", }); }); it("defaults to hide whitespace changes", async () => { const projectId = ProjectId.make("project-default-whitespace"); const threadId = ThreadId.make("thread-default-whitespace"); const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1); const diffCheckpointsCalls: Array<{ readonly ignoreWhitespace: boolean }> = []; const threadCheckpointContext = makeThreadCheckpointContext({ projectId, threadId, workspaceRoot: "/tmp/workspace", worktreePath: null, checkpointTurnCount: 1, checkpointRef: toCheckpointRef, }); const checkpointStore: CheckpointStoreShape = { isGitRepository: () => Effect.succeed(true), captureCheckpoint: () => Effect.void, hasCheckpointRef: () => Effect.succeed(true), restoreCheckpoint: () => Effect.succeed(true), diffCheckpoints: ({ ignoreWhitespace }) => Effect.sync(() => { diffCheckpointsCalls.push({ ignoreWhitespace }); return "diff patch"; }), deleteCheckpointRefs: () => Effect.void, }; const layer = CheckpointDiffQueryLive.pipe( Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)), Layer.provideMerge( Layer.succeed(ProjectionSnapshotQuery, { getCommandReadModel: () => Effect.die("CheckpointDiffQuery should not request the command read model"), getSnapshot: () => Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"), getShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"), getArchivedShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request archived shell snapshots"), getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), getProjectShellById: () => Effect.succeed(Option.none()), getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), getFullThreadDiffContext: () => Effect.die("unused"), getThreadShellById: () => Effect.succeed(Option.none()), getThreadDetailById: () => Effect.succeed(Option.none()), }), ), ); await Effect.runPromise( Effect.gen(function* () { const query = yield* CheckpointDiffQuery; return yield* query.getTurnDiff({ threadId, fromTurnCount: 0, toTurnCount: 1, }); }).pipe(Effect.provide(layer)), ); expect(diffCheckpointsCalls).toEqual([{ ignoreWhitespace: true }]); }); it("does not preflight checkpoint refs before diffing", async () => { const projectId = ProjectId.make("project-no-preflight"); const threadId = ThreadId.make("thread-no-preflight"); const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1); let hasCheckpointRefCallCount = 0; const threadCheckpointContext = makeThreadCheckpointContext({ projectId, threadId, workspaceRoot: "/tmp/workspace", worktreePath: null, checkpointTurnCount: 1, checkpointRef: toCheckpointRef, }); const checkpointStore: CheckpointStoreShape = { isGitRepository: () => Effect.succeed(true), captureCheckpoint: () => Effect.void, hasCheckpointRef: () => Effect.sync(() => { hasCheckpointRefCallCount += 1; return true; }), restoreCheckpoint: () => Effect.succeed(true), diffCheckpoints: () => Effect.succeed("diff patch"), deleteCheckpointRefs: () => Effect.void, }; const layer = CheckpointDiffQueryLive.pipe( Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)), Layer.provideMerge( Layer.succeed(ProjectionSnapshotQuery, { getCommandReadModel: () => Effect.die("CheckpointDiffQuery should not request the command read model"), getSnapshot: () => Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"), getShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"), getArchivedShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request archived shell snapshots"), getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), getProjectShellById: () => Effect.succeed(Option.none()), getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), getFullThreadDiffContext: () => Effect.die("unused"), getThreadShellById: () => Effect.succeed(Option.none()), getThreadDetailById: () => Effect.succeed(Option.none()), }), ), ); await Effect.runPromise( Effect.gen(function* () { const query = yield* CheckpointDiffQuery; return yield* query.getTurnDiff({ threadId, fromTurnCount: 0, toTurnCount: 1, ignoreWhitespace: true, }); }).pipe(Effect.provide(layer)), ); expect(hasCheckpointRefCallCount).toBe(0); }); it("fails when the thread is missing from the snapshot", async () => { const threadId = ThreadId.make("thread-missing"); const checkpointStore: CheckpointStoreShape = { isGitRepository: () => Effect.succeed(true), captureCheckpoint: () => Effect.void, hasCheckpointRef: () => Effect.succeed(true), restoreCheckpoint: () => Effect.succeed(true), diffCheckpoints: () => Effect.succeed(""), deleteCheckpointRefs: () => Effect.void, }; const layer = CheckpointDiffQueryLive.pipe( Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)), Layer.provideMerge( Layer.succeed(ProjectionSnapshotQuery, { getCommandReadModel: () => Effect.die("CheckpointDiffQuery should not request the command read model"), getSnapshot: () => Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"), getShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"), getArchivedShellSnapshot: () => Effect.die("CheckpointDiffQuery should not request archived shell snapshots"), getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), getProjectShellById: () => Effect.succeed(Option.none()), getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), getThreadCheckpointContext: () => Effect.succeed(Option.none()), getFullThreadDiffContext: () => Effect.succeed(Option.none()), getThreadShellById: () => Effect.succeed(Option.none()), getThreadDetailById: () => Effect.succeed(Option.none()), }), ), ); await expect( Effect.runPromise( Effect.gen(function* () { const query = yield* CheckpointDiffQuery; return yield* query.getTurnDiff({ threadId, fromTurnCount: 0, toTurnCount: 1, }); }).pipe(Effect.provide(layer)), ), ).rejects.toThrow("Thread 'thread-missing' not found."); });});
import { CheckpointRef, ProjectId, ThreadId, TurnId } from "@t3tools/contracts";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import { describe, expect, it } from "vitest";
import {
ProjectionSnapshotQuery,
type ProjectionThreadCheckpointContext,
} from "../../orchestration/Services/ProjectionSnapshotQuery.ts";
import { checkpointRefForThreadTurn } from "../Utils.ts";
import { CheckpointDiffQueryLive } from "./CheckpointDiffQuery.ts";
import { CheckpointStore, type CheckpointStoreShape } from "../Services/CheckpointStore.ts";
import { CheckpointDiffQuery } from "../Services/CheckpointDiffQuery.ts";
function makeThreadCheckpointContext(input: {
readonly projectId: ProjectId;
readonly threadId: ThreadId;
readonly workspaceRoot: string;
readonly worktreePath: string | null;
readonly checkpointTurnCount: number;
readonly checkpointRef: CheckpointRef;
}): ProjectionThreadCheckpointContext {
return {
threadId: input.threadId,
projectId: input.projectId,
workspaceRoot: input.workspaceRoot,
worktreePath: input.worktreePath,
checkpoints: [
{
turnId: TurnId.make("turn-1"),
checkpointTurnCount: input.checkpointTurnCount,
checkpointRef: input.checkpointRef,
status: "ready",
files: [],
assistantMessageId: null,
completedAt: "2026-01-01T00:00:00.000Z",
},
],
};
}
describe("CheckpointDiffQueryLive", () => {
it("uses the narrow full-thread context lookup for all-turns diffs", async () => {
const projectId = ProjectId.make("project-full-thread");
const threadId = ThreadId.make("thread-full-thread");
const toCheckpointRef = checkpointRefForThreadTurn(threadId, 4);
let getThreadCheckpointContextCalls = 0;
let getFullThreadDiffContextCalls = 0;
const diffCheckpointsCalls: Array<{
readonly fromCheckpointRef: CheckpointRef;
readonly toCheckpointRef: CheckpointRef;
readonly cwd: string;
readonly ignoreWhitespace: boolean;
}> = [];
const checkpointStore: CheckpointStoreShape = {
isGitRepository: () => Effect.succeed(true),
captureCheckpoint: () => Effect.void,
hasCheckpointRef: () => Effect.succeed(true),
restoreCheckpoint: () => Effect.succeed(true),
diffCheckpoints: ({ fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace }) =>
Effect.sync(() => {
diffCheckpointsCalls.push({
fromCheckpointRef,
toCheckpointRef,
cwd,
ignoreWhitespace,
});
return "full thread diff patch";
}),
deleteCheckpointRefs: () => Effect.void,
};
const layer = CheckpointDiffQueryLive.pipe(
Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)),
Layer.provideMerge(
Layer.succeed(ProjectionSnapshotQuery, {
getCommandReadModel: () =>
Effect.die("CheckpointDiffQuery should not request the command read model"),
getSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"),
getShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"),
getArchivedShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request archived shell snapshots"),
getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }),
getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }),
getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()),
getProjectShellById: () => Effect.succeed(Option.none()),
getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()),
getThreadCheckpointContext: () =>
Effect.sync(() => {
getThreadCheckpointContextCalls += 1;
return Option.none();
}),
getFullThreadDiffContext: () =>
Effect.sync(() => {
getFullThreadDiffContextCalls += 1;
return Option.some({
threadId,
projectId,
workspaceRoot: "/tmp/workspace",
worktreePath: "/tmp/worktree",
latestCheckpointTurnCount: 4,
toCheckpointRef,
});
}),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
}),
),
);
const result = await Effect.runPromise(
Effect.gen(function* () {
const query = yield* CheckpointDiffQuery;
return yield* query.getFullThreadDiff({
threadId,
toTurnCount: 4,
ignoreWhitespace: true,
});
}).pipe(Effect.provide(layer)),
);
expect(getThreadCheckpointContextCalls).toBe(0);
expect(getFullThreadDiffContextCalls).toBe(1);
expect(diffCheckpointsCalls).toEqual([
{
cwd: "/tmp/worktree",
fromCheckpointRef: checkpointRefForThreadTurn(threadId, 0),
toCheckpointRef,
ignoreWhitespace: true,
},
]);
expect(result).toEqual({
threadId,
fromTurnCount: 0,
toTurnCount: 4,
diff: "full thread diff patch",
});
});
it("computes diffs using canonical turn-0 checkpoint refs", async () => {
const projectId = ProjectId.make("project-1");
const threadId = ThreadId.make("thread-1");
const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);
const diffCheckpointsCalls: Array<{
readonly fromCheckpointRef: CheckpointRef;
readonly toCheckpointRef: CheckpointRef;
readonly cwd: string;
readonly ignoreWhitespace: boolean;
}> = [];
const threadCheckpointContext = makeThreadCheckpointContext({
projectId,
threadId,
workspaceRoot: "/tmp/workspace",
worktreePath: null,
checkpointTurnCount: 1,
checkpointRef: toCheckpointRef,
});
const checkpointStore: CheckpointStoreShape = {
isGitRepository: () => Effect.succeed(true),
captureCheckpoint: () => Effect.void,
hasCheckpointRef: () => Effect.succeed(true),
restoreCheckpoint: () => Effect.succeed(true),
diffCheckpoints: ({ fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace }) =>
Effect.sync(() => {
diffCheckpointsCalls.push({
fromCheckpointRef,
toCheckpointRef,
cwd,
ignoreWhitespace,
});
return "diff patch";
}),
deleteCheckpointRefs: () => Effect.void,
};
const layer = CheckpointDiffQueryLive.pipe(
Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)),
Layer.provideMerge(
Layer.succeed(ProjectionSnapshotQuery, {
getCommandReadModel: () =>
Effect.die("CheckpointDiffQuery should not request the command read model"),
getSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"),
getShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"),
getArchivedShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request archived shell snapshots"),
getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }),
getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }),
getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()),
getProjectShellById: () => Effect.succeed(Option.none()),
getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()),
getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)),
getFullThreadDiffContext: () => Effect.die("unused"),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
}),
),
);
const result = await Effect.runPromise(
Effect.gen(function* () {
const query = yield* CheckpointDiffQuery;
return yield* query.getTurnDiff({
threadId,
fromTurnCount: 0,
toTurnCount: 1,
ignoreWhitespace: true,
});
}).pipe(Effect.provide(layer)),
);
const expectedFromRef = checkpointRefForThreadTurn(threadId, 0);
expect(diffCheckpointsCalls).toEqual([
{
cwd: "/tmp/workspace",
fromCheckpointRef: expectedFromRef,
toCheckpointRef,
ignoreWhitespace: true,
},
]);
expect(result).toEqual({
threadId,
fromTurnCount: 0,
toTurnCount: 1,
diff: "diff patch",
});
});
it("defaults to hide whitespace changes", async () => {
const projectId = ProjectId.make("project-default-whitespace");
const threadId = ThreadId.make("thread-default-whitespace");
const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);
const diffCheckpointsCalls: Array<{ readonly ignoreWhitespace: boolean }> = [];
const threadCheckpointContext = makeThreadCheckpointContext({
projectId,
threadId,
workspaceRoot: "/tmp/workspace",
worktreePath: null,
checkpointTurnCount: 1,
checkpointRef: toCheckpointRef,
});
const checkpointStore: CheckpointStoreShape = {
isGitRepository: () => Effect.succeed(true),
captureCheckpoint: () => Effect.void,
hasCheckpointRef: () => Effect.succeed(true),
restoreCheckpoint: () => Effect.succeed(true),
diffCheckpoints: ({ ignoreWhitespace }) =>
Effect.sync(() => {
diffCheckpointsCalls.push({ ignoreWhitespace });
return "diff patch";
}),
deleteCheckpointRefs: () => Effect.void,
};
const layer = CheckpointDiffQueryLive.pipe(
Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)),
Layer.provideMerge(
Layer.succeed(ProjectionSnapshotQuery, {
getCommandReadModel: () =>
Effect.die("CheckpointDiffQuery should not request the command read model"),
getSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"),
getShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"),
getArchivedShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request archived shell snapshots"),
getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }),
getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }),
getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()),
getProjectShellById: () => Effect.succeed(Option.none()),
getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()),
getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)),
getFullThreadDiffContext: () => Effect.die("unused"),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
}),
),
);
await Effect.runPromise(
Effect.gen(function* () {
const query = yield* CheckpointDiffQuery;
return yield* query.getTurnDiff({
threadId,
fromTurnCount: 0,
toTurnCount: 1,
});
}).pipe(Effect.provide(layer)),
);
expect(diffCheckpointsCalls).toEqual([{ ignoreWhitespace: true }]);
});
it("does not preflight checkpoint refs before diffing", async () => {
const projectId = ProjectId.make("project-no-preflight");
const threadId = ThreadId.make("thread-no-preflight");
const toCheckpointRef = checkpointRefForThreadTurn(threadId, 1);
let hasCheckpointRefCallCount = 0;
const threadCheckpointContext = makeThreadCheckpointContext({
projectId,
threadId,
workspaceRoot: "/tmp/workspace",
worktreePath: null,
checkpointTurnCount: 1,
checkpointRef: toCheckpointRef,
});
const checkpointStore: CheckpointStoreShape = {
isGitRepository: () => Effect.succeed(true),
captureCheckpoint: () => Effect.void,
hasCheckpointRef: () =>
Effect.sync(() => {
hasCheckpointRefCallCount += 1;
return true;
}),
restoreCheckpoint: () => Effect.succeed(true),
diffCheckpoints: () => Effect.succeed("diff patch"),
deleteCheckpointRefs: () => Effect.void,
};
const layer = CheckpointDiffQueryLive.pipe(
Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)),
Layer.provideMerge(
Layer.succeed(ProjectionSnapshotQuery, {
getCommandReadModel: () =>
Effect.die("CheckpointDiffQuery should not request the command read model"),
getSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"),
getShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"),
getArchivedShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request archived shell snapshots"),
getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }),
getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }),
getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()),
getProjectShellById: () => Effect.succeed(Option.none()),
getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()),
getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)),
getFullThreadDiffContext: () => Effect.die("unused"),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
}),
),
);
await Effect.runPromise(
Effect.gen(function* () {
const query = yield* CheckpointDiffQuery;
return yield* query.getTurnDiff({
threadId,
fromTurnCount: 0,
toTurnCount: 1,
ignoreWhitespace: true,
});
}).pipe(Effect.provide(layer)),
);
expect(hasCheckpointRefCallCount).toBe(0);
});
it("fails when the thread is missing from the snapshot", async () => {
const threadId = ThreadId.make("thread-missing");
const checkpointStore: CheckpointStoreShape = {
isGitRepository: () => Effect.succeed(true),
captureCheckpoint: () => Effect.void,
hasCheckpointRef: () => Effect.succeed(true),
restoreCheckpoint: () => Effect.succeed(true),
diffCheckpoints: () => Effect.succeed(""),
deleteCheckpointRefs: () => Effect.void,
};
const layer = CheckpointDiffQueryLive.pipe(
Layer.provideMerge(Layer.succeed(CheckpointStore, checkpointStore)),
Layer.provideMerge(
Layer.succeed(ProjectionSnapshotQuery, {
getCommandReadModel: () =>
Effect.die("CheckpointDiffQuery should not request the command read model"),
getSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the full orchestration snapshot"),
getShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request the orchestration shell snapshot"),
getArchivedShellSnapshot: () =>
Effect.die("CheckpointDiffQuery should not request archived shell snapshots"),
getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }),
getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }),
getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()),
getProjectShellById: () => Effect.succeed(Option.none()),
getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()),
getThreadCheckpointContext: () => Effect.succeed(Option.none()),
getFullThreadDiffContext: () => Effect.succeed(Option.none()),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
}),
),
);
await expect(
Effect.runPromise(
Effect.gen(function* () {
const query = yield* CheckpointDiffQuery;
return yield* query.getTurnDiff({
threadId,
fromTurnCount: 0,
toTurnCount: 1,
});
}).pipe(Effect.provide(layer)),
),
).rejects.toThrow("Thread 'thread-missing' not found.");
});
});