@@ -537,6 +537,73 @@ func TestSyncSingleSessionPartialFullWritesQueueNewChild(t *testing.T) {
537537 }
538538}
539539
540+ func TestSyncSingleSessionPartialFullWriteRepairsAttemptedSession (t * testing.T ) {
541+ root := t .TempDir ()
542+ sourcePath , fingerprint := writeProcessProviderSource (
543+ t , root , "partial-parent-write.jsonl" ,
544+ )
545+ child := processFixtureResult (
546+ "cowork:child" , parser .AgentCowork , "fixture-project" ,
547+ sourcePath , fingerprint ,
548+ )
549+ child .Session .ParentSessionID = "cowork:path-parent"
550+ child .Session .RelationshipType = parser .RelSubagent
551+ provider := newProcessFixtureProvider (
552+ processFixtureSource (sourcePath ), fingerprint ,
553+ parser.ParseOutcome {
554+ Results : []parser.ParseResultOutcome {{
555+ Result : child , DataVersion : parser .DataVersionCurrent ,
556+ }},
557+ ResultSetComplete : true ,
558+ ForceReplace : true ,
559+ },
560+ )
561+ engine := newProcessFixtureEngine (t , root , provider )
562+ database := engine .db
563+ actualParent := "cowork:spawner"
564+ started := "2026-01-01T00:00:00Z"
565+ require .NoError (t , database .UpsertSession (db.Session {
566+ ID : actualParent , Agent : string (parser .AgentCowork ),
567+ Project : "fixture-project" , Machine : "devbox" , StartedAt : & started ,
568+ MessageCount : 1 ,
569+ }))
570+ require .NoError (t , database .UpsertSession (db.Session {
571+ ID : "cowork:child" , Agent : string (parser .AgentCowork ),
572+ Project : "fixture-project" , Machine : "devbox" ,
573+ ParentSessionID : & actualParent , RelationshipType : string (parser .RelSubagent ),
574+ }))
575+ require .NoError (t , database .InsertMessages ([]db.Message {{
576+ SessionID : actualParent , Ordinal : 0 , Role : string (parser .RoleAssistant ),
577+ Content : "spawn child" , HasToolUse : true ,
578+ ToolCalls : []db.ToolCall {{
579+ ToolUseID : "spawn-child" , ToolName : "Agent" ,
580+ SubagentSessionID : "cowork:child" ,
581+ }},
582+ }}))
583+
584+ raw , err := sql .Open ("sqlite3" , database .Path ())
585+ require .NoError (t , err )
586+ t .Cleanup (func () { require .NoError (t , raw .Close ()) })
587+ _ , err = raw .Exec (fmt .Sprintf (`
588+ CREATE TRIGGER fail_child_write_completion
589+ BEFORE UPDATE OF data_version ON sessions
590+ WHEN NEW.id = 'cowork:child' AND NEW.data_version = %d
591+ BEGIN
592+ SELECT RAISE(FAIL, 'injected child completion failure');
593+ END` , db .CurrentDataVersion ()))
594+ require .NoError (t , err )
595+
596+ syncErr := engine .SyncSingleSession ("cowork:child" )
597+
598+ require .ErrorContains (t , syncErr , "injected child completion failure" )
599+ stored , err := database .GetSession (t .Context (), "cowork:child" )
600+ require .NoError (t , err )
601+ require .NotNil (t , stored )
602+ require .NotNil (t , stored .ParentSessionID )
603+ assert .Equal (t , actualParent , * stored .ParentSessionID ,
604+ "deferred repair must reconcile the partially written session itself" )
605+ }
606+
540607func TestProcessFileProviderAuthoritativeSuppressesUncleanSkipCache (t * testing.T ) {
541608
542609 root := t .TempDir ()
0 commit comments