Skip to content

fix(cache): canonicalize local source before computing the index cache key (. collides across repos) #100

Description

@amondnet

Summary

The on-disk index cache key hashes the raw source string the CLI/MCP receives, while the persisted manifest records the absolute path. Upstream semble canonicalizes before keying. Found while implementing csp clear orphans (#87).

Where

  • crates/csp/src/indexing/cache.rs::resolve_cache_dirnormalize_source(source) only runs a POSIX path.normalize (./../duplicate slashes). "." stays ".".
  • crates/csp/src/bin/csp/main.rs Search arm: let source = path.unwrap_or_else(|| ".".to_string()) is passed to load_or_build_index verbatim. Same for MCP (mcp.rs::DiskLoadOrBuild, source as sent by the agent).
  • crates/csp/src/indexing/index.rs::from_path stores std::path::absolute(path) as root → manifest sourceId.

Upstream

cache.py::cache_key:

normalized = Path(path).expanduser().resolve()
data = str(normalized).encode("utf-8")

Consequences

  1. Cache-key collision across repos. Every csp search "q" (default .) in any repo resolves to the same ~/.csp/index/<key>. Switching between repos makes the content-hash oracle mismatch and forces a full rebuild each time (the incremental reuse path is seeded from the other repo's manifest, so it reuses nothing useful either).
  2. clear orphans cannot use upstream's key-reproduction guard. Upstream trusts an entry only when cache_key(root_path) reproduces the directory name. In csp that check never matches entries built from . or any relative path, so parity(#243): add csp clear orphans to remove cached indexes whose source path no longer exists #87 dropped it and relies on is_git_url + an absolute-sourceId requirement instead (see the PR for parity(#243): add csp clear orphans to remove cached indexes whose source path no longer exists #87). Canonicalizing the key would let csp restore the upstream guard.
  3. Same repo reached via ., ./, /abs/path, or ~/path produces separate cache entries.

Tasks

  • Canonicalize local sources before keying (std::path::absolute at minimum; consider canonicalize to fold symlinks — decide and document in ADR-0002). Git URLs stay verbatim.
  • Apply at one seam so CLI, MCP, and the SDK agree (load_or_build_index / resolve_cache_dir), and keep the manifest sourceId equal to the keyed form so clear orphans can regain upstream's key-reproduction guard.
  • Bump/cache-invalidate as needed: existing entries keyed from relative strings become unreachable (harmless, clear index / clear orphans cleans them).
  • Tests: . and /abs map to the same key; two different cwds with . map to different keys.

Refs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:proposalProposal for discussiontype:bugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions