Summary
The generic management mutations have no kind guard:
forgetMemory / archiveUserMemory (src/main/presenter/memoryPresenter/services/managementService.ts:67-105) archive any row, including kind='persona'.
restoreMemory (managementService.ts:52-65) restores any archived row to pending_embedding.
Persona (and working) rows are permanently excluded from the embedding pipeline (agentMemoryTable.listPendingEmbedding / requeueForEmbedding filter kind NOT IN ('persona','working')), so a restored persona row is stuck in pending_embedding forever: getHealth().embeddings.pending and getStatus().pendingEmbedding never drain, and the persona state machine's fts_only invariant is broken.
The path is reachable from the UI: listMemories includes persona rows (managementService.ts:110), and the Memories tab renders them with archive/restore actions. Also note archiving an active persona is a silent no-op for injection (getActivePersona ignores status), which is misleading on its own.
Severity
P2 — state corruption via a legitimate management surface; consequence is permanent health/status pollution and lifecycle-invariant bypass.
Fix direction
Refuse persona/working kinds at the archive/restore/forget boundary, exclude persona rows from the generic memory.list surface (the Persona tab already owns them), and repair already-stuck rows back to fts_only during cheap maintenance.
SDD doc: docs/issues/memory-audit-hardening/ (spec.md + plan.md + tasks.md)
Summary
The generic management mutations have no kind guard:
forgetMemory/archiveUserMemory(src/main/presenter/memoryPresenter/services/managementService.ts:67-105) archive any row, includingkind='persona'.restoreMemory(managementService.ts:52-65) restores any archived row topending_embedding.Persona (and working) rows are permanently excluded from the embedding pipeline (
agentMemoryTable.listPendingEmbedding/requeueForEmbeddingfilterkind NOT IN ('persona','working')), so a restored persona row is stuck inpending_embeddingforever:getHealth().embeddings.pendingandgetStatus().pendingEmbeddingnever drain, and the persona state machine'sfts_onlyinvariant is broken.The path is reachable from the UI:
listMemoriesincludes persona rows (managementService.ts:110), and the Memories tab renders them with archive/restore actions. Also note archiving an active persona is a silent no-op for injection (getActivePersonaignoresstatus), which is misleading on its own.Severity
P2 — state corruption via a legitimate management surface; consequence is permanent health/status pollution and lifecycle-invariant bypass.
Fix direction
Refuse persona/working kinds at the archive/restore/forget boundary, exclude persona rows from the generic
memory.listsurface (the Persona tab already owns them), and repair already-stuck rows back tofts_onlyduring cheap maintenance.SDD doc:
docs/issues/memory-audit-hardening/ (spec.md + plan.md + tasks.md)