Add scope_check solver: report or gate on out-of-scope sandbox file changes#4462
Draft
kimjune01 wants to merge 1 commit into
Draft
Add scope_check solver: report or gate on out-of-scope sandbox file changes#4462kimjune01 wants to merge 1 commit into
scope_check solver: report or gate on out-of-scope sandbox file changes#4462kimjune01 wants to merge 1 commit into
Conversation
…hanges Closes UKGovernmentBEIS#4461. Inspect grades an agentic sample by what the scorer inspects at the end and asserts nothing about the rest of the sandbox state the agent touched, so a run that completes its task while deleting or corrupting unrelated files scores the same as a clean one (the pass-to-pass / frame condition). scope_check wraps a solver, snapshots the watched roots before and after it runs, and records the paths changed outside an allowed footprint as a scope_check score. Observational by default; gate=True scores an out-of-scope change INCORRECT (and fails closed if the footprint can't be verified). One new solver module plus a two-line export; no changes to the runner, hooks, scorer core, or checkpointing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains:
What is the current behavior? (You can also link to an open issue here)
Closes #4461. Inspect grades an agentic sample by what the scorer inspects at the end and asserts nothing about the rest of the sandbox state the agent touched on the way, so a run that completes its task while deleting or corrupting unrelated files scores the same as a clean one. This is the pass-to-pass / frame condition that final-state scoring omits (SWE-bench's PASS_TO_PASS is the same guarantee). Inspect's agentic evals have the fail-to-pass half and nothing playing the pass-to-pass role. The gap is measurable: a construct-validity audit of Terminal-Bench 2.1 (writeup, terminal-bench#1459) found 40 of 83 gold-passing tasks still score 1 after a careless deletion in their own workspace.
What is the new behavior?
Adds
scope_check(wrapped, roots=("."), allowed=(), gate=False), an opt-in solver that snapshots the watched roots before and after the wrapped solver runs and records the paths changed outside an allowed footprint as ascope_checkscore.gate=Truescores an out-of-scope change INCORRECT.allowedargument (task-wide) or extended per sample throughmetadata["scope_check"]["allowed"]; a sample may widen the allowed set but not weakenrootsorgate. Default ignores are minimal (__pycache__,.pyc,.pyo).lstat, chunked SHA-256, no symlink follow, per-path errors captured rather than aborting) and diffs the two manifests.solver/_scope_check.py) plus a two-line export.Design choices worth review:
rootsis relative to the sandbox working directory; on a bare image with no WORKDIR, watch an explicit root.Receipt from a real Docker sandbox (mock model). The solver writes the allowed
result.txtand deletes a planted/work/secret.key, and the gate catches the deletion:Does this PR introduce a breaking change? (What changes might users need to make in their application due to this PR?)
No. Purely additive and opt-in; nothing changes unless a task wraps a solver in
scope_check.Other information:
Tests in
tests/solver/test_scope_check.pycover the git-style globstar matcher, the manifest diff, and the solver's observational / gating / fail-closed / metadata-extension paths (sandbox stubbed), plus the receipt above as an end-to-end check.Alternatives considered:
Hooksimplementation rather than a solver. Rejected: a hook can attach to the log but can't change aScore, which gating needs, andon_sample_startfires once so a baseline keyed there doesn't survive sample retries. A solver wrapper runs once per attempt and produces a first-class score.util/_restic(checkpoint/resume). Deferred: it would couple the check to checkpoint internals and require restic present. A self-contained manifest keeps this dependency-light; a reusable restic-backed snapshot API could be a follow-up.size/mtimeinstead of hashing for change detection. Rejected: mtime is noisy and misses a rewrite-in-place; the SHA-256 is worth the cost on an opt-in check.Known limits:
python3inside the same sandbox as the untrusted solver, so a sufficiently privileged solver could shim the interpreter to replay the baseline. This is inherent to in-sandbox measurement; a host-side backend would be a separate proposal.roots/ignores narrow it.Happy to adjust the API surface (argument names, whether the default ignore set belongs here, whether gating should sit behind a stricter opt-in) to whatever fits Inspect's conventions.
🤖 Generated with Claude Code