5.5 Graph-cached aggregates — cache: {kind: graph}
For an aggregate producer, cache: {kind: graph} makes the committed graph itself the cache
tier: the first traversal collects the anchor’s neighborhood, LLM-reduces it, and persists the
result as a real, committed, scope-stamped node; while the inputs are unchanged, a repeat
traversal is a plain graph hit with zero model calls. The canonical use is the on-demand document
summary:
- name: docSummary kind: aggregate edgeType: HAS_SUMMARY collect: targetLabel: Chunk via: PART_OF incoming: true # the neighbors point AT the anchor: (Chunk)-[:PART_OF]->(Document) anchorLabel: Document anchorKeyProperty: uri # PER-ANCHOR mode: each key is one document, owner-guarded text: "{{ text }}" reduce: { using: summarize, into: summary, args: ["Summarize this document faithfully."] } identityField: id anchorKeyField: docUri cache: { kind: graph }MATCH (d:Document …)-[:HAS_SUMMARY]->(s:Summary) RETURN s.summary then generates once per document
and serves from the graph thereafter.
The rules, all engine-enforced:
- Per-anchor collection.
collect.anchorKeyPropertyswitches the aggregate from its default per-principal mode (the anchor is the acting principal’s own node when it has one; one reduction for the whole batch) to per-ANCHOR: each producer key names one anchor (matched on that property, guarded to the acting user’s own nodes), and each anchor’s neighborhood reduces independently.incoming: trueflips the traversal for containment shapes like(Chunk)-[:PART_OF]->(Document). - Freshness is an INPUT HASH, not a clock. The persisted node carries a hash of the collected items plus the full reduce fingerprint (function, effective instruction, role, band). Edit the document — or the realm’s prompt, or the role — and exactly that node regenerates, in place. The re-check costs one graph read; the model runs only when something actually changed.
- Wordcount BANDS. A query’s
{ai: {wordcount: N}}quantizes to the nearest persisted band — gist (~40 words), standard (~200), long (~600) — and each band is its own committed node. The directive-free canonical is the standard band; a nearby request (250) is a hit on the same node. The band’s own target (never the raw request) folds into the instruction, so one band has one stable fingerprint. - Semantic steering stays transient. A
hint,voice,realm.*parameter, or amodeloverride makes the result a non-canonical artifact: it is reduced fresh for that query, returned with its own transient identity, and never persisted, never served from, and never overwrites the canonical node.{ai: {fresh: true}}bypasses the hash check but writes the regenerated canonical through. - An honest miss never earns a node. A reduction that answers with the no-answer sentinel stays transient and is retried on the next traversal — fabrication can never become durable.
- Regeneration is progressive — for reductions whose results compose. When content changes,
only the changed portions re-reduce: the reduction proceeds over content windows, unchanged
windows reuse their previous partial results, and the partials combine into the answer — a
re-reduction for prose (
summarize,synthesize), a pure truth combination for a verdict (holds: any supporting window ⇒ true; a grounded no with no true ⇒ false; all silent ⇒ no answer), a union for a semantic filter (relevant). Appending to a large document costs a reduction proportional to the appended content, not the whole document; the same content always yields the same windows. A reduction that must read the whole group at once (classify,score,themes,cluster,extract,argmax) always re-reduces whole.{ai: {fresh: true}}regenerates everything. A window whose reduction honestly found nothing keeps that answer until its own content changes. - Node-only, scope-stamped writes. The engine commits only the node, stamped with immutable
worldId,contextId, and access-policy revision. Two contexts summarizing an identical URI do not share a node unless policy explicitly promotes the result toWORLDvisibility; two worlds never share one. The anchor edge is re-linked transiently per query. The node’s label is stamped by the engine from the join’s own target type — a realm never declares it, so it can never drift.
Cost intuition: the expensive thing (the reduction) runs once per
(worldId, contextId, access-policy revision, anchor, band) and again only on
change — and on change, only over the windows that changed; everything else — the freshness probe,
the hit path, the re-link — is indexed graph reads.

