symfonic.kernel.contracts.snapshot¶
snapshot ¶
Freezing what a caller says about a turn, so it stays what they said.
A governance rule that asks "is this account in the EU?" needs an answer, and
the answer is a fact about the deployment rather than about the prompt. Until
now there was nowhere to put it: GovernanceSubject.properties existed and
was empty at every rung, so the one example that needed it supplied the fact
out of band, through an object the rule reached for directly.
Three properties make that safe to fix, and all three are about not being a back channel.
Frozen, not merely typed as a Mapping. Several rungs read the same
snapshot in one turn. A stage that mutated it would be editing what a later
rung sees, which is a capability the ladder deliberately does not grant: a
stage says what it decided by returning a StageResult, not by writing into
the turn behind the next stage's back. Freezing is recursive, because a
shallow proxy over a dict whose values are dicts is not frozen at all.
Copied at the boundary. The caller's own object is not retained. A caller that mutates the dict it passed after the turn starts changes nothing, so the snapshot cannot disagree with itself halfway through a turn.
Invisible in output. These are the deployment's facts -- an account
region, an entitlement, a verification flag -- and they are not the kernel's
to print. The field carrying them is excluded from its dataclass repr so a
formatted request, a traceback frame and an error message that interpolates
the turn all stay clean by construction rather than by everyone remembering.
freeze_properties ¶
A recursively immutable copy of what the caller declared.
None and an empty mapping both give :data:EMPTY_PROPERTIES, so a
turn that declared nothing carries nothing rather than an empty container
of its own.
Nested mappings become proxies and nested sequences become tuples. A value that is neither -- a string, a number, a date, an adopter's own object -- is carried as it is: this function freezes the structure the caller handed over, and cannot make an arbitrary object immutable. An adopter who puts a mutable object in a property has kept a handle on it, which is theirs to reason about; what this guarantees is that the shape a rule reads cannot be rewritten under it by another rule.