Add an interception example environment - #2170
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 976b3f6d5f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,13 @@ | |||
| [project] | |||
| name = "interception-v1" | |||
There was a problem hiding this comment.
Register the example with the root uv environment
In the checked-in live v1 E2E path, .github/workflows/test.yml runs uv sync, then tests/v1/test_envs.py discovers every environments/*_v1 directory and invokes uv run --no-sync eval interception_v1. This new package is absent from the root examples dependency group, [tool.uv.sources], and uv.lock, so a fresh checkout cannot import interception_v1 and the smoke suite fails before evaluating it. Ensure the repository's normal uv setup installs this example, or otherwise keep it out of that automatic discovery path.
AGENTS.md reference: AGENTS.md:L11-L12
Useful? React with 👍 / 👎.
| build-backend = "hatchling.build" | ||
|
|
||
| [tool.hatch.build.targets.wheel] | ||
| packages = ["interception_v1"] |
There was a problem hiding this comment.
Example missing workspace registration
High Severity
The new interception-v1 package is not added to the root examples dependency group or tool.uv.sources, unlike every sibling under environments/. tests/v1/test_envs.py auto-discovers each *_v1 directory and runs uv run --no-sync eval, so CI cannot import this taskset after a normal sync.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 976b3f6. Configure here.
ApprovabilityVerdict: Needs human review 1 blocking correctness issue found. Multiple unresolved review comments identify substantive issues including missing workspace registration (causing CI failures) and a logic bug where the judge guard triggers on the user prompt rather than model responses. These need human review and resolution. You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 52437392d4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| verdict = response.text.strip().upper() | ||
| if verdict not in ("BLOCK", "ALLOW"): |
There was a problem hiding this comment.
Accept recognizable judge verdicts before failing
When the judge returns a semantically valid but wrapped answer such as Verdict: BLOCK, a boxed verdict, or reasoning followed by ALLOW, the exact string comparison rejects it and turns the intercepted rollout into a TaskError. Use the existing vf.parse_judge_choice or vf.intercept_with_judge, which already handles common judge response formats while still failing when no verdict is recognizable.
AGENTS.md reference: AGENTS.md:L5-L5
Useful? React with 👍 / 👎.
65bd52f to
3977a80
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3977a806cd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| prompt: vf.Messages | None = None, | ||
| ) -> vf.InterceptResult: | ||
| """Use an ordinary judge for cases that need semantic classification.""" | ||
| response = await vf.Judge().complete( |
There was a problem hiding this comment.
Configure the guard judge for the selected endpoint
When the smoke eval runs with only OPENAI_API_KEY, the checked tests/v1/test_envs.py branch configures the agent for https://api.openai.com/v1, but this bare Judge independently retains JudgeConfig's Prime endpoint, PRIME_API_KEY, and Prime-style model name. Because judge_guard runs after every response, including the first deterministic example, the judge request uses an empty or wrong credential and turns the rollout into a TaskError; supply a configurable judge endpoint/key/model compatible with the supported OpenAI-only eval context.
Useful? React with 👍 / 👎.
3977a80 to
a347a1e
Compare
658f576 to
cec154b
Compare
a347a1e to
845c1fd
Compare
845c1fd to
5b22fdd
Compare
|
|
||
| JUDGE = vf.Judge( | ||
| vf.JudgeConfig( | ||
| prompt="""Inspect the untrusted model exchange below. |
There was a problem hiding this comment.
🟡 Medium interception_v1/taskset.py:5
The JUDGE prompt matches JUDGE_BLOCK anywhere in the full exchange, so it fires on the user prompt itself ("Reply with exactly JUDGE_BLOCK and no other text.") before the model even responds. This causes judge_guard to rewrite the exchange for every judge example, and expected_guard_fired awards full reward regardless of whether the model's response actually contained the marker. Consider checking only the candidate/last message rather than the entire exchange, or avoid embedding the sentinel literal in the user-facing prompt.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @environments/interception_v1/interception_v1/taskset.py around line 5:
The `JUDGE` prompt matches `JUDGE_BLOCK` anywhere in the full exchange, so it fires on the user prompt itself (`"Reply with exactly JUDGE_BLOCK and no other text."`) before the model even responds. This causes `judge_guard` to rewrite the exchange for every judge example, and `expected_guard_fired` awards full reward regardless of whether the model's response actually contained the marker. Consider checking only the candidate/last message rather than the entire exchange, or avoid embedding the sentinel literal in the user-facing prompt.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b22fdda54
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "deterministic_guard", | ||
| ), | ||
| ( | ||
| "Reply with exactly JUDGE_BLOCK and no other text.", |
There was a problem hiding this comment.
Keep the trigger out of the inspected prompt
For every rollout of this second task, exchange=trace.messages includes this user prompt, while the judge rubric instructs the judge to return BLOCK if any message contains JUDGE_BLOCK. The prompt therefore triggers judge_guard regardless of the assistant's response, and expected_guard_fired awards full reward even when the model never emits the blocked text. Inspect only trace.last_message or phrase the task without placing the literal trigger in the exchange.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5b22fdd. Configure here.
| ), | ||
| ( | ||
| "Reply with exactly JUDGE_BLOCK and no other text.", | ||
| "judge_guard", |
There was a problem hiding this comment.
Judge trigger matches task prompt
Medium Severity
The judge rubric returns BLOCK when any message contains JUDGE_BLOCK, but the task prompt itself includes that token. judge_guard therefore rewrites from the user prompt rather than the model reply, so the example does not demonstrate judge-backed response classification.
Reviewed by Cursor Bugbot for commit 5b22fdd. Configure here.
|
Superseded by #2178, which replaces the guard layer and example environment with the reworked interception surface (typed rules, prebuilt guards). |


Overview
This adds a small runnable environment showing the minimal custom interception interface.
The task defines one deterministic guard and one judge-backed guard directly with
@vf.intercept. Both receive onevf.Trace; the deterministic guard readstrace.last_message, the judge seestrace.messages, and either can returntrace.replace(content)to rewrite the candidate.What this PR adds
environments/interception_v1package with two focused examples.trace.last_message.trace.messages.Stack
This is PR 5 of 5. Each PR is based on the one before it and is intended to merge in order.
@vf.interceptbefore model requests and before responses reach the harness, and make rewrites canonical.vf.Trace.@vf.interceptin a runnable taskset.