Skip to content

symfonic.memory.backends.mongo_positional_forget_admission

mongo_positional_forget_admission

Mongo apply admits only what the public Mongo v1 forget can erase (CORE-171).

Before apply, a v0 row is erased by the positional forget, which has no inventory cap. After apply, a migrated row is erased only by capabilities.memory.mongo_native_forget, whose inventory (mongo_native_forget_inventory) refuses every forget of the tenant while the tenant holds more than 1024 nodes or 1024 edges, or the database more than 1024 vectors, or any of those rows has an owner it cannot read (NULL or malformed). So inside the guarded transaction, before any re-key, apply projects every owner the re-key would write and refuses unless those tenant-wide conditions would hold afterwards. The caps are read here, never changed.

That forget also refuses every scope whose node a quarantined edge touches, because no scope selects a quarantine key (owner decision, 2026-10-14). So apply refuses (QUARANTINED_LINK) while a quarantined edge would remain touching a node the forget can erase, where the plan moves the edge or the node. An edge quarantined earlier leaves by delete_quarantined_rows; an edge this plan quarantines leaves in the same transaction when apply names it in delete_quarantined_edges. Not checked: the refusal that depends on the forgotten scope, of a forget whose erased node has a settled edge outside that scope. The migration guide lists it as a residual.

refuse_unerasable async

refuse_unerasable(cols: dict, tenant: str, changes: tuple[tuple[str, str, Any, Any], ...], deleted: frozenset[str]) -> None

Refuse unless the forget's inventory admits the result and no link blocks it.

UNERASABLE when the inventory would refuse the tenant; else QUARANTINED_LINK naming the edges that would block a forget.

Source code in src/symfonic/memory/backends/mongo_positional_forget_admission.py
async def refuse_unerasable(
    cols: dict, tenant: str, changes: tuple[tuple[str, str, Any, Any], ...],
    deleted: frozenset[str],
) -> None:
    """Refuse unless the forget's inventory admits the result and no link blocks it.

    ``UNERASABLE`` when the inventory would refuse the tenant; else
    ``QUARANTINED_LINK`` naming the edges that would block a forget.
    """
    new = {(kind, row_id): target for kind, row_id, _expected, target in changes}
    try:
        native, links = await _admit(cols, tenant, new)
    except MemoryContractError as error:
        raise MemoryContractError(UNERASABLE) from error
    blocking = _blocking(native, links, new, deleted)
    if blocking:
        raise MemoryContractError(QUARANTINED_LINK + ", ".join(blocking))