Area
Proxy and routing
What are you trying to accomplish?
Maintain provider routing and API-key failover without coupling the router's selection-snapshot helper to the module that performs persisted selection changes and route resolution. The scope is a small dependency cleanup with unchanged routing and key-selection behavior.
What prevents this today?
At dev commit 76436a3ee221d577cc94d6c10544c03bb1f9d9db, the following direct runtime cycle exists:
src/router.ts
-> src/providers/api-key-selection.ts
-> src/router.ts
The helper only derives entryId, reference, and revision from its provider argument. It does not need route resolution or persisted mutation. Keeping it in the larger module creates an unnecessary direct cycle.
This is a maintainability issue. No current startup crash or guaranteed module-order failure is claimed.
What should OpenCodex do?
Remove this specific direct cycle while preserving current routing, selection snapshots, and failover behavior:
- Put the snapshot helper in a small module whose dependencies are type-only, or use an equivalent existing dependency-neutral location.
- Have the router and API-key-selection module consume that helper.
- Preserve the existing helper export if it is moved, so existing imports remain compatible.
- Keep
routedProviderConfig imports in the selection and failover modules within this scope.
- Add focused coverage for the helper's behavior and its runtime dependency boundary, including that the router no longer imports the stateful API-key-selection module directly.
This issue does not require making the entire router dependency graph acyclic or banning every provider-to-router dependency.
Example usage or interface
Before:
// src/router.ts
import { captureProviderApiKeySelection } from "./providers/api-key-selection";
Possible after:
// src/router.ts
import { captureProviderApiKeySelection } from "./providers/api-key-selection-capture";
// src/providers/api-key-selection.ts
import { captureProviderApiKeySelection } from "./api-key-selection-capture";
export { captureProviderApiKeySelection } from "./api-key-selection-capture";
import { routedProviderConfig } from "../router";
For a provider with a selected pool key and a selection revision, the captured entry ID, key reference, and revision must remain identical. Missing pool entries and optional fields must retain their existing behavior.
Alternatives or workarounds
Extracting routedProviderConfig itself would move a broader set of routing dependencies and is not necessary to remove this direct edge.
A global router cycle test would exceed this issue's scope. An in-memory graph check applying only the proposed router import change still found this other static cycle:
router -> routing/evaluator -> routing/quota -> providers/quota
-> codex/auth-api -> lib/state-store-registrations -> router
The return edge is state-store-registrations.ts:42. That broader dependency work should be evaluated separately.
Additional context
The current direct cycle was verified from source and a static runtime-import graph using Bun's TypeScript transpiler. The hypothetical extraction was evaluated in memory; no source change is included.
On this checkout, all 17 tests in tests/lab/core-lab-boundary.test.ts passed. The focused key-failover and provider-key-store test files could not load because zod/v4 was missing, so their success is not claimed. They should be run alongside the new focused coverage when implementing this change:
bun test tests/adapters/key-failover.test.ts tests/providers/provider-key-store.test.ts tests/lab/core-lab-boundary.test.ts
Checks
Area
Proxy and routing
What are you trying to accomplish?
Maintain provider routing and API-key failover without coupling the router's selection-snapshot helper to the module that performs persisted selection changes and route resolution. The scope is a small dependency cleanup with unchanged routing and key-selection behavior.
What prevents this today?
At
devcommit76436a3ee221d577cc94d6c10544c03bb1f9d9db, the following direct runtime cycle exists:captureProviderApiKeySelection.routedProviderConfigand defines the small snapshot helper.The helper only derives
entryId,reference, andrevisionfrom its provider argument. It does not need route resolution or persisted mutation. Keeping it in the larger module creates an unnecessary direct cycle.This is a maintainability issue. No current startup crash or guaranteed module-order failure is claimed.
What should OpenCodex do?
Remove this specific direct cycle while preserving current routing, selection snapshots, and failover behavior:
routedProviderConfigimports in the selection and failover modules within this scope.This issue does not require making the entire router dependency graph acyclic or banning every provider-to-router dependency.
Example usage or interface
Before:
Possible after:
For a provider with a selected pool key and a selection revision, the captured entry ID, key reference, and revision must remain identical. Missing pool entries and optional fields must retain their existing behavior.
Alternatives or workarounds
Extracting
routedProviderConfigitself would move a broader set of routing dependencies and is not necessary to remove this direct edge.A global router cycle test would exceed this issue's scope. An in-memory graph check applying only the proposed router import change still found this other static cycle:
The return edge is state-store-registrations.ts:42. That broader dependency work should be evaluated separately.
Additional context
The current direct cycle was verified from source and a static runtime-import graph using Bun's TypeScript transpiler. The hypothetical extraction was evaluated in memory; no source change is included.
On this checkout, all 17 tests in
tests/lab/core-lab-boundary.test.tspassed. The focused key-failover and provider-key-store test files could not load becausezod/v4was missing, so their success is not claimed. They should be run alongside the new focused coverage when implementing this change:bun test tests/adapters/key-failover.test.ts tests/providers/provider-key-store.test.ts tests/lab/core-lab-boundary.test.tsChecks