Skip to content

fix(evals): remove synthetic system prompt hints, install skills natively per harness (AI-1034) - #180

Draft
claude[bot] wants to merge 3 commits into
mainfrom
fix/remove-eval-prompt-hints
Draft

fix(evals): remove synthetic system prompt hints, install skills natively per harness (AI-1034)#180
claude[bot] wants to merge 3 commits into
mainfrom
fix/remove-eval-prompt-hints

Conversation

@claude

@claude claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Requested via Slack thread

Closes AI-1034 (linear) and #164.

An eval is supposed to measure what an agent does out of the box. The harness was undercutting that in two ways: it prepended a system prompt of its own invention to every agent, and it installed skills where only one of the three CLI harnesses could find them.

Before

Every agent — Claude Code, Codex, OpenCode, and the in-process ai-sdk one — received a harness-authored system prompt:

  • Task framing describing a bash tool and files_* tools. Those tools only exist for ai-sdk; the CLI harnesses never see them and work the workspace with their own built-ins, so the text named tools they do not have.
  • "End your turn with a short summary…", i.e. coaching on stopping behaviour — one of the things the evals score.
  • An ## Available skills block listing each skill and instructing the agent to files_read .claude/skills/<name>/SKILL.md. Again an ai-sdk-only tool, and for Codex a path it cannot see at all.

Codex and OpenCode have no system-prompt flag, so all of that was glued onto the user prompt — the task the model reads.

Skills were installed with a flagless skills add. With no agent CLI installed yet, that CLI falls back to every one of the 71 agents it knows: ~53 stray roots in the workspace (.adal, .factory, .windsurf, … plus non-dotted data/ and skills/). The workspace is exported into run artifacts and is what gets scored. Only .claude/skills was ever verified, and only .claude/skills was advertised — but Codex does not read .claude/skills, so Codex runs effectively had no skills, whatever the injected listing claimed.

After

  • Claude Code, Codex, OpenCode receive no harness system prompt at all. Nothing is staged, --append-system-prompt-file is omitted, and nothing is prepended to the user prompt. The user prompt is exactly the eval's prompt.
  • ai-sdk is unchanged in substance — it is the one harness with no system prompt of its own, so it still gets task framing, the tool-surface description, and either the skills listing (local-stack, files_read) or the load_skill tool (tools mode). The two turn-ending sentences are gone for it too.
  • Skills land where each harness natively looks for them: .claude/skills/ for Claude Code, .agents/skills/ for Codex and OpenCode. Each CLI then discovers, advertises and loads them through its own mechanism — Codex injects its own <skills_instructions> block, OpenCode exposes its skill tool. Skills are genuinely available to Codex for the first time.
  • The workspace is clean: two skill directories instead of ~53 roots.
  • The assembled system prompt is recorded in results/<experiment>/<eval>.json, so what an agent was told is now verifiable from the artifacts.

How

packages/core/src/agents/engine.ts treats the system prompt as optional: empty means no staged file and systemPromptPath: undefined. The three runners drop their respective mechanisms for passing it. Prompt assembly moves out of run-eval.ts (an entry script that calls main() at import, so untestable) into apps/framework/harness/system-prompt.ts, keyed on exp.agent.id; every block is ai-sdk-only, so a CLI harness assembles to ''. buildSkillsPrompt gains the same agent guard buildToolSurfaceAddendum already has.

packages/sandbox/src/skills.ts names the harnesses explicitly:

skills add /tmp/skills-src --agent claude-code codex opencode --skill '*' --copy --yes

Two details are load-bearing and covered by tests. --agent is variadic, so the source directory must come before it — skills add --agent codex <dir> swallows <dir> as an agent name and dies with Missing required argument: source — and --skill terminates the agent list. --copy stays because symlink mode skips any agent whose top-level directory does not already exist.

All three agents are installed unconditionally rather than threading exp.agent.id down to the installer. The three ids collapse to two directories, an unused one costs a few kilobytes of copied files, and this keeps a single code path. Naming them explicitly also removes a latent time bomb: the flagless fallback is install-order dependent, and had an agent CLI been installed before skills, it would have silently stopped producing .claude/skills.

The post-install check now verifies every agent scope, so a skill missing from one fails loudly rather than leaving that harness quietly skill-less.

Skill-usage scoring is unaffected — the extractor regex matches any <anything>skills/<name>/SKILL.md path, so it handles .agents/skills/… as readily as .claude/skills/….

Verification

Automated: pnpm format:check clean; @supabase-evals/framework 6/6, @supabase-evals/core 111/111, @supabase-evals/sandbox 49/49; pnpm typecheck clean. New coverage asserts that a CLI harness assembles to an empty prompt even with skills installed, that ai-sdk still gets its listing, that the install command carries all three agents in the correct argument order, and that a skill missing from any agent scope throws.

Two pre-existing failures on main are untouched and unrelated: the framework smoke test (needs OPENAI_API_KEY) and apps/web's eval-results.test.ts > describes each experiment the same way on every one of its runs (a data problem in the on-hold results JSON).

Not verified here: no Docker daemon, so packages/sandbox/test/docker.test.ts — including the updated multi-scope install assertions — did not run, and no real eval was executed. The CLI behaviour the change relies on was established by executing the pinned binaries directly (skills@1.5.11, @openai/codex@0.138.0, opencode-ai@1.18.5): codex debug prompt-input finds .agents/skills and not .claude/skills, and opencode serve + GET /skill finds both.

Prompt-level verification is now possible for the first time. The assembled systemPrompt is recorded in the run artifacts — it previously was not, for any CLI harness — so a single narrow dispatch is enough to confirm the prompt an agent actually received:

gh workflow run eval-refresh.yml \
  --ref fix/remove-eval-prompt-hints \
  -f eval=<eval-id> \
  -f experiments=<experiment-name> \
  -f suite=benchmark \
  -f experiment_suite=benchmark \
  -f runs=1 \
  -f timeout_sec=720 \
  -f merge=true \
  -f commit_to_branch=false

A single eval × experiment pair takes roughly 4–6 minutes. Then read systemPrompt out of results/<experiment>/<eval>.json — it should be "" for claude-code, codex and opencode — and confirm the skills the agent loaded came from its own harness listing.

Expected impact on numbers

This changes what every CLI-harness agent is told, so benchmark numbers are expected to move — in both directions. Codex in particular gains working skills where it previously had none. Published numbers are not comparable across this change until a full results refresh lands.

Results JSON is deliberately untouched in this PR: a refresh is already in flight, and this branch must not conflict with it. Refresh after merge, then compare.

Open item

The team AI Policy doc could not be reviewed: the Notion page is not shared with the "Claude Tag General Agent Access" integration.


Generated by Claude Code

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
evals Ignored Ignored Preview Aug 7, 2026 4:46pm

Request Review

@claude
claude Bot force-pushed the fix/remove-eval-prompt-hints branch from f2952bb to ecfee71 Compare August 7, 2026 16:37
claude added 3 commits August 7, 2026 16:43
The eval harness handed every agent a synthetic system prompt. For the
three CLI harnesses that prompt described a tool surface they do not have
(`bash`, `files_read`) and coached them on how to end a turn — both of
which bias exactly what an eval is supposed to measure: out-of-the-box
behaviour.

The CLI engine now treats the system prompt as optional. When it is
empty, nothing is staged and nothing is passed:

- `engine` writes `$HOME/.eval/system-prompt.txt` only for a non-empty
  prompt, and leaves `RunnerExecArgs.systemPromptPath` undefined otherwise.
- claude-code omits `--append-system-prompt-file` entirely.
- codex and opencode, which have no system-prompt flag, stop prepending a
  block (and its blank-line separator) to the *user* prompt.

`LocalStackSessionArgs` gains a required `agent: AgentHarnessId` so the
sandbox layer can tell a CLI harness from the in-process ai-sdk one.

Refs AI-1034, #164

Co-Authored-By: Claude <noreply@anthropic.com>
…listing (AI-1034)

Skills were installed with a flagless `skills add`, which — finding no
agent CLI installed yet — falls back to every one of the 71 agents the CLI
knows. That scattered ~53 stray roots across the workspace (`.adal`,
`.factory`, and non-dotted `data/` and `skills/` among them), and the
workspace is exported into run artifacts and scored. It is also
order-dependent: had an agent CLI been installed first, the fallback would
have quietly stopped producing `.claude/skills` altogether.

`skills add` now names the three CLI harnesses explicitly, which installs
into exactly the two project scopes they discover natively:

- `.claude/skills/` — Claude Code
- `.agents/skills/` — Codex and OpenCode

This is the actual fix for Codex, which does not read `.claude/skills` at
all and therefore saw no skills in any eval. All three are installed
unconditionally: the ids collapse to two directories, an unused copy costs
a few kilobytes, and no agent id has to be threaded through
`createAgentEnvironment` for correctness. Argument order matters —
`--agent` is variadic, so the source directory must precede it and
`--skill` terminates the list. `--copy` stays: symlink mode skips agents
whose top-level directory does not already exist.

With each CLI discovering, advertising and loading skills itself,
`buildSkillsPrompt` becomes ai-sdk-only, like `buildToolSurfaceAddendum`.
The block it rendered told agents to read `.claude/skills/<name>/SKILL.md`
with `files_read` — a path Codex cannot see and a tool no CLI harness has.

The post-install check now verifies every agent scope, so a skill missing
from one of them fails loudly instead of leaving that harness silently
skill-less.

Refs AI-1034, #164

Co-Authored-By: Claude <noreply@anthropic.com>
… (AI-1034)

Prompt assembly moves out of `run-eval.ts` (an entry script that runs
`main()` on import, so it cannot be unit-tested) into
`harness/system-prompt.ts`, keyed on `exp.agent.id`. Every block is now
ai-sdk-only — the task framing, the tool-surface addendum, the skills
listing — so a CLI harness assembles to `''` and the engine stages no
system prompt file. The two "end your turn with a short summary" sentences
are gone from both modes: stopping behaviour is part of what is measured.

`runOne` now returns the exact assembled `systemPrompt`, so it lands in
`results/<experiment>/<eval>.json` and what an agent was told is
verifiable from the artifacts. It was previously unrecorded for every CLI
harness. `export-results.ts` builds an explicit whitelist, so it does not
reach the published web data.

`apps/framework` gains a `test` script (`vitest run harness`), wired into
`check`, so the prompt-assembly tests have a runner.

Refs AI-1034, #164

Co-Authored-By: Claude <noreply@anthropic.com>
@claude
claude Bot force-pushed the fix/remove-eval-prompt-hints branch from ecfee71 to a2873a2 Compare August 7, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant