@@ -143,59 +143,21 @@ describe("SessionGoal", () => {
143143 } ) ,
144144 )
145145
146- it . effect ( "todo.updated replaces the whole list and a replayed event is a no-op" , ( ) =>
146+ it . effect ( "todo.updated replaces the whole list and replay is a no-op" , ( ) =>
147147 Effect . gen ( function * ( ) {
148148 const { db, bus } = yield * setup
149- yield * bus . publish ( SessionEvent . Goal . Updated , { sessionID, snapshot : snapshot ( 1 ) } )
150- yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos } )
149+ yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 1 , todos } )
151150 expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [
152151 { position : 0 , content : "first" , status : "pending" , acceptance : "tests pass" , feedback : undefined } ,
153152 { position : 1 , content : "second" , status : "in_progress" , acceptance : undefined , feedback : undefined } ,
154153 ] )
155154
156- // The same revision arrives twice; only the first one advances the goal.
157- yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos : [ todos [ 0 ] ] } )
158- expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toHaveLength ( 2 )
159- } ) ,
160- )
161-
162- it . effect ( "todo.updated never rewinds the goal revision, so a stale verdict stays rejected" , ( ) =>
163- Effect . gen ( function * ( ) {
164- const { db, bus } = yield * setup
165- yield * bus . publish ( SessionEvent . Goal . Updated , { sessionID, snapshot : snapshot ( 1 ) } )
166- yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos } )
167- yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 3 , todos } )
168-
169- // A late list event carrying an older revision must not move the goal back to a
170- // number a verdict judged against the previous list still carries.
171- yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos : [ todos [ 0 ] ] } )
172- expect ( yield * SessionGoal . read ( db , sessionID ) ) . toMatchObject ( { revision : 3 } )
173- expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toHaveLength ( 2 )
174-
175- yield * bus . publish ( SessionEvent . Todo . Verified , {
176- sessionID,
177- revision : 2 ,
178- items : [ { position : 0 , status : "completed" , feedback : "stale" } ] ,
179- } )
180- expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 0 )
181- expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [
182- { position : 0 , content : "first" , status : "pending" , acceptance : "tests pass" , feedback : undefined } ,
183- { position : 1 , content : "second" , status : "in_progress" , acceptance : undefined , feedback : undefined } ,
184- ] )
185- } ) ,
186- )
187-
188- it . effect ( "todo.updated without a goal row is dropped" , ( ) =>
189- Effect . gen ( function * ( ) {
190- const { db, bus } = yield * setup
191- // A list only exists under a goal, so an event without one leaves no state.
192155 yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 1 , todos } )
193- expect ( yield * SessionGoal . read ( db , sessionID ) ) . toBeUndefined ( )
194- expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [ ] )
156+ expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toHaveLength ( 2 )
195157 } ) ,
196158 )
197159
198- it . effect ( "todo.verified applies a fresh verdict and advances rounds " , ( ) =>
160+ it . effect ( "todo.verified applies a fresh verdict" , ( ) =>
199161 Effect . gen ( function * ( ) {
200162 const { db, bus } = yield * setup
201163 yield * bus . publish ( SessionEvent . Goal . Updated , { sessionID, snapshot : snapshot ( 1 ) } )
@@ -212,7 +174,8 @@ describe("SessionGoal", () => {
212174 { position : 0 , content : "first" , status : "completed" , acceptance : "tests pass" , feedback : "ok" } ,
213175 { position : 1 , content : "second" , status : "in_progress" , acceptance : undefined , feedback : "needs work" } ,
214176 ] )
215- expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 1 )
177+ // The driver owns the round counter; a verdict does not advance it.
178+ expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 0 )
216179 } ) ,
217180 )
218181
@@ -247,7 +210,10 @@ describe("SessionGoal", () => {
247210 items : [ { position : 0 , status : "completed" , feedback : "ok" } ] ,
248211 } )
249212 expect ( yield * SessionGoal . read ( db , sessionID ) ) . toBeUndefined ( )
250- expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [ ] )
213+ expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [
214+ { position : 0 , content : "first" , status : "pending" , acceptance : "tests pass" , feedback : undefined } ,
215+ { position : 1 , content : "second" , status : "in_progress" , acceptance : undefined , feedback : undefined } ,
216+ ] )
251217 } ) ,
252218 )
253219} )
@@ -334,22 +300,30 @@ describe("SessionGoal.Service", () => {
334300
335301 serviceIt . effect ( "set starts a new goal with a fresh round budget" , ( ) =>
336302 Effect . gen ( function * ( ) {
337- const { db, bus } = yield * setup
303+ const { db } = yield * setup
338304 const goal = yield * SessionGoal . Service
339- const first = yield * goal . update ( sessionID , { action : "set" , objective : "first" } )
340- yield * goal . replaceTodo ( sessionID , [ { content : "first" , status : "pending" } ] )
341- yield * bus . publish ( SessionEvent . Todo . Verified , {
342- sessionID,
343- revision : first . revision + 1 ,
344- items : [ { position : 0 , status : "pending" , feedback : "not yet" } ] ,
345- } )
346- expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 1 )
305+ yield * goal . update ( sessionID , { action : "set" , objective : "first" } )
306+ yield * goal . recordRound ( sessionID )
307+ yield * goal . recordRound ( sessionID )
308+ expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 2 )
347309
348310 yield * goal . update ( sessionID , { action : "set" , objective : "second" } )
349311 expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 0 )
350312 } ) ,
351313 )
352314
315+ serviceIt . effect ( "recordRound counts only an active goal" , ( ) =>
316+ Effect . gen ( function * ( ) {
317+ const { db } = yield * setup
318+ const goal = yield * SessionGoal . Service
319+ yield * goal . update ( sessionID , { action : "set" , objective : "ship it" } )
320+ yield * goal . block ( sessionID , { code : "max_rounds" , message : "done" } )
321+ // A goal that settled while a round was being admitted keeps its final count.
322+ yield * goal . recordRound ( sessionID )
323+ expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 0 )
324+ } ) ,
325+ )
326+
353327 serviceIt . effect ( "readState reports the goal, its todo list, and process-local arming" , ( ) =>
354328 Effect . gen ( function * ( ) {
355329 yield * setup
0 commit comments