@@ -41,11 +41,110 @@ function makeThreadCheckpointContext(input: {
4141}
4242
4343describe ( "CheckpointDiffQueryLive" , ( ) => {
44+ it ( "uses the narrow full-thread context lookup for all-turns diffs" , async ( ) => {
45+ const projectId = ProjectId . make ( "project-full-thread" ) ;
46+ const threadId = ThreadId . make ( "thread-full-thread" ) ;
47+ const toCheckpointRef = checkpointRefForThreadTurn ( threadId , 4 ) ;
48+ let getThreadCheckpointContextCalls = 0 ;
49+ let getFullThreadDiffContextCalls = 0 ;
50+ const diffCheckpointsCalls : Array < {
51+ readonly fromCheckpointRef : CheckpointRef ;
52+ readonly toCheckpointRef : CheckpointRef ;
53+ readonly cwd : string ;
54+ readonly ignoreWhitespace : boolean ;
55+ } > = [ ] ;
56+
57+ const checkpointStore : CheckpointStoreShape = {
58+ isGitRepository : ( ) => Effect . succeed ( true ) ,
59+ captureCheckpoint : ( ) => Effect . void ,
60+ hasCheckpointRef : ( ) => Effect . succeed ( true ) ,
61+ restoreCheckpoint : ( ) => Effect . succeed ( true ) ,
62+ diffCheckpoints : ( { fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace } ) =>
63+ Effect . sync ( ( ) => {
64+ diffCheckpointsCalls . push ( {
65+ fromCheckpointRef,
66+ toCheckpointRef,
67+ cwd,
68+ ignoreWhitespace,
69+ } ) ;
70+ return "full thread diff patch" ;
71+ } ) ,
72+ deleteCheckpointRefs : ( ) => Effect . void ,
73+ } ;
74+
75+ const layer = CheckpointDiffQueryLive . pipe (
76+ Layer . provideMerge ( Layer . succeed ( CheckpointStore , checkpointStore ) ) ,
77+ Layer . provideMerge (
78+ Layer . succeed ( ProjectionSnapshotQuery , {
79+ getCommandReadModel : ( ) =>
80+ Effect . die ( "CheckpointDiffQuery should not request the command read model" ) ,
81+ getSnapshot : ( ) =>
82+ Effect . die ( "CheckpointDiffQuery should not request the full orchestration snapshot" ) ,
83+ getShellSnapshot : ( ) =>
84+ Effect . die ( "CheckpointDiffQuery should not request the orchestration shell snapshot" ) ,
85+ getArchivedShellSnapshot : ( ) =>
86+ Effect . die ( "CheckpointDiffQuery should not request archived shell snapshots" ) ,
87+ getSnapshotSequence : ( ) => Effect . succeed ( { snapshotSequence : 0 } ) ,
88+ getCounts : ( ) => Effect . succeed ( { projectCount : 0 , threadCount : 0 } ) ,
89+ getActiveProjectByWorkspaceRoot : ( ) => Effect . succeed ( Option . none ( ) ) ,
90+ getProjectShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
91+ getFirstActiveThreadIdByProjectId : ( ) => Effect . succeed ( Option . none ( ) ) ,
92+ getThreadCheckpointContext : ( ) =>
93+ Effect . sync ( ( ) => {
94+ getThreadCheckpointContextCalls += 1 ;
95+ return Option . none ( ) ;
96+ } ) ,
97+ getFullThreadDiffContext : ( ) =>
98+ Effect . sync ( ( ) => {
99+ getFullThreadDiffContextCalls += 1 ;
100+ return Option . some ( {
101+ threadId,
102+ projectId,
103+ workspaceRoot : "/tmp/workspace" ,
104+ worktreePath : "/tmp/worktree" ,
105+ latestCheckpointTurnCount : 4 ,
106+ toCheckpointRef,
107+ } ) ;
108+ } ) ,
109+ getThreadShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
110+ getThreadDetailById : ( ) => Effect . succeed ( Option . none ( ) ) ,
111+ } ) ,
112+ ) ,
113+ ) ;
114+
115+ const result = await Effect . runPromise (
116+ Effect . gen ( function * ( ) {
117+ const query = yield * CheckpointDiffQuery ;
118+ return yield * query . getFullThreadDiff ( {
119+ threadId,
120+ toTurnCount : 4 ,
121+ ignoreWhitespace : true ,
122+ } ) ;
123+ } ) . pipe ( Effect . provide ( layer ) ) ,
124+ ) ;
125+
126+ expect ( getThreadCheckpointContextCalls ) . toBe ( 0 ) ;
127+ expect ( getFullThreadDiffContextCalls ) . toBe ( 1 ) ;
128+ expect ( diffCheckpointsCalls ) . toEqual ( [
129+ {
130+ cwd : "/tmp/worktree" ,
131+ fromCheckpointRef : checkpointRefForThreadTurn ( threadId , 0 ) ,
132+ toCheckpointRef,
133+ ignoreWhitespace : true ,
134+ } ,
135+ ] ) ;
136+ expect ( result ) . toEqual ( {
137+ threadId,
138+ fromTurnCount : 0 ,
139+ toTurnCount : 4 ,
140+ diff : "full thread diff patch" ,
141+ } ) ;
142+ } ) ;
143+
44144 it ( "computes diffs using canonical turn-0 checkpoint refs" , async ( ) => {
45145 const projectId = ProjectId . make ( "project-1" ) ;
46146 const threadId = ThreadId . make ( "thread-1" ) ;
47147 const toCheckpointRef = checkpointRefForThreadTurn ( threadId , 1 ) ;
48- const hasCheckpointRefCalls : Array < CheckpointRef > = [ ] ;
49148 const diffCheckpointsCalls : Array < {
50149 readonly fromCheckpointRef : CheckpointRef ;
51150 readonly toCheckpointRef : CheckpointRef ;
@@ -65,11 +164,7 @@ describe("CheckpointDiffQueryLive", () => {
65164 const checkpointStore : CheckpointStoreShape = {
66165 isGitRepository : ( ) => Effect . succeed ( true ) ,
67166 captureCheckpoint : ( ) => Effect . void ,
68- hasCheckpointRef : ( { checkpointRef } ) =>
69- Effect . sync ( ( ) => {
70- hasCheckpointRefCalls . push ( checkpointRef ) ;
71- return true ;
72- } ) ,
167+ hasCheckpointRef : ( ) => Effect . succeed ( true ) ,
73168 restoreCheckpoint : ( ) => Effect . succeed ( true ) ,
74169 diffCheckpoints : ( { fromCheckpointRef, toCheckpointRef, cwd, ignoreWhitespace } ) =>
75170 Effect . sync ( ( ) => {
@@ -102,6 +197,7 @@ describe("CheckpointDiffQueryLive", () => {
102197 getProjectShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
103198 getFirstActiveThreadIdByProjectId : ( ) => Effect . succeed ( Option . none ( ) ) ,
104199 getThreadCheckpointContext : ( ) => Effect . succeed ( Option . some ( threadCheckpointContext ) ) ,
200+ getFullThreadDiffContext : ( ) => Effect . die ( "unused" ) ,
105201 getThreadShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
106202 getThreadDetailById : ( ) => Effect . succeed ( Option . none ( ) ) ,
107203 } ) ,
@@ -121,7 +217,6 @@ describe("CheckpointDiffQueryLive", () => {
121217 ) ;
122218
123219 const expectedFromRef = checkpointRefForThreadTurn ( threadId , 0 ) ;
124- expect ( hasCheckpointRefCalls ) . toEqual ( [ expectedFromRef , toCheckpointRef ] ) ;
125220 expect ( diffCheckpointsCalls ) . toEqual ( [
126221 {
127222 cwd : "/tmp/workspace" ,
@@ -184,6 +279,7 @@ describe("CheckpointDiffQueryLive", () => {
184279 getProjectShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
185280 getFirstActiveThreadIdByProjectId : ( ) => Effect . succeed ( Option . none ( ) ) ,
186281 getThreadCheckpointContext : ( ) => Effect . succeed ( Option . some ( threadCheckpointContext ) ) ,
282+ getFullThreadDiffContext : ( ) => Effect . die ( "unused" ) ,
187283 getThreadShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
188284 getThreadDetailById : ( ) => Effect . succeed ( Option . none ( ) ) ,
189285 } ) ,
@@ -204,6 +300,74 @@ describe("CheckpointDiffQueryLive", () => {
204300 expect ( diffCheckpointsCalls ) . toEqual ( [ { ignoreWhitespace : true } ] ) ;
205301 } ) ;
206302
303+ it ( "does not preflight checkpoint refs before diffing" , async ( ) => {
304+ const projectId = ProjectId . make ( "project-no-preflight" ) ;
305+ const threadId = ThreadId . make ( "thread-no-preflight" ) ;
306+ const toCheckpointRef = checkpointRefForThreadTurn ( threadId , 1 ) ;
307+ let hasCheckpointRefCallCount = 0 ;
308+
309+ const threadCheckpointContext = makeThreadCheckpointContext ( {
310+ projectId,
311+ threadId,
312+ workspaceRoot : "/tmp/workspace" ,
313+ worktreePath : null ,
314+ checkpointTurnCount : 1 ,
315+ checkpointRef : toCheckpointRef ,
316+ } ) ;
317+
318+ const checkpointStore : CheckpointStoreShape = {
319+ isGitRepository : ( ) => Effect . succeed ( true ) ,
320+ captureCheckpoint : ( ) => Effect . void ,
321+ hasCheckpointRef : ( ) =>
322+ Effect . sync ( ( ) => {
323+ hasCheckpointRefCallCount += 1 ;
324+ return true ;
325+ } ) ,
326+ restoreCheckpoint : ( ) => Effect . succeed ( true ) ,
327+ diffCheckpoints : ( ) => Effect . succeed ( "diff patch" ) ,
328+ deleteCheckpointRefs : ( ) => Effect . void ,
329+ } ;
330+
331+ const layer = CheckpointDiffQueryLive . pipe (
332+ Layer . provideMerge ( Layer . succeed ( CheckpointStore , checkpointStore ) ) ,
333+ Layer . provideMerge (
334+ Layer . succeed ( ProjectionSnapshotQuery , {
335+ getCommandReadModel : ( ) =>
336+ Effect . die ( "CheckpointDiffQuery should not request the command read model" ) ,
337+ getSnapshot : ( ) =>
338+ Effect . die ( "CheckpointDiffQuery should not request the full orchestration snapshot" ) ,
339+ getShellSnapshot : ( ) =>
340+ Effect . die ( "CheckpointDiffQuery should not request the orchestration shell snapshot" ) ,
341+ getArchivedShellSnapshot : ( ) =>
342+ Effect . die ( "CheckpointDiffQuery should not request archived shell snapshots" ) ,
343+ getSnapshotSequence : ( ) => Effect . succeed ( { snapshotSequence : 0 } ) ,
344+ getCounts : ( ) => Effect . succeed ( { projectCount : 0 , threadCount : 0 } ) ,
345+ getActiveProjectByWorkspaceRoot : ( ) => Effect . succeed ( Option . none ( ) ) ,
346+ getProjectShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
347+ getFirstActiveThreadIdByProjectId : ( ) => Effect . succeed ( Option . none ( ) ) ,
348+ getThreadCheckpointContext : ( ) => Effect . succeed ( Option . some ( threadCheckpointContext ) ) ,
349+ getFullThreadDiffContext : ( ) => Effect . die ( "unused" ) ,
350+ getThreadShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
351+ getThreadDetailById : ( ) => Effect . succeed ( Option . none ( ) ) ,
352+ } ) ,
353+ ) ,
354+ ) ;
355+
356+ await Effect . runPromise (
357+ Effect . gen ( function * ( ) {
358+ const query = yield * CheckpointDiffQuery ;
359+ return yield * query . getTurnDiff ( {
360+ threadId,
361+ fromTurnCount : 0 ,
362+ toTurnCount : 1 ,
363+ ignoreWhitespace : true ,
364+ } ) ;
365+ } ) . pipe ( Effect . provide ( layer ) ) ,
366+ ) ;
367+
368+ expect ( hasCheckpointRefCallCount ) . toBe ( 0 ) ;
369+ } ) ;
370+
207371 it ( "fails when the thread is missing from the snapshot" , async ( ) => {
208372 const threadId = ThreadId . make ( "thread-missing" ) ;
209373
@@ -234,6 +398,7 @@ describe("CheckpointDiffQueryLive", () => {
234398 getProjectShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
235399 getFirstActiveThreadIdByProjectId : ( ) => Effect . succeed ( Option . none ( ) ) ,
236400 getThreadCheckpointContext : ( ) => Effect . succeed ( Option . none ( ) ) ,
401+ getFullThreadDiffContext : ( ) => Effect . succeed ( Option . none ( ) ) ,
237402 getThreadShellById : ( ) => Effect . succeed ( Option . none ( ) ) ,
238403 getThreadDetailById : ( ) => Effect . succeed ( Option . none ( ) ) ,
239404 } ) ,
0 commit comments