Skip to content

symfonic.memory.retraction

retraction

Soft-retraction marker contract (namespaced, framework-reserved keys).

A retract_node extraction op soft-retracts a false-positive/corrected memory by stamping a flag on MemoryNode.properties -- the read-side filters (GraphMemoryStore.get_node / bump_spreading / get_neighbors / query_nodes / distinct_labels) hide any node carrying that flag, and consolidation's prune_retracted phase later hard-deletes it.

The marker MUST be namespaced. properties is an open, caller-writable bag that also carries arbitrary domain data (e.g. a fact literally about a retraction, {"retracted": True}). If the framework used a plain "retracted" key, that domain data would collide with the framework's own control flag: the node would silently become invisible (read filter) and then get permanently hard-deleted on the next consolidation pass, even though nothing ever asked the framework to retract it. Namespacing the key under a __symfonic_..._​__ prefix makes that collision structurally impossible -- no legitimate domain payload will ever coincidentally match it.

This module owns the marker contract exclusively (one concept, SOLID): the key names and the read-side predicate. Every caller that needs to check or stamp retraction state imports from here rather than re-deriving the key inline.

is_retracted

is_retracted(properties: dict | None) -> bool

Return True iff properties carries the framework retraction flag.

Treats a missing/None properties bag as "not retracted". This is the single predicate every read-side choke point (and the prune phase) must call -- never inline properties.get(RETRACTED_KEY) -- so the marker contract has exactly one place that can drift.

Source code in src/symfonic/memory/retraction.py
def is_retracted(properties: dict | None) -> bool:
    """Return True iff ``properties`` carries the framework retraction flag.

    Treats a missing/``None`` properties bag as "not retracted". This is the
    single predicate every read-side choke point (and the prune phase) must
    call -- never inline ``properties.get(RETRACTED_KEY)`` -- so the marker
    contract has exactly one place that can drift.
    """
    return bool((properties or {}).get(RETRACTED_KEY))