@@ -90,7 +90,18 @@ class RunContext: |
| 90 | 90 | to calibrate per-kind null stats for. |
| 91 | 91 | """ |
| 92 | 92 | |
| 93 | | - backend: DifferentialBackend |
| 93 | + backend: DifferentialBackend | None = None |
| 94 | + """The model-scoring backend. Required for every probe with |
| 95 | + ``needs_backend=True`` (the default). Pre-run probes |
| 96 | + (``needs_backend=False``, e.g. S25 ``gradient_ghost``) tolerate |
| 97 | + ``None`` here so the runner can skip backend construction |
| 98 | + entirely when only pre-flight probes are scheduled. |
| 99 | + |
| 100 | + Existing probes access ``self.require_backend`` instead of |
| 101 | + ``backend`` directly — the property narrows the type for mypy |
| 102 | + and gives a clear runtime error if the runner ever passes |
| 103 | + ``None`` to a probe that needs the backend. |
| 104 | + """ |
| 94 | 105 | seed: int = 0 |
| 95 | 106 | top_k: int = 256 |
| 96 | 107 | sections: tuple[Section, ...] | None = None |
@@ -101,6 +112,26 @@ class RunContext: |
| 101 | 112 | ) |
| 102 | 113 | downstream_kinds: tuple[str, ...] = field(default_factory=tuple) |
| 103 | 114 | |
| 115 | + @property |
| 116 | + def require_backend(self) -> DifferentialBackend: |
| 117 | + """Return :attr:`backend`, asserting non-None. |
| 118 | + |
| 119 | + Probes with ``needs_backend=True`` (default) call this to |
| 120 | + narrow the type from ``DifferentialBackend | None`` to |
| 121 | + ``DifferentialBackend``. The runner contract guarantees |
| 122 | + non-None when scheduling backend-dependent probes; this |
| 123 | + accessor turns a runner bug into a clear error rather than |
| 124 | + a confusing AttributeError on ``None.as_base()``. |
| 125 | + """ |
| 126 | + if self.backend is None: |
| 127 | + raise RuntimeError( |
| 128 | + "RunContext.backend is None — probe requires a backend " |
| 129 | + "(needs_backend=True) but the runner did not provide one. " |
| 130 | + "If this is a pre-run probe, set needs_backend=False on " |
| 131 | + "the Probe subclass." |
| 132 | + ) |
| 133 | + return self.backend |
| 134 | + |
| 104 | 135 | |
| 105 | 136 | _REGISTRY: dict[str, type[Probe]] = {} |
| 106 | 137 | |