Skip to main content

Agent Scorecards

A scorecard is the set of numbers one Agent is judged on: "5 pull requests merged a week", "12 posts published a month", "keep the backlog under 20". Each metric carries a target, the current value, and optional floor and stretch bounds, and the platform colors it — Exceeded, On track, Behind, Critical — from those numbers alone.

It is the answer to "is this AI employee actually delivering?" in a form you can read in two seconds, attached to the Agent itself rather than to a report you have to assemble.

Status — increment 1: the numbers are yours to maintain

Everything on this page ships today: the data model, the editor, the status coloring, and the full REST contract. Two things deliberately do not exist yet:

  • current never updates itself. No run, task, or commit rolls a number up into a scorecard — you (or a script calling the API) type it.
  • There is no org-wide roll-up. No "agents at risk" tile aggregates scorecards across your workspace. The aggregation helper exists in the codebase (summarizeScorecard() in packages/agent/src/agents/scorecard.ts, which counts metrics per status), but nothing renders it.

Both are named follow-ups, not hidden features. Plan around a scorecard being a shared, structured record of intent — not a live telemetry feed.

Where to find it

Sidebar → Teams → Agents tab → open any Agent → Settings (/agents/:id/settings) → the Scorecard card, below Merge policy.

A brand-new Agent has no scorecard, and the card says so: "No metrics yet. Add quantified goals so this Agent's output is measurable."

Anatomy of a metric

A scorecard is an ordered array of up to 12 metrics stored on the Agent. Each one:

FieldRequiredRules
keyyesKebab-case (prs-merged, nps, weekly-revenue-usd), ≤ 64 chars, unique in the array.
labelyesWhat a human reads — 1 to 80 characters. Shown on the card.
targetyesThe goal value for the period. Any finite number, including 0 and negatives.
currentyesThe latest measured value. Manually maintained in this increment.
floornoMinimum acceptable value. Below it, the metric reads Critical.
stretchnoAmbition line. At or above it, the metric reads Exceeded.
unitnoDisplay-only suffix — PRs, %, usd. ≤ 20 characters. Set through the API (see below).
periodyesweekly, monthly, or quarterly — how often you intend to meet or reset the target.

The period is documentation, not a scheduler: nothing resets current when a week rolls over. It tells whoever reads the card what "5" means.

key is the stable handle — it survives label edits, and it is what future automation will match on. When you add a row in the UI you never type it: the key is derived by kebab-casing your label (and de-duplicated with a numeric suffix if that collides). Rows that already exist keep the key they were stored with.

How a metric is scored

Status is derived on every render from the four numbers — nothing is stored:

StatusMeaningOn the card
CriticalA floor is set and current is below it.Red badge, red bar.
ExceededA stretch is set and current is at or above it.Green badge, green bar.
On trackcurrent is at or above target.Neutral badge, accent bar.
BehindBelow target, but not under the floor.Amber badge, amber bar.

The floor check runs first, so an under-floor metric reads Critical regardless of the other bounds. Floor and stretch are both optional — a metric with neither only ever reads On track or Behind.

The progress bar is current / target, clamped to 0–100%. For a target of 0 or less the ratio is meaningless, so the bar fills only once current is strictly above the target — a target: 0, current: 0 metric reads as "not started" rather than "done".

Under each metric the card prints the raw numbers: 3 / 5 PRs · Weekly.

How to add metrics to an Agent

  1. Open Sidebar → Teams → Agents, click the Agent, and go to its Settings tab (/agents/:id/settings).
  2. Scroll to the Scorecard card and press Edit.
  3. Press Add metric. A row appears with Metric, Period, Target, Current, Floor and Stretch fields. The button disables once the scorecard holds 12 rows.
  4. Type the metric name into Metric (this also seeds the stored key) and pick the Period.
  5. Fill Target and Current. Leave Floor and Stretch blank unless you want the red and green bands.
  6. Repeat for each metric. Use the button on a row to drop it.
  7. Press Save scorecard. A Scorecard saved toast confirms the write, and the card re-renders with badges and bars.

Cancel discards the whole editing session — including rows you added and rows you removed — and restores what is stored.

Two client-side checks fire before the request leaves the browser:

ToastCause
Every metric needs a labelA row's Metric field is empty or whitespace.
Targets and values must be numbersTarget or Current is blank or not a finite number, or a filled Floor / Stretch is not a number.

Deleting every row and saving clears the scorecard back to "none configured" — the card returns to its empty state.

Editing a metric is the whole scorecard

Save scorecard writes the entire array, not the row you touched. If two people edit the same Agent's scorecard at once, the last save wins outright. For anything you script, read the current array first, change what you need, and send the whole thing back.

Setting a scorecard over the API

Scorecards are written through the Agent's normal update endpoint.

MethodEndpointNotes
PATCH/api/agents/:idThe only way to write a scorecard. Whole-array replace.
GET/api/agents/:idReturns scorecard verbatim — null when none is configured.
GET/api/agentsThe index list carries scorecard on every Agent it returns.
curl -X PATCH http://localhost:3100/api/agents/<agent-id> \
-H "Authorization: Bearer <jwt-token>" \
-H "Content-Type: application/json" \
-d '{
"scorecard": [
{
"key": "prs-merged",
"label": "Pull requests merged",
"target": 5,
"current": 3,
"floor": 2,
"stretch": 10,
"unit": "PRs",
"period": "weekly"
},
{
"key": "posts-published",
"label": "Blog posts published",
"target": 12,
"current": 12,
"period": "monthly"
}
]
}'

This is also how you set a unit — the card has no unit editor in this increment, but a unit written through the API is preserved by every subsequent UI save and shown next to the numbers.

You cannot create an Agent with a scorecard in one call

POST /api/agents rejects an inline scorecard with 400 — the create body has no such property, and the API refuses unknown properties rather than silently dropping them. Create the Agent first, then PATCH the scorecard onto it. A fresh Agent always starts at scorecard: null.

Replace and clear semantics

You sendResult
A new arrayReplaces the stored array outright — there is no per-key merge.
[] (empty array)Clears the scorecard; the stored value normalizes to null.
nullClears the scorecard.
No scorecard key at allLeaves an existing scorecard untouched (patch other Agent fields freely).

Storage is faithful in both directions: unset optional fields stay omitted rather than being coerced to null, an explicit null for floor / stretch / unit round-trips as null, array order is preserved, and negative, zero and decimal values survive unchanged. A scorecard write bumps the Agent's updatedAt.

Validation

Every write is checked twice — once by the HTTP layer (per-metric messages naming the offending index) and once inside the service, so non-HTTP callers such as tools and imports get the same rules. Both reject with 400.

RuleViolation returns
key matches ^[a-z0-9]+(?:-[a-z0-9]+)*$400 — the key must be kebab-case.
key ≤ 64 chars, unique within the array400 — over-long, or duplicated keys.
label 1–80 chars400 — empty, missing, or over-long label.
target / current finite numbers400 — strings, NaN and Infinity are all rejected.
floor / stretch finite numbers when present400 — a non-number bound.
unit ≤ 20 chars400 — over-long unit.
period in weekly/monthly/quarterly400 — any other value.
At most 12 metrics400 — 12 is accepted, 13 is not.
No unknown properties on a metric400 — a typo'd field is rejected, not ignored.
scorecard is an array (or null)400 — an object or string is rejected.

A rejected write changes nothing: the previously stored scorecard is left exactly as it was.

Access follows the same rules as the rest of the Agent API — an unauthenticated PATCH is 401; another user's Agent is 404 on both read and write (never 403, so nothing leaks about whether the id exists); an unknown-but-valid UUID is 404; a malformed UUID is 400.

Scorecards, Goals, and budgets

Three different things measure an Ever Works workspace. They do not overlap:

SurfaceWhere the number comes fromWhat it does about it
Agent scorecardYou type it (this increment).Colors the metric. Records intent. Changes nothing else.
GoalA metrics provider plugin, read on a schedule.Records a sample per evaluation, tracks progress and lifecycle toward a target.
BudgetActual AI spend, computed per call.Enforced — blocks or alerts on the next call once the cap is hit.

Read it as: a Goal is a measured number with history and a scheduler; a scorecard is a stated number attached to a worker; a budget is a hard limit with teeth. If you want the number checked automatically, create a Goal. If you want an Agent stopped when it overspends, set a budget on the Agent's Budgets tab.

A scorecard is deliberately inert: no status — not even Critical — pauses an Agent, blocks a run, skips a heartbeat, or raises a notification.

What increment 1 does not do

Not yetWhat that means for you
Automatic current from run outputUpdate the number yourself, from the UI or with a scripted PATCH (a nightly job against /api/agents/:id works well today).
Org-level roll-up of scorecardsReview scorecards Agent by Agent on each Settings tab; there is no cross-Agent summary screen.
History or samplesSaving a new current overwrites the old one. Nothing keeps the previous value — use a Goal when you need a trend.
A unit editor in the cardSet unit through the API; the UI preserves it.
Scorecards in the per-Agent export envelopeExporting and re-importing an Agent does not carry its scorecard — re-apply it with a PATCH after the import.

Suggested starting scorecards

Concrete metrics beat abstract ones. A few that map cleanly onto what Agents actually produce in Ever Works:

AgentMetricTargetFloorStretchPeriod
Reviewer (community PRs)Pull requests reviewed20840weekly
Editor (blog Work)Posts published12620monthly
Researcher (Mission)Ideas accepted418monthly
Maintainer (quality gates)Required checks passing65weekly

Note the shape of that last row. Scorecard status is higher-is-better only: a floor fires Critical when current drops below it, so it cannot flag a lower-is-better metric like open failures — a target: 0, floor: 0 metric reads On track the moment a failure appears, not Critical. Restate the metric so that more is better, as above (target = the number of required checks, floor = the count below which the Agent is blocking a release), or track the failure count as a Goal, which keeps history.

  • Agents (Your AI Employees) — the Agent concept, scopes, definition files and heartbeats.
  • Agent Capabilities — what the Agent being measured is actually allowed to do.
  • Goals — measured metrics with a provider, a schedule, and history.
  • Budgets & Usage — the enforced spend limits, including the Agent's Budgets tab.
  • Teams — org chart, reporting lines, and the Agents tab the scorecard hangs off.
  • Activity — what an Agent actually did, run by run.
  • Settings Map — every settings surface in the dashboard, including the Agent tabs.