Redesign VeRO as a generic program optimization harness#41
Open
varunursekar wants to merge 67 commits into
Open
Redesign VeRO as a generic program optimization harness#41varunursekar wants to merge 67 commits into
varunursekar wants to merge 67 commits into
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
…knobs)
load_harbor_build_config resolves ${NAME} / ${NAME:-default} / ${NAME:?msg}
from --param (repeatable) layered over the environment, so run-time knobs —
optimizer model, inner sandbox provider, concurrency — vary without editing/
recompiling the task by hand. Untemplated fields stay fixed (the reproducible
measurement substrate). vero harbor run maps --model to the reserved
${optimizer_model}, so a build whose producer allowed_models references it keeps
codex's model and the gateway allow-list in lockstep (kills the 403 mismatch
that silently wedged GAIA). GAIA build parametrizes environment_name and the
producer model as the first users.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-list The per-scope model allow-list is role-differentiated, but it is not a hard guarantee that the target model stays fixed: an adversarial optimizer can smuggle its producer token into the candidate and reach the shared gateway's producer scope from the eval sandbox. Record this as a known, deferred limitation (only exploitable by a deliberately-cheating optimizer); the hard fix is per-role egress isolation, not path-scoping. Comment only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stage 1 of the native-runner modernization. Deletes the VeroAgentHook token-limit machinery (MaxTokenCountExceededError, the on_llm_start re-tokenization guard, the max_tokens field) — an approximate, input-only, mid-run-aborting limit that duplicated what loop config + a metered budget provide. Limits move to the orchestration boundary. Bumps openai-agents[litellm] from <0.14 to 0.18.3 to gain the SandboxAgent API (agents.sandbox: SandboxAgent/Manifest + unix-local/docker/modal/e2b clients), the substrate for the Stage 3 native rebuild. Full suite green on the bump (461 passed, 5 skipped); VeroAgent tests unaffected. (uv.lock secret-scan hit is a false positive: the sentry-sdk sdist SHA256.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…anitization Stage 3 (substrate). Rebuilds VeroAgent on the OpenAI Agents SDK SandboxAgent: the harness (model orchestration, tool dispatch, event streaming) runs on the host while shell/file effects run inside a sandbox via the SDK's Shell + Filesystem capabilities (real shell, apply_patch). The sandbox is bound to the candidate checkout's host dir via Manifest(root=...) + UnixLocalSandboxClient (validated: workspace_root_owned=False, execs land on the host dir, dir is not deleted), so mid-run evaluation and boundary capture operate unchanged. The sandbox client is the containment seam (swap to Docker/Modal/E2B later). default_tool_sets drops bash/file_read/file_write/grep/git_* (subsumed by the SDK capabilities); only vero-specific tools remain (evaluation, planning). Deleting those now-unused tool files + the filesystem ACL is a follow-up. Deletes run_agent_with_json_sanitization (an Anthropic invalid-JSON retry hack); VeroAgent and SubAgentTool now drive Runner.run_streamed + stream_events directly. Full suite green (461 passed, 5 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… hooks
Stage 3 cleanup. The SDK Shell/Filesystem capabilities now provide shell/file/
grep/git, so the custom coding tools and vero's in-process access-control are
dead weight:
- Delete tools/{bash,file_read,file_write,grep,git_control,version_control,
git_viewer,history_viewer}.py. Relocate the subprocess-termination-safety
helper run_bash_command to utils/asyncio.py (next to run_subprocess_with_tee).
- Remove the filesystem ACL as a security boundary: delete filesystem.py
(AccessType/AccessRule/WorkspaceAccessPolicy), strip the access layer from
workspace/base.py (keeping standalone path resolution), workspace/git.py, and
runtime/context.py's read-access configuration. Enforcement of "don't touch
the context dir" now rests on the instruction text, not fake in-process ACLs.
- Strip ClaudeCodeAgent's in-process hooks (the PostToolUse auto-commit +
PreToolUse bash blocklist + ACL-derived disallowed_tools + enable_hooks); keep
the agent and its cli/config wiring and permission_mode.
- Delete the per-write auto-commit (FileSystemWriteBase.run_and_commit): the
optimizer already commits the dirty tree at the generate boundary.
Fix tools/__init__ + sub_agent defaults to surviving tools; drop the ACL/tool
tests (test_filesystem, test_write_tools, Grep/FileRead/Bash suites in
test_tools). Full suite green (299 passed, 5 skipped).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eds it) Live smoke test (native optimizer on circle-packing) surfaced that SandboxAgent's Shell/Filesystem capabilities register *hosted* tools (apply_patch, shell) which the SDK only supports over the OpenAI Responses API, not ChatCompletions. The prior LitellmModel path raised "Hosted tools are not supported with the ChatCompletions API". Rebuild the template model on OpenAIResponsesModel with an AsyncOpenAI client pointed at OPENAI_BASE_URL (the LiteLLM proxy exposes /v1/responses for the OpenAI-family models it fronts). Default model openai/gpt-5.4, overridable via VERO_OPTIMIZER_MODEL or VeroAgent.for_model(). model_str() recognizes the new model type. Consequence: the native agentic path is OpenAI-family only (Responses + hosted tools). Non-OpenAI models would need a function-tool variant that drives the sandbox session directly, or go through Harbor. Documented; not implemented. Validated live: circle-packing 0.96 -> 2.167 (valid) with gpt-5-mini, mid-run self-eval and constraint-aware selection working end-to-end. Suite green (299 passed, 5 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ssion The live smoke test showed SandboxAgent's hosted Shell/Filesystem capabilities require the OpenAI Responses API, locking the native path to OpenAI-family models. Per the "one path, any provider" decision, replace the hosted capabilities with plain function tools (shell/read_file/write_file, new tools/sandbox_tools.py) that drive an SDK SandboxSession via session.exec. VeroAgent is now a plain Agent + LitellmModel (any provider) that creates a vero-managed SandboxSession bound to the candidate checkout (Manifest(root=host_path), UnixLocalSandboxClient by default; the client is the containment seam for docker/modal/e2b). Function tools work over ChatCompletions OR Responses, so no OpenAI lock-in and no dual code paths. Reverts the OpenAIResponsesModel default from the prior commit. Add orjson (litellm 1.92 imports it on its completion path). Validated live on circle-packing (native optimizer, 1 round): - openai/gpt-5-mini: 0.96 -> 2.167 (valid) - anthropic/claude-sonnet-4-5 (LiteLLM/ChatCompletions, non-OpenAI): 0.96 -> 1.686 (valid) — proving the single path is genuinely model-agnostic. Suite green (299 passed, 5 skipped). (uv.lock scan hit = sentry-sdk hash, false positive.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stage 2. Introduce a GenerationBackend abstraction so candidate production is a swappable unit — the native in-process producer (Optimizer default) or a Harbor run — behind one interface, without changing the working native path. - Promote the per-proposal production result to a public GenerationOutcome (optimization/models.py), documenting the eval boundary: its trial_evaluations are the *generation-time feedback* the producer observed while iterating, distinct from the orchestrator's separate selection/target scoring. - Add the GenerationBackend protocol (optimization/protocols.py): generate(proposal, parent, context, evaluation_records) -> GenerationOutcome. - Optimizer gains an optional generation_backend; the production step routes through it when set, else the built-in native flow (default None = unchanged behavior). The generation-time-vs-selection split already lived in the loop (scoped-gateway trial evals vs post-production evaluate()) and is now named. - harbor/generation.py: HarborGenerationBackend interface skeleton (full harbor-run-as-production wiring, reusing GitCandidateTransport, is a follow-on). - Test: the Optimizer routes production through an injected backend and still improves. Suite green (300 passed, 5 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stage 4. Add a stateful population strategy — the native runner's distinctive capability over Harbor's single-agent-as-optimizer. Each round it ingests the disclosed evaluations into a direction-aware fitness archive (via ObjectiveSpec), keeps the fittest population_size candidates, and emits num_offspring proposals whose parents are chosen by tournament selection; before any feasible candidate exists it seeds offspring from the current best (or baseline). Runs directly on the existing Optimizer (which already fans out N proposals/round concurrently) and the GenerationBackend seam — offspring can be produced natively or via Harbor. Fitness reads only disclosed views (EvaluationRecord/Summary objective values); target/none views carry no objective, so held-out scores never leak into selection. Only mutation is implemented; crossover (combine two parents) is a follow-on. CLI/config wiring to select this strategy is a follow-on too. Tests cover baseline seeding, tournament selection from the fittest population (infeasible excluded), and minimize-direction ranking. Suite green (303 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stage 5. Wire the coding agent's per-step activity into the structured event stream so tool calls, reasoning, and messages land in events.jsonl (and any registered sink, e.g. W&B) live — the native runner's introspection advantage over opaque environments. - agent_event_emitter(bus, session_id, agent): adapts an agent's normalized serialize_event() stream onto the runtime EventBus (kind="agent"). - OptimizationSession wires each agent producer's on_event to the bus (respecting a caller-provided hook); EventSink remains the pluggable observer point. - VeroAgent.run awaits awaitable on_event callbacks; serialize_event now also emits `thinking` (reasoning) events alongside message/tool_call/tool_result. Non-agentic producers were cut from this stage: for generic code, blind mutation/crossover operators produce garbage; the agentic producer is the unit of variation. Tests cover the emitter (normalized publish, async sinks, no-op for non-normalizing agents). Suite green (306 passed, 5 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Removes genuinely-unused symbols (not forward-looking extension points): - exceptions.py: the FileEditException hierarchy (NoFilesChangedError, InputTooLongError, FileNotTrackedError, StringNotFoundError) and CommitNotInBranchHistory — all raised only by the deleted file/git tools. Only StreamEventTimeout remains. - utils/tokens.py: get_token_count — orphaned when the token-limit hook was removed (run_result_to_messages kept; still tested/usable). - VeroAgent + ClaudeCodeAgent: unused introspection helpers with no callers and not required by the CodingAgent protocol (tool_set_enabled, orchestrator_tool_sets, sub_agent_tool_sets, sub_agents_enabled, reset_trace). Kept forward-looking/used surfaces: GenerationBackend + HarborGenerationBackend skeleton, the AgentEvent type vocabulary, AgentRequirements.host_visible_workspace. Verified by an AST occurrence scan (no removed symbol referenced elsewhere) and ruff (no unused imports). Suite green (306 passed, 5 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The matmul pair is a vero-tasks example (matmul-eval is a scale-vero-tasks task consumed by matmul-kernel's run.py), so it belongs with the task package, not in vero's core examples. Moved to vero-tasks/examples/; fixed matmul-eval's scale-vero-tasks source path (../../../vero-tasks -> ../..) and repointed the vero e2e test at the new location. Test still passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
harbor-circle-packing demonstrates Harbor driving a coding agent (outer loop) that edits packing.py, with a SIMPLE inner loop — a plain CommandBackend scorer (harness/evaluate.py), not a nested `harbor run`. This is the custom-sidecar- factory path (`vero harbor serve --factory circle_factory:build`), the counterpart to `vero harbor build` (which always nests a harbor eval). Includes the custom factory (CommandBackend + sidecar disclosure policies + CanonicalVerifier), the scorer, the target program, task.toml/instruction, and main+sidecar Dockerfiles that install scale-vero[harbor] (local-checkout install documented). Objective: sum_radii maximize s.t. valid==1 (baseline ~0.96, optimum ~2.635). Mirrors a pattern validated end-to-end earlier; requires Docker + a coding agent + model access to run (not covered by the unit test suite). Factory imports verified against the current API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The guide described a pre-v0.5 system (.veroaccess ACLs, Policy, VeroResources/
@resource/ResourceControl, core/scaffolds, old vero init tasks / run CLI) — all
removed. Rewrote it around the current model: authoring an evaluation as a
command harness ({workspace}/{request}/{report}, dict-of-metrics report) or a
scale-vero-tasks Python task; running via config (vero init/check/evaluate/run
--config) or one-shot (vero optimize --harness-root/--evaluate/--agent vero);
and the VeroAgent producer (SandboxAgent function tools, any provider via
LiteLLM, sandbox-as-boundary, EvolutionaryStrategy, Harbor for containment).
Points at the real examples (c-matmul, circle-packing, vero-tasks/examples,
harbor-circle-packing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The README was written at the CLI/config/concepts level that survived the refactor, so no claims were stale; these are additive updates: - coding-agent section: the vero agent runs on any provider via LiteLLM with its shell/file edits inside a sandbox bound to the checkout; model via VERO_OPTIMIZER_MODEL; pointer to the Harbor example for contained producers. - core concepts: add GenerationBackend; note EvolutionaryStrategy under OptimizationStrategy. - safety boundaries: add the sandbox-as-boundary bullet (containment is the sandbox, not in-process checks; Harbor for contained/untrusted). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Programs" reads narrowly (a single file), so the title and lead paragraph now name all three target kinds explicitly — a program (a function up to a whole codebase), text (prompt/spec/config), or an agent (scaffold, tools, prompts) — and note that agents are programs treated as a first-class target. Applied to both the repo-root and package READMEs; broadened the Candidate concept row to "a program, text, or agent". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…EADMEs Give the READMEs more visual life (inspired by the sia README), using GitHub-native mermaid instead of binary image assets: - badge row (arXiv, MIT, Python 3.11+) on both; - the optimization loop as a rendered mermaid flowchart (replacing ASCII); - an architecture mermaid in Core concepts showing the GenerationBackend seam (native VeroAgent+sandbox vs contained Harbor run) into the evaluator; - a Highlights table and a "just want to try it?" callout to the Quickstart. No PyPI/CI badges: there is no CI workflow, and the `scale-vero` name on PyPI is an unrelated placeholder, so a version badge would be misleading. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `scale-vero` name on public PyPI is an unrelated empty placeholder, so installing it does not get VeRO (and is a dependency-confusion foothold). Switch all documented installs to a local checkout: - README quickstart / W&B / harbor sections use `uv sync --extra ...` + `uv run vero`, with an explicit "do NOT pip install scale-vero" note. - The harbor example vendors the package (COPY vero /opt/vero + install from the build context) instead of pip-installing the placeholder; README documents the `cp -r <repo>/vero environment/vero` step and .gitignore excludes the copy. The generated program-opt-bench/.../compiled bundle inlines vero/README.md and will pick up the fix when recompiled. Reclaiming the PyPI name (PEP 541) is a separate follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable running the VeRO Harbor optimizer as Claude Code with corpus-analysis subagents baked into its workspace, and make the inference gateway provider-agnostic. - inference gateway: drop the hardcoded endpoint allow-list for a path-traversal guard (light passthrough to the upstream litellm proxy); accept the scope token via x-api-key (Anthropic) or Authorization: Bearer (OpenAI). Metering already handled both OpenAI and Anthropic usage shapes. - workspace_overlays: general build field to bake arbitrary files/dirs into the compiled task's /work/agent (git-excluded so injected tooling doesn't pollute candidate diffs). - claude: _compiled_run_environment sets ANTHROPIC_API_KEY/BASE_URL at the producer scope (base root, /v1 stripped for the Anthropic SDK); seed + overlay-gated instruction nudge. - vero harbor run --env-file: load run secrets from a dotenv file, applied before the compile-time credential check and kept off the command line. - insights-bundle example: Insights Generator reproduced as Claude Code subagents (insights-generator/ig-scout/ig-investigator) + skill + scripts, grounded against the real .vero Harbor trace layout. - GAIA baseline: inject the bundle via overlay; secrets.env.example template. 39 harbor unit tests pass. Validated end-to-end on a full GAIA run (claude-sonnet-4-6 optimizer; held-out test 0.636 vs 0.576 baseline). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The README was dense and buried Harbor as a ### subsection under "Python API" (~line 480), opening with "Harbor is also an evaluation backend." - Reframe the intro + Highlights around Harbor as the recommended backend. - Add a "Backends" overview near the top: a Harbor-first table with a one-command `vero harbor run` teaser and jump links, so Harbor is reachable immediately. - Promote Harbor to a top-level "Optimize an agent with Harbor" section and move it directly after Quickstart (the first backend section). - Rewrite that section from a ~200-line prose wall into scannable pieces: an architecture diagram, numbered build/run steps, a build-knobs table, graded boundary bullets, and a security callout. Deep operational minutiae and the low-level HarborBackend API move into collapsible <details> blocks. - Note the gateway now forwards any inference endpoint (matches the provider-agnostic passthrough), and demote the command backend to "the lightest path when you don't need containment." All internal anchors resolve; <details> and code fences balanced. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Same digestibility pass on the rest of the dense sections: - Command-harness section: annotate the vero.toml inline so it's self- documenting, trim the surrounding prose, and collapse the retry / error- threshold / aggregation details into a <details> block. - Core concepts: rewrite the disclosure/`.vero` prose wall as scannable full/aggregate/none bullets plus two tight paragraphs. Anchors resolve; <details> and code fences balanced. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…runs
The Harbor sidecar's evaluation sub-run silently produced 0 trials for any
task that declares environment variables. tau3 declares them for its
user-simulator (TAU2_USER_*) and NL-assertion grader (TAU2_NL_ASSERTIONS_MODEL),
which makes `harbor run` prompt "Proceed? (Y/n)"; with no stdin the sub-run
aborts (returncode=1). That collapsed to a generic "no matching trials" error
and a budget refund, indistinguishable from a real miss. GAIA declares none,
so it was the only benchmark that ever worked.
- backend.py: pass `--yes` on the sub-run (`_command()`). It is always
non-interactive, so this is the one correct value; the real credential
control is `_environment()` (eval-scope token), not the human prompt.
- backend.py: enrich the failure message with found trial-group keys + exit
code so "produced nothing" is distinguishable from "produced non-matching
trials" — this is what surfaced the root cause.
- tau3 agent.py: forward OPENAI_API_KEY/OPENAI_BASE_URL into the in-env
runner exec (the runner is a fresh process; only the host agent had creds).
- tau3 + swe-atlas build.yaml: template the producer allow-list as
${optimizer_model:-gpt-5.4} so a non-default optimizer isn't 403'd.
Verified: a claude-sonnet shakedown ran its first dev eval, charged 3/6
cases (a failed eval refunds), with zero eval errors and no prompt aborts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A stateful OptimizationStrategy in the style of the Darwin Godel Machine (arXiv 2505.22954). Unlike EvolutionaryStrategy's fittest-K tournament, it keeps the *entire* archive as candidate parents and samples parent p with weight score(p) / (1 + children(p)) — every archived agent stays reachable as a stepping stone, while performance and inverse-children bias selection toward promising, under-explored lineages. Defaults to a "self" producer (each offspring is the parent's own harness modifying itself) — the self-referential loop the Godel Machine wanted but validated empirically. Held-out target scores never enter selection (they carry no objective in context.evaluations). 5 tests: seed-from-baseline, whole-archive reachability (incl. low/unscored), inverse-children downweighting, cross-round child tracking, minimize direction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…orward
A direct probe proved a stock in-container MCP agent solves tau3 (claude-code
scored reward 1.0), so the bespoke two-piece design (host shim + uploaded
runner.py + os.environ cred-forward) was over-built. The tau3-runtime MCP
server is stateful but HEADER-keyed (Mcp-Session-Id), and the session persists
across independent requests — verified by probing the FastMCP endpoint. So no
persistent in-container process is needed.
Rewrite Tau3Agent as a single class whose run() is the whole loop, host-side:
- target-model calls happen host-side via the OpenAI responses API (keeping
previous_response_id caching); creds come straight from os.environ, which
Harbor already populates in the agent process (HarborBackend._environment) —
the cred-forward hack only ever failed because it targeted a separate
in-container process.
- each MCP call is an environment.exec("curl … tau3-runtime:8000/mcp") into
main, threading the session id; payloads are base64-encoded so arbitrary
tool arguments survive the shell.
Deletes runner.py, the in-container mcp/openai install, and the upload/exec
dance. Declares openai as a target dep (host-side import) and relocks. Rewrote
the tests for the new surface (session/JSON-RPC parsing, curl-based tool calls,
full loop with a fake env + stubbed client). Verified live: new agent scores
reward 1.0 on tau3-airline-0 (docker), 6 turns, ~79% prompt-cache hit, no
credential or MCP errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In black-box optimization you expect solution quality to rise with budget; vero (and the paper) found it doesn't. The hypothesis is that budget-*awareness* changes the solver agent's behavior — given more budget it re-plans (front-loads, paces, treats "exhaust the budget" as the goal) rather than simply running the same per-step policy for longer, so quality is flat/non-monotone in budget. Testing that requires the ability to hide budget from the optimizer while still enforcing it. Add `disclose_budget` (default True = current behavior) as a master switch over every channel that leaks budget signal to the agent: - instruction template: the three always-on budget mentions are now gated, and the existing instruct_multifidelity / instruct_exhaust_budget blocks are AND-ed with it (both are inherently budget-aware), so disclose_budget=False renders zero budget language. - sidecar `/status`: `evaluation_access[].budget` and `inference_usage` (remaining requests/tokens) are omitted when disclosure is off — this closes the live quantitative leak (`vero harbor status`), which had no toggle before. Enforcement/metering are untouched: the budget is still charged and the run still stops when it's exhausted; the agent just can't see or reason about it. The budget-blind experiment arm is disclose_budget=False + instruct_multifidelity=False + instruct_exhaust_budget=False. Plumbed build config -> compiler (instruction + serve.json) -> deployment -> EvaluationSidecar, mirroring submit_enabled. Tests: instruction renders 0 budget mentions and serve.json carries the flag when off; /status omits budget and inference usage when off. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-sim/grader) tau3's environment makes its own LLM calls — the user-simulator (inside the task container) and the NL-assertions grader (verifier) — which failed with "Connection error" under vero: the inference gateway is a compose-internal host (inference-gateway:8001) reachable from the optimizer's `main` (where the agent runs host-side) but NOT from the modal task containers where those eval services live. GAIA never hit this (its agent calls the LLM host-side; it has no in-task LLM service). Add `task_services_use_upstream` (HarborBuildConfig -> compiler -> HarborBackend). When set, the eval sub-run's `_environment` points OPENAI_* at the real upstream (read from the VERO_INFERENCE_UPSTREAM_* vars vero already injects into `main`), so the task's user-sim/grader reach a real provider; the candidate agent keeps its metered, model-allow-listed gateway on dedicated VERO_AGENT_INFERENCE_* vars. This preserves the fixed-target-model control the budget-scaling experiment depends on — the agent can't switch models — while unblocking the eval services. `task_environment` injects the TAU2_USER_MODEL / TAU2_NL_ASSERTIONS_MODEL the tasks otherwise default to gpt-5.2 (not on our proxy). Tau3Agent reads VERO_AGENT_INFERENCE_* for its client when present (falls back to OPENAI_*). tau3 build.yaml enables the flag + sets both TAU2 models to gpt-5.4-mini. 375 registry task.tomls are unpatchable (downloaded per eval), so the fix lives entirely in vero's env routing. Tests: backend _environment routing (default gateway vs upstream mode) + its validation; compiler plumbs the flags into serve.json; Tau3Agent prefers the dedicated gateway creds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…use_upstream
The first cut of task_services_use_upstream read VERO_INFERENCE_UPSTREAM_* from
the eval process env, but those are deliberately scrubbed to "" in both `main`
and the eval-sidecar (only the inference-gateway container holds the raw upstream)
— so the tau3 user-sim/grader flipped from "Connection error" to "Missing
credentials". Evals actually execute in the trusted eval-sidecar, so expose the
real upstream to *that* container (like the gateway service) when
task_services_use_upstream is set. The optimizer/main still sees these scrubbed to
"", so the candidate never gains raw upstream access — isolation preserved.
docker-compose.yaml.j2: add gateway_environment (VERO_INFERENCE_UPSTREAM_*) to the
eval-sidecar env under `{% if task_services_use_upstream %}`; compiler passes the
flag into the template context. Test asserts the sidecar gets the upstream while
main keeps it empty.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
|
Pushed a batch of work onto tau3 benchmark now runs end-to-end
Budget-blindness
Strategy
Full vero test suite green (327 passed, 5 skipped). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
vero.tomlworkflow, baseline-onlyvero evaluate, repeatablevero run, and a complete CLI for objectives, constraints, case selection, refs, environments, limits, and concurrencypaper/v1and thepaper-v1tagHarbor and Modal
harbor runjobsharbor[modal]==0.18.0and select an explicit inner Python versionHardening
database.jsonfrom canonical completed evaluation directories on resume, closing the write-order crash windowrefs/vero/...refs without leaking normal branchesValidation
uv lock --checkgpt-5) run completed the full nested workflow on Modal: status, candidate validation through inner Harbor, submission, trusted hidden final evaluation through inner Harbor, and outer reward 1.0 with no exceptionsThe final live topology is:
Harbor (Modal, Codex) -> VeRO sidecar -> Harbor validation (Modal) -> submit -> VeRO verifier -> Harbor hidden final (Modal)Greptile Summary
This PR replaces the paper-era VeRO implementation with a fully generic program optimization harness: backend-neutral candidate/evaluation/optimization/runtime interfaces, a declarative
vero.tomlworkflow, Harbor/Modal integration as both outer task and inner evaluation backend, and hardened worktree, budget, and session lifecycle management.EvaluationEnginewith authorization resolver, discriminated-union optimizer config,asyncio.to_threadfor all durable writes (engine, budget, sidecar, session), andasyncio.TaskGroup-based parallel candidate production replacing the previousasyncio.gatherapproach.GitCandidateTransportfetches via unique temporary refs to avoidFETCH_HEADraces, retains commits under hiddenrefs/vero/…refs, and validates all paths and symlinks before extraction in the build compiler.Optimizer._produce_candidate, thefinally: await candidate_workspace.destroy()is not wrapped withasyncio.shield, so whenTaskGroupcancels sibling tasks the git worktree removal can be interrupted mid-execution, leaving worktrees registered and on disk — unliketemp_copy, which already usesasyncio.shieldfor the same cleanup.Confidence Score: 4/5
Safe to merge with the worktree cleanup fix; all other hardening, atomicity, and security changes look correct.
The entire new evaluation/optimization/harbor stack is well-structured and the previously-flagged blocking-write issues are resolved with asyncio.to_thread throughout. One gap remains: Optimizer._produce_candidate calls await candidate_workspace.destroy() in a bare finally block that is not shielded, so a TaskGroup cancellation during parallel production can abort the git worktree teardown mid-way, leaving worktrees registered on disk. All other cleanup paths (temp_copy, budget reservation, session manifest) are already shielded consistently.
vero/src/vero/optimization/optimizer.py — the _produce_candidate finally block needs asyncio.shield on destroy().
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant CLI as vero CLI participant Session as OptimizationSession participant Optimizer as Optimizer participant TG as asyncio.TaskGroup participant Producer as CandidateProducer participant Workspace as GitWorkspace copy participant Engine as EvaluationEngine participant Backend as EvaluationBackend CLI->>Session: run() Session->>Optimizer: run(baseline) Optimizer->>Engine: evaluate_candidate(baseline) Engine->>Backend: evaluate(request) Backend-->>Engine: EvaluationReport Engine-->>Optimizer: EvaluationRecord baseline loop max_rounds Optimizer->>Optimizer: strategy.propose(context) Optimizer->>TG: create tasks parallel proposals TG->>Workspace: copy from_version parent.version TG->>Producer: produce workspace evaluation_gateway Producer->>Engine: evaluate_current optional checkpoints Engine-->>Producer: EvaluationRecord Producer-->>TG: CandidateChange Note over TG,Workspace: finally await destroy not shielded TG->>Engine: evaluate_candidate new_candidate Engine->>Backend: evaluate request Backend-->>Engine: EvaluationReport Engine-->>Optimizer: EvaluationRecord end Optimizer-->>Session: OptimizationResult Session-->>CLI: best candidate%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant CLI as vero CLI participant Session as OptimizationSession participant Optimizer as Optimizer participant TG as asyncio.TaskGroup participant Producer as CandidateProducer participant Workspace as GitWorkspace copy participant Engine as EvaluationEngine participant Backend as EvaluationBackend CLI->>Session: run() Session->>Optimizer: run(baseline) Optimizer->>Engine: evaluate_candidate(baseline) Engine->>Backend: evaluate(request) Backend-->>Engine: EvaluationReport Engine-->>Optimizer: EvaluationRecord baseline loop max_rounds Optimizer->>Optimizer: strategy.propose(context) Optimizer->>TG: create tasks parallel proposals TG->>Workspace: copy from_version parent.version TG->>Producer: produce workspace evaluation_gateway Producer->>Engine: evaluate_current optional checkpoints Engine-->>Producer: EvaluationRecord Producer-->>TG: CandidateChange Note over TG,Workspace: finally await destroy not shielded TG->>Engine: evaluate_candidate new_candidate Engine->>Backend: evaluate request Backend-->>Engine: EvaluationReport Engine-->>Optimizer: EvaluationRecord end Optimizer-->>Session: OptimizationResult Session-->>CLI: best candidatePrompt To Fix All With AI
Reviews (6): Last reviewed commit: "Document nested Modal Harbor workflow" | Re-trigger Greptile