---
title: Work output by load (what the fleet actually finished)
---

# Work output by load — what the fleet actually finished

## What it is

A per-load reading of **finished work**, in three countable measures, next to the tokens the same
window produced. It exists because tokens measure how much was SAID: a fleet that talks more while
finishing less looks identical to a productive one when the only column is output tokens.

The question it answers is narrow and specific: **as the live-session count rises, does the fleet
finish less per session-hour — and does it still finish more per hour overall?**

## Where to find it

- `npm run perf:work-output` — the full report on the terminal, including the plain-English answer
  at the top.
- `npm run perf:work-output -- --share` — the same report published to the owner's Omniscio Shares.
- The Performance Monitor panel renders the same fold, so a bucket reads identically on both.

## How it behaves

### The three measures

Each is a record the app already wrote — nothing is inferred from elapsed time or activity:

- **Ready-to-merge tags applied** — the mint's own record in `worktree_events`
  (`ops-ready`, `ops-ready-user-override`). A tag minted by hand, or on another machine, is absent.
  This leads, because a tag is stamped the moment an agent FINISHES the branch.
- **Commits landed** — this app's own lander record: `auto_lander_events` in the live database, read
  windowed and read-only, `outcome = 'landed'`. NOT the `GET /auto-lander/events` activity route: it
  serves the newest 500 rows — a display limit for the panel's log — while the table keeps 72 h, so a
  busy window came back short under a coverage line that read `ok`.
- **Items brought to the owner's inbox** — `inbox_alert_items`, counted at the moment the card
  REACHED the inbox. A card still held back is not yet a delivery.

**Lands are not a load's output.** The lander works a queue, so a bucket's lands include branches
finished at an earlier load — at 0-25 sessions one night it was draining earlier tags, 21.84 lands/h
against only 4.37 tags/h. Both surfaces print one line saying so and make NO causal claim in either
direction: read on tags, the same rows rose 7x with load.

### Reading the two tables

The report prints the same rows twice, on purpose:

- **Counts, and the whole fleet per hour** — how much the fleet finished in an hour, whatever its
  size.
- **Counts per session-hour** — how much each individual session finished in an hour.

A per-hour figure that **holds** while the per-session-hour figure **falls** means the busier load
bought session-hours rather than finished work. Where the per-hour figure itself falls, the busier
load simply finished less.

Both are read inside **matched hours** where possible: the load comparison in the plain-English
answer at the top only ever compares two live-session buckets observed in the SAME clock hour, so
the box's own drift between hours cannot be mistaken for a load effect. The pooled table below it is
labelled as confounded and is never the answer.

### When a number is missing

- A bucket with under **ten covered minutes of tape** prints its counts and `thin` instead of a
  rate, because a rate off a few minutes is noise.
- A source that cannot be read, or whose history starts inside the window, reads `partial` or
  `unmeasured` **with its reason** — never a low number that looks like a slow fleet.
- An event whose live-session count could not be read is counted on its own `unknown` row, never
  folded into the quietest bucket, so the parts still sum to the whole.
- **No session-count recommendation appears anywhere.** A peak is a reading of the data, not a
  number of sessions to run.

### The stable link

The share page is refreshed **hourly to the same URL** by an in-app scheduled job
(`AMC-work-output-share`), which passes the stored share token back as `updateToken`. The address
never changes, so it is safe to bookmark: a refresh replaces the page in place rather than minting a
new link each run. The job deliberately does NOT defer under load — a busy box is exactly the
reading that is wanted.

The page states this only as strongly as the app can support it: it is published by hand until the
branch that ships the job has landed, so the script probes `GET /ops/jobs` at publish time and says
"once the job is registered" until the row is really there.

If the link is ever revoked, or its 30-day default lapses, the server answers `400 No such active
share to update` before publishing anything — an update deliberately keeps the target's own expiry.
The job then forgets the dead token and republishes without it, so the next run mints a fresh link
and the hourly refresh recovers on its own instead of failing forever. That means the address CAN
change after a revocation; the state file at `~/.amc/work-output-share.json` always names the current
one.

## For agents

### Where the data lives

- Fold (pure, no I/O): [`scripts/perf/work-rate.mjs`](../../scripts/perf/work-rate.mjs), shared with
  the app's Performance Monitor block.
- Reader and rendering: [`scripts/perf/work-output-report.mjs`](../../scripts/perf/work-output-report.mjs).
- Buckets and the time join: [`scripts/perf/load-buckets.mjs`](../../scripts/perf/load-buckets.mjs).
- All three measures: the live `mission-control.db`, read-only, short windowed queries that each ride
  an index (`worktree_events`, `auto_lander_events`, `inbox_alert_items`).
- The hourly publish job: `scripts/ops/ops-tasks/amc-work-output-share.mjs`; its state (share token
  and last publish) lives at `~/.amc/work-output-share.json`.
- Contract: [work-rate-by-load-readout-contract.md](../../.claude/memory/contracts/work-rate-by-load-readout-contract.md).

Two things to know before changing a reader:

- **Trim every value a `sqlite3` child returns before parsing it.** On Windows that child writes
  CRLF, and a single-column read swallows the `\r`, so `Date.parse` answers `NaN` for every row and
  the whole ledger reads as empty. This silently reported **0** ready tags while the identical
  predicate counted 573.
- **Ride `idx_worktree_events_recorded` for `worktree_events`.** The event-first plan took 11.7 s for
  559 rows where the indexed plan took 0.66 s.

## Related

- [perf-load-curve.md](perf-load-curve.md) — the wait half: how long each step takes at each load.
- [perf-status.md](perf-status.md) — the live performance readout this is built from.
