Skip to content

feat(memory): evolve agent memory architecture - #2036

Merged
yyhhyyyyyy merged 47 commits into
devfrom
feat/agent-memory-evolution
Jul 27, 2026
Merged

feat(memory): evolve agent memory architecture#2036
yyhhyyyyyy merged 47 commits into
devfrom
feat/agent-memory-evolution

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Evolve Agent Memory around authoritative atomic claims, temporal correctness, rebuildable projections, incremental consolidation, and an explicit user-controlled directive plane.

This improves memory continuity, preference adherence, time-aware correctness, correction handling, observability, and long-term maintainability.

What changed

  • Extend atomic claims with:
    • memory scope
    • factual confidence
    • independent temporal confidence
    • temporal kind, validity interval, precision, and timezone
    • durable provenance and derivation lineage
  • Evaluate temporal validity at retrieval and injection time.
    • High-confidence expired states are excluded from current context.
    • Events remain available as historical facts.
    • Expired plans remain “previously planned” and are never treated as completed events.
  • Add deterministic, rebuildable working projections with temporal boundary refresh.
  • Replace broad consolidation scans with bounded dirty-claim consolidation.
  • Add persistent merge, supersede, reflection, and manual-edit derivations.
  • Add hash-based exact-provenance tombstones to prevent forgotten claims from being recreated by replay.
  • Add a typed directive plane:
    • manually created directives become active immediately
    • extracted suggestions remain drafts until explicitly approved
    • directives are injected as user-level contributions and cannot override system instructions
    • suppression directives also filter matching memory recall
  • Add bounded maintenance budgets, local domain clocks, scoped retrieval, and detailed retrieval diagnostics.
  • Add schema migration, startup repair, import isolation, and backward-compatible parsing.
  • Add localized Directive and memory diagnostic UI.

Summary by CodeRabbit

  • New Features

    • Added a Directives tab to manage standing instructions and topic suppressions (draft/approve/reject/delete), including directive drafts in the inbox bar.
    • Enabled scope-aware memory recall/search and added temporal validity plus annotations to memory results.
    • Added structured working-memory projections and turn-level contribution budget allocation breakdown (directive/persona/working/queryRecall).
  • Improvements

    • Hardened forgetting, re-learning, and deletion/clear behavior to prevent unintended replay; agent deletion now retains directives via retirement.
    • Extended memory update events and UI to surface directive identifiers and allocation details.
  • Documentation

    • Published Agent Memory Evolution plan/spec/tasks and updated the core memory-system architecture overview.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements a large "Agent Memory Evolution" architecture change: temporal claim metadata, scope-aware applicability, exact-forgetting via tombstones, durable derivation lineage with bounded dirty-work consolidation, a directive plane (create/approve/reject/delete standing directives), and a budgeted context allocator. Changes span storage schema/migrations, memory services, DeepChat agent runtime integration, renderer UI, i18n, and extensive tests.

Changes

Agent Memory Evolution

Layer / File(s) Summary
Architecture docs
docs/architecture/agent-memory-evolution/*, docs/architecture/memory-system.md
Adds plan/spec/tasks documents and updates the memory-system doc describing temporal, directive, scope, tombstone, and consolidation design.
Shared types & contracts
src/shared/types/agent-memory.ts, src/shared/contracts/routes/memory.routes.ts, src/shared/contracts/routes.ts, src/shared/contracts/events/memory.events.ts, src/shared/types/tape-view-manifest.ts, src/types/i18n.d.ts
Adds temporal/scope/directive constants, directive route contracts, updated memory item/status schemas, and widened tape manifest schema version.
Core domain logic
src/main/memory/core/*, src/main/memory/domain/*, src/shared/lib/memoryDirectiveTopic.ts
Implements temporal policy/normalization, scope predicates, tombstone identities, directive normalization/contribution/policy, contribution budget allocator, structured working projection, and domain clock.
Runtime context & ports
src/main/memory/context.ts, ports.ts, types.ts, injection.ts, runtimeConstants.ts, index.ts
Adds domain-clock support, expands repository ports for lineage/dirty/directive, and wires DirectiveService into MemoryService.
Storage schema & migrations
src/main/memory/data/tables/*, src/main/data/schemaCatalog.ts, src/main/data/sqliteCopyExclusions.ts, src/main/sync/dataImporter.ts
Adds agent_memory_directive table, extends agent_memory with temporal/scope/tombstone/derivation/dirty artifacts and migrations, updates importer for new columns.
Memory services
src/main/memory/services/*, src/main/memory/routes.ts
Adds DirectiveService and updates conflict/maintenance/management/persona/reflection/retrieval/rowMutations/workingMemory/writeCoordinator services and route DTOs.
DeepChat agent runtime integration
src/main/agent/deepchat/**, src/main/app/composition.ts, src/main/tool/*
Wires memory and directive contributions into prompt building, context assembly, and agent deletion (namespace retirement).
Tape replay support
src/main/tape/**
Extends tape view manifest/capabilities to represent directive contributions and allocation budgets.
Renderer UI & i18n
src/renderer/**, src/renderer/src/i18n/*/*.json
Adds MemoryDirectivesPanel, extends inbox/settings/turn-dialog UI, and localizes directive/budget strings across all locales.
Tests
test/main/**, test/renderer/**, test/fixtures/**
Extensive unit/integration tests covering all above areas.

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

Possibly related PRs

  • ThinkInAIXYZ/deepchat#1962: Both PRs modify MemoryRuntimeCoordinator.contribute() to enforce bounded timeouts and change injection/assembly behavior on timeout.
  • ThinkInAIXYZ/deepchat#1868: Extends the same memory.updated event/payload plumbing with directiveId and directive-specific update reasons.
  • ThinkInAIXYZ/deepchat#1996: Both PRs touch the Tape view-manifest layer around schema versioning and synthetic contribution reasons.

Suggested reviewers: zerob13

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 accurately captures the broad agent-memory architecture changes in this PR.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-memory-evolution

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.

- validate batched decisions through scope-aware revalidation
- model an active winner in sibling retirement performance coverage
- refresh the renderer boundary baseline for directive notifications

@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: 7

Caution

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

⚠️ Outside diff range comments (1)
src/shared/contracts/routes/memory.routes.ts (1)

121-144: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Enforce the temporal metadata invariants in response DTOs.

These schemas accept atemporal rows with temporal fields, non-atemporal rows without temporalConfidence, and reversed intervals. That violates the documented shared contract and can expose malformed temporal state to management and retrieval clients.

Proposed fix
+function enforceProjectedTemporalInvariant(
+  value: {
+    temporalKind: (typeof AGENT_MEMORY_TEMPORAL_KINDS)[number]
+    validFrom: number | null
+    validUntil: number | null
+    temporalConfidence: number | null
+    temporalPrecision: (typeof AGENT_MEMORY_TEMPORAL_PRECISIONS)[number] | null
+    temporalTimeZone: string | null
+  },
+  context: z.RefinementCtx
+): void {
+  if (value.temporalKind === 'atemporal') {
+    if (
+      value.validFrom !== null ||
+      value.validUntil !== null ||
+      value.temporalConfidence !== null ||
+      value.temporalPrecision !== null ||
+      value.temporalTimeZone !== null
+    ) {
+      context.addIssue({ code: 'custom', message: 'atemporal memories cannot carry temporal metadata' })
+    }
+  } else if (
+    value.temporalConfidence === null ||
+    (value.validFrom !== null && value.validUntil !== null && value.validFrom >= value.validUntil)
+  ) {
+    context.addIssue({ code: 'custom', message: 'invalid temporal metadata' })
+  }
+}
🤖 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/shared/contracts/routes/memory.routes.ts` around lines 121 - 144, Update
enforceProjectedScopeInvariant, which is applied to both MemoryItemSchema and
MemorySearchResultSchema, to validate temporal metadata: atemporal rows must not
carry temporal fields, non-atemporal rows must provide temporalConfidence, and
any populated validFrom/validUntil interval must not be reversed. Preserve the
existing projected-scope checks while ensuring both response DTOs reject these
malformed states.
🧹 Nitpick comments (23)
src/renderer/settings/components/MemoryInboxBar.vue (1)

322-385: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the shared directive approve/reject state machine into a composable.

setDirectivePending/approveDirective/rejectDirective here duplicate setPending/transition in MemoryDirectivesPanel.vue (same revision-guard, pending-set, rejected-reason, reload-on-error pattern). A shared useDirectiveActions() composable would keep the two panels from drifting.

🤖 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/MemoryInboxBar.vue` around lines 322 - 385,
Extract the shared directive action state machine from setDirectivePending,
approveDirective, and rejectDirective into a useDirectiveActions composable,
then update MemoryInboxBar.vue and MemoryDirectivesPanel.vue to use it. Preserve
the existing revision guard, pending-ID tracking, rejected-reason notification,
stale-agent protection, and reload-on-error behavior in both panels.
src/renderer/src/components/chat/MemoryTurnDialog.vue (1)

94-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Prefer an interpolated message over label + value concatenation.

{{ t('...overhead') }} {{ n }} · {{ t('...unused') }} {{ n }} hardcodes ordering and the · separator in the template, which is awkward for RTL locales (fa-IR, he-IL) and for languages that place the value first. A single key such as t('chat.memory.turn.overheadSummary', { overhead, unused }) would let each locale control layout.

🤖 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/src/components/chat/MemoryTurnDialog.vue` around lines 94 - 97,
Replace the label-and-value concatenation in the MemoryTurnDialog template with
one interpolated translation using a key such as
chat.memory.turn.overheadSummary and named overhead and unused values. Add the
corresponding locale message entries so translators control ordering and
separators, while preserving the displayed token values.

Source: Coding guidelines

src/main/memory/core/workingProjection.ts (2)

216-229: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Redundant temporal recomputation. resolveNextRefreshAt re-derives temporalMetadataFromRow for every row although toCandidate already computed it; pass the computed metadata through instead.

🤖 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/memory/core/workingProjection.ts` around lines 216 - 229, Update
resolveNextRefreshAt to accept and use the temporal metadata already computed by
toCandidate, rather than calling temporalMetadataFromRow for each row. Adjust
the caller and input shape as needed while preserving selection of the earliest
future validFrom or validUntil boundary.

269-288: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Quadratic re-render during budget selection.

Each accepted/rejected candidate re-renders the entire projection (renderSections(next)) and re-runs estimateTokens over the full string, so cost grows as O(N²) in candidate count and string size. With a section-label overhead table you can track the incremental token cost per line instead, and only render once at the end.

🤖 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/memory/core/workingProjection.ts` around lines 269 - 288, The
budget-selection loop in working projection repeatedly calls renderSections and
estimateTokens for every candidate, causing quadratic work. Replace the
per-candidate full rendering in the selection logic with incremental token
accounting using the section-label overhead table, accept candidates only when
their incremental line cost stays within budget, and render the final selected
projection once after selection.
src/main/memory/core/scoring.ts (1)

20-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Merge the duplicated ../domain/types type imports. Two separate import type statements target the same module.

🤖 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/memory/core/scoring.ts` around lines 20 - 29, Merge the two
type-only imports from ../domain/types in scoring.ts into a single import
declaration containing MemoryScope, AgentMemoryKind, MemoryTemporalMetadata, and
MemoryTemporalPolicyResult; leave all other imports unchanged.
src/main/memory/domain/directives.ts (2)

128-135: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead guard. assertBoundedText already throws on empty input, so line 134 can never trigger.

🤖 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/memory/domain/directives.ts` around lines 128 - 135, Remove the
redundant empty-topic guard after the `normalizedTopic = assertBoundedText(...)`
call in the directive normalization flow. Keep the existing `assertBoundedText`
validation and normalization behavior unchanged.

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

Use the node: protocol for consistency.

src/main/memory/core/tombstone.ts imports node:crypto; this module uses the bare specifier.

♻️ Proposed change
-import { createHash } from 'crypto'
+import { createHash } from 'node:crypto'
🤖 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/memory/domain/directives.ts` at line 1, Update the crypto import in
the directives module to use the `node:crypto` specifier, matching the
convention used by the nearby tombstone module and preserving the existing
createHash usage.
src/main/memory/core/temporal.ts (1)

376-388: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Double normalization on the retrieval hot path.

Callers such as fuse in src/main/memory/core/scoring.ts (lines 212-217) already build temporal via temporalMetadataFromRow, which normalizes; evaluateMemoryTemporalPolicy then normalizes again per candidate (including timezone canonicalization). Consider trusting an already-canonical MemoryTemporalMetadata here, or exposing a evaluateNormalizedMemoryTemporalPolicy variant for hot paths.

🤖 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/memory/core/temporal.ts` around lines 376 - 388, The retrieval path
redundantly normalizes temporal metadata in evaluateMemoryTemporalPolicy. Add or
use a normalized-policy evaluation path, such as
evaluateNormalizedMemoryTemporalPolicy, and update callers like fuse that
already use temporalMetadataFromRow to call it, while preserving normalization
for untrusted direct callers.
src/main/memory/index.ts (1)

615-641: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer explicit parameter/return types over Parameters<DirectiveService[...]> on the public facade.

These are public MemoryService methods consumed across the route/DTO boundary; deriving their signatures from DirectiveService internals makes the contract opaque at call sites and lets an internal signature change silently reshape the public API. Naming the shared directive input/result types directly would keep the facade self-describing.

🤖 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/memory/index.ts` around lines 615 - 641, The public MemoryService
directive facade methods listDirectives, createDirective, and
createDirectiveResult should use explicit shared directive input, result,
options, source, and return types instead of Parameters<DirectiveService[...]>.
Import the existing shared types and annotate each method’s parameters and
return value directly, preserving the current delegation behavior and public API
semantics.
src/main/memory/core/injectionPort.ts (1)

388-402: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deduplicate recall ordering/rendering with assembleMemorySection.

buildRecallCandidates repeats the kind !== 'working' filter, the non-episodic/episodic ordering, and the content + temporalAnnotation rendering already present at Lines 326-338. Two copies of this policy will drift (e.g. a future annotation format change applied in one place only).

♻️ Suggested direction
-  const recalled = payload.memories.filter((memory) => memory.kind !== 'working')
-  const ordered = [
-    ...recalled.filter((memory) => memory.kind !== 'episodic'),
-    ...recalled.filter((memory) => memory.kind === 'episodic')
-  ]
-  const lines: string[] = []
+  const lines: string[] = []

and have assembleMemorySection iterate buildRecallCandidates(payload.memories), reusing candidate.line instead of re-rendering.

🤖 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/memory/core/injectionPort.ts` around lines 388 - 402, Deduplicate
recall filtering, ordering, and rendering by making assembleMemorySection
consume buildRecallCandidates(payload.memories) and append each candidate.line
directly. Remove the duplicate memory-kind selection and
content/temporalAnnotation rendering logic from assembleMemorySection,
preserving the existing candidate order and sanitized output.
src/main/memory/services/writeCoordinator.ts (1)

470-482: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cancellation inside the directive-suggestion loop skips the extraction audit.

Returning here bypasses writeExtractionAudit even though claims from this batch were already applied above, so a cancelled run leaves no memory/extract audit row. Every other fence check in this method returns after the audit is written. Prefer breaking out of the loop and letting the existing post-audit fence check (Line 491) handle the cancellation.

♻️ Proposed change
       if (!batch.failed) {
         for (const suggestion of parsed.directiveSuggestions) {
-          if (!this.ctx.canContinueOperation(operationFence)) {
-            extractionOutcome = 'cancelled'
-            return { ok: false }
-          }
+          if (!this.ctx.canContinueOperation(operationFence)) break
           this.ports.suggestDirective(input.agentId, suggestion)
         }
       }
🤖 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/memory/services/writeCoordinator.ts` around lines 470 - 482, Update
the directive-suggestion loop in the extraction method to stop iterating when
canContinueOperation returns false without returning immediately; preserve the
cancelled outcome, break the loop, and allow writeExtractionAudit to run before
the existing post-audit fence check handles cancellation.
src/main/memory/routes.ts (1)

288-308: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Trim the unused directive methods from MemoryRouteService.

createDirective and approveDirective (row-or-null variants) are declared but never called — the handlers use createDirectiveResult / approveDirectiveResult. Keeping both forces every implementation and test double to provide two shapes per operation, and the pairing is already inconsistent (rejectDirective has no *Result twin).

♻️ Proposed change
-  createDirective(
-    agentId: string,
-    input: MemoryDirectiveInput,
-    source?: 'explicit_user' | 'manual'
-  ): AgentMemoryDirectiveRow | null
   createDirectiveResult(
     agentId: string,
     input: MemoryDirectiveInput,
     source?: 'explicit_user' | 'manual'
   ): MemoryDirectiveCommandResult
-  approveDirective(agentId: string, directiveId: string): AgentMemoryDirectiveRow | null
   approveDirectiveResult(agentId: string, directiveId: string): MemoryDirectiveCommandResult
🤖 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/memory/routes.ts` around lines 288 - 308, Remove the unused
createDirective and approveDirective row-or-null methods from the
MemoryRouteService interface, leaving createDirectiveResult and
approveDirectiveResult as the supported operations. Update any implementations
and test doubles to stop providing these obsolete methods while preserving the
existing rejectDirective contract.
src/main/memory/data/tables/agentMemory.ts (1)

962-988: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Wrap the scope-trigger swap in a transaction like ensureTemporalArtifacts does.

AGENT_MEMORY_SCOPE_TRIGGER_DROP_SQL and AGENT_MEMORY_SCOPE_TRIGGER_SQL run as two separate exec calls, so a failure in the second leaves the database with no scope-validation triggers until the next startup repair. ensureTemporalArtifacts (Line 939) already uses this.db.transaction(...) for the same drop/recreate pattern.

♻️ Proposed change
-    this.db.exec(AGENT_MEMORY_SCOPE_TRIGGER_DROP_SQL)
-    this.db.exec(AGENT_MEMORY_SCOPE_TRIGGER_SQL)
+    this.db.transaction(() => {
+      this.db.exec(AGENT_MEMORY_SCOPE_TRIGGER_DROP_SQL)
+      this.db.exec(AGENT_MEMORY_SCOPE_TRIGGER_SQL)
+    })()
🤖 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/memory/data/tables/agentMemory.ts` around lines 962 - 988, Update
ensureScopeArtifacts to wrap the scope trigger drop-and-recreate operations in a
single this.db.transaction(...) call, matching ensureTemporalArtifacts. Ensure
AGENT_MEMORY_SCOPE_TRIGGER_DROP_SQL and AGENT_MEMORY_SCOPE_TRIGGER_SQL execute
within that transaction so a failure preserves the existing triggers.
src/main/memory/services/rowMutations.ts (1)

135-164: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Provenance key and insert scope are derived from two independent sources.

buildClaimInsertInput takes provenanceKey from the caller but derives the persisted scope from input.options.scope. Nothing enforces that the caller built that key with the same scope, so a mismatched caller silently persists a row whose scope_type/scope_id disagrees with its provenance key — which then breaks resolveProvenance's new scope-equality check (Line 126) for that row forever. The call sites in this file are correct today; the contract is what's fragile.

Consider taking scope as an explicit field and building the key inside the helper via buildScopedMemoryProvenanceKey, so the pair cannot diverge.

🤖 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/memory/services/rowMutations.ts` around lines 135 - 164, Update
buildClaimInsertInput to accept an explicit scope rather than a caller-provided
provenanceKey, normalize that scope once, and build the provenance key inside
the helper with buildScopedMemoryProvenanceKey using the same normalized scope.
Persist that identical scope in the insert input, and update all callers in this
file to pass scope data instead of constructing or supplying the key.
src/main/memory/services/workingMemoryService.ts (1)

286-300: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consult the freshness cache before hitting the repository.

readWorkingMemory calls flushWorkingMemoryIfDirty on every read, which now routes the clean case here. resolveWorkingRow (two getByProvenanceKey lookups) runs before the freshness check even when the cached projection is still valid, so every clean read pays for lookups it then discards.

♻️ Proposed reorder
   private refreshWorkingMemoryAtTemporalBoundary(agentId: string): void {
     if (!this.ctx.canReadAgentMemory(agentId)) return
-    const existing = this.resolveWorkingRow(agentId)
-    if (!existing) return
     const freshness = this.workingProjectionFreshness.get(agentId)
     const now = this.ctx.now()
     if (
       freshness &&
       now >= freshness.builtAt &&
       (freshness.nextRefreshAt === null || now < freshness.nextRefreshAt)
     ) {
       return
     }
+    if (!this.resolveWorkingRow(agentId)) return
     this.refreshWorkingMemory(agentId, { preserveOrphanedExisting: true })
   }
🤖 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/memory/services/workingMemoryService.ts` around lines 286 - 300,
Reorder refreshWorkingMemoryAtTemporalBoundary so it evaluates the
workingProjectionFreshness entry and current time before calling
resolveWorkingRow. Return immediately when the cached projection is still valid,
then perform the existing resolveWorkingRow lookup and refresh behavior only
when a refresh is due, while preserving the canReadAgentMemory guard.
test/main/memory/memoryNativeMigration.test.ts (1)

68-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated dropV48DerivedArtifacts across two migration test files. Both files define a byte-identical helper because there is no shared home for native SQLite migration test helpers; when the v48 dirty/derivation artifact set changes, both copies must be updated in lockstep.

  • test/main/memory/memoryNativeMigration.test.ts#L68-L76: move this definition into the shared native SQLite test harness (alongside nativeSqliteDescribeIf) and import it here.
  • test/main/memory/agentMemoryTable.test.ts#L258-L267: delete the local copy and import the shared helper.
🤖 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/memory/memoryNativeMigration.test.ts` around lines 68 - 76, The
helper dropV48DerivedArtifacts is duplicated across two migration test files.
Move its single definition into the shared native SQLite test harness alongside
nativeSqliteDescribeIf, import it in
test/main/memory/memoryNativeMigration.test.ts#L68-L76, and delete the local
copy while importing the shared helper in
test/main/memory/agentMemoryTable.test.ts#L258-L267.
test/main/memory/retrievalService.test.ts (1)

386-396: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

vi.waitFor on an already-true condition can't detect a late deletion.

The vector is present before the recall, so the polling assertion resolves on the first tick — the test passes even if inline cleanup never ran, and it would still pass if cleanup deleted sessionTwoId shortly after. Prefer awaiting the cleanup completion seam (or flushing pending work) and then asserting presence once.

🤖 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/memory/retrievalService.test.ts` around lines 386 - 396, Replace
the vi.waitFor polling around the sessionTwoId vector assertion with an await of
the cleanup completion seam or an equivalent flush of pending inline work after
presenter.recall. Then assert store.vectors.has(sessionTwoId) once, preserving
the existing repository metadata assertions.
test/main/memory/maintenanceService.test.ts (1)

200-206: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assertion tolerates a missing row.

row?.superseded_by === null is false for undefined, so this passes even if 00-source or 99-neighbor were deleted rather than superseded. Asserting the surviving id directly (e.g. expect(repo.getById('99-neighbor')?.superseded_by).toBeNull() plus the source's superseded_by) pins the intended merge direction.

🤖 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/memory/maintenanceService.test.ts` around lines 200 - 206, Replace
the aggregate filtered-length assertion with direct assertions on both records
returned by repo.getById: verify 99-neighbor remains present with superseded_by
null and 00-source has the expected superseded_by value pointing to the
neighbor, preserving the intended merge direction.
test/main/memory/memoryAdd.test.ts (1)

330-378: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use it.each for the UPDATE/SUPERSEDE matrix.

The manual for loop over cases collapses two independent scenarios into one test, so a failure doesn't identify which decision kind regressed.

🤖 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/memory/memoryAdd.test.ts` around lines 330 - 378, Refactor the test
around “keeps decision rewrites atomic when they would recreate a forgotten
claim” to use it.each with separate UPDATE and SUPERSEDE cases. Move each case’s
decision and candidate values into the parameterized test table so failures
identify the specific decision kind, while preserving the existing setup and
assertions.
test/renderer/api/clients.test.ts (1)

985-1003: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead conditional in shared mock case.

routeName === 'memory.rejectDirective' can never be true inside this case 'memory.createDirective': case 'memory.approveDirective': block — reject has its own separate case below. Harmless (status still resolves correctly to 'active'), but it's leftover copy-paste logic that could confuse future readers of this mock.

🧹 Proposed cleanup
-              return {
-                action: 'applied',
-                directive: {
-                  id:
-                    typeof payload?.directiveId === 'string'
-                      ? payload.directiveId
-                      : 'directive-created',
-                  agentId: payload?.agentId ?? 'agent-1',
-                  kind: 'instruction',
-                  status: routeName === 'memory.rejectDirective' ? 'rejected' : 'active',
-                  source: 'manual',
-                  content: 'Be concise.',
-                  topic: null,
-                  createdAt: 1_000,
-                  updatedAt: 2_000
-                }
-              }
+              return {
+                action: 'applied',
+                directive: {
+                  id:
+                    typeof payload?.directiveId === 'string'
+                      ? payload.directiveId
+                      : 'directive-created',
+                  agentId: payload?.agentId ?? 'agent-1',
+                  kind: 'instruction',
+                  status: 'active',
+                  source: 'manual',
+                  content: 'Be concise.',
+                  topic: null,
+                  createdAt: 1_000,
+                  updatedAt: 2_000
+                }
+              }
🤖 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/api/clients.test.ts` around lines 985 - 1003, Remove the
unreachable routeName === 'memory.rejectDirective' conditional from the shared
memory.createDirective/memory.approveDirective mock case and set the directive
status directly to 'active'. Leave the separate rejectDirective case unchanged.
test/main/routes/memoryDto.test.ts (1)

932-940: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the accepting boundary case for directive content length.

Only the over-limit case is asserted, so a regression that lowers the effective cap below AGENT_MEMORY_DIRECTIVE_CONTENT_MAX_CHARS would still pass. The memory.add block above tests both sides of the limit; mirroring that here keeps the code-point counting behavior pinned.

♻️ Suggested addition
+    expect(
+      memoryCreateDirectiveRoute.input.safeParse({
+        agentId: 'deepchat',
+        directive: {
+          kind: 'instruction',
+          content: '😀'.repeat(AGENT_MEMORY_DIRECTIVE_CONTENT_MAX_CHARS)
+        }
+      }).success
+    ).toBe(true)
     expect(
       memoryCreateDirectiveRoute.input.safeParse({
         agentId: 'deepchat',
         directive: {
           kind: 'instruction',
           content: '😀'.repeat(AGENT_MEMORY_DIRECTIVE_CONTENT_MAX_CHARS + 1)
         }
       }).success
     ).toBe(false)
🤖 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/routes/memoryDto.test.ts` around lines 932 - 940, Add an accepting
boundary assertion in the memoryCreateDirectiveRoute validation tests for
directive content consisting of AGENT_MEMORY_DIRECTIVE_CONTENT_MAX_CHARS code
points, alongside the existing over-limit case. Preserve the same agentId and
instruction payload structure so both sides of the limit and code-point counting
are verified.
test/renderer/components/MemoryInboxBar.test.ts (1)

117-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared directive draft limit constant instead of the literal 64.

AGENT_MEMORY_ACTIVE_DIRECTIVE_MAX_COUNT already defines 64; using it here (and for the directiveCapacityDescription max value) keeps this test aligned with the shared configuration.

♻️ Suggested change
+import { AGENT_MEMORY_ACTIVE_DIRECTIVE_MAX_COUNT } from '../../../src/shared/types/agent-memory'
@@
     expect(memoryClient.listDirectives).toHaveBeenCalledWith('deepchat', {
       statuses: ['draft'],
-      limit: 64
+      limit: AGENT_MEMORY_ACTIVE_DIRECTIVE_MAX_COUNT
     })
@@
-      description: 'settings.memory.redesign.directiveCapacityDescription:{"max":64}'
+      description: `settings.memory.redesign.directiveCapacityDescription:${JSON.stringify({
+        max: AGENT_MEMORY_ACTIVE_DIRECTIVE_MAX_COUNT
+      })}`

Also applies to: 174-178

🤖 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/MemoryInboxBar.test.ts` around lines 117 - 120,
Replace the hardcoded directive limit value 64 in the MemoryInboxBar test
expectations, including the directiveCapacityDescription max value, with the
shared AGENT_MEMORY_ACTIVE_DIRECTIVE_MAX_COUNT constant so both assertions
follow the shared configuration.
src/main/agent/deepchat/runtime/contextBuilder.ts (1)

1372-1408: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Duplicated drop-memory/drop-directive/overflow logic across three functions.

buildCacheAwareContextWithMetadata, buildCacheAwareResumeContextWithMetadata, and fitCacheAwareMessagesToContextWindow each repeat the same "drop memory if over budget → drop directives if still over physical budget → throw overflow" sequence, only differing in how the active-turn message is rebuilt. Also note fitCacheAwareMessagesToContextWindow hardcodes physicalInputBudget = contextLength - 1 (Line 1790) instead of reusing resolvePhysicalInputBudget(contextLength, extraReserveTokens) used by the other two functions — worth confirming these stay equivalent (this function has no extraReserveTokens option) so future changes to the shared formula don't silently diverge here.

Consider extracting a shared helper (e.g. dropUntilWithinPhysicalBudget(activeTurn, context, physicalBudget, rebuild)) parameterized by the rebuild strategy, to avoid triplicated budget-fitting logic.

Also applies to: 1511-1544, 1792-1830

🤖 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/agent/deepchat/runtime/contextBuilder.ts` around lines 1372 - 1408,
Extract the repeated memory/directives removal and overflow handling from
buildCacheAwareContextWithMetadata, buildCacheAwareResumeContextWithMetadata,
and fitCacheAwareMessagesToContextWindow into one helper parameterized by the
active-turn message rebuild strategy. Update all three callers to use it,
preserving their existing budget checks and overflow behavior; have
fitCacheAwareMessagesToContextWindow obtain its physical budget through
resolvePhysicalInputBudget(contextLength, extraReserveTokens) with the
appropriate no-extra-reserve value instead of hardcoding contextLength - 1.
🤖 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/memory/core/tombstone.ts`:
- Around line 26-47: Update buildMemoryTombstoneIdentities so content tombstone
hashes include the normalized scope alongside agentId and normalized content, or
reuse the scoped provenance key for both identity kinds. Ensure identical
content in different scopes produces distinct content identities while
preserving provenance identity behavior.

In `@src/main/memory/services/maintenanceService.ts`:
- Around line 517-554: Defer dirty seeds when embedding readiness is transient
instead of settling them: in the dimensions === null branch, replace
settleDirtySeeds with deferDirtySeeds using the repository directly. In the
current-row validation, separate the embedding-readiness failure from the
terminal decision_revision mismatch; defer the seed for non-ready/transient rows
and settle only revision-mismatched or otherwise terminal seeds, preserving
retry semantics through deferSeed where applicable.

In `@src/renderer/src/i18n/da-DK/chat.json`:
- Around line 512-518: Translate the newly added memory-turn budget labels
(allocation, directive, persona, working, queryRecall, overhead, and unused) and
directive-management UI strings in src/renderer/src/i18n/da-DK/chat.json:512-518
and settings.json:3117-3156 into Danish; apply the equivalent translations in
src/renderer/src/i18n/de-DE/chat.json:512-518 and settings.json:3108-3147 for
German, src/renderer/src/i18n/ko-KR/chat.json:512-518 and
settings.json:3117-3156 for Korean,
src/renderer/src/i18n/ms-MY/chat.json:512-518 and settings.json:3108-3147 for
Malay, src/renderer/src/i18n/pl-PL/chat.json:512-518 and settings.json:3108-3147
for Polish, and src/renderer/src/i18n/pt-BR/chat.json:512-518 and
settings.json:3117-3156 for Brazilian Portuguese, preserving all existing
translation keys and JSON structure.

In `@src/renderer/src/i18n/es-ES/chat.json`:
- Around line 512-518: Translate the newly added memory-budget labels
(allocation, directive, persona, working, queryRecall, overhead, and unused) in
src/renderer/src/i18n/es-ES/chat.json lines 512-518, fa-IR/chat.json lines
512-518, ru-RU/chat.json lines 512-518, tr-TR/chat.json lines 512-518, and
vi-VN/chat.json lines 512-518. Translate the added directive-management strings
in src/renderer/src/i18n/es-ES/settings.json lines 3107-3147,
ru-RU/settings.json lines 3116-3156, tr-TR/settings.json lines 3107-3147, and
vi-VN/settings.json lines 3107-3147, preserving the existing keys and JSON
structure.

In `@src/renderer/src/i18n/fa-IR/settings.json`:
- Around line 3117-3156: Translate the new memory UI strings instead of leaving
English fallbacks: translate the budget-label entries in
src/renderer/src/i18n/fr-FR/chat.json (512-518), he-IL/chat.json (512-518),
id-ID/chat.json (512-518), it-IT/chat.json (512-518), and ja-JP/chat.json
(512-518) into their respective languages; translate the directive-management
entries keyed by tabDirectives through directiveDeleteDescription in
src/renderer/src/i18n/fa-IR/settings.json (3117-3156), fr-FR/settings.json
(3117-3156), he-IL/settings.json (3117-3156), id-ID/settings.json (3108-3147),
it-IT/settings.json (3108-3147), and ja-JP/settings.json (3117-3156) into
Persian, French, Hebrew, Indonesian, Italian, and Japanese respectively,
preserving placeholders and JSON structure.

In `@src/shared/contracts/routes/memory.routes.ts`:
- Around line 213-223: Update MemoryDirectiveItemSchema to enforce the same
kind/topic invariant as the input union: suppress_topic items must have a null
topic, while instruction items must have a non-null topic. Preserve the
remaining item fields and validation rules.

In `@test/renderer/components/chat/MemoryTurnDialog.test.ts`:
- Around line 183-197: Update the test setup around makeTurn so the allocation
is provided through the helper’s manifest/nested allocation overrides instead of
assigning next.manifest.allocation afterward. Preserve the existing allocation
values, then assign the fully constructed result to memoryActivity.selectedTurn.

---

Outside diff comments:
In `@src/shared/contracts/routes/memory.routes.ts`:
- Around line 121-144: Update enforceProjectedScopeInvariant, which is applied
to both MemoryItemSchema and MemorySearchResultSchema, to validate temporal
metadata: atemporal rows must not carry temporal fields, non-atemporal rows must
provide temporalConfidence, and any populated validFrom/validUntil interval must
not be reversed. Preserve the existing projected-scope checks while ensuring
both response DTOs reject these malformed states.

---

Nitpick comments:
In `@src/main/agent/deepchat/runtime/contextBuilder.ts`:
- Around line 1372-1408: Extract the repeated memory/directives removal and
overflow handling from buildCacheAwareContextWithMetadata,
buildCacheAwareResumeContextWithMetadata, and
fitCacheAwareMessagesToContextWindow into one helper parameterized by the
active-turn message rebuild strategy. Update all three callers to use it,
preserving their existing budget checks and overflow behavior; have
fitCacheAwareMessagesToContextWindow obtain its physical budget through
resolvePhysicalInputBudget(contextLength, extraReserveTokens) with the
appropriate no-extra-reserve value instead of hardcoding contextLength - 1.

In `@src/main/memory/core/injectionPort.ts`:
- Around line 388-402: Deduplicate recall filtering, ordering, and rendering by
making assembleMemorySection consume buildRecallCandidates(payload.memories) and
append each candidate.line directly. Remove the duplicate memory-kind selection
and content/temporalAnnotation rendering logic from assembleMemorySection,
preserving the existing candidate order and sanitized output.

In `@src/main/memory/core/scoring.ts`:
- Around line 20-29: Merge the two type-only imports from ../domain/types in
scoring.ts into a single import declaration containing MemoryScope,
AgentMemoryKind, MemoryTemporalMetadata, and MemoryTemporalPolicyResult; leave
all other imports unchanged.

In `@src/main/memory/core/temporal.ts`:
- Around line 376-388: The retrieval path redundantly normalizes temporal
metadata in evaluateMemoryTemporalPolicy. Add or use a normalized-policy
evaluation path, such as evaluateNormalizedMemoryTemporalPolicy, and update
callers like fuse that already use temporalMetadataFromRow to call it, while
preserving normalization for untrusted direct callers.

In `@src/main/memory/core/workingProjection.ts`:
- Around line 216-229: Update resolveNextRefreshAt to accept and use the
temporal metadata already computed by toCandidate, rather than calling
temporalMetadataFromRow for each row. Adjust the caller and input shape as
needed while preserving selection of the earliest future validFrom or validUntil
boundary.
- Around line 269-288: The budget-selection loop in working projection
repeatedly calls renderSections and estimateTokens for every candidate, causing
quadratic work. Replace the per-candidate full rendering in the selection logic
with incremental token accounting using the section-label overhead table, accept
candidates only when their incremental line cost stays within budget, and render
the final selected projection once after selection.

In `@src/main/memory/data/tables/agentMemory.ts`:
- Around line 962-988: Update ensureScopeArtifacts to wrap the scope trigger
drop-and-recreate operations in a single this.db.transaction(...) call, matching
ensureTemporalArtifacts. Ensure AGENT_MEMORY_SCOPE_TRIGGER_DROP_SQL and
AGENT_MEMORY_SCOPE_TRIGGER_SQL execute within that transaction so a failure
preserves the existing triggers.

In `@src/main/memory/domain/directives.ts`:
- Around line 128-135: Remove the redundant empty-topic guard after the
`normalizedTopic = assertBoundedText(...)` call in the directive normalization
flow. Keep the existing `assertBoundedText` validation and normalization
behavior unchanged.
- Line 1: Update the crypto import in the directives module to use the
`node:crypto` specifier, matching the convention used by the nearby tombstone
module and preserving the existing createHash usage.

In `@src/main/memory/index.ts`:
- Around line 615-641: The public MemoryService directive facade methods
listDirectives, createDirective, and createDirectiveResult should use explicit
shared directive input, result, options, source, and return types instead of
Parameters<DirectiveService[...]>. Import the existing shared types and annotate
each method’s parameters and return value directly, preserving the current
delegation behavior and public API semantics.

In `@src/main/memory/routes.ts`:
- Around line 288-308: Remove the unused createDirective and approveDirective
row-or-null methods from the MemoryRouteService interface, leaving
createDirectiveResult and approveDirectiveResult as the supported operations.
Update any implementations and test doubles to stop providing these obsolete
methods while preserving the existing rejectDirective contract.

In `@src/main/memory/services/rowMutations.ts`:
- Around line 135-164: Update buildClaimInsertInput to accept an explicit scope
rather than a caller-provided provenanceKey, normalize that scope once, and
build the provenance key inside the helper with buildScopedMemoryProvenanceKey
using the same normalized scope. Persist that identical scope in the insert
input, and update all callers in this file to pass scope data instead of
constructing or supplying the key.

In `@src/main/memory/services/workingMemoryService.ts`:
- Around line 286-300: Reorder refreshWorkingMemoryAtTemporalBoundary so it
evaluates the workingProjectionFreshness entry and current time before calling
resolveWorkingRow. Return immediately when the cached projection is still valid,
then perform the existing resolveWorkingRow lookup and refresh behavior only
when a refresh is due, while preserving the canReadAgentMemory guard.

In `@src/main/memory/services/writeCoordinator.ts`:
- Around line 470-482: Update the directive-suggestion loop in the extraction
method to stop iterating when canContinueOperation returns false without
returning immediately; preserve the cancelled outcome, break the loop, and allow
writeExtractionAudit to run before the existing post-audit fence check handles
cancellation.

In `@src/renderer/settings/components/MemoryInboxBar.vue`:
- Around line 322-385: Extract the shared directive action state machine from
setDirectivePending, approveDirective, and rejectDirective into a
useDirectiveActions composable, then update MemoryInboxBar.vue and
MemoryDirectivesPanel.vue to use it. Preserve the existing revision guard,
pending-ID tracking, rejected-reason notification, stale-agent protection, and
reload-on-error behavior in both panels.

In `@src/renderer/src/components/chat/MemoryTurnDialog.vue`:
- Around line 94-97: Replace the label-and-value concatenation in the
MemoryTurnDialog template with one interpolated translation using a key such as
chat.memory.turn.overheadSummary and named overhead and unused values. Add the
corresponding locale message entries so translators control ordering and
separators, while preserving the displayed token values.

In `@test/main/memory/maintenanceService.test.ts`:
- Around line 200-206: Replace the aggregate filtered-length assertion with
direct assertions on both records returned by repo.getById: verify 99-neighbor
remains present with superseded_by null and 00-source has the expected
superseded_by value pointing to the neighbor, preserving the intended merge
direction.

In `@test/main/memory/memoryAdd.test.ts`:
- Around line 330-378: Refactor the test around “keeps decision rewrites atomic
when they would recreate a forgotten claim” to use it.each with separate UPDATE
and SUPERSEDE cases. Move each case’s decision and candidate values into the
parameterized test table so failures identify the specific decision kind, while
preserving the existing setup and assertions.

In `@test/main/memory/memoryNativeMigration.test.ts`:
- Around line 68-76: The helper dropV48DerivedArtifacts is duplicated across two
migration test files. Move its single definition into the shared native SQLite
test harness alongside nativeSqliteDescribeIf, import it in
test/main/memory/memoryNativeMigration.test.ts#L68-L76, and delete the local
copy while importing the shared helper in
test/main/memory/agentMemoryTable.test.ts#L258-L267.

In `@test/main/memory/retrievalService.test.ts`:
- Around line 386-396: Replace the vi.waitFor polling around the sessionTwoId
vector assertion with an await of the cleanup completion seam or an equivalent
flush of pending inline work after presenter.recall. Then assert
store.vectors.has(sessionTwoId) once, preserving the existing repository
metadata assertions.

In `@test/main/routes/memoryDto.test.ts`:
- Around line 932-940: Add an accepting boundary assertion in the
memoryCreateDirectiveRoute validation tests for directive content consisting of
AGENT_MEMORY_DIRECTIVE_CONTENT_MAX_CHARS code points, alongside the existing
over-limit case. Preserve the same agentId and instruction payload structure so
both sides of the limit and code-point counting are verified.

In `@test/renderer/api/clients.test.ts`:
- Around line 985-1003: Remove the unreachable routeName ===
'memory.rejectDirective' conditional from the shared
memory.createDirective/memory.approveDirective mock case and set the directive
status directly to 'active'. Leave the separate rejectDirective case unchanged.

In `@test/renderer/components/MemoryInboxBar.test.ts`:
- Around line 117-120: Replace the hardcoded directive limit value 64 in the
MemoryInboxBar test expectations, including the directiveCapacityDescription max
value, with the shared AGENT_MEMORY_ACTIVE_DIRECTIVE_MAX_COUNT constant so both
assertions follow the shared configuration.
🪄 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 Plus

Run ID: 5fc022c6-a870-4fe0-9874-52f1048b355a

📥 Commits

Reviewing files that changed from the base of the PR and between 4334d6a and e655181.

📒 Files selected for processing (168)
  • docs/architecture/agent-memory-evolution/plan.md
  • docs/architecture/agent-memory-evolution/spec.md
  • docs/architecture/agent-memory-evolution/tasks.md
  • docs/architecture/memory-system.md
  • src/main/agent/deepchat/deepChatAgentRepository.ts
  • src/main/agent/deepchat/memory/memoryPromptContributor.ts
  • src/main/agent/deepchat/memory/memoryRuntimeCoordinator.ts
  • src/main/agent/deepchat/runtime/contextBuilder.ts
  • src/main/agent/deepchat/runtime/contextContributions.ts
  • src/main/agent/deepchat/runtime/promptAssemblyService.ts
  • src/main/agent/repository/index.ts
  • src/main/app/composition.ts
  • src/main/data/schemaCatalog.ts
  • src/main/data/sqliteCopyExclusions.ts
  • src/main/memory/context.ts
  • src/main/memory/core/batchDecision.ts
  • src/main/memory/core/candidates.ts
  • src/main/memory/core/contributionBudget.ts
  • src/main/memory/core/decision.ts
  • src/main/memory/core/directiveContribution.ts
  • src/main/memory/core/directivePolicy.ts
  • src/main/memory/core/extraction.ts
  • src/main/memory/core/injectionPort.ts
  • src/main/memory/core/scope.ts
  • src/main/memory/core/scoring.ts
  • src/main/memory/core/temporal.ts
  • src/main/memory/core/tombstone.ts
  • src/main/memory/core/workingProjection.ts
  • src/main/memory/data/database.ts
  • src/main/memory/data/tables/agentMemory.ts
  • src/main/memory/data/tables/agentMemoryDirective.ts
  • src/main/memory/domain/clock.ts
  • src/main/memory/domain/directives.ts
  • src/main/memory/domain/types.ts
  • src/main/memory/index.ts
  • src/main/memory/injection.ts
  • src/main/memory/ports.ts
  • src/main/memory/routes.ts
  • src/main/memory/runtimeConstants.ts
  • src/main/memory/services/conflictService.ts
  • src/main/memory/services/directiveService.ts
  • src/main/memory/services/maintenanceService.ts
  • src/main/memory/services/managementService.ts
  • src/main/memory/services/personaService.ts
  • src/main/memory/services/reflectionService.ts
  • src/main/memory/services/retrievalService.ts
  • src/main/memory/services/rowMutations.ts
  • src/main/memory/services/workingMemoryService.ts
  • src/main/memory/services/writeCoordinator.ts
  • src/main/memory/types.ts
  • src/main/sync/dataImporter.ts
  • src/main/tape/application/viewReplayService.ts
  • src/main/tape/domain/replay.ts
  • src/main/tape/domain/viewManifest.ts
  • src/main/tape/ports/capabilities.ts
  • src/main/tool/agentTools/agentMemoryTools.ts
  • src/main/tool/runtimePorts.ts
  • src/renderer/api/MemoryClient.ts
  • src/renderer/settings/components/MemoryDirectivesPanel.vue
  • src/renderer/settings/components/MemoryInboxBar.vue
  • src/renderer/settings/components/MemorySettings.vue
  • src/renderer/settings/components/memoryRedesignUtils.ts
  • src/renderer/src/components/chat/MemoryTurnDialog.vue
  • src/renderer/src/i18n/da-DK/chat.json
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/de-DE/chat.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/en-US/chat.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/es-ES/chat.json
  • src/renderer/src/i18n/es-ES/settings.json
  • src/renderer/src/i18n/fa-IR/chat.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/fr-FR/chat.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/chat.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/id-ID/chat.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/it-IT/chat.json
  • src/renderer/src/i18n/it-IT/settings.json
  • src/renderer/src/i18n/ja-JP/chat.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/ko-KR/chat.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/ms-MY/chat.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/renderer/src/i18n/pl-PL/chat.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/pt-BR/chat.json
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/renderer/src/i18n/ru-RU/chat.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/tr-TR/chat.json
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/renderer/src/i18n/vi-VN/chat.json
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/zh-CN/chat.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/zh-HK/chat.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • src/renderer/src/i18n/zh-TW/chat.json
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/shared/contracts/events/memory.events.ts
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/memory.routes.ts
  • src/shared/types/agent-memory.ts
  • src/shared/types/tape-view-manifest.ts
  • src/types/i18n.d.ts
  • test/fixtures/memory/behavior-v1.json
  • test/main/agent/deepchat/deepChatAgentRepository.test.ts
  • test/main/agent/deepchat/harness/deepChatAgentHarness.test.ts
  • test/main/agent/deepchat/loop/contextCoordinator.test.ts
  • test/main/agent/deepchat/memory/memoryRuntimeCoordinator.test.ts
  • test/main/agent/deepchat/runtime/contextBuilder.test.ts
  • test/main/agent/deepchat/runtime/promptAssemblyService.test.ts
  • test/main/app/databaseSecurity.test.ts
  • test/main/memory/agentMemoryDirectiveTable.test.ts
  • test/main/memory/agentMemoryTable.test.ts
  • test/main/memory/conflictService.test.ts
  • test/main/memory/contributionBudget.test.ts
  • test/main/memory/directiveContribution.test.ts
  • test/main/memory/directivePolicy.test.ts
  • test/main/memory/directiveService.test.ts
  • test/main/memory/embeddingDiagnostics.test.ts
  • test/main/memory/lifecycleRegression.test.ts
  • test/main/memory/maintenanceService.test.ts
  • test/main/memory/managementService.test.ts
  • test/main/memory/memory-persona-eval.test.ts
  • test/main/memory/memoryAdd.test.ts
  • test/main/memory/memoryBatchDecision.test.ts
  • test/main/memory/memoryBehavior.eval.test.ts
  • test/main/memory/memoryDecision.test.ts
  • test/main/memory/memoryEmbeddingScale.test.ts
  • test/main/memory/memoryExtraction.test.ts
  • test/main/memory/memoryInjectionPort.test.ts
  • test/main/memory/memoryLifecycle.test.ts
  • test/main/memory/memoryNativeMigration.test.ts
  • test/main/memory/memoryRuntimeContext.test.ts
  • test/main/memory/memoryScope.test.ts
  • test/main/memory/memorySearch.test.ts
  • test/main/memory/memoryService.test.ts
  • test/main/memory/memoryServiceHarness.test.ts
  • test/main/memory/memoryTemporal.test.ts
  • test/main/memory/memoryUpdate.test.ts
  • test/main/memory/memoryUpdateNative.test.ts
  • test/main/memory/personaService.test.ts
  • test/main/memory/repositoryHarness.test.ts
  • test/main/memory/retrievalDiagnostics.test.ts
  • test/main/memory/retrievalService.test.ts
  • test/main/memory/serviceTestSupport.ts
  • test/main/memory/support/memoryFakes.ts
  • test/main/memory/support/memoryServiceTestAdapter.ts
  • test/main/memory/workingMemoryService.test.ts
  • test/main/memory/workingProjection.test.ts
  • test/main/memory/writeCoordinator.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/routes/memoryDto.test.ts
  • test/main/session/data/tapeViewManifest.test.ts
  • test/main/session/data/tapeViewReplay.test.ts
  • test/main/tool/agentTools/agentMemoryTools.test.ts
  • test/memory-test-scope.json
  • test/renderer/api/clients.test.ts
  • test/renderer/components/MemoryDiagnosticsPanel.test.ts
  • test/renderer/components/MemoryDirectivesPanel.test.ts
  • test/renderer/components/MemoryInboxBar.test.ts
  • test/renderer/components/MemorySettings.test.ts
  • test/renderer/components/chat/MemoryTurnDialog.test.ts

Comment thread src/main/memory/core/tombstone.ts
Comment thread src/main/memory/services/maintenanceService.ts
Comment thread src/renderer/src/i18n/da-DK/chat.json Outdated
Comment thread src/renderer/src/i18n/es-ES/chat.json Outdated
Comment thread src/renderer/src/i18n/fa-IR/settings.json Outdated
Comment thread src/shared/contracts/routes/memory.routes.ts Outdated
Comment thread test/renderer/components/chat/MemoryTurnDialog.test.ts Outdated
Keep legacy agent tombstones, scope narrow content identities, and derive provenance from scope.

Defer transient consolidation work and enforce response and cancellation audit contracts.
Use exact incremental token accounting and normalized temporal fast paths.

Localize directive and allocation UI, and strengthen asynchronous and native test coverage.

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

Caution

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

⚠️ Outside diff range comments (2)
src/main/memory/data/tables/agentMemory.ts (2)

2690-2695: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Preserve the persisted scope when checking tombstones.

Tombstone identities include scope, but these checks omit it and therefore hash the default agent scope. A forgotten user/project/session claim can be restored, revived, activated, or recreated through an edit.

  • src/main/memory/data/tables/agentMemory.ts#L2690-L2695: pass scope: memoryScopeFromRow(before).
  • src/main/memory/data/tables/agentMemory.ts#L2781-L2786: pass scope: memoryScopeFromRow(before).
  • src/main/memory/data/tables/agentMemory.ts#L2838-L2844: pass scope: memoryScopeFromRow(before).
  • src/main/memory/data/tables/agentMemory.ts#L3390-L3395: pass scope: memoryScopeFromRow(before).
🤖 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/memory/data/tables/agentMemory.ts` around lines 2690 - 2695,
Preserve each claim’s persisted scope when checking tombstones. In the four
hasTombstoneForClaim call sites in src/main/memory/data/tables/agentMemory.ts at
lines 2690-2695, 2781-2786, 2838-2844, and 3390-3395, add scope:
memoryScopeFromRow(before) alongside the existing claim identity fields.

1545-1550: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Backfill legacy user scopes before making scope columns authoritative.

Existing rows with user_scope receive scope_type = 'agent' and scope_id = NULL. That combination passes the new constraints, so formerly user-scoped claims become agent-wide after upgrade and may be recalled for other users. Migrate valid legacy user_scope values to scope_type = 'user' and scope_id = user_scope before provisioning scope artifacts; include migration coverage for legacy user-scoped rows.

🤖 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/memory/data/tables/agentMemory.ts` around lines 1545 - 1550, Update
the schema migration returned for AGENT_MEMORY_SCOPE_SCHEMA_VERSION to backfill
existing rows with valid user_scope values to scope_type = 'user' and scope_id =
user_scope before creating or relying on the new scope index/artifacts. Preserve
the new column constraints and ensure legacy user-scoped rows are covered by
migration tests so they cannot remain agent-wide.
🤖 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.

Outside diff comments:
In `@src/main/memory/data/tables/agentMemory.ts`:
- Around line 2690-2695: Preserve each claim’s persisted scope when checking
tombstones. In the four hasTombstoneForClaim call sites in
src/main/memory/data/tables/agentMemory.ts at lines 2690-2695, 2781-2786,
2838-2844, and 3390-3395, add scope: memoryScopeFromRow(before) alongside the
existing claim identity fields.
- Around line 1545-1550: Update the schema migration returned for
AGENT_MEMORY_SCOPE_SCHEMA_VERSION to backfill existing rows with valid
user_scope values to scope_type = 'user' and scope_id = user_scope before
creating or relying on the new scope index/artifacts. Preserve the new column
constraints and ensure legacy user-scoped rows are covered by migration tests so
they cannot remain agent-wide.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6c5e4095-2ef8-442d-81cf-5e1eb87ff0a6

📥 Commits

Reviewing files that changed from the base of the PR and between d3f2a0e and abef4dd.

📒 Files selected for processing (73)
  • docs/architecture/agent-memory-evolution/spec.md
  • docs/architecture/agent-memory-evolution/tasks.md
  • src/main/memory/core/injectionPort.ts
  • src/main/memory/core/scoring.ts
  • src/main/memory/core/temporal.ts
  • src/main/memory/core/tombstone.ts
  • src/main/memory/core/workingProjection.ts
  • src/main/memory/data/tables/agentMemory.ts
  • src/main/memory/domain/directives.ts
  • src/main/memory/index.ts
  • src/main/memory/ports.ts
  • src/main/memory/routes.ts
  • src/main/memory/services/conflictService.ts
  • src/main/memory/services/directiveService.ts
  • src/main/memory/services/maintenanceService.ts
  • src/main/memory/services/retrievalService.ts
  • src/main/memory/services/rowMutations.ts
  • src/main/memory/services/workingMemoryService.ts
  • src/main/memory/services/writeCoordinator.ts
  • src/main/sync/dataImporter.ts
  • src/renderer/settings/components/MemoryInboxBar.vue
  • src/renderer/src/components/chat/MemoryTurnDialog.vue
  • src/renderer/src/i18n/da-DK/chat.json
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/de-DE/chat.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/en-US/chat.json
  • src/renderer/src/i18n/es-ES/chat.json
  • src/renderer/src/i18n/es-ES/settings.json
  • src/renderer/src/i18n/fa-IR/chat.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/fr-FR/chat.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/chat.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/id-ID/chat.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/it-IT/chat.json
  • src/renderer/src/i18n/it-IT/settings.json
  • src/renderer/src/i18n/ja-JP/chat.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/ko-KR/chat.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/ms-MY/chat.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/renderer/src/i18n/pl-PL/chat.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/pt-BR/chat.json
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/renderer/src/i18n/ru-RU/chat.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/tr-TR/chat.json
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/renderer/src/i18n/vi-VN/chat.json
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/zh-CN/chat.json
  • src/renderer/src/i18n/zh-HK/chat.json
  • src/renderer/src/i18n/zh-TW/chat.json
  • src/shared/contracts/routes/memory.routes.ts
  • test/main/memory/agentMemoryTable.test.ts
  • test/main/memory/maintenanceService.test.ts
  • test/main/memory/memoryAdd.test.ts
  • test/main/memory/memoryExtraction.test.ts
  • test/main/memory/memoryNativeMigration.test.ts
  • test/main/memory/retrievalService.test.ts
  • test/main/memory/serviceTestSupport.ts
  • test/main/memory/support/memoryFakes.ts
  • test/main/memory/workingProjection.test.ts
  • test/main/nativeSqliteHarness.ts
  • test/main/routes/memoryDto.test.ts
  • test/renderer/api/clients.test.ts
  • test/renderer/components/MemoryInboxBar.test.ts
  • test/renderer/components/chat/MemoryTurnDialog.test.ts
💤 Files with no reviewable changes (1)
  • src/main/memory/ports.ts
🚧 Files skipped from review as they are similar to previous changes (60)
  • src/renderer/src/i18n/pl-PL/chat.json
  • src/renderer/src/i18n/de-DE/chat.json
  • src/renderer/src/i18n/zh-CN/chat.json
  • src/renderer/src/i18n/vi-VN/chat.json
  • src/renderer/src/i18n/ms-MY/chat.json
  • src/renderer/src/i18n/en-US/chat.json
  • src/renderer/src/i18n/fa-IR/chat.json
  • src/renderer/src/i18n/ja-JP/chat.json
  • src/renderer/src/i18n/fr-FR/chat.json
  • src/renderer/src/components/chat/MemoryTurnDialog.vue
  • src/renderer/src/i18n/id-ID/chat.json
  • src/renderer/src/i18n/ru-RU/chat.json
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/it-IT/chat.json
  • src/renderer/src/i18n/zh-HK/chat.json
  • src/renderer/src/i18n/zh-TW/chat.json
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/renderer/src/i18n/es-ES/settings.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/he-IL/chat.json
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/pt-BR/chat.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/renderer/src/i18n/es-ES/chat.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • test/renderer/components/chat/MemoryTurnDialog.test.ts
  • src/main/memory/core/temporal.ts
  • test/renderer/api/clients.test.ts
  • src/renderer/src/i18n/it-IT/settings.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/da-DK/chat.json
  • src/renderer/settings/components/MemoryInboxBar.vue
  • test/main/memory/memoryAdd.test.ts
  • src/main/memory/core/scoring.ts
  • src/renderer/src/i18n/pt-BR/settings.json
  • test/main/routes/memoryDto.test.ts
  • test/main/memory/serviceTestSupport.ts
  • src/main/memory/services/workingMemoryService.ts
  • test/main/memory/memoryExtraction.test.ts
  • src/main/sync/dataImporter.ts
  • src/main/memory/services/directiveService.ts
  • test/main/memory/retrievalService.test.ts
  • src/main/memory/core/workingProjection.ts
  • test/main/memory/memoryNativeMigration.test.ts
  • src/main/memory/services/conflictService.ts
  • src/main/memory/core/injectionPort.ts
  • src/main/memory/services/rowMutations.ts
  • src/shared/contracts/routes/memory.routes.ts
  • src/main/memory/routes.ts
  • src/main/memory/services/retrievalService.ts
  • src/main/memory/index.ts
  • src/main/memory/services/maintenanceService.ts
  • src/main/memory/services/writeCoordinator.ts
  • test/main/memory/support/memoryFakes.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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/main/memory/services/managementService.ts (1)

185-198: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Resume serializes all pending clears.

await this.enqueueMemoryClear(job.agentId) inside the loop runs each agent's clear to completion before starting the next. With several pending jobs this stretches startup recovery; the per-agent dedupe map already makes concurrent execution safe.

♻️ Optional: kick off all jobs and settle together
-    for (const job of jobs) {
-      if (!isSafeAgentId(job.agentId)) {
-        logger.error(`[Memory] refusing to resume clear job with invalid agent id: ${job.agentId}`)
-        continue
-      }
-      try {
-        await this.enqueueMemoryClear(job.agentId)
-      } catch (error) {
-        logger.error(`[Memory] failed to resume clear job for ${job.agentId}: ${String(error)}`)
-      }
-    }
+    const resumed = jobs.flatMap((job) => {
+      if (!isSafeAgentId(job.agentId)) {
+        logger.error(`[Memory] refusing to resume clear job with invalid agent id: ${job.agentId}`)
+        return []
+      }
+      return [
+        this.enqueueMemoryClear(job.agentId).catch((error) => {
+          logger.error(`[Memory] failed to resume clear job for ${job.agentId}: ${String(error)}`)
+        })
+      ]
+    })
+    await Promise.all(resumed)
🤖 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/memory/services/managementService.ts` around lines 185 - 198, Update
resumePendingMemoryClears to start all valid enqueueMemoryClear jobs without
awaiting each one inside the loop, then await them collectively so pending
clears recover concurrently while preserving per-job error logging and
invalid-agent filtering.
src/main/memory/services/retrievalService.ts (1)

958-1067: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the termination invariant of the refill loop.

The while (true) is bounded only implicitly: each non-breaking iteration must strictly raise candidateLimit or vectorCandidateLimit, and nextMemoryRetrievalCandidateLimit clamps at MEMORY_RETRIEVAL_MAX_CANDIDATES, so the next > current guards eventually fail. That reasoning is load-bearing and easy to break in a later edit (e.g. if refillVectorCandidates ever stopped assigning vectorCandidateLimit = limit). A short comment stating the invariant would protect it.

📝 Suggested comment
+      // Bounded loop: every non-breaking iteration strictly increases candidateLimit and/or
+      // vectorCandidateLimit, both clamped by MEMORY_RETRIEVAL_MAX_CANDIDATES, so the
+      // `next > current` guards terminate it. Keep refill paths monotonic.
       while (true) {
🤖 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/memory/services/retrievalService.ts` around lines 958 - 1067, Add a
concise comment immediately before the refill loop around the while (true) block
documenting that every non-breaking iteration must strictly increase
candidateLimit or vectorCandidateLimit, and that
nextMemoryRetrievalCandidateLimit is capped by MEMORY_RETRIEVAL_MAX_CANDIDATES
so the next > current guards eventually terminate the loop. Mention that
refillVectorCandidates must preserve this assignment invariant.
🤖 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/memory/data/tables/agentMemory.ts`:
- Around line 173-193: The agent_memory write methods exposed by
MemoryRepositoryPort—recordAccess, recordAccessBatch, setConfidence, and
setLastConsolidatedAt—must honor pending clear jobs. Route these updates through
the existing context gate used by recordInjectionAccess/recordAccessBatch, or
make them no-op when isMemoryClearPending(agentId) is true, while preserving
normal writes when no clear is pending.

---

Nitpick comments:
In `@src/main/memory/services/managementService.ts`:
- Around line 185-198: Update resumePendingMemoryClears to start all valid
enqueueMemoryClear jobs without awaiting each one inside the loop, then await
them collectively so pending clears recover concurrently while preserving
per-job error logging and invalid-agent filtering.

In `@src/main/memory/services/retrievalService.ts`:
- Around line 958-1067: Add a concise comment immediately before the refill loop
around the while (true) block documenting that every non-breaking iteration must
strictly increase candidateLimit or vectorCandidateLimit, and that
nextMemoryRetrievalCandidateLimit is capped by MEMORY_RETRIEVAL_MAX_CANDIDATES
so the next > current guards eventually terminate the loop. Mention that
refillVectorCandidates must preserve this assignment invariant.
🪄 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 Plus

Run ID: d7991623-994a-47c7-a457-0c150e62f246

📥 Commits

Reviewing files that changed from the base of the PR and between 37195f2 and 933a080.

📒 Files selected for processing (60)
  • docs/architecture/agent-memory-evolution/plan.md
  • docs/architecture/agent-memory-evolution/spec.md
  • docs/architecture/agent-memory-evolution/tasks.md
  • docs/architecture/memory-system.md
  • src/main/memory/context.ts
  • src/main/memory/core/directiveContribution.ts
  • src/main/memory/core/directivePolicy.ts
  • src/main/memory/core/retrievalBudget.ts
  • src/main/memory/data/tables/agentMemory.ts
  • src/main/memory/domain/directives.ts
  • src/main/memory/domain/types.ts
  • src/main/memory/index.ts
  • src/main/memory/ports.ts
  • src/main/memory/services/conflictService.ts
  • src/main/memory/services/directiveService.ts
  • src/main/memory/services/maintenanceService.ts
  • src/main/memory/services/managementService.ts
  • src/main/memory/services/personaService.ts
  • src/main/memory/services/retrievalService.ts
  • src/main/memory/services/writeCoordinator.ts
  • src/main/memory/types.ts
  • src/main/tool/agentTools/agentMemoryTools.ts
  • src/renderer/settings/components/MemoryDirectivesPanel.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/lib/memoryDirectiveTopic.ts
  • src/shared/types/agent-memory.ts
  • test/main/memory/agentMemoryTable.test.ts
  • test/main/memory/directiveContribution.test.ts
  • test/main/memory/directivePolicy.test.ts
  • test/main/memory/directiveService.test.ts
  • test/main/memory/managementService.test.ts
  • test/main/memory/memoryAdd.test.ts
  • test/main/memory/memoryNativeMigration.test.ts
  • test/main/memory/retrievalService.test.ts
  • test/main/memory/support/memoryFakes.ts
  • test/main/performance/memory/clearScale.perf.ts
  • test/main/performance/memory/workloadBounds.perf.ts
  • test/main/tool/agentTools/agentMemoryTools.test.ts
  • test/memory-test-scope.json
  • test/renderer/components/MemoryDiagnosticsPanel.test.ts
  • test/renderer/components/MemoryDirectivesPanel.test.ts
🚧 Files skipped from review as they are similar to previous changes (35)
  • test/main/memory/directivePolicy.test.ts
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • test/main/memory/directiveContribution.test.ts
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • test/memory-test-scope.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/it-IT/settings.json
  • test/main/memory/directiveService.test.ts
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/shared/types/agent-memory.ts
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/main/memory/context.ts
  • src/main/memory/domain/directives.ts
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/main/memory/core/directivePolicy.ts
  • src/main/memory/services/directiveService.ts
  • src/main/memory/services/conflictService.ts
  • src/renderer/settings/components/MemoryDirectivesPanel.vue
  • test/renderer/components/MemoryDirectivesPanel.test.ts
  • docs/architecture/agent-memory-evolution/spec.md
  • docs/architecture/agent-memory-evolution/plan.md
  • src/main/memory/types.ts
  • src/main/memory/ports.ts
  • test/main/memory/retrievalService.test.ts
  • src/main/memory/services/maintenanceService.ts
  • test/main/memory/memoryNativeMigration.test.ts
  • src/main/memory/domain/types.ts
  • src/main/memory/index.ts
  • test/main/memory/support/memoryFakes.ts

Comment thread src/main/memory/data/tables/agentMemory.ts
@yyhhyyyyyy
yyhhyyyyyy merged commit e2d43dd into dev Jul 27, 2026
12 checks passed
@yyhhyyyyyy
yyhhyyyyyy deleted the feat/agent-memory-evolution branch July 27, 2026 15:01
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