19 Commits

Author SHA1 Message Date
ayghri fcd0e2fb53 Merge main into PR #125, preserving eval runner isolation
Resolve the runner conflict by retaining the neutral working directory and closing child stdin. Preserve the contributor commit and both regression tests.

Validation: 43 unit tests passed; eval catalog valid; real subprocess smoke check passed; both regressions reproduced against unpatched main. No paid provider calls.

Owner-directed amendment prepared with Codex (GPT-6).
2026-09-09 18:53:15 -06:00
Ayoub Ghriss 284a2013ae Merge pull request #97 from Defaultuser361/eval-harness-judge-and-fidelity-fixes
Make the eval harness runnable, then run it
2026-09-01 12:35:35 -06:00
Ayoub G. 44d9630f1e style: simplify eval type annotations 2026-09-01 18:44:41 +02:00
Ayoub G. 57e65fd9e6 evals: remove redundant judge cwd shim 2026-09-01 14:02:35 +02:00
Ayoub G. 76b66165ad evals: harden blind judge isolation 2026-09-01 14:00:24 +02:00
Felipe Lipkin e7dbd13769 fix(evals): close child stdin and tolerate output after the JSON result
The claude runner is invoked without a stdin redirect, so the child inherits
the parent's stdin. In a non-interactive parent the CLI waits 3s per call and
warns; worse, when that stdin carries data the CLI reads it and appends it to
the prompt. One observed call billed 155,004 cache-creation input tokens for a
4-token prompt (USD 1.58 against a USD 0.03 median), which is silent unbounded
cost in a harness whose budget flag is its only guard.

The same warning can land after the JSON document on stdout, and
_parse_response used json.loads on the whole buffer, aborting the run with
"Extra data: line 2 column 1" and discarding the responses still in flight.

Close the child's stdin and decode only the first JSON document.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 20:46:23 -03:00
Ayoub G. 75ee1c49dc hooks: fail open on context inspection errors 2026-08-17 18:56:08 +02:00
OMP Agent 3bbfaaac75 Support OMP session context API 2026-08-14 23:28:06 +05:30
Ayoub G. 1c90740bec feat(pi): declare package resources explicitly 2026-08-06 10:15:33 -04:00
Lonnie Zane ff1d3c41f9 feat: add a blind judge so the harness can produce scores
`score` aggregates rows a human already wrote, so running the harness end to end
meant hand-scoring every response across five dimensions -- 420 numbers for the
documented 3-trial pass. That is the step that kept the harness unrun.

scripts/judge.py grades a whole `(case, trial)` group in one call, so conditions
are compared against each other rather than scored in isolation. Blinding is
structural rather than a convention the grader is asked to honour: conditions
are relabelled A/B/C and the label order is permuted per group from a digest of
the group key, so position carries no signal and a resumed run reproduces the
labels it used before. Over the 42 groups of a 3-trial pass the candidate landed
in each position 19/23 times.

Only the region of rubric.md between the new judge:begin/judge:end markers
reaches the grader. The release-gate rules below them name both conditions, so
sending the whole document to a blind grader would hand over the exact
vocabulary the blinding exists to hide -- caught by the end-to-end test, after
every unit test had passed on a prompt that was still leaking.

The prompt goes in on stdin. Appending it as a trailing argument let the claude
runner's `--tools ""` consume it as that option's value; generation only escapes
this because `--max-budget-usd` happens to sit in between. Stdin also removes an
argv ceiling that matters when prompts embed whole responses.

Graders occasionally drop a field or wrap JSON in prose. Verdicts are validated
with the offending case named, retried on the same budget as a failed process,
and if still unusable the group is skipped, reported on stderr, and the exit
code is non-zero -- one bad reply should not discard the groups already written
or the ones queued behind it. Groups missing a condition are reported the same
way rather than dropped, since conditions are only comparable when judged on
identical rows.

Stdlib only, matching the existing scripts. 18 tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 05:23:43 -05:00
Lonnie Zane 0a82e608c6 fix: grade the prompt that ships, in a context that isn't this repo
Two ways the harness graded something other than what it claimed to. Both
surfaced on the first real run; neither is visible without executing it.

Working directory: the runner subprocess inherited `cwd=ROOT`, so an agent CLI
adopted this checkout as project context and answered prompts by inspecting the
harness instead of the task. One baseline response opened by identifying
`scripts/run_evals.py` and emitted tool-call syntax for tools it did not have.
Over a 14-case pass it hit 8 baseline responses and 2 candidate ones -- the
asymmetry favours whichever condition discourages exploration, so it lands
directly on the number under test. The runners already pass `--setting-sources ""`
for exactly this reason; the working directory was an unguarded channel for the
same class of leak. Both conditions now run in an empty scratch directory.

Frontmatter: `_condition_prompt` injected SKILL.md verbatim, so the candidate
prompt carried `disable-model-invocation: true` and the `hermes` metadata block.
hooks/always-on.sh strips that frontmatter before injecting the ruleset, so the
eval was grading text that never reaches a user. `_strip_frontmatter` mirrors
the hook's awk -- byte-identical output on the current SKILL.md -- and leaves a
file alone if it opens a block without closing it, rather than emptying it.

Tests cover both: the working-directory one uses a stub runner that reports its
own `pwd`, and fails against the previous behaviour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 05:23:21 -05:00
BoxChen 53f86a0458 feat(pi): add native session-persistent mode 2026-07-28 18:55:51 +08:00
thejesh23 19b0c31403 fix(evals): reject unmetered runners before the first paid call
The "Runner did not report dollar cost" rejection fired after the subprocess
had already run — but for the text and codex-jsonl response formats
_parse_response returns cost None unconditionally, so the outcome is fully
determined by the runner config. First run with the shipped codex runner and
no --allow-unmetered paid for one full API call, then discarded the response;
the rerun paid for it again.

Pre-flight the check from response_format before the loop. The per-call check
stays as a backstop for claude-json responses that omit total_cost_usd, which
is only knowable at runtime.
2026-07-21 23:09:51 -07:00
Ayoub Ghriss 1a9e52ad93 Merge pull request #48 from thejesh23/fix/score-pairing
fix(evals): reject unpaired conditions before scoring the release gate
2026-07-21 23:49:33 -06:00
Ayoub Ghriss 57dcf96071 Merge pull request #49 from thejesh23/fix/unknown-case-id
fix(evals): fail when `--case` matches no evaluation case
2026-07-21 23:49:28 -06:00
thejesh23 e5aaa0bd5b fix(evals): name the real flag when the condition skill is missing
The message interpolated the condition name and told the user to pass
--candidate-skill / --comparator-skill; the parser only defines
--condition-skill, so following the error verbatim produced
"unrecognized arguments".
2026-07-21 22:45:10 -07:00
thejesh23 274d936d1b fix(evals): fail when --case matches no evaluation case
An unknown id filtered out every case, so the run wrote nothing and exited 0
with "Reported cost: $0.0000" — indistinguishable from a resumed run that had
nothing left to do. The operator believes the condition was measured, and
score then compares whatever happens to be in the results file.

Validate --case against the loaded catalog before the loop.
2026-07-21 22:44:33 -07:00
thejesh23 4e38331d6e fix(evals): reject unpaired conditions before scoring the release gate
summarize_scores averaged each condition independently, so a baseline judged
on two hard cases and a candidate judged on one easy case returned
"passed": true with no warning. evals/README.md already requires identical
cases across conditions; nothing enforced it, and a partial candidate run or a
skipped judge row silently produced a confident verdict.

Compare the (case_id, trial) multiset of every condition against baseline and
raise with the concrete difference; reject duplicate rows within a condition,
which otherwise double-count into the average.
2026-07-21 22:43:49 -07:00
isatimur 8e850c2785 Add reproducible skill quality evaluations
14 cases across direct answers, agent autonomy, debugging, explanations,
destructive actions, ambiguity, progress, output contracts, planning,
and medical boundaries. Weighted correctness/autonomy/actionability/
safety/concision rubric, baseline/candidate/comparator matrix, budget
ceiling, blind scoring, resumability. Trimmed to the evaluation
foundation; the repo-governance files from the original branch were
split out. Groundwork for #4.
2026-07-21 23:21:19 -06:00