Thanks to visit codestin.com
Credit goes to github.com

Skip to content

refactor(lib): break import cycles and enforce no-cycle in lint - #541

Merged
ndycode merged 3 commits into
mainfrom
claude/audit-21-no-cycle
Jun 10, 2026
Merged

ndycode merged 3 commits into
mainfrom
claude/audit-21-no-cycle

Conversation

@ndycode

@ndycode ndycode commented Jun 10, 2026

Copy link
Copy Markdown
Owner

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).

Stacked on #530 (storage public-types) — its public-types.ts is the foundation. Merge that first.

Cycles broken, by technique

  1. Import retargets (19 cycles) — all 22 from "../storage.js" back-imports inside lib/storage/* now point at the layer below (./public-types.js, ./backup-metadata.js, sibling modules), with import type on type-only edges.
  2. Verbatim type movesFlaggedAccount*V1, BackupMetadata/RestoreAssessment/RestoreReason out of the facade; Workspace out of accounts.ts (it was the public-types → accounts back-edge); ModelFamily/MODEL_FAMILIES from prompts/codex.ts to the leaf request/helpers/model-map.ts. Originals re-export, so no importer changes.
  3. Value movesaveAccountsWithRetry/isRetryableStorageWriteError to new lib/storage/save-retry.ts, fixing the accounts → codex-manager layering violation (forecast-report-shared re-exports).

Enforcement

  • import-x/no-cycle: ["error", { maxDepth: Infinity, ignoreExternal: true }] scoped to index.ts + lib/**/*.ts, with the TS resolver. New devDeps: [email protected] + [email protected] (flat-config native; lockfile diff is additions-only — the rollup libc entries the container npm strips were restored byte-identical).
  • Non-obvious config note, documented in the eslint config: the rule silently no-ops on .ts files without "import-x/extensions": [".ts"] — import-x's traversal defaults to .js only. Verified the rule actually fires by temporarily reintroducing a cycle (eslint errored), then removing it.
  • lib/AGENTS.md conventions now document the types/constants → storage → accounts → runtime → manager/CLI layering.

Validation

  • npx madge --circular lib/ index.ts0 cycles (was 23)
  • Full npm run lint (with the new rule active) + npm run typecheck
  • 22 storage/accounts/codex-cli suites (596 tests): failure lists identical to base via stash diff (all known environment-only EACCES)
  • Independently re-verified: madge 0, full lint, storage-parser + accounts 177/177

Risk / 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 via import-x/no-cycle. all public-facade surfaces are preserved via re-exports; no runtime behavior changes.

  • moves AccountMetadataV3, AccountStorageV3, FlaggedAccount*, Workspace, CooldownReason, RateLimitStateV3 to lib/storage/public-types.ts so sub-modules no longer back-import the storage.ts facade; moves BackupMetadata/RestoreAssessment to backup-metadata.ts; moves ModelFamily/MODEL_FAMILIES to the leaf model-map.ts.
  • extracts saveAccountsWithRetry/isRetryableStorageWriteError from codex-manager/forecast-report-shared.ts into the new lib/storage/save-retry.ts, fixing the accounts → codex-manager layering violation; both original export paths re-export from the new location.
  • wires up eslint-plugin-import-x + eslint-import-resolver-typescript in flat config, scoped to index.ts and lib/**/*.ts with \"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

Filename Overview
lib/storage/save-retry.ts new module housing saveAccountsWithRetry/isRetryableStorageWriteError; verbatim logic from forecast-report-shared.ts; retries EBUSY/EPERM only; no dedicated unit test
lib/storage/public-types.ts new leaf module collecting current-version storage shapes; self-contained imports (model-map, types); no logic, pure type definitions
eslint.config.js adds import-x/no-cycle rule scoped to lib/**; extensions setting correctly overridden to ['.ts'] so ExportMap traversal includes TypeScript files; TypeScript resolver wired in
lib/storage/backup-metadata.ts BackupMetadata/RestoreAssessment/RestoreReason moved here from storage.ts; re-exported from facade; restore-metadata.ts retains its own local RestoreReason (pre-existing duplication)
lib/codex-manager/forecast-report-shared.ts save-retry functions moved out; re-exported here to preserve historical surface; no behavior change
lib/storage/migrations.ts AccountMetadataV3/AccountStorageV3 moved to public-types; now correctly imports ModelFamily from model-map.ts (leaf) instead of prompts/codex.ts; V1 shapes remain here
lib/storage.ts facade updated: removes AccountMetadataV1/AccountStorageV1 from exports (no external consumers found), adds FlaggedAccount*/BackupMetadata/RestoreAssessment to explicit export block
lib/accounts.ts saveAccountsWithRetry now imported from storage/save-retry (breaks manager→accounts cycle); Workspace moved to public-types and re-exported; ModelFamily from model-map
lib/request/helpers/model-map.ts ModelFamily/MODEL_FAMILIES moved here from prompts/codex.ts; prompts/codex.ts re-exports; now importable from low-level storage/schema modules without creating upward cycles

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 --> APW
Loading

Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
lib/storage/save-retry.ts:1-33
**no direct vitest coverage for the new module**

`save-retry.ts` is now a standalone shared utility but has no test file. the retry cap (`attempt >= 3` → 4 total attempts), the exponential delay formula (`10 * 2 ** attempt` → 10/20/40 ms), and the exact set of retryable codes are all observable contracts. indirect coverage exists via `codex-manager-cli.test.ts` and `issue-474-pin-safety.test.ts`, but those tests go through many layers. a direct unit test would catch a future change to the retry set or cap without having to exercise the full manager stack.

also: `RETRYABLE_STORAGE_WRITE_CODES` is `["EBUSY", "EPERM"]` — on windows, storage-path writes (e.g. under `AppData\`) can fail with `EACCES` when a competing process holds an exclusive lock. that code isn't retried today. this is verbatim from the original location, so not a regression here, but now that the set is a named shared constant it's a natural place to add it.

Reviews (2): Last reviewed commit: "refactor(lib): import MODEL_FAMILIES fro..." | Re-trigger Greptile

claude added 2 commits June 10, 2026 08:49
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
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@ndycode, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4ed84be2-7e0a-4d65-a77e-1d787bd26442

📥 Commits

Reviewing files that changed from the base of the PR and between 98d9819 and 1da22ba.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (38)
  • eslint.config.js
  • lib/AGENTS.md
  • lib/accounts.ts
  • lib/codex-cli/sync.ts
  • lib/codex-manager/account-pool-write.ts
  • lib/codex-manager/forecast-report-shared.ts
  • lib/prompts/codex.ts
  • lib/request/helpers/model-map.ts
  • lib/schemas.ts
  • lib/storage.ts
  • lib/storage/account-persistence.ts
  • lib/storage/account-port.ts
  • lib/storage/account-save-entry.ts
  • lib/storage/account-save.ts
  • lib/storage/backup-metadata-builder.ts
  • lib/storage/backup-metadata.ts
  • lib/storage/backup-restore.ts
  • lib/storage/fixture-guards.ts
  • lib/storage/flagged-load-entry.ts
  • lib/storage/flagged-save-entry.ts
  • lib/storage/flagged-storage-file.ts
  • lib/storage/flagged-storage-io.ts
  • lib/storage/flagged-storage.ts
  • lib/storage/import-export.ts
  • lib/storage/migrations.ts
  • lib/storage/named-backups-entry.ts
  • lib/storage/project-migration.ts
  • lib/storage/public-types.ts
  • lib/storage/restore-assessment.ts
  • lib/storage/restore-backup-entry.ts
  • lib/storage/restore-metadata.ts
  • lib/storage/restore.ts
  • lib/storage/save-retry.ts
  • lib/storage/snapshot-inspectors.ts
  • lib/storage/storage-parser.ts
  • lib/storage/transactions.ts
  • package.json
  • test/codex-manager-account-pool-write.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/audit-21-no-cycle
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/audit-21-no-cycle

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread lib/storage/migrations.ts Outdated
…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
@ndycode
ndycode merged commit f4e07d9 into main Jun 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants