LearnNewsExamplesServices
Frontmatter
id12823
titleRemove the obsolete 30-day window from session summarization
stateClosed
labels
bugaiarchitecture
assigneesneo-opus-grace
createdAtJun 9, 2026, 7:13 PM
updatedAtJun 9, 2026, 7:44 PM
githubUrlhttps://github.com/neomjs/neo/issues/12823
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 9, 2026, 7:44 PM

Remove the obsolete 30-day window from session summarization

Closed v13.0.0/archive-v13-0-0-chunk-17 bugaiarchitecture
neo-opus-grace
neo-opus-grace commented on Jun 9, 2026, 7:13 PM

Context

The orchestrator session-summary backlog metric sat at ~387 and "would not move" during the 2026-06-09 cost-safety / summarization session, while the per-turn miniSummary backfill drained normally. Diagnosed against the live Memory Core graph: the periodic summarizer is hard-windowed to the last 30 days, but the backlog metric is all-time, so old sessions form a permanent floor the sweep structurally never reaches.

The Problem

  • The periodic drainer (summarize-sessions.mjs) calls summarizeSessions({includeAll: false}), which filters to Date.now() - 30 days.
  • The backlog metric getPendingSessionSummaryCount is all-time (distinct AGENT_MEMORY sessionIds with no matching SESSION_SUMMARY).
  • Live graph: 478 distinct sessions, 299 older than 30 days, pending = 387. So ~299 are structurally outside the sweep window — the periodic path can never summarize them (and they have no pending markers, so the marker-drain path misses them too).
  • Why the window existed (historical): it dates to when Memory Core had a boot-triggered summarizeSessions MCP tool coupled to healthchecks — the 30-day cap kept server boot / healthchecks from timing out (or even blocking agents from starting the MCP server). That coupling is gone: summarization moved to the spawned orchestrator daemon child, and the MC healthcheck no longer calls summarization (verified — no summarizeSession/findSessionsToSummarize reference in HealthService.mjs). The 30-day guard is now obsolete dead-weight that silently strands the historical tail.

The Architectural Reality

  • ai/services/memory-core/SessionService.mjs:328findSessionsToSummarize(includeAll = false); ONE_MONTH_MS/minTimestamp at :332-333, applied at :350-353 (memory fetch) and :406-409 (summary-metadata fetch).
  • ai/services/memory-core/SessionService.mjs:1257summarizeSessions({includeAll}):1280 calls findSessionsToSummarize(includeAll).
  • ai/scripts/lifecycle/summarize-sessions.mjs:42 — passes includeAll: false (the only call site).
  • ai/daemons/orchestrator/scheduling/summary.mjs:74getPendingSessionSummaryCount, the all-time anti-join.
  • #12820's degraded miniSummary/truncated-raw fallback is live, so oversized old sessions produce degraded summaries (not null) once the sweep reaches them.

The Fix

  • Delete ONE_MONTH_MS + minTimestamp and the two if (!includeAll) time-filter blocks in findSessionsToSummarize → it always scans all sessions.
  • Remove the now-vestigial includeAll parameter from findSessionsToSummarize and summarizeSessions; update the single call site (summarize-sessions.mjs:42) + specs.
  • Net: the periodic sweep becomes all-time, matching the all-time metric; the ~299 historical sessions drain (degraded via #12820 where oversized), bounded by summarizationBatchLimit per run.

Contract Ledger

Surface Source of Authority Proposed Fallback Consumer
findSessionsToSummarize(includeAll) param SessionService.mjs:328 remove param; always all-time n/a (callers updated) summarizeSessions, summarize-sessions.mjs
periodic session-summary scope summarize-sessions.mjs:42 30-day → all-time n/a orchestrator periodic sweep

Acceptance Criteria

  • ONE_MONTH_MS/minTimestamp and both if (!includeAll) filters removed from findSessionsToSummarize; it scans all sessions.
  • includeAll parameter removed from findSessionsToSummarize + summarizeSessions; summarize-sessions.mjs call site updated; no dangling references.
  • Unit spec proves the sweep now selects a >30-day-old unsummarized session (previously excluded).
  • Post-merge: orchestrator pending-session-summary count drains toward 0 over successive sweeps (no longer floors at ~299).

Out of Scope

  • The backlog metric / log shape — the deletion aligns drainer and metric, so no metric change is needed.
  • Scheduler heavy-task fairness (challenged separately; not the cause of this backlog).
  • Backfilling properties.sessionId onto the ~110 legacy SESSION_SUMMARY nodes that lack it (separate false-pending cleanup).

Avoided Traps

  • Scoping the metric to 30 days instead of deleting the window — rejected: hides the 299-session historical tail rather than draining it; the goal is to summarize them.
  • Keeping includeAll "for flexibility" — rejected: with the window gone it's vestigial; re-introduce deliberately if an all-vs-window need ever returns.
  • First-run cost: the first all-time sweeps are heavier (299 oversized sessions → degraded gemma4 summaries) and compete with the miniSummary backfill for the exclusive-heavy slot. Acceptable; optionally sequence the first run after the backfill completes.

Decision Record impact

none — the 30-day window is not ADR-codified; this removes an obsolete operational safeguard.

Related

  • Builds on #12820 / #12819 (degraded session-summary fallback). Context: #12740 (cost-safety), #12768 (sibling MC healthcheck trim), #12812 (summary backfill logging).

Origin Session ID: 0f94aade-f9b1-4214-b17e-be0bdf33e2be Retrieval Hint: "session-summary backlog 30-day window all-time scope mismatch includeAll findSessionsToSummarize"

tobiu closed this issue on Jun 9, 2026, 7:44 PM