Skip to content

symfonic.agent.cutover.memory_fold

memory_fold

When a folded bundle gets a memory capability, and which ports it gets.

TA8.71. One call site held two defects, and each produced a store that stayed empty while :meth:RetrievalBundle.serves_consolidation answered True:

  • the capability was built only if hydrator is not None, so a deployment that consolidates without hydrating got none at all -- no write stage was declared, the producer was never called, and the turn recorded nothing. Measured as zero records produced, which reads like "nothing worth keeping" rather than like a wiring gap;
  • lifecycle was never passed, and the capability declares its finalize stage only when it has one. So even a hydrating deployment staged its records and never published them -- write once, flush never -- and pending rows are not retrievable by contract.

The second is the sharper lesson. serves_consolidation() asks whether the ports can flush, and they could. What nobody asked is whether anything calls them. Capability is not occurrence, and that predicate's licence now rests on tests/agent/test_turn_publishes_to_the_store.py, which takes a turn through a real agent and reads the store back, rather than on the ports being present.

memory_capability

memory_capability(hydrator: Any, *, scope: Any, limit: int, writes: Any, records_from: Any, lifecycle: Any, consolidating: bool) -> MemoryCapability | None

The capability this bundle should fold, or None for neither half.

Folded when the deployment hydrates or consolidates: the two halves are independent, and a deployment asking for one must not be silently given neither. Which stages it then declares is the capability's own decision, made per port -- it declares retrieval only with a hydrator, the write stage only with a writer and a producer, and finalize only with a lifecycle.

Source code in src/symfonic/agent/cutover/memory_fold.py
def memory_capability(
    hydrator: Any,
    *,
    scope: Any,
    limit: int,
    writes: Any,
    records_from: Any,
    lifecycle: Any,
    consolidating: bool,
) -> MemoryCapability | None:
    """The capability this bundle should fold, or ``None`` for neither half.

    Folded when the deployment hydrates **or** consolidates: the two halves are
    independent, and a deployment asking for one must not be silently given
    neither. Which stages it then declares is the capability's own decision,
    made per port -- it declares retrieval only with a hydrator, the write
    stage only with a writer and a producer, and finalize only with a
    lifecycle.
    """
    if hydrator is None and not consolidating:
        return None
    return MemoryCapability(
        hydrator,
        scope=scope,
        limit=limit,
        writer=writes,
        records_from=records_from,
        lifecycle=lifecycle,
    )