1import { NonNegativeInt, ThreadId, type VcsError } from "@t3tools/contracts";
2import * as Schema from "effect/Schema";
3
4import type { ProjectionRepositoryError } from "../persistence/Errors.ts";
5
6export const CheckpointDiffOperation = Schema.Literals([
7 "CheckpointDiffQuery.getTurnDiff",
8 "CheckpointDiffQuery.getFullThreadDiff",
9]);
10export type CheckpointDiffOperation = typeof CheckpointDiffOperation.Type;
11
12/** The computed result does not satisfy the checkpoint RPC contract. */
13export class CheckpointDiffResultInvalidError extends Schema.TaggedError<CheckpointDiffResultInvalidError>()(
14 "CheckpointDiffResultInvalidError",
15 {
16 operation: CheckpointDiffOperation,
17 threadId: ThreadId,
18 },
19) {
20 override get message(): string {
21 const result =
22 this.operation === "CheckpointDiffQuery.getTurnDiff" ? "turn diff" : "full thread diff";
23 return `Checkpoint invariant violation in ${this.operation}: Computed ${result} result does not satisfy contract schema.`;
24 }
25}
26
27/** Projection state no longer contains the requested checkpoint thread. */
28export class CheckpointThreadNotFoundError extends Schema.TaggedError<CheckpointThreadNotFoundError>()(
29 "CheckpointThreadNotFoundError",
30 {
31 operation: CheckpointDiffOperation,
32 threadId: ThreadId,
33 },
34) {
35 override get message(): string {
36 return `Checkpoint invariant violation in ${this.operation}: Thread '${this.threadId}' not found.`;
37 }
38}
39
40/** The checkpoint thread has no workspace path from which to compute a diff. */
41export class CheckpointWorkspacePathMissingError extends Schema.TaggedError<CheckpointWorkspacePathMissingError>()(
42 "CheckpointWorkspacePathMissingError",
43 {
44 operation: CheckpointDiffOperation,
45 threadId: ThreadId,
46 },
47) {
48 override get message(): string {
49 const diff =
50 this.operation === "CheckpointDiffQuery.getTurnDiff" ? "turn diff" : "full thread diff";
51 return `Checkpoint invariant violation in ${this.operation}: Workspace path missing for thread '${this.threadId}' when computing ${diff}.`;
52 }
53}
54
55/** The requested turn lies beyond the latest available checkpoint. */
56export class CheckpointTurnRangeUnavailableError extends Schema.TaggedError<CheckpointTurnRangeUnavailableError>()(
57 "CheckpointTurnRangeUnavailableError",
58 {
59 operation: CheckpointDiffOperation,
60 threadId: ThreadId,
61 requestedTurnCount: NonNegativeInt,
62 availableTurnCount: NonNegativeInt,
63 },
64) {
65 override get message(): string {
66 return `Checkpoint unavailable for thread ${this.threadId} turn ${this.requestedTurnCount}: Turn diff range exceeds current turn count: requested ${this.requestedTurnCount}, current ${this.availableTurnCount}.`;
67 }
68}
69
70/** Expected checkpoint metadata does not contain the requested Git ref. */
71export class CheckpointRefUnavailableError extends Schema.TaggedError<CheckpointRefUnavailableError>()(
72 "CheckpointRefUnavailableError",
73 {
74 operation: CheckpointDiffOperation,
75 threadId: ThreadId,
76 turnCount: NonNegativeInt,
77 checkpoint: Schema.Literals(["from", "to"]),
78 },
79) {
80 override get message(): string {
81 return `Checkpoint unavailable for thread ${this.threadId} turn ${this.turnCount}: Checkpoint ref is unavailable for turn ${this.turnCount}.`;
82 }
83}
84
85export type CheckpointStoreError = VcsError;
86
87export type CheckpointServiceError =
88 | CheckpointStoreError
89 | ProjectionRepositoryError
90 | CheckpointDiffResultInvalidError
91 | CheckpointThreadNotFoundError
92 | CheckpointWorkspacePathMissingError
93 | CheckpointTurnRangeUnavailableError
94 | CheckpointRefUnavailableError;