Skip to content

fix(storage): prevent re-login across PR worktrees#12

Closed
ndycode wants to merge 6 commits into
mainfrom
fix/worktree-account-sharing
Closed

fix(storage): prevent re-login across PR worktrees#12
ndycode wants to merge 6 commits into
mainfrom
fix/worktree-account-sharing

Conversation

@ndycode
Copy link
Copy Markdown
Owner

@ndycode ndycode commented Feb 28, 2026

Summary

  • fix per-project account keying for linked Git worktrees by resolving storage identity to the shared repository root
  • keep non-worktree behavior unchanged (regular repo roots and non-git directories)
  • add one-time legacy migration that merges old worktree-keyed account files into the canonical repo-shared file
  • preserve legacy files when canonical persistence fails (no destructive cleanup on failed migration)

Root cause

perProjectAccounts keyed storage by normalized filesystem path. Each PR worktree path produced a different <project-key>, so account pools looked empty and users had to log in again.

Changes

  • lib/storage/paths.ts
    • added resolveProjectStorageIdentityRoot(projectRoot)
    • detects linked worktrees from .git file + commondir and returns shared repo identity root
  • lib/storage.ts
    • setStoragePath now keys project storage by identity root (repo-shared for linked worktrees)
    • added legacy worktree-scoped migration/merge into canonical storage
    • migration cleanup now deletes legacy files only after successful persist
  • tests
    • test/paths.test.ts: worktree identity resolution cases
    • test/storage.test.ts: shared keying + migration/merge/failure-retention regressions
  • docs
    • updated storage path reference, upgrade guide, troubleshooting note for PR worktree re-login symptom

Validation

  • npm run typecheck
  • npm run lint
  • npm test
  • npm run build

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 pr fixes per-project account re-login when switching between git worktrees by resolving storage identity to the shared repository root instead of individual worktree paths.

key changes:

  • lib/storage/paths.ts: added resolveProjectStorageIdentityRoot() that detects linked worktrees via .git file parsing, validates commondir and gitdir backrefs to prevent forged repository aliasing, handles windows unc paths and backslash separators
  • lib/storage.ts: modified setStoragePath() to use identity root for keying, added legacy worktree-scoped migration that merges old files into canonical storage, retains legacy files on persist failure
  • comprehensive test coverage including concurrent migration, windows path styles, forged pointer attacks
  • docs updated to explain worktree identity sharing

windows filesystem safety:

  • path normalization uses win32 module for unc/backslash handling
  • concurrent loadAccounts() calls tested (test/storage.test.ts:1259) - relies on saveAccounts mutex + deduplication in normalizeAccountStorage to prevent duplicate accounts
  • legacy file deletion ignores ENOENT to handle concurrent unlink races
  • migration keeps legacy files when canonical persist fails (simulates antivirus EACCES)

issue - org rule 637a42e6-7a78-40d6-9ef8-6a45e02e73b6 compliance:
the pr description should explicitly explain:

  1. how concurrent migrations are serialized (persist uses withStorageLock, reads don't but deduplication prevents duplicates)
  2. how file deletion races are handled (ENOENT ignored at line 288)
  3. logging redaction strategy (paths logged at lines 274, 284, 290, 297 contain user dirs but no tokens - acceptable risk but should be documented)
  4. reference to regression test (test/storage.test.ts:1259 "handles concurrent loadAccounts migration")

token leakage:

  • no token content logged, only file paths
  • storage path hashing prevents exposing project paths in filenames
  • worktree validation prevents cross-repo account aliasing attacks

Confidence Score: 4/5

  • safe to merge - worktree identity resolution is well-tested, migration is failure-safe, windows concurrency handled via mutex + deduplication
  • extensive test coverage (concurrent migration, windows paths, unc, forged pointers) and proper security validation, but pr description missing required concurrency/redaction explanation per org rule 637a42e6-7a78-40d6-9ef8-6a45e02e73b6
  • lib/storage.ts needs pr description update to document concurrency strategy and logging approach per org standards

Important Files Changed

Filename Overview
lib/storage/paths.ts added resolveProjectStorageIdentityRoot() with comprehensive worktree detection and security validation (gitdir backref, commondir validation, UNC/Windows path support)
lib/storage.ts integrated identity-based keying, added legacy worktree migration with merge logic, proper failure handling, but lacks mutex on loadAccounts and doesn't explain concurrency strategy per org rule
test/paths.test.ts comprehensive test coverage for worktree resolution, Windows/UNC paths, relative pointers, forged gitdir validation, and submodule differentiation
test/storage.test.ts extensive migration tests including concurrent loadAccounts, persist failure handling, Windows pointer styles, and forged commondir attacks

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[setStoragePath called] --> B{findProjectRoot}
    B --> C[resolveProjectStorageIdentityRoot]
    C --> D{.git exists?}
    D -->|no| E[return projectRoot]
    D -->|yes| F{.git is file?}
    F -->|no/dir| E
    F -->|yes| G[parse gitdir pointer]
    G --> H{contains /.git/worktrees/?}
    H -->|no| E
    H -->|yes| I[validate gitdir backref]
    I -->|invalid| E
    I -->|valid| J[read commondir]
    J --> K{gitdir under commondir/worktrees?}
    K -->|no| E
    K -->|yes| L[return dirname commondir]
    
    L --> M[getProjectGlobalConfigDir identityRoot]
    E --> M
    M --> N[storage keyed to shared repo]
    
    N --> O[loadAccounts]
    O --> P[migrateLegacyProjectStorageIfNeeded]
    P --> Q{legacy paths exist?}
    Q -->|no| R[load from canonical]
    Q -->|yes| S[load canonical + legacy]
    S --> T[mergeStorageForMigration]
    T --> U[saveAccounts mutex]
    U --> V[deduplicateAccounts]
    V --> W[persist to canonical]
    W --> X{success?}
    X -->|yes| Y[delete legacy files]
    X -->|no| Z[keep legacy files]
Loading

Last reviewed commit: 6d137a8

Context used:

  • Rule from dashboard - What: Every code change must explain how it defends against Windows filesystem concurrency bugs and ... (source)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 7cea336 and 6d137a8.

📒 Files selected for processing (3)
  • lib/storage/paths.ts
  • test/paths.test.ts
  • test/storage.test.ts
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (2)
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage/paths.ts
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/paths.test.ts
  • test/storage.test.ts
🧬 Code graph analysis (1)
test/storage.test.ts (2)
lib/storage.ts (3)
  • setStoragePath (179-206)
  • getStoragePath (219-224)
  • setStoragePathDirect (208-213)
lib/storage/paths.ts (2)
  • getConfigDir (137-139)
  • getProjectStorageKey (199-207)
🔇 Additional comments (31)
lib/storage/paths.ts (9)

6-11: lgtm, imports are minimal and appropriate.

the imports from node:fs are limited to sync variants needed for worktree detection. no async io helpers are imported here, which aligns with the synchronous nature of resolveProjectStorageIdentityRoot.


16-23: parseGitDirPointer handles crlf and whitespace correctly.

correctly splits on first line, trims, and extracts the gitdir value. empty values return null as expected.


29-31: isWindowsRootedPath covers drive letters and unc patterns.

the regex handles C:\, \\server\share, and //server/share variants. the unc detection uses [^\\] and [^/] to ensure at least one char follows the prefix.


33-54: resolveGitPath cross-platform resolution looks solid.

returns basePath unchanged for empty pointer. uses win32.resolve when either path is windows-rooted, then normalizes back to forward slashes on non-windows platforms. posix path uses standard resolve.


56-69: readGitCommonDir swallows read errors and falls back safely.

when commondir file is missing or unreadable, returns gitDirPath unchanged. this is the correct fallback behavior.


71-74: isWorktreeGitDirPath pattern check is case-insensitive but may miss edge cases.

the check uses .toLowerCase() and looks for /.git/worktrees/. on windows, \\ would have been normalized before this call via normalizePathDelimiters, so this should work. however, if a path contains .Git or .GIT mixed casing on case-sensitive filesystems, the lowercased check still works correctly since we're checking structure, not filesystem identity.


91-112: worktreeGitDirBelongsToProject validates back-ref correctly.

reads gitdir file from worktree gitdir, resolves it, and compares normalized paths to projectRoot/.git. this prevents forged pointers from passing validation.


114-128: isGitDirUnderCommonWorktrees guards against commondir aliasing.

validates that gitDirPath is actually under commonGitDir/worktrees/. this is the defense against forged commondir pointing to a foreign repo.


239-281: resolveProjectStorageIdentityRoot validation chain is complete.

the function chains all security checks:

  1. exits early if .git doesn't exist (line 241-243).
  2. exits if .git is a directory (standard repo) (line 247-249).
  3. exits if .git is not a file (line 250-252).
  4. parses gitdir pointer and exits if invalid (line 254-258).
  5. resolves gitdir path and exits if not under worktrees (line 260-263).
  6. validates gitdir back-ref belongs to project (line 264-266).
  7. validates gitdir is under commondir/worktrees (line 268-271).
  8. validates candidate repo root has .git directory (line 273-275).

all paths fall back to projectRoot on failure. the catch block on line 278-280 ensures exceptions don't leak. tests are cited in test/paths.test.ts and test/storage.test.ts per the pr summary.

test/storage.test.ts (11)

3-5: imports look correct.

dirname and join from node:path, plus getConfigDir and getProjectStorageKey from storage paths. these are used to compute legacy and canonical paths in migration tests.


772-804: worktree key sharing test is well-structured.

creates a real worktree fixture with proper gitdir/commondir/gitdir-backref files, then validates both main repo and worktree resolve to the same getStoragePath(). env restoration uses the conditional delete pattern correctly (lines 798-801).


926-930: env restoration uses correct conditional delete pattern.

lines 926-929 restore HOME and USERPROFILE using delete when original was undefined, matching the pattern established at test/storage.test.ts:38-39.


1017-1021: env restoration in legacy migration suite is correct.

uses the same conditional delete pattern for HOME, USERPROFILE.


1054-1144: worktree-scoped storage migration test scaffolding is solid.

prepareWorktreeFixture creates a realistic worktree structure with options for windows-style pointers. buildStorage helper keeps storage objects consistent. env restoration includes CODEX_MULTI_AUTH_DIR which is set in fixture setup.


1146-1175: migration test correctly validates legacy file removal.

creates legacy worktree-keyed storage, calls loadAccounts(), and asserts: loaded accounts match legacy, canonical path exists, legacy path removed.


1177-1214: merge test validates both sources are combined.

creates both canonical and legacy files with different accounts, calls loadAccounts(), asserts both accounts present and legacy removed.


1216-1257: persist failure test validates legacy retention.

mocks fs.writeFile to fail on .wal file, calls loadAccounts(), asserts legacy file is preserved. this covers the "no destructive cleanup on failure" requirement from the pr objectives.


1259-1302: concurrent migration test exercises parallel loadAccounts calls.

runs 4 parallel loadAccounts() calls with both canonical and legacy files present. asserts all return 2 accounts and final persisted state has 2 deduplicated accounts. this validates the transactional migration doesn't create duplicates.

as per coding guidelines, test/**: demand regression cases that reproduce concurrency bugs.


1304-1336: windows-style gitdir pointer fixture test exercises backslash paths.

uses pointerStyle: "windows" to create backslash-heavy pointer files, then validates migration works correctly. this covers cross-platform pointer parsing.


1338-1393: forged commondir aliasing regression is comprehensive.

creates a hostile worktree with commondir pointing to a foreign repo, sets up storage files for both original and foreign repos, calls loadAccounts(), and asserts:

  • canonical path is the safe (worktree-scoped) path, not the foreign one.
  • loaded accounts are from the original worktree only.
  • foreign repo storage remains untouched.

this directly addresses the security concern from past reviews.

test/paths.test.ts (11)

5-11: mock setup is correct.

vi.mock("node:fs") with existsSync, readFileSync, statSync stubs. imports the mocked functions for test control.


27-38: buildMockStat helper is clean.

returns a typed object matching statSync return shape with isDirectory and isFile methods.


167-175: getProjectGlobalConfigDir test is minimal but sufficient.

validates the return path contains expected segments.


176-191: standard .git directory test validates early exit.

confirms readFileSync is not called when .git is a directory.


193-237: linked worktree resolution test exercises the happy path.

sets up gitEntry, worktreeGitDir, commondir, gitdir back-ref. validates resolution to shared repo root.


239-283: relative gitdir pointer test exercises relative path resolution.

uses ../../.git/worktrees/pr-relative as gitdir value. this addresses the past review comment on relative pointer coverage.


285-328: windows backslash gitdir pointer test exercises win32 path handling.

uses path.win32.join for paths and backslash commondir content. normalized comparison validates resolution.


330-374: windows unc gitdir pointer test exercises unc path handling.

uses \\server\share\repo as shared root. this addresses the past review comment on unc coverage.


376-412: forged worktree pointer test validates fallback.

gitdir points to foreign repo's worktrees, gitdir back-ref doesn't match project, function falls back to projectRoot.


414-450: forged commondir aliasing test validates fallback.

gitdir back-ref matches project, but commondir points to foreign repo. function falls back to projectRoot. this addresses the past review comment on hostile commondir.


452-468: non-worktree .git file test validates fallback.

gitdir points to .git/modules/submodule (not worktrees), function falls back to projectRoot.


📝 Walkthrough

Summary

This PR resolves a user experience issue where opening a PR worktree incorrectly triggered re-login by changing the storage identity key from per-worktree paths to a canonical repository-root identity. Git linked worktrees now share account storage with their primary repository, while preserving backward compatibility through automatic migration of legacy per-worktree account files on first load.

Key Changes

Core Functionality

  • Introduces resolveProjectStorageIdentityRoot(projectRoot) in lib/storage/paths.ts to detect linked worktrees by parsing .git pointer files and resolving the shared repository root; includes cross-platform support (Windows/UNC paths) and validation against forged commondir aliasing
  • Updates setStoragePath() in lib/storage.ts to key account storage by identity root rather than project path
  • Implements automatic legacy migration via migrateLegacyProjectStorageIfNeeded() that loads worktree-scoped and project-scoped account files, merges them into the canonical storage, and retains legacy files on persist failure to avoid destructive cleanup

Public API Additions

  • resolveProjectStorageIdentityRoot(projectRoot): string — resolves storage identity for project-scoped accounts
  • StorageError class and formatStorageErrorHint(error, path): string — improved error handling with platform-aware hints

Documentation & Migration

  • Updated storage-paths.md to document identity derivation rules (standard repos, worktrees, non-Git directories)
  • Added "Worktree Storage Migration" subsection to upgrade.md describing automatic migration behavior
  • Added troubleshooting entry for re-login symptom in PR worktrees

Test Coverage & Risk Assessment

Regression Tests Exist: Comprehensive coverage added for:

  • Worktree identity resolution (standard repos, relative/absolute gitdir pointers, Windows backslash/UNC paths)
  • Forged pointer defense (invalid commondir, foreign repo aliases)
  • Storage migration scenarios (legacy merge, concurrent migration deduplication, persist failure retention)
  • Cross-platform path handling (Windows UNC, path normalization)

Data Safety: Legacy files are retained on canonical persist failure, ensuring no account loss if migration encounters filesystem errors

Architectural Consideration: Storage key derivation now depends on worktree detection logic; misconfiguration of .git files or corrupt worktree pointers could affect storage lookups (mitigated by forged-pointer validation and fallback to project root)

No security vulnerabilities or test coverage gaps identified.

Walkthrough

adds worktree-aware storage identity resolution and automatic legacy worktree→repo-shared storage migration. worktrees that reference a shared git commondir resolve to the shared repo root as the storage identity; legacy worktree-scoped files are detected, merged, and removed only after a successful canonical write.

Changes

Cohort / File(s) Summary
documentation
docs/reference/storage-paths.md, docs/troubleshooting.md, docs/upgrade.md
clarified <project-key> derivation and added docs for automatic legacy worktree→repo-shared migration and failure/fallback behavior. added troubleshooting row for "opening a pr worktree asks for login again" (docs/troubleshooting.md).
path identity resolution
lib/storage/paths.ts:+170
new public API resolveProjectStorageIdentityRoot(projectRoot) and many helpers (parseGitDirPointer, readGitCommonDir, resolveGitPath, path normalization). resolves linked git worktrees to a common repo root and handles windows path forms and commondir pointers. review attention: git pointer parsing, windows backslash/unc handling, and commondir validation (lib/storage/paths.ts:+170).
storage migration logic
lib/storage.ts:+125
migration rewritten to consider multiple legacy candidates (worktree + project), added loadNormalizedStorageFromPath(), mergeStorageForMigration(), candidatePaths logic, and new exports StorageError and formatStorageErrorHint(). legacy files removed only after successful canonical write; failures preserve legacy files. review attention: merge correctness, active-index handling, persistence semantics, and cross-process concurrency risks (lib/storage.ts:+125).
path resolution tests
test/paths.test.ts:+321
added tests for resolveProjectStorageIdentityRoot() covering standard repos, linked worktrees, relative and windows-style gitdir pointers. expanded fs mocks (readFileSync/statSync). review attention: tests are heavily mocked; add real git worktree integration and windows regression tests (test/paths.test.ts:+321).
storage migration tests
test/storage.test.ts:+386
added end-to-end tests for worktree→canonical migration, merging, persistence-failure fallback, and concurrent migration scenarios. exports test helpers getConfigDir() and getProjectStorageKey(). review attention: simulated concurrency and heavy fs mocking; add real-concurrency and windows regression tests (test/storage.test.ts:+386).

Sequence Diagram(s)

sequenceDiagram
    participant app as application
    participant paths as identity_resolver
    participant fs as filesystem
    participant storage as storage_layer
    participant legacy as legacy_storage
    participant canonical as canonical_storage

    app->>storage: setStoragePath(projectRoot)
    storage->>paths: resolveProjectStorageIdentityRoot(projectRoot)
    paths->>fs: read .git (file or dir)
    alt .git is file with gitdir pointer
        paths->>fs: resolve gitdir, read commondir
        fs-->>paths: shared_repo_root
        paths-->>storage: return shared_repo_root
    else .git is directory or pointer absent
        paths-->>storage: return projectRoot
    end

    storage->>storage: compute currentStoragePath and legacy candidate paths
    storage->>legacy: check candidate legacy paths
    alt legacy exists
        legacy-->>storage: load legacy storage
        storage->>canonical: load canonical storage
        storage->>storage: mergeStorageForMigration()
        storage->>canonical: write merged storage
        alt write succeeds
            storage->>legacy: remove legacy file
        else write fails
            storage-->>legacy: keep legacy file
        end
    else no legacy
        storage->>canonical: load canonical storage
    end

    storage-->>app: return account_pool
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

notes (explicit flags you must review)

  • validate git pointer parsing and commondir resolution logic and windows path handling in lib/storage/paths.ts:+170. missing real-windows integration tests.
  • inspect merge logic and active-index/account deduplication in lib/storage.ts:+125. ensure indices are preserved and duplicates avoided after merge.
  • concurrency risk: migration and canonical write lack cross-process locking (lib/storage.ts:+125). add cross-process coordination or explicit retry/lock tests.
  • tests rely on heavy fs mocking in test/paths.test.ts:+321 and test/storage.test.ts:+386. add integration tests that exercise actual git worktrees and real concurrency on windows.

Suggested labels

bug

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed title follows conventional commits format (fix type, storage scope, lowercase imperative summary under 72 chars) and accurately describes the main change: fixing re-login when switching git worktrees.
Description check ✅ Passed description is directly related to the changeset, providing root cause analysis, detailed change summary across affected files, validation steps, and architectural decisions around worktree identity resolution and legacy migration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/worktree-account-sharing

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/storage.ts`:
- Around line 241-257: The code unconditionally calls
loadNormalizedStorageFromPath(currentStoragePath, "current account storage")
before verifying any legacy files exist, causing unnecessary reads; change the
flow to first probe candidatePaths (from
currentLegacyWorktreeStoragePath/currentLegacyProjectStoragePath) with
existsSync to detect any real legacy files and only call
loadNormalizedStorageFromPath when at least one legacyPath exists (or move the
load inside the loop just before migrating that legacyPath), ensuring
candidatePaths de-duplication remains and keeping references to candidatePaths,
legacyPath, loadNormalizedStorageFromPath, and currentStoragePath intact.

In `@test/paths.test.ts`:
- Around line 193-232: Add two regression test cases to the existing spec around
resolveProjectStorageIdentityRoot: one where mockedReadFileSync returns a
relative gitdir pointer (e.g., "gitdir: ../.git\n" or "../..\n" in the
commondir) and another where it returns a Windows backslash-style pointer (e.g.,
"gitdir: C:\\repo\\.git\\worktrees\\pr-8\n" or backslash-heavy commondir
content); update mockedExistsSync/mockedStatSync/mockedReadFileSync behaviors in
those tests to return true/appropriate stats for the candidate paths and assert
that resolveProjectStorageIdentityRoot(projectRoot) still resolves to the
sharedRepoRoot, ensuring normalize logic and relative-path resolution are
exercised for both POSIX and Windows-style inputs.

In `@test/storage.test.ts`:
- Around line 1047-1232: Add deterministic Vitest regression tests that exercise
concurrent migrations and Windows-style gitdir paths: extend the
"worktree-scoped storage migration" suite to include one test that calls
loadAccounts() concurrently (e.g., Promise.all over multiple loadAccounts()
invocations after setStoragePath(worktreeRepo)) against the same worktree
fixture and asserts the canonical file exists, legacy file removed, and merged
accounts without duplicate race artifacts; and another test that uses a
Windows-style worktree fixture (write the worktreeRepo/.git file with a
"gitdir:" line using backslashes and ensure commondir uses backslashes) to
verify getProjectStorageKey/getConfigDir path handling and migration behavior.
Reference functions/helpers: loadAccounts, setStoragePath, getStoragePath,
getConfigDir, getProjectStorageKey, and prepareWorktreeFixture to locate where
to add the new tests and fixture variations. Ensure tests remain deterministic
(use vi mocks only when needed) and use vitest async assertions.
- Around line 797-799: The tests restore environment variables unsafely by
reassigning potentially undefined originals (originalHome, originalUserProfile)
which can leak invalid env state across tests; replace those direct assignments
with the conditional delete pattern used earlier (if originalHome === undefined
then delete process.env.HOME else set it, and same for USERPROFILE) in all four
places where envs are restored around testWorkDir cleanup, and ensure
testWorkDir is removed after env fixups; additionally extend the migration test
suite (the migration tests covering persist/fail paths) to add
parallel/concurrent migration races and Windows-specific filesystem cases
(drive-letter paths and NT separator behaviors) and include tests for concurrent
lock scenarios so the migration code is exercised under race and Windows path
edge conditions.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 0636840 and 3566d0f.

📒 Files selected for processing (7)
  • docs/reference/storage-paths.md
  • docs/troubleshooting.md
  • docs/upgrade.md
  • lib/storage.ts
  • lib/storage/paths.ts
  • test/paths.test.ts
  • test/storage.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
docs/**

⚙️ CodeRabbit configuration file

keep README, SECURITY, and docs consistent with actual CLI flags and workflows. whenever behavior changes, require updated upgrade notes and mention new npm scripts.

Files:

  • docs/upgrade.md
  • docs/troubleshooting.md
  • docs/reference/storage-paths.md
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/paths.test.ts
  • test/storage.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage/paths.ts
  • lib/storage.ts
🧬 Code graph analysis (3)
test/paths.test.ts (1)
lib/storage/paths.ts (2)
  • getProjectGlobalConfigDir (140-142)
  • resolveProjectStorageIdentityRoot (154-192)
test/storage.test.ts (2)
lib/storage.ts (4)
  • setStoragePath (179-206)
  • getStoragePath (219-224)
  • setStoragePathDirect (208-213)
  • loadAccounts (612-614)
lib/storage/paths.ts (2)
  • getConfigDir (52-54)
  • getProjectStorageKey (114-122)
lib/storage.ts (2)
lib/storage/paths.ts (3)
  • resolveProjectStorageIdentityRoot (154-192)
  • getProjectGlobalConfigDir (140-142)
  • getProjectConfigDir (64-66)
lib/storage/migrations.ts (1)
  • AccountStorageV3 (59-64)

Comment thread lib/storage.ts Outdated
Comment thread test/paths.test.ts
Comment thread test/storage.test.ts Outdated
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/paths.test.ts`:
- Around line 275-317: The test currently skips on non-Windows platforms but the
mocked fs calls (mockedExistsSync, mockedStatSync, mockedReadFileSync) and use
of path.win32 make it safe to run cross-platform; remove the early platform
guard (the if (process.platform !== "win32") return;) so the Windows-style
backslash gitdir pointer case always runs. Keep the existing
path.win32.join/normalize usage, the mocked implementations for
mockedExistsSync/mockedStatSync/mockedReadFileSync, and the call to
resolveProjectStorageIdentityRoot so the test validates Windows backslash
handling on all CI environments.

In `@test/storage.test.ts`:
- Around line 1297-1333: The current test only runs on win32 and misses a
cross-platform regression where resolveProjectStorageIdentityRoot() calls
path.resolve() which treats backslashes literally on POSIX; add a new unit test
for parseGitDirPointer() (and/or a variant of the existing worktree fixture
test) that supplies a Windows-style gitdir pointer string containing backslashes
and asserts the returned path is normalized/canonical on POSIX, or alternatively
mock path.resolve() in the test to simulate Windows resolution and validate that
parseGitDirPointer()/resolveProjectStorageIdentityRoot() convert backslashes
into proper separators before resolving; reference parseGitDirPointer,
resolveProjectStorageIdentityRoot, and path.resolve in the test to locate the
code to exercise.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 3566d0f and 8084ff2.

📒 Files selected for processing (3)
  • lib/storage.ts
  • test/paths.test.ts
  • test/storage.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/storage.test.ts
  • test/paths.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage.ts
🧬 Code graph analysis (2)
test/storage.test.ts (2)
lib/storage.ts (3)
  • setStoragePath (179-206)
  • getStoragePath (219-224)
  • setStoragePathDirect (208-213)
lib/storage/paths.ts (2)
  • getConfigDir (52-54)
  • getProjectStorageKey (114-122)
lib/storage.ts (2)
lib/storage/paths.ts (3)
  • resolveProjectStorageIdentityRoot (154-192)
  • getProjectGlobalConfigDir (140-142)
  • getProjectConfigDir (64-66)
lib/storage/migrations.ts (1)
  • AccountStorageV3 (59-64)
🔇 Additional comments (24)
lib/storage.ts (10)

8-15: imports look good.

added resolveProjectStorageIdentityRoot import aligns with the new identity resolution logic. no issues here.


148-148: worktree legacy path tracking is correctly scoped.

currentLegacyWorktreeStoragePath is properly initialized and cleared in all relevant paths: setStoragePath, setStoragePathDirect, and reset to null when appropriate. the conditional at lib/storage.ts:198-199 correctly avoids self-referential legacy paths.

Also applies to: 183-183, 198-199, 203-203, 211-211


191-199: identity root computation and legacy path setup look correct.

lib/storage.ts:191-192 properly derives canonical path via identity root, and lib/storage.ts:194-199 computes the previous worktree-scoped path only when it differs from canonical. this prevents migration loops.


237-254: migration preflight now correctly gates on existing files.

the past review comment flagged unconditional canonical reads before checking legacy existence. lib/storage.ts:251-254 now filters candidatePaths via existsSync before loading canonical storage, eliminating unnecessary reads and windows file-lock pressure.


256-280: migration loop handles persist failures gracefully.

lib/storage.ts:268-280 catches persist errors, logs a warning, and continues with fallback storage. legacy files are only deleted after successful persist. this matches the pr objective of preserving legacy files on failed migration.


282-301: legacy file cleanup with proper error handling.

lib/storage.ts:282-295 only unlinks after successful persist, ignores ENOENT, and logs warnings for other errors. logging at lib/storage.ts:297-301 includes path and account count but no tokens or emails—good.


304-311: migration return logic handles edge cases.

lib/storage.ts:304-310 returns migrated storage when migration succeeded, falls back to target storage if canonical file doesn't exist yet (covers first-load-from-legacy scenario), or returns null. this aligns with expected behavior.


313-336: loadNormalizedStorageFromPath helper is clean.

lib/storage.ts:313-336 encapsulates load-and-normalize with proper error handling. schema warnings are logged with path context but no sensitive data. ENOENT is silently ignored, other errors are logged as warnings.


338-356: mergeStorageForMigration correctly concatenates accounts.

lib/storage.ts:346-351 merges incoming accounts after current accounts, preserves current's active index, and normalizes the result. if normalization fails, it falls back to current. deduplication happens inside normalizeAccountStorage, so duplicates across legacy and canonical will be handled.


669-694: loadAccountsInternal migration integration is sound.

lib/storage.ts:670-672 calls migration early when persist is available. lib/storage.ts:693-694 returns migrated legacy storage on ENOENT if canonical file doesn't exist yet—this covers the first-load-after-migration case where canonical was just created.

test/storage.test.ts (8)

771-803: worktree storage path identity test is well-structured.

test/storage.test.ts:772-803 correctly sets up a worktree fixture with .git file pointing to gitdir:, verifies both main repo and worktree resolve to the same storage path. env restoration uses conditional delete pattern. cleanup in finally block is proper.


925-929: env restoration fixed per past review.

test/storage.test.ts:925-928 now uses the conditional delete pattern instead of direct assignment. this prevents leaking undefined as a string into the environment.


1016-1020: env restoration fixed in legacy migration suite.

same conditional delete pattern applied at test/storage.test.ts:1016-1019. consistent with other cleanup blocks.


1053-1137: worktree migration test suite setup is thorough.

test/storage.test.ts:1053-1137 sets up:

  • prepareWorktreeFixture helper with pointerStyle option for windows
  • buildStorage helper for consistent test data
  • proper env save/restore with CODEX_MULTI_AUTH_DIR
  • vi.restoreAllMocks() in afterEach

1139-1168: basic worktree migration test covers happy path.

test/storage.test.ts:1139-1168 verifies legacy worktree storage migrates to canonical path, legacy file is removed, and canonical file exists. assertions are clear.


1170-1207: merge test validates both accounts are preserved.

test/storage.test.ts:1170-1207 sets up both canonical and legacy files, verifies merge produces 2 accounts with both ids present, and legacy file is removed. good coverage.


1209-1250: persist failure retention test uses targeted mock.

test/storage.test.ts:1230-1241 mocks fs.writeFile only for the WAL path to simulate persist failure. verifies legacy file is retained when persist fails. this matches the pr objective of "no destructive cleanup on failed migration".


1252-1295: concurrent migration test addresses past review concern.

test/storage.test.ts:1252-1295 runs 4 parallel loadAccounts() calls against the same worktree fixture. verifies:

  • all results have 2 accounts
  • persisted file has exactly 2 accounts (no duplicates from race)
  • legacy file is removed

this covers the concurrency gap flagged in past reviews.

test/paths.test.ts (6)

5-11: mock setup and helper are clean.

test/paths.test.ts:5-11 adds readFileSync and statSync mocks. test/paths.test.ts:27-38 provides a typed helper for stat results. this keeps tests deterministic per coding guidelines.

Also applies to: 23-38


167-174: getProjectGlobalConfigDir test relocated into nested describe.

test/paths.test.ts:167-174 nests the test properly. assertions verify path contains expected segments.


176-191: standard .git directory test verifies no worktree resolution.

test/paths.test.ts:177-191 confirms resolveProjectStorageIdentityRoot returns project root unchanged when .git is a directory. verifies readFileSync is not called (no gitdir pointer needed).


193-232: linked worktree test validates shared repo root resolution.

test/paths.test.ts:193-232 sets up worktree fixture with absolute gitdir: pointer and commondir: ../... verifies resolution returns shared repo root. this is the core worktree identity behavior.


234-273: relative gitdir pointer test addresses past review.

test/paths.test.ts:234-273 uses gitdir: ../../.git/worktrees/pr-relative (relative path). verifies the resolve logic in lib/storage/paths.ts:175-177 handles relative pointers correctly.


319-336: non-worktree .git file test validates fallback.

test/paths.test.ts:319-335 simulates a submodule with gitdir: /repo/.git/modules/submodule (not under worktrees/). verifies isWorktreeGitDirPath returns false and project root is kept unchanged.

Comment thread test/storage.test.ts
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/storage/paths.ts`:
- Around line 206-217: The code currently accepts any .git pointer under
worktrees by existence alone; update the logic in
resolveGitPath/isWorktreeGitDirPath/readGitCommonDir usage so that after
computing commonGitDir you call fs.realpath on commonGitDir and projectRoot and
only accept candidateRepoRoot when the realpath of candidateRepoRoot is a
subpath of the realpath(projectRoot) (reject otherwise), preventing forged
pointers from aliasing another repo's store; also add a unit test (vitest) that
simulates a forged worktree pointer (including Windows symlink behavior) and
verify rejection, add retry handling for EBUSY/429 in any new IO helpers, and
ensure logging in this flow (in functions above) does not include tokens/emails.

In `@test/paths.test.ts`:
- Around line 176-332: Add a regression test to the
resolveProjectStorageIdentityRoot suite that simulates a ".git" file pointing at
a worktrees directory that is not under the repository's shared git dir and
asserts the function returns the original projectRoot; specifically, create a
test that stubs existsSync/statSync/readFileSync so .git is a file (isFile true)
returning a "gitdir: /some/other/repo/.git/worktrees/xxx" pointer and ensure the
shared .git path is not considered present, then call
resolveProjectStorageIdentityRoot(projectRoot) and expect it to equal
projectRoot to guard the fallback logic in resolveProjectStorageIdentityRoot.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8084ff2 and 5e5c661.

📒 Files selected for processing (3)
  • lib/storage/paths.ts
  • test/paths.test.ts
  • test/storage.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/paths.test.ts
  • test/storage.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage/paths.ts
🧬 Code graph analysis (1)
test/paths.test.ts (1)
lib/storage/paths.ts (2)
  • getProjectGlobalConfigDir (171-173)
  • resolveProjectStorageIdentityRoot (185-221)
🔇 Additional comments (1)
test/storage.test.ts (1)

1252-1330: good migration regression coverage for race + windows paths.

test/storage.test.ts:1252-1330 (line 1252-1330) now exercises concurrent loadAccounts() migration and windows-style gitdir fixtures without platform gating. this is the right integration safety net for the storage identity change.

Comment thread lib/storage/paths.ts
Comment thread test/paths.test.ts
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/storage/paths.ts`:
- Around line 252-258: The current code trusts readGitCommonDir output and
accepts candidateRepoRoot if join(candidateRepoRoot, ".git") exists, which
allows a forged commondir to alias another repo; update the logic in the
function that computes candidateRepoRoot to validate that the resolved
commonGitDir is actually located inside candidateRepoRoot (or is a subpath of
candidateRepoRoot) and that the gitdir/commondir symlink chain resolves to paths
staying within the candidateRepoRoot; if any validation fails (commondir points
outside candidateRepoRoot or symlink resolution escapes the root), return
projectRoot as the safe fallback. Refer to readGitCommonDir, candidateRepoRoot,
existsSync(join(candidateRepoRoot, ".git"))) and projectRoot to locate the
patch. Also add a Vitest regression that creates the described fake worktree
files (.git with gitdir -> attacker path, worktrees/pr-x/gitdir ->
projectRoot/.git, worktrees/pr-x/commondir -> /repo/victim/.git) and assert the
function falls back to projectRoot.

In `@test/paths.test.ts`:
- Around line 330-366: Add a deterministic regression test for the hostile
commondir aliasing scenario by extending or adding a vitest case that calls
resolveProjectStorageIdentityRoot(projectRoot) where the .git file points
(gitdir) to a worktree whose gitdir back-ref matches the project but its
commondir contents point outside the project; ensure mockedExistsSync returns
true for project .git, foreign commondir, foreign gitdir back-ref and victim
.git, mockedStatSync reports the project .git as a file (not dir), and
mockedReadFileSync returns the gitdir line for the project .git, a forged
"../.." for the foreign commondir, and the absolute path for the foreign gitdir
back-ref; finally assert the resolved path equals projectRoot to lock down the
regression.

In `@test/storage.test.ts`:
- Around line 1054-1337: Add a deterministic vitest integration that simulates a
forged worktree commondir alias: using prepareWorktreeFixture create two repos
(original worktreeRepo and a foreign repo), then modify the worktree's
worktreeGitDir/commondir to point at the foreign repo (mimicking a hostile
commondir), write a legacy worktree storage file for the original worktree (via
getProjectStorageKey(worktreeRepo)) and a conflicting canonical file for the
foreign repo, call setStoragePath(worktreeRepo) and loadAccounts(), and assert
that loadAccounts returns only the original worktree's accounts (no
cross-contamination into the foreign repo), the canonical path for the original
project contains the migrated data, and any legacy file behavior (existence or
removal) matches existing migration semantics; place the test inside the
existing "worktree-scoped storage migration" describe block and follow patterns
used by prepareWorktreeFixture, setStoragePath, getStoragePath, getConfigDir,
getProjectStorageKey, and loadAccounts so it remains deterministic and uses
vitest APIs.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5e5c661 and 7cea336.

📒 Files selected for processing (3)
  • lib/storage/paths.ts
  • test/paths.test.ts
  • test/storage.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage/paths.ts
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/paths.test.ts
  • test/storage.test.ts
🧬 Code graph analysis (2)
test/paths.test.ts (1)
lib/storage/paths.ts (2)
  • getProjectGlobalConfigDir (209-211)
  • resolveProjectStorageIdentityRoot (223-262)
test/storage.test.ts (2)
lib/storage.ts (5)
  • setStoragePath (179-206)
  • getStoragePath (219-224)
  • setStoragePathDirect (208-213)
  • loadAccounts (613-615)
  • normalizeAccountStorage (517-606)
lib/storage/paths.ts (2)
  • getConfigDir (121-123)
  • getProjectStorageKey (183-191)
🔇 Additional comments (2)
lib/storage/paths.ts (1)

33-54: good cross-platform path resolution split.

lib/storage/paths.ts:33-54 cleanly separates windows-rooted and posix flows and normalizes delimiters before final comparison paths. this is a solid base for windows filesystem behavior.

test/storage.test.ts (1)

1259-1336: nice coverage for concurrency and windows migration paths.

test/storage.test.ts:1259-1336 now exercises concurrent loadAccounts() migration and windows-style gitdir fixtures without skips. this is the right direction for race and filesystem portability regressions.

Comment thread lib/storage/paths.ts
Comment thread test/paths.test.ts
Comment thread test/storage.test.ts
Reject worktree identity aliasing when commondir does not match the worktree gitdir ancestry, and add unit/integration regressions for hostile commondir scenarios.

Co-authored-by: Codex <noreply@openai.com>
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/paths.test.ts`:
- Around line 285-328: The test covers drive-letter Windows pointers but misses
a UNC-share gitdir pointer case; add a vitest regression that calls
resolveProjectStorageIdentityRoot with a UNC projectRoot (e.g.
"\\\\server\\share\\repo\\worktrees\\pr-8") and extend the mockedExistsSync,
mockedStatSync and mockedReadFileSync branches to recognize the UNC .git,
worktree gitdir, commondir and sharedGitDir equivalents (use
path.win32.join/normalize for comparisons) so the function follows the same
resolution path and the expectation asserts the normalized shared UNC root;
reference resolveProjectStorageIdentityRoot and the existing test block in
test/paths.test.ts to mirror the drive-letter case but using UNC paths.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 7cea336 and 04aa86b.

📒 Files selected for processing (3)
  • lib/storage/paths.ts
  • test/paths.test.ts
  • test/storage.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/paths.test.ts
  • test/storage.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage/paths.ts
🧬 Code graph analysis (2)
test/paths.test.ts (1)
lib/storage/paths.ts (2)
  • getProjectGlobalConfigDir (225-227)
  • resolveProjectStorageIdentityRoot (239-281)
test/storage.test.ts (2)
lib/storage.ts (5)
  • setStoragePath (179-206)
  • getStoragePath (219-224)
  • setStoragePathDirect (208-213)
  • loadAccounts (613-615)
  • normalizeAccountStorage (517-606)
lib/storage/paths.ts (2)
  • getConfigDir (137-139)
  • getProjectStorageKey (199-207)
🔇 Additional comments (2)
lib/storage/paths.ts (1)

239-277: worktree identity hardening looks good.

lib/storage/paths.ts:260-270 now blocks aliasing unless both back-ref ownership and commondir ancestry checks pass, and lib/storage/paths.ts:241-279 safely falls back to projectRoot on invalid or hostile layouts. coverage in test/paths.test.ts:330-404 and test/storage.test.ts:1338-1393 matches the risk surface.

test/storage.test.ts (1)

1054-1394: migration regression coverage is strong here.

test/storage.test.ts:1259-1302 (concurrency race), test/storage.test.ts:1304-1336 (windows-style pointers), and test/storage.test.ts:1338-1393 (forged commondir isolation) directly cover the high-risk paths for this pr.

Comment thread test/paths.test.ts
Add a deterministic Windows UNC pointer regression for storage identity root resolution.

Co-authored-by: Codex <noreply@openai.com>
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@greptile review

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review please

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

@ndycode i'll review the changes now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

ndycode added a commit that referenced this pull request Feb 28, 2026
…tion audit

Resolved conflicts in docs/README.md, docs/reference/commands.md,
docs/reference/storage-paths.md, docs/troubleshooting.md by combining
PR #12 worktree content with PR #13 professional formatting.
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

Superseded by #14 — all changes from this PR have been merged into the unified supersede PR.

@ndycode ndycode closed this Feb 28, 2026
ndycode added a commit that referenced this pull request Feb 28, 2026
feat: unified overhaul — consolidate PRs #7, #8, #10, #11, #12, #13
@ndycode ndycode deleted the fix/worktree-account-sharing branch March 3, 2026 12:12
ndycode added a commit that referenced this pull request Apr 6, 2026
…tion audit

Resolved conflicts in docs/README.md, docs/reference/commands.md,
docs/reference/storage-paths.md, docs/troubleshooting.md by combining
PR #12 worktree content with PR #13 professional formatting.
ndycode added a commit that referenced this pull request Apr 6, 2026
feat: unified overhaul — consolidate PRs #7, #8, #10, #11, #12, #13
ndycode added a commit that referenced this pull request Apr 16, 2026
Resolves all open Dependabot alerts on package-lock.json:

- hono <4.12.14: JSX SSR HTML injection, cookie name bypass, IPv4-mapped IPv6 ipRestriction, setCookie validation, serveStatic repeated-slash bypass, toSSG path traversal (alerts #16, #18, #20, #22, #24, #26)

- vite <7.3.2: dev server WebSocket arbitrary file read, optimized deps .map path traversal, server.fs.deny query bypass (alerts #12, #13, #14)

Lockfile refreshed via npm install --package-lock-only. Typecheck, lint, and 3418/3418 tests pass.
ndycode added a commit that referenced this pull request Apr 16, 2026
Resolves all open Dependabot alerts on package-lock.json:

- hono <4.12.14: JSX SSR HTML injection, cookie name bypass, IPv4-mapped IPv6 ipRestriction, setCookie validation, serveStatic repeated-slash bypass, toSSG path traversal (alerts #16, #18, #20, #22, #24, #26)

- vite <7.3.2: dev server WebSocket arbitrary file read, optimized deps .map path traversal, server.fs.deny query bypass (alerts #12, #13, #14)

Lockfile refreshed via npm install --package-lock-only. Typecheck, lint, and 3418/3418 tests pass.
ndycode added a commit that referenced this pull request Jun 2, 2026
Completes partial fixes flagged in the third review, bumps to v2.2.1.

Real bugs (prior fixes were incomplete):
- request/fetch-helpers: isUnsupportedCodexModelForChatGpt (the handleErrorResponse
  path) now also matches NORMALIZED_UNSUPPORTED_MODEL_PATTERN, so a 400 'model not
  currently available for this chatgpt account' gets the entitlement rewrite, not
  generic error guidance (#13)
- forecast: quota-exhausted accounts are classified 'delayed' (not 'unavailable'),
  so they slipped the recommendation filter; added an explicit exhausted flag and
  excluded it, returning null when the whole pool is exhausted (#10)
- storage/record-utils: clampIndex guards NaN -> 0 (Math.trunc(NaN) propagated) (#16)
- local-client-tokens: debounce lastUsedAt persistence (60s threshold) so the
  bearer-verify hot path stops writing to disk every request; +chmod 0o700
  re-assert on the token-store dir (#12, #11)
- mcodex: relay SIGTERM/SIGINT to the spawned child so it isn't orphaned (#15)

Test quality:
- local-client-tokens: parameterized rename-retry test over ENOTEMPTY/EAGAIN/EACCES (#18)
- storage-flagged: clear the H4 deadlock-guard timer on the happy path (#17)
- storage: removeWithRetry for suite cleanup (#19)

Release:
- bump package.json + .codex-plugin/plugin.json to 2.2.1
- add docs/releases/v2.2.1.md; point docs portal + README at it

Known follow-ups (documented, deferred — need design, not rushed into a patch):
config env-path save is a single-process CAS not a true cross-process lock (#8/#9);
verifyLocalClientBearerToken read stays serialized but a fuller lease is future work;
runtime proxy routingMutex='enabled' still has a select/commit cursor race (#14)
requiring an async refactor of chooseAccount across its call sites.

Full suite: 4337 passed, 3 skipped, 0 failed; typecheck + lint clean.
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