@@ -229,6 +229,90 @@ describe('SessionManager', () => {
229229 expect ( after [ 1 ] ) . not . toBe ( before [ 1 ] ) ; // changed row is a new object
230230 } ) ;
231231
232+ describe ( 'clear()' , ( ) => {
233+ it ( 'drops finished sessions (stopped, forgotten) but keeps in-flight ones' , async ( ) => {
234+ const { manager, created } = makeManager ( ) ;
235+ manager . create ( 'done' ) ; // 0 → completed
236+ manager . create ( 'busy' ) ; // 1 → running (kept)
237+ manager . create ( 'gone' ) ; // 2 → interrupted
238+ await flush ( ) ;
239+ created [ 0 ] ?. drive ( 'completed' , 'sdk-0' ) ;
240+ created [ 1 ] ?. drive ( 'running' , 'sdk-1' ) ;
241+ created [ 2 ] ?. drive ( 'interrupted' , 'sdk-2' ) ;
242+
243+ const cleared = manager . clear ( ) ;
244+
245+ expect ( cleared ) . toBe ( 2 ) ;
246+ // Only the in-flight (running) session remains in the list.
247+ expect ( manager . getSnapshot ( ) . map ( ( s ) => s . title ) ) . toEqual ( [ 'busy' ] ) ;
248+ // Cleared sessions were quietly stopped (not aborted), running one untouched.
249+ expect ( created [ 0 ] ?. stopped ) . toBe ( true ) ;
250+ expect ( created [ 2 ] ?. stopped ) . toBe ( true ) ;
251+ expect ( created [ 1 ] ?. stopped ) . toBe ( false ) ;
252+ expect ( created . some ( ( s ) => s . aborted ) ) . toBe ( false ) ;
253+ } ) ;
254+
255+ it ( 'excludes cleared sessions from the persisted snapshot (stay gone after restart)' , async ( ) => {
256+ const { manager, created } = makeManager ( ) ;
257+ manager . create ( 'done' ) ;
258+ await flush ( ) ;
259+ created [ 0 ] ?. drive ( 'completed' , 'sdk-0' ) ;
260+ expect ( manager . persistableState ( ) . sessions ) . toHaveLength ( 1 ) ;
261+
262+ manager . clear ( ) ;
263+
264+ expect ( manager . persistableState ( ) . sessions ) . toEqual ( [ ] ) ;
265+ } ) ;
266+
267+ it ( 'signals a persist and notifies subscribers when it removes sessions' , async ( ) => {
268+ const onPersist = vi . fn ( ) ;
269+ const created : FakeSession [ ] = [ ] ;
270+ const manager = new SessionManager ( {
271+ worktrees : fakeWorktrees ( ) ,
272+ queryFn : ( ( ) => {
273+ throw new Error ( 'unused' ) ;
274+ } ) as never ,
275+ now : ( ) => 100 ,
276+ onPersist,
277+ createSession : ( { input, onChange } ) => {
278+ const s = new FakeSession ( input , onChange ) ;
279+ created . push ( s ) ;
280+ return s ;
281+ } ,
282+ } ) ;
283+ manager . create ( 'done' ) ;
284+ await flush ( ) ;
285+ created [ 0 ] ?. drive ( 'completed' , 'sdk-0' ) ;
286+ const listener = vi . fn ( ) ;
287+ manager . subscribe ( listener ) ;
288+ onPersist . mockClear ( ) ;
289+
290+ manager . clear ( ) ;
291+
292+ expect ( onPersist ) . toHaveBeenCalledTimes ( 1 ) ;
293+ expect ( listener ) . toHaveBeenCalled ( ) ; // store rebuild notified subscribers
294+ } ) ;
295+
296+ it ( 'is a no-op (no persist) when there is nothing finished to clear' , async ( ) => {
297+ const onPersist = vi . fn ( ) ;
298+ const manager = new SessionManager ( {
299+ worktrees : fakeWorktrees ( ) ,
300+ queryFn : ( ( ) => {
301+ throw new Error ( 'unused' ) ;
302+ } ) as never ,
303+ now : ( ) => 100 ,
304+ onPersist,
305+ createSession : ( { input, onChange } ) => new FakeSession ( input , onChange ) ,
306+ } ) ;
307+ manager . create ( 'busy' ) ;
308+ await flush ( ) ;
309+ onPersist . mockClear ( ) ;
310+ expect ( manager . clear ( ) ) . toBe ( 0 ) ;
311+ expect ( onPersist ) . not . toHaveBeenCalled ( ) ;
312+ expect ( manager . getSnapshot ( ) ) . toHaveLength ( 1 ) ;
313+ } ) ;
314+ } ) ;
315+
232316 it ( 'dispose() quietly stops every session (resumable, not marked failed)' , async ( ) => {
233317 const { manager, created } = makeManager ( ) ;
234318 manager . create ( 'a' ) ;
0 commit comments