Skip to content
Merged
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
31 changes: 31 additions & 0 deletions packages/contracts/src/orchestration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,37 @@ it.effect("decodes thread pull request links with snapshot and stack", () =>
}),
);

// A stored event that fails to decode stops the event store read, and with it
// server startup, so rows written before `turnId` existed must still load.
it.effect("decodes a legacy message-sent event persisted without turnId", () =>
Effect.gen(function* () {
const event = yield* decodeOrchestrationEvent({
sequence: 539,
eventId: "event-message-legacy-1",
aggregateKind: "thread",
aggregateId: "thread-1",
type: "thread.message-sent",
occurredAt: "2026-01-01T00:00:00.000Z",
commandId: "cmd-message-legacy-1",
causationEventId: null,
correlationId: "cmd-message-legacy-1",
metadata: {},
payload: {
threadId: "thread-1",
messageId: "message-1",
role: "user",
text: "written before turn ids were recorded",
streaming: false,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
},
});
assert.strictEqual(event.type, "thread.message-sent");
if (event.type !== "thread.message-sent") return;
assert.strictEqual(event.payload.turnId, null);
}),
);

it.effect("decodes thread archived and unarchived events", () =>
Effect.gen(function* () {
const archived = yield* decodeOrchestrationEvent({
Expand Down
3 changes: 2 additions & 1 deletion packages/contracts/src/orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1873,7 +1873,8 @@ export const ThreadMessageSentPayload = Schema.Struct({
text: Schema.String,
attachments: Schema.optional(Schema.Array(ChatAttachment)),
context: Schema.optional(OrchestrationMessageContext),
turnId: Schema.NullOr(TurnId),
// Events persisted before the field existed carry no key at all.
turnId: Schema.NullOr(TurnId).pipe(Schema.withDecodingDefault(Effect.succeed(null))),
streaming: Schema.Boolean,
createdAt: IsoDateTime,
updatedAt: IsoDateTime,
Expand Down
Loading