| ... | ... | @@ -3967,6 +3967,130 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => { |
| 3967 | 3967 | ]); |
| 3968 | 3968 | }), |
| 3969 | 3969 | ); |
| 3970 | |
| 3971 | it.effect("does not let a later missing placeholder clobber a ready checkpoint", () => |
| 3972 | Effect.gen(function* () { |
| 3973 | const projectionPipeline = yield* OrchestrationProjectionPipeline; |
| 3974 | const eventStore = yield* OrchestrationEventStore; |
| 3975 | const sql = yield* SqlClient.SqlClient; |
| 3976 | const appendAndProject = (event: Parameters<typeof eventStore.append>[0]) => |
| 3977 | eventStore |
| 3978 | .append(event) |
| 3979 | .pipe(Effect.flatMap((savedEvent) => projectionPipeline.projectEvent(savedEvent))); |
| 3980 | |
| 3981 | yield* appendAndProject({ |
| 3982 | type: "project.created", |
| 3983 | eventId: EventId.make("evt-checkpoint-guard-1"), |
| 3984 | aggregateKind: "project", |
| 3985 | aggregateId: ProjectId.make("project-checkpoint-guard"), |
| 3986 | occurredAt: "2026-02-26T14:00:00.000Z", |
| 3987 | commandId: CommandId.make("cmd-checkpoint-guard-1"), |
| 3988 | causationEventId: null, |
| 3989 | correlationId: CorrelationId.make("cmd-checkpoint-guard-1"), |
| 3990 | metadata: {}, |
| 3991 | payload: { |
| 3992 | projectId: ProjectId.make("project-checkpoint-guard"), |
| 3993 | title: "Project Checkpoint Guard", |
| 3994 | workspaceRoot: "/tmp/project-checkpoint-guard", |
| 3995 | defaultModelSelection: null, |
| 3996 | scripts: [], |
| 3997 | createdAt: "2026-02-26T14:00:00.000Z", |
| 3998 | updatedAt: "2026-02-26T14:00:00.000Z", |
| 3999 | }, |
| 4000 | }); |
| 4001 | |
| 4002 | yield* appendAndProject({ |
| 4003 | type: "thread.created", |
| 4004 | eventId: EventId.make("evt-checkpoint-guard-2"), |
| 4005 | aggregateKind: "thread", |
| 4006 | aggregateId: ThreadId.make("thread-checkpoint-guard"), |
| 4007 | occurredAt: "2026-02-26T14:00:01.000Z", |
| 4008 | commandId: CommandId.make("cmd-checkpoint-guard-2"), |
| 4009 | causationEventId: null, |
| 4010 | correlationId: CorrelationId.make("cmd-checkpoint-guard-2"), |
| 4011 | metadata: {}, |
| 4012 | payload: { |
| 4013 | threadId: ThreadId.make("thread-checkpoint-guard"), |
| 4014 | projectId: ProjectId.make("project-checkpoint-guard"), |
| 4015 | title: "Thread Checkpoint Guard", |
| 4016 | modelSelection: { |
| 4017 | instanceId: ProviderInstanceId.make("codex"), |
| 4018 | model: "gpt-5-codex", |
| 4019 | }, |
| 4020 | runtimeMode: "full-access", |
| 4021 | interactionMode: "default", |
| 4022 | branch: null, |
| 4023 | worktreePath: null, |
| 4024 | createdAt: "2026-02-26T14:00:01.000Z", |
| 4025 | updatedAt: "2026-02-26T14:00:01.000Z", |
| 4026 | }, |
| 4027 | }); |
| 4028 | |
| 4029 | yield* appendAndProject({ |
| 4030 | type: "thread.turn-diff-completed", |
| 4031 | eventId: EventId.make("evt-checkpoint-guard-3"), |
| 4032 | aggregateKind: "thread", |
| 4033 | aggregateId: ThreadId.make("thread-checkpoint-guard"), |
| 4034 | occurredAt: "2026-02-26T14:00:02.000Z", |
| 4035 | commandId: CommandId.make("cmd-checkpoint-guard-3"), |
| 4036 | causationEventId: null, |
| 4037 | correlationId: CorrelationId.make("cmd-checkpoint-guard-3"), |
| 4038 | metadata: {}, |
| 4039 | payload: { |
| 4040 | threadId: ThreadId.make("thread-checkpoint-guard"), |
| 4041 | turnId: TurnId.make("turn-ready"), |
| 4042 | checkpointTurnCount: 1, |
| 4043 | checkpointRef: CheckpointRef.make("refs/t3/checkpoints/thread-checkpoint-guard/turn/1"), |
| 4044 | status: "ready", |
| 4045 | files: [], |
| 4046 | assistantMessageId: MessageId.make("assistant-ready"), |
| 4047 | completedAt: "2026-02-26T14:00:02.000Z", |
| 4048 | }, |
| 4049 | }); |
| 4050 | |
| 4051 | yield* appendAndProject({ |
| 4052 | type: "thread.turn-diff-completed", |
| 4053 | eventId: EventId.make("evt-checkpoint-guard-4"), |
| 4054 | aggregateKind: "thread", |
| 4055 | aggregateId: ThreadId.make("thread-checkpoint-guard"), |
| 4056 | occurredAt: "2026-02-26T14:00:03.000Z", |
| 4057 | commandId: CommandId.make("cmd-checkpoint-guard-4"), |
| 4058 | causationEventId: null, |
| 4059 | correlationId: CorrelationId.make("cmd-checkpoint-guard-4"), |
| 4060 | metadata: {}, |
| 4061 | payload: { |
| 4062 | threadId: ThreadId.make("thread-checkpoint-guard"), |
| 4063 | turnId: TurnId.make("turn-ready"), |
| 4064 | checkpointTurnCount: 1, |
| 4065 | checkpointRef: CheckpointRef.make("refs/t3/checkpoints/thread-checkpoint-guard/turn/1"), |
| 4066 | status: "missing", |
| 4067 | files: [], |
| 4068 | assistantMessageId: MessageId.make("assistant-ready"), |
| 4069 | completedAt: "2026-02-26T14:00:03.000Z", |
| 4070 | }, |
| 4071 | }); |
| 4072 | |
| 4073 | const turnRows = yield* sql<{ |
| 4074 | readonly turnId: string; |
| 4075 | readonly checkpointStatus: string | null; |
| 4076 | readonly checkpointRef: string | null; |
| 4077 | }>` |
| 4078 | SELECT |
| 4079 | turn_id AS "turnId", |
| 4080 | checkpoint_status AS "checkpointStatus", |
| 4081 | checkpoint_ref AS "checkpointRef" |
| 4082 | FROM projection_turns |
| 4083 | WHERE thread_id = 'thread-checkpoint-guard' |
| 4084 | `; |
| 4085 | assert.deepEqual(turnRows, [ |
| 4086 | { |
| 4087 | turnId: "turn-ready", |
| 4088 | checkpointStatus: "ready", |
| 4089 | checkpointRef: "refs/t3/checkpoints/thread-checkpoint-guard/turn/1", |
| 4090 | }, |
| 4091 | ]); |
| 4092 | }), |
| 4093 | ); |
| 3970 | 4094 | }); |
| 3971 | 4095 | |
| 3972 | 4096 | it.layer(makeProjectionPipelinePrefixedTestLayer("t3-pending-turn-terminal-test-"))( |