What
/api/sources returns each source's memory_dir as the raw configured path, while /api/memory-dirs/status returns the resolved path (after Path.resolve()).
Repro on macOS with a memory_dir registered under /tmp (which symlinks to /private/tmp):
$ curl -s http://127.0.0.1:8090/api/sources?limit=1 | jq -r '.sources[0].memory_dir'
/tmp/mm-cold-load-home/.claude/projects/.../memory
$ curl -s http://127.0.0.1:8090/api/memory-dirs/status | jq -r '.dirs[0].path'
/private/tmp/mm-cold-load-home/.claude/projects/.../memory
The two prefixes (/tmp vs /private/tmp) come from the same on-disk directory but read as distinct strings, breaking client-side dictionary lookups.
Where it bites
packages/memtomem/src/memtomem/web/static/app.js builds STATE.memoryStatusByPath keyed off /api/memory-dirs/status path values, then looks up STATE.memoryStatusByPath[source.memory_dir] to derive the vendor (status.provider) for a given source. With the prefix mismatch the lookup misses, so vendor inference fails — fallbacks downstream end up rendering the source under whatever sub-tab is currently active.
This was caught in #674's verification fixture (which uses /tmp/ as the isolated HOME); switching the fixture to /private/tmp/ made every test pass. The typical real-HOME deployment (/Users/<name>/... on macOS, /home/<name>/... on Linux) doesn't trigger any symlink resolution, so the bug stays invisible — but a user who registers a memory_dir under any symlinked prefix would hit it silently.
Why this is a #668 follow-up
#668 normalized /api/memory-dirs/status to resolve path. The same treatment never reached /api/sources. This issue tracks bringing the two endpoints into agreement so both surfaces use post-resolve paths.
Suggested fix
Apply the same path.resolve() (or the equivalent helper #668 introduced) to the memory_dir field in the /api/sources response. Add a parity test: take a memory_dir under a symlinked prefix and assert the two endpoints' paths match.
Severity
Low — invisible on the dominant real-HOME deployments. Surfaces in CI fixtures, Docker mounts, or any user who registers a memory_dir via a symlinked path. Not blocking — listed as out-of-scope in #674's PR body.
What
/api/sourcesreturns each source'smemory_diras the raw configured path, while/api/memory-dirs/statusreturns the resolved path (afterPath.resolve()).Repro on macOS with a memory_dir registered under
/tmp(which symlinks to/private/tmp):The two prefixes (
/tmpvs/private/tmp) come from the same on-disk directory but read as distinct strings, breaking client-side dictionary lookups.Where it bites
packages/memtomem/src/memtomem/web/static/app.jsbuildsSTATE.memoryStatusByPathkeyed off/api/memory-dirs/statuspathvalues, then looks upSTATE.memoryStatusByPath[source.memory_dir]to derive the vendor (status.provider) for a given source. With the prefix mismatch the lookup misses, so vendor inference fails — fallbacks downstream end up rendering the source under whatever sub-tab is currently active.This was caught in #674's verification fixture (which uses
/tmp/as the isolatedHOME); switching the fixture to/private/tmp/made every test pass. The typical real-HOMEdeployment (/Users/<name>/...on macOS,/home/<name>/...on Linux) doesn't trigger any symlink resolution, so the bug stays invisible — but a user who registers a memory_dir under any symlinked prefix would hit it silently.Why this is a #668 follow-up
#668 normalized
/api/memory-dirs/statusto resolvepath. The same treatment never reached/api/sources. This issue tracks bringing the two endpoints into agreement so both surfaces use post-resolve paths.Suggested fix
Apply the same
path.resolve()(or the equivalent helper #668 introduced) to thememory_dirfield in the/api/sourcesresponse. Add a parity test: take a memory_dir under a symlinked prefix and assert the two endpoints' paths match.Severity
Low — invisible on the dominant real-
HOMEdeployments. Surfaces in CI fixtures, Docker mounts, or any user who registers a memory_dir via a symlinked path. Not blocking — listed as out-of-scope in #674's PR body.