Skip to content

symfonic.agent.cutover.registers

registers

The capabilities the refactor built — and that route nothing (#15).

Split out of :mod:~symfonic.agent.cutover.routes for the line budget, and the split follows the distinction rather than cutting across it: that module now holds only what dispatch consults, and this one holds only evidence. Both ledgers are still re-exported from routes at the addresses importers already hold, so nothing moved for a caller.

Reading them apart is the whole point of the rename #15 performed. 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. "Nine of eleven flipped" was published as migration progress and measured how much code existed.

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.

register_for

register_for(capability: str) -> CoverageRegister

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
def register_for(capability: str) -> CoverageRegister:
    """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.
    """
    # Imported inside the function, because ``routes`` imports this module:
    # the mirror-mistake message is the one place a register has to know the
    # switch ledger, and a module-level import would make that one message a
    # circular dependency between the two halves of one split.
    from symfonic.agent.cutover.routes import CAPABILITY_SWITCHES
    from symfonic.core.contracts.errors import ConfigurationError

    try:
        return COVERAGE_REGISTERS[capability]
    except KeyError:
        pass
    if capability in CAPABILITY_SWITCHES:
        raise ConfigurationError(
            f"{capability!r} is a switch, not a coverage register: dispatch "
            "reads it every turn. Ask switch_for() for it."
        )
    raise ConfigurationError(
        f"unknown capability {capability!r}; known registers are "
        f"{sorted(COVERAGE_REGISTERS)}."
    )