Skip to content

tau3: gpt-5.4-mini baseline + a benchmark-agnostic optimizer-commit analysis skill - #84

Open
yash-scaleai wants to merge 6 commits into
mainfrom
tau3-baseline-and-skill
Open

tau3: gpt-5.4-mini baseline + a benchmark-agnostic optimizer-commit analysis skill#84
yash-scaleai wants to merge 6 commits into
mainfrom
tau3-baseline-and-skill

Conversation

@yash-scaleai

@yash-scaleai yash-scaleai commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Pins tau3's gpt-5.4-mini baseline at 0.5618 (rescore_candidate.py --seed, 3 rounds x 3 attempts, n=1335, sd 0.0063) and backfills it into every finalized cell's finalization.json, so the gain can be derived from S3 artifacts directly rather than added separately downstream.
  • Fixes the seed's empty-turn crash (was killing 17/150 held-out cases) and adds the Azure routing variant the gpt-5.6 cells needed.
  • Records the two pre-fix cells quarantined out of the S3 cell namespace, and why.
  • Adds skills/analyze-optimizer-commits/: reads what an optimizer LLM actually did to a target harness across a benchmark's grid cells and produces a citation-bearing observations file. Benchmark-agnostic by construction — both scripts take --benchmark as an argument and walk runs/<benchmark>/ generically, no hardcoded target-repo path or model name anywhere. Run on tau3's 16 cells as its first real test; tau3/OPTIMIZER-BEHAVIOR.md is that run's output.

Known open issue, not fixed by this PR: two of the 16 cells shipped a harness byte-identical to the seed (confirmed by git tree hash) and scored ~0.12 below the pinned 0.5618 baseline for that same code. Documented in OPTIMIZER-BEHAVIOR.md's "measurement-path gap" section. Resolving it needs a rescore of the seed through the finalization path, which needs a funded LiteLLM key.

Test plan

  • extract_candidates.py --benchmark tau3 reproduces the 83-candidate, 16-cell corpus from S3 artifacts
  • Seed-tree-hash check verified independently against the two quarantined-behavior cells
  • All 15 draft observations in OPTIMIZER-BEHAVIOR.md passed through an adversarial verification pass (12 confirmed as stated, 3 corrected)
  • Confirmed no hardcoded tau3/model references in either skill script

🤖 Generated with Claude Code

Greptile Summary

This PR pins and backfills tau3’s GPT-5.4-mini baseline while adding reusable optimizer-commit analysis tooling.

  • Adds baseline rescoring, backfill, and alternate model-routing support.
  • Fixes tau3’s empty-turn handling and introduces GPT-5.4-mini and Azure build variants.
  • Adds benchmark-agnostic candidate extraction, cross-cell statistics, analysis guidance, and the resulting tau3 optimizer-behavior report.

Confidence Score: 4/5

The PR is not yet safe to merge because candidate code can still access the upstream credential and branched Git histories can still corrupt optimizer-commit analysis.

The tau3 configurations continue to combine upstream task-service credentials with disabled harness isolation, leaving the raw key in the candidate environment, while the extractor’s merge and root checks do not establish the single linear lineage required by its git log --all ordering.

Files Needing Attention: harness-engineering-bench/tau3/baseline/build.gpt54mini.yaml, harness-engineering-bench/tau3/baseline/build.azure.yaml, harness-engineering-bench/skills/analyze-optimizer-commits/scripts/extract_candidates.py

Important Files Changed

Filename Overview
harness-engineering-bench/tau3/baseline/build.gpt54mini.yaml Adds the pinned GPT-5.4-mini tau3 configuration, but retains the previously reported upstream-credential exposure.
harness-engineering-bench/tau3/baseline/build.azure.yaml Adds Azure routing with the same unresolved credential and harness-isolation boundary.
harness-engineering-bench/skills/analyze-optimizer-commits/scripts/extract_candidates.py Adds generic candidate extraction and archive containment checks, but its topology guards still permit branched single-root histories to be misclassified as one candidate sequence.
harness-engineering-bench/scripts/rescore_candidate.py Extends rescoring to selectable build variants and seed baselines.
harness-engineering-bench/scripts/backfill_baseline.py Adds schema-validated baseline reward backfilling with separate provenance artifacts.
harness-engineering-bench/tau3/baseline/target/src/tau3_agent/agent.py Handles empty assistant turns without crashing the tau3 seed agent.

Reviews (3): Last reviewed commit: "extract_candidates: fix extraction conta..." | Re-trigger Greptile

yash-scaleai and others added 4 commits August 2, 2026 20:38
… turns

Follows the variant convention swe-atlas-qna set: build.yaml stays on
deepseek-v4-flash and build.gpt54mini.yaml retargets the benchmark, so the change
is reversible and the two configs are comparable side by side. The variant carries
no baseline_reward, because 0.7321 was measured on deepseek with no
reasoning_effort at all and a delta against it would be a model comparison. It
reports an absolute held-out score instead.

Reasoning effort stays at the seed's medium. At xhigh the seed lost 16 of 150
held-out cases to context-window overflow; at medium it lost none.

The seed raised RuntimeError when the model returned neither text nor a tool call,
which killed 17 of 150 cases on this target and put the run above the 0.1
error_rate_threshold that aborts an evaluation. gaia took the same fix in 4e90dace
and tau3 never got it; it stayed invisible while the target was deepseek, which
does not emit reason-only turns.

Producer allow-list gains the bare and aux slots, after 8 of 157 producer calls in
a gaia opencode run came back 403 model_denied.

rescore_candidate.py gains --config so a retargeted variant can be scored by the
same path as the pinned baselines. Without it the script only ever reads
build.yaml, and swe-atlas-qna's new variant cannot be pinned at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The producer allow-list grew from one slot to three (swe-atlas-qna, and tau3's
gpt-5.4-mini variant), and the launcher could only fill the first. An
anthropic-routed optimizer therefore had no way to declare its bare and aux model
names and took a 403 model_denied on each. Trailing KEY=VALUE args now become
--param pairs, validated at launch so a typo fails there rather than at the
gateway. Note the slots must resolve to distinct strings: allowed_models is
uniqueness-checked, so passing the wire form for both the prefixed and bare slot
fails config validation before any container starts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
codex strips the provider prefix, so --model azure_ai/gpt-5.6-sol reaches the
gateway as bare gpt-5.6-sol and is forwarded unchanged; the proxy then
load-balances it across two Azure deployments and every turn after the first fails
with invalid_encrypted_content, because the reasoning payload cannot be decrypted
by the deployment that did not create it. That killed all four gpt-5.6 cells
earlier today at 2-4 minutes.

Uses the gateway model_aliases added in PR #81 to pin both bare names to their
Azure deployment, which is the only place the prefix can be reattached since the
harness will not carry it. Varun measured the split against the live proxy: bare
failed 5 of 5 encrypted-content replays, azure_ai/-prefixed passed 5 of 5. My own
3-chain probe passed both ways and was too small to see it.

Also why this cannot wait for the shared config: the OpenAI contract expired
2026-07-31, so every GPT model now has to route through Azure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hed cells

Measured on the config the cells actually ran: 3 rounds x 3 attempts of the
unmodified seed through rescore_candidate.py, n=1335, rounds 0.568/0.553/0.564,
sd 0.0063. The old 0.7321 is not a valid comparator and the comment now says so
in the imperative -- it was deepseek-v4-flash with no reasoning_effort, so a
delta against it measures the model swap, not the optimizer.

The 14 cells that finished before the pin existed wrote an empty
baseline_rewards, which leaves tau3 the one benchmark whose gain cannot be
derived from its own artifacts. backfill_baseline.py fills the field and pushes,
so the analysis script reads it the same way it reads every other benchmark
rather than carrying a tau3 special case. Provenance goes in a sibling file
because VerificationResult forbids extra keys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment on lines +180 to +183
# Optimizer-agent env (forwarded to the harbor claude-code agent as --ae KEY=VALUE).
# Claude Code's Bash tool caps a single call at BASH_MAX_TIMEOUT_MS (default
# 600000=10min), well under one inner eval, which pushed the officeqa optimizer
# into --detach + background-poll + end-turn -- and a headless --print run is

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Upstream credential reaches candidates

When an optimizer-generated candidate runs under this configuration, task_services_use_upstream places the raw upstream credential in its OPENAI_API_KEY while harness_user: null disables harness isolation, allowing the candidate to bypass the metered, model-allow-listed gateway and its spend controls. The Azure variant introduces the same combination. How this was verified: The configured credential was traced through the Harbor environment construction into the candidate process.

Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-engineering-bench/tau3/baseline/build.gpt54mini.yaml
Line: 180-183

Comment:
**Upstream credential reaches candidates**

When an optimizer-generated candidate runs under this configuration, `task_services_use_upstream` places the raw upstream credential in its `OPENAI_API_KEY` while `harness_user: null` disables harness isolation, allowing the candidate to bypass the metered, model-allow-listed gateway and its spend controls. The Azure variant introduces the same combination. **How this was verified:** The configured credential was traced through the Harbor environment construction into the candidate process.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing pattern, not introduced here: tau3's own build.yaml (already on main) has the identical task_services_use_upstream: true / harness_user: null, and so do swe-atlas-qna and browsecomp-plus. It's needed because tau3 runs its own in-container user-simulator LLM service, which needs the real credential to call out. Documented as a known, accepted tradeoff in skills/run-benchmark/SKILL.md ("Both are known, deferred, and recorded... Treat the boundary as 'an honest optimizer cannot reach the key by accident,' not 'an adversarial one cannot reach it at all'"). This PR's two new configs (build.gpt54mini.yaml, build.azure.yaml) just carry the existing pattern forward for the new target model -- not a new risk surface.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged — you're right that this pattern predates the PR. The new configs carry forward an existing, documented tradeoff rather than introducing a new risk surface, and the SKILL.md callout makes the boundary explicit. I'll withdraw the flag.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

Comment on lines +124 to +127
log = [l.split("\t", 2) for l in
git(repo, "log", "--all", "--format=%H\t%at\t%s").splitlines() if l.strip()]
log.reverse() # oldest first: the seed is index 0
for position, parts in enumerate(log):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Git history misidentifies candidates

When candidate versions contain multiple commits or independent histories, git log --all treats every reachable commit as a candidate and derives ordering from Git history rather than candidate creation metadata. This inflates candidate counts, corrupts shipped positions, and can label the wrong commit as the seed.

Prompt To Fix With AI
This is a comment left during a code review.
Path: harness-engineering-bench/skills/analyze-optimizer-commits/scripts/extract_candidates.py
Line: 124-127

Comment:
**Git history misidentifies candidates**

When candidate versions contain multiple commits or independent histories, `git log --all` treats every reachable commit as a candidate and derives ordering from Git history rather than candidate creation metadata. This inflates candidate counts, corrupts shipped positions, and can label the wrong commit as the seed.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

…y did

Squashed from four commits written while building and then running it once on
tau3: the initial version, a fix for a fabricated citation the first real run
caught in itself, a cut of two stages (blind lenses, completeness critic) that
turned out to reproduce what a 20-line script already gave for free, and a
restructure of the output into attributed tables instead of prose.

Benchmark-agnostic by construction, not just in name: both scripts take
--benchmark as an argument and walk runs/<benchmark>/ generically, with no
hardcoded target-repo path or model name anywhere in either. The tau3 mentions
in SKILL.md and lenses.md are calibration examples for the kind of finding
expected, explicitly labeled as such -- run it on officeqa or swe-atlas and it
reads their cells the same way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yash-scaleai
yash-scaleai force-pushed the tau3-baseline-and-skill branch from f2001ee to 22b87f6 Compare August 2, 2026 20:46
…ount assumption

Two issues from an automated review, both real.

Legacy extraction (Python <3.12, where filter="data" is a TypeError) passed
tarfile members straight to extractall with no path or symlink checks. Added
the same containment check filter="data" does, so a traversal member or a
symlink pointing outside dest is rejected on every Python version, not just
3.12+.

The candidate-count concern was that `git log --all` walks every commit
reachable from every ref, which is only a correct candidate enumeration if the
repo is one linear lineage. Tried the principled-looking alternative --
refs/vero/candidates/*, one ref per candidate -- and it silently undercounted:
two real, git-committed candidates in claude-opus-5-opencode-r1 have no ref at
all, because they were superseded before ever being submitted for scoring
(empty `scores` dict, which the analysis already reports as its own finding).
Switching would have quietly corrupted a report that's already been verified
and shared. Kept log --all, and instead made the assumption it depends on
explicit: checked all 16 tau3 repos by hand (zero merges, one root commit
each), and the script now checks that itself and refuses to guess on a repo
where it doesn't hold, rather than silently mis-attributing positions on some
other benchmark's history shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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