Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions apps/server/src/provider/ProviderInstanceRegistry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderL
import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions";
import * as HostProcess from "@t3tools/shared/HostProcess";
import * as DateTime from "effect/DateTime";
import * as Deferred from "effect/Deferred";
import * as Effect from "effect/Effect";
import * as Fiber from "effect/Fiber";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
Expand Down Expand Up @@ -192,6 +194,61 @@ describe("ProviderInstanceRegistry — multi-instance codex slice", () => {
Layer.provideMerge(layerBase),
);

it.effect.each(["replacement", "removal"] as const)(
"hides retired instances during reconcile (%s)",
(scenario) =>
Effect.gen(function* () {
let closed = 0;
const replacementStarted = yield* Deferred.make<void>();
const releaseReplacement = yield* Deferred.make<void>();
const driver: typeof CodexDriver = {
...CodexDriver,
create: (input) =>
Effect.gen(function* () {
if (input.displayName === "Second") {
yield* Deferred.succeed(replacementStarted, undefined);
yield* Deferred.await(releaseReplacement);
}
const instance = yield* CodexDriver.create(input);
yield* Effect.addFinalizer(() => Effect.sync(() => closed++));
return instance;
}),
};
const retiredId = ProviderInstanceId.make("retired");
const siblingId = ProviderInstanceId.make("sibling");
const nextId = scenario === "replacement" ? retiredId : ProviderInstanceId.make("new");
const entry = {
driver: CodexDriver.driverKind,
enabled: false,
config: makeCodexConfig({}),
};
const { registry, mutator } = yield* makeProviderInstanceRegistry({
drivers: [driver],
configMap: { [retiredId]: entry, [siblingId]: entry },
});
const sibling = yield* registry.getInstance(siblingId);
const reconcile = yield* mutator
.reconcile({
[siblingId]: entry,
[nextId]: {
...entry,
displayName: "Second",
config: makeCodexConfig({ customModels: ["replacement-model"] }),
},
})
.pipe(Effect.forkChild({ startImmediately: true }));
yield* Deferred.await(replacementStarted);
expect(closed).toBe(1);
expect(yield* registry.getInstance(retiredId)).toBeUndefined();
expect(yield* registry.listInstances).toEqual([sibling]);
yield* Deferred.succeed(releaseReplacement, undefined);
yield* Fiber.join(reconcile);
expect((yield* registry.getInstance(nextId))?.displayName).toBe("Second");
expect(yield* registry.getInstance(siblingId)).toBe(sibling);
expect(closed).toBe(1);
}).pipe(Effect.provide(layerTest)),
);

it.live("treats an explicit in-config enabled:false as disabling despite the envelope", () =>
Effect.gen(function* () {
const staleId = ProviderInstanceId.make("codex_stale");
Expand Down
8 changes: 7 additions & 1 deletion apps/server/src/provider/ProviderInstanceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,13 @@ const makeReconcile = <R>(input: {
replacedIds.add(instanceId);
}
}
for (const id of [...removedIds, ...replacedIds]) {
const retiredIds = [...removedIds, ...replacedIds];
if (retiredIds.length > 0) {
const remaining = new Map(previousEntries);
for (const id of retiredIds) remaining.delete(id);
yield* Ref.set(state.entries, remaining);
}
for (const id of retiredIds) {
const live = previousEntries.get(id);
if (live) {
yield* Scope.close(live.scope, Exit.void).pipe(Effect.ignore);
Expand Down
Loading