Problems, Tasks and Environments
A failed run does not identify a failed model: it identifies a mismatch somewhere among the configured system, the task we meant, the world it acted in and the instrument that judged it.
TLDR
- A failed run is evidence about a configured loop, not automatically a defective model. The same red result can arise from the system, task definition, environment or evaluator.
- Those causes become distinguishable only through controlled interventions: hold three cuts stable, change one, replay the trace, and compare against independently stated intent and evidence.
- Before retraining, record action and observation semantics, terminal conditions, state distribution, success constraints, sampling frame and scoring logic as versioned specifications.
- The four cuts are diagnostic boundaries, not natural kinds. Coupled faults and contested intent can leave attribution set-valued, so uncertainty must remain in the incident record.
- Replace model-first debugging with a four-cut attribution test. Retrain only when the failure follows the system after task, environment and evaluator checks survive.
The courier that failed without changing
A small delivery robot leaves cell (0,0), must reach (2,1), and must never cross a restricted cell at (1,0). On Monday, its planner emits S, E, E. The robot reaches the destination without entering the restricted cell. On Tuesday, the same model, prompt, planner code and random seed emit the same commands, yet the robot ends at (1,2). The dashboard marks the model as failed.
The model did not change. A runtime update had exchanged the meanings of E and S. The published action contract still said that E increased the horizontal coordinate. The live environment interpreted it as a vertical move. Retraining the planner on Tuesday's traces might make the score recover, but it would train the system around a broken interface.
Now alter one more fact. Suppose the robot reaches the destination through the restricted cell, and the evaluator checks only the final position. The dashboard marks the run as a pass. The operational outcome is still wrong, but the error now belongs to measurement rather than motion. In another version, the task supplied to the planner simply omitted the restriction. The same trajectory then exposes a task-definition fault.
One red or green lamp cannot reveal which part of the loop deserves credit or blame. The first useful question is not “which model failed?” It is “which cut changed, and what intervention would make the result change while the other cuts remain fixed?”
A failure belongs to a loop
The phrase “the AI failed” compresses several causal claims into one sentence. It may mean that a learned policy selected a poor action. It may mean that the goal was translated into the wrong operational objective. It may mean that the world presented inputs outside the stated operating conditions, or that an interface reported stale state. It may mean that the scoring procedure looked at the wrong evidence. These are different engineering problems. They require different repairs, owners and release decisions.
The four cuts used here are deliberately diagnostic:
- The system is the implemented decision mechanism: model weights, prompt, rules, planner, tools, memory, wrappers, compute limits and stochastic settings.
- The task is the normative contract: what outcome counts, which constraints apply, what actions are allowed, and when the attempt ends.
- The environment is the causal process with which the system interacts: initial states, dynamics, observations, action semantics, exogenous events and interface behaviour.
- The evaluator is the measurement process: sampled cases, labels or oracle, instrumentation, aggregation, thresholds and the moment at which evidence is observed.
A problem sits further upstream. “Reduce late deliveries” is a concern. It becomes a task only after someone chooses a target, constraints, population, horizon and acceptable trade-offs. A task might be “deliver 99 per cent of eligible parcels by 18:00 without entering restricted zones or exceeding a battery reserve”. That task can be instantiated in many environments: different maps, weather, traffic rules, sensors and command protocols. Several systems can attempt it. Several evaluators can judge the traces.
A small formalism makes the ambiguity visible
Let S denote the configured system, T the operational task, E the environment and ξ uncontrolled variation such as a seed or exogenous event. A run produces a trajectory τ. The evaluator V converts that trajectory into a verdict y:
Here τ contains the observations, proposals, actions, state transitions and effects that occurred. V may inspect all of that evidence or only a thin projection, such as final answer accuracy. A failure indicator might be F = 1 when y falls below a threshold or when any hard constraint is violated.
The causal problem is simple: many different tuples (S,T,E,V) can produce the same value of F. One failed run is therefore compatible with several explanations. More data from the identical configuration estimates how often the configuration fails, but it does not by itself localise the cause. Frequency is not attribution. Localisation requires a contrast in which one cut changes and the remaining cuts are held fixed or independently checked.
Modern machine-learning evaluations repeatedly show this non-identifiability in different forms. D’Amour and colleagues found that models with indistinguishable held-out performance can behave differently under deployment-relevant stress tests, a problem they call underspecification [4]. Jayawardana and colleagues showed that evaluating reinforcement-learning methods on a few point MDPs can produce a different method ranking from evaluation across a parameterised MDP family [5]. These results do not prove that every failure is external to the model. They show that a nominal score can leave important causes unresolved.
This paper’s contribution is a four-cut attribution method. It treats the cuts as intervention sites, not as claims that the world naturally arrives in four boxes. The method asks what must be specified, replayed and varied before a team spends money changing the learner.
Why the task-environment line moves
Reinforcement-learning formalisms often package transition dynamics, observations, rewards and terminal states inside an MDP. That is mathematically coherent and useful for algorithms [1]. The diagnostic cut in this article serves a different purpose. It separates what people require from what the world causes, because those two changes normally have different owners and repairs.
Three counterfactual questions locate the boundary. First, would the quantity change because a policy owner changed what ought to count? A delivery deadline tightened from 18:00 to 16:00 is a task change. Second, would it change because the physical or digital world behaved differently? A road closure or a service returning stale state is an environment change. Third, would only the recorded verdict change while the trajectory remained fixed? That points to the evaluator.
Some variables can move between cuts. A time limit may express a real business deadline, making it part of the task, or an experimental compute budget, making it external truncation. A human reviewer may be part of the environment when their response changes the next state, yet part of the evaluator when they judge a frozen trace. The boundary need not be universal, but it must be explicit and stable for the incident being diagnosed. Otherwise a team can “repair” a failure by silently redefining the task or moving an inconvenient case outside the environment envelope.
Four cuts through one result
The cuts are easiest to understand as four different kinds of question. The system cut asks, “Given the stated task and interface, did the implementation select an unsuitable action?” The task cut asks, “Did we operationalise the intended outcome and constraints correctly?” The environment cut asks, “Did the world and interface behave within the declared conditions?” The evaluator cut asks, “Did the evidence and scoring rule support the verdict we recorded?”
The system cut: did the implementation choose badly?
A system fault follows the configured decision mechanism. In the courier example, a constraint-blind planner chooses the shortest path through the restricted cell even when the task and action contract state the restriction correctly. Replacing the planner while keeping task, environment and evaluator fixed repairs the trajectory. That intervention is evidence for a system attribution.
The system boundary must include more than model weights. A prompt can drop a condition. A tool wrapper can coerce a decimal into an integer. A context compiler can omit the freshest document. A retry policy can duplicate an effect. A small model can be sound while the configured compound system fails. The diagnostic question is whether the fault follows the executable configuration.
The task cut: did we ask for the wrong thing?
A task is an executable interpretation of intent. It chooses the target, constraints, population, time horizon and permitted actions. Those choices are partly normative. No amount of model accuracy can recover a constraint that was never represented. If the intended rule says “reach the destination without entering restricted cells” but the operational task says only “reach the destination”, a direct path through the restriction may be perfectly optimal for the stated task.
Reward misspecification research makes this distinction concrete. AI Safety Gridworlds separates the reward visible to the agent from a hidden performance function used by the experimenter, allowing an agent to score well while violating the intended objective [6]. Inverse reward design treats a designed reward as evidence about a designer’s intent rather than a literal and complete objective [7]. Pan and colleagues further show that the effects of reward misspecification vary with model and environment conditions [8]. These are bounded research environments, but the lesson travels: the objective encoded for optimisation is not identical to the purpose that justified the system.
The environment cut: did the world honour the contract?
An environment is not just “the data”. It includes the transition process, observation semantics, action semantics, reset conditions, clocks, external actors and interface failures. The environment can change while the system remains byte-for-byte identical. A new traffic pattern can alter a controller’s state distribution. A document service can begin returning stale replicas. A simulator can map an action to the wrong actuator. A timeout can end an episode without the task reaching a terminal state.
This last distinction is operationally important. Gymnasium’s environment API returns separate terminated and truncated signals: termination belongs to the task’s terminal condition, whereas truncation can arise from an external limit such as a time cap [2]. Conflating the two changes bootstrapping logic and can therefore change learning results [3]. A single Boolean called done can hide whether the task ended or the experimental apparatus stopped observing it.
The evaluator cut: did the instrument deserve its verdict?
An evaluator selects evidence, encodes an oracle, chooses a denominator and aggregates cases. It can miss a real failure or manufacture one. A final-state scorer cannot detect a forbidden intermediate action. A stale answer key can reject a correct response. A benchmark that samples only easy environments can reverse the apparent ranking of methods. An aggregate score can hide a critical slice whose denominator is small but consequence is high.
Behavioural testing methods such as CheckList were motivated by the fact that aggregate test accuracy can conceal basic capability failures [12]. The Arcade Learning Environment’s revised evaluation protocols similarly show that sticky actions, episode handling and reporting conventions affect what an agent score means [13]. An evaluator is an instrument with failure modes, not a transparent window onto capability.
The evaluator contains a population and an oracle
Evaluation has two separable jobs. The sampling frame decides which environments and cases stand for the claim. The oracle decides what counts as success on each sampled case. A perfect oracle applied to an unrepresentative sample can still support the wrong release decision. A representative sample scored by a stale or shallow oracle can do the same.
Jayawardana and colleagues expose the first problem: a few chosen MDP instances can rank methods differently from the wider family [5]. Henderson and colleagues document sensitivity to implementation, seeds and reporting choices in deep RL [14]. These findings motivate two distinct evaluator interventions. Resampling changes the population while preserving the per-case judge. Rescoring changes the judge while preserving the cases and traces.
This separation prevents an easy mistake. When a method’s score falls after harder cases are added, the system may not have regressed at all; the claim’s denominator became more honest. When a trace-level constraint check replaces endpoint accuracy, the measured pass rate may fall because the instrument now sees more. A release gate should record whether a score change came from behaviour, population or oracle.
flowchart LR A["The courier that failed<br/>without changing"] --> B["A failure belongs to a loop"] B --> C["Four cuts through one result"] C --> D["Two separating experiments"] D -. "re-test" .-> A classDef first fill:#0a1424,stroke:#007D68,stroke-width:2px,color:#e6f3ff classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff class A first class B,C,D step
Two separating experiments
A useful thought experiment changes one causal feature at a time. The purpose is not to prove that a chosen cut is always independent. It is to make clear what evidence would distinguish one explanation from another.
Thought experiment one: the same courier, a different interface
Freeze the planner binary, task object, start state, random seed and evaluator. Run it in Environment A, where E=(+1,0) and S=(0,+1). Then run it in Environment B, where a versioning error swaps those commands. The system emits the same sequence in both environments. In A, it succeeds. In B, it stops in the wrong cell.
The variation is sufficient to make the failure follow the environment. It does not prove that the planner is ideal. It establishes a narrower fact: this observed difference does not require a change in model competence. A system repair that memorises the swapped semantics may restore the score, but the diagnostic repair is to restore or version the interface contract.
Thought experiment two: two judges, one trajectory
Freeze the system, task, environment and resulting trajectory. The robot crosses the restricted cell and reaches its destination. Evaluator A checks only the final state. Evaluator B checks the whole trace against the hard constraint. A returns pass; B returns fail. Nothing about the robot’s action changed. Only the evidence aperture changed.
This experiment separates evaluator quality from system behaviour. A stronger evaluator does not improve the trajectory; it improves what the organisation knows about the trajectory. That distinction matters in production because a better test can initially make a system look worse. The apparent regression is often a measurement correction.
The negative control: when the preferred explanation looks unnecessary
A critic might say that a capable system should infer the real intention, adapt to interface changes and compensate for weak evaluation. Construct the strongest case for that view. Give a frontier model the natural-language purpose, action documentation, examples of past interface migrations and permission to inspect its own trace. It may detect the mismatch and avoid the restricted state even when the formal task is incomplete.
That result would show that system capability can mask faults in other cuts. It would not make those cuts unnecessary. The same system might silently infer a different intention on another case, or use an undocumented workaround that the evaluator cannot see. More importantly, a compensating model does not repair the shared contract for other systems. Robust intelligence can reduce observed failures while increasing ambiguity about why the loop worked. The negative control therefore tests whether the four-cut method adds diagnostic value beyond raw task success.
Intervene before retraining
Attribution becomes defensible when a team can produce an intervention fingerprint. The idea is familiar from fault isolation: rerun the same case after one controlled repair. Replace only the system. Repair only the task. Restore only the environment contract. Rescore the unchanged trace with an independent evaluator. Record which changes flip the verdict.
This is stronger than asking people to vote on the root cause. It is also more modest than claiming a unique cause from one success. A one-cut repair can show that a change is sufficient for the case. It does not always show that the changed component was the sole cause. Two faults may compensate for each other, and several repairs may work.
For stochastic language-model agents, a one-cut replay must become distributional. Causal Agent Replay resamples a selected step under the same policy, runs the remainder repeatedly and measures the change in outcome with confidence intervals. Its point-of-commitment rule matters because an early intervention also re-rolls every stochastic step downstream: a large early effect need not locate the decision that committed the failure. This sharpens the protocol here. Compare outcome distributions, not one lucky counterfactual trace. The method also marks a hard boundary: noisy model judges and live tools with irreversible side effects can make faithful replay impossible.
- Freeze the evidence. Preserve system version, task version, environment version, evaluator version, seed, inputs, observations, actions, timestamps and effects. Do not debug from a prose recollection.
- Restate intent independently. Ask the accountable owner to write the required outcome and hard constraints without copying the implemented reward or existing test. This exposes circular validation.
- Replay against the declared environment. Check observation units, action semantics, reset state, time, exogenous events, termination and truncation. Compare the live interface with the versioned contract.
- Rescore the frozen trace. Use a second oracle, direct postcondition readback or trace-level property tests. Keep the system and environment untouched.
- Run one-cut repairs. Change one cut at a time. Where replay is impossible, simulate the smallest counterfactual that preserves the other evidence.
- Record the attribution set. Mark causes as supported, contradicted or unresolved. Assign a repair owner and a release consequence to each supported cut.
The four-cut attribution laboratory
The accompanying Python program implements the courier as a deterministic 3 × 3 environment. It uses standard-library dataclasses for the task, environment, trace and verdict. The baseline passes. Four single faults are then injected, one per cut. Each matching one-cut repair restores the pass. A negative control sends a constraint-violating trace to a weak endpoint evaluator, which falsely approves it.
The artefact assumes that one fault is injected at a time and that the intended task is available to the strict evaluator. Those assumptions make the causal logic visible. They are not guarantees about live incidents. The expected output is:
scenario observed one-cut repair repaired observed path
------------------------------------------------------------------------------------------------
baseline PASS none PASS 0,0 -> 0,1 -> 1,1 -> 2,1
system_fault FAIL replace system PASS 0,0 -> 1,0 -> 2,0 -> 2,1
task_fault FAIL repair task PASS 0,0 -> 1,0 -> 2,0 -> 2,1
environment_fault FAIL restore environment PASS 0,0 -> 1,0 -> 1,1 -> 1,2
evaluator_fault FAIL replace evaluator PASS 0,0 -> 0,1 -> 1,1 -> 2,1
negative control
weak evaluator: PASS
strict evaluator: FAIL ('forbidden_state_visited',)
When the diagonal bends: interaction tests
The laboratory injects one fault at a time and verifies only the matching repair, so the displayed diagonal is evidence of sufficiency for those four cases, not evidence that cross-repairs fail. Live failures often arise from interactions. A brittle planner may succeed under the declared action map and fail only after an interface change. An incomplete task may appear harmless while a rich evaluator infers the missing constraint, then become visible when that evaluator is replaced. Either component can be repaired, yet the vulnerability belongs to the pair.
A small factorial experiment detects this. Choose two versions of the system, S₀ and S₁, and two versions of the environment, E₀ and E₁. Run all four combinations under the same task and evaluator. Let q(S,E) be the pass rate or loss. The interaction is the difference of differences:
If ISE is near zero within uncertainty, the changes act approximately additively. A large value means the effect of one cut depends on the other. The next decision is then joint: repair the interface contract and test the planner across the supported environment family. Do not force an interaction into a single-owner root cause simply because the incident system requires one dropdown value.
The same discipline applies to evaluator interactions. Hold the recorded trajectory fixed, score it with the original evaluator and a blinded replacement, then repeat after changing only the task statement. If the replacement reverses the verdict only for one wording, the failure cannot be assigned to either task or evaluator alone. Preserve all four cells, the scorer versions and disagreements rather than averaging them into one benchmark number.
Uncertainty belongs in this matrix. Repeat stochastic policies, use paired cases where possible and report the interval for each repair effect and interaction. A visually strongest cell is not a diagnosis when ordinary run-to-run variation spans the apparent gain. The permitted outcome is therefore sometimes “insufficient evidence”: freeze broad retraining, improve the experimental seam and rerun the discriminating intervention.
The strongest objection: everything can be called “the system”
A broad systems view can absorb prompts, task parsers, environment adapters and evaluators into one socio-technical system. That boundary is useful for accountability: someone must own the whole outcome. It is less useful for diagnosis. “The system failed” does not tell an engineer whether to retrain a policy, repair a requirement, roll back an interface or replace a label set.
The four cuts therefore answer an instrumental question. They create seams at which controlled variation, ownership and evidence differ. They do not deny interaction. A task compiler may itself be software. An evaluator may use a model. An environment adapter may be maintained by the same team as the agent. The relevant test is whether the seam enables a discriminating intervention and a different repair decision.
The explicit failure boundary
The method breaks when the cuts cannot be varied or checked independently enough to distinguish explanations. That happens when intent is genuinely contested, historical state cannot be reconstructed, the system adapts during replay, environment versions are unknown, evaluator labels were produced by the same model, or several components changed together. It also happens when a live human response becomes part of the environment and cannot ethically be replayed.
In those cases, attribution should remain set-valued. The incident record can say “supported: task or evaluator; unresolved between them” rather than forcing one root cause. Causal humility is operationally useful. It prevents a confident model patch from closing an incident whose measurement process is still untrusted.
From benchmark to operating contract
The four cuts matter most when a benchmark becomes a live workflow. A benchmark often freezes the dataset, action space and scoring script. Production reintroduces effective time, partial state, external services, authority, irreversible effects and people who can change the case while the system is reasoning. Without versioned boundaries, every incident becomes a story about the model.
Production-shaped worked scenario: the invoice exception
Consider a synthetic accounts-payable assistant. It reads an invoice, purchase order and goods-receipt record. It may propose pay, hold or review. A deterministic authority service decides whether the proposal can be executed. The intended outcome is to pay valid invoices promptly while preventing duplicates and payments without receipt evidence.
At 09:00, the source system records a receipt. At 09:04, a replica still reports “not received”. At 09:05, the assistant proposes hold. The evaluator uses the case’s final human disposition at 17:00 as the label and marks the proposal wrong because the invoice was eventually paid. An aggregate model report now contains a false negative.
Four explanations remain possible. The system may have ignored a fresh event already present in context. The task may have omitted the rule that stale receipt state should trigger review rather than hold. The environment may have breached a declared five-minute freshness guarantee. The evaluator may have compared a 09:05 proposal with a 17:00 label that incorporated later evidence. Each explanation implies a different control and owner.
The correct evaluation object is therefore not merely the final disposition. It is the time-indexed decision: “Given evidence legally and operationally available at 09:05, did the proposal follow the task and authority constraints?” The trace needs source versions, observation times, effective times, proposal, policy decision and eventual effect. World state, evidence history and remembered belief must remain separate.
Distribution-shift evidence reinforces this concern. Freshly collected ImageNet-like test data produced lower classifier accuracy than the original test set even after efforts to match the original collection process [11]. WILDS assembled naturally occurring shifts across domains precisely because an unchanged model can encounter a changed data-generating process [10]. Goal-misgeneralisation experiments show another boundary: an agent can receive the correct training reward and still pursue an unintended goal out of distribution [9]. Together, these results argue against treating every production miss as evidence that more in-domain optimisation is the first repair.
The environment specification
An environment specification makes the causal seam inspectable. It should be versioned beside the system and evaluator, not left in a design diagram. The specification need not model the entire world. It must state the parts of the world whose behaviour the task and system rely upon.
State and sampling
Initial-state population, inclusion and exclusion rules, state variables, units, missingness, freshness, effective time and representativeness assumptions.
Observation contract
What the system can observe, source authority, latency, confidence, provenance, entitlement and how absent or contradictory observations are encoded.
Action contract
Typed actions, preconditions, effects, rejection semantics, idempotency, timeout behaviour and whether an accepted command has a known or unknown outcome.
Dynamics and interruptions
Exogenous actors, ordering, concurrency, stochasticity, reset rules, clock semantics, termination, truncation, retry and recovery conditions.
Evidence surface
Trace fields, effect receipts, readback, postcondition checks, version identifiers and the minimum record needed to replay or bound the incident.
Validity envelope
Known unsupported states, shift monitors, assumptions that can expire, escalation triggers and the owner authorised to withdraw the environment version.
The four-cut attribution card
The following decision instrument is intended for an incident review, experiment plan or architecture decision record. A “yes” is not a final verdict. It identifies the next discriminating test. The card should be completed against a frozen trace and attached evidence, not from memory.
| Cut | Question | Minimum discriminating test | Evidence to preserve | Primary repair |
|---|---|---|---|---|
| System | Does the failure follow the executable configuration under the intended task and a conforming environment? | Replace or ablate the decision mechanism while replaying the same case. | Model, prompt, code, tools, context, limits, seed and full trace. | Repair, constrain, retrain, reroute or abstain. |
| Task | Does the operational objective omit, distort or conflict with independently stated intent? | Rewrite success and constraints independently, then rescore or replan without changing the world. | Intent owner, task version, reward, constraints, population and horizon. | Repair requirement, objective, decomposition or authority boundary. |
| Environment | Did live state, dynamics or interface semantics leave the declared validity envelope? | Replay in a conforming version or inject the suspected interface variation into a reference system. | State snapshots, schema, action map, timestamps, receipts, termination and truncation. | Restore contract, version adapter, monitor shift or narrow release. |
| Evaluator | Would an independent oracle judge the frozen trace differently? | Rescore the unchanged trace using a trace-level property test, readback or blinded review. | Sample frame, labels, observation time, denominator, slices, aggregation and threshold. | Repair oracle, sampling, instrumentation, metric or release gate. |
The card changes architecture as well as incident handling. It requires task versions, environment versions and evaluator versions to travel with the system release. It encourages direct effect readback rather than trusting an accepted command. It makes an unknown outcome distinct from a known failure. It also separates authority from capability: a model may propose an action, but task constraints and environment preconditions should be mechanically enforceable where consequence warrants it.
Model cards established a useful discipline of recording intended use, evaluation conditions and disaggregated performance [15]. For consequential workflows, extend that discipline and release the tuple rather than the model: system + task + environment specification + evaluator. A model checksum alone cannot reconstruct what the release was expected to do, where it was valid, or how its outcome was measured.
The proposed method predicts that teams using four-cut interventions will avoid some unnecessary retraining and will discover more interface, requirement and measurement faults. The thesis would weaken if, across realistic incidents, task, environment and evaluator variation rarely changed attribution, method ranking or repair choice after the system was held fixed.
stateDiagram-v2 [*] --> Claim state "A small formalism makes the<br/>ambiguity visible" as Claim state "Why the task-environment line<br/>moves" as Test state "The system cut: did the<br/>implementation choose badly?" as Revision Claim --> Test: expose mechanism Test --> Decision: evidence survives Test --> Revision: rival survives Revision --> Claim: narrow or rebuild Decision --> [*]
Compact glossary
- Problem
- An upstream concern or desired change before it has been converted into an executable contract.
- Task
- A normative specification of success, constraints, allowed actions, population and horizon.
- Environment
- The causal process and interface through which observations, actions, state transitions and exogenous events occur.
- Evaluator
- The sampling, oracle, instrumentation, aggregation and threshold that turn evidence into a verdict.
- Trajectory
- The time-ordered record of observations, proposals, actions, state transitions and effects for one run.
- One-cut repair
- A controlled intervention that changes one diagnostic cut while preserving or independently checking the other three.
- Attribution set
- The causes still compatible with the evidence when a unique diagnosis is not identifiable.
Executable artefact: full Python source
, then run it with Python 3.10 or later. It has no third-party dependencies. The assertions encode the positive cases and negative control.
#!/usr/bin/env python3
"""Four-cut failure attribution lab.
Synthetic grid-world example for the article "Problems, Tasks and Environments".
Uses only the Python standard library.
Expected result: each injected single fault is repaired only by changing the
corresponding cut (system, task, environment, or evaluator). A deliberately
weak evaluator also produces a false pass for a bad trajectory.
"""
from __future__ import annotations
from collections import deque
from dataclasses import dataclass
from typing import Callable, Iterable, Mapping, Sequence
Position = tuple[int, int]
Delta = tuple[int, int]
STANDARD_ACTIONS: dict[str, Delta] = {
"E": (1, 0),
"S": (0, 1),
"W": (-1, 0),
"N": (0, -1),
}
# The runtime interprets two commands differently from the published contract.
SWAPPED_ACTIONS: dict[str, Delta] = {
"E": (0, 1),
"S": (1, 0),
"W": (0, -1),
"N": (-1, 0),
}
@dataclass(frozen=True)
class TaskSpec:
name: str
start: Position
goal: Position
forbidden: frozenset[Position]
max_steps: int
@dataclass(frozen=True)
class EnvironmentSpec:
name: str
width: int
height: int
action_map: Mapping[str, Delta]
@dataclass(frozen=True)
class Trace:
commands: tuple[str, ...]
positions: tuple[Position, ...]
terminated: bool
truncated: bool
@dataclass(frozen=True)
class Verdict:
passed: bool
reasons: tuple[str, ...]
@dataclass(frozen=True)
class PlannerSystem:
name: str
expected_environment: EnvironmentSpec
honour_constraints: bool = True
def plan(self, task: TaskSpec) -> tuple[str, ...]:
"""Find a shortest path under the system's expected action semantics."""
blocked = task.forbidden if self.honour_constraints else frozenset()
frontier: deque[tuple[Position, tuple[str, ...]]] = deque([(task.start, ())])
seen = {task.start}
# Fixed ordering makes the negative case reproducible. With the task
# constraint omitted, the first shortest path crosses (1, 0).
action_order = ("E", "S", "W", "N")
while frontier:
position, path = frontier.popleft()
if position == task.goal:
return path
if len(path) >= task.max_steps:
continue
for command in action_order:
delta = self.expected_environment.action_map[command]
candidate = move(position, delta, self.expected_environment)
if candidate in blocked or candidate in seen:
continue
seen.add(candidate)
frontier.append((candidate, path + (command,)))
raise RuntimeError(f"{self.name} found no path for task {task.name!r}")
def move(position: Position, delta: Delta, env: EnvironmentSpec) -> Position:
x = min(max(position[0] + delta[0], 0), env.width - 1)
y = min(max(position[1] + delta[1], 0), env.height - 1)
return (x, y)
def run(system: PlannerSystem, operational_task: TaskSpec, runtime_env: EnvironmentSpec) -> Trace:
commands = system.plan(operational_task)
positions: list[Position] = [operational_task.start]
terminated = False
for command in commands[: operational_task.max_steps]:
delta = runtime_env.action_map[command]
positions.append(move(positions[-1], delta, runtime_env))
if positions[-1] == operational_task.goal:
terminated = True
break
truncated = not terminated and len(positions) - 1 >= operational_task.max_steps
return Trace(tuple(commands), tuple(positions), terminated, truncated)
def strict_evaluator(trace: Trace, intended_task: TaskSpec) -> Verdict:
reasons: list[str] = []
if trace.positions[-1] != intended_task.goal:
reasons.append("goal_not_reached")
if any(position in intended_task.forbidden for position in trace.positions[1:]):
reasons.append("forbidden_state_visited")
if len(trace.positions) - 1 > intended_task.max_steps:
reasons.append("horizon_exceeded")
return Verdict(not reasons, tuple(reasons))
def weak_final_state_evaluator(trace: Trace, intended_task: TaskSpec) -> Verdict:
reasons = () if trace.positions[-1] == intended_task.goal else ("goal_not_reached",)
return Verdict(not reasons, reasons)
def stale_goal_evaluator(trace: Trace, intended_task: TaskSpec) -> Verdict:
stale_task = TaskSpec(
name="stale-evaluator-task",
start=intended_task.start,
goal=(2, 2),
forbidden=intended_task.forbidden,
max_steps=intended_task.max_steps,
)
return strict_evaluator(trace, stale_task)
def one_cut_result(
*,
system: PlannerSystem,
operational_task: TaskSpec,
runtime_env: EnvironmentSpec,
evaluator: Callable[[Trace, TaskSpec], Verdict],
intended_task: TaskSpec,
) -> tuple[Trace, Verdict]:
trace = run(system, operational_task, runtime_env)
return trace, evaluator(trace, intended_task)
def render_path(trace: Trace) -> str:
return " -> ".join(f"{x},{y}" for x, y in trace.positions)
def main() -> None:
intended = TaskSpec(
name="deliver-without-crossing-restricted-cell",
start=(0, 0),
goal=(2, 1),
forbidden=frozenset({(1, 0)}),
max_steps=4,
)
flawed_task = TaskSpec(
name="deliver-to-goal-only",
start=intended.start,
goal=intended.goal,
forbidden=frozenset(),
max_steps=intended.max_steps,
)
declared_env = EnvironmentSpec("declared-v1", 3, 3, STANDARD_ACTIONS)
changed_runtime = EnvironmentSpec("runtime-v2-swapped", 3, 3, SWAPPED_ACTIONS)
reference_system = PlannerSystem("reference-planner", declared_env, honour_constraints=True)
faulty_system = PlannerSystem("constraint-blind-planner", declared_env, honour_constraints=False)
scenarios = []
# Positive case.
trace, verdict = one_cut_result(
system=reference_system,
operational_task=intended,
runtime_env=declared_env,
evaluator=strict_evaluator,
intended_task=intended,
)
scenarios.append(("baseline", verdict, "none", verdict, trace))
# System fault and system-only repair.
trace, observed = one_cut_result(
system=faulty_system,
operational_task=intended,
runtime_env=declared_env,
evaluator=strict_evaluator,
intended_task=intended,
)
_, repaired = one_cut_result(
system=reference_system,
operational_task=intended,
runtime_env=declared_env,
evaluator=strict_evaluator,
intended_task=intended,
)
scenarios.append(("system_fault", observed, "replace system", repaired, trace))
# Task fault and task-only repair. System code is unchanged; only the
# operational task is repaired to match the intended constraint.
trace, observed = one_cut_result(
system=reference_system,
operational_task=flawed_task,
runtime_env=declared_env,
evaluator=strict_evaluator,
intended_task=intended,
)
_, repaired = one_cut_result(
system=reference_system,
operational_task=intended,
runtime_env=declared_env,
evaluator=strict_evaluator,
intended_task=intended,
)
scenarios.append(("task_fault", observed, "repair task", repaired, trace))
# Environment fault and environment-only repair.
trace, observed = one_cut_result(
system=reference_system,
operational_task=intended,
runtime_env=changed_runtime,
evaluator=strict_evaluator,
intended_task=intended,
)
_, repaired = one_cut_result(
system=reference_system,
operational_task=intended,
runtime_env=declared_env,
evaluator=strict_evaluator,
intended_task=intended,
)
scenarios.append(("environment_fault", observed, "restore environment", repaired, trace))
# Evaluator fault and evaluator-only repair. The trajectory does not change.
trace, observed = one_cut_result(
system=reference_system,
operational_task=intended,
runtime_env=declared_env,
evaluator=stale_goal_evaluator,
intended_task=intended,
)
repaired = strict_evaluator(trace, intended)
scenarios.append(("evaluator_fault", observed, "replace evaluator", repaired, trace))
# Negative control: a bad trace receives a false pass from a weak evaluator.
bad_trace = run(faulty_system, intended, declared_env)
weak = weak_final_state_evaluator(bad_trace, intended)
strict = strict_evaluator(bad_trace, intended)
print("scenario observed one-cut repair repaired observed path")
print("-" * 96)
for name, observed, repair, repaired, trace in scenarios:
print(
f"{name:<19} "
f"{('PASS' if observed.passed else 'FAIL'):<9} "
f"{repair:<22} "
f"{('PASS' if repaired.passed else 'FAIL'):<9} "
f"{render_path(trace)}"
)
print("\nnegative control")
print(f"weak evaluator: {'PASS' if weak.passed else 'FAIL'}")
print(f"strict evaluator: {'PASS' if strict.passed else 'FAIL'} {strict.reasons}")
# Assertions make the expected output executable rather than illustrative.
expected = {
"baseline": (True, True),
"system_fault": (False, True),
"task_fault": (False, True),
"environment_fault": (False, True),
"evaluator_fault": (False, True),
}
for name, observed, _, repaired, _ in scenarios:
assert (observed.passed, repaired.passed) == expected[name]
assert weak.passed and not strict.passed
if __name__ == "__main__":
main()
Extended method notes: what a one-cut replay can and cannot prove
A successful one-cut repair establishes sufficiency for the replayed case under the preserved conditions. It does not establish necessity, generality across the operating distribution or sole causation. To strengthen the claim, sample cases from the declared environment family, randomise intervention order where practical, use independent oracles, and report the proportion of cases whose verdict changes with uncertainty intervals.
Where several cuts produce a repair, retain all of them in the attribution set. Then design a second experiment that creates different predictions. For example, a task repair and evaluator repair may both flip a historical score. A prospective run under the repaired task can separate them because only the task repair changes future behaviour.
Diagnose the loop before replacing the learner
Machine-intelligence work often begins with a model and ends with a score. The causal object in between is larger. A configured system receives a task, acts in an environment and is judged by an evaluator. The same failure can be produced by any of those cuts, and the same apparent success can conceal a breach that the evaluator never observed.
The durable intuition is the courier’s unchanged command sequence. When the interface changes, the path changes without a change in policy. When the judge changes, the verdict changes without a change in path. Those contrasts show why a score alone cannot locate a cause.
The changed decision is simple: do not authorise retraining from an unattributed failure. First freeze the trace, restate intent, verify the environment contract, rescore independently and run the smallest one-cut repairs. Retrain when the failure follows the executable system after the other cuts survive. Repair the task when the optimisation target misstates the purpose. Restore or narrow the environment when live conditions breach the contract. Replace the evaluator when its evidence cannot support the verdict.
Some incidents will remain unresolved. That is not a failure of discipline. An explicit attribution set is more useful than a fictional root cause. It preserves what is known, identifies the next experiment and keeps the release decision tied to evidence rather than to the cultural habit of blaming the model.
sequenceDiagram participant Q as Question participant M as Mechanism participant E as Evidence participant D as Decision Q->>M: The courier that failed without changing M->>E: A small formalism makes the ambiguity visible E-->>M: compare rival explanations E->>D: Source ledger D-->>Q: revise scope or act
Source ledger
- Primary text Richard S. Sutton and Andrew G. Barto, Reinforcement Learning: An Introduction, second edition, MIT Press, 2018. Used for the agent-environment interaction background; it does not prescribe this paper’s four diagnostic cuts.
- Official specification Farama Foundation, Gymnasium
EnvAPI. Used for the documentedstepinterface and separate termination and truncation signals. - Official guidance Farama Foundation, Handling Time Limits. Used for the bootstrapping consequence of distinguishing task termination from external truncation.
- Primary paper Alexander D’Amour et al., “Underspecification Presents Challenges for Credibility in Modern Machine Learning”, Journal of Machine Learning Research 23, 2022. Supports the claim that equivalent held-out performance can conceal deployment-relevant behavioural differences.
- Primary paper Vindula Jayawardana, Catherine Tang, Sirui Li, Dajiang Suo and Cathy Wu, “The Impact of Task Underspecification in Evaluating Deep Reinforcement Learning”, NeurIPS, 2022. Supports the point-MDP versus MDP-family ranking problem; it does not establish the four-cut method.
- Primary paper Jan Leike et al., “AI Safety Gridworlds”, 2017. Used for the experimental separation between observed reward and a hidden performance function.
- Primary paper Dylan Hadfield-Menell, Smitha Milli, Pieter Abbeel, Stuart Russell and Anca Dragan, “Inverse Reward Design”, NeurIPS, 2017. Used for the view that a designed reward is evidence about intent rather than a complete literal objective.
- Primary paper Alexander Pan, Kush Bhatia and Jacob Steinhardt, “The Effects of Reward Misspecification: Mapping and Mitigating Misaligned Models”, 2022. Included as evidence that reward misspecification effects depend on model and environment conditions.
- Primary paper Lauro Langosco, Jack Koch, Lee D. Sharkey, Jacob Pfau and David Krueger, “Goal Misgeneralization in Deep Reinforcement Learning”, ICML, 2022. Supports the boundary between correct training reward and unintended out-of-distribution goals.
- Benchmark paper Pang Wei Koh et al., “WILDS: A Benchmark of in-the-Wild Distribution Shifts”, ICML, 2021. Used for naturally occurring distribution-shift evaluation.
- Primary paper Benjamin Recht, Rebecca Roelofs, Ludwig Schmidt and Vaishaal Shankar, “Do ImageNet Classifiers Generalize to ImageNet?”, ICML, 2019. Used for the fresh-test-set accuracy drop under an attempted reproduction of the original data process.
- Primary paper Marco Tulio Ribeiro, Tongshuang Wu, Carlos Guestrin and Sameer Singh, “Beyond Accuracy: Behavioral Testing of NLP Models with CheckList”, ACL, 2020. Supports targeted capability tests beyond aggregate accuracy.
- Benchmark paper Marlos C. Machado et al., “Revisiting the Arcade Learning Environment: Evaluation Protocols and Open Problems for General Agents”, Journal of Machine Learning Research 19, 2018. Used for the claim that protocol details help determine score meaning.
- Primary paper Peter Henderson, Riashat Islam, Philip Bachman, Joelle Pineau, Doina Precup and David Meger, “Deep Reinforcement Learning that Matters”, AAAI, 2018. Used as evidence of implementation, variance and reporting sensitivity in deep RL.
- Operating source Margaret Mitchell et al., “Model Cards for Model Reporting”, FAT*, 2019. Supports documenting intended use, evaluation conditions and disaggregated performance; the release-tuple recommendation is practitioner synthesis.
- Primary paper Jaineet Shah, “Causal Agent Replay: Counterfactual Attribution for LLM-Agent Failures”, 2026. Supports distributional replay, point-of-commitment localisation and interaction-aware attribution; its demonstrations use reproducible tools rather than live side effects.