Skip to content

release: align main with published 1.2.2#336

Merged
ndycode merged 27 commits intomainfrom
release/integration-1.2.2
Apr 1, 2026
Merged

release: align main with published 1.2.2#336
ndycode merged 27 commits intomainfrom
release/integration-1.2.2

Conversation

@ndycode
Copy link
Copy Markdown
Owner

@ndycode ndycode commented Apr 1, 2026

What Changed

  • aligns main with the already-published codex-multi-auth@1.2.2 release integration
  • carries the validated account-pool health, refresh persistence, guardian, forecast/report, and runtime identity fixes from release/integration-1.2.2
  • supersedes the individual main-target PR stack #323-#334

Why

  • 1.2.2 was published from the validated integration branch before the PR stack landed on main
  • this catch-up PR makes main match the shipped npm package before tagging and GitHub release creation

Validation

  • npm run clean:repo:check
  • npm run lint
  • npm run typecheck
  • npm run build
  • npm test -- --pool=threads --maxWorkers=1
  • npm pack --dry-run
  • full suite passed: 221/221 files, 3221/3221 tests

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

this catch-up pr aligns main with the already-published codex-multi-auth@1.2.2 release by merging the validated release/integration-1.2.2 branch. it carries a substantial set of correctness and reliability fixes across the account pool, token refresh path, and circuit-breaker integration.

key changes:

  • transactional refresh persistenceAccountManager.commitRefreshedAuth replaces the previous fire-and-forget updateFromAuth + clearAuthFailures + saveToDiskDebounced triple with a single withAccountStorageTransaction call that rolls back live in-memory state if the disk write fails
  • circuit-breaker integration — account selection, consumeToken, recordSuccess, and recordFailure all now route through the per-account circuit breaker; healthy-count requires circuitState === "closed"
  • runtime tracker keysHealthScoreTracker and TokenBucketTracker accept string | number tracker keys so accounts keep consistent health/token state across index reassignments
  • proactive-refresh ordering fix — missing access token now triggers refresh before checking for a missing expiry
  • guardian hardeningRefreshGuardian.tick filters cooling-down accounts, graduates auth-failure penalties by streak, delegates persistence to commitRefreshedAuth
  • retryable error classification — refresh failures and auth-setter errors are classified as retryable vs non-retryable, driving different cooldown policies
  • rate-limit scoping fix — token-level and concurrency-level failures no longer pollute the family-base quota key
  • windows filesystem safetyEAGAIN/EBUSY/EPERM retries present in all new persistence paths

two p2 style notes: duplicate persistence helpers in forecast.ts/report.ts, and missing vitest coverage for the consumeToken open-circuit token-refund path.

Confidence Score: 5/5

safe to merge — all remaining findings are p2 style/coverage suggestions with no correctness impact

the core transactional refresh, circuit-breaker wiring, and guardian hardening all look correctly implemented; rollback on persist failure is properly scoped; the only open items are a duplicate-code extraction opportunity and one missing vitest case for the token-refund-on-open-circuit path, both p2

lib/codex-manager/commands/forecast.ts and lib/codex-manager/commands/report.ts — duplicate persistence helpers should be extracted before the two copies diverge

Important Files Changed

Filename Overview
index.ts replaces inline updateFromAuth+clearAuthFailures+saveToDiskDebounced triples with commitRefreshedAuth; adds retryable-refresh error branching with session-affinity forget and correct failure policy routing
lib/accounts.ts large refactor: adds commitRefreshedAuth transactional write, circuit-breaker integration in consumeToken/recordSuccess/recordFailure, runtime tracker keys, and identity resolution helpers; rollback on persist failure is correctly implemented
lib/codex-manager/commands/forecast.ts adds token-refresh persistence to the forecast loop with retry; helpers are functionally correct but duplicated verbatim from report.ts
lib/codex-manager/commands/report.ts same refresh-persistence helpers as forecast.ts, copy-pasted; logic itself is correct
lib/circuit-breaker.ts adds isAvailable(), getTimeUntilAvailable(), and half-open probe-window reset; logic looks correct and aligns with the new consumeToken usage
lib/refresh-guardian.ts replaces inline applyRefreshResult with commitRefreshedAuth transaction, graduates auth-failure penalties correctly, adds eligibleSnapshot filtering to skip already-cooling accounts
lib/rotation.ts generalises tracker keys from number to TrackerKey = number
lib/request/fetch-helpers.ts adds isRetryableRefreshFailure and isRetryableAuthSetterError to propagate retryable classification through CodexAuthError; isRetryableAuthSetterError duplicates the EAGAIN/EBUSY/EPERM logic already in accounts.ts
lib/health.ts healthy-count now requires circuitState === closed, correctly excluding open/half-open accounts from the healthy pool
lib/storage/identity.ts extracts getIdentityKeyFromRef to support a runtime variant (getRuntimeAccountIdentityKey) that omits the refresh-token hash fallback for in-memory tracking
lib/proactive-refresh.ts fixes check ordering so a missing access token triggers refresh regardless of expiry state; adds onResult callback for guardian integration

Sequence Diagram

sequenceDiagram
    participant Plugin as index.ts
    participant AM as AccountManager
    participant ST as StorageTransaction
    participant CB as CircuitBreaker
    participant Disk as Storage

    Plugin->>AM: commitRefreshedAuth(source, auth)
    AM->>ST: acquire storage lock
    ST-->>AM: (current, persist)
    AM->>AM: buildStorageSnapshot()
    AM->>AM: findAccountIndexByIdentity()
    AM->>AM: patch storedAccount tokens and clear cooldown
    AM->>AM: getAccountByIdentity() to get liveAccount
    AM->>AM: snapshot previousLiveAccountState
    AM->>AM: updateFromAuth(liveAccount, auth)
    AM->>AM: enable liveAccount and clear auth failures
    AM->>ST: persist(nextStorage)
    ST->>Disk: writeFile accounts.json
    alt persist succeeds
        Disk-->>ST: ok
        ST-->>AM: committed
        AM->>CB: recordSuccess()
        AM-->>Plugin: liveAccount
    else persist fails
        Disk-->>ST: error
        ST-->>AM: throw
        AM->>AM: revert liveAccount to previousLiveAccountState
        AM-->>Plugin: throw CodexAuthError retryable flag set
    end
Loading

Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: lib/codex-manager/commands/forecast.ts
Line: 120-188

Comment:
**duplicated persistence helpers across forecast and report**

`saveAccountsWithRetry`, `AccountIdentityMatch`, `RefreshedAccountPatch`, `applyRefreshedAccountPatch`, and `persistRefreshedAccountPatch` are copy-pasted verbatim in `lib/codex-manager/commands/report.ts` (lines 258–327). any bug fix or behaviour change to one must be manually mirrored in the other. worth extracting to a shared internal module (e.g. `lib/codex-manager/commands/refresh-persistence.ts`) before these diverge.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: lib/accounts.ts
Line: 730-746

Comment:
**`consumeToken` has no vitest coverage for the open-circuit refund path**

the new branch where a token is successfully consumed from the bucket and then refunded because `canExecute()` throws (`CircuitOpenError`) is not exercised by any test in `test/accounts.test.ts` or `test/circuit-breaker.test.ts`. a focused test (open circuit → `consumeToken` → returns `false`, token bucket unchanged) would lock down the invariant.

additionally: `canExecute()` increments `halfOpenAttempts` as a side-effect. calling `consumeToken` on a half-open account therefore uses a probe slot at token-consumption time rather than at request-execution time. with very low `halfOpenMaxAttempts` configs this could exhaust probe slots before any real request fires — worth a note in the method doc or a concurrency test.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "chore: finalize 1.2.2 release integratio..." | Re-trigger Greptile

ndycode and others added 27 commits April 1, 2026 05:49
# Conflicts:
#	test/accounts-load-from-disk.test.ts
# Conflicts:
#	lib/refresh-guardian.ts
#	test/refresh-guardian.test.ts
@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
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 45e274ea-734c-4507-b773-e2585f01815f

📥 Commits

Reviewing files that changed from the base of the PR and between 53488ef and 22db10b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (27)
  • index.ts
  • lib/accounts.ts
  • lib/circuit-breaker.ts
  • lib/codex-manager.ts
  • lib/codex-manager/commands/forecast.ts
  • lib/codex-manager/commands/report.ts
  • lib/health.ts
  • lib/parallel-probe.ts
  • lib/proactive-refresh.ts
  • lib/refresh-guardian.ts
  • lib/request/fetch-helpers.ts
  • lib/rotation.ts
  • lib/storage/identity.ts
  • package.json
  • test/accounts-edge.test.ts
  • test/accounts-load-from-disk.test.ts
  • test/accounts.test.ts
  • test/circuit-breaker.test.ts
  • test/codex-manager-forecast-command.test.ts
  • test/codex-manager-report-command.test.ts
  • test/fetch-helpers.test.ts
  • test/health.test.ts
  • test/parallel-probe.test.ts
  • test/proactive-refresh.test.ts
  • test/refresh-guardian.test.ts
  • test/rotation.test.ts
  • test/storage.test.ts

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting


📝 Walkthrough

Summary

This PR aligns the main branch with the already-published codex-multi-auth@1.2.2 release by merging the validated integration branch. The changes introduce foundational improvements to token refresh persistence, account identity resolution, and circuit-breaker availability tracking; test validation was comprehensive (221/221 files, 3221/3221 tests passed). The PR modifies public method signatures in rotation.ts (switching from accountIndex: number to accountKey: TrackerKey across HealthScoreTracker and TokenBucketTracker), which constitutes a breaking change for external consumers; no security or data-loss risks are evident given the refresh-persistence logic includes transactional safeguards and error retries.

Key Changes

Core Token Refresh & Account Persistence

  • Centralized refresh-token persistence via new AccountManager.commitRefreshedAuth(account, authDetails), which matches accounts by derived identity (accountId/email/refreshToken) and persists with rollback on failure
  • Added identity-based account resolution in forecast and report commands, enabling refreshed tokens to persist even when account indices shift
  • Implemented retry logic (3 attempts with exponential backoff) for filesystem-persistence failures, treating specific error codes (EBUSY, EPERM) as retryable
  • Extended refresh-error classification: CodexAuthError now distinguishes retryable failures (transient HTTP/network errors, EAGAIN/EBUSY/EPERM permission issues) from terminal failures (401/403, missing refresh token)

Account Selection & Runtime Identity Keying

  • Introduced RuntimeTrackerKey abstraction and getRuntimeTrackerKey(account) to enable stable health/token tracking across account pool mutations (e.g., index shifts during identity enrichment)
  • Updated HealthScoreTracker and TokenBucketTracker public APIs: all tracking methods now accept TrackerKey (number or string) instead of accountIndex, with internal normalization via normalizeTrackerKey(accountKey, quotaKey?)
  • Modified hybrid account selection to use account.trackerKey ?? account.index for metric lookups, decoupling selection logic from physical array indices
  • Refactored selectHybridAccount to prioritize circuit-breaker availability alongside health scoring

Circuit Breaker Enhancements

  • Added isAvailable(now?) and getTimeUntilAvailable(now?) query methods to expose availability windows
  • Improved half-open state handling: when max half-open attempts are exhausted, canExecute() now checks elapsed probe-wait time before throwing; if elapsed, it resets the window and allows retry
  • Updated minimum-wait computation to include circuit-breaker delay via getTimeUntilAvailable()

Auth Refresh Guardian Consolidation

  • Replaced per-account applyRefreshResult() calls with centralized applyRefreshOutcome() in refresh-guardian tick cycle
  • Added failure-streak cooldown escalation: getAuthFailureCooldownMs(failureCount) scales cooldown duration with consecutive auth failures, resetting the counter after network/rate-limit outcomes
  • Introduced onResult callback in refreshExpiringAccounts() to decouple outcome handling from refresh logic
  • Network-error cooldown is now capped as min(bufferMs, NETWORK_FAILURE_COOLDOWN_MS) to bound delay

Health & Storage Transactionality

  • New withAccountStorageTransaction(handler) enables atomic read-modify-write patterns for refresh-persistence flows
  • Identity-key derivation now supports fallback to index for "refresh-only" accounts lacking accountId/email via getRuntimeAccountIdentityKey(account)
  • Expanded getAccountHealth() to include circuit-breaker state (circuitState === "closed") when computing healthy-account count

Test Enhancements

  • Added comprehensive coverage for transactional persistence, identity-based account resolution, and circuit-breaker availability transitions
  • Extended refresh-guardian tests to validate cooldown escalation, failure-streak reset logic, and commit success/failure paths
  • Introduced mocks for withAccountStorageTransaction and updated storage-mock surfaces to support concurrent-snapshot and retry scenarios

Breaking Changes

  • Public API: HealthScoreTracker and TokenBucketTracker method signatures changed from accepting accountIndex: number to accountKey: TrackerKey (can be number | string). Callers must adapt index-based calls to use the new tracker-key abstraction.
  • Package version: Updated from 1.2.1 to 1.2.2

Validation

All validation steps passed: lint, typecheck, build, test suite (3221/3221 tests), and dry-run package creation.

Walkthrough

major auth refresh refactor introducing circuit-breaker-based per-account gating, runtime-derived identity keying for trackers, restructured refresh error handling with CodexAuthError to distinguish retryable vs terminal failures, and consolidated auth persistence via new commitRefreshedAuth() method. touches core account rotation, auth flows, storage, and all refresh pathways.

Changes

Cohort / File(s) Summary
Circuit breaker integration
lib/circuit-breaker.ts, lib/accounts.ts, lib/health.ts, lib/rotation.ts
Added isAvailable() and getTimeUntilAvailable() to circuit breaker with new half-open probe wait logic. ManagedAccount now carries optional circuitKeyId and _runtimeTrackerKey. Circuit state required for account availability checks and hybrid selection. healthyCount computation now filters accounts with circuitState !== "closed".
Runtime identity keying refactor
lib/storage/identity.ts, lib/rotation.ts, lib/accounts.ts, lib/parallel-probe.ts
Introduced TrackerKey type abstraction. HealthScoreTracker and TokenBucketTracker now accept TrackerKey instead of numeric index. Added getRuntimeAccountIdentityKey() for non-refresh-token-dependent identity. selectHybridAccount now uses trackerKey ?? index for health/token lookups. New getRuntimeTrackerKey() and getAccountCircuitKey() for keying abstraction.
Auth refresh error classification
lib/request/fetch-helpers.ts, index.ts
Added CodexAuthError with retryable flag. New helpers isRetryableRefreshFailure() and isRetryableAuthSetterError() classify OAuth/setter failures. refreshAndUpdateToken() now catches setter errors and wraps all failures in CodexAuthError. In index.ts, refresh error handling branches on err instanceof CodexAuthError && err.retryable.
Refresh outcome consolidation
lib/refresh-guardian.ts, lib/accounts.ts, lib/codex-manager/commands/*
New commitRefreshedAuth() in AccountManager persists refreshed credentials via identity matching with transactional rollback. refresh-guardian.ts now applies outcomes via new applyRefreshOutcome() callback, using commitRefreshedAuth() for success case. forecast.ts and report.ts now extract email/accountId from tokens and persist patches with retry logic on EBUSY/EPERM.
Proactive refresh integration
lib/proactive-refresh.ts
Added optional onResult callback to refreshExpiringAccounts(). applyRefreshResult now updates accountId/accountIdSource and re-sanitizes email from access token.
Cooldown and failure management
lib/refresh-guardian.ts, lib/accounts.ts
New getNetworkFailureCooldownMs() and getAuthFailureCooldownMs(failureCount) helpers. Network errors now capped via bufferMs. Auth failures escalate per failure streak. Eligible accounts filtered upfront excluding those already cooling down.
Storage transaction layer
lib/accounts.ts, lib/codex-manager.ts
New withAccountStorageTransaction() for atomic persistence. buildStorageSnapshot() extracted for reusable snapshot construction. saveToDisk() refactored to use transaction wrapper. saveAccounts dependency now passed to forecast/report commands.
Test coverage expansion
test/accounts.test.ts, test/refresh-guardian.test.ts, test/circuit-breaker.test.ts, test/codex-manager-*.test.ts, test/fetch-helpers.test.ts, test/health.test.ts, test/parallel-probe.test.ts, test/proactive-refresh.test.ts, test/rotation.test.ts, test/storage.test.ts
Added extensive test cases for circuit breaker availability queries, runtime tracker keying, commitRefreshedAuth transactional behavior, cooldown escalation, retryable auth error handling, persistence with retry-on-EBUSY/EPERM, and health filtering by circuit state. Mocks refactored to support withAccountStorageTransaction and saveAccounts.
Package bump
package.json
Version updated from 1.2.1 to 1.2.2.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

flags for reviewer attention:

  • concurrency risk: lib/accounts.ts:commitRefreshedAuth() relies on identity matching to find and update stored accounts. if two refresh threads resolve the same account differently (e.g., by accountId vs refreshToken fallback), persisted state could diverge from in-memory. test coverage uses sequential mocks; no concurrent refresh scenario tested (test/accounts.test.ts, test/refresh-guardian.test.ts).

  • missing windows regression tests: EBUSY/EPERM retry logic in lib/codex-manager/commands/forecast.ts:line ~140 and report.ts:line ~130 is node.js-specific. no assertion that non-retryable codes (e.g., EACCES on windows) fail fast. backoff uses Math.random() without seeding in tests.

  • email/accountid trim and normalization drift: lib/refresh-guardian.ts calls sanitizeEmail() and extractAccountId() on refresh, but findMatchingAccountIndex() in lib/accounts.ts has its own normalization logic. if these diverge, identity matching fails silently and persists the wrong account. test coverage (test/refresh-guardian.test.ts:findAccountByIdentity) checks one path; no cross-method normalization assertions.

  • circuit breaker state transitions under load: lib/circuit-breaker.ts:resetHalfOpenProbeWindow() updates lastStateChange and halfOpenAttempts in-place. if two callers invoke canExecute() in half-open state concurrently, the probe window reset could race. current tests use fake timers only (test/circuit-breaker.test.ts).

  • unguarded tracker key changes: lib/accounts.ts assigns _runtimeTrackerKey at account creation time, but getRuntimeTrackerKey() can return different values if accountId/email change post-creation (during refresh). selectHybridAccount() and tracker methods use trackerKey ?? index, creating possible inconsistency if refresh enriches identity after scoring. test in test/parallel-probe.test.ts:~line 180 checks one scenario; no test validates tracker lookups after identity enrichment mid-flight.

  • no regression test for debounced disk saves during concurrent refresh: lib/refresh-guardian.ts sets requiresSave flag and calls manager.saveToDiskDebounced() at end of tick. if multiple refreshes fire before the debounce window closes, only the final saveToDisk() fires. no assertion that intermediate failures (e.g., failed commitRefreshedAuth) don't block the save operation or that retry-exhaustion is captured.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/integration-1.2.2
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch release/integration-1.2.2

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.

@ndycode ndycode merged commit b9c9273 into main Apr 1, 2026
1 of 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.

1 participant