Skip to content

fix(ai-vault): keep sessions recorded under a symlinked workspace's real path in Workspace scope - #31244

Merged
nwparker merged 2 commits into
mainfrom
nwparker/ai-vault-symlinked-workspace-scope
Oct 20, 2026
Merged

nwparker merged 2 commits into
mainfrom
nwparker/ai-vault-symlinked-workspace-scope

Conversation

@nwparker

@nwparker nwparker commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​285 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​284
Prod 9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​198 $\color{#cf222e}{\Huge{\mathbf{−}}}$​16 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​182

ELI5

If you open a workspace through a shortcut folder (a symlink, like ~/work/api pointing at /data/src/api), some agents record the real folder instead. Session History compared folder names as plain text, so those sessions vanished from the Workspace view. Now the computer that has the files works out where the shortcut really points, and those sessions count as part of the workspace.

What Changed

  • Before: with a workspace registered as ~/work/api (a symlink to /data/src/api, or on macOS anything under /var, which is really /private/var), sessions an agent recorded under /data/src/api were missing from the Workspace scope. They also counted against the history depth cap for other folders, so with a small depth they could disappear from the list entirely.
  • After: those sessions show up under the workspace, count as in-scope for the depth cap, and are attributed to the right project. Resuming still uses the exact folder the agent recorded, so nothing about resume changes.

Mechanism:

  • src/main/ai-vault/session-scope-real-paths.ts (new) runs inside cached-session-list.ts, which is the single entry point for both the desktop's own scan and an Orca server's aiVault.listSessions (orcad on SSH hosts). So the real path is always resolved on the host that owns the files, never on the desktop for a remote host.
    • It resolves realpath of each scope path (at most 64). Paths missing on that host are skipped quietly, as today; WSL network paths are skipped because Windows can't resolve a Linux link through the share.
    • The scan gets the registered paths plus their real paths, so older in-scope sessions recorded under the real path are found before the recency cap.
    • Each session whose cwd is under a linked scope's real path gets an optional, matching-only cwdAliases with the registered spelling (/data/src/api/pkg -> ~/work/api/pkg).
  • One helper, aiVaultSessionInScope (src/shared/ai-vault-scope-cwd-match.ts), checks cwd plus aliases. Both the Workspace filter and truncateAiVaultListResult use it. That truncation is the depth cap used by the host cache, the desktop's per-host cache and the renderer cache.
  • Project attribution (ai-vault-session-project-candidates.ts, new) also checks aliases. For aliased sessions it picks the root that leaves the shortest remaining path, so a parent folder whose link happens to have a longer name can't take a nested repo's sessions.

Why

This follows @quzard's proposal in #31139: resolve on the scanning host, carry aliases next to the recorded cwd, and never change the path resume uses. Alternatives considered:

  • Resolve on the desktop: wrong for SSH and server hosts, because the desktop would read its own disk.
  • Have the host send back a scope -> real-path map and let clients match against it: same wire cost, but every client would need its own matching logic.
  • Also resolve every recorded cwd, to catch a cwd spelled through a different link: dropped after review. A listing must not touch arbitrary folders. Those can sit on a hung network mount, or trigger macOS privacy prompts. That case stays unmatched and is noted below.

Wire compatibility: cwdAliases is a new optional field on a session row.

  • Older desktops parse rows with a stripping zod object, so they drop it and behave as before.
  • Older hosts never send it, so newer desktops behave as before.
  • Mobile keeps rows opaque.
  • A malformed alias list is caught and drops only the aliases, never the row.
  • No session object is spread into the strict resume params.

Linked Issue

Part of #31139 (item 4, "Workspaces opened through a symlink"). Reported, with this approach, by @quzard. The issue stays open for the remaining items.

Visual Proof

N/A: no layout change. The affected sessions now appear in the existing Workspace list instead of being missing.

Testing

Tests that fail on main:

  • session-scope-real-paths.test.ts uses real symlinks (junctions on Windows): the scope gets its real path, nested cwds get aliases, and unrelated, cwd-less and WSL rows are left alone.
  • cached-session-list.test.ts: the scan receives both spellings, the row gets its alias, and a depth-1 cache hit keeps the aliased row as in scope.
  • ai-vault-session-filters.test.ts, ai-vault-session-depth.test.ts and session-list-result-validation.test.ts: the alias counts for the Workspace filter and the depth cap, and malformed aliases drop only the aliases.
  • ai-vault-session-projects.test.ts: attribution follows the alias, and a nested repo beats a longer-spelled parent link.

Other checks:

  • Full static-analysis job run locally on this head (all steps). The only failure was check-e2e-typecheck-ratchet on tests/e2e/helpers/phone-feed-list.ts, a file this PR doesn't touch.

  • Unit folders around the changed files: src/main/ai-vault, src/shared/ai-vault*, right-sidebar/ai-vault*, src/main/ipc/ai-vault*, src/main/ai-vault-search.

  • Live check on a real Linux host (omarchy), in an isolated /tmp dir: I bundled the host module plus the shared Workspace filter, then ran it against real symlinks. A scope work-api -> data/src/api gave aliases to data/src/api and data/src/api/pkg, and all of those rows stayed in the Workspace filter. An unrelated folder was left untouched. I did not run a full desktop + orcad session over SSH by hand.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Review

  • Codex (gpt-6.1-sol, high), round 1: one P2. Attribution compared root lengths across different link spellings, so a longer-spelled parent link could win. Fixed with remainder ranking and a test.
  • Codex round 2: clean.
  • Independent Opus review, round 1: one P2. Resolving every out-of-scope cwd could stall a listing on a hung mount or raise macOS privacy prompts. Fixed by removing that fallback.
  • Opus round 2: no P0-P2 findings.

Opus P3s left as follow-ups:

  • Workspace-scope search (session-search-row-filter.ts) still matches exact paths.
  • A workspace registered at its real path, where the agent recorded the symlink spelling, isn't matched.
  • The mobile active-worktree marker reads only cwd.

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

The only new filesystem work is up to 64 realpath calls on workspace paths per uncached scan. Recorded cwds are never touched.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…eal path in Workspace scope (#31139)

A workspace registered through a symlink (~/work/api -> /data/src/api, or
macOS /var -> /private/var) lost every session whose agent recorded the
resolved cwd: scope matching compared strings, and those rows also counted
against the out-of-scope depth cap.

The host that owns the files (the desktop for local scans, orcad for SSH and
server hosts) now resolves each scope path's real path, scans both spellings,
and gives matching sessions optional matching-only cwdAliases in the
registered spelling. Workspace scope, the depth cap and project attribution
read cwd plus aliases; resume still uses the recorded cwd. Older hosts send
no aliases and older clients strip the field.

Reported, with the proposed approach, by @quzard in #31139.
…e recorded cwds

Review feedback: a longer-spelled parent link could take a nested project's
session, and resolving every out-of-scope cwd could stall a listing on a hung
network mount or raise macOS privacy prompts.
@coderabbitai

coderabbitai Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1d5a6f46-4581-4e4d-99a5-720cb5681e13
📥 Commits

Reviewing files that changed from the base of the PR and between fb81ee5 and 877a419.

📒 Files selected for processing: 15
  • src/main/ai-vault/cached-session-list.test.ts
  • src/main/ai-vault/cached-session-list.ts
  • src/main/ai-vault/session-list-result-validation.test.ts
  • src/main/ai-vault/session-list-result-validation.ts
  • src/main/ai-vault/session-scope-real-paths.test.ts
  • src/main/ai-vault/session-scope-real-paths.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-project-candidates.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-projects.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-session-projects.ts
  • src/shared/ai-vault-scope-cwd-match.ts
  • src/shared/ai-vault-session-depth.test.ts
  • src/shared/ai-vault-session-depth.ts
  • src/shared/ai-vault-session-filters.test.ts
  • src/shared/ai-vault-session-filters.ts
  • src/shared/ai-vault-types.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.

📝 Walkthrough

AI Vault session listing now scans resolved real paths for symlinked scopes and attaches matching host-path aliases to sessions. Session validation accepts optional aliases. Shared scope filters and project attribution use aliases when matching sessions to workspaces and repositories.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 877a4

The reviewed evidence identifies no issue that needs correction before merge; normal checks still apply.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 15 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly identifies the primary change: preserving sessions recorded under a symlinked workspace real path in Workspace scope.
Description check Passed The description follows the required template and explains the user impact, implementation, rationale, linked issue, testing, compatibility considerations, review findings, and checklist status. It co…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description follows the required template and explains the user impact, implementation, rationale, linked issue, testing, compatibility considerations, review findings, and checklist status. It correctly marks visual proof as not applicable.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

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