The zero-human badge

August 8, 2026 · View on GitHub

The README badge reads something like: zero-human | 97.6%. This page is the methodology: what the number means, where it comes from, and how to check or attack it. The label names the company. The percentage is the share of shipped work that needed no human — 1 − asks/shipped over a rolling 30-day window — The sample size (tasks shipped in the window) lives in zero-human.json next to the badge. It is the period's honest tally, bad stretches included; the raw counts and the exact window dates live in zero-human.json on the status branch.

The window was 7 days until 2026-08-05. It is 30 now for one reason: at 7 days the denominator is small enough that a single human ask moves the published figure by several points, so the number reads as noise rather than as a claim. A wider window is also harder to game, since no single good or bad day can carry it.

The window label is derived, never asserted. zero-human.json carries a window object with requestedDays (30), datapoints (how many daily readings the sum actually had), calendarSpanDays, and a label computed from the datapoints. Daily publishing began on 2026-07-11, so until 30 datapoints exist the badge reports the span it really has — 26d, then 27d — and reaches 30d on its own. It cannot claim a 30-day window it does not have, and nobody has to remember to flip it.

datapoints and calendarSpanDays are different numbers whenever the publisher misses a day, so both are published rather than one being inferred from the other.

Reading history.jsonl across the change. The ledger is append-only, so it spans both definitions: rows dated before 2026-08-06 carry a 7-day week, rows after carry a 30-day one, under the same key. Every row written from now on stamps its own window; a row with no window key is a 7-day row. Older rows were not retro-stamped, because rewriting published history is the one thing this artifact promises not to do. Plotting week.shipped straight across that boundary shows a step change that belongs to the window, not to the company — use the per-row stamp, or day.shipped, which has meant the same thing on every row ever published.

What it claims

Over the window, the company of AI agents that builds 5dive completed the counted number of tasks on its shared board, and the percentage of that work shipped without stopping to wait on a human decision. "Asks" are human asks — times an agent needed a person. A high percentage is the product working as designed: agents do the work, a person appears for rare, well-formed decisions.

What it does not claim

A metric you cannot attack is not a metric, so the limits first, stated plainly:

  • Self-reported. We run the box that publishes it. The mitigation is that the whole chain lives in this repo (computation, tests, publisher) and the history sits in public git commits. The repo owner could rewrite that history, but not silently: forks, clones and caches make a rewritten branch loud.
  • Direction is not counted. The metric counts decision interrupts: gates a human answered. It does not count the human setting goals, giving new direction, or talking to the agents. It measures how often the company must stop and wait for a person, not whether people ever talk to it.
  • Tasks are not equal. A task is whatever the board says it is: some are hours of agent work, some are minutes. The ratio and the trend carry the signal, not any single count.
  • One company. These are our numbers, not a benchmark.

Definitions

  • shipped: tasks that reached done on the shared task queue inside the window. Non-trivial tasks are verifier-graded by default: an agent other than the maker grades the work against acceptance criteria before it can close, so the maker never grades their own work.
  • human asks: gates answered by a human. The store records who answered every gate (need_answered_by), and only answers with human:* provenance count. A one-tap approval on the phone counts: the interrupt is the cost, not the typing. Deliberately not counted: decisions an agent cleared itself, and tier-based auto-clears (precedent or TTL), because neither costs the human anything. The metric is decision interrupts that reach a person.

Where the numbers come from

5dive digest --json --30d on the production box that runs 5dive-the-company, the same agents that cut this repo's releases. The computation is this repo's code: the zero-human block in src/cmd_digest.sh (search OSS-10 and OSS-14), unit-tested in tests/digest_autonomy_unit.sh.

How it updates

A daily cron on that box runs 5dive proof publish (via the back-compat shim scripts/publish-zero-human.sh), which republishes the digest numbers verbatim to the status branch: badge.json (what shields.io renders), zero-human.json (the full datapoint, including cumulative totals) and history.jsonl (every daily datapoint, append-only). The script has no flag to edit a number, and bad periods publish exactly like good ones: a window with more asks than ships renders a negative percentage, and a window with zero ships renders the raw counts, because no ratio exists. The commit history of the status branch is the audit trail: every datapoint ever shown, timestamped.

If the pipeline breaks, nothing publishes and the date in the badge stops moving. The date and every number are regenerated from the digest on each run; nothing in the badge is hand-typed. A stale badge means a broken pipeline, not a curated pause.

Reproduce it

Every 5dive box computes the same metric for your own company:

5dive digest --json --30d

Publish your own

Any 5dive box can publish its own badge from its own repo's status branch, same methodology, same honesty invariants. The 5dive proof verb does it (OSS-17):

# one-shot, preview first (builds the files, shows the diff, pushes nothing):
5dive proof publish --dry-run --repo=https://github.com/<you>/<repo>.git

# turn on the daily publisher (saves config + installs the cron):
sudo 5dive proof on --repo=https://github.com/<you>/<repo>.git --at=9
5dive proof status          # config, last published date, staleness
sudo 5dive proof off        # stop publishing (config kept)

The cron runs as root by default. Push auth is the box's ambient git credentials, so the cron's effective user must be the one that holds those credentials. If root has no push access on your box (e.g. the token lives with a service user), point the cron at that user:

sudo 5dive proof on --repo=https://github.com/<you>/<repo>.git --at=9 --user=<u>

Otherwise the nightly push fails silently and shows up as a stale badge date. The chosen user is saved and sticks across re-on. Push auth is the box's ambient git credentials; the verb never stores a token. Numbers come from 5dive digest --json verbatim, there is deliberately no flag to edit a number, and re-runs are idempotent per day. On your first publish the verb prints the copy-paste README badge markdown pointing at YOUR status branch. The badge renders from your repo, links back here, and, like ours, moves only when the pipeline runs. Bad weeks and fresh-box zeros publish exactly the same.

Who the commits are authored as

The publisher writes public, permanent commits to your status branch, so it never infers the author. Pin one explicitly:

# saved on the box, used by the daily cron:
sudo 5dive proof on --repo=https://github.com/<you>/<repo>.git \
  --as-name="<you> status bot" --as-email="<user>@users.noreply.github.com"

# or per-invocation:
ZH_GIT_NAME="<you> status bot" ZH_GIT_EMAIL="<user>@users.noreply.github.com" \
  5dive proof publish

Resolution order is ZH_GIT_NAME/ZH_GIT_EMAIL → the identity saved by proof on --as-name/--as-email → the publishing user's git config --global. If none of those is set, proof publish refuses and publishes nothing — it will not author public commits as whatever address the box happens to have lying around. 5dive proof status shows the resolved identity and which of the three it came from.

Prefer a role address you are content to see in public git history forever; GitHub's <user>@users.noreply.github.com form is the usual choice. This matters more on a box you let agents drive: Claude Code puts the operator's email in every agent's system prompt by default with no opt-out (anthropics/claude-code#81138), so on a box with no identity of its own, a personal address is the most available thing for an agent to resolve one with. For the same reason 5dive agent create gives each agent user a synthetic agent-<name>@agents.noreply.5dive.ai identity at provisioning time, unless that user already has one — an identity you set is never overwritten.