| ... | @@ -2186,6 +2186,99 @@ it.layer(layerTest)("ProjectionStoreV2", (it) => { | ... | @@ -2186,6 +2186,99 @@ it.layer(layerTest)("ProjectionStoreV2", (it) => { |
| 2186 | }), | 2186 | }), |
| 2187 | ); | 2187 | ); |
| 2188 | | 2188 | |
| | 2189 | it.effect("dates thread activity by a run event and leaves the rest of the thread alone", () => |
| | 2190 | Effect.gen(function* () { |
| | 2191 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; |
| | 2192 | const createdAt = yield* DateTime.now; |
| | 2193 | const runOccurredAt = DateTime.add(createdAt, { seconds: 5 }); |
| | 2194 | const threadId = ThreadId.make("thread:projection-activity"); |
| | 2195 | const runId = RunId.make("run:projection-activity"); |
| | 2196 | const rootNodeId = NodeId.make("node:projection-activity"); |
| | 2197 | const thread = { |
| | 2198 | createdBy: "user" as const, |
| | 2199 | creationSource: "web" as const, |
| | 2200 | id: threadId, |
| | 2201 | projectId: ProjectId.make("project:projection-activity"), |
| | 2202 | title: "Projection activity", |
| | 2203 | providerInstanceId, |
| | 2204 | modelSelection, |
| | 2205 | runtimeMode: "full-access" as const, |
| | 2206 | interactionMode: "default" as const, |
| | 2207 | branch: "feature/activity", |
| | 2208 | worktreePath: null, |
| | 2209 | activeProviderThreadId: null, |
| | 2210 | lineage: { |
| | 2211 | parentThreadId: null, |
| | 2212 | relationshipToParent: null, |
| | 2213 | rootThreadId: threadId, |
| | 2214 | }, |
| | 2215 | forkedFrom: null, |
| | 2216 | createdAt, |
| | 2217 | updatedAt: createdAt, |
| | 2218 | archivedAt: null, |
| | 2219 | settledOverride: null, |
| | 2220 | settledAt: null, |
| | 2221 | lastVisitedAt: createdAt, |
| | 2222 | deletedAt: null, |
| | 2223 | }; |
| | 2224 | |
| | 2225 | yield* projectionStore.apply({ |
| | 2226 | id: EventId.make("event:projection-activity:created"), |
| | 2227 | type: "thread.created", |
| | 2228 | threadId, |
| | 2229 | occurredAt: createdAt, |
| | 2230 | payload: thread, |
| | 2231 | }); |
| | 2232 | const created = (yield* projectionStore.getThreadProjection(threadId)).thread; |
| | 2233 | |
| | 2234 | const { statements, tracer } = traceSqlStatements(); |
| | 2235 | yield* projectionStore |
| | 2236 | .apply({ |
| | 2237 | id: EventId.make("event:projection-activity:run-created"), |
| | 2238 | type: "run.created", |
| | 2239 | threadId, |
| | 2240 | runId, |
| | 2241 | nodeId: rootNodeId, |
| | 2242 | driver, |
| | 2243 | providerInstanceId, |
| | 2244 | occurredAt: runOccurredAt, |
| | 2245 | payload: { |
| | 2246 | id: runId, |
| | 2247 | threadId, |
| | 2248 | ordinal: 1, |
| | 2249 | providerInstanceId, |
| | 2250 | modelSelection, |
| | 2251 | providerThreadId: null, |
| | 2252 | userMessageId: MessageId.make("message:projection-activity"), |
| | 2253 | rootNodeId, |
| | 2254 | activeAttemptId: null, |
| | 2255 | status: "running" as const, |
| | 2256 | requestedAt: runOccurredAt, |
| | 2257 | startedAt: runOccurredAt, |
| | 2258 | completedAt: null, |
| | 2259 | checkpointId: null, |
| | 2260 | contextHandoffId: null, |
| | 2261 | }, |
| | 2262 | }) |
| | 2263 | .pipe(Effect.withTracer(tracer)); |
| | 2264 | // The thread row is patched in place, never read back to be re-encoded. |
| | 2265 | assert.isEmpty( |
| | 2266 | statements.filter( |
| | 2267 | (statement) => |
| | 2268 | /^\s*SELECT/i.test(statement) && |
| | 2269 | statement.includes("orchestration_v2_projection_threads"), |
| | 2270 | ), |
| | 2271 | ); |
| | 2272 | |
| | 2273 | const active = (yield* projectionStore.getThreadProjection(threadId)).thread; |
| | 2274 | assert.deepEqual(active, { ...created, updatedAt: runOccurredAt }); |
| | 2275 | const shell = (yield* projectionStore.getShellSnapshot()).threads.find( |
| | 2276 | (candidate) => candidate.id === threadId, |
| | 2277 | ); |
| | 2278 | assert.deepEqual(shell?.updatedAt, runOccurredAt); |
| | 2279 | }), |
| | 2280 | ); |
| | 2281 | |
| 2189 | it.effect("preserves delegated completion ownership across stale run and task updates", () => | 2282 | it.effect("preserves delegated completion ownership across stale run and task updates", () => |
| 2190 | Effect.gen(function* () { | 2283 | Effect.gen(function* () { |
| 2191 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; | 2284 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; |