Repository navigation
[feat] Show scorer results in the dashboard #1182
Description
Parent: #1137
Goal
Labels on teamai-reports are only worth their cost if someone reads them. Add a scorer view to the existing teamai dashboard that answers two questions: is a scorer's pass rate moving, and does it differ by tool, model, team revision or definition.
What it shows
- One measurement at a time. Before any grouping, the view fixes the scorer, the rubric version (
rubric.sourceHash, withteamRevisionshown when there is one), the source (workfloworsession), the run mode (runMode, [feat] Run mode: tell attended runs from unattended ones #1384) and the judge (judge.kind, and for a model judge its recorded tool and model). Labels that differ in any of these are never summed: a changed rubric, another kind of subject, another run mode or another judge is a different measurement. The view opens on the newest rubric whose labels carryrubric.teamRevision, so a member's uncommitted draft does not become the team's view. When no label of the scorer has one, it opens on the rubric with the latestscoredAt. Labels without judge data form their ownjudge not recordedcohort. - Pass rate over a window, 30 days by default, as a headline and as a weekly trend. The window is about when labels were scored, not when the work happened, and the view says so. A week with no labels is a gap, not 0%. The headline shows
n, passed and failed next to the rate. - Group by one recorded configuration dimension: tool, model, team revision, definition, the role, project or tag selection the assets were delivered for (
team.selection, [feat] Score a finished session on demand #1178), or fingerprint ([feat] Record the agent each workflow step ran as #1176). Session labels can also be grouped by a team skill the session loaded (used.skills, [feat] Score a finished session on demand #1178), with a label counted under each skill it lists. Model groups keep whether the model was reported or only requested. Labels without that part form anot recordedgroup, never a guessed one. - Before and after a team commit. Given a commit of the team repo, the view splits the labels in two: those whose
agent.team.revisioncontains it, checked withgit merge-base --is-ancestorin this machine's team clone, and those that do not. A label withoutteam, or whose revision this clone does not have, isnot recorded. It combines with grouping by skill, so a team sees the sessions that loaded a skill before and after the commit that changed it. - Failing labels. Under a cohort, the view lists its failing labels: author,
scoredAt, platform andused.skills, newest first. When the team repo holds a pushed session whoseoriginis the label's subject, the row shows that session's name, so a teammate can open it withteamai session resumeor score it withteamai score --team-session. It shows no native session id, prompt or reasoning. - Pass/fail as stored.
passedis read from the label, never recomputed against the scorer's currentpassingScore. - A line under the view:
Labels from sessions and workflow runs show how the tools did on the work that came along. To compare tools, models or team assets on the same tasks, run a benchmark.
Data
- Workflow labels are read from
scores/<member>/<YYYY-MM>.jsonlonteamai-reports, like session labels, told apart bysource([feat] Add workflow scorers and recorded quality labels #1085). For a workflow label,subject.runId,subject.stepandsubject.passjoin it to its run record onteamai-reports, which gives that pass'sagent. This view reads labels in [feat] Add workflow scorers and recorded quality labels #1085's shape. A label withoutrubric.sourceHashgoes to arubric not recordedcohort and is never counted under the current rubric. A label that names no pass, on a step that ran more than once, is shown under its step without a pass, and never attached to an arbitrary one. A record from before [feat] Record the agent each workflow step ran as #1176, with a stringagent, gives only the definition. - Session labels come from
scores/*/*.jsonl([feat] Score a finished session on demand #1178). - Order. Read every source, keep the newest
scoredAtper(scorer, rubric.sourceHash, subject, judge.kind), and only then apply the window, so a label rescored in a later month counts once. - Benchmarks. Labels from a run whose end record has
benchmark([feat] Run benchmark suites as workflows #1180) are left out. If [feat] Run benchmark suites as workflows #1180 lands later, it adds this exclusion. - Privacy. The dashboard server answers with
Access-Control-Allow-Origin: *(dashboard.ts:300). Its responses carry labels and recorded configuration fields only. No judge reasoning, prompt, transcript or diff is read or returned.
Scope
- Read API and page in the existing dashboard structure, next to the Workflow Runs page ([feat] Add a Workflow Runs page to the dashboard #1088) if it has merged.
- English and Chinese strings.
docs/guide/dashboard.mdanddocs/guide/zh-CN/dashboard.md: a section on the scorer view and what it can and cannot show.
Acceptance criteria
- With fixture labels from two versions of a scorer, the view opens on the newest one whose labels carry
rubric.teamRevision, with its ownn, and the other shows only its labels. A version whose labels carry noteamRevisiondoes not open by default while another does. - Workflow and session labels of the same scorer are never in the same count. Neither are labels from two judge models with the same rubric.
- Grouping by model puts labels with no model under
not recorded, and keeps reported and requested-only models apart. - A workflow label is grouped by the
agentof the pass it judges. A label on a pre-[feat] Record the agent each workflow step ran as #1176 record has only the definition. - A label scored in January and rescored in February counts once, in February.
- Raising
passingScorein the scorer file does not change any past pass rate. - A week without labels shows a gap in the trend.
- No API response contains a key or value from a judge's reasoning file or a session transcript (test with fixture text).
- Grouping by role puts labels from members with different
primaryRoleon the same team revision in different groups. Grouping by skill counts a label whoseused.skillsnames two skills under both. - A fixture benchmark run with
diff-qualitylabels leaves the view'snfordiff-qualityunchanged. - With labels on revisions
A,B(child ofA) andC(child ofB), splitting atBputsAbefore andBandCafter. A label with noteamisnot recorded. - A failing label whose subject is a pushed session is listed with that session's name and no native session id.
Test plan
- Unit tests for the aggregation: dedup before the window, cohorts, the join to passes, grouping, gaps.
- API tests for criterion 8.
- One real run of
teamai dashboardagainst a fixture reports branch, with a screenshot in the PR.
Out of scope
Benchmark comparisons in the dashboard, per-asset health (#408), judge reasoning in the browser, and alerts.
Dependencies
- Blocked by: [feat] Score a finished session on demand #1178 and [feat] Run mode: tell attended runs from unattended ones #1384. [feat] Add workflow scorers and recorded quality labels #1085 is transitive through [feat] Score a finished session on demand #1178.
- Unblocks: [feat] Open improvement PRs proven by a before/after benchmark #1249, [feat] Compare scorers with hand labels, including on pushed sessions #1269
Update: run modes and session labels
The original scope already covers session labels. This update adds the runMode split (#1384) and a reminder table. Run mode is fixed like source and judge: labels of different modes are never summed, and labels without runMode form their own not recorded cohort.
scorer runMode source labels passed
review-quality attended session 14 71%
review-quality unattended workflow 52 88%
review-quality not recorded session 9 67%
A member's own dashboard also compares the sessions the friction reminder fired on with those it did not, so the threshold can be tuned with data. "Reminder fired" is hinted in the member's local per-session contribute state (getSessionPath, src/contribute-check.ts:120-128), the learning offer. That state is only on the member's machine, so this table covers only sessions run on that machine, says so, and is not part of the team view.
| Column | What it counts |
|---|---|
| sessions | this machine's dashboard session_start events without launchedBy (#1384), in the window |
| labelled | those sessions with a label of this cohort, from the local label store and teamai-reports, deduplicated by #1178's key |
| passed | labelled sessions whose label passed; the rate is over labelled sessions only |
It compares within one scorer, rubric, source, judge and run mode. A session with no reminder state is not recorded; no reminder state is inferred from a label.
review-quality · rubric 3a9e1c · session · claude / <recorded-model> · attended · sessions run on this machine
reminder fired sessions labelled passed
yes 18 11 64%
no 96 40 85%
not recorded 12 3 —
The state file is swept 24 hours after its session's last write (cleanupStaleSessions, STALE_SESSION_MS, src/contribute-check.ts:183-208), which is shorter than the 30 days window. So the reminder decision must outlive it: when cleanup sweeps a state file, it keeps a record of only hinted, true or false, for every swept file (and the facts #1388 adds to it), removed after 30 days. #1388 needs the same record for its once-per-session offers; whichever of the two issues lands first adds it, and the other reuses it. The table stays local to this machine.
Added criteria:
- Labels of the same scorer with different
runModeare never in the same count; labels withoutrunModeare counted undernot recorded. - The reminder table reads only this machine's contribute state, and a label whose session has no state there is
not recorded. - A session whose reminder fired 10 days ago, whose state file was swept since, still counts under
yesin a 30 days window. - A session whose reminder did not fire, whose state file was swept 10 days ago, still counts under
noin a 30 days window.
Activity
SaulMoro commentedon Oct 20, 2026
Benchmark trials are scored through #1085 at sampling 100 (#1180), so their labels land in run records as workflow labels, and nothing keeps them out of this view. Arms deliberately use other models and other asset sets (#1181), so they would distort each scorer's trend and groupings. Leave out labels from a run whose end record has
benchmark. #1180 adds that field; if it lands later, it adds this exclusion. Criterion: a fixture benchmark run withdiff-qualitylabels leaves the view'snfordiff-qualityunchanged.#1249 reads this view's aggregation through
teamai digest --json, so the aggregation lives where both can use it.SaulMoro commentedon Oct 23, 2026
Notes from building #1085 (#1297) and #1178 (#1298), for the data this view reads:
Workflow labels are not in run records. Both kinds of label live in one store on
teamai-reports,scores/<member>/<YYYY-MM>.jsonl, told apart bysource. A workflow label names its pass insubject: {runId, step, pass}, so the join to that pass'sagentgoes through the run record, but the label itself is read fromscores/.The judge is recorded the way a pass records its agent:
So a judge cohort can keep reported and requested-only models apart, like criterion 3 asks for the agent, and show the team alias the scorer asked for.
used.skillsis stable across rescoring. It is checked against what the session-start capture recorded as delivered, so a member's own skill with a team skill's name is not listed, and a pull that later removes a skill does not change a rescore.Workflow labels carry no
used.skillsyet. A pass does not record the skills its launch loaded; that needs a field on the stored pass, one in the driver report and one on the label. Until then, grouping by skill applies to session labels only.teamai score --team-sessiondoes not exist. [feat] Score a finished session on demand #1178 scores sessions on this machine only. For the failing-labels list,teamai session resume <name>works today; scoring a pushed session needs its own change (it can read the archive throughSyncManager.loadSessionand feed the same session scorer).Linking a label to a pushed session: an archived session keeps
origin(src/session-flow/sync.ts), which holds the source platform and session id the label'ssubjectnames.SaulMoro commentedon Oct 25, 2026
Added a section at the end of the body: this issue now also shows session labels, which are published today but never read, and splits every view by run mode (#1384). It also asks for a friction-reminder vs label view to tune the reminder threshold. Now blocked by #1384. Part of #1383.
SaulMoro commentedon Oct 25, 2026
Rewrote the appended section. Labels are grouped by
runMode(#1384), and records without it form anot recordedcohort.The friction-reminder comparison is now local to the member's dashboard, because reminder state is only in
~/.teamai/sessions/<id>.jsonon that machine (src/contribute-check.ts:120-128). It also shows the labelled count, so its pass rate covers labelled sessions only.Added two criteria for these. The rest of the body is unchanged.
SaulMoro commentedon Oct 25, 2026
scores/<member>/<YYYY-MM>.jsonl, like session labels, and joined to their run record only for the pass'sagent(src/dashboard/workflow-runs.ts:269-278).runModeis now also one of the fixed measurement keys in the first section.SaulMoro commentedon Oct 25, 2026
The record kept after the 24 hours cleanup now holds
hintedfor every swept session, false included. Without that, the reminder table'snorow would lose every older session.Added criterion 16 for that case.
feat(dashboard): page the failing labels and list a measurement's lab…
fix(dashboard): fill the friction reminder table of a user scope inst…
docs(dashboard): labels by value, paged failing labels, and the user …
fix(contribute): keep reminder decisions only for contribute state fi…
feat(dashboard): show scorer pass rates, what drives them, and labels…