@@ -56,6 +56,125 @@ test('reconciles coherent completed evidence before output preparation or launch
5656 await started . pendingHandle . transfer ( ) . forceCleanup ( ) ;
5757} ) ;
5858
59+ test ( 'retires completed evidence whose recorder pid was reassigned, signaling nothing' , async ( ) => {
60+ let marker = JSON . stringify ( completedEvidence ( ) ) ;
61+ const calls : string [ ] = [ ] ;
62+ const runtime = await start ( {
63+ readManifest : async ( path : string ) =>
64+ path . startsWith ( '/sdcard' ) && marker
65+ ? { status : 'read' as const , contents : marker }
66+ : { status : 'missing' as const } ,
67+ inspect : async ( ) => 'ownership-lost' as const ,
68+ stop : async ( { pid } : { pid : string } ) => {
69+ calls . push ( `signal:${ pid } ` ) ;
70+ return 'already-missing' as const ;
71+ } ,
72+ remove : async ( path : string ) => {
73+ calls . push ( `artifact:${ path } ` ) ;
74+ return true ;
75+ } ,
76+ removeManifest : async ( ) => {
77+ calls . push ( 'manifest' ) ;
78+ marker = '' ;
79+ return true ;
80+ } ,
81+ outputs : {
82+ prepare : async ( ) => {
83+ calls . push ( 'prepare' ) ;
84+ } ,
85+ } ,
86+ start : async ( ) => {
87+ calls . push ( 'launch' ) ;
88+ return recordingProcess ( '77' ) ;
89+ } ,
90+ } ) ;
91+ const started = await runtime . screenRecordingStart ( newInput ( ) ) ;
92+ expect ( calls ) . toEqual ( [
93+ 'artifact:/sdcard/agent-device-recording-1.mp4' ,
94+ 'manifest' ,
95+ 'prepare' ,
96+ 'launch' ,
97+ ] ) ;
98+ await started . pendingHandle . transfer ( ) . forceCleanup ( ) ;
99+ } ) ;
100+
101+ test . each ( [
102+ [ 'an unconfirmed recorder identity' , 'uncertain' ] ,
103+ [ 'a live recorder' , 'owned-alive' ] ,
104+ ] ) ( 'refuses completed evidence named by %s' , async ( _name , ownership ) => {
105+ const marker = JSON . stringify ( completedEvidence ( ) ) ;
106+ const calls : string [ ] = [ ] ;
107+ const runtime = await start ( {
108+ readManifest : async ( path : string ) =>
109+ path . startsWith ( '/sdcard' )
110+ ? { status : 'read' as const , contents : marker }
111+ : { status : 'missing' as const } ,
112+ inspect : async ( ) => ownership ,
113+ stop : async ( { pid } : { pid : string } ) => {
114+ calls . push ( `signal:${ pid } ` ) ;
115+ return 'already-missing' as const ;
116+ } ,
117+ remove : async ( ) => {
118+ calls . push ( 'artifact' ) ;
119+ return true ;
120+ } ,
121+ removeManifest : async ( ) => {
122+ calls . push ( 'manifest' ) ;
123+ return true ;
124+ } ,
125+ outputs : {
126+ prepare : async ( ) => {
127+ calls . push ( 'prepare' ) ;
128+ } ,
129+ } ,
130+ start : async ( ) => {
131+ calls . push ( 'launch' ) ;
132+ return recordingProcess ( '77' ) ;
133+ } ,
134+ } ) ;
135+ await expect ( runtime . screenRecordingStart ( newInput ( ) ) ) . rejects . toThrow (
136+ 'cannot be safely retired' ,
137+ ) ;
138+ expect ( calls ) . toEqual ( [ ] ) ;
139+ } ) ;
140+
141+ test ( 'retains completed evidence and its marker while a replacement recorder writes the same path' , async ( ) => {
142+ const marker = JSON . stringify ( completedEvidence ( ) ) ;
143+ const calls : string [ ] = [ ] ;
144+ const runtime = await start ( {
145+ readManifest : async ( path : string ) =>
146+ path . startsWith ( '/sdcard' )
147+ ? { status : 'read' as const , contents : marker }
148+ : { status : 'missing' as const } ,
149+ inspect : async ( ) => 'foreign-writer' as const ,
150+ stop : async ( { pid } : { pid : string } ) => {
151+ calls . push ( `signal:${ pid } ` ) ;
152+ return 'already-missing' as const ;
153+ } ,
154+ remove : async ( path : string ) => {
155+ calls . push ( `artifact:${ path } ` ) ;
156+ return true ;
157+ } ,
158+ removeManifest : async ( ) => {
159+ calls . push ( 'manifest' ) ;
160+ return true ;
161+ } ,
162+ outputs : {
163+ prepare : async ( ) => {
164+ calls . push ( 'prepare' ) ;
165+ } ,
166+ } ,
167+ start : async ( ) => {
168+ calls . push ( 'launch' ) ;
169+ return recordingProcess ( '77' ) ;
170+ } ,
171+ } ) ;
172+ await expect ( runtime . screenRecordingStart ( newInput ( ) ) ) . rejects . toThrow (
173+ 'another recorder is writing' ,
174+ ) ;
175+ expect ( calls ) . toEqual ( [ ] ) ;
176+ } ) ;
177+
59178test ( 'retirement failure blocks launch and can succeed on a later retry' , async ( ) => {
60179 let marker = JSON . stringify ( completedEvidence ( ) ) ;
61180 let allowRemoval = false ;
0 commit comments