Context
Carved from #13647 (parent; epic #13624) so the delivering PR has an honest standalone close-target. The session-summary-pending marker getPendingSessionSummaryCount (ai/daemons/orchestrator/scheduling/summary.mjs) over-counts orphaned sessions. Confirmed by a cross-store V-B-A (#13647 issuecomment-4759938336) and a live orchestrator run after #13656 merged: the miniSummary aged-drain processed 1344 rows → 1335 missing-content (orphaned — graph AGENT_MEMORY nodes whose Chroma content is gone: GC'd or partial-write-crash).
Live latest-open sweep: checked the latest 20 open issues at ~2026-06-20T21:05Z — no equivalent found.
The Problem
Of 285 graph-pending sessions: 238 orphaned (no recoverable Chroma content → unsummarizable), 45 Chroma-summarized-but-graph-unprojected, 2 genuinely pending. The drift sweep finds only ~40 candidates/run and processing them never moves the marker — the orphans are structurally non-drainable. Counting them produces the misleading "backlog doesn't drain (283→284)" telemetry, but orphaned content can never be summarized — it is an integrity concern, not summary-pending work.
The Architectural Reality
getPendingSessionSummaryCount is a graph-only SQL anti-join over Nodes (AGENT_MEMORY minus SESSION_SUMMARY ∪ SESSION-with-chromaId LIKE 'summary_%'). It is telemetry-only — it feeds the periodic-sweep trigger reason and does NOT gate the sweep — and runs inside getDueTask, a pure-sync projection (registry contract: no I/O, no async), so it cannot query Chroma. The graph carries archivedAt/archivedReason on AGENT_MEMORY nodes; the miniSummary backfill (#13656) already archives no-content rows.
The Fix
Add archivedAt IS NULL to the marker's memory_sessions CTE: a session counts while it has any un-archived memory node; a fully-archived (orphaned) session drops out. Mirrors the archived-skip #13656 added to the backfill fetch. Couples with the orphan archival (grace's axis-3 #13639 + #13656): as orphans are archived, the marker excludes them → it reflects drainable truth instead of inflating on records the drain can never drain.
Decision Record impact
aligned-with ADR-0019 — graph-only, reuses the existing archivedAt convention; introduces no new config leaf.
Acceptance Criteria
Out of Scope
- The 45 projection-lag (Chroma-summarized-but-graph-unprojected) — separate projection-health concern, stays on #13647.
- The orphan archival itself (grace's axis-3 #13639 / #13656).
- WHY 1335 are missing-content (expected GC vs data-loss bug) — axis-3 integrity question, routed to grace.
- #13647's gate-hardening ACs (future-skewed / unparseable timestamp edges on
findSessionsToSummarize).
Related
Parent: #13647. Epic: #13624. Couples: #13639 (axis-3 archival), #13656 (miniSummary archival, mirrored pattern).
Origin Session ID: c4fcedd0-c449-4f8c-b368-e3ac0c0509ff
Retrieval Hint: "getPendingSessionSummaryCount archivedAt orphaned session-summary marker measurement-truth #13647"
Context
Carved from #13647 (parent; epic #13624) so the delivering PR has an honest standalone close-target. The session-summary-pending marker
getPendingSessionSummaryCount(ai/daemons/orchestrator/scheduling/summary.mjs) over-counts orphaned sessions. Confirmed by a cross-store V-B-A (#13647 issuecomment-4759938336) and a live orchestrator run after #13656 merged: the miniSummary aged-drain processed 1344 rows → 1335 missing-content (orphaned — graphAGENT_MEMORYnodes whose Chroma content is gone: GC'd or partial-write-crash).Live latest-open sweep: checked the latest 20 open issues at ~2026-06-20T21:05Z — no equivalent found.
The Problem
Of 285 graph-pending sessions: 238 orphaned (no recoverable Chroma content → unsummarizable), 45 Chroma-summarized-but-graph-unprojected, 2 genuinely pending. The drift sweep finds only ~40 candidates/run and processing them never moves the marker — the orphans are structurally non-drainable. Counting them produces the misleading "backlog doesn't drain (283→284)" telemetry, but orphaned content can never be summarized — it is an integrity concern, not summary-pending work.
The Architectural Reality
getPendingSessionSummaryCountis a graph-only SQL anti-join overNodes(AGENT_MEMORY minus SESSION_SUMMARY ∪ SESSION-with-chromaId LIKE 'summary_%'). It is telemetry-only — it feeds the periodic-sweep trigger reason and does NOT gate the sweep — and runs insidegetDueTask, a pure-sync projection (registry contract: no I/O, no async), so it cannot query Chroma. The graph carriesarchivedAt/archivedReasonon AGENT_MEMORY nodes; the miniSummary backfill (#13656) already archives no-content rows.The Fix
Add
archivedAt IS NULLto the marker'smemory_sessionsCTE: a session counts while it has any un-archived memory node; a fully-archived (orphaned) session drops out. Mirrors the archived-skip #13656 added to the backfill fetch. Couples with the orphan archival (grace's axis-3 #13639 + #13656): as orphans are archived, the marker excludes them → it reflects drainable truth instead of inflating on records the drain can never drain.Decision Record impact
aligned-with ADR-0019— graph-only, reuses the existingarchivedAtconvention; introduces no new config leaf.Acceptance Criteria
getPendingSessionSummaryCountexcludes sessions whose memory nodes are all archived; a session with any un-archived node still counts.SessionSummaryBacklogCount.spec.mjs.Out of Scope
findSessionsToSummarize).Related
Parent: #13647. Epic: #13624. Couples: #13639 (axis-3 archival), #13656 (miniSummary archival, mirrored pattern).
Origin Session ID: c4fcedd0-c449-4f8c-b368-e3ac0c0509ff Retrieval Hint: "getPendingSessionSummaryCount archivedAt orphaned session-summary marker measurement-truth #13647"