Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,9 @@ repos:
language: script
files: ^(harness/|docs/|hack/lint-agent-docs)
pass_filenames: false
- id: lint-measurements
name: lint eval measurement manifests
entry: ./eval/lint-measurements.sh
language: script
files: ^(eval/measurements/|eval/lint-measurements)
pass_filenames: false
6 changes: 4 additions & 2 deletions LOCAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,10 @@ fullsend run triage \
## Functional eval tests

The `eval/` directory contains functional test scenarios that run agents
against ephemeral GitHub repos and score the results. See
[eval/README.md](eval/README.md) for setup and usage.
against ephemeral GitHub repos and score the results, plus default
online-scoring manifests under [`eval/measurements/`](eval/measurements/README.md)
consumed by `fullsend eval-measure`. See [eval/README.md](eval/README.md)
for setup and usage.

To run triage evals:

Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ script-test:
$(call run-timed,bash scripts/validate-output-schema-test.sh)
$(call run-timed,bash scripts/gitlint-forbidden-type-scope-test.sh)
$(call run-timed,bash hack/lint-agent-docs-test.sh)
$(call run-timed,bash eval/lint-measurements-test.sh)
$(call run-timed,bash .github/scripts/check-e2e-authorization-test.sh)
$(call run-timed,bash .github/scripts/select-eval-agents-test.sh)
$(call run-timed,python3 scripts/process-fix-result-test.py)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ schemas/ JSON Schema for validating agent structured output
scripts/ Pre-scripts (input validation) and post-scripts (forge mutations)
skills/ Reusable skill definitions loaded by agents at runtime
plugins/ Sandbox plugins (e.g. gopls LSP for the code agent)
eval/ Functional eval harness and default online-scoring manifests
```

## Architecture
Expand Down
14 changes: 14 additions & 0 deletions eval/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ running:

```bash
bash eval/lint-cases.sh <agent>
bash eval/lint-measurements.sh
```

## Prerequisites
Expand Down Expand Up @@ -136,3 +137,16 @@ Each test case follows this lifecycle:
- **`checkStatus` drops string errors.** fullsend's `checkStatus` does
not handle string-typed error responses from the GitHub API, causing
silent failures.

## Measurement manifests (online scoring)

Per-agent manifests under [`eval/measurements/`](./measurements/) are the
**default online-scoring policy** for stock agents (which scorers run after
managed jobs via `fullsend eval-measure`). They are **not** functional PR-gate
scenarios under `eval/<agent>/`.

Scorer *implementations* live in `fullsend-ai/fullsend`; this repo only
declares defaults. Jobs fetch these files from `agents@v0` unless a consumer
overrides under `FULLSEND_DIR`. See [`eval/measurements/README.md`](./measurements/README.md)
and [fullsend#6036](https://github.com/fullsend-ai/fullsend/pull/6036) (ADR 0087
lands with that PR).
138 changes: 138 additions & 0 deletions eval/lint-measurements-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env bash
# lint-measurements-test.sh — Tests for eval/lint-measurements.sh
#
# Run from the repo root:
# bash eval/lint-measurements-test.sh

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LINTER="${SCRIPT_DIR}/lint-measurements.sh"

FAILURES=0
WORKDIR="$(mktemp -d)"
trap 'rm -rf "${WORKDIR}"' EXIT

VALID_YAML='---
agent: code
measurements:
- id: em-001
scorer: trace_fitness
version: 1
'

# run_case NAME YAML EXPECTED_EXIT [EXPECTED_OUTPUT_SUBSTRING] [FILENAME]
run_case() {
local name="$1" yaml="$2" expected_exit="$3" expected_substring="${4:-}" filename="${5:-code.yaml}"

local case_dir="${WORKDIR}/${name}"
mkdir -p "${case_dir}/eval/measurements" "${case_dir}/agents"
printf '%s\n' "# Code Agent" > "${case_dir}/agents/code.md"
if [[ -n "$yaml" ]]; then
printf '%s' "$yaml" > "${case_dir}/eval/measurements/${filename}"
fi

local output
local actual_exit=0
output="$(REPO_ROOT="${case_dir}" MEASUREMENTS_DIR="${case_dir}/eval/measurements" AGENTS_DIR="${case_dir}/agents" "${LINTER}" 2>&1)" || actual_exit=$?

if [[ "${actual_exit}" != "${expected_exit}" ]]; then
echo "FAIL: ${name} (exit ${actual_exit}, expected ${expected_exit})"
echo "${output}" | sed 's/^/ /'
FAILURES=$((FAILURES + 1))
return
fi

if [[ -n "${expected_substring}" ]] && [[ "${output}" != *"${expected_substring}"* ]]; then
echo "FAIL: ${name} (missing expected output: '${expected_substring}')"
echo "${output}" | sed 's/^/ /'
FAILURES=$((FAILURES + 1))
return
fi

echo "PASS: ${name}"
}

run_case "valid-manifest-passes" \
"${VALID_YAML}" 0 "code.yaml: OK"

run_case "unknown-scorer" \
"---
agent: code
measurements:
- id: em-001
scorer: trace-fitness
version: 1
" 1 "unknown scorer 'trace-fitness'"

run_case "unknown-agent" \
"---
agent: not-an-agent
measurements:
- id: em-001
scorer: trace_fitness
version: 1
" 1 "has no agents/not-an-agent.md" "not-an-agent.yaml"

run_case "filename-agent-mismatch" \
"---
agent: review
measurements:
- id: em-001
scorer: trace_fitness
version: 1
" 1 "does not match filename stem" "code.yaml"

run_case "duplicate-id" \
"---
agent: code
measurements:
- id: em-001
scorer: trace_fitness
version: 1
- id: em-001
scorer: trace_fitness
version: 1
" 1 "duplicate id 'em-001'"

run_case "uppercase-id" \
"---
agent: code
measurements:
- id: EM-001
scorer: trace_fitness
version: 1
" 1 "must be lowercase like em-001"

run_case "missing-version" \
"---
agent: code
measurements:
- id: em-001
scorer: trace_fitness
" 1 "version must be a positive integer"

run_case "empty-dir" \
"" 1 "no eval/measurements/*.yaml files found"

run_case "flow-style-unsupported" \
"agent: code
measurements: [{id: em-001, scorer: trace_fitness, version: 1}]
" 1 "unsupported YAML shape"

run_case "nested-assert-unsupported" \
"agent: code
measurements:
- id: em-001
scorer: trace_fitness
version: 1
assert:
- path: x
" 1 "unsupported YAML shape"

echo ""
if [[ ${FAILURES} -gt 0 ]]; then
echo "${FAILURES} test(s) failed"
exit 1
fi
echo "All tests passed"
Loading
Loading