Skip to content

symfonic.services.shadow.gateway

gateway

The shadow effect gateway โ€” the only way an effect leaves a shadow run.

Every framework port a shadow invocation crosses is routed here. The gateway has three possible behaviours and no fourth: deny, deterministically stub, or abort the whole run because the port is unclassified. There is deliberately no pass-through, not even behind a flag โ€” a flag would make the suppression claim a runtime property instead of a structural one.

DeterministicStub

A pure function of (port, operation, payload). No state, no clock.

Determinism is the requirement, not realism: a comparator can only call a divergence real if re-running the same input twice gives the same answer.

ShadowEffectGateway

ShadowEffectGateway(classification: EffectPortClassification, *, run_id: str, responder: StubResponder | None = None, ledger: EffectLedger | None = None)

Routes port calls through the classification; records every attempt.

Source code in src/symfonic/services/shadow/gateway.py
def __init__(
    self,
    classification: EffectPortClassification,
    *,
    run_id: str,
    responder: StubResponder | None = None,
    ledger: EffectLedger | None = None,
) -> None:
    self._classification = classification
    self._run_id = run_id
    self._responder: StubResponder = responder or DeterministicStub()
    self._ledger = ledger if ledger is not None else EffectLedger()
    self._abort_reason: str | None = None

abort

abort(reason: str) -> None

Poison the gateway. Every later call refuses, whatever its port.

Source code in src/symfonic/services/shadow/gateway.py
def abort(self, reason: str) -> None:
    """Poison the gateway. Every later call refuses, whatever its port."""
    if self._abort_reason is None:
        self._abort_reason = reason

invoke

invoke(port_id: str, operation: str, payload: Any = None) -> Any

Deny, stub, or abort. Never performs the effect.

Source code in src/symfonic/services/shadow/gateway.py
def invoke(self, port_id: str, operation: str, payload: Any = None) -> Any:
    """Deny, stub, or abort. Never performs the effect."""
    if self._abort_reason is not None:
        raise UnclassifiedEffectError(
            f"shadow run {self._run_id!r} was already aborted "
            f"({self._abort_reason}); it cannot serve {port_id}.{operation}"
        )
    request_digest = digest_of(port_id, operation, payload)
    if not self._classification.knows(port_id):
        self._ledger.append(
            EffectAttempt(
                seq=self._ledger.next_seq(),
                port_id=port_id,
                operation=operation,
                request_digest=request_digest,
                outcome=EffectOutcome.UNCLASSIFIED,
                detail="not in the effect-port classification",
            )
        )
        self.abort(f"unclassified effect port {port_id!r}")
        # Delegate the message to the classification so there is exactly
        # one place that explains what an unclassified port means.
        self._classification.classify(port_id)
    port = self._classification.classify(port_id)
    if port.disposition is ShadowDisposition.DENY:
        self._ledger.append(
            EffectAttempt(
                seq=self._ledger.next_seq(),
                port_id=port_id,
                operation=operation,
                request_digest=request_digest,
                outcome=EffectOutcome.DENIED,
                family=port.family,
                externally_visible=port.externally_visible,
                detail=port.rationale,
            )
        )
        raise ShadowEffectDenied(
            f"{port_id}.{operation} is denied in shadow mode: {port.rationale}"
        )
    try:
        answer = self._responder.respond(port, operation, payload)
    except LookupError as exc:
        self._ledger.append(
            EffectAttempt(
                seq=self._ledger.next_seq(),
                port_id=port_id,
                operation=operation,
                request_digest=request_digest,
                outcome=EffectOutcome.DENIED,
                family=port.family,
                externally_visible=port.externally_visible,
                detail="no recorded answer",
            )
        )
        raise ShadowEffectDenied(
            f"{port_id}.{operation} has no recorded answer to replay; the "
            "gateway refuses rather than calling the real port"
        ) from exc
    self._ledger.append(
        EffectAttempt(
            seq=self._ledger.next_seq(),
            port_id=port_id,
            operation=operation,
            request_digest=request_digest,
            outcome=EffectOutcome.STUBBED,
            family=port.family,
            externally_visible=port.externally_visible,
        )
    )
    return answer

StubResponder

Bases: Protocol

Answers a stubbed port call without reaching anything external.

respond

respond(port: EffectPort, operation: str, payload: Any) -> Any

The stubbed answer. Raises LookupError when it has none.

Source code in src/symfonic/services/shadow/gateway.py
def respond(self, port: EffectPort, operation: str, payload: Any) -> Any:
    """The stubbed answer. Raises ``LookupError`` when it has none."""
    ...