Skip to content

injectionCache is keyed by sessionId alone, so one database's rendered block replays into another #307

Description

@iceteaSA

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.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions