Skip to content

fix(memory): harden kernel reliability#1891

Merged
yyhhyyyyyy merged 2 commits into
devfrom
fix/memory-kernel-reliability
Jul 7, 2026
Merged

fix(memory): harden kernel reliability#1891
yyhhyyyyyy merged 2 commits into
devfrom
fix/memory-kernel-reliability

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator
  • Unify superseded provenance collision handling across write exits
  • Add restore audit ordering for forget tombstones
  • Keep vector recall ready when orphan reconcile fails
  • Make error retry bounded and fair
  • Harden vector delete with row embedding snapshots
  • Settle manual reindex pending state from backend status

Summary by CodeRabbit

  • New Features

    • Added a manual memory reindex action with “reindexing” in-progress UI on the health panel.
    • Introduced cursor-based maintenance for working candidates and embedding requeueing to improve fairness and responsiveness.
    • Added an embedding ID listing capability to support paginated vector maintenance.
  • Bug Fixes

    • Improved decision/update conflict handling to preserve or revive the correct live memory targets.
    • Strengthened maintenance retries and background cleanup with bounded error retry and orphan-vector reconciliation.
    • More robust decay/consolidation bookkeeping and transactional audit/state updates.
  • Documentation / i18n

    • Updated the architecture spec and added localization strings for reindex/reindexing.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR updates agent memory contracts, storage, write coordination, embedding recovery, maintenance flows, reindex routing, renderer controls, documentation, and tests.

Changes

Agent Memory Reindex & Write-Path Overhaul

Layer / File(s) Summary
Contracts and SQLite storage
src/main/presenter/memoryPresenter/types.ts, src/main/presenter/memoryPresenter/runtimeConstants.ts, src/main/presenter/sqlitePresenter/tables/agentMemory.ts, src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts, src/main/presenter/memoryPresenter/infra/memoryVectorStore.ts
Extends presenter contracts and SQLite-backed memory storage with cursor pagination, transaction support, status views, decay refresh, consolidation stamping, audit lookup, and vector ID listing.
Provenance and decision write path
src/main/presenter/memoryPresenter/services/rowMutations.ts, src/main/presenter/memoryPresenter/services/retrievalService.ts, src/main/presenter/memoryPresenter/services/writeCoordinator.ts
Reworks provenance-hit handling, decision retrieval, and write coordination to support structured outcomes, live-target checks, and revival or folding across update, supersede, challenge, and direct-add paths.
Embedding retry and vector cleanup
src/main/presenter/memoryPresenter/infra/vectorStoreManager.ts, src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts
Updates vector deletion semantics, embedding drain retry logic, and orphan-vector reconciliation with per-agent tracking, cooldowns, and cleanup of stale vector refs.
Maintenance, working memory, and management
src/main/presenter/memoryPresenter/services/maintenanceService.ts, src/main/presenter/memoryPresenter/services/workingMemoryService.ts, src/main/presenter/memoryPresenter/services/managementService.ts, src/main/presenter/memoryPresenter/services/conflictService.ts
Moves decay and consolidation maintenance into repository helpers, bounds working-memory scanning, wraps status mutations in transactions, and changes vector deletion and reindex-trigger behavior in management flows.
Reindex API and UI surface
src/main/presenter/memoryPresenter/index.ts, src/shared/contracts/routes/memory.routes.ts, src/shared/contracts/routes.ts, src/main/routes/index.ts, src/renderer/api/MemoryClient.ts, src/renderer/settings/components/MemoryHealthSection.vue, src/renderer/settings/components/MemoryManagerPanel.vue, src/renderer/src/i18n/*/settings.json
Adds the memory.reindex route contract and dispatcher, wires the presenter and renderer client to it, and adds the reindex control and state to the memory health UI with localized labels.
Architecture spec updates
docs/architecture/agent-memory-system/spec.md
Updates the agent memory architecture spec to describe the revised retry, reconciliation, provenance, transaction, audit, and reindex behavior.
Backend tests
test/main/presenter/*, test/main/routes/*
Covers cursor pagination, bounded retry, provenance revival, orphan-vector reconciliation, vector deletion, and route contract behavior across the presenter and route layers.
Renderer tests
test/renderer/*
Covers the new reindex client call, health control UI, and pending-state lifecycle in renderer components.
Presenter fakes and test scaffolding
test/main/presenter/fakes/memoryFakes.ts, test/main/presenter/memoryExtraction.test.ts, test/main/presenter/memoryPresenter.test.ts, test/main/presenter/memoryVectorStore.test.ts
Expands fake repositories and vector stores to support pagination, transactions, retry state, audit checks, and the new reindex/vector cleanup flows.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and broadly matches the memory reliability hardening work in this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/memory-kernel-reliability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts (1)

39-49: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Track orphan reconciliation in in-flight cleanup. reconcileOrphanVectorsOnce() is fire-and-forget, so cleanupAgent() can clear orphanVectorReconciled / orphanVectorReconcileRetryAt before that task settles. withAgentLock() still serializes it through vectorStore.settleAgent(), but a late completion can repopulate stale per-agent state and suppress the next reconcile for the same agent/key.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts` around lines
39 - 49, Track orphan reconciliation as an in-flight task in EmbeddingPipeline
so cleanupAgent() does not clear
orphanVectorReconciled/orphanVectorReconcileRetryAt before
reconcileOrphanVectorsOnce() settles. Add a per-agent promise/settlement entry
alongside the existing warmup/drain maps, register it when
reconcileOrphanVectorsOnce() starts, and remove or ignore stale completion
updates after cleanupAgent() has run. Make sure withAgentLock() and
vectorStore.settleAgent() still serialize the work, but late completions do not
repopulate stale agent state.
🧹 Nitpick comments (5)
test/main/presenter/memoryPresenter.test.ts (1)

2932-3013: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Repeated inline IMemoryVectorStore stubs — consider a shared factory.

Several tests hand-roll the full IMemoryVectorStore shape inline instead of using the existing FakeVectorStore (already imported/used elsewhere in this file, e.g. around line 3195) with targeted overrides. This diff had to add listMemoryIds to 5+ separate inline stubs; a shared makeVectorStoreStub(overrides) helper (or reusing FakeVectorStore with method overrides) would prevent this churn on the next interface change.

Also applies to: 4118-4118, 6517-6517, 6655-6665

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/presenter/memoryPresenter.test.ts` around lines 2932 - 3013, The
tests are duplicating full IMemoryVectorStore stubs inline, which makes every
interface change require edits in many places. Refactor the affected cases in
MemoryPresenter tests to use the existing FakeVectorStore or a shared
makeVectorStoreStub(overrides) helper, and keep only the per-test behavior
overrides such as upsert, query, deleteByMemoryIds, or isUsable. This will
centralize the IMemoryVectorStore shape and avoid repeated additions like
listMemoryIds across the file.
test/main/presenter/memoryExtraction.test.ts (1)

537-801: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consolidate duplicate FakeRepository implementation.

makeFakeRepo() duplicates most of FakeRepository (memoryFakes.ts) — insert, requeueForEmbedding, listEmbeddingStatusIds, runInTransaction, listWorkingCandidates, etc. — and is passed via repository: repo as any at call sites, so TypeScript never verifies it actually satisfies MemoryRepositoryPort. This PR had to add ~7 new methods to both copies in lockstep; future port changes will require the same duplicate maintenance, and a missed method here would only fail at runtime instead of compile time.

Consider replacing makeFakeRepo() with new FakeRepository() from memoryFakes.ts and dropping the as any cast.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/presenter/memoryExtraction.test.ts` around lines 537 - 801,
`makeFakeRepo()` is a duplicated fake repository that mirrors `FakeRepository`
and is being passed as `repository: repo as any`, which bypasses compile-time
checking. Replace the local fake in memoryExtraction.test.ts with the shared
`FakeRepository` from memoryFakes.ts so methods like `insert`,
`requeueForEmbedding`, `listEmbeddingStatusIds`, `runInTransaction`, and
`listWorkingCandidates` stay in sync. Remove the `as any` cast at the call sites
so TypeScript verifies the object still satisfies `MemoryRepositoryPort`.
src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts (1)

87-122: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Error-retry cooldown isn't engaged when requeued comes back 0 despite found retryIds.

If listEmbeddingStatusIds returns ids but requeueForEmbedding requeues 0 rows (e.g., a race where the rows' status changed between the two calls), errorRetryAt/errorRetryAfterId are never updated (Lines 116-120 only run if (requeued > 0)). This means the next drainPendingEmbeddings call with no pending rows will immediately re-run the same listEmbeddingStatusIds + requeueForEmbedding pair without the intended ERROR_RETRY_COOLDOWN_MS cooldown, effectively bypassing the cooldown on repeated calls until a requeue eventually succeeds.

🔧 Proposed fix: always record retry attempt timestamp
-        if (requeued > 0) {
-          this.errorRetryAt.set(agentId, now)
+        if (retryIds.length) {
+          this.errorRetryAt.set(agentId, now)
+        }
+        if (requeued > 0) {
           this.errorRetryAfterId.set(agentId, retryIds[retryIds.length - 1])
           pending = this.ctx.deps.repository.listPendingEmbedding(limit, agentId)
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts` around lines
87 - 122, The retry cooldown in the embedding pipeline is only recorded when
`requeueForEmbedding` returns a positive count, so `drainPendingEmbeddings` can
immediately retry the same `listEmbeddingStatusIds` path after a 0-row requeue.
Update the `EmbeddingPipeline` logic to always set `errorRetryAt` for the agent
whenever retry IDs are found and an attempt is made, and only set
`errorRetryAfterId` from the latest attempted ID when appropriate, so the
cooldown in `ERROR_RETRY_COOLDOWN_MS` is enforced even if `requeueForEmbedding`
returns 0.
src/renderer/settings/components/MemoryHealthSection.vue (1)

153-157: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: announce reindex state change to assistive tech.

The label/icon swap on click is visual-only; screen-reader users focused elsewhere won't be notified that reindexing started unless the button itself is re-announced. Consider aria-live="polite" on a wrapping element or aria-busy="reindexing" on the button for better feedback.

♿ Optional accessibility hint
           <Button
             type="button"
             variant="outline"
             size="sm"
             class="h-7 px-2 text-xs"
             :disabled="reindexing"
+            :aria-busy="reindexing"
             `@click`="emit('reindex')"
           >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/settings/components/MemoryHealthSection.vue` around lines 153 -
157, The reindexing status change is only reflected visually through the Icon
swap in MemoryHealthSection.vue, so assistive tech may not announce it. Update
the reindex trigger UI in MemoryHealthSection to expose the state change via an
accessibility attribute such as aria-busy on the button or aria-live on a nearby
wrapper, and ensure it toggles based on the reindexing state so screen readers
are notified when reindexing starts and ends.
test/renderer/components/MemoryHealthSection.test.ts (1)

226-239: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a disabled-state assertion to lock in the guard.

The tests cover the emit and the label swap but don't assert the button is actually disabled while reindexing is true. Since the Button mock's <button> inherits the disabled fallthrough attribute automatically (no declared props on the mock), this is a cheap addition that protects against a future regression re-enabling double-submits.

✅ Suggested addition
     const running = mountSection({ health: loadedHealth, reindexing: true })
     expect(running.find('button').text()).toContain(
       'settings.deepchatAgents.memoryManager.health.reindexing'
     )
+    expect(running.find('button').attributes('disabled')).not.toBeUndefined()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/renderer/components/MemoryHealthSection.test.ts` around lines 226 - 239,
The MemoryHealthSection test covers the reindex emit and label change but does
not assert the button is disabled during reindexing. Update the test around the
mountSection/loadedHealth/reindexing flow to also verify the rendered button has
the disabled state when reindexing is true, using the existing button lookup in
MemoryHealthSection.test.ts so the guard against double-submits stays covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/presenter/memoryPresenter/infra/vectorStoreManager.ts`:
- Around line 178-238: The fallback logic in deleteVectorsForMemoryIdsOpening
incorrectly couples embedding and dimension resolution, so a valid
options.embeddingModel can be overwritten when only embeddingDim is missing.
Update the resolution flow in
VectorStoreManager.deleteVectorsForMemoryIdsOpening to handle targetEmbedding
and targetDimensions independently: keep the parsed embeddingFingerprint result
when present, only fall back to resolveAgentConfig/getCurrentEmbeddingDimension
for the missing dimension, and preserve the originally requested store identity
when opening/deleting vectors.

In `@src/main/presenter/memoryPresenter/services/rowMutations.ts`:
- Around line 126-150: The provenance absorption flow in rowMutations should be
atomic with the fold: handleProvenanceHit() currently revives archived owners
before applyContentUpdate() enters the transaction, so a failure later in the
same flow can leave the owner revived without the fold. Refactor
handleProvenanceHit() to return only a decision (or defer the revive mutation),
and move the absorbed-status update into the existing
repository.runInTransaction block in applyContentUpdate() so the revive,
category update, and markSuperseded all commit or roll back together; apply the
same fix in the other affected call site as well.

In `@src/main/presenter/memoryPresenter/services/writeCoordinator.ts`:
- Around line 410-417: The fallback in writeCoordinator’s challenge flow can act
on a stale target because it passes target.id directly into
reviveProvenanceOwner without re-validating liveness first. Add the same
archived/superseded check used in the successful challenge path before calling
reviveProvenanceOwner, and only proceed when the current target is still live.
Use the existing currentTarget handling in writeCoordinator as the reference
point so the duplicate-folding path mirrors the guarded mutation path.

In `@src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts`:
- Around line 236-271: `hasForgetEvent` is using a fixed `LIMIT 200` before
checking whether a row references the target `memoryId`, so older forget/archive
events can be skipped. Update the query in `agentMemoryAudit.ts` so the
`memoryId` filter is applied in SQL (or remove the limit/window entirely) and
keep the existing `memory/restore` override behavior in `hasForgetEvent`. Ensure
the lookup still uses `metadataReferencesMemoryId`-equivalent matching semantics
for `input_refs_json` and `output_refs_json`.

---

Outside diff comments:
In `@src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts`:
- Around line 39-49: Track orphan reconciliation as an in-flight task in
EmbeddingPipeline so cleanupAgent() does not clear
orphanVectorReconciled/orphanVectorReconcileRetryAt before
reconcileOrphanVectorsOnce() settles. Add a per-agent promise/settlement entry
alongside the existing warmup/drain maps, register it when
reconcileOrphanVectorsOnce() starts, and remove or ignore stale completion
updates after cleanupAgent() has run. Make sure withAgentLock() and
vectorStore.settleAgent() still serialize the work, but late completions do not
repopulate stale agent state.

---

Nitpick comments:
In `@src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts`:
- Around line 87-122: The retry cooldown in the embedding pipeline is only
recorded when `requeueForEmbedding` returns a positive count, so
`drainPendingEmbeddings` can immediately retry the same `listEmbeddingStatusIds`
path after a 0-row requeue. Update the `EmbeddingPipeline` logic to always set
`errorRetryAt` for the agent whenever retry IDs are found and an attempt is
made, and only set `errorRetryAfterId` from the latest attempted ID when
appropriate, so the cooldown in `ERROR_RETRY_COOLDOWN_MS` is enforced even if
`requeueForEmbedding` returns 0.

In `@src/renderer/settings/components/MemoryHealthSection.vue`:
- Around line 153-157: The reindexing status change is only reflected visually
through the Icon swap in MemoryHealthSection.vue, so assistive tech may not
announce it. Update the reindex trigger UI in MemoryHealthSection to expose the
state change via an accessibility attribute such as aria-busy on the button or
aria-live on a nearby wrapper, and ensure it toggles based on the reindexing
state so screen readers are notified when reindexing starts and ends.

In `@test/main/presenter/memoryExtraction.test.ts`:
- Around line 537-801: `makeFakeRepo()` is a duplicated fake repository that
mirrors `FakeRepository` and is being passed as `repository: repo as any`, which
bypasses compile-time checking. Replace the local fake in
memoryExtraction.test.ts with the shared `FakeRepository` from memoryFakes.ts so
methods like `insert`, `requeueForEmbedding`, `listEmbeddingStatusIds`,
`runInTransaction`, and `listWorkingCandidates` stay in sync. Remove the `as
any` cast at the call sites so TypeScript verifies the object still satisfies
`MemoryRepositoryPort`.

In `@test/main/presenter/memoryPresenter.test.ts`:
- Around line 2932-3013: The tests are duplicating full IMemoryVectorStore stubs
inline, which makes every interface change require edits in many places.
Refactor the affected cases in MemoryPresenter tests to use the existing
FakeVectorStore or a shared makeVectorStoreStub(overrides) helper, and keep only
the per-test behavior overrides such as upsert, query, deleteByMemoryIds, or
isUsable. This will centralize the IMemoryVectorStore shape and avoid repeated
additions like listMemoryIds across the file.

In `@test/renderer/components/MemoryHealthSection.test.ts`:
- Around line 226-239: The MemoryHealthSection test covers the reindex emit and
label change but does not assert the button is disabled during reindexing.
Update the test around the mountSection/loadedHealth/reindexing flow to also
verify the rendered button has the disabled state when reindexing is true, using
the existing button lookup in MemoryHealthSection.test.ts so the guard against
double-submits stays covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f7edef38-e20e-447b-9c0b-cd30c255a4c9

📥 Commits

Reviewing files that changed from the base of the PR and between 7c69d03 and 4ac8f45.

📒 Files selected for processing (52)
  • docs/architecture/agent-memory-system/spec.md
  • src/main/presenter/memoryPresenter/index.ts
  • src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts
  • src/main/presenter/memoryPresenter/infra/memoryVectorStore.ts
  • src/main/presenter/memoryPresenter/infra/vectorStoreManager.ts
  • src/main/presenter/memoryPresenter/runtimeConstants.ts
  • src/main/presenter/memoryPresenter/services/conflictService.ts
  • src/main/presenter/memoryPresenter/services/maintenanceService.ts
  • src/main/presenter/memoryPresenter/services/managementService.ts
  • src/main/presenter/memoryPresenter/services/retrievalService.ts
  • src/main/presenter/memoryPresenter/services/rowMutations.ts
  • src/main/presenter/memoryPresenter/services/workingMemoryService.ts
  • src/main/presenter/memoryPresenter/services/writeCoordinator.ts
  • src/main/presenter/memoryPresenter/types.ts
  • src/main/presenter/sqlitePresenter/tables/agentMemory.ts
  • src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts
  • src/main/routes/index.ts
  • src/renderer/api/MemoryClient.ts
  • src/renderer/settings/components/MemoryHealthSection.vue
  • src/renderer/settings/components/MemoryManagerPanel.vue
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/es-ES/settings.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/it-IT/settings.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/memory.routes.ts
  • test/main/presenter/agentMemoryTable.test.ts
  • test/main/presenter/fakes/memoryFakes.ts
  • test/main/presenter/memoryExtraction.test.ts
  • test/main/presenter/memoryPresenter.test.ts
  • test/main/presenter/memoryVectorStore.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/routes/memoryDto.test.ts
  • test/renderer/api/clients.test.ts
  • test/renderer/components/MemoryHealthSection.test.ts
  • test/renderer/components/MemoryManagerDialog.test.ts

Comment thread src/main/presenter/memoryPresenter/infra/vectorStoreManager.ts
Comment thread src/main/presenter/memoryPresenter/services/rowMutations.ts
Comment thread src/main/presenter/memoryPresenter/services/writeCoordinator.ts
Comment thread src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts (1)

236-270: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Unbounded scan on every hasForgetEvent call.

Removing LIMIT 200 correctly fixes the previously-flagged missed-forget-event bug, but the query now has no bound at all — it fetches every completed memory/forget/memory/archive/memory/restore row for the agent on each call, then filters in JS by memoryId. For agents with long audit histories, and given this is likely invoked per-memory during vector recall/reconciliation, this can become a real hot-path cost that grows unbounded with the agent's history.

Consider adding a cheap SQL-level pre-filter on input_refs_json/output_refs_json (e.g. LIKE '%"memoryId":"' || ? || '"%') to narrow the row set before the exact metadataReferencesMemoryId check, keeping correctness while avoiding pulling the entire history into memory each time.

⚡ Proposed SQL pre-filter
   hasForgetEvent(agentId: string, memoryId: string): boolean {
     const rows = this.db
       .prepare(
         `SELECT event_type,
                 actor_type,
                 input_refs_json,
                 output_refs_json
          FROM agent_memory_audit
          WHERE agent_id = ?
            AND status = 'completed'
            AND (
              (event_type = 'memory/forget' AND actor_type = 'runtime')
              OR (event_type = 'memory/archive' AND actor_type = 'user')
              OR event_type = 'memory/restore'
            )
+           AND (input_refs_json LIKE ? OR output_refs_json LIKE ?)
          ORDER BY created_at DESC, id DESC`
       )
-      .all(agentId) as Array<{
+      .all(agentId, `%"memoryId":"${memoryId}"%`, `%"memoryId":"${memoryId}"%`) as Array<{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts` around lines
236 - 270, `hasForgetEvent` now scans the full `agent_memory_audit` history for
every call, which can become expensive on long-lived agents. Update the query in
`agentMemoryAudit.ts` to add a cheap SQL-side pre-filter on `input_refs_json`
and `output_refs_json` before the existing `metadataReferencesMemoryId` check,
so only likely-matching rows are loaded while preserving the current
restore/forget/archive precedence logic in `hasForgetEvent`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts`:
- Around line 236-270: `hasForgetEvent` now scans the full `agent_memory_audit`
history for every call, which can become expensive on long-lived agents. Update
the query in `agentMemoryAudit.ts` to add a cheap SQL-side pre-filter on
`input_refs_json` and `output_refs_json` before the existing
`metadataReferencesMemoryId` check, so only likely-matching rows are loaded
while preserving the current restore/forget/archive precedence logic in
`hasForgetEvent`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1679d5bc-2bda-421e-bc20-7e339f516d3c

📥 Commits

Reviewing files that changed from the base of the PR and between 4ac8f45 and 8f6e5ab.

📒 Files selected for processing (9)
  • src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts
  • src/main/presenter/memoryPresenter/infra/vectorStoreManager.ts
  • src/main/presenter/memoryPresenter/services/rowMutations.ts
  • src/main/presenter/memoryPresenter/services/writeCoordinator.ts
  • src/main/presenter/sqlitePresenter/tables/agentMemoryAudit.ts
  • src/renderer/settings/components/MemoryHealthSection.vue
  • test/main/presenter/agentMemoryTable.test.ts
  • test/main/presenter/memoryPresenter.test.ts
  • test/renderer/components/MemoryHealthSection.test.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • test/renderer/components/MemoryHealthSection.test.ts
  • src/renderer/settings/components/MemoryHealthSection.vue
  • test/main/presenter/agentMemoryTable.test.ts
  • src/main/presenter/memoryPresenter/infra/vectorStoreManager.ts
  • src/main/presenter/memoryPresenter/services/writeCoordinator.ts
  • src/main/presenter/memoryPresenter/infra/embeddingPipeline.ts
  • src/main/presenter/memoryPresenter/services/rowMutations.ts
  • test/main/presenter/memoryPresenter.test.ts

@yyhhyyyyyy yyhhyyyyyy merged commit 0bd5fb0 into dev Jul 7, 2026
3 checks passed
@yyhhyyyyyy yyhhyyyyyy deleted the fix/memory-kernel-reliability branch July 7, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant