You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`verdict` (`pass`|`fail`|`skip`)** + fractional **`score`** (not a boolean; needs skip
37
-
+ fractional). These rows are the generic AgentV grader evidence channel: every
38
-
grader returns `assertions[]`; deterministic graders typically return one row,
39
-
while multi-aspect graders return one row per distinct criterion/aspect. The
40
-
artifact preserves both the flattened rows and each grader's nested rows.
41
-
Default judge = skeptical evidence-by-path (opt-out via
42
-
explicit `prompt`); grader target selection flows through the config graph
43
-
(`defaults.grader`) or assertion-level target selection, not a system-under-test
44
-
target field. Evidence stays in `grading.json`.
50
+
4.**Per-attempt grading sidecar**: `grading.json` is an AgentV-native public contract
51
+
that keeps Promptfoo's aggregate grading vocabulary while preserving AgentV's richer
52
+
nested breakdown. It exposes top-level `pass`, `score`, `reason`, optional
53
+
`threshold`/`details`, and an always-present `graders[]` array. Each grader exposes
54
+
`name`, `type`, `pass`, `score`, `reason`, optional `threshold`/`details`, and
55
+
optional `checks[]`. Each check exposes `id?`, `text`, `pass`, optional `score`,
56
+
`reason`, and optional `evidence` only when the evidence is distinct from `reason`.
57
+
There are no public top-level `checks`, no dynamic single-grader shortcut, and no
58
+
public `assertion_results`, `assertions`, `passed`-only aliases, or
59
+
evidence-as-reason aliases. Authored YAML uses `assert`, `assert-set`, and
60
+
`llm-rubric`; result artifacts describe evaluated graders and checks. Default judge =
61
+
skeptical evidence-by-path (opt-out via explicit `prompt`); grader target selection
62
+
flows through the config graph (`defaults.grader`) or assertion-level target
63
+
selection, not a system-under-test target field. Evidence stays in `grading.json`.
45
64
5.**Bundle layout / naming**: machine files move under per-run **`.internal/`**
46
65
(`index.jsonl`, `progress.json`, `events.jsonl`, `bundle.json`); run root stays clean
47
66
(`summary.json` + per-case dirs). Rename the reference field `manifest_path` →
@@ -61,9 +80,57 @@ Confirms ADR-0009 + ADR-0012 (not a new decision):
61
80
62
81
### Artifact filenames (locked — accuracy over cosmetic consistency)
63
82
-**`summary.json`** (run-root AND per-case) — the aggregate. Kept over margin's `results.json`: it's a *summary*, not the full results (those are the per-case dirs + `index.jsonl`); avoids the `results/<run_id>/results.json` stutter; symmetric at both levels (run aggregates cases, case aggregates samples); vercel-aligned. We match margin on the aggregate *concept/shape*, not the filename.
64
-
- Per-sample triad (distinct, all kept): **`result.json`** (what happened), **`grading.json`** (verdict = `assertion_results`+`verdict`+`score`), **`metrics.json`** (duration+tokens+cost+execution/trajectory; the `timing.json` merge).
83
+
- Per-sample triad (distinct, all kept): **`result.json`** (what happened), **`grading.json`** (aggregate `pass`/`score`/`reason` plus `graders[]`/`checks[]`), **`metrics.json`** (duration+tokens+cost+execution/trajectory; the `timing.json` merge).
65
84
-**`grading.json`** kept (not `grades.json`) — source-consistent with agentskills (whose file is `grading.json`), and "grading" names the grading *result*.
66
85
86
+
### `grading.json` wire-format example
87
+
```json
88
+
{
89
+
"pass": false,
90
+
"score": 0.62,
91
+
"reason": "The answer names the right API but misses the rollback condition.",
92
+
"threshold": 0.8,
93
+
"details": {
94
+
"aggregation": "weighted_mean"
95
+
},
96
+
"graders": [
97
+
{
98
+
"name": "rubric",
99
+
"type": "llm-rubric",
100
+
"pass": false,
101
+
"score": 0.62,
102
+
"reason": "Two of three rubric checks passed.",
103
+
"threshold": 0.8,
104
+
"checks": [
105
+
{
106
+
"id": "api",
107
+
"text": "Identifies the API used to publish result bundles.",
108
+
"pass": true,
109
+
"score": 1,
110
+
"reason": "Correctly identifies the publish command."
111
+
},
112
+
{
113
+
"id": "rollback",
114
+
"text": "Explains when to roll back a failed publish.",
115
+
"pass": false,
116
+
"score": 0,
117
+
"reason": "Mentions retrying but not rollback criteria.",
118
+
"evidence": "The response says to rerun the command after any failure."
119
+
}
120
+
]
121
+
}
122
+
]
123
+
}
124
+
```
125
+
126
+
Summary and index guidance: use `pass_rate`, `pass_count`, and `sample_count` for run
127
+
and case aggregates; use `passed` for one execution outcome and `pass_any` when any
128
+
sample in a repeated case passed. Use `pass_at_k` only when the metric is an explicit
129
+
sampling metric with a real `k` and the calculation is documented on the summary row.
130
+
Index rows should stay lightweight: identity/outcome/named score/token usage fields
131
+
plus paths such as `result_path`, `grading_path`, `metrics_path`, `transcript_path`,
132
+
and `outputs_path`, not a full embedded grading tree.
133
+
67
134
### Full results-tree layout (two levels — no per-run `.indexes`)
68
135
```
69
136
.agentv/results/
@@ -80,12 +147,12 @@ Confirms ADR-0009 + ADR-0012 (not a new decision):
80
147
```
81
148
- Per-run index (rows = cases) = `<run_id>/.internal/index.jsonl`; **no separate per-run `.indexes`** — `.internal` already holds it. Cross-run catalog (rows = runs) = `.agentv/results/.indexes/runs.jsonl`. Names signal scope: `.internal` = one bundle; `.indexes`/`.cache` = across runs. Both dot-prefixed (skipped by discovery).
82
149
-**Cross-run filtering needs `cases.jsonl`, not just `runs.jsonl`.**`runs.jsonl` (one row/run) answers "which runs match"; **case-level cross-run** queries ("every `fizzbuzz` across runs", "failing cases with tag X over last 10 runs", "trend of `test_id` T") need one row per (run x case) → `.indexes/cases.jsonl`, rebuilt by concatenating every `<run_id>/.internal/index.jsonl` + run metadata. Join key for trends = the layered identity (content-hash `test_id` + author governance tag, ADR-0016 pt8). Both catalogs are derived/rebuildable; if JSONL scanning outgrows laptop scale, a rebuildable SQLite **view** is the escape hatch (optional adapter, never core — exploitbench pattern, Phoenix boundary intact).
83
-
- **margin-lab consistency & divergence:** matches on the *filesystem* substance — top-level queryable aggregate (`results.json`=`summary.json`), `internal/` machine folder (we dot-prefix `.internal/`), per-execution-unit dirs, one pure `Build()` for pass@k, `instance_key = test_id#sample_index`. **Divergences (deliberate):** (1) margin's *runner* uses a persistent **`RunStore` (in-memory / Postgres, NOT SQLite)** for scheduling + queries; **AgentV declines a store entirely** (laptop-first; resumability via `index.jsonl` + `--rerun-failed`). (2) hierarchical `<test-id>/sample-N/` vs margin's flat `instances/<case>#<sample>/`. (3) `timing`→`metrics` merge. The **rebuildable derived index/view** idea (JSONL `.indexes/`, optional SQLite escape hatch) is from **exploitbench** (`import`/`export` bijection), not margin — margin's store is the operational source during a run, not a filesystem-derived index. (Nuance: margin *can* rehydrate a run's completed-work state from its run-dir for **resume** — `LoadProgressSnapshot` + `loadSavedResumeBundle` + `carryForwardLocalCases` — but that's targeted carry-forward, not a general `import` that rebuilds the multi-run query DB from files; the memory store is ephemeral, the Postgres store persists independently. **AgentV follows exploitbench's model** — filesystem is source of truth, `.indexes/*.jsonl` are derived/rebuildable — with `--rerun-failed` reading `index.jsonl` from fs and no store to rehydrate.)
150
+
- **margin-lab consistency & divergence:** matches on the *filesystem* substance — top-level queryable aggregate (`results.json`=`summary.json`), `internal/` machine folder (we dot-prefix `.internal/`), per-execution-unit dirs, one pure `Build()` for pass rates and explicit sampling metrics, `instance_key = test_id#sample_index`. **Divergences (deliberate):** (1) margin's *runner* uses a persistent **`RunStore` (in-memory / Postgres, NOT SQLite)** for scheduling + queries; **AgentV declines a store entirely** (laptop-first; resumability via `index.jsonl` + `--rerun-failed`). (2) hierarchical `<test-id>/sample-N/` vs margin's flat `instances/<case>#<sample>/`. (3) `timing`→`metrics` merge. The **rebuildable derived index/view** idea (JSONL `.indexes/`, optional SQLite escape hatch) is from **exploitbench** (`import`/`export` bijection), not margin — margin's store is the operational source during a run, not a filesystem-derived index. (Nuance: margin *can* rehydrate a run's completed-work state from its run-dir for **resume** — `LoadProgressSnapshot` + `loadSavedResumeBundle` + `carryForwardLocalCases` — but that's targeted carry-forward, not a general `import` that rebuilds the multi-run query DB from files; the memory store is ephemeral, the Postgres store persists independently. **AgentV follows exploitbench's model** — filesystem is source of truth, `.indexes/*.jsonl` are derived/rebuildable — with `--rerun-failed` reading `index.jsonl` from fs and no store to rehydrate.)
84
151
-**Dashboard default view is sensible, never odd/empty:** because `tags.experiment` is value-defaulted to the eval/suite name (always populated), the default view groups by `experiment` (real names, no "(none)" wall) or a recent-runs list; the grouping key is a user preference they can change, not the absence of a default.
85
152
86
153
### Run organization: cross-run index, repeat naming, experiment-as-tag
87
-
-**Cross-run index (rebuildable cache, not source of truth):** keep per-run `index.jsonl` (rows = cases); add a cross-run catalog `.agentv/results/.indexes/runs.jsonl` (already-reserved `.indexes/` namespace) — **one row per run** (run_id, timestamp, targets, `tags` incl experiment, aggregate pass@k). Derived by scanning `*/summary.json`, rebuildable, optional (Dashboard can glob summaries as fallback). JSONL (append per run), **not `index.json`**.
88
-
-**Repeat folder = `sample-N`, not `run-N`.** "run" is overloaded (`run_id` = the whole invocation). Rename `run-${attempt+1}` → `sample-1`, `sample-2`, … (matches margin `samples_per_case`/`sample_index`, pass@k, and AgentV's `repeat`; Inspect's `epoch` is the ML-jargon alt). Keep the metadata split: `sample_index` = repeats, `retry_index` = infra retries.
154
+
-**Cross-run index (rebuildable cache, not source of truth):** keep per-run `index.jsonl` (rows = cases); add a cross-run catalog `.agentv/results/.indexes/runs.jsonl` (already-reserved `.indexes` namespace) — **one row per run** (run_id, timestamp, targets, `tags` incl experiment, aggregate `pass_rate`/`pass_count`/`sample_count`, and explicit `pass_at_k` only when present). Derived by scanning `*/summary.json`, rebuildable, optional (Dashboard can glob summaries as fallback). JSONL (append per run), **not `index.json`**.
155
+
-**Repeat folder = `sample-N`, not `run-N`.** "run" is overloaded (`run_id` = the whole invocation). Rename `run-${attempt+1}` → `sample-1`, `sample-2`, … (matches margin `samples_per_case`/`sample_index`, explicit sampling metrics, and AgentV's `repeat`; Inspect's `epoch` is the ML-jargon alt). Keep the metadata split: `sample_index` = repeats, `retry_index` = infra retries.
89
156
-**`experiment` has no *structural* privilege, but its *value* is auto-defaulted.** No storage dir (already `<run_id>/`), no top-level field (`tags.experiment`), no special schema; tag keys sort **alphabetically**; the default grouping/compare **key** is a user preference (any tag — AgentV blesses none). `--experiment X` = sugar for `--tag experiment=X`. **The one convenience:** the harness auto-populates the `experiment` tag's **value** when unset, deriving it from the eval/suite name (ADR-0009: `--experiment` > authored `tags.experiment` > eval/suite name). So every run always has a meaningful `experiment` value and is groupable — without the author setting anything. This is a default *value*, not a privileged *key*.
90
157
91
158
## Decision — workspace resolver (provenance vs acquisition)
@@ -322,7 +331,12 @@ Borrow vercel's judge model, which is stronger than a prompt-stuffed rubric:
322
331
- **Judge pinning** knob: `grader_target` = `{ agent?, model }` with self-grade default. AgentV already has `grader_target`; formalize the `{model}`-required pinning for apples-to-apples comparison.
323
332
- **Gap to fix vs vercel:** they capture no token/cost — AgentV already does; keep it.
324
333
325
-
### 5.3 `grading.json` contract — reconciled with agentskills (owner-flagged main risk)
334
+
### 5.3 `grading.json` contract — stale historical design
335
+
336
+
Stale after `av-kfik.28.1` and ADR-0017's 2026-07-04 update. Keep this section
337
+
as historical context only; implementation workers must use the current
338
+
`graders[]`/`checks[]` contract documented in ADR-0017 and in the Bead notes.
339
+
326
340
The output contract originates from agentskills' [evaluating-skills](https://github.com/agentskills/agentskills/blob/main/docs/skill-creation/evaluating-skills.mdx). Its `grading.json` is:
0 commit comments