symfonic.core.prompt.blocks.protocol_read¶
protocol_read ¶
The two read capabilities a block source can present.
Split out of :mod:symfonic.core.prompt.blocks.protocol (441 lines against the
300-line budget). protocol keeps the capability argument -- the long
docstring on why these are structural Protocols rather than self-reported
flags -- along with WritableBlockSource, the one capability that mutates.
The two read capabilities live here.
The direction is one-way and must stay that way: WritableBlockSource
extends HistoryCapableBlockSource, so protocol imports this module and
nothing here may import protocol back. protocol re-exports both names.
BlockSource ¶
Bases: Protocol
Reads the current revision of a prompt block for a scope.
offline_safe and scope_aware are declared members, so
isinstance requires them to be present: a source that never
decided whether it can be reached offline does not satisfy the
Protocol.
offline_safe--Falseforfeits authored-spine survival when the backing system is unreachable.scope_aware--Falsemeans the source is deployment-global: it serves one value for every tenant. Pairing such a source with a non-deployment block scope is a construction-time error.
isinstance verifies that load and both flags exist. It does
not verify that load honours scope or block_id -- a
source that was never implemented is caught; a source implemented
badly is not.
Both flags are declared as abstract properties, not bare
annotations, so the nominal inheritance path is closed the same
way the methods are: typing._ProtocolMeta.__instancecheck__
short-circuits on the real-subclass check before ever consulting a
Protocol's data members, so a bare annotation is only ever enforced
on the duck-typed path -- a class Mine(BlockSource) that
implements load but forgets offline_safe would answer
isinstance truthfully while mine.offline_safe raised
AttributeError. Marking them abstract makes that subclass
un-instantiable instead, matching what @abstractmethod already
does for load itself. A duck-typed source setting a plain
offline_safe = True class attribute is unaffected: ABCMeta
clears an inherited abstract name the moment the subclass provides
any non-abstract value for it, property or plain attribute alike.
load
abstractmethod
async
¶
Return the current revision of block_id for scope.
scope is required and typed :class:TenantScope: the
isolation argument cannot be dropped, and the isolation key is
scope.scope_path (see :func:block_isolation_key), never
scope.tenant_id alone.
block_id is the block spec's name. A source backing several
blocks selects by it instead of returning an arbitrary row for
the scope. A single-block source may ignore it, but must accept
it.
Source code in src/symfonic/core/prompt/blocks/protocol_read.py
HistoryCapableBlockSource ¶
Bases: BlockSource, Protocol
A :class:BlockSource whose prior revisions remain retrievable.
An operator-editable block must be backed by one of these: being able to see what a block used to say, and to identify the revision to go back to, is the whole restore path core requires.
Both methods take the same (scope, block_id) key as
:meth:BlockSource.load, by signature. History is cumulative, so a
history read that forgot isolation would leak strictly more than a
current-value read; there is no overload omitting scope, so the
argument cannot be forgotten.
isinstance(src, HistoryCapableBlockSource) is True only when
both history methods exist. It catches the source that never
implemented them -- the common case, since a store with no versioning
cannot present them. It does not catch a source that implements
them badly: returning an empty list, ignoring block_id, or
fabricating revisions all pass presence checks. Only that adapter's
own tests can catch those.
Note that this Protocol carries no write verb. Reading history does not imply permission to change it.
list_revisions
abstractmethod
async
¶
Return the known revisions of block_id for scope.
Ordering is the adapter's own (a git log, an append-only table).
Every returned revision must belong to this scope_path and
this block_id.
Source code in src/symfonic/core/prompt/blocks/protocol_read.py
load_revision
abstractmethod
async
¶
Return one specific prior revision of block_id.
scope is required for the same reason it is on
:meth:BlockSource.load: a revision table keyed only by
block_id, with the scope recorded on the current-value row
alone, would expose every tenant's history through this method.