symfonic.agent.cutover.routes¶
routes ¶
The per-capability cutover switches the legacy engine routes through.
A switch is a pair: the migrated implementation and the legacy path that
stays dormant behind it. Recording the fallback by name is what makes the
rollback story checkable — a switch whose legacy_fallback no longer
resolves is a deletion that happened before T4.4.6 retired it, and that is a
thing a test can notice.
Nothing here reads an environment variable or a file. The route is a function
of filed cutover criteria (criteria.py) plus explicit operator action, so
"which implementation served this turn?" has one answer per process and it is
derivable from evidence rather than from deployment trivia.
CapabilitySwitch
dataclass
¶
CapabilitySwitch(capability: str, migrated_by: str, verified_by: str, legacy_fallback: str, retired_by: str = 'T4.4.6')
One capability's migration, its verification, and its way back.
Reserved for names dispatch actually consults. Four today —
:data:INVOCATION_RUN, :data:INVOCATION_STREAM,
:data:INVOCATION_STREAM_TYPED and :data:INVOCATION_CONTINUATION — and
a name earns one by owning an atomic segment of a turn, not by having an
implementation.
CoverageRegister
dataclass
¶
CoverageRegister(capability: str, migrated_by: str, verified_by: str, legacy_fallback: str, retired_by: str = 'T4.4.6')
A capability that was built — and that routes nothing.
Nine of the eleven names this module used to call switches were registers:
route_for("memory") answered kernel or legacy and no dispatch
read it, so flipping one changed which turn? None. They were counted as
migration progress all the same, and "nine of eleven flipped" measured how
much code existed rather than how much of it ran.
The distinction that decides which type a name gets: a switch owns an atomic segment of a turn, so flipping it changes what serves that turn. A register records that a capability was migrated and verified, which is real evidence and a different claim.
Memory and prompting are the sharpest illustration. Both are composed and dispatched since #14 and #21 — through the envelope, on evidence a bundle carries. Their registers still route nothing, and calling them switches would say the flip did work the envelope actually did.
Route ¶
Bases: StrEnum
Which implementation of a capability serves an invocation.
register_for ¶
The coverage register named capability, or a ConfigurationError.
Registers answer "was this built and verified?" -- never "what serves this turn?". Asking a switch for evidence is the mirror mistake and fails the same way.
Source code in src/symfonic/agent/cutover/registers.py
switch_for ¶
The switch named capability, or a ConfigurationError.
Unknown names fail loudly rather than defaulting: a mistyped capability that silently answered "legacy" would be a cutover nobody notices did not happen.