feat(memory): state-keyed repo-map reuse via L3 presence check; embed entries on fresh scan
- Add existsByTurnIdPrefix(prefix) to L3MemoryStore interface; implemented in InMemoryL3MemoryStore (mutex-guarded) and TurboVecL3MemoryStore (metadata map). All test fakes (TrackingL3MemoryStore, CapturingStore) updated. - Introduce RepoMapIndexerPort interface; RepoMapIndexer implements it. Allows injection of test doubles without framework overhead. - ProjectMemoryService.indexAndRecord: reads latest WorkspaceStateObservedEvent from event store (invariant #9 — recorded fact, not re-probe); skips scan+embed when L3 already holds entries for "repomap:<root>:<stateKey>"; passes stateKey to RepoMapComputedEvent; embeds each scanned entry after append; per-entry failures warn-logged (CancellationException rethrown). - Tests: InMemoryL3MemoryStoreTest +2 cases; new ProjectMemoryServiceReuseTest covers same-key skip, changed-key re-index, no-observation always-reindex, and embedding turnId tag verification.
This commit is contained in:
@@ -33,6 +33,9 @@ class InMemoryL3MemoryStore : L3MemoryStore {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun existsByTurnIdPrefix(prefix: String): Boolean =
|
||||
mutex.withLock { entries.any { it.turnId.startsWith(prefix) } }
|
||||
|
||||
override suspend fun close() {
|
||||
// No-op for in-memory store
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@ interface L3MemoryStore {
|
||||
*/
|
||||
suspend fun query(query: L3Query): List<L3Hit>
|
||||
|
||||
/**
|
||||
* Returns true if any stored entry has a [L3MemoryEntry.turnId] that starts with [prefix].
|
||||
*/
|
||||
suspend fun existsByTurnIdPrefix(prefix: String): Boolean
|
||||
|
||||
/**
|
||||
* Close the store and release resources.
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,33 @@ class InMemoryL3MemoryStoreTest {
|
||||
assertEquals("entry-2", hits[1].entry.id, "Second match should be entry-2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByTurnIdPrefix returns true when entry turnId starts with prefix`(): Unit = runBlocking {
|
||||
val store = InMemoryL3MemoryStore()
|
||||
val sessionId = TypeId("session-1")
|
||||
store.store(
|
||||
L3MemoryEntry(
|
||||
id = "e1", sessionId = sessionId, turnId = "repomap:/repo:git:abc123",
|
||||
text = "foo", vector = floatArrayOf(1f), timestampMs = 1L
|
||||
)
|
||||
)
|
||||
assert(store.existsByTurnIdPrefix("repomap:/repo:git:abc123")) { "should match exact" }
|
||||
assert(store.existsByTurnIdPrefix("repomap:/repo:git:")) { "should match prefix" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByTurnIdPrefix returns false when no entry matches`(): Unit = runBlocking {
|
||||
val store = InMemoryL3MemoryStore()
|
||||
val sessionId = TypeId("session-1")
|
||||
store.store(
|
||||
L3MemoryEntry(
|
||||
id = "e1", sessionId = sessionId, turnId = "project:/repo",
|
||||
text = "foo", vector = floatArrayOf(1f), timestampMs = 1L
|
||||
)
|
||||
)
|
||||
assert(!store.existsByTurnIdPrefix("repomap:/repo:git:")) { "should not match different prefix" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `respects sessionIdFilter`(): Unit = runBlocking {
|
||||
val store = InMemoryL3MemoryStore()
|
||||
|
||||
Reference in New Issue
Block a user