Repository navigation
fix(ci): report an eval policy probe with no tool call as unproven - #13885
Conversation
The probe in eval-hill-climb-pass.yml and eval-browser-dispatch.yml (six legs) fell through to "the managed policy did not record a denied Read" when its session made no tool call in all three attempts, so a model that declined the reads was reported as a policy failure. policy-probe.mjs now judges the stream per expected call: denied, made and not denied, or not made. A call made and not denied stops the leg at once, as before. A session that ended normally without every call runs again and, after three attempts, stops the leg as unproven with its own error. A session that errored stops with its own error. Every outcome except four denials still stops the leg, so no case runs on an unproven policy. The claude call stays inline so the credential guards in callers.test.mjs still see it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review Summary: ❌ FailedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
claude-security-review reviewed Security review: no security issues foundI read every hunk of all 6 changed files. Both workflows are on the trusted-path list, so I read each of their changes in full, plus the probe steps they sit in and the What changed. In all six eval probe steps (three legs in The policy gate is no weaker.
No stream text reaches the logs.
Where the judge now loads from. The judge now loads from Test files. Spend. No new spend beyond the existing probe: the retry still runs at most 3 The PR context contains no embedded instructions aimed at the review. There are no prior findings, so this is a full review rather than a blocking-only one. critical-findings: 0 |
|
claude-review reviewed Code review: PR #13885, no findingsI read all six changed files hunk by hunk. I also read the head probe step in What the change doesThe probe step had a jq judge copied into six places. This PR replaces those copies with one module,
What I checked
Closing issue #13854
I did not run any tests or actionlint. The pass counts in the PR body are the author's claims. SpendA session that makes some of the calls but not all four now gets up to 3 Not applicable
|
Post-merge mutation testingKilled 5, survived 2, timeout 0, no coverage 0, invalid 0, not run 0.
Surviving mutants
The run's summary has the full report. |
Closes #13854
Summary
The eval policy probe reported a session that made no tool call as a policy
failure ("the managed policy did not record a denied Read of
/proc/self/environ"). It now reports that case as unproven, with its own
error, and still stops the leg, so no case runs on a policy nobody proved.
Fix
.github/scripts/plugin-eval/policy-probe.mjsjudges the probe'sstream once per expected call (Read /proc/self/environ, Read
$RUNNER_ROOT/.runner, Grep /proc/self, Glob /proc/self). Each call issorted as denied, made and not denied, or not made:
old "did not record a denied" message, the only case that now uses it.
with its own message.
runs it again. After 3 attempts it stops with
::error::the policy is unproven: ....eval-hill-climb-pass.ymland the three in
eval-browser-dispatch.yml. The issue named four.claude -pcall stays inline in each step on purpose. The credentialguards in
callers.test.mjs(isLauncherStep, the stray-reader check andthe fallback-token rule) find launchers by a
claudeline instep.run.Moving the call into a shell script would hide the probe from those
guards.
.basewithout the credential (env -u), after theleak check, as
eval-handoff.mjsdoes.Done-when 2 (prove the policy without depending on the model choosing a tool
call) is met only by the issue's "or" arm:
passed every probe step (pass-a, pass-a2, pass-b, key and subscription
legs) after fix(ci): frame the eval policy probe as a self-test and rerun a no-call session #13665 added the self-test framing and the retry.
42166675433
(05:09Z) ran before fix(ci): frame the eval policy probe as a self-test and rerun a no-call session #13665 merged (05:37Z).
claude --helplists no flag thatforces a tool call.
managed-settings.jsoncannot stand in for the live probe. Thedeny step already asserts the
/procrule is in the installed file(
jq -e '.permissions.deny | index("Read(//proc/**)")'), but Claude Codeenforces Read deny rules on Grep and Glob only best-effort
(code.claude.com/docs/en/permissions, "Read & Edit"). A correct file
therefore does not prove the calls are denied, and letting cases run on a
file check alone would loosen a security control.
Done-when 3 was already fixed on main:
(00:41Z) logged
eval-handoff: Invalid package config /home/runner/work/claude-code-plugins/claude-code-plugins/package.json.lane.cjs, required in place, climbed to the workspace root'spackage.json, which did not parse.package.json. The regression test is "the classifier loads under aworkspace whose root package.json does not parse" in
eval-handoff.test.mjs.Verification
Each file was run serially with
bash scripts/run-test-file.sh, aftermerging origin/main:
policy-probe.test.mjs: 7/7. It covers the no-call, skipped-call,made-not-denied, errored and non-JSON-line cases, and checks that the
facts carry no stream text.
callers.test.mjs: 273/273. The step harness now runs the real judge. Asession that never calls a tool runs 3 times and ends
unproven, neverdid not record. A Read that is not denied stops after 1 attempt.workflow.test.mjs: 6/6.hill-climb-workflow.test.mjs: 11/11.eval-handoff.test.mjs: 15/15.cache-modekey ones, also present on main.This PR touches no plugin, so
eval-browser-dispatch'sstagestays off andthe probe does not run against the old
.base.Related
🤖 Generated with Claude Code