| 1 | import * as NodeCrypto from "@effect/platform-node/NodeCrypto"; |
| 2 | import { assert, it } from "@effect/vitest"; |
| 3 | import * as DateTime from "effect/DateTime"; |
| 4 | import * as Effect from "effect/Effect"; |
| 5 | import * as Layer from "effect/Layer"; |
| 6 | import { |
| 7 | CheckpointId, |
| 8 | EventId, |
| 9 | MessageId, |
| 10 | NodeId, |
| 11 | ProjectId, |
| 12 | ProviderInstanceId, |
| 13 | ProviderThreadId, |
| 14 | RunId, |
| 15 | ThreadId, |
| 16 | } from "@t3tools/contracts"; |
| 17 | import * as CheckpointDiffQuery from "../checkpointing/CheckpointDiffQuery.ts"; |
| 18 | import * as CheckpointStore from "../checkpointing/CheckpointStore.ts"; |
| 19 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 20 | import { checkpointRefForScopeOrdinal } from "./CheckpointService.ts"; |
| 21 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 22 | import * as ThreadManagement from "./ThreadManagementService.ts"; |
| 23 | import * as SqlitePersistence from "../persistence/Sqlite.ts"; |
| 24 | const layerProjection = Layer.mergeAll( |
| 25 | ProjectionStore.layer.pipe(Layer.provideMerge(SqlitePersistence.layerMemory)), |
| 26 | SqlitePersistence.layerMemory, |
| 27 | IdAllocator.layer, |
| 28 | NodeCrypto.layer, |
| 29 | ); |
| 30 | it.effect("resolves the thread baseline after a second root run replaces scope ownership", () => |
| 31 | Effect.gen(function* () { |
| 32 | const ids = yield* IdAllocator.IdAllocatorV2; |
| 33 | const projections = yield* ProjectionStore.ProjectionStoreV2; |
| 34 | const now = DateTime.makeUnsafe("2026-09-08T12:00:00.000Z"); |
| 35 | const threadId = ThreadId.make("thread:audit-root-scope"); |
| 36 | const firstRunId = RunId.make("run:audit-root-scope:1"); |
| 37 | const secondRunId = RunId.make("run:audit-root-scope:2"); |
| 38 | const firstNodeId = NodeId.make("node:audit-root-scope:1"); |
| 39 | const secondNodeId = NodeId.make("node:audit-root-scope:2"); |
| 40 | const providerThreadId = ProviderThreadId.make("provider-thread:audit-root-scope"); |
| 41 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 42 | const scopeId = yield* ids.allocate.checkpointScope({ threadId, name: "root" }); |
| 43 | const checkpointRefs = [ |
| 44 | yield* checkpointRefForScopeOrdinal({ scopeId, ordinalWithinScope: 0 }), |
| 45 | yield* checkpointRefForScopeOrdinal({ scopeId, ordinalWithinScope: 1 }), |
| 46 | yield* checkpointRefForScopeOrdinal({ scopeId, ordinalWithinScope: 2 }), |
| 47 | ]; |
| 48 | const baselineId = CheckpointId.make("checkpoint:audit-root-scope:0"); |
| 49 | const firstCheckpointId = CheckpointId.make("checkpoint:audit-root-scope:1"); |
| 50 | const secondCheckpointId = CheckpointId.make("checkpoint:audit-root-scope:2"); |
| 51 | const modelSelection = { instanceId: providerInstanceId, model: "gpt-5.4" } as const; |
| 52 | |
| 53 | yield* projections.apply({ |
| 54 | id: EventId.make("event:audit-root-scope:thread"), |
| 55 | type: "thread.created", |
| 56 | threadId, |
| 57 | providerInstanceId, |
| 58 | occurredAt: now, |
| 59 | payload: { |
| 60 | createdBy: "user", |
| 61 | creationSource: "web", |
| 62 | id: threadId, |
| 63 | projectId: ProjectId.make("project:audit-root-scope"), |
| 64 | title: "Checkpoint scope probe", |
| 65 | providerInstanceId, |
| 66 | modelSelection, |
| 67 | runtimeMode: "full-access", |
| 68 | interactionMode: "default", |
| 69 | branch: null, |
| 70 | worktreePath: null, |
| 71 | activeProviderThreadId: providerThreadId, |
| 72 | lineage: { parentThreadId: null, relationshipToParent: null, rootThreadId: threadId }, |
| 73 | forkedFrom: null, |
| 74 | createdAt: now, |
| 75 | updatedAt: now, |
| 76 | archivedAt: null, |
| 77 | settledOverride: null, |
| 78 | settledAt: null, |
| 79 | lastVisitedAt: null, |
| 80 | deletedAt: null, |
| 81 | }, |
| 82 | }); |
| 83 | |
| 84 | const applyRun = (input: { |
| 85 | readonly runId: RunId; |
| 86 | readonly nodeId: NodeId; |
| 87 | readonly ordinal: number; |
| 88 | readonly checkpointId: CheckpointId; |
| 89 | }) => |
| 90 | projections.apply({ |
| 91 | id: EventId.make(`event:audit-root-scope:run:${input.ordinal}`), |
| 92 | type: "run.updated", |
| 93 | threadId, |
| 94 | runId: input.runId, |
| 95 | nodeId: input.nodeId, |
| 96 | providerInstanceId, |
| 97 | occurredAt: now, |
| 98 | payload: { |
| 99 | id: input.runId, |
| 100 | threadId, |
| 101 | ordinal: input.ordinal, |
| 102 | providerInstanceId, |
| 103 | modelSelection, |
| 104 | providerThreadId, |
| 105 | userMessageId: MessageId.make(`message:audit-root-scope:${input.ordinal}`), |
| 106 | rootNodeId: input.nodeId, |
| 107 | activeAttemptId: null, |
| 108 | status: "completed" as const, |
| 109 | requestedAt: now, |
| 110 | startedAt: now, |
| 111 | completedAt: now, |
| 112 | checkpointId: input.checkpointId, |
| 113 | contextHandoffId: null, |
| 114 | }, |
| 115 | }); |
| 116 | const applyScope = (runId: RunId, nodeId: NodeId, ordinal: number) => |
| 117 | projections.apply({ |
| 118 | id: EventId.make(`event:audit-root-scope:scope:${ordinal}`), |
| 119 | type: "checkpoint-scope.created", |
| 120 | threadId, |
| 121 | runId, |
| 122 | nodeId, |
| 123 | providerInstanceId, |
| 124 | occurredAt: now, |
| 125 | payload: { |
| 126 | id: scopeId, |
| 127 | threadId, |
| 128 | runId, |
| 129 | nodeId, |
| 130 | parentScopeId: null, |
| 131 | providerThreadId, |
| 132 | kind: "root_run" as const, |
| 133 | ordinalWithinParent: 0, |
| 134 | advancesAppRunCount: true, |
| 135 | cwd: ordinal === 1 ? "/repo" : "/prepared-repo", |
| 136 | createdAt: now, |
| 137 | }, |
| 138 | }); |
| 139 | const applyCheckpoint = (input: { |
| 140 | readonly id: CheckpointId; |
| 141 | readonly runId: RunId | null; |
| 142 | readonly nodeId: NodeId; |
| 143 | readonly ordinal: number; |
| 144 | readonly appRunOrdinal: number | null; |
| 145 | }) => |
| 146 | projections.apply({ |
| 147 | id: EventId.make(`event:audit-root-scope:checkpoint:${input.ordinal}`), |
| 148 | type: "checkpoint.captured", |
| 149 | threadId, |
| 150 | ...(input.runId === null ? {} : { runId: input.runId }), |
| 151 | nodeId: input.nodeId, |
| 152 | providerInstanceId, |
| 153 | occurredAt: now, |
| 154 | payload: { |
| 155 | id: input.id, |
| 156 | scopeId, |
| 157 | threadId, |
| 158 | runId: input.runId, |
| 159 | nodeId: input.nodeId, |
| 160 | parentCheckpointId: |
| 161 | input.ordinal === 0 ? null : input.ordinal === 1 ? baselineId : firstCheckpointId, |
| 162 | ordinalWithinScope: input.ordinal, |
| 163 | appRunOrdinal: input.appRunOrdinal, |
| 164 | ref: checkpointRefs[input.ordinal]!, |
| 165 | status: "ready" as const, |
| 166 | files: [], |
| 167 | capturedAt: now, |
| 168 | }, |
| 169 | }); |
| 170 | |
| 171 | yield* applyRun({ |
| 172 | runId: firstRunId, |
| 173 | nodeId: firstNodeId, |
| 174 | ordinal: 1, |
| 175 | checkpointId: firstCheckpointId, |
| 176 | }); |
| 177 | yield* applyScope(firstRunId, firstNodeId, 1); |
| 178 | yield* applyCheckpoint({ |
| 179 | id: baselineId, |
| 180 | runId: null, |
| 181 | nodeId: firstNodeId, |
| 182 | ordinal: 0, |
| 183 | appRunOrdinal: null, |
| 184 | }); |
| 185 | yield* applyCheckpoint({ |
| 186 | id: firstCheckpointId, |
| 187 | runId: firstRunId, |
| 188 | nodeId: firstNodeId, |
| 189 | ordinal: 1, |
| 190 | appRunOrdinal: 1, |
| 191 | }); |
| 192 | yield* applyRun({ |
| 193 | runId: secondRunId, |
| 194 | nodeId: secondNodeId, |
| 195 | ordinal: 2, |
| 196 | checkpointId: secondCheckpointId, |
| 197 | }); |
| 198 | yield* applyScope(secondRunId, secondNodeId, 2); |
| 199 | yield* applyCheckpoint({ |
| 200 | id: secondCheckpointId, |
| 201 | runId: secondRunId, |
| 202 | nodeId: secondNodeId, |
| 203 | ordinal: 2, |
| 204 | appRunOrdinal: 2, |
| 205 | }); |
| 206 | |
| 207 | const context = yield* projections.getCheckpointContext(threadId); |
| 208 | assert.deepEqual( |
| 209 | context.checkpoints.map((checkpoint) => checkpoint.appRunOrdinal), |
| 210 | [null, 1, 2], |
| 211 | ); |
| 212 | assert.equal(context.checkpointScopes.length, 1); |
| 213 | assert.equal(context.checkpointScopes[0]?.runId, secondRunId); |
| 214 | |
| 215 | const layerQuery = CheckpointDiffQuery.layer.pipe( |
| 216 | Layer.provide( |
| 217 | Layer.mergeAll( |
| 218 | Layer.mock(ThreadManagement.ThreadManagementService)({ |
| 219 | getCheckpointContext: () => Effect.succeed(context), |
| 220 | }), |
| 221 | Layer.mock(CheckpointStore.CheckpointStore)({ |
| 222 | listAuthoredPaths: () => Effect.succeed(null), |
| 223 | diffCheckpoints: (input) => { |
| 224 | assert.equal(input.cwd, "/prepared-repo"); |
| 225 | assert.equal(input.fromCheckpointRef, checkpointRefs[0]); |
| 226 | assert.equal(input.toCheckpointRef, checkpointRefs[2]); |
| 227 | return Effect.succeed("two-run diff"); |
| 228 | }, |
| 229 | }), |
| 230 | ), |
| 231 | ), |
| 232 | ); |
| 233 | const result = yield* Effect.gen(function* () { |
| 234 | const query = yield* CheckpointDiffQuery.CheckpointDiffQuery; |
| 235 | return yield* query.getFullThreadDiff({ threadId, toTurnCount: 2 }); |
| 236 | }).pipe(Effect.provide(layerQuery)); |
| 237 | assert.equal(result.diff, "two-run diff"); |
| 238 | }).pipe(Effect.provide(layerProjection)), |
| 239 | ); |