@@ -367,13 +367,15 @@ describe("WorkboardStore", () => {
367367 status : "review" ,
368368 metadata : { lifecycleStatusSourceUpdatedAt : 0 } ,
369369 } ) ;
370+ expect ( staleZeroLifecycle ) . toEqual ( manual ) ;
370371 expect ( staleZeroLifecycle . status ) . toBe ( "running" ) ;
371372 expect ( staleZeroLifecycle . metadata ?. lifecycleStatusSourceUpdatedAt ) . toBeUndefined ( ) ;
372373
373374 const staleLifecycle = await store . update ( card . id , {
374375 status : "review" ,
375376 metadata : { lifecycleStatusSourceUpdatedAt : 2000 } ,
376377 } ) ;
378+ expect ( staleLifecycle ) . toEqual ( manual ) ;
377379 expect ( staleLifecycle . status ) . toBe ( "running" ) ;
378380 expect ( staleLifecycle . updatedAt ) . toBe ( manual . updatedAt ) ;
379381 expect ( staleLifecycle . events ) . toHaveLength ( manual . events ?. length ?? 0 ) ;
@@ -390,6 +392,74 @@ describe("WorkboardStore", () => {
390392 ) ;
391393 } ) ;
392394
395+ it ( "keeps non-status fields from stale lifecycle patches" , async ( ) => {
396+ const store = new WorkboardStore ( createMemoryStore ( ) ) ;
397+ const card = await store . create ( {
398+ title : "Keep stale sync details" ,
399+ execution : {
400+ id : "exec-1" ,
401+ kind : "agent-session" ,
402+ engine : "codex" ,
403+ mode : "autonomous" ,
404+ status : "running" ,
405+ model : "openai/gpt-5.5" ,
406+ sessionKey : "agent:main:dashboard:1" ,
407+ runId : "run-1" ,
408+ startedAt : 1 ,
409+ updatedAt : 1000 ,
410+ } ,
411+ } ) ;
412+ const lifecycleMoved = await store . update ( card . id , {
413+ status : "review" ,
414+ metadata : {
415+ lifecycleStatusSourceUpdatedAt : 1000 ,
416+ stale : {
417+ detectedAt : 1000 ,
418+ lastSessionUpdatedAt : 1000 ,
419+ reason : "Session has not reported recent activity." ,
420+ } ,
421+ } ,
422+ } ) ;
423+ const manual = await store . update ( card . id , {
424+ status : "running" ,
425+ metadata : lifecycleMoved . metadata ,
426+ } ) ;
427+
428+ const synced = await store . update ( card . id , {
429+ status : "review" ,
430+ execution : {
431+ id : "exec-1" ,
432+ kind : "agent-session" ,
433+ engine : "codex" ,
434+ mode : "autonomous" ,
435+ status : "done" ,
436+ model : "openai/gpt-5.5" ,
437+ sessionKey : "agent:main:dashboard:1" ,
438+ runId : "run-1" ,
439+ startedAt : 1 ,
440+ updatedAt : 2000 ,
441+ } ,
442+ metadata : {
443+ lifecycleStatusSourceUpdatedAt : 1000 ,
444+ stale : null ,
445+ } ,
446+ } ) ;
447+
448+ expect ( manual . metadata ?. stale ) . toBeDefined ( ) ;
449+ expect ( synced . status ) . toBe ( "running" ) ;
450+ expect ( synced . execution ) . toMatchObject ( {
451+ runId : "run-1" ,
452+ status : "done" ,
453+ updatedAt : 2000 ,
454+ } ) ;
455+ expect ( synced . metadata ?. stale ) . toBeUndefined ( ) ;
456+ expect ( synced . metadata ?. lifecycleStatusSourceUpdatedAt ) . toBeUndefined ( ) ;
457+ expect ( synced . events ?. at ( - 1 ) ) . toMatchObject ( {
458+ kind : "attempt_updated" ,
459+ runId : "run-1" ,
460+ } ) ;
461+ } ) ;
462+
393463 it ( "clears copied lifecycle provenance on manual status patches" , async ( ) => {
394464 const store = new WorkboardStore ( createMemoryStore ( ) ) ;
395465 const card = await store . create ( { title : "Clear copied provenance" } ) ;
0 commit comments