Skip to content

symfonic.capabilities.memory.conversation_fork

conversation_fork

Owner-authorized transcript forks; no Agent invocation or session markers.

The host must authorize both sessions. Positional identities use explicit tenant/principal/session fields; native identities require an app-supplied session kind and preserve every owner boundary, including case/workspace.

ConversationForkPort

Bases: Protocol

fork async

fork(request: ConversationForkRequest) -> ConversationForkResult

Atomically reserve a new destination, snapshot rows, and persist a receipt.

A retry with the same owner/fork_id must retain source, destination and committed cutoff. Another request cannot adopt an occupied destination. Missing sources are refused. Only durable transcript rows are copied. Session erasure retains a receipt tombstone: retries refuse a forgotten destination. Owner-wide erasure removes receipt metadata and transcript together. Hosts must retain their normal session authorization policy.

Source code in src/symfonic/capabilities/memory/conversation_fork.py
async def fork(self, request: ConversationForkRequest) -> ConversationForkResult:
    """Atomically reserve a new destination, snapshot rows, and persist a receipt.

    A retry with the same owner/fork_id must retain source, destination and
    committed cutoff. Another request cannot adopt an occupied destination.
    Missing sources are refused. Only durable transcript rows are copied.
    Session erasure retains a receipt tombstone: retries refuse a forgotten
    destination. Owner-wide erasure removes receipt metadata and transcript
    together. Hosts must retain their normal session authorization policy.
    """

ConversationForkRequest dataclass

ConversationForkRequest(owner: MemoryScope, source: ConversationKey, destination: ConversationKey, fork_id: str, session_kind: str | None = None)

validate

validate() -> None

Validate again at storage admission, before acquiring a connection.

Source code in src/symfonic/capabilities/memory/conversation_fork.py
def validate(self) -> None:
    """Validate again at storage admission, before acquiring a connection."""
    if not self.fork_id or not self.fork_id.strip() or "\x00" in self.fork_id:
        raise MemoryContractError("fork requires a nonempty idempotency identity")
    if (not self.source.session_id or not self.destination.session_id
            or self.source.session_id == self.destination.session_id):
        raise MemoryContractError("fork requires distinct nonempty session IDs")
    for key in (self.source, self.destination):
        expected = self.session_scope(key.session_id)
        if key.scope_path != expected.path:
            raise ScopeViolation("fork session does not belong to the exact owner")

ConversationForkResult dataclass

ConversationForkResult(destination: ConversationKey, source_position: int, copied_turns: int)

Stable committed snapshot metadata, returned unchanged on retries.