Repository navigation
[Proposal] Score agent sessions and run benchmarks as workflows #1137
Description
Superseded. The revised proposal is in this comment, and its work is split into the issues listed there. The body below is kept for the discussion that led to it.
Related: #818 (workflow runtime), #1085 (scorers on workflow steps), #1083 (run records), #408 (asset health: execution effect, skill lift), #1135 (session read side). Earlier attempt: #200.
Goal
Show whether a team's skills, rules, agents and model choices make agent work better. Use two inputs: sessions members already ran, and benchmark runs that can be repeated. Add no server and no new command, and keep transcripts on the machine that produced them.
Warp Factories ships scorers and Factory Benchmarks for the same goal. This proposal keeps their scorer format, so a team can bring its scorers over, and closes the gaps listed at the end.
Scoring
One scorer definition, one sampling rule and one label record for every source.
flowchart LR
subgraph sources [what gets scored]
step["workflow step<br/>succeeded agent step"]:::wf
trial["benchmark trial<br/>a workflow step too"]:::wf
sess["finished session<br/>any tool, member opts in"]:::sess
end
subgraph machine [member's machine]
sample{"deterministic sample<br/>hash of scorer + subject"}:::teamai
read["AgentSession.find + transcript<br/>redacted, plus the diff"]:::teamai
judge["scorer agent<br/>member's tool and model"]:::teamai
reason[("judge reasoning<br/>stays local")]:::local
end
subgraph reports [teamai-reports branch]
run[("run record<br/>label per step")]:::git
scores[("scores/<member>/<month>.jsonl")]:::git
stats[("stats per scorer, agent,<br/>model and source")]:::git
end
dash["dashboard #408"]:::ui
step & trial --> sample
sess --> sample
sample --> read --> judge
judge --> reason
judge -- "source: workflow" --> run
judge -- "source: session" --> scores
run & scores --> stats --> dash
classDef wf fill:#2f6f9f,stroke:#1f4f73,color:#fff;
classDef sess fill:#6a4f8f,stroke:#513b6d,color:#fff;
classDef teamai fill:#2f8f4f,stroke:#1f6b39,color:#fff;
classDef git fill:#b5762f,stroke:#865520,color:#fff;
classDef local fill:#555,stroke:#333,color:#fff;
classDef ui fill:#8f2f5f,stroke:#6b1f45,color:#fff;
- Scorer file.
scorers/<ns>/<name>.mdtakes Warp'sscorer.mdfields:name,description,agents,labels(1 to 20{ value, score, description }),passingScore,samplingRate(0 to 100, default 25, 0 disables), andmodel(an alias or a model name). At least one label must be at or abovepassingScoreand one below it. [feat] Add workflow scorers and recorded quality labels #1085 defines the file; this proposal only adds sources. - Sampling is deterministic. A subject is scored when
hash(scorer, subject) mod 10000 < samplingRate × 100. Every machine picks the same sample, and a rerun of the scorer replaces its earlier label for that subject instead of adding a second one. - What a scorer reads. For a workflow step, its diff ([feat] Add workflow workspaces, branches, and session continuation #1080) and its session. For a session, the session. Sessions are read through
AgentSession.findandAgentSession.transcript(feat(session): safe session reads and copies for workflows, drivers, scorers, and benchmarks (#593 follow-up) #1135), always redacted, with tool results kept apart from what the member and the agent wrote. - Where it runs. On the member's machine, with the member's tool and model, like the workflow driver. Transcripts never leave the machine.
- Sessions outside workflows are opt-in. A member turns session scoring on in their local config. It is off by default, as
auto_startis in Discussion: Workflow Runtime Integration #818: no tokens are spent without the member.
Label record
This is the decision to make now in #1085, so that a session label later needs no migration:
type ScoreLabel = {
scorer: string; // scorer name
label: string; // one of the scorer's label values
score: number; // 0 to 1, from that label
passed: boolean; // score >= passingScore when it was scored
source: 'workflow' | 'session';
subject:
| { runId: string; step: string } // step path, e.g. ship.check.classify
| { platform: string; sessionId: string }; // native session id
agent?: string; // team agent, when there is one
tool: string;
model?: string;
modelAlias?: string;
teamRevision: string; // team repo commit the scorer was read from
scoredAt: string;
};- Workflow labels go in the run record ([feat] Publish privacy-preserving workflow run records and stats #1083), next to the step they judge.
- Session labels go in
scores/<member>/<YYYY-MM>.jsonlonteamai-reports, one line per label. Each member writes only their own file, the same splitvotes/uses, so two machines never conflict. Readers keep the newest line per(scorer, subject). - Stats sum labels per scorer, agent, model and
source([feat] Publish privacy-preserving workflow run records and stats #1083). - Never published: prompts, transcripts, diffs, and the judge's reasoning. The reasoning stays in local state (the run state for a workflow,
~/.teamai/for a session), so the member can still see why a run failed. passedis stored, not derived. A later change topassingScorewould otherwise rewrite old pass rates.teamRevisionshows which version of the rubric gave each label.
Benchmarks
A benchmark is a workflow, not a command. Each trial is an agent step, so it gets a workspace, a run record and labels with no new mechanism.
flowchart LR
past["past session<br/>clean start commit recorded"]:::sess
task["benchmarks/<suite>/tasks/<task>.yaml<br/>prompt, start commit, check, criteria"]:::git
wf["benchmark workflow<br/>for_each task x arm x repetition"]:::teamai
subgraph arms [arms: one variable each]
a1["A: claude, opus, team skills"]:::wf
a2["B: codex, gpt, team skills"]:::wf
a3["C: claude, opus, skill X removed"]:::wf
end
ws["fresh workspace at the start commit<br/>#1080"]:::teamai
check["check command<br/>exit code"]:::teamai
corr["correctness scorer<br/>only where no check decides"]:::teamai
report["comparison per arm<br/>pass rate, cost, tokens, time"]:::ui
past -- "becomes a task" --> task --> wf --> a1 & a2 & a3 --> ws
ws --> check & corr
check & corr -- "labels, source: workflow" --> report
classDef wf fill:#2f6f9f,stroke:#1f4f73,color:#fff;
classDef sess fill:#6a4f8f,stroke:#513b6d,color:#fff;
classDef teamai fill:#2f8f4f,stroke:#1f6b39,color:#fff;
classDef git fill:#b5762f,stroke:#865520,color:#fff;
classDef ui fill:#8f2f5f,stroke:#6b1f45,color:#fff;
- Suite.
benchmarks/<suite>/suite.yamltakesname,descriptionandarms(1 to 6). Each arm changes one thing:model,tool, or the set of team assets (a skill, rule or instruction file removed or replaced).repetitionsdefaults to 3. - Task.
benchmarks/<suite>/tasks/<task>.yamltakestitle,prompt,startingRef(repo and full 40-character commit),check(a command whose exit code decides),successCriteria(for the correctness scorer where no check decides) and an optionalsource({ platform, sessionId }). - From a session to a task. A session started on a clean tree already records its start commit (feat(session): safe session reads and copies for workflows, drivers, scorers, and benchmarks (#593 follow-up) #1135), so a member can turn a real session into a task. A session that started with tracked changes is refused, because its start state cannot be rebuilt. [Proposal] rules/skills 效果评测 harness + 社区可贡献的评测集 #200 had to recover that commit from tool output.
- Running it. A shipped workflow runs
for_eachover task × arm × repetition. Each trial starts in a fresh workspace atstartingRefand runs through whatever driver the member uses. The report compares the arms on pass rate, cost, tokens and time, task by task.
Compared with Warp Factories
| Warp Factories | This proposal | |
|---|---|---|
| Scorer file | scorer.md |
The same fields, so a Warp scorer can be reused |
| Sampling | A percentage of runs | Deterministic: the same sample on every machine, and reruns are idempotent |
| What is scored | Runs in Warp's cloud | Workflow steps, plus any local Claude Code, Codex, OpenCode, Cursor, CodeBuddy or WorkBuddy session |
| Where traces live | Warp's cloud or the company's storage | Only on the member's machine. Only the label is published. |
| Rubric version | Not recorded on a score | teamRevision on every label |
| Benchmark grading | LLM correctness scorer | The check exit code first, a scorer only where no check decides |
| Benchmark arms | model or harness |
model, tool, or the set of team assets, which gives the effect #408 calls skill lift |
| Benchmark source | A past Warp run | A past session from any supported tool, with its start commit |
| Runner | Warp's cloud runners | A workflow run by any driver |
Out of scope
Dashboard pages (#408), automatic improvement PRs from failing labels (selfImprovement), scoring subagents at SubagentStop, scorer access to secrets, and new CLI commands.
Open questions
- How are sessions picked for a scorer outside workflows? Warp scopes a scorer to agents. A session in a project has no team agent. One option is
agents: [{ session: true, tools: [...] }]. - When is a session finished? Proposal: idle for 6 hours, scored by the background pull at the next session start, with a daily budget per member.
- Should a benchmark suite live next to the skill it tests (as Proposal: Team Context Assets Health — Skills / Rules / CLAUDE.md 多维质量与效果评估 #408's
evals/proposes), or in its ownbenchmarks/namespace?
Activity
SaulMoro commentedon Oct 16, 2026
Two things from #1148 that the benchmark workflow runs into:
for_eachtakes one list, and expressions havelength,any,allandfilter, with no map, product or range. An item of a nestedfor_eachin an inline workflow reads only its own value, not the outer item. The simplest trial list comes from a command step that prints it as an output.--driver local, trials run in sequence whatevermax_parallelsays, until [feat] Add workflow workspaces, branches, and session continuation #1080 gives each step its own worktree. At 3 arms, 3 repetitions and 20 tasks, that is 180 agent runs back to back.SaulMoro commentedon Oct 16, 2026
Revised proposal
What changed from the body above:
agentis the composition the model worked inside, recorded as observed.scorers:step key and the label that sessions reuse. Everything here builds on workflow issues. The only workflow issue that waits on these is [feat] Open improvement PRs proven by a before/after benchmark #1249 (improvement PRs), on [feat] Compare team asset sets in benchmarks #1181, [feat] Show scorer results in the dashboard #1182 and [feat] Compare scorers with hand labels, including on pushed sessions #1269.teamai pull --team-ref#1183) that benchmarks use.What "agent" means
A model never runs as a file in
agents/. It runs inside a composition: the definition, if any, and how the tool applied it; the tool and its version; the model and effort, as requested and as the tool reported them; the team revision TeamAI delivered to the checkout, the roles, projects and tags it was delivered for, and whether a delivered file was edited; and the project commit. #1176 records this for every workflow pass, and #1178 for every session, when it starts. #1178 adds the roles, projects and tags to both. A field that was not observed is left out, never guessed. A fingerprint over the shared parts is written only when tool, model and team revision are all known, so "same agent" is never claimed from partial data.This is what TeamAI can record that a hosted factory cannot: what each member's tool actually had, on every tool, without publishing transcripts to team reports. The step key
agent:still selects a definition, andmodel:on a prompt step (#1177) picks a model without one.Names.
teamRefis a branch, tag or commit of the team repo, as a person writes it.teamRevision, andteam.revisioninsideagent, is the full 40-character commit it resolved to. On the project side a commit is always written in full:startingCommitin a benchmark task,project.commitinagent.Scoring
scorer.md, thescorers:step key, deterministic sampling,passedstored when scored. The subject names the pass, withrubric: { sourceHash, teamRevision? }andjudge: { kind: "model", tool, model?, usage? } | { kind: "human" }. A hand label ([feat] Compare scorers with hand labels, including on pushed sessions #1269) hasjudge: { kind: "human" }.agentfrom that pass. A session label goes toscores/<member>/<YYYY-MM>.jsonlonteamai-reportsand carriesagentitself. The key is(scorer, rubric.sourceHash, subject, judge.kind)in both.teamai score --scorer <name> [--session <id> [--source <platform>]] [--push], a command of its own rather than asession saveoption, since scoring shares none ofsave's flags and its subject is not always a session. It ignores sampling, so a team can calibrate a scorer on a few sessions before sampling more. A label is about the whole session, and carriesagentonly when the session ran as one agent throughout.scoring.sessions, and only for scorers withsessions: true. A session is finished after 6 hours idle. Each session is tried once per rubric, and a daily limit counts every judge launch.Benchmarks
benchmarks/<suite>/suite.yaml(repo, defaults, 1 to 6 arms, repetitions, a timeout, optional setup and scorers) andbenchmarks/<suite>/tasks/<task>.yaml(prompt, starting commit, acheckorsuccessCriteria).teamai workflow start <suite> --benchmarkbuilds a workflow run: one trial per task, arm and repetition, each a prompt step in a new workspace at the starting commit, then the check.Labels from sessions and workflow runs show how agents did on the work that came along. Benchmarks compare tools, models and team assets on the same tasks. The scorer view in the dashboard (#1182) fixes the rubric, the source and the judge before it groups by any part of the agent.
Compared with Warp Factories
scorers/<name>/scorer.md, with runner, secret and MCP settings besides the rubric, and the agents it judgesscorers/<name>.md(#1085). The workflow step names its scorers inscorers:. Porting a Warp scorer means moving its path, replacingagentswithscorers:on the steps, and adaptingmodelincludeDescendantstheir child runs. Sampled automatically, or one run on demandteamai score, or in the background if the member opts inrubric.sourceHashand, when resolvable,teamRevision.passedis kept as scoredThe open questions
sessions: true, and the member turns onscoring.sessions. A workflow step lists its scorers inscorers:([feat] Add workflow scorers and recorded quality labels #1085), and a benchmark suite lists them insuite.yaml.benchmarks/<suite>/, not next to a skill, because an arm can change several assets at once. Proposal: Team Context Assets Health — Skills / Rules / CLAUDE.md 多维质量与效果评估 #408 can present results per asset.Issues
Arrows point from an issue to what it unblocks.
flowchart LR subgraph wf ["#818 workflows"] i1080["#1080 workspaces"] i1082["#1082 requirements"] i1085["#1085 workflow scorers"] n1["#1176 agent each step ran as"] n2["#1177 model of prompt steps"] i1090["#1090 PR delivery"] i1246["#1246 workspace gaps"] end subgraph sc ["#1137 scorers and benchmarks"] n3["#1178 score a session on demand"] n4["#1179 score sessions in the background"] n5["#1180 benchmark suites"] n6["#1181 team asset arms"] n7["#1182 scorer view in the dashboard"] n9["#1269 compare scorers with hand labels"] n10["#1247 score what a run delivered"] end n8["#1183 pull --team-ref"] i1085 --> n3 n1 --> n3 n3 --> n4 n3 --> n7 n3 --> n9 n7 --> n9 i1080 --> n5 i1082 --> n5 i1085 --> n5 n1 --> n5 n2 --> n5 n5 --> n6 n8 --> n5 i1085 --> n10 i1246 --> n5 i1246 --> i1085 i1090 --> n10 n3 --> n10 n5 --> n10#1176, #1177 and #1183 have shipped. #1183 pins a workspace to a team ref, a pull feature of its own that benchmarks use to prepare trials. #1178 and #1182 follow #1085, #1269 follows both, #1180 and #1181 come next, and #1247 comes last, after #1090 too.
Out of scope
Self-improvement PRs, scoring subagents at
SubagentStop, the member's own tool config in the agent record, per-asset health pages (#408), uncertainty intervals and recommendations in benchmark reports, and new CLI commands other thanteamai score(#1178).SaulMoro commentedon Oct 20, 2026
Whole-run scoring, which the 2026-10-15 decision on #1085 kept here, is now #1247: it scores what a run delivered, next to the session and step labels, which stay as they are. Improvement PRs, which this proposal leaves out, are #1249 under #818; they reuse #1180 and #1181 to prove each change before review.
SaulMoro commentedon Oct 22, 2026
Update from #1085's design: "Two homes, one label" becomes one home.
A workflow label takes the judged pass's
agentfrom the run at read time, so it still carries no copy of it.