Decouple the config facade: a 222-module dependency cycle undermines the core-path guarantee
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Refactor
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- typescript
Research direction
Start with the runtime import graph and the import matcher used by tests/lab/core-lab-boundary.test.ts; the issue identifies several smaller cycles to inspect first. Then trace callers through src/config.ts and the owning src/config/ submodules. Done means the 222-module component is broken and src/config.ts is a thin compatibility re-export.
Written by the indexing model from the issue text.
Description
Area
Multiple areas
What are you trying to accomplish?
Keep the proxy module layering intact so that a user who configures one provider and one model executes no optional-subsystem code, and so that a change in one domain stays locally scoped instead of being able to reach almost every other module. Today a single strongly-connected component of 222 runtime modules means nearly any edit can reach nearly any other module, and the three files this repository explicitly protects from Compatibility Lab (src/router.ts, src/server/lifecycle.ts, src/server/responses/core.ts) all sit inside that component.
What prevents this today?
src/config.ts is a 386-line facade that re-exports the already-split src/config/ submodules (config/paths, config/subagent-models, config/salvage, config/diagnostics, config/load-degrade, config/provider-name, config/warn-memo and others). The implementations were physically moved out, but about 198 modules, the highest fan-in in the tree, still import everything through the single config entry rather than from the module that actually owns the concern. That is what keeps the cycle closed.
Measured on the runtime import graph (value imports only; import type is excluded because it is erased at runtime), across 1598 modules and 7655 edges:
- 6 strongly-connected components with more than one module; 238 modules sit inside a cycle.
- The largest component holds 222 modules: bridge, config, router, oauth/index, all 30 codex/catalog modules, providers/quota, server/lifecycle, service, storage and usage/log among them.
- Of 228 back edges, 92 (about 40 percent) terminate at config. The next highest target is codex/main-account with 26, then oauth/index with 19.
Because the facade is the single return target, splitting config further cannot break the cycle: the split already happened and the cycle survived it.
What should OpenCodex do?
Callers that need exactly one concern should import from the src/config submodule that owns it, or from a narrow per-concern facade, so that src/config.ts shrinks to a thin compatibility re-export and the import graph becomes layered instead of cyclic. Concretely, break the 222-module component: start with the small cycles, then move per-domain callers off the config facade. The Compatibility Lab decoupling already proved the slot-registration pattern works (devlog/_fin/260814_lab_core_decoupling/); the same technique has simply never been applied to config. Note that bun run structure:check passes today, so no structure doc is stale: the layering is just not recorded as an invariant anywhere.
Example usage or interface
Before, in about 198 modules:
import { getConfigDir, loadConfig } from "../config";
After, importing the owning module directly:
import { getConfigDir } from "../config/paths";
import { loadConfig } from "../config/load-degrade";
Smaller cycles worth fixing first, each verified by hand against the source:
- oauth/chatgpt and oauth/chatgpt-device (2 modules)
- server/claude-messages and server/messages-native (2 modules)
- claude/desktop-first-party to desktop-picker to intercept/picker-runtime to intercept/runtime (4 modules)
- client/machine-listener and client/runtime (2 modules)
One real cycle inside the 222-module component, every link source-checked: usage/log to codex/account-label to codex/main-account to codex/native-profile-startup to codex/native-profile-manager to codex/native-profile-processes to codex/app-server-processes to codex/catalog/parsing to oauth/index to providers/api-keys to providers/api-key-selection to router to routing/evaluator to routing/health to routing/history/indexer and back to usage/log.
Alternatives or workarounds
Leaving src/config.ts as the single entry point and documenting the cycle is the status quo. It works, but it means the guarantee that an optional subsystem stays off the core path rests on a foundation where every protected file is cyclically coupled to every other one. Splitting config into more submodules without changing call sites was already tried and did not break the cycle.
Additional context
Related but separate: issue 6297 (native Anthropic passthrough drops anthropic-ratelimit response headers), verified still present on main. That is a bug, not this refactor.
The numbers above come from walking the runtime import graph with the same import matcher that tests/lab/core-lab-boundary.test.ts uses; type-only imports are excluded.
Checks
- I searched existing issues and documentation.
- This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
- I removed secrets and personal data.
- Dominant language
- TypeScript
- Stars
- 16.9k
- Forks
- 1.3k
- Avg merge
- 9h 39m
- Merged PRs (30d)
- 633
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from lidge-jun/opencodex
-
catalog enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
lidge-jun/opencodex#7704 · 1 comment ·
Maintainers usually reply within 1 day
-
bug streaming
Difficulty 4/5 3-5 days Newbie friendliness 35/100
lidge-jun/opencodex#7720 · 2 comments ·
Maintainers usually reply within 1 day
-
bug gui platform
Difficulty 4/5 3-5 days Newbie friendliness 45/100
lidge-jun/opencodex#7716 · 1 comment ·
Maintainers usually reply within 1 day
-
account-pool provider provider-compatibility
Difficulty 4/5 3-5 days Newbie friendliness 45/100
Maintainers usually reply within 1 day
-
catalog enhancement
Difficulty 5/5 Over a week Newbie friendliness 30/100
lidge-jun/opencodex#7705 · 1 comment ·
Maintainers usually reply within 1 day
All issues in lidge-jun/opencodex
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
MoonshotAI/kimi-code#4398 ·
Maintainers usually reply within 1 day
-
Xiaomi Mimo语音识别服务异常Openpending triage
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day
-
package-lock.json : les binaires natifs macOS et Linux de unrs-resolver ont disparu depuis #1823Openbug dependencies
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
AxaFrance/design-system#2064 ·
Maintainers usually reply within 3 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 85/100