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:328 — findSessionsToSummarize(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:1257 — summarizeSessions({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:74 — getPendingSessionSummaryCount, 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
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"
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
summarize-sessions.mjs) callssummarizeSessions({includeAll: false}), which filters toDate.now() - 30 days.getPendingSessionSummaryCountis all-time (distinctAGENT_MEMORYsessionIds with no matchingSESSION_SUMMARY).summarizeSessionsMCP 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 — nosummarizeSession/findSessionsToSummarizereference inHealthService.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:328—findSessionsToSummarize(includeAll = false);ONE_MONTH_MS/minTimestampat:332-333, applied at:350-353(memory fetch) and:406-409(summary-metadata fetch).ai/services/memory-core/SessionService.mjs:1257—summarizeSessions({includeAll})→:1280callsfindSessionsToSummarize(includeAll).ai/scripts/lifecycle/summarize-sessions.mjs:42— passesincludeAll: false(the only call site).ai/daemons/orchestrator/scheduling/summary.mjs:74—getPendingSessionSummaryCount, 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
ONE_MONTH_MS+minTimestampand the twoif (!includeAll)time-filter blocks infindSessionsToSummarize→ it always scans all sessions.includeAllparameter fromfindSessionsToSummarizeandsummarizeSessions; update the single call site (summarize-sessions.mjs:42) + specs.#12820where oversized), bounded bysummarizationBatchLimitper run.Contract Ledger
findSessionsToSummarize(includeAll)paramSessionService.mjs:328summarizeSessions,summarize-sessions.mjssummarize-sessions.mjs:42Acceptance Criteria
ONE_MONTH_MS/minTimestampand bothif (!includeAll)filters removed fromfindSessionsToSummarize; it scans all sessions.includeAllparameter removed fromfindSessionsToSummarize+summarizeSessions;summarize-sessions.mjscall site updated; no dangling references.pending-session-summarycount drains toward 0 over successive sweeps (no longer floors at ~299).Out of Scope
properties.sessionIdonto the ~110 legacySESSION_SUMMARYnodes that lack it (separate false-pending cleanup).Avoided Traps
includeAll"for flexibility" — rejected: with the window gone it's vestigial; re-introduce deliberately if an all-vs-window need ever returns.Decision Record impact
none— the 30-day window is not ADR-codified; this removes an obsolete operational safeguard.Related
Origin Session ID: 0f94aade-f9b1-4214-b17e-be0bdf33e2be Retrieval Hint: "session-summary backlog 30-day window all-time scope mismatch includeAll findSessionsToSummarize"