Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions apps/mobile/src/state/queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,32 @@ describe("appQueries", () => {
});
});

it("routes a single-turn first range through the turn diff query", () => {
const environmentId = EnvironmentId.make("environment-a");
const threadId = ThreadId.make("thread-a");

expect(
buildCheckpointDiffTargets({
environmentId,
threadId,
fromTurnCount: 0,
toTurnCount: 1,
ignoreWhitespace: false,
}),
).toEqual({
fullThread: null,
turn: {
environmentId,
input: {
threadId,
fromTurnCount: 0,
toTurnCount: 1,
ignoreWhitespace: false,
},
},
});
});

it("routes later ranges through the incremental turn diff query", () => {
const environmentId = EnvironmentId.make("environment-a");
const threadId = ThreadId.make("thread-a");
Expand Down
51 changes: 4 additions & 47 deletions apps/mobile/src/state/queryTargets.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,8 @@
import type { EnvironmentId, ThreadId } from "@t3tools/contracts";

export interface CheckpointDiffTarget {
readonly environmentId: EnvironmentId | null;
readonly threadId: ThreadId | null;
readonly fromTurnCount: number | null;
readonly toTurnCount: number | null;
readonly ignoreWhitespace: boolean;
}
export {
buildCheckpointDiffTargets,
type CheckpointDiffTarget,
} from "@t3tools/client-runtime/state/threads";

export function normalizeComposerPathSearchQuery(query: string | null): string {
return query?.trim() ?? "";
}

export function buildCheckpointDiffTargets(target: CheckpointDiffTarget) {
if (
target.environmentId === null ||
target.threadId === null ||
target.fromTurnCount === null ||
target.toTurnCount === null
) {
return { fullThread: null, turn: null } as const;
}

if (target.fromTurnCount === 0) {
return {
fullThread: {
environmentId: target.environmentId,
input: {
threadId: target.threadId,
toTurnCount: target.toTurnCount,
ignoreWhitespace: target.ignoreWhitespace,
},
},
turn: null,
} as const;
}

return {
fullThread: null,
turn: {
environmentId: target.environmentId,
input: {
threadId: target.threadId,
fromTurnCount: target.fromTurnCount,
toTurnCount: target.toTurnCount,
ignoreWhitespace: target.ignoreWhitespace,
},
},
} as const;
}
136 changes: 135 additions & 1 deletion apps/server/src/checkpointing/CheckpointDiffQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { OrchestratorProjectionError } from "../orchestration-v2/Orchestrator.ts
import type { ProjectionCheckpointContext } from "../orchestration-v2/ProjectionStore.ts";
import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts";
import * as CheckpointDiffQuery from "./CheckpointDiffQuery.ts";
import { checkpointStartRef } from "./Utils.ts";
import * as CheckpointStore from "./CheckpointStore.ts";
import {
CheckpointRefUnavailableError,
Expand Down Expand Up @@ -46,6 +47,7 @@ function makeProjection(): ProjectionCheckpointContext {

function layerFor(input: {
readonly projection: Effect.Effect<ProjectionCheckpointContext, OrchestratorProjectionError>;
readonly hasStartSnapshot?: boolean;
readonly diffCheckpoints?: CheckpointStore.CheckpointStore["Service"]["diffCheckpoints"];
}) {
return CheckpointDiffQuery.layer.pipe(
Expand All @@ -55,6 +57,7 @@ function layerFor(input: {
getCheckpointContext: () => input.projection,
}),
Layer.mock(CheckpointStore.CheckpointStore)({
hasCheckpointRef: () => Effect.succeed(input.hasStartSnapshot ?? false),
diffCheckpoints: input.diffCheckpoints ?? (() => Effect.succeed("diff")),
}),
),
Expand All @@ -66,7 +69,11 @@ it.effect("computes V2 run diffs from projected checkpoint scopes", () => {
const diffCheckpoints = vi.fn((_input: CheckpointStore.DiffCheckpointsInput) =>
Effect.succeed("diff --git a/file b/file"),
);
const layer = layerFor({ projection: Effect.succeed(makeProjection()), diffCheckpoints });
const layer = layerFor({
projection: Effect.succeed(makeProjection()),
diffCheckpoints,
hasStartSnapshot: true,
});

return Effect.gen(function* () {
const query = yield* CheckpointDiffQuery.CheckpointDiffQuery;
Expand All @@ -91,6 +98,44 @@ it.effect("computes V2 run diffs from projected checkpoint scopes", () => {
}).pipe(Effect.provide(layer));
});

it.effect("diffs a single-turn thread from the root baseline, not the turn-start ref", () => {
const diffCheckpoints = vi.fn((_input: CheckpointStore.DiffCheckpointsInput) =>
Effect.succeed("patch"),
);
const firstRef = checkpointRefForScopeOrdinal({
scopeId: firstScopeId,
ordinalWithinScope: 1,
});
const layer = layerFor({
hasStartSnapshot: true,
diffCheckpoints,
projection: Effect.succeed({
runs: [{ id: firstRunId, ordinal: 1, status: "completed" }],
checkpointScopes: [{ id: firstScopeId, runId: firstRunId, kind: "root_run", cwd: "/repo" }],
checkpoints: [
{
scopeId: firstScopeId,
runId: firstRunId,
appRunOrdinal: 1,
status: "ready",
ref: firstRef,
},
],
}),
});

return Effect.gen(function* () {
const query = yield* CheckpointDiffQuery.CheckpointDiffQuery;
const result = yield* query.getFullThreadDiff({ threadId, toTurnCount: 1 });

assert.equal(result.diff, "patch");
assert.equal(
diffCheckpoints.mock.calls[0]?.[0].fromCheckpointRef,
checkpointRefForScopeOrdinal({ scopeId: firstScopeId, ordinalWithinScope: 0 }),
);
}).pipe(Effect.provide(layer));
});

it.effect("preserves the typed missing-thread error contract", () => {
const layer = layerFor({
projection: Effect.fail(new OrchestratorProjectionError({ threadId })),
Expand Down Expand Up @@ -193,3 +238,92 @@ it.effect("preserves the typed missing-baseline-ref error contract", () => {
);
}).pipe(Effect.provide(layer));
});

it.effect("diffs a single turn from its start ref when the prior turn left no checkpoint", () => {
const projection = makeProjection();
const diffCheckpoints = vi.fn((_input: CheckpointStore.DiffCheckpointsInput) =>
Effect.succeed("patch"),
);
const layer = layerFor({
hasStartSnapshot: true,
diffCheckpoints,
projection: Effect.succeed({
...projection,
runs: projection.runs.map((run) =>
run.id === firstRunId ? { ...run, status: "interrupted" as const } : run,
),
}),
});
return Effect.gen(function* () {
const query = yield* CheckpointDiffQuery.CheckpointDiffQuery;
const result = yield* query.getTurnDiff({ threadId, fromTurnCount: 1, toTurnCount: 2 });
assert.equal(result.diff, "patch");
assert.equal(
diffCheckpoints.mock.calls[0]?.[0].fromCheckpointRef,
checkpointStartRef(secondRef),
);
}).pipe(Effect.provide(layer));
});

it.effect("still requires the prior checkpoint when no start ref exists", () => {
const projection = makeProjection();
const layer = layerFor({
hasStartSnapshot: false,
projection: Effect.succeed({
...projection,
runs: projection.runs.map((run) =>
run.id === firstRunId ? { ...run, status: "interrupted" as const } : run,
),
}),
});
return Effect.gen(function* () {
const query = yield* CheckpointDiffQuery.CheckpointDiffQuery;
const error = yield* query
.getTurnDiff({ threadId, fromTurnCount: 1, toTurnCount: 2 })
.pipe(Effect.flip);
assert.instanceOf(error, CheckpointRefUnavailableError);
assert.deepEqual(
{ checkpoint: error.checkpoint, turnCount: error.turnCount },
{ checkpoint: "from", turnCount: 1 },
);
}).pipe(Effect.provide(layer));
});

it.effect.each([false, true])(
"uses the fresh baseline for an individual V2 turn when it has a start snapshot: %s",
(hasStartSnapshot) => {
const projection = makeProjection();
const firstRef = checkpointRefForScopeOrdinal({
scopeId: firstScopeId,
ordinalWithinScope: 1,
});
const diffCheckpoints = vi.fn((_input: CheckpointStore.DiffCheckpointsInput) =>
Effect.succeed("patch"),
);
const layer = layerFor({
hasStartSnapshot,
diffCheckpoints,
projection: Effect.succeed({
...projection,
checkpoints: [
...projection.checkpoints,
{
scopeId: firstScopeId,
runId: firstRunId,
appRunOrdinal: 1,
status: "ready",
ref: firstRef,
},
],
}),
});
return Effect.gen(function* () {
const query = yield* CheckpointDiffQuery.CheckpointDiffQuery;
yield* query.getTurnDiff({ threadId, fromTurnCount: 1, toTurnCount: 2 });
assert.equal(
diffCheckpoints.mock.calls[0]?.[0].fromCheckpointRef,
hasStartSnapshot ? checkpointStartRef(secondRef) : firstRef,
);
}).pipe(Effect.provide(layer));
},
);
Loading
Loading