.vally.yaml Configuration
The .vally.yaml file defines project-level configuration for eval discovery, named environments, eval suites, and plugins. Place it at your project root.
Minimal Example
Section titled “Minimal Example”paths: evals: evals/Configures where to find skills, evals, and results.
| Field | Type | Default | Description |
|---|---|---|---|
skills |
string or string[] |
["skills/"] |
Directories containing SKILL.md files |
evals |
string or string[] |
["evals/"] |
Directories to scan for eval files |
results |
string |
"vally-results/" |
Directory for output results |
evalFilenames |
string or string[] |
["eval.yaml", "eval.yml"] |
Filename patterns for eval discovery (supports globs) |
Custom Filename Patterns
Section titled “Custom Filename Patterns”paths: evals: [evals/, tests/evals/] evalFilenames: ["eval.yaml", "eval.yml", "*.eval.yaml"]suites
Section titled “suites”Named groups of evals for running specific subsets. Each suite must have at least one of filter or evals.
Filter-Only Suite
Section titled “Filter-Only Suite”Select stimuli by tags across all discovered evals:
suites: fast: description: "P0 tests for inner loop" filter: priority: p0Filter semantics: AND across keys, OR within values. Suite filter: blocks are include-only — the --tag key!=value exclusion form is available only on the CLI, and a bare key like flaky! here is treated as a literal tag key, not a negation.
Evals-Only Suite
Section titled “Evals-Only Suite”Scope to specific eval files by path or glob:
suites: safety: description: "All safety evals" evals: - "evals/safety/**/*.eval.yaml" - "evals/shared/baseline.eval.yaml"Combined Suite
Section titled “Combined Suite”Scope by file path, then filter by tags:
suites: safety-p0: evals: ["evals/safety/**/*.eval.yaml"] filter: { priority: p0 }File scoping narrows which eval files are loaded. Tag filtering narrows which stimuli within those files run.
Path casing
Section titled “Path casing”Paths written in suite or experiment evals patterns and --eval-filter must match on-disk
casing. The project or experiment directory used to resolve relative patterns follows the
filesystem’s casing rules.
Metadata
Section titled “Metadata”Root metadata provides passive, JSON-compatible defaults to integrations such as reporter plugins:
metadata: dashboard: project: agent-platform owners: [evals]
suites: nightly: evals: ["evals/**/*.eval.yaml"] metadata: dashboard: project: nightly-evals cadence: nightlyMetadata does not affect eval discovery or stimulus filtering. Reporters receive the effective metadata in
PlanRunStartContext.metadata. When a named suite is selected, its metadata is shallow-merged over the root
metadata, replacing matching top-level keys. The selected suite and the same effective metadata are also available
in PlanRunStartContext.suite.
Namespace integration-specific keys to avoid collisions. Do not store secrets in metadata. Reporter plugins may persist or display the values.
environments
Section titled “environments”Named agent environments for reuse throughout an eval spec via its agent_environment field. These
blocks are agent-visible setup; grader-only files belong in the eval spec’s
grading_environment block and cannot use a named reference.
environments: auth-workspace: skills: - skills/auth files: - src: fixtures/users.json dest: test-data/users.json commands: - npm installReference in eval specs by name:
agent_environment: auth-workspacestimuli: - name: login-test prompt: "Test the login flow"Place the fields shown below directly under environments.<name> as shown above.
Agent environment fields
skills: - ./path/to/my-skill # Skill directory (containing SKILL.md)files: - src: fixtures/input.txt dest: input.txt - src: fixtures/test-data # directories are copied recursively dest: test-data - src: fixtures/session-log # staged outside the graded workspace dest: session-state/session-1 dest_root: assetscommands: - npm installcommandTimeout: 2mgit: type: worktree ref: v2.1.0 source: ../my-repomcpServers: db: type: stdio command: db-serve args: ["--port", "5432"] api: type: http url: http://localhost:3000/mcpenv: COPILOT_AGENT_ACTION: review LOG_LEVEL: debug| Field | Type | Description |
|---|---|---|
commands |
string[] |
Shell commands to run during setup (uses /bin/sh on Unix, cmd.exe on Windows) |
commandTimeout |
duration |
Per-command timeout for commands (e.g. 2m, 90s). Must be positive (0 is rejected). Defaults to 60s |
env |
Record<string, string> |
Environment variables set on the agent process (see below) |
files |
{src, dest, dest_root?}[] |
Files or directories to copy during setup (see below) |
git |
object |
Git configuration for fixture data — local worktree or remote clone (see below) |
mcpServers |
Record<string, McpServerConfig> |
Named MCP servers to start or connect to |
skills |
string[] |
Paths to skill directories (each containing a SKILL.md) to load |
Staging files
Each files entry copies src to a destination chosen by dest_root:
| Field | Type | Required | Description |
|---|---|---|---|
src |
string | Yes | File or directory to copy, relative to the eval file’s directory (or absolute) |
dest |
string | Yes | Destination path. Under dest_root: assets it must stay inside the assets root once normalized (no absolute paths, and no traversal that ends up above the root); under the default workdir it is resolved relative to the workspace root |
dest_root |
"workdir" | "assets" |
No | Which root dest is resolved under. Defaults to workdir |
dest_root: workdir(default) — the file lands inside the graded workspace, exactly as before. It stays out of diffs — the workspace baseline is captured after setup — but it is a real file inworkDir, so tree- and file-scanning graders (file-exists,file-contains, …) do see it.dest_root: assets— the file is staged into a managed, per-trial assets directory that sits outside the graded workspace. Nothing is copied intoworkDir, so neither diff- nor tree-scanning graders see it, and the directory is cleaned up automatically at the end of the trial (no manual delete step). Use this for setup inputs that must exist on disk but should not contaminate the graded tree — recorded session logs to seed a session, or large fixtures a grader reads.
The assets directory is exposed as the EVALUATE_ASSETS environment variable (see
Agent env) and to graders as trajectory.assetsDir (the program grader also receives
it as EVALUATE_ASSETS). It is a host path allocated fresh per trial; treat it as absent when
re-grading a stored trajectory on a different host.
Harness customization files
Use files to stage agent customizations at the location expected by the selected executor:
GitHub Copilot
files: - src: ./agents/reviewer.agent.md dest: .github/agents/reviewer.agent.mdClaude Code
files: - src: ./agents/reviewer.md dest: .claude/agents/reviewer.mdBoth harnesses discover staged profiles natively — Vally does not parse agent files, so the full frontmatter each harness supports works as documented upstream. Staging the file is the entire setup: there is no flag to turn on and no agent field in the eval spec. For Copilot, a Git-backed workspace with a GitHub remote may additionally expose organization or enterprise agents to the authenticated identity, so the available roster can differ between runners.
Neither harness replaces the default root agent. A matching profile is invoked as a model-routed subagent: the harness compares the stimulus against each profile’s name and description and decides whether to delegate. Write a stimulus that clearly matches the profile you want to exercise.
Because that routing is a model decision, gate it with a grader rather than assuming it happened.
For Copilot, given an agent file whose frontmatter sets name: Reviewer:
stimuli: - name: delegates-to-reviewer prompt: "Delegate this to the Reviewer specialist and report its findings." graders: - type: tool-calls config: required: - name: "^task$" args: agent_type: "^Reviewer$"The eval now fails when the agent is never used. agent_type is the agent’s display name: the
frontmatter name when present, otherwise the filename stem — a reviewer.agent.md with no name
is matched as ^reviewer$. Argument names are harness-specific, so inspect a recorded trajectory
before writing the matcher for another executor. Subagent events also carry an agentId, which
grader-level scope can target to assert what the subagent
actually did, not just that it ran.
Agent env
The env field sets environment variables on the agent process for every
run of this environment. The built-in copilot-sdk executor and custom
process-spawning executors that support environment variables merge these over
the inherited environment. The built-in mock executor has no agent process
and ignores env; Vally prints a warning listing the ignored variable names so
the misconfiguration is visible.
env: COPILOT_AGENT_ACTION: review LOG_LEVEL: debug| Field | Type | Description |
|---|---|---|
env |
Record<string, string> |
Variable name → value, set on the agent process. In experiment variant overrides, values support ${…} interpolation like other override fields. |
Git config
The git field sets up the evaluation workspace from a Git repository. It has two
modes, discriminated by type:
worktree— check out a ref from a local repository as a detached worktree.clone— clone a remote repository at a given ref into the workspace.
type: worktree
git: type: worktree ref: v2.1.0 source: ../my-repo timeout: 10m # optional; raise for very large reposcommands: - dotnet restore| Field | Type | Required | Description |
|---|---|---|---|
type |
"worktree" |
Yes | Must be "worktree" |
ref |
string | Yes | A commit-ish value (tag, commit SHA, or branch name) to check out |
source |
string | Yes | Path to the local repo used as the worktree source |
timeout |
duration |
No | Timeout for the local git steps that materialize the worktree (add, remove), e.g. 10m. Must be positive. Defaults to 5m |
type: clone
git: type: clone url: https://github.com/octocat/hello.git ref: v2.1.0 # optional; defaults to the remote's default branch shallow: true # optional; true → depth 1, or an explicit integer depth sparse: # optional; cone-mode paths to materialize - src/core timeout: 10m # optional; local clone steps only (not the network fetch)| Field | Type | Required | Description |
|---|---|---|---|
type |
"clone" |
Yes | Must be "clone" |
url |
string | Yes | Remote repository URL to clone: http(s), ssh, git, file://, or scp-style git@host:path |
ref |
string | No | Commit-ish to check out; defaults to the remote’s default branch |
shallow |
boolean | integer | No | Shallow-clone history: true fetches depth 1, an integer sets an explicit depth |
sparse |
string[] | No | Sparse-checkout (cone mode) paths — only these directories are materialized |
timeout |
duration |
No | Timeout for each local git step (init, remote add, sparse-checkout, checkout), e.g. 10m. Does not cap the network fetch. Must be positive. Defaults to 5m |
HTTP(S) clone URLs must not contain userinfo, query/fragment parameters, or backslashes, and SSH URLs must not contain passwords. Use host Git authentication such as Git Credential Manager, an SSH agent, or GIT_ASKPASS.
MCP server config
Each entry in mcpServers is either a stdio server (launched as a child process) or a remote server (connected over HTTP/SSE).
The copilot-sdk executor supports MCP sampling (model requests made by an MCP server)
automatically, with no additional configuration. These requests count toward the trial’s
time budget and can incur additional model usage.
Stdio (child process)
mcpServers: db: type: stdio command: db-serve args: ["--port", "5432"] env: DB_HOST: localhost cwd: ./services/db timeout: 5000| Field | Type | Required | Description |
|---|---|---|---|
type |
"stdio" |
Yes | Launch as a child process |
command |
string | Yes | Executable to run |
args |
string[] | No | Arguments passed to the command |
env |
Record<string, string> |
No | Extra environment variables for the child process |
cwd |
string | No | Working directory for the child process |
timeout |
number | No | Timeout in milliseconds for connecting to / invoking the server |
Remote (HTTP/SSE)
mcpServers: api: type: http # or "sse" url: http://localhost:3000/mcp headers: Authorization: "Bearer ${API_TOKEN}" timeout: 10000| Field | Type | Required | Description |
|---|---|---|---|
type |
"http" | "sse" |
Yes | Connect to a remote server |
url |
string | Yes | Server endpoint URL |
headers |
Record<string, string> |
No | Extra HTTP headers (e.g. auth tokens) |
timeout |
number | No | Timeout in milliseconds for connecting to / invoking the server |
plugins
Section titled “plugins”Loads plugins for commands operating on the project. Package names and local paths are supported;
local paths resolve relative to the directory containing .vally.yaml.
plugins: grader: - ./plugins/grader.mjs executor: - ./plugins/executor.mjs reporter: - ./plugins/reporter.mjs eval_provider: - ./plugins/eval-provider.mjs backend: - ./plugins/backend.mjs
defaults: eval_provider: custom-eval-provider backend: name: custom-backend args: [pool=research]plugins lists the modules to load and register with Vally. All fields are additive lists, and
duplicate modules are loaded once. Loading makes implementations available; where selection is
required, it happens in the configuration that uses them. For example, an eval selects an executor
through defaults.executor and a grader through a stimulus’s graders entries.
The matching grader, executor, and reporter CLI options add modules. --eval-plugin replaces both
project eval-provider loading and defaults.eval_provider. --backend and --backend-args replace
project backend loading and the entire defaults.backend selection.
For vally eval, defaults.eval_provider requires --eval-spec and cannot be used with --suite.
| Command | Project plugins used |
|---|---|
eval |
grader, executor, reporter, eval_provider, backend |
experiment run |
grader, executor, reporter, eval_provider, backend |
grade, oracle, lint, export |
grader |
When eval receives explicit eval files from multiple projects, additive plugins are loaded from
each owning project. The run can use only one backend. If --backend is omitted, all projects must
select the same defaults.backend name and arguments. A project without a backend selection uses
local, so it conflicts with a project that selects a custom backend. Vally reports these conflicts
before starting the run. Pass --backend to choose one backend for all projects.
Complete Example
Section titled “Complete Example”paths: skills: [skills/] evals: [evals/] results: vally-results/ evalFilenames: ["eval.yaml", "eval.yml", "*.eval.yaml"]
metadata: dashboard: project: vally owner: eval-platform
environments: default-workspace: skills: [skills/copilot] commands: [npm install] mcpServers: db: type: stdio command: db-serve args: ["--port", "5432"]
suites: ci-gate: description: "Fast checks for every PR" metadata: dashboard: project: vally-ci filter: priority: [p0, p1]
safety: description: "All safety evals" evals: ["evals/safety/**/*.eval.yaml"]
safety-p0: description: "Critical safety evals only" evals: ["evals/safety/**/*.eval.yaml"] filter: { priority: p0 }
plugins: grader: - ./plugins/project-graders.mjs executor: - ./plugins/project-executor.mjs reporter: - ./plugins/ci-reporter.mjs backend: - ./plugins/project-backend.mjs
defaults: backend: name: project-backend args: [pool=research]Schema Validation
Section titled “Schema Validation”The JSON schema is at packages/core/src/config/vally-config.schema.json. For VS Code autocompletion, add to settings:
{ "yaml.schemas": { "./packages/core/src/config/vally-config.schema.json": ".vally.yaml" }}