@@ -23,6 +23,7 @@ import * as HostProcess from "@t3tools/shared/HostProcess";
2323import * as DateTime from "effect/DateTime" ;
2424import * as Deferred from "effect/Deferred" ;
2525import * as Effect from "effect/Effect" ;
26+ import * as Fiber from "effect/Fiber" ;
2627import * as FileSystem from "effect/FileSystem" ;
2728import * as Layer from "effect/Layer" ;
2829import * as Path from "effect/Path" ;
@@ -201,6 +202,61 @@ describe("ProviderInstanceRegistry — multi-instance codex slice", () => {
201202 Layer . provideMerge ( layerBase ) ,
202203 ) ;
203204
205+ it . effect . each ( [ "replacement" , "removal" ] as const ) (
206+ "hides retired instances during reconcile (%s)" ,
207+ ( scenario ) =>
208+ Effect . gen ( function * ( ) {
209+ let closed = 0 ;
210+ const replacementStarted = yield * Deferred . make < void > ( ) ;
211+ const releaseReplacement = yield * Deferred . make < void > ( ) ;
212+ const driver : typeof CodexDriver = {
213+ ...CodexDriver ,
214+ create : ( input ) =>
215+ Effect . gen ( function * ( ) {
216+ if ( input . displayName === "Second" ) {
217+ yield * Deferred . succeed ( replacementStarted , undefined ) ;
218+ yield * Deferred . await ( releaseReplacement ) ;
219+ }
220+ const instance = yield * CodexDriver . create ( input ) ;
221+ yield * Effect . addFinalizer ( ( ) => Effect . sync ( ( ) => closed ++ ) ) ;
222+ return instance ;
223+ } ) ,
224+ } ;
225+ const retiredId = ProviderInstanceId . make ( "retired" ) ;
226+ const siblingId = ProviderInstanceId . make ( "sibling" ) ;
227+ const nextId = scenario === "replacement" ? retiredId : ProviderInstanceId . make ( "new" ) ;
228+ const entry = {
229+ driver : CodexDriver . driverKind ,
230+ enabled : false ,
231+ config : makeCodexConfig ( { } ) ,
232+ } ;
233+ const { registry, mutator } = yield * makeProviderInstanceRegistry ( {
234+ drivers : [ driver ] ,
235+ configMap : { [ retiredId ] : entry , [ siblingId ] : entry } ,
236+ } ) ;
237+ const sibling = yield * registry . getInstance ( siblingId ) ;
238+ const reconcile = yield * mutator
239+ . reconcile ( {
240+ [ siblingId ] : entry ,
241+ [ nextId ] : {
242+ ...entry ,
243+ displayName : "Second" ,
244+ config : makeCodexConfig ( { customModels : [ "replacement-model" ] } ) ,
245+ } ,
246+ } )
247+ . pipe ( Effect . forkChild ( { startImmediately : true } ) ) ;
248+ yield * Deferred . await ( replacementStarted ) ;
249+ expect ( closed ) . toBe ( 1 ) ;
250+ expect ( yield * registry . getInstance ( retiredId ) ) . toBeUndefined ( ) ;
251+ expect ( yield * registry . listInstances ) . toEqual ( [ sibling ] ) ;
252+ yield * Deferred . succeed ( releaseReplacement , undefined ) ;
253+ yield * Fiber . join ( reconcile ) ;
254+ expect ( ( yield * registry . getInstance ( nextId ) ) ?. displayName ) . toBe ( "Second" ) ;
255+ expect ( yield * registry . getInstance ( siblingId ) ) . toBe ( sibling ) ;
256+ expect ( closed ) . toBe ( 1 ) ;
257+ } ) . pipe ( Effect . provide ( layerTest ) ) ,
258+ ) ;
259+
204260 it . live ( "treats an explicit in-config enabled:false as disabling despite the envelope" , ( ) =>
205261 Effect . gen ( function * ( ) {
206262 const staleId = ProviderInstanceId . make ( "codex_stale" ) ;
0 commit comments