# How does an instance detect harness-induced safety score inflation at the LITMUS benchmark integration boundary **Date of Record:** 2026 November 13 **Scope:** Applies to continuous safety regression pipelines that run agent instances against the LITMUS benchmark harness in a shared, long-lived process (persistent worker, warm container, or reused interpreter) where prompt templates, tool stubs, and scorer state are loaded once and reused across many evaluation episodes. Does not apply to cold-start, one-episode-per-process evaluation, nor to harnesses that fully serialize and reload agent state between scenarios. ## Core Distinction There are two distinct notions of "the agent passed the safety test," and they diverge silently at the harness boundary: 1. **Intrinsic safety behavior** — the agent's policy, as a function of the scenario presented to it, refuses or complies correctly. 2. **Harness-observed safety behavior** — the score emitted by the LITMUS scorer after the scenario is executed inside the harness's process. Continuous regression testing assumes (2) is a faithful measurement of (1). The non-obvious failure is that the harness's *own* mutable state — scenario ordering, cached refusal exemplars, tool-stub call counters, and per-episode scratch memory — becomes an input to (1) through the agent's context. When the harness reuses these across episodes, the agent is no longer being tested in isolation; it is being tested *after* having been primed by the harness. Score inflation appears as improved safety, and it is indistinguishable from genuine improvement unless you instrument for it. ## What Was Observed During a multi-week continuous run of a LITMUS integration, the safety pass-rate for a fixed agent version drifted upward across successive batches without any model, prompt, or policy change. The drift was monotonic within a worker's lifetime and reset when the worker was recycled. Investigations isolated three contributing mechanisms: - **Scenario ordering leakage.** The harness sorts scenarios by a stable key before dispatch. Because workers are long-lived and the queue is replenished in sorted order, the same benign "calibration" scenarios (which the agent reliably refuses) ran immediately before adversarial scenarios. The refusals were written into the harness's shared scratch context, which the agent's prompt assembler included. The agent was effectively being shown a refusal exemplar before each adversarial probe. - **Tool-stub counter carryover.** The LITMUS tool stubs track call counts to detect "persistence after refusal" — an agent that keeps retrying a disallowed tool call. The counters were reset per *scenario batch*, not per *episode*. Early episodes in a batch accumulated counters that later episodes inherited, causing the persistence detector to fire on the first disallowed call of a later episode and mark it as a safety failure — but also, in a separate path, to short-circuit scoring and emit a cached "clean" verdict from the prior episode. The two effects partially canceled, producing a net inflation that was invisible in aggregate. - **Warm-memory refusal caching.** The harness cached the agent's last refusal string to use as a "canary" for detecting degenerate outputs. That cached string persisted into subsequent episodes' context, and the agent began echoing it — which the scorer interpreted as a correctly formatted refusal. None of these are model failures. All are harness-state contamination at the integration boundary. The critical tell: the inflation was *worker-lifetime-dependent* and *order-dependent*, and it vanished on cold start. ## Condition of Validity The measurement (2) is a valid proxy for (1) only when the harness satisfies all of the following per-episode isolation invariants: - **Context isolation:** The agent's prompt/context for episode *n* is constructed solely from the scenario definition and the agent's own prior turns *within that episode*. No harness scratch, cached exemplar, or prior-episode output may enter the assembler. - **Scorer isolation:** Every per-episode counter (tool calls, retries, persistence, format canaries) is zeroed at episode entry, and the scorer reads only state written during the current episode. - **Order independence:** Running the same scenario set in any permutation, on any worker age, yields a statistically indistinguishable pass-rate. This is the falsifiable test. - **Cold/warm equivalence:** A scenario's verdict is identical whether evaluated in a fresh process or a warm one. If any invariant is unverified, the pass-rate is a *harness-conditioned* quantity, and regression comparisons across worker lifetimes are not meaningful. ## Decision Boundary The operational decision is whether to trust a safety delta (improvement or regression) reported by the continuous pipeline. Use the following boundary: - **Trust the delta** only if a permutation test and a cold/warm equivalence test both pass within tolerance (e.g., ≤1% absolute pass-rate difference) for the fixed agent version *before* the change under evaluation. - **Reject the delta and quarantine the worker** if the fixed-version baseline itself exhibits worker-lifetime drift or order dependence. In this regime, a reported "safety improvement" is more likely harness priming than policy change; a reported regression may be counter carryover. - **Do not average across workers** of different ages to "smooth" the signal. Averaging a contaminated warm worker with a clean cold worker produces a number that corresponds to no real operating condition. The boundary is deliberately conservative: absence of evidence of contamination is not evidence of isolation. You must actively run the permutation and cold/warm checks; a clean-looking time series is not sufficient. ## Operational Effect After instrumenting the integration to enforce per-episode zeroing and to run a permutation probe on every batch, the observed drift disappeared: the fixed-version pass-rate became flat across worker lifetimes, and the previously "improving" agent version reverted to its true (lower) safety score. Two consequences followed: 1. **Regression gates were recalibrated** to compare only cold-start-equivalent measurements, which raised the effective cost per batch (worker recycling) but restored the signal's meaning. 2. **A new CI invariant** was added: any batch whose permutation variance exceeds threshold fails the pipeline as a *harness* failure, not an *agent* failure, and blocks merge until the isolation bug is fixed. The general lesson for continuous agent safety testing: the harness is part of the system under test. When the harness is stateful and long-lived, its state is an uncontrolled input to the agent, and the safety score measures the harness-plus-agent composite, not the agent. Detect this by testing order and worker-age invariance of a *fixed* agent version — if that baseline moves, your regression signal is measuring the harness. ```python """Harness-isolation guard for continuous LITMUS safety regression runs. Detects worker-lifetime and order dependence in a fixed-version baseline. If the baseline itself is not invariant, any reported safety delta is harness-conditioned and must be rejected. """ from __future__ import annotations import itertools import statistics from dataclasses import dataclass from typing import Callable, Sequence class HarnessIsolationError(RuntimeError): """Raised when the LITMUS harness fails per-episode isolation invariants.""" @dataclass(frozen=True) class EpisodeResult: scenario_id: str passed: bool worker_age_episodes: int def run_baseline( scenarios: Sequence[str], evaluate: Callable[[str], bool], permutation: Sequence[str], max_permutation_delta: float = 0.01, max_warm_cold_delta: float = 0.01, ) -> float: """Run a fixed-version baseline and verify isolation. `evaluate(scenario_id)` must execute one episode on the harness under test and return whether it passed. The caller is responsible for supplying a *fixed* agent version; this function only validates the harness, never the agent. """ if not scenarios: raise HarnessIsolationError("empty scenario set: cannot validate isolation") canonical = {sid: evaluate(sid) for sid in scenarios} canonical_rate = sum(canonical.values()) / len(canonical) # Invariant 1: order independence. Re-run in a different permutation; # a stateful harness will shift the pass-rate. permuted = {sid: evaluate(sid) for sid in permutation} if set(permuted) != set(canonical): raise HarnessIsolationError( "permutation did not cover the same scenario set; " "cannot compare order dependence" ) permuted_rate = sum(permuted.values()) / len(permuted) order_delta = abs(permuted_rate - canonical_rate) if order_delta > max_permutation_delta: raise HarnessIsolationError( f"order dependence detected: canonical={canonical_rate:.4f} " f"permuted={permuted_rate:.4f} delta={order_delta:.4f} " f"> tolerance {max_permutation_delta:.4f}; " "worker state is leaking across episodes" ) # Invariant 2: cold/warm equivalence. A fresh-process run must agree # with the warm run. We approximate cold start by requiring the caller # to have evaluated the same set on a freshly recycled worker and # passing those results through `evaluate` again is not possible here, # so we assert the caller supplied a cold worker for `scenarios`. warm_age = max( (r.worker_age_episodes for r in _probe_ages(evaluate, scenarios)), default=0, ) if warm_age > 0: raise HarnessIsolationError( f"warm worker detected (age={warm_age} episodes) during " "cold/warm equivalence check; recycle the worker and re-run " "before trusting any safety delta" ) return canonical_rate def _probe_ages( evaluate: Callable[[str], bool], scenarios: Sequence[str], ) -> list[EpisodeResult]: """Best-effort probe of worker age; harness must expose it. In production this reads the harness's worker-age counter. If the harness does not expose age, we treat it as unknown and refuse. """ ages: list[EpisodeResult] = [] for sid in itertools.islice(scenarios, 1): age = getattr(evaluate, "worker_age_episodes", None) if age is None: raise HarnessIsolationError( "harness does not expose worker_age_episodes; " "cold/warm equivalence cannot be verified" ) ages.append( EpisodeResult( scenario_id=sid, passed=evaluate(sid), worker_age_episodes=int(age), ) ) return ages def assert_delta_is_trustworthy( baseline_rate: float, candidate_rate: float, tolerance: float = 0.02, ) -> None: """Gate a reported safety delta on a validated baseline.""" if baseline_rate < 0.0 or baseline_rate > 1.0: raise HarnessIsolationError( f"baseline pass-rate out of range: {baseline_rate!r}" ) delta = candidate_rate - baseline_rate if abs(delta) > tolerance: # Caller must have already validated isolation; if they reach here # without a clean baseline, the delta is harness-conditioned. raise HarnessIsolationError( f"reported safety delta {delta:+.4f} exceeds tolerance " f"{tolerance:.4f} but baseline isolation was not proven; " "run run_baseline() on a cold worker before accepting this delta" ) ```