symfonic.capabilities.memory.overlay¶
overlay ¶
The scope a cycle works on, held in memory until the cycle commits.
A consolidation cycle has to be able to read what it has already written --
prune_orphans runs after meta_nodes and must see the meta nodes,
semantic_merge retracts what earlier phases strengthened -- while nothing
it writes may be visible to anybody else until the whole cycle commits. That is
a read-your-writes overlay, and the temptation is to hand-roll one: a dict of
pending nodes, a set of deleted ids, and a filter matcher written from memory
to make query_nodes behave.
That matcher is the trap. The filter vocabulary is real -- layer, label,
label_prefix, arbitrary property equality, prefix-visibility by scope path,
subtree containment, the limit rules -- and a second implementation of it
that disagrees anywhere is a phase that silently reads a different graph inside
a cycle than outside one. So this overlay does not reimplement any of it: it
is :class:~symfonic.memory.backends.in_memory.InMemoryGraphBackend, the
reference implementation of that vocabulary, pre-loaded with the scope. Reads
then behave in the overlay exactly as they behave in the durable store, because
the code answering them is the code the store's own tests pin.
One honest caveat: reads inside a cycle are answered by the in-memory matcher, so if a durable backend ever disagreed with it about filter semantics -- a property comparison, a limit edge case -- a phase would read slightly differently inside a cycle than outside one. The vocabulary is documented and shared, and this is the implementation its tests pin, which is the strongest available position; it is not a proof that every backend agrees.
Subclassed rather than composed for one reason: :meth:seed has to place rows
into the backend's tables as they were stored, and every public write path
stamps the writing scope's path into the property bag. An ancestor node loaded
through add_node would come back stamped with the cycle's scope and become
visible to scopes that must not see it. Seeding is the one operation that must
bypass the write path, and a subclass is where that is legitimate.
IncompleteScopeProjection ¶
Bases: RuntimeError
A bounded snapshot cannot prove that it contains the complete scope.
ScopeOverlay ¶
Bases: InMemoryGraphBackend
An in-memory graph pre-loaded with one scope's durable rows.
Source code in src/symfonic/capabilities/memory/overlay.py
seed
async
¶
Load everything a cycle over scope can read. Once per scope.
Three reads, because the two node directions answer different
questions and neither substitutes for the other: query_nodes is
prefix-visibility-scoped and returns what this scope may see (its
ancestors), query_subtree returns what this scope and its children
produced. A cycle does both, so the overlay holds both.
A full page cannot establish completeness with these unpaged ports. Refuse before populating the overlay; an incomplete adjacency list would otherwise turn a connected node into a deletable orphan.