LearnNewsExamplesServices
Frontmatter
id13566
titleArchive content-gone AGENT_MEMORY rows from the miniSummary backfill
stateClosed
labels
bugaiarchitecturemodel-experience
assigneesneo-opus-grace
createdAtJun 19, 2026, 7:43 PM
updatedAtJun 20, 2026, 1:57 AM
githubUrlhttps://github.com/neomjs/neo/issues/13566
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 20, 2026, 1:57 AM

Archive content-gone AGENT_MEMORY rows from the miniSummary backfill

Closed v13.1.0/archive-v13-1-0-chunk-4 bugaiarchitecturemodel-experience
neo-opus-grace
neo-opus-grace commented on Jun 19, 2026, 7:43 PM

Context

V-B-A under #12065 (3 comments) found the root cause of the persistently-stuck mini-summary backfill, sized from live data: of 12,496 AGENT_MEMORY graph nodes, 4,347 are pending (miniSummary IS NULL); 3,327 (77% of the pending set) have no Chroma embedding at all — orphan-residue from past embedding-orphaning incidents (graph node written, embedding never landed). Cross-referenced against the WAL: only 2 are recoverable (in the WAL window); 3,327 are content-gone. Converged with @neo-opus-vega on an archivedAt archive-not-delete (reversible) model (her refinement; dissolves the deeper-recovery question since archiving is reversible).

The Problem

MemoryService.backfillMiniSummaries (ai/services/memory-core/MemoryService.mjs:1630-1634) counts a row whose Chroma metadata lacks both prompt and response as missingContent and skips it (continue) — it never receives a miniSummary, so it stays miniSummary: NULL permanently. But getPendingMemorySummaryBackfillCount (ai/daemons/orchestrator/scheduling/memorySummaryBackfill.mjs:50) counts ALL NULL rows. Therefore:

  • These structurally-un-summarizable rows are a permanent backlog floor — the pending metric can never reach zero ("the backfill is still an issue").
  • A newest-50 fetch (getPendingMemorySummaryBackfillJobs:20, ORDER BY timestamp DESC LIMIT 50) dominated by them drains zero → the 10-min no-progress backoff arms → stall.

The Architectural Reality

  • MemoryService.backfillMiniSummaries (the missingContent branch) owns per-row summarization; it already updates the node when it sets miniSummary.
  • memorySummaryBackfill.mjs owns the scheduler-side fetch (getPendingMemorySummaryBackfillJobs) + uncapped count (getPendingMemorySummaryBackfillCount) + the no-progress backoff.
  • Per @neo-opus-vega's convergence: mark un-summarizable nodes with a reversible data.properties.archivedAt timestamp (archive-not-delete) — the metadata-stub persists as provenance; archivedAt is clearable if content ever reappears.

The Fix

  1. Permanent backfill fix: in backfillMiniSummaries, on missingContent, set archivedAt on the node (instead of continue) → it leaves the pending set AND counts as progress (kills the no-progress-backoff misfire).
  2. Query exclusion: getPendingMemorySummaryBackfillJobs + getPendingMemorySummaryBackfillCount add AND json_extract(data,'$.properties.archivedAt') IS NULL, so both the fetch and the metric reflect only the drainable backlog.
  3. One-shot reconcile: archive the existing 3,327 content-gone nodes (reversible) + re-embed the 2 in-WAL recoverables (ephemeral /tmp script; not committed).

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
data.properties.archivedAt on AGENT_MEMORY nodes This ticket (Vega convergence on #12065) Reversible timestamp marker set when a node is structurally un-summarizable (no Chroma content) Absent archivedAt = active/pending (current behavior); marker is clearable to un-archive backfillMiniSummaries JSDoc + this ticket Unit tests + live 3,327 archive
getPendingMemorySummaryBackfillJobs / …Count queries memorySummaryBackfill.mjs Exclude archivedAt IS NOT NULL from fetch + count If column absent, IS NULL matches all (no regression) Function JSDoc Unit tests w/ archived-row fixtures

Decision Record impact

aligned-with #12065 (Orchestrator-as-SSOT REM pipeline); no ADR authority change.

Acceptance Criteria

  • backfillMiniSummaries sets archivedAt on a missingContent row instead of skipping it (verified: the row leaves the pending set + the run reports it as non-pending progress).
  • getPendingMemorySummaryBackfillJobs + getPendingMemorySummaryBackfillCount exclude archivedAt IS NOT NULL.
  • An all-missingContent batch no longer arms the no-progress backoff (it makes archive-progress).
  • archivedAt is reversible (clearing it returns the node to the pending set).
  • Unit coverage with archived-row + missingContent fixtures; existing backfill tests stay green.
  • One-shot reconcile archives the 3,327 content-gone nodes + re-embeds the 2 in-WAL (post-merge operational step; reversible).

Out of Scope

  • Re-embedding the 3,327 content-gone rows (content is gone — archive, not recover).
  • The deeper May-baseline (recovery-2026-05) extraction (dissolved by reversibility — an optional later leaf if ever wanted).
  • #13496's Chroma stored-embedding export-repair (separate mechanism).
  • Changing the newest-first fetch ordering or the backoff window.

Related

  • Parent: #12065 (REM/Sandman pipeline epic)
  • Root: #13496 (Chroma orphaning family), #13503 (coverage audit, the diagnostic that exposed this)
  • Complement: #13551 (@neo-opus-vega's drain-watchdog — prevents future silent drain deaths; this reconciles the accumulated residue)

Origin Session ID: fca648cb-7dbf-418a-b2d2-498df8e0213c

Handoff Retrieval Hint: query_raw_memories("backfill archivedAt missingContent no-embedding orphan-residue reconcile 3329")