refactor(lib): break import cycles and enforce no-cycle in lint - #541
Conversation
Audit roadmap section 4.1.4 (storage type hygiene). Types/exports only; zero runtime behavior change. - Add lib/storage/public-types.ts holding the current-version shapes that external consumers and lib/index.ts need: CooldownReason, RateLimitStateV3, AccountMetadataV3, AccountStorageV3 (moved verbatim from lib/storage/migrations.ts). The lib/storage.ts facade re-exports them as before, so the published ./storage subpath surface for these names is unchanged. - lib/storage/migrations.ts now contains only the historical v1 shapes (AccountMetadataV1, AccountStorageV1) plus migrateV1ToV3, importing the current shapes from public-types.ts. - Drop AccountMetadataV1 and AccountStorageV1 from the facade's export list: nothing outside lib/storage/migrations.ts (and the facade's own internal migrateV1ToV3 call) imports them, and test/public-api-contract.test.ts pins only runtime exports and package subpaths, not these types. - Update the direct deep importers of AccountMetadataV3 to the new home: lib/codex-manager/account-pool-write.ts and test/codex-manager-account-pool-write.test.ts. - Note: the audit proposed also removing RateLimitStateV3 from the facade, but that premise is stale; it is part of the current v3 shape (AccountMetadataV3.rateLimitResetTimes) and is consumed via the facade by lib/accounts.ts, so it stays public in public-types.ts. https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
madge reported 23 circular chains on the base branch; all are now broken with zero public-surface and zero behavior change (audit roadmap 4.1.6, stacked on the storage type-hygiene branch, PR #530): - Facade back-imports (cycles 2-14, 16-22): every lib/storage/* submodule that imported types back from "../storage.js" now imports them from the layer below — AccountStorageV3/AccountMetadataV3/FlaggedAccountStorageV1 from "./public-types.js", BackupMetadata/RestoreAssessment from "./backup-metadata.js" (definitions moved there verbatim next to their constituent section/snapshot types), NamedBackupSummary from its defining module "./named-backups.js". FlaggedAccountMetadataV1/FlaggedAccountStorageV1 moved verbatim into storage/public-types.ts; lib/storage.ts re-exports everything it exported before. - schemas cycle (1): ModelFamily/MODEL_FAMILIES moved verbatim from lib/prompts/codex.ts to the leaf module lib/request/helpers/model-map.ts (which already defined PromptModelFamily); prompts/codex.ts re-exports them, schemas.ts and storage/public-types.ts import the leaf directly. - accounts -> codex-cli -> storage cycle (15): Workspace moved verbatim from lib/accounts.ts into storage/public-types.ts (it is part of the persisted storage shape) with a re-export from accounts.ts, and codex-cli/sync.ts retargets its type-only AccountStorageV3 import to storage/public-types.ts. - accounts -> codex-manager -> forecast cycle (23): the shared VALUE helpers saveAccountsWithRetry/isRetryableStorageWriteError moved verbatim to a new lib/storage/save-retry.ts; forecast-report-shared.ts re-exports them for its codex-manager consumers and accounts.ts imports the lower module. Enforcement: eslint-plugin-import-x (flat-config native, ESLint 10 compatible) + eslint-import-resolver-typescript added as devDependencies; "import-x/no-cycle": ["error", { maxDepth: Infinity, ignoreExternal: true }] scoped to lib/** and index.ts only (scripts/ and test/ stay out of scope). "import-x/extensions": [".ts"] is required — without it import-x silently skips .ts files during cycle traversal. Verified the rule actually fires by temporarily re-adding a lib/storage/transactions.ts -> ../storage.js import (eslint reported import-x/no-cycle) and then removing it. Documented the dependency direction (types/constants -> storage -> accounts -> runtime -> manager/CLI) in lib/AGENTS.md conventions. Verification: madge --circular lib/ index.ts reports 0 cycles; typecheck and full lint pass; storage/accounts/codex-cli suites show the identical 41 known environment-only EACCES failures as the base branch (555 passed on both). Lockfile diff is additions-only for the two new dev packages (rollup optional-dep "libc" entries restored byte-identical). https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
Warning Review limit reached
More reviews will be available in 10 minutes and 40 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (38)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…orage Review follow-up: migrations.ts (and the same pattern in accounts.ts and codex-cli/sync.ts) imported MODEL_FAMILIES/ModelFamily via the prompts/codex.ts re-export, which sits above the storage layer in the documented hierarchy. Retargeted to the model-map leaf the symbols were moved to, so a future back-import into the storage layer cannot silently re-open a cycle through these edges. https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
Summary
Breaks all 23 import cycles in
lib/and enforces zero-cycles with lint — audit roadmap §4.1.6 (docs/audits/AUDIT_2026-06-10.md, PR #522). The audit's premise ("no cycles exist today") was wrong — madge reported 23 chains on main — so this PR first makes it true, then locks it. Zero behavior change; the storage facade surface is byte-for-byte unchanged (every moved name re-exported).Cycles broken, by technique
from "../storage.js"back-imports insidelib/storage/*now point at the layer below (./public-types.js,./backup-metadata.js, sibling modules), withimport typeon type-only edges.FlaggedAccount*V1,BackupMetadata/RestoreAssessment/RestoreReasonout of the facade;Workspaceout ofaccounts.ts(it was thepublic-types → accountsback-edge);ModelFamily/MODEL_FAMILIESfromprompts/codex.tsto the leafrequest/helpers/model-map.ts. Originals re-export, so no importer changes.saveAccountsWithRetry/isRetryableStorageWriteErrorto newlib/storage/save-retry.ts, fixing theaccounts → codex-managerlayering violation (forecast-report-shared re-exports).Enforcement
import-x/no-cycle: ["error", { maxDepth: Infinity, ignoreExternal: true }]scoped toindex.ts+lib/**/*.ts, with the TS resolver. New devDeps:[email protected]+[email protected](flat-config native; lockfile diff is additions-only — the rolluplibcentries the container npm strips were restored byte-identical)..tsfiles without"import-x/extensions": [".ts"]— import-x's traversal defaults to.jsonly. Verified the rule actually fires by temporarily reintroducing a cycle (eslint errored), then removing it.lib/AGENTS.mdconventions now document thetypes/constants → storage → accounts → runtime → manager/CLIlayering.Validation
npx madge --circular lib/ index.ts→ 0 cycles (was 23)npm run lint(with the new rule active) +npm run typecheckRisk / Rollback
Re-export-preserving moves + a lint rule; revert the single commit. The rule prevents the 23-cycle situation from regrowing.
https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
Generated by Claude Code
note: greptile review for oc-chatgpt-multi-auth. cite files like
lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.Greptile Summary
breaks 23 import cycles in
lib/and enforces zero-cycles viaimport-x/no-cycle. all public-facade surfaces are preserved via re-exports; no runtime behavior changes.AccountMetadataV3,AccountStorageV3,FlaggedAccount*,Workspace,CooldownReason,RateLimitStateV3tolib/storage/public-types.tsso sub-modules no longer back-import thestorage.tsfacade; movesBackupMetadata/RestoreAssessmenttobackup-metadata.ts; movesModelFamily/MODEL_FAMILIESto the leafmodel-map.ts.saveAccountsWithRetry/isRetryableStorageWriteErrorfromcodex-manager/forecast-report-shared.tsinto the newlib/storage/save-retry.ts, fixing theaccounts → codex-managerlayering violation; both original export paths re-export from the new location.eslint-plugin-import-x+eslint-import-resolver-typescriptin flat config, scoped toindex.tsandlib/**/*.tswith\"import-x/extensions\": [\".ts\"]to ensure the plugin's ExportMap traversal includes TypeScript files.Confidence Score: 5/5
safe to merge — pure structural refactor with all original export surfaces preserved via re-exports and no logic changes
every moved type and function is re-exported from its original location; the new no-cycle lint rule was author-verified by reintroducing a cycle; 596 tests pass with identical failure lists; no runtime behavior changed
lib/storage/save-retry.ts — new standalone module with no direct unit test
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD subgraph "types/constants (leaf)" MM["request/helpers/model-map.ts\nModelFamily, MODEL_FAMILIES"] UT["utils.ts\nsleep"] TY["types.ts\nAccountIdSource …"] end subgraph "storage layer" PT["storage/public-types.ts\nWorkspace, AccountMetadataV3\nAccountStorageV3, FlaggedAccount*\nCooldownReason, RateLimitStateV3"] BM["storage/backup-metadata.ts\nBackupMetadata, RestoreAssessment"] SR["storage/save-retry.ts\nsaveAccountsWithRetry\nisRetryableStorageWriteError"] MG["storage/migrations.ts\nAccountStorageV1 (legacy)"] ST["storage.ts (facade)\nre-exports all above"] end subgraph "accounts layer" AC["accounts.ts\nWorkspace re-export"] end subgraph "manager/CLI layer" FRS["codex-manager/forecast-report-shared.ts\nre-exports save-retry"] APW["codex-manager/account-pool-write.ts"] end MM --> PT TY --> PT PT --> SR UT --> SR PT --> MG MM --> MG BM --> ST PT --> ST MG --> ST SR --> AC ST --> AC SR --> FRS ST --> FRS AC --> APWPrompt To Fix All With AI
Reviews (2): Last reviewed commit: "refactor(lib): import MODEL_FAMILIES fro..." | Re-trigger Greptile