@@ -201,6 +201,47 @@ describe("live daemon registry recovery", () => {
201201 expect ( metadata ( root , name ) . lastAttachAt ) . toBe ( mutated . lastAttachAt ) ;
202202 } ) ;
203203
204+ it ( "fails closed when metadata publication stops after revision advancement" , ( ) => {
205+ const root = makeRoot ( ) ;
206+ const name = "interrupted-revision" ;
207+ startProvider ( root , name ) ;
208+ const before = metadata ( root , name ) ;
209+ const snapshot = writeSnapshot ( root , name , before ) ;
210+ const mutated : SessionMetadata = {
211+ ...before ,
212+ tags : { role : "must-not-publish" } ,
213+ } ;
214+
215+ expect ( ( ) => writeMetadata ( name , mutated , {
216+ afterRecoveryRevisionPublished : ( ) => {
217+ throw new Error ( "stop before metadata publication" ) ;
218+ } ,
219+ } ) ) . toThrow ( "stop before metadata publication" ) ;
220+
221+ // Publication stopped in the exact revision-before-metadata window: the
222+ // old metadata remains visible, but its signed revision is no longer
223+ // authoritative.
224+ expect ( metadata ( root , name ) ) . toEqual ( before ) ;
225+ const advanced = readBoundedJson < RecoveryRevision > (
226+ recoveryRevisionPath ( root , name ) ,
227+ ) ;
228+ expect ( verifyRecoveryRevision ( before . recovery ! . secret , advanced ) ) . toBe ( true ) ;
229+ expect ( advanced . metadataRevision ) . toBe ( metadataRevision ( mutated ) ) ;
230+ expect ( advanced . metadataRevision ) . not . toBe ( before . recovery ! . metadataRevision ) ;
231+
232+ unlinkRegistry ( root , name ) ;
233+ for ( let attempt = 0 ; attempt < 2 ; attempt ++ ) {
234+ const refused = run ( root , [ "recover" , name , "--snapshot" , snapshot ] ) ;
235+ expect ( refused . status ) . not . toBe ( 0 ) ;
236+ expect ( fs . existsSync ( path . join ( root , `${ name } .sock` ) ) ) . toBe ( false ) ;
237+ expect ( fs . existsSync ( path . join ( root , `${ name } .pid` ) ) ) . toBe ( false ) ;
238+ expect ( fs . existsSync ( path . join ( root , `${ name } .json` ) ) ) . toBe ( false ) ;
239+ expect ( readBoundedJson < RecoveryRevision > (
240+ recoveryRevisionPath ( root , name ) ,
241+ ) ) . toEqual ( advanced ) ;
242+ }
243+ } , 20_000 ) ;
244+
204245 it ( "rebinds the original daemon while preserving provider and attached client" , async ( ) => {
205246 const root = makeRoot ( ) ;
206247 const name = "positive" ;
0 commit comments