Skip to content

Daemon-maintained interaction summary: stop counting citations by enumerating them #947

Description

@ericvicenti

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 changesciting_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:

  1. 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.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions