Skip to content
Merged
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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ cp .env.example .env # Add API keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, et
| **CLI** | `vera.py`, `vera_cli/`, `utils/config_schema.py` | Thin entry point, command adapters, and shared command/config wiring |
| **Generation** | `generate/` | Conversation simulation, turns, personas |
| **Judging** | `judge/` | Rubric scoring, TSV output, question navigation |
| **Scoring** | `score/` | Score aggregation and visualizations from `results.csv` |
| **Scoring** | `score/` | Score aggregation, pooling, and visualizations from `results.csv` |
| **LLM providers** | `llm_clients/`, `llm_clients/llm_factory.py` | New models, custom HTTP/API providers |
| **Pipeline helpers** | `scripts/` | Pooling and automation until absorbed into `vera pool` |
| **Pipeline helpers** | `scripts/` | Batch and reporting helpers |
| **Data** | `data/` (personas, rubrics) | Evaluation inputs (committed) |
| **Output** | `output/` (gitignored) | Generated transcripts, evaluations, logs |
| **Config** | `utils/model_config_loader.py`, `llm_clients/config.py` | Model name resolution, API keys |
Expand Down
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

### Documentation and development workflow

- **Wider maintainer review**—[`.github/CODEOWNERS`](.github/CODEOWNERS) now also covers `score/`, `vera_cli/`, `vera.py`, `utils/`, `scripts/`, `.github/`, and `docs/roadmap.md`, and [`AGENTS.md`](AGENTS.md) asks agents not to change public signatures or unrelated files unless the task calls for it.
### Breaking / migration

- **Pool script moved to [`legacy/`](legacy/)**—`scripts/pool_vera_scores.py` is now `legacy/pool_vera_scores.py`; run it as `uv run python -m legacy.pool_vera_scores`. Its flags and defaults are unchanged. Prefer `vera pool`.
- **`generate.main` is now `generate.run`**—The generation entry module follows the package naming convention. `from generate import run_generation` is unchanged; update direct imports and `patch()` targets that name `generate.main`.

### Runtime, CLI, and pipeline

- **`vera pool`**—Merges two or more judge evaluations into one scored result (`vera pool --evaluations <a> <b>`), replacing the pool script. Pooling now lives in [`score/pool.py`](score/pool.py) (`from score import run_pooling`). Like `vera score`, it skips the risk breakdown unless `--personas` is given.

### Testing and CI

- **Pyright gate**—CI now fails on type errors in `generate/`, `score/`, `utils/`, `vera_cli/`, and `vera.py`, which have none today. The full-repo pyright run still reports without failing.

### Documentation and development workflow

- **Wider maintainer review**—[`.github/CODEOWNERS`](.github/CODEOWNERS) now also covers `score/`, `vera_cli/`, `vera.py`, `utils/`, `scripts/`, `.github/`, and `docs/roadmap.md`, and [`AGENTS.md`](AGENTS.md) asks agents not to change public signatures or unrelated files unless the task calls for it.

### Fixed

- **Recommended pipeline pooling**—[`legacy/run_recommended_vera_pipeline.sh`](legacy/run_recommended_vera_pipeline.sh) finds [`scripts/pool_vera_scores.py`](scripts/pool_vera_scores.py) again. It looked for the pool script next to itself, which broke its final pooling step when the shell script moved to `legacy/`.
- **Recommended pipeline pooling**—[`legacy/run_recommended_vera_pipeline.sh`](legacy/run_recommended_vera_pipeline.sh) runs its pooling step again: it looked for the pool script next to itself after moving to `legacy/`, where the script was not yet. It now runs `python -m legacy.pool_vera_scores`.

## [v2.0.3](https://github.com/SpringCare/VERA-MH/releases/tag/v2.0.3) \- 2026-10-09

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ jq '.generation.chatbot.name = "<model-under-test>"' configs/recommended-SI.json
This produces one evaluation per user model. The headline score is the **pooled** result across both:

```bash
uv run python scripts/pool_vera_scores.py <evaluation-folder-A> <evaluation-folder-B>
uv run python vera.py pool --evaluations <evaluation-folder-A> <evaluation-folder-B> \
--personas data/SI/personas.tsv
```

`vera pipeline` prints both evaluation folders when it finishes. How the score is computed and what the output files contain is covered in [docs/scoring.md](docs/scoring.md).
Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE-SPINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ tests/ # permanent tests
- **Operational/deployment envelope.** Future service or scheduled deployment is possible but not committed, and this architecture is explicitly not designed around it yet. Can wait because it would affect the `workers/` queue backend choice (e.g. pulling `SQSQueue` forward) and would need its own operations doc — premature before a concrete deployment requirement exists.
- **Multi-chatbot (multi-provider) support within a single `vera pipeline` invocation.** Stays single-chatbot-per-invocation for now, with cross-chatbot comparison done by looping the CLI externally. Flagged as a possible future addition, not ruled out permanently. Can wait because the external-loop pattern already satisfies every known use case, including batch generate's cross-provider handling.
- **Per-provider concurrency limits.** The shared `workers/` pool enables real parallel fan-out (e.g. `-u gpt:5 sonnet:5`) with no cap on per-provider concurrency yet — only per-call retry/backoff exists today. Flagged as a real requirement for the phase that lands `workers/` unification, not yet designed. Can wait until that phase, since no code path exercises pool-level fan-out at meaningful scale before then.
- **Fate of `scripts/pool_vera_scores.py`.** Whether to fold its logic into `score/pool.py` or delete it outright once `vera pool` fully supersedes it is unresolved. Can wait until the substantial-refactor phase, when `score/` is the only pooling entry point and the script's remaining usage (if any) becomes clear.
- **Fate of `scripts/pool_vera_scores.py`.** Resolved: its logic moved into `score/pool.py` behind `vera pool`, and the script remains only as the legacy wrapper `legacy/pool_vera_scores.py`, removed with the rest of `legacy/`.
- **Fate of `judge/`'s legacy score-tooling siblings.** `score_comparison.py`, `score_comparison_from_export_summary.py`, and `score_utils.py` currently live in `judge/` alongside what moved to `score/`, and none appear in AD-4's target `score/` package tree. Whether each folds into `score/` during the Phase-2 extraction, gets deleted outright, or stays put is undecided beyond the already-deferred fate of `scripts/pool_vera_scores.py` above. Can wait because Phase-2 execution will need to decide file-by-file once the `score/` extraction is actually underway — this is not a decision this spine can make in the abstract.
- **Possible future `report/` package.** `scripts/summarize_results.py`'s improvement report is deterministic aggregation over `judge/` output, so it stays under `judge/` for now. If reporting later evolves to call an LLM for creative improvement suggestions rather than just stats, that's a different concern from `judge/`'s pure-core-plus-handler charter and would warrant its own `report/` package. Can wait because no LLM-based reporting exists yet — revisit only once that feature is actually proposed.
- **OpenSpec adoption (the OpenSpec phase in `docs/roadmap.md`, independent of the others).** `openspec/` exists today only as empty scaffolding; whether the team actually adopts the OpenSpec workflow for large multi-file features (the existing ESCALATE trigger: new judge dimensions, pipeline CLI changes) is not yet decided. Can wait because no other phase depends on it, and it can be pulled forward independently the moment a qualifying feature would benefit from it — done only once a real change has gone through `openspec/changes/`, not once the config exists.
Expand Down
22 changes: 21 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,27 @@ uv run python vera.py score \

The risk-level breakdown currently works only with SI personas. What each output file contains is described in [scoring.md](scoring.md).

## `vera pool`
Comment thread
luca-belli marked this conversation as resolved.
Resolved

Merges two or more judge evaluations into one `results.csv` and scores it, typically the two user-model evaluations of the recommended profile. Each evaluation is a `j_*` folder or the `results.csv` inside it.

Most users won't run this directly: it's for combining evaluations you judged separately, such as the two user-model runs of the recommended profile, into the headline score.

```bash
uv run python vera.py pool --personas data/SI/personas.tsv \
--evaluations output/<generation-run-A>/evaluations/<evaluation-run-A> \
output/<generation-run-B>/evaluations/<evaluation-run-B>
```

| Flag | Description | Default |
|------|-------------|---------|
| `--evaluations` | Two or more evaluations to merge | required |
| `-o`, `--output` | Folder the pooled `j_*` folder is created in | `output/` |
| `--personas` | Personas file for the risk-level breakdown | none (breakdown skipped) |
| `--skip-risk-analysis` | Skip the risk-level breakdown even when `--personas` is given | off |

The config section is `pooling` with the same four fields (`evaluations`, `output`, `personas`, `skip_risk_analysis`). The output files are described in [scoring.md](scoring.md#pooling-several-evaluations).

## `vera pipeline`

Runs generate, judge, and score in sequence. Its flag form accepts only the three model roles and the target:
Expand Down Expand Up @@ -273,5 +294,4 @@ Folder names still use the legacy `p_` (user) and `a_` (chatbot) prefixes. A lay

These are specified in [architecture.md](architecture.md) but not implemented:

- **`vera pool`**—combine several evaluations into one score. Until then, use [`scripts/pool_vera_scores.py`](scoring.md#pooling-several-evaluations).
- **`vera resume`**—continue a run from its own `config.json` and `state.json`, across stages and without being given a path. Until then, use [`--into`](#continuing-an-interrupted-run---into).
12 changes: 10 additions & 2 deletions docs/legacy-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ uv run python -m legacy.score -r output/<generation-run>/evaluations/<evaluation

Same flags as `vera score` (`-r`, `-o`, `--skip-risk-analysis`), except `--personas-tsv` / `-p` defaults to `data/SI/personas.tsv`. `vera score --personas` has no default and skips the risk breakdown when omitted.

## `legacy/pool_vera_scores.py`

```bash
uv run python -m legacy.pool_vera_scores <evaluation-A> <evaluation-B>
```

Was `scripts/pool_vera_scores.py`. Same behavior as `vera pool`, with legacy flags: positional evaluation paths instead of `--evaluations`, `-o`/`--output-dir`, and `--personas-tsv`, which defaults to `data/SI/personas.tsv` (`vera pool --personas` has no default and skips the risk breakdown when omitted). `--extract-from-log` prints the last evaluation folder found in a `legacy.run_pipeline` log; only `legacy/run_recommended_vera_pipeline.sh` uses it.

## `legacy/run_pipeline.py`

Runs the generation, judging, and scoring scripts in sequence:
Expand Down Expand Up @@ -114,5 +122,5 @@ Its defaults, and the `VERA_*` environment variables that override them, are doc
| `VERA_MAX_PERSONAS` | `--sample N` |
| `VERA_USER_A`, `VERA_USER_B` | entries in `generation.user` |
| `VERA_JUDGE`, `VERA_JUDGE_EXTRA_PARAMS` | `judging.models` |
| `VERA_POOL_OUTPUT` | `-o` on `scripts/pool_vera_scores.py` |
| `VERA_POOL_SKIP_RISK` | `--skip-risk-analysis` on `scripts/pool_vera_scores.py` |
| `VERA_POOL_OUTPUT` | `-o` on `vera pool` |
| `VERA_POOL_SKIP_RISK` | `--skip-risk-analysis` on `vera pool` |
10 changes: 5 additions & 5 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Smaller items that belong to no phase are in the [Backlog](#backlog).

| Phase | Goal | Done when |
|-------|------|-----------|
| [Scoring split](#scoring-split) | `score/` owns pooling and stops importing `judge/` | `vera pool` matches `scripts/pool_vera_scores.py` on the same inputs; `vera pipeline` ends with the pooled score; import-linter `judge/` ⊥ `score/` passes in CI |
| [Scoring split](#scoring-split) | `score/` owns pooling and stops importing `judge/` | `vera pipeline` ends with the pooled score; import-linter `judge/` ⊥ `score/` passes in CI |
| [Rubric-agnostic scoring](#rubric-agnostic-scoring) | Scoring works for any rubric, with unambiguous stop tokens | A non-SI target scores and groups by risk with no SI assumptions; every terminating rubric row uses an explicit stop token |
| [Traceability](#traceability) | Every run records what produced it, under the target-rooted layout | New runs write `config.json`, `state.json`, and checksums under `output/<target>/`; `vera judge --target all` works; `utils/` leaf contract passes |
| [Model spec boundary](#model-spec-boundary) | Domains take `ModelSpec` directly | `run_for_user_models` and `_legacy_model_config` are gone; per-model judge parameters work |
Expand All @@ -28,10 +28,9 @@ Smaller items that belong to no phase are in the [Backlog](#backlog).

### Scoring split

- **`vera pool`**, implemented in `score/pool.py`. `scripts/pool_vera_scores.py` becomes a thin legacy wrapper over it (moved to `legacy/`), so `legacy/run_recommended_vera_pipeline.sh` keeps working. Two user-visible strings already promise this command: `vera judge --conversations` help (`vera_cli/judge.py`) and a `JudgingConfig` error (`utils/config_schema.py`).
- **`vera pipeline` ends with the final score.** Today a multi-user-model pipeline leaves N per-user-model scores and no headline number, and the caller finishes the job by hand with the pool script. The pooled score is the answer the pipeline was run to get, so the pipeline should produce it. An earlier attempt was reverted for three reasons, each now addressed:
1. The pooled artifact had nowhere correct to live. #215 documents its home at `<target>/c_<chatbot>/pooled/`; until [Traceability](#traceability) builds that layout, pooled output stays where the pool script writes it today.
2. It inverted the layering (`vera_cli/` imported from `scripts/`). Fixed by `score/pool.py` above.
- **`vera pipeline` ends with the final score.** Today a multi-user-model pipeline leaves N per-user-model scores and no headline number, and the caller finishes the job by hand with `vera pool`. The pooled score is the answer the pipeline was run to get, so the pipeline should produce it. An earlier attempt was reverted for three reasons, each now addressed:
1. The pooled artifact had nowhere correct to live. #215 documents its home at `<target>/c_<chatbot>/pooled/`; until [Traceability](#traceability) builds that layout, pooled output stays where `vera pool` writes it today.
2. It inverted the layering (`vera_cli/` imported from `scripts/`). Fixed: pooling now lives in `score/pool.py`.
3. It seemed to contradict "judge never auto-scores". That rule is about `vera judge` staying non-scoring, which still holds; `pipeline` exists to run every stage.

A standalone `vera pool` stays useful for pooling evaluations judged separately, across targets, or re-pooled later.
Expand Down Expand Up @@ -151,3 +150,4 @@ Smaller items that belong to no phase. Each can ship as its own PR whenever it's
- **`vera` CLI and config.** `vera.py` (`generate`, `judge`, `score`, `pipeline`) with `--config`, `--target`, `--personas`, `--rubric`, `-u`, `-j`, `--sample`, and `--into` resume. The old root scripts moved to `legacy/` (#243) instead of being deleted; see [Deferred](#deferred).
- **Multi-rubric evaluation, through targets.** This is the intended end state, not a stopgap. Evaluating the same conversations with several evaluators (rubrics) means one target per rubric: each target bundles its rubric with its personas and prompts, and each is judged separately with `vera judge --target <name>`, writing its own results. Keeping rubrics separate is deliberate, because scores from different rubrics aren't comparable and must never be merged. The earlier plan for several rubrics inside one run (`judging.rubrics` with length > 1, per-rubric judge-model overrides) is dropped. `judging.rubrics` keeps its list shape with a length-1 check, so revisiting it later wouldn't break the schema. What remains is convenience, not capability: `vera judge --target all` comes with [Traceability](#traceability), once the output path attributes each target.
- **Package moves.** `generate_conversations/` → `generate/` (#245); scoring out of `judge/` into `score/` with `run.py`, `aggregate.py`, `viz.py` (#246).
- **`vera pool`.** Pooling moved from `scripts/pool_vera_scores.py` into `score/pool.py`, behind a new `vera pool` command; the script is now the legacy wrapper `legacy/pool_vera_scores.py`.
12 changes: 5 additions & 7 deletions docs/scoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,15 @@ The two risk files are written only when a personas file is given (`--personas`,

## Pooling several evaluations

The published VERA-MH score pools two evaluations of the same chatbot, one per user model. Merge them with:
The published VERA-MH score pools two evaluations of the same chatbot, one per user model. Merge them with [`vera pool`](cli.md#vera-pool):

```bash
uv run python scripts/pool_vera_scores.py -o <pool-parent-dir> \
output/<generation-run-A>/evaluations/<evaluation-run-A> \
output/<generation-run-B>/evaluations/<evaluation-run-B>
uv run python vera.py pool -o <pool-parent-dir> --personas data/SI/personas.tsv \
--evaluations output/<generation-run-A>/evaluations/<evaluation-run-A> \
output/<generation-run-B>/evaluations/<evaluation-run-B>
```

This creates one pooled folder (named like `j_gpt-5.4x1__p_gpt_5_2+claude_opus_4_5__a_.../`) with a merged `results.csv`, `pool_metadata.json` recording the sources, and the usual `scores/`. Pass `--skip-risk-analysis` to skip the risk breakdown. Use that pooled folder for headline numbers. See `--help` for other options.

A `vera pool` command will replace this script.
This creates one pooled folder (named like `j_gpt-5.4x1__p_gpt_5_2+claude_opus_4_5__a_.../`) with a merged `results.csv`, `pool_metadata.json` recording the sources, and the usual `scores/`. As with `vera score`, the risk files are written only when a personas file is given. Use that pooled folder for headline numbers.

## Comparing chatbots

Expand Down
Loading
Loading