Problem
InteractionSummary derives {citations, comments, changes, children, authorUids, blocks} — a handful of small integers — by fetching the target's entire citation list through ListCitations. Each ListCitations call costs the daemon 0.3–2.5s regardless of page size, because qListCitationsTpl materializes the target's whole citation fan-out (the changes → citing_blobs UNION expanding each change by every Ref in its genesis chain) and sorts it in temp B-trees before applying LIMIT.
This was a primary cause of the 2026-08-11 production saturation (see docs/daemon-saturation-incident.md): unbounded chains of these calls from /api/InteractionSummary held the 12-connection SQLite read pool and convoyed every other read. #946 capped every client-side enumeration to one page as a stopgap, at the cost of under-reporting counts for documents with >500 citations (two exist in production today, ~6.4k citations each).
Proposed fix
Serve the summary from the daemon with one bounded aggregation, the way children_count already works (qDocumentsOuterColumns in backend/api/documents/v3alpha/documents.go does the cheap indexed thing for directories).
Two possible shapes:
- Extend
ActivitySummary with citation_count (and optionally latest_citation_time), computed by an indexed COUNT over resource_links by target — enough for header/card counts, and lets counts return to SSR if desired.
- New
GetInteractionSummary RPC that also returns the per-block {citations, comments} breakdown (GROUP BY on resource_links.extra_attrs->>'f') and distinct author list, replacing the client-side calculateInteractionSummary entirely, including dedup semantics (deduplicateCitations).
Option 2 removes the enumeration from every consumer (web margin markers, embed cards, desktop) and makes the result cacheable server-side. Option 1 is a smaller first step.
Either way the aggregation must be bounded: no per-row genesis-chain expansion, index seeks only.
Also worth doing while in there
qListCitationsTpl pays the full fan-out cost even for one page; if the citations panel is to show >500 citations again, pagination needs to become genuinely incremental.
- Per-RPC deadlines / in-flight caps and Remix client-disconnect propagation are the systemic guards (incident follow-ups 4–5).
🤖 Generated with Claude Code
Problem
InteractionSummaryderives{citations, comments, changes, children, authorUids, blocks}— a handful of small integers — by fetching the target's entire citation list throughListCitations. EachListCitationscall costs the daemon 0.3–2.5s regardless of page size, becauseqListCitationsTplmaterializes the target's whole citation fan-out (thechanges→citing_blobsUNION expanding each change by every Ref in its genesis chain) and sorts it in temp B-trees before applyingLIMIT.This was a primary cause of the 2026-08-11 production saturation (see
docs/daemon-saturation-incident.md): unbounded chains of these calls from/api/InteractionSummaryheld the 12-connection SQLite read pool and convoyed every other read. #946 capped every client-side enumeration to one page as a stopgap, at the cost of under-reporting counts for documents with >500 citations (two exist in production today, ~6.4k citations each).Proposed fix
Serve the summary from the daemon with one bounded aggregation, the way
children_countalready works (qDocumentsOuterColumnsinbackend/api/documents/v3alpha/documents.godoes the cheap indexed thing for directories).Two possible shapes:
ActivitySummarywithcitation_count(and optionallylatest_citation_time), computed by an indexedCOUNToverresource_linksby target — enough for header/card counts, and lets counts return to SSR if desired.GetInteractionSummaryRPC that also returns the per-block{citations, comments}breakdown (GROUP BYonresource_links.extra_attrs->>'f') and distinct author list, replacing the client-sidecalculateInteractionSummaryentirely, including dedup semantics (deduplicateCitations).Option 2 removes the enumeration from every consumer (web margin markers, embed cards, desktop) and makes the result cacheable server-side. Option 1 is a smaller first step.
Either way the aggregation must be bounded: no per-row genesis-chain expansion, index seeks only.
Also worth doing while in there
qListCitationsTplpays the full fan-out cost even for one page; if the citations panel is to show >500 citations again, pagination needs to become genuinely incremental.🤖 Generated with Claude Code