symfonic.capabilities.memory.admission¶
admission ¶
Which legacy entries the migrated adapter may serve, and on whose authority.
Split from :mod:.compat when that module passed its budget, and the seam is
the honest one: compat translates shapes between legacy and this
capability, in both directions and without judgement. This decides admission
— whether a stored row can be attributed to a scope at all, and what happens to
the ones that cannot. Translation that always succeeds and a gate that
sometimes refuses are different jobs.
admit_legacy_entries ¶
admit_legacy_entries(entries: Iterable[Any]) -> tuple[tuple[MemoryRecord, ...], tuple[tuple[str, str], ...]]
Convert what legacy retrieval returned, excluding what cannot be attributed.
Returns the admitted records in the order retrieval chose and the entries that were dropped, each with the reason. Order is preserved because ranking belongs to the retrieval engine; re-ordering here would silently re-rank the prompt, and both orders render as plausible text.
Excludes rather than raises. One unattributable row must not end the turn: that would turn a data-quality problem into an outage and make the migrated path less available than the legacy one it replaces. The drop is returned rather than logged and forgotten, because a silently shorter recall is indistinguishable from a model that simply remembered less.
Source code in src/symfonic/capabilities/memory/admission.py
admit_legacy_pairs ¶
admit_legacy_pairs(entries: Iterable[Any]) -> tuple[tuple[tuple[MemoryRecord, Any], ...], tuple[tuple[str, str], ...]]
Like :func:admit_legacy_entries, keeping each record beside its entry.
The record deliberately does not carry a relevance score — it is what a
memory is, not how it rated for one query — so a caller that needs
legacy's ranking needs the entry too. Returning the pair is what stops that
caller from reaching for the nearest number on the record instead: review
found the port scoring on salience, which is derived from legacy
importance, so the most important memory outranked the most
relevant one and the recall block came out inverted.
Source code in src/symfonic/capabilities/memory/admission.py
record_from_legacy_entry ¶
Read a legacy MemoryEntry into a record, resolving its provenance.
The mirror of :func:legacy_entry_payload, and the adapter's single
decision about where a record's isolation key comes from. Three branches,
settled on 2026-08-17 (#14):
- an explicit
metadata['scope_path']-> used, read through the legacy wire form; - absent or blank, with a valid
tenant_id-> the tenant root, exactly as legacy's own dual-read resolves it; - neither -> :class:
MemoryContractError.
Branch 2 is the one that needed deciding, and the distinction it rests on
is narrow but load-bearing. What the #16 design rejected was deriving a
record's path from the query's scope: that makes the bridge's
revalidation pass by construction, so an isolation test written over it
would be green and prove nothing. This derives from the record's own stored
tenant_id -- per-record, deterministic, and narrower than a deep path,
since a tenant root is visible only within its own tenant. It is also what
legacy already does, which is what keeps the migrated path at parity
instead of quietly recalling less.