injectionCache (inject-compartments.ts:99) is process-global and keyed on sessionId, but the value it holds is rendered from a database — compartments, and the <project-memory> block. Two independent stores that share a session id therefore serve each other's content: on a defer pass, prepareCompartmentInjection replays the cached entry without checking which database produced it.
Reproduced on clean master @ 885e93dc, fresh worktree + bun install. Store A seeds one memory and renders; store B is a brand-new empty store reusing the same session id, on a non-cache-busting pass:
PROBE A: injected=true hasMarker=true
PROBE B: injected=false LEAKED_FROM_A=true
LEAKED_FROM_A=true is store A's <project-memory> block coming back out of store B's call. B has no memories at all — the correct answer is null.
Where it bites today
Test isolation. Two suites that each use ses-1 with their own temp database poison one another through the shared cache. The failure shape is unpleasant: the assertion that breaks is in a file that never touches the injection cache, it only breaks when the two files co-run, and each file passes in isolation. I lost a while bisecting one of these before the cache was even a suspect.
Production
Session ids are unique per store in a normal run, so I can't demonstrate a live-session failure and I'm not claiming one. The invariant is the narrow one: a cache keyed by session id must not serve content derived from a different database. Any path where one process touches more than one store — explicit database paths, a maintenance or migration pass over a second file, a harness driving several — has the same replay available to it.
Fix
Carry the Database handle on each cache entry; discard rather than replay when cached.db !== db; record the db on every write. PR incoming — 17 insertions, and it keeps the defer-replay path otherwise untouched, since that path is load-bearing for prompt-cache stability.
Regression test RED-checks: with the fix reverted it fails with store A's marker present in store B's block.
Gates with the fix: plugin 3758/0, pi-plugin 0 fail, typecheck 0 across three packages. (bun run lint reports one pre-existing error in latch-permanence-guard.test.ts — byte-identical to master and failing there too, unrelated.)
injectionCache(inject-compartments.ts:99) is process-global and keyed onsessionId, but the value it holds is rendered from a database — compartments, and the<project-memory>block. Two independent stores that share a session id therefore serve each other's content: on a defer pass,prepareCompartmentInjectionreplays the cached entry without checking which database produced it.Reproduced on clean
master@885e93dc, fresh worktree +bun install. Store A seeds one memory and renders; store B is a brand-new empty store reusing the same session id, on a non-cache-busting pass:LEAKED_FROM_A=trueis store A's<project-memory>block coming back out of store B's call. B has no memories at all — the correct answer isnull.Where it bites today
Test isolation. Two suites that each use
ses-1with their own temp database poison one another through the shared cache. The failure shape is unpleasant: the assertion that breaks is in a file that never touches the injection cache, it only breaks when the two files co-run, and each file passes in isolation. I lost a while bisecting one of these before the cache was even a suspect.Production
Session ids are unique per store in a normal run, so I can't demonstrate a live-session failure and I'm not claiming one. The invariant is the narrow one: a cache keyed by session id must not serve content derived from a different database. Any path where one process touches more than one store — explicit database paths, a maintenance or migration pass over a second file, a harness driving several — has the same replay available to it.
Fix
Carry the
Databasehandle on each cache entry; discard rather than replay whencached.db !== db; record the db on every write. PR incoming — 17 insertions, and it keeps the defer-replay path otherwise untouched, since that path is load-bearing for prompt-cache stability.Regression test RED-checks: with the fix reverted it fails with store A's marker present in store B's block.
Gates with the fix: plugin 3758/0, pi-plugin 0 fail, typecheck 0 across three packages. (
bun run lintreports one pre-existing error inlatch-permanence-guard.test.ts— byte-identical to master and failing there too, unrelated.)