Persist and check the memory-free root proof beside a pause (CORE-194).
The authority is the kernel's continuation_attestation: it judges whether
the live run qualifies and it alone admits a verified proof. This module only
moves rows. The private _seal_written is called only by the private
TurnCheckpointStore._record_issued, which only capability.pause calls,
right after it wrote the turn state of the pause it just minted: the kernel
seals those claims and the exact text that store encoded. The public
TurnCheckpointStore.record writes and never seals. The
host calls :func:admit_continuation last before the claim: it reads the turn
row once, has the kernel verify the proof over that row's digest, and returns
the decoded checkpoint the resume must use instead of reading again.
A pause whose ids are still live here keeps today's path once the resumed
run's lineage over them is admitted before the claim. Everything else --
no attestor (a custom signer without one, a Keyset subclass), a resuming
composition that reaches memory, an absent, corrupt, altered or rebound proof
-- refuses typed before the claim, and the token stays unspent.
admit_continuation
async
admit_continuation(capability: Any, claims: Any, agent: Any) -> tuple[Any, Any]
(admission, turn): (lineage, None) for live ids, else the verified pair.
Live ids are not proof by being registered: the resumed run's lineage over
them is admitted as its start would admit it -- every declared prover over
the named ancestry, then an unresolved lineage refuses -- and returned for
the host to hold through the claim and the run. Raises the kernel's
UNPROVABLE_LINEAGE (typed) when nothing proves the paused run, a
prover's own refusal as raised, and the turn store's own errors when its
row is missing.
Source code in src/symfonic/capabilities/human/continuation_proof.py
| async def admit_continuation(capability: Any, claims: Any, agent: Any) -> tuple[Any, Any]:
"""``(admission, turn)``: ``(lineage, None)`` for live ids, else the verified pair.
Live ids are not proof by being registered: the resumed run's lineage over
them is admitted as its start would admit it -- every declared prover over
the named ancestry, then an unresolved lineage refuses -- and returned for
the host to hold through the claim and the run. Raises the kernel's
``UNPROVABLE_LINEAGE`` (typed) when nothing proves the paused run, a
prover's own refusal as raised, and the turn store's own errors when its
row is missing.
"""
from symfonic.kernel.contracts.continuation_attestation import admit_root, turn_digest
from symfonic.kernel.contracts.run_admission import refuse_unprovable, unprovable
named = (claims.run_id, claims.root_run_id)
if not unprovable(*named):
from symfonic.kernel.contracts.run_preclaim import admit_before_start
# The resumed run descends from the paused run and its root.
return await admit_before_start("", claims.root_run_id, claims.run_id), None
from symfonic.capabilities.human.turnstate import TurnCheckpointStore
# Only the SDK's own turn store can hold a proof; a structural capability
# without one is unprovable here, typed and before the claim.
turns = getattr(capability, "turns", None)
if not isinstance(turns, TurnCheckpointStore):
refuse_unprovable(*named)
attestor = turns._attestor # noqa: SLF001 - this package's own store
if attestor is None or resuming_memory(agent):
refuse_unprovable(*named)
raw = await turns.read_raw(claims) # read once; the resume uses these bytes
if not isinstance(raw, str):
refuse_unprovable(*named)
row = await _read_proof(turns, claims)
admission = admit_root(attestor, row, _fields(claims, turn_digest(raw)))
return admission, turns.decode(claims, raw)
|
resuming_memory
resuming_memory(agent: Any) -> bool
Whether the resuming agent's authority reaches memory, or cannot be read.
Through the agent's declared seams: authorized_effects (the grants its
invocations run under) and composition_manifest (each folded stage's
declared effects). Anything missing or unreadable counts as memory.
Source code in src/symfonic/capabilities/human/continuation_proof.py
| def resuming_memory(agent: Any) -> bool:
"""Whether the resuming agent's authority reaches memory, or cannot be read.
Through the agent's declared seams: ``authorized_effects`` (the grants its
invocations run under) and ``composition_manifest`` (each folded stage's
declared effects). Anything missing or unreadable counts as memory.
"""
from symfonic.kernel.contracts.run_admission import memory_in
try:
effects = frozenset(agent.authorized_effects)
stages = agent.composition_manifest["stages"]
declared = {str(effect) for row in stages for effect in row[4]}
except Exception: # noqa: BLE001 - unreadable authority is not memory-free
return True
return memory_in(effects | declared, ())
|