symfonic.capabilities.memory.phases.phase_graph¶
phase_graph ¶
What a phase is handed when it asks for the graph.
Its own module because it is a composition decision, not a QUICK-cadence one: every roster factory routes through it, and what it returns decides whether a phase's writes are deferred into the cycle's journal and whether they are fenced against a lost lease. Reading it should not mean reading four phase implementations first.
quick re-exports it, which is where every factory and test imports it from.
phase_graph ¶
The store the phases write through, from a store or a bare backend.
isinstance rather than duck-typing: both objects answer to
query_nodes and their signatures differ (the store takes layer=,
the backend takes a filter mapping), so a check that guessed from the shape
would guess wrong exactly where it mattered.
The backend is wrapped in :class:~symfonic.capabilities.memory.journal. JournalledGraph and the store in :class:~symfonic.capabilities.memory. fencing.FencedGraph, which is why every factory routes through this one
function. Ten of the roster's phase modules write to the graph directly
rather than through the write coordinator, so anything applied phase by
phase would be a rule each of them -- and each one written later -- has to
remember. Applied here it is structural: a phase gets both by being handed
its graph.
Both are inert outside a cycle. The journal defers mutations only while one is running on this task and the fence checks only while a lease is held, so single-process use, an ordinary turn and every unit test behave exactly as before.
A deployment that shares one backend between the phases and its memory
layers should wrap it once at the composition root instead -- see
:class:~symfonic.capabilities.memory.journal.JournalledGraph. Wrapping
here covers what the phases reach; it cannot cover what
ProceduralLayer writes through a store this function never sees.