Context
AC2 of #13637 (session-summary drift churn-gate), carved out at PR #13645 (which Resolves #13637's AC1 — the gate that stops re-summarizing active sessions). The gate fixes the behavior; this ticket fixes the measurement: getPendingSessionSummaryCount still counts the all-time pending set (the epic's 283→284 stuck symptom), so it never reaches 0 / signals "done." Design pre-converged with @neo-opus-ada in the #13645 review.
Live latest-open sweep: checked latest 20 open issues at 2026-06-20T17:36Z — no equivalent found (#13637 / #13638 / #13592 are distinct slices; #12435 is adjacent test-isolation). A2A in-flight claim sweep (last 30 msgs): no marker-rescope claim.
The Problem
getPendingSessionSummaryCount (ai/daemons/orchestrator/scheduling/summary.mjs:80) counts ALL sessions with AGENT_MEMORY turns but no SESSION_SUMMARY — including the non-drainable population (per #13624 DB evidence: 283/285 are >2d old, 172/285 are ≤2-turn). Its own JSDoc admits it is "a proxy, not a claim that Chroma drift has drainable work." So the marker is flat-forever (no "done" signal) even while the drift drain is actively working — the operator's "14h, backlog 283→284" symptom. Goal: after a heavy-maintenance window the orchestrator reaches a stable "backlog resolved" state — the marker must be able to reach 0.
The Architectural Reality
Two different surfaces: the marker getPendingSessionSummaryCount (graph SQL over Nodes, summary.mjs:80) vs the drift drain findSessionsToSummarize (Chroma, ai/services/memory-core/SessionService.mjs) — now gated on swarmHeartbeat.idleThresholdMs (PR #13645). For the marker to equal the drainable set it must apply the SAME idle criterion the gate uses, plus the tiny-session exclusion.
The Fix
Re-scope getPendingSessionSummaryCount to count only sessions that are (a) idle past idleThresholdMs (the gate's drainable criterion — reuse the leaf, no new config) AND (b) above a tiny-turn floor (≤N-turn excluded). Reaches 0 = "done." If a clean SQL re-scope proves too costly, the converged fallback is to re-label the marker as explicit static observability (the #13637 AC2 "OR re-labeled" clause).
Folded-in (ada's #13645 review flags on the gate, same drift code): (c) future-skewed ts (lastActivity > now) → add a >= 0 guard in findSessionsToSummarize so a clock-skewed session can't be perpetually gated; (d) unparseable-ts fail-open → documenting comment (corrupt-ts is rare + self-heals via Case-A/B).
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
getPendingSessionSummaryCount + the pending-session-summary log marker |
summary.mjs:80 |
count only the drainable set (idle-past-window + non-tiny) |
re-label as static observability |
function JSDoc |
#13624 DB analysis (issuecomment-4758667977) |
Decision Record impact
aligned-with ADR-0019 — reuses the resolved idleThresholdMs leaf at the use site; introduces no new config leaf.
Acceptance Criteria
Out of Scope
- The churn-gate itself (delivered by PR #13645).
- miniSummary backfill throughput (#13638).
- The process-termination integrity daemon (#13624 axis-3 (b)).
Related
Parent epic: #13624. Sibling: #13637 (delivered by PR #13645). #13638 (miniSummary). #12435 (test-isolation, adjacent). ADR-0019.
Release classification: post-release (Approve+Follow-Up follow-up of #13637 — non-blocking; boardless).
Origin Session ID: 9ecfc519-a1c5-4153-872c-1e8ad69ed7f2
Retrieval Hint: "session-summary marker re-scope drainable getPendingSessionSummaryCount idleThresholdMs gate timestamp future-skew #13637 AC2"
Context
AC2 of #13637 (session-summary drift churn-gate), carved out at PR #13645 (which Resolves #13637's AC1 — the gate that stops re-summarizing active sessions). The gate fixes the behavior; this ticket fixes the measurement:
getPendingSessionSummaryCountstill counts the all-time pending set (the epic's 283→284 stuck symptom), so it never reaches 0 / signals "done." Design pre-converged with @neo-opus-ada in the #13645 review.Live latest-open sweep: checked latest 20 open issues at 2026-06-20T17:36Z — no equivalent found (#13637 / #13638 / #13592 are distinct slices; #12435 is adjacent test-isolation). A2A in-flight claim sweep (last 30 msgs): no marker-rescope claim.
The Problem
getPendingSessionSummaryCount(ai/daemons/orchestrator/scheduling/summary.mjs:80) counts ALL sessions withAGENT_MEMORYturns but noSESSION_SUMMARY— including the non-drainable population (per #13624 DB evidence: 283/285 are >2d old, 172/285 are ≤2-turn). Its own JSDoc admits it is "a proxy, not a claim that Chroma drift has drainable work." So the marker is flat-forever (no "done" signal) even while the drift drain is actively working — the operator's "14h, backlog 283→284" symptom. Goal: after a heavy-maintenance window the orchestrator reaches a stable "backlog resolved" state — the marker must be able to reach 0.The Architectural Reality
Two different surfaces: the marker
getPendingSessionSummaryCount(graph SQL overNodes,summary.mjs:80) vs the drift drainfindSessionsToSummarize(Chroma,ai/services/memory-core/SessionService.mjs) — now gated onswarmHeartbeat.idleThresholdMs(PR #13645). For the marker to equal the drainable set it must apply the SAME idle criterion the gate uses, plus the tiny-session exclusion.The Fix
Re-scope
getPendingSessionSummaryCountto count only sessions that are (a) idle pastidleThresholdMs(the gate's drainable criterion — reuse the leaf, no new config) AND (b) above a tiny-turn floor (≤N-turn excluded). Reaches 0 = "done." If a clean SQL re-scope proves too costly, the converged fallback is to re-label the marker as explicit static observability (the #13637 AC2 "OR re-labeled" clause).Folded-in (ada's #13645 review flags on the gate, same drift code): (c) future-skewed
ts(lastActivity > now) → add a>= 0guard infindSessionsToSummarizeso a clock-skewed session can't be perpetually gated; (d) unparseable-tsfail-open → documenting comment (corrupt-ts is rare + self-heals via Case-A/B).Contract Ledger
getPendingSessionSummaryCount+ thepending-session-summarylog markersummary.mjs:80Decision Record impact
aligned-with ADR-0019— reuses the resolvedidleThresholdMsleaf at the use site; introduces no new config leaf.Acceptance Criteria
getPendingSessionSummaryCountcounts only the drainable set (idle pastidleThresholdMs+ above the tiny floor) and can reach 0 once the drain has cleared it — OR is explicitly re-labeled as static observability.lastActivityis not perpetually gated (>= 0guard).Out of Scope
Related
Parent epic: #13624. Sibling: #13637 (delivered by PR #13645). #13638 (miniSummary). #12435 (test-isolation, adjacent). ADR-0019.
Release classification: post-release (Approve+Follow-Up follow-up of #13637 — non-blocking; boardless).
Origin Session ID: 9ecfc519-a1c5-4153-872c-1e8ad69ed7f2 Retrieval Hint: "session-summary marker re-scope drainable getPendingSessionSummaryCount idleThresholdMs gate timestamp future-skew #13637 AC2"