[PR #1971] feat(evaluation): run agent evaluation against Claude Code and Gemini CLI #4943

Open
opened 2026-10-31 00:57:13 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1971
Author: @Nick-800
Created: 10/11/2026
Status: 🔄 Open

Base: main ← Head: feat/agent-eval-clients


📝 Commits (10+)

  • 95cbdef feat(evaluation): run agent evaluation against Claude Code and Gemini CLI
  • 7e5869d fix(evaluation): own agent client processes and credentials
  • 05cdc28 fix(evaluation): preserve client authority and isolated permissions
  • a84907b fix(evaluation): retain verified process ownership and selected authentication
  • b79e445 fix(eval): admit only unambiguous terminal Codex routing
  • a3d2ea9 fix(evaluation): preserve client identity and process ownership
  • a1576a3 Merge commit 'ebaaa79ca0' into HEAD
  • bab7827 fix(evaluation): preserve client command and evidence identity
  • 1ce664c Merge branch 'main' into feat/agent-eval-clients
  • b17b9b6 Merge branch 'main' into feat/agent-eval-clients

📊 Changes

20 files changed (+3174 additions, -470 deletions)

View changed files

📝 docs/client-verification.md (+68 -5)
📝 docs/testing.md (+5 -3)
➕ scripts/lib/agent-eval-clients.d.mts (+53 -0)
➕ scripts/lib/agent-eval-clients.mjs (+394 -0)
📝 scripts/lib/agent-evaluation-commands.mjs (+16 -32)
📝 scripts/verify-agent-experience.mjs (+85 -74)
📝 tests/boundary/mcp/agentKnownAnswerMcp.test.ts (+12 -1)
📝 tests/boundary/process/agentEvaluationCleanup.test.ts (+1 -1)
📝 tests/boundary/process/agentEvaluationCommands.test.ts (+10 -9)
📝 tests/evaluation/model-evals/AgentEval.ts (+55 -128)
📝 tests/evaluation/model-evals/AgentEvaluationReport.ts (+3 -0)
📝 tests/evaluation/model-evals/AgentEvaluationScenarios.ts (+29 -5)
➕ tests/evaluation/model-evals/AgentTranscript.ts (+786 -0)
📝 tests/evaluation/model-evals/KnownAnswerEvaluation.ts (+10 -209)
📝 tests/evaluation/model-evals/agentEval.test.ts (+15 -1)
📝 tests/evaluation/model-evals/agentEvaluationReport.test.ts (+15 -0)
📝 tests/evaluation/model-evals/agentEvaluationScenarios.test.ts (+60 -1)
➕ tests/evaluation/model-evals/agentTranscript.test.ts (+1098 -0)
📝 tests/evaluation/model-evals/knownAnswerEvaluation.test.ts (+22 -1)
➕ tests/process-global/agentEvalClients.test.ts (+437 -0)

📄 Description

Summary

npm run verify:agent can now run the same scenarios, fixture claims and gates through Claude Code or Gemini CLI as well as Codex. Choose the client with REA_AGENT_EVAL_CLIENT=codex|claude-code|gemini-cli. Codex is still the default.

Fixes #1932

Problem and expected behavior

verify:agent was the only lane that measures REA's agent experience against a real model, and it only drove Codex. The verify:claude-client / verify:gemini-client lanes use a local model fixture, so they check integration, not investigation quality. Parsing was Codex-shaped in two places: evaluateCodexEvents and the known-answer grader's completedCall / deliveredCodexEvidence. That meant a regression that only shows up with another client went unmeasured.

Change and scope

  • One client-neutral transcript (tests/evaluation/model-evals/AgentTranscript.ts, beside the other development-owned evaluation modules main moved there; the verifier loads it from source like the fixtures and scenarios). It owns result parsing for every client: failure, error-code certainty (no code is chosen among duplicate JSON members) and the successful content used for Evidence authentication are derived once per call. Each client's events are parsed once into MCP calls, retained results, the final answer and token usage. parseCodexEvents, parseClaudeCodeEvents and parseGeminiCliEvents each follow their client's own grammar:
    • Codex item.completed / mcp_tool_call.
    • Claude tool_use / tool_result, plus the raw MCP tool_use_result that keeps structuredContent even when model-visible content was spilled to a file. The raw detail is attached only when one result block makes it unambiguous; batched parallel results each stand on their own block content.
    • Gemini tool_use / tool_result, where only joined MCP text is kept.
  • Both graders consume the transcript. evaluateAgentTranscript (tests/evaluation/model-evals/AgentEval.ts, replacing CodexAgentEval.ts) and evaluateKnownAnswers read the shared record; KnownAnswerEvaluation authenticates Evidence from it instead of parsing Codex items itself. The Codex-only interpretation is removed; main's grader tests keep their event shapes through a test-local evaluateCodexEvents wrapper.
  • Behavior changes:
    • A completed call is delivered only when the client retained one consistent result record, and any non-null result error, even a bare string, is a failure that delivers no Evidence. A Codex item.completed without result/output (or with a non-object one), and any result whose structuredContent and structured_content contradict each other, keeps its routing facts but is not a completed workflow step, so two such records cannot satisfy the native open_binary/close_binary sequence.
    • If a client reports the same call twice with conflicting results, the call is a failure with no certain error code: it establishes neither Evidence delivery nor an expected refusal. Previously the metric path kept the first report and the grader saw both.
    • The transcript carries the client's own failures (clientFailures): Claude Code permission_denials entries and is_error results, a Gemini CLI result status other than success, a Gemini tool result that failed without REA's own MCP tool error type, and a Claude Code error result without REA's own error envelope (a policy refusal, an unavailable or unregistered server, a client exception). REA's own error results stay ordinary call failures, and an error code is exposed only when every simultaneously present error record agrees on it. Any entry fails agentEvaluationPassed even when the client still returned an answer, so a refused dontAsk request cannot pass a scenario on an "unavailable" explanation.
    • REA server attribution must be explicit, as on main since 443c7aff; this branch's earlier server-less legacy Codex routing is gone.
  • Client runner (scripts/lib/agent-eval-clients.mjs). Each client gets an isolated profile configured through public rea setup --client <id> (dry run, then --yes), and only its authentication is copied in. Processes run through main's shared createAgentEvaluationCommands owner, generalized from runCodex to runClient/readCommandVersion over the selected client's launch; a selected JavaScript entry point (cli.js, bundle/gemini.js) runs under the verifier's Node for the preflight and every scenario. Each client names its credential destination before preparation, so a run that fails earlier still removes it. The version preflight receives the client's non-authentication containment controls (no self-update, no nonessential traffic, no relaunch) without credentials.
    • Claude Code: limited to read-only built-ins (Read,Grep,Glob,Skill,ToolSearch,WaitForMcpServers) in dontAsk mode. claude.ai account connectors are disabled. ANTHROPIC_AUTH_TOKEN is forwarded beside ANTHROPIC_API_KEY for gateway authentication. An empty CLAUDE_CONFIG_DIR counts as unset, as REA's own client resolver reads it.
    • Gemini CLI: runs headless in its default approval mode with a policy file. The policy also allows activate_skill so the REA skill can load. The caller's settings file is read through the owning client-configuration parser, so an empty placeholder counts as no settings.
    • Codex: arguments are unchanged.
  • Tool allow-list. Claude Code and Gemini CLI receive a permission policy taken from each tool's audited effects: a tool that may run foreign code, change a device, discard caller state, launch a process, or reach the network is refused unless the scenario approves it by name. Every scenario additionally approves by name the tools its own contract requires (expected first tool and required subsequence, beside explicit approvals), so the JavaScript scenarios may run analyze_javascript_application, which main's audit marks as launching a worker process. The MCP hints derived from those effects are not consulted, so a hint edit cannot change permissions.
  • Token usage includes cached input for every client. Claude's cache-creation and cache-read counts are added to its uncached input.
  • Report schema 5 adds client, clientCommand and clientVersion, names the setup method rea_setup_<client>, and records each scenario's clientFailures with their total in clientFailureCount. Codex reports keep codex, codexVersion and isolatedCodexHome.
  • Not included: no new scenarios, no gate or threshold changes, and no CI requirement for paid runs. The lane stays manual.

Contract and boundary impact

  • Semantic owner and earliest changed stage: transcript interpretation at the client boundary (tests/evaluation/model-evals/AgentTranscript.ts).
  • CLI and MCP/tool-catalog contract: none.
  • Provider, bridge, target-format, or platform compatibility: none. This is verifier-only. The Gemini lane needs a POSIX host for the same reasons as verify:gemini-client.
  • Evidence, artifact, provenance, or reconstruction contract: grading semantics are unchanged, except that conflicting duplicate reports of one call now fail closed.
  • Process execution, authorization, cleanup, or containment impact:
    • Copied credential files (auth.json, .credentials.json, .gemini/oauth_creds.json) are removed in finally, as before.
    • Client, provider and REA environment variables are stripped, except the selected client's API key and gateway endpoint (OPENAI_BASE_URL, ANTHROPIC_BASE_URL or GOOGLE_GEMINI_BASE_URL).
    • When Gemini CLI keeps an API key in the OS keychain, only the caller's selected auth type is carried over, and the keychain credential is used where it is.
  • Generated metadata, package, or installation impact: none. The evaluator stays development-owned under tests/evaluation/model-evals/ and is not part of the shipped runtime, so knip.json is unchanged.

Evidence and regression coverage

  • Tests added or updated:
    • tests/evaluation/model-evals/agentTranscript.test.ts adds Claude/Gemini parser tests on shapes trimmed from real transcripts. They cover call/result pairing, client-reported failures with REA error codes, non-rea servers, subagent text, token totals, missing usage, permission denials and terminal client errors, delivery requiring a retained result (missing, non-object and empty-object Codex results), duplicate JSON error members, conflicting duplicate reports, and reused call IDs.
    • agentEval.test.ts (main's codexAgentEval.test.ts through the wrapper), knownAnswerEvaluation.test.ts, agentEvaluationScenarios.test.ts and tests/boundary/mcp/agentKnownAnswerMcp.test.ts (main's Notes cases included) go through parseCodexEvents → evaluateAgentTranscript. Two of main's lifecycle-accounting cases now supply result: {} on their completed steps, matching the delivery contract above.
    • agentEvaluationReport.test.ts rejects a result with client failures; agentEvaluationScenarios.test.ts covers scenario approvals and grades an unconfirmed open or close as failed.
    • tests/process-global/agentEvalClients.test.ts covers the effects-based allow-list against TOOL_CONTRACTS[].effects, the JavaScript entry launch, the Claude auth token, and credential destinations; tests/boundary/process/agentEvaluationCommands.test.ts drives the generalized command owner.
  • Base reproduction or other evidence: real runs below.
  • Remaining proof gaps:
    • Codex was not run (not installed here). Codex behavior is covered by the existing tests.
    • Gemini's asar and missing-target scenarios did not complete because of the free-tier daily quota. The activate_skill policy fix has not been checked on a real run.
    • Gemini MCP failures carry a generic client message instead of REA's error record. Validation failures there are counted from the call's arguments against the REA schema.

Validation performed

Re-fit onto main at ff6d96bce (2026-10-25, merge b04661d54, follow-ups 07f44cae8, 5763d60ef, 69c22a621, 0963d13c2, d2e1a7aa5, 86ae9b4bf, ed2e3ba1b, 4dbebd70c, 38a51aa91, 6d2d2c328 and d00eca532 for the Greptile and Codex findings on the pushed heads), then re-fit again onto main at d6f88becd (2026-10-27, merge f36dcbd4e; one conflict, in the verifier's import block, where main now loads parseSkillManifestMetadata through importVerifierSource; follow-up fbd8e10fc for the Greptile finding on that head), Linux, Node 24.22.1 from .nvmrc, npm ci, npm run build:cached:

  • npm run test:focused on agentEval, knownAnswerEvaluation, agentEvaluationReport, agentTranscript, agentEvaluationScenarios, agentEvalClients, agentKnownAnswerMcp, agentEvaluationCommands and agentEvaluationCleanup: 9 files, 170 tests pass (the last two drive verify-agent-experience.mjs with fixture clients, including the early-failure credential cleanup).
  • Every test file under tests/evaluation/: 21 files, 450 passed, 1 skipped (pre-existing).
  • node scripts/run-typescript.mjs --noEmit, npm run lint (no errors; the complexity/length warnings on the verifier and transcript parser are of the same kind main already carries), oxfmt --check ., npx knip: clean.
  • On fbd8e10fc: node scripts/run-typescript.mjs --noEmit, oxlint on the parser and its test, the pre-push check:fast, and the six evaluation suites that import the transcript parser: 166 tests pass.
  • On the merged head f36dcbd4e: npm run build:cached, node scripts/run-typescript.mjs --noEmit, oxfmt --check and oxlint on the lane's files, the pre-push check:fast (turbo typecheck and lint), and the same nine focused suites: 196 tests pass. The first agentEvaluationCleanup case timed out once on a cold cache right after the rebuild and passed on re-run (4/4). Not re-run on this head: every file under tests/evaluation/, oxfmt --check . over the whole repo, npx knip.
  • Not run: a real Claude Code, Gemini CLI or Codex session, npm run docs:check, Windows or macOS. The permission_denials and tool_use_result shapes are those scripts/verify-claude-client.mjs already asserts; the Gemini result.status shape is the one recorded in agentTranscript.test.ts.

Earlier evidence, recorded on the pre-merge head b17b9b6ce before the delivery and client-failure gates existed:

  • Real Claude Code 2.1.296 (REA_AGENT_EVAL_CLIENT=claude-code REA_AGENT_EVAL_SCENARIOS=asar,native,missing-target):
    • asar: passed. All configured fixture claims were correct, against authenticated delivered Evidence.
    • native: failed correctFirstTool. Claude called binary_session before open_binary. The lifecycle subsequence was met, with no repeats or validation failures.
    • missing-target: failed targetClarificationPassed. Claude correctly asked for the target, but wrapped the required JSON in prose and a code fence.
    • Both failures are model behavior measured by the shared gates, which is what #1932 asks for.
  • Real Gemini CLI 0.63.0 (gemini-3-flash-preview, API-key auth), run before the activate_skill fix:
    • native: open_binary was the first call, followed by 13 calls, 1 repeat and 1 validation failure (~1.6M input tokens).
    • asar: Evidence was parsed from Gemini's text-only results, then the run stopped at the daily quota.
    • missing-target: stopped at the daily quota.

Compatibility, safety, and release

  • Breaking changes or migration steps: report schemaVersion 4 → 5, which is additive. Codex reports keep their existing fields.
  • Real Hopper/Ghidra, browser, or OS coverage: Linux only. No provider changes.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: credentials are copied with mode 0600 and removed after the run. No user configuration is copied beyond auth (and Gemini's selected auth type). Account-level claude.ai connectors are disabled in the isolated Claude profile.

Author readiness

Human owner: @Nick-800

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1971 **Author:** [@Nick-800](https://github.com/Nick-800) **Created:** 10/11/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/agent-eval-clients` --- ### 📝 Commits (10+) - [`95cbdef`](https://github.com/morluto/rea/commit/95cbdef60b7d3a97cec133de656430e9efe0f458) feat(evaluation): run agent evaluation against Claude Code and Gemini CLI - [`7e5869d`](https://github.com/morluto/rea/commit/7e5869d8e62642e7bd191cee8cbbd0db0cb2b503) fix(evaluation): own agent client processes and credentials - [`05cdc28`](https://github.com/morluto/rea/commit/05cdc283c080cf750ba259fca55a8f80f3dc1bd0) fix(evaluation): preserve client authority and isolated permissions - [`a84907b`](https://github.com/morluto/rea/commit/a84907bf06b9fb5cb23d389e3c0cea3bbd64aecb) fix(evaluation): retain verified process ownership and selected authentication - [`b79e445`](https://github.com/morluto/rea/commit/b79e44522c64304de5047b5d44cebae2ccc51a15) fix(eval): admit only unambiguous terminal Codex routing - [`a3d2ea9`](https://github.com/morluto/rea/commit/a3d2ea930d4bb0d83974444e558eb58e5f7ecd18) fix(evaluation): preserve client identity and process ownership - [`a1576a3`](https://github.com/morluto/rea/commit/a1576a34c621ee45137156f1c286a48ceebba2ee) Merge commit 'ebaaa79ca02bde4a73013a2a9a781d0b07a57ee4' into HEAD - [`bab7827`](https://github.com/morluto/rea/commit/bab7827263910a1934b15a301c75714133e90ba4) fix(evaluation): preserve client command and evidence identity - [`1ce664c`](https://github.com/morluto/rea/commit/1ce664c6558b14410bda6e13f0ef8da2c1badd2b) Merge branch 'main' into feat/agent-eval-clients - [`b17b9b6`](https://github.com/morluto/rea/commit/b17b9b6cead74c474d5c802890c7bd9a9ee2978c) Merge branch 'main' into feat/agent-eval-clients ### 📊 Changes **20 files changed** (+3174 additions, -470 deletions) <details> <summary>View changed files</summary> 📝 `docs/client-verification.md` (+68 -5) 📝 `docs/testing.md` (+5 -3) ➕ `scripts/lib/agent-eval-clients.d.mts` (+53 -0) ➕ `scripts/lib/agent-eval-clients.mjs` (+394 -0) 📝 `scripts/lib/agent-evaluation-commands.mjs` (+16 -32) 📝 `scripts/verify-agent-experience.mjs` (+85 -74) 📝 `tests/boundary/mcp/agentKnownAnswerMcp.test.ts` (+12 -1) 📝 `tests/boundary/process/agentEvaluationCleanup.test.ts` (+1 -1) 📝 `tests/boundary/process/agentEvaluationCommands.test.ts` (+10 -9) 📝 `tests/evaluation/model-evals/AgentEval.ts` (+55 -128) 📝 `tests/evaluation/model-evals/AgentEvaluationReport.ts` (+3 -0) 📝 `tests/evaluation/model-evals/AgentEvaluationScenarios.ts` (+29 -5) ➕ `tests/evaluation/model-evals/AgentTranscript.ts` (+786 -0) 📝 `tests/evaluation/model-evals/KnownAnswerEvaluation.ts` (+10 -209) 📝 `tests/evaluation/model-evals/agentEval.test.ts` (+15 -1) 📝 `tests/evaluation/model-evals/agentEvaluationReport.test.ts` (+15 -0) 📝 `tests/evaluation/model-evals/agentEvaluationScenarios.test.ts` (+60 -1) ➕ `tests/evaluation/model-evals/agentTranscript.test.ts` (+1098 -0) 📝 `tests/evaluation/model-evals/knownAnswerEvaluation.test.ts` (+22 -1) ➕ `tests/process-global/agentEvalClients.test.ts` (+437 -0) </details> ### 📄 Description ## Summary `npm run verify:agent` can now run the same scenarios, fixture claims and gates through Claude Code or Gemini CLI as well as Codex. Choose the client with `REA_AGENT_EVAL_CLIENT=codex|claude-code|gemini-cli`. Codex is still the default. Fixes #1932 ## Problem and expected behavior `verify:agent` was the only lane that measures REA's agent experience against a real model, and it only drove Codex. The `verify:claude-client` / `verify:gemini-client` lanes use a local model fixture, so they check integration, not investigation quality. Parsing was Codex-shaped in two places: `evaluateCodexEvents` and the known-answer grader's `completedCall` / `deliveredCodexEvidence`. That meant a regression that only shows up with another client went unmeasured. ## Change and scope - **One client-neutral transcript** (`tests/evaluation/model-evals/AgentTranscript.ts`, beside the other development-owned evaluation modules main moved there; the verifier loads it from source like the fixtures and scenarios). It owns result parsing for every client: failure, error-code certainty (no code is chosen among duplicate JSON members) and the successful content used for Evidence authentication are derived once per call. Each client's events are parsed once into MCP calls, retained results, the final answer and token usage. `parseCodexEvents`, `parseClaudeCodeEvents` and `parseGeminiCliEvents` each follow their client's own grammar: - Codex `item.completed` / `mcp_tool_call`. - Claude `tool_use` / `tool_result`, plus the raw MCP `tool_use_result` that keeps `structuredContent` even when model-visible content was spilled to a file. The raw detail is attached only when one result block makes it unambiguous; batched parallel results each stand on their own block content. - Gemini `tool_use` / `tool_result`, where only joined MCP text is kept. - **Both graders consume the transcript.** `evaluateAgentTranscript` (`tests/evaluation/model-evals/AgentEval.ts`, replacing `CodexAgentEval.ts`) and `evaluateKnownAnswers` read the shared record; `KnownAnswerEvaluation` authenticates Evidence from it instead of parsing Codex items itself. The Codex-only interpretation is removed; main's grader tests keep their event shapes through a test-local `evaluateCodexEvents` wrapper. - **Behavior changes:** - A completed call is delivered only when the client retained one consistent result record, and any non-null result error, even a bare string, is a failure that delivers no Evidence. A Codex `item.completed` without `result`/`output` (or with a non-object one), and any result whose `structuredContent` and `structured_content` contradict each other, keeps its routing facts but is not a completed workflow step, so two such records cannot satisfy the native `open_binary`/`close_binary` sequence. - If a client reports the same call twice with conflicting results, the call is a failure with no certain error code: it establishes neither Evidence delivery nor an expected refusal. Previously the metric path kept the first report and the grader saw both. - The transcript carries the client's own failures (`clientFailures`): Claude Code `permission_denials` entries and `is_error` results, a Gemini CLI result status other than `success`, a Gemini tool result that failed without REA's own MCP tool error type, and a Claude Code error result without REA's own error envelope (a policy refusal, an unavailable or unregistered server, a client exception). REA's own error results stay ordinary call failures, and an error code is exposed only when every simultaneously present error record agrees on it. Any entry fails `agentEvaluationPassed` even when the client still returned an answer, so a refused `dontAsk` request cannot pass a scenario on an "unavailable" explanation. - REA server attribution must be explicit, as on main since 443c7aff; this branch's earlier server-less legacy Codex routing is gone. - **Client runner** (`scripts/lib/agent-eval-clients.mjs`). Each client gets an isolated profile configured through public `rea setup --client <id>` (dry run, then `--yes`), and only its authentication is copied in. Processes run through main's shared `createAgentEvaluationCommands` owner, generalized from `runCodex` to `runClient`/`readCommandVersion` over the selected client's `launch`; a selected JavaScript entry point (`cli.js`, `bundle/gemini.js`) runs under the verifier's Node for the preflight and every scenario. Each client names its credential destination before preparation, so a run that fails earlier still removes it. The version preflight receives the client's non-authentication containment controls (no self-update, no nonessential traffic, no relaunch) without credentials. - Claude Code: limited to read-only built-ins (`Read,Grep,Glob,Skill,ToolSearch,WaitForMcpServers`) in `dontAsk` mode. claude.ai account connectors are disabled. `ANTHROPIC_AUTH_TOKEN` is forwarded beside `ANTHROPIC_API_KEY` for gateway authentication. An empty `CLAUDE_CONFIG_DIR` counts as unset, as REA's own client resolver reads it. - Gemini CLI: runs headless in its default approval mode with a policy file. The policy also allows `activate_skill` so the REA skill can load. The caller's settings file is read through the owning client-configuration parser, so an empty placeholder counts as no settings. - Codex: arguments are unchanged. - **Tool allow-list.** Claude Code and Gemini CLI receive a permission policy taken from each tool's audited `effects`: a tool that may run foreign code, change a device, discard caller state, launch a process, or reach the network is refused unless the scenario approves it by name. Every scenario additionally approves by name the tools its own contract requires (expected first tool and required subsequence, beside explicit approvals), so the JavaScript scenarios may run `analyze_javascript_application`, which main's audit marks as launching a worker process. The MCP hints derived from those effects are not consulted, so a hint edit cannot change permissions. - **Token usage** includes cached input for every client. Claude's cache-creation and cache-read counts are added to its uncached input. - **Report schema 5** adds `client`, `clientCommand` and `clientVersion`, names the setup method `rea_setup_<client>`, and records each scenario's `clientFailures` with their total in `clientFailureCount`. Codex reports keep `codex`, `codexVersion` and `isolatedCodexHome`. - **Not included:** no new scenarios, no gate or threshold changes, and no CI requirement for paid runs. The lane stays manual. ## Contract and boundary impact - Semantic owner and earliest changed stage: transcript interpretation at the client boundary (`tests/evaluation/model-evals/AgentTranscript.ts`). - CLI and MCP/tool-catalog contract: none. - Provider, bridge, target-format, or platform compatibility: none. This is verifier-only. The Gemini lane needs a POSIX host for the same reasons as `verify:gemini-client`. - Evidence, artifact, provenance, or reconstruction contract: grading semantics are unchanged, except that conflicting duplicate reports of one call now fail closed. - Process execution, authorization, cleanup, or containment impact: - Copied credential files (`auth.json`, `.credentials.json`, `.gemini/oauth_creds.json`) are removed in `finally`, as before. - Client, provider and REA environment variables are stripped, except the selected client's API key and gateway endpoint (`OPENAI_BASE_URL`, `ANTHROPIC_BASE_URL` or `GOOGLE_GEMINI_BASE_URL`). - When Gemini CLI keeps an API key in the OS keychain, only the caller's selected auth type is carried over, and the keychain credential is used where it is. - Generated metadata, package, or installation impact: none. The evaluator stays development-owned under `tests/evaluation/model-evals/` and is not part of the shipped runtime, so `knip.json` is unchanged. ## Evidence and regression coverage - Tests added or updated: - `tests/evaluation/model-evals/agentTranscript.test.ts` adds Claude/Gemini parser tests on shapes trimmed from real transcripts. They cover call/result pairing, client-reported failures with REA error codes, non-`rea` servers, subagent text, token totals, missing usage, permission denials and terminal client errors, delivery requiring a retained result (missing, non-object and empty-object Codex results), duplicate JSON error members, conflicting duplicate reports, and reused call IDs. - `agentEval.test.ts` (main's `codexAgentEval.test.ts` through the wrapper), `knownAnswerEvaluation.test.ts`, `agentEvaluationScenarios.test.ts` and `tests/boundary/mcp/agentKnownAnswerMcp.test.ts` (main's Notes cases included) go through `parseCodexEvents` → `evaluateAgentTranscript`. Two of main's lifecycle-accounting cases now supply `result: {}` on their completed steps, matching the delivery contract above. - `agentEvaluationReport.test.ts` rejects a result with client failures; `agentEvaluationScenarios.test.ts` covers scenario approvals and grades an unconfirmed open or close as failed. - `tests/process-global/agentEvalClients.test.ts` covers the effects-based allow-list against `TOOL_CONTRACTS[].effects`, the JavaScript entry launch, the Claude auth token, and credential destinations; `tests/boundary/process/agentEvaluationCommands.test.ts` drives the generalized command owner. - Base reproduction or other evidence: real runs below. - Remaining proof gaps: - Codex was not run (not installed here). Codex behavior is covered by the existing tests. - Gemini's `asar` and `missing-target` scenarios did not complete because of the free-tier daily quota. The `activate_skill` policy fix has not been checked on a real run. - Gemini MCP failures carry a generic client message instead of REA's error record. Validation failures there are counted from the call's arguments against the REA schema. ## Validation performed Re-fit onto main at ff6d96bce (2026-10-25, merge b04661d54, follow-ups 07f44cae8, 5763d60ef, 69c22a621, 0963d13c2, d2e1a7aa5, 86ae9b4bf, ed2e3ba1b, 4dbebd70c, 38a51aa91, 6d2d2c328 and d00eca532 for the Greptile and Codex findings on the pushed heads), then re-fit again onto main at d6f88becd (2026-10-27, merge f36dcbd4e; one conflict, in the verifier's import block, where main now loads `parseSkillManifestMetadata` through `importVerifierSource`; follow-up fbd8e10fc for the Greptile finding on that head), Linux, Node 24.22.1 from `.nvmrc`, `npm ci`, `npm run build:cached`: - `npm run test:focused` on `agentEval`, `knownAnswerEvaluation`, `agentEvaluationReport`, `agentTranscript`, `agentEvaluationScenarios`, `agentEvalClients`, `agentKnownAnswerMcp`, `agentEvaluationCommands` and `agentEvaluationCleanup`: 9 files, 170 tests pass (the last two drive `verify-agent-experience.mjs` with fixture clients, including the early-failure credential cleanup). - Every test file under `tests/evaluation/`: 21 files, 450 passed, 1 skipped (pre-existing). - `node scripts/run-typescript.mjs --noEmit`, `npm run lint` (no errors; the complexity/length warnings on the verifier and transcript parser are of the same kind main already carries), `oxfmt --check .`, `npx knip`: clean. - On fbd8e10fc: `node scripts/run-typescript.mjs --noEmit`, `oxlint` on the parser and its test, the pre-push `check:fast`, and the six evaluation suites that import the transcript parser: 166 tests pass. - On the merged head f36dcbd4e: `npm run build:cached`, `node scripts/run-typescript.mjs --noEmit`, `oxfmt --check` and `oxlint` on the lane's files, the pre-push `check:fast` (turbo typecheck and lint), and the same nine focused suites: 196 tests pass. The first `agentEvaluationCleanup` case timed out once on a cold cache right after the rebuild and passed on re-run (4/4). Not re-run on this head: every file under `tests/evaluation/`, `oxfmt --check .` over the whole repo, `npx knip`. - Not run: a real Claude Code, Gemini CLI or Codex session, `npm run docs:check`, Windows or macOS. The `permission_denials` and `tool_use_result` shapes are those `scripts/verify-claude-client.mjs` already asserts; the Gemini `result.status` shape is the one recorded in `agentTranscript.test.ts`. Earlier evidence, recorded on the pre-merge head b17b9b6ce before the delivery and client-failure gates existed: - **Real Claude Code 2.1.296** (`REA_AGENT_EVAL_CLIENT=claude-code REA_AGENT_EVAL_SCENARIOS=asar,native,missing-target`): - `asar`: **passed**. All configured fixture claims were correct, against authenticated delivered Evidence. - `native`: failed `correctFirstTool`. Claude called `binary_session` before `open_binary`. The lifecycle subsequence was met, with no repeats or validation failures. - `missing-target`: failed `targetClarificationPassed`. Claude correctly asked for the target, but wrapped the required JSON in prose and a code fence. - Both failures are model behavior measured by the shared gates, which is what #1932 asks for. - **Real Gemini CLI 0.63.0** (`gemini-3-flash-preview`, API-key auth), run before the `activate_skill` fix: - `native`: `open_binary` was the first call, followed by 13 calls, 1 repeat and 1 validation failure (~1.6M input tokens). - `asar`: Evidence was parsed from Gemini's text-only results, then the run stopped at the daily quota. - `missing-target`: stopped at the daily quota. ## Compatibility, safety, and release - Breaking changes or migration steps: report `schemaVersion` 4 → 5, which is additive. Codex reports keep their existing fields. - Real Hopper/Ghidra, browser, or OS coverage: Linux only. No provider changes. - Package or release metadata impact: none. - Security, privacy, process, or containment review: credentials are copied with mode 0600 and removed after the run. No user configuration is copied beyond auth (and Gemini's selected auth type). Account-level claude.ai connectors are disabled in the isolated Claude profile. ## Author readiness Human owner: @Nick-800 - [ ] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [ ] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [ ] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4943
No description provided.