# Shipped

Joins each session to the commits it produced at a labelled confidence, then reports the measured cost of the sessions that left no joined commit and the ones whose commits were all reverted.

---

Every other analyzer asks what a session cost. Shipped asks what it left behind. It joins each session to the commits it produced, records the confidence of each join, and reports how many of the window's sessions put a commit on the default branch that stayed there. Alongside that sits the measured cost of two groups the rollup sums together: the sessions that left no joined commit at all, and the ones whose commits were all backed out. Sessions whose commits exist but have not reached the default branch are counted separately.

```bash
tj optimize shipped
```

The analyzer itself runs no git. It reads tables the matcher keeps current, so the figures can be served from a stored report. The matcher runs earlier in the same daemon pass, and ahead of a direct-database `tj optimize` when no daemon is running, which means the finding is at most one pass behind your repo.

## How a session is joined to a commit

Read-only git, bounded to the session's own window (60 seconds before it started to 15 minutes after it ended), with a two-second timeout, and never on a request path. A repo whose checkout no longer exists is skipped. Each `(session, commit)` pair is written once at the best confidence the evidence supports, and a later pass can raise a row but never lowers one.

| Confidence | Source | Evidence |
|---|---|---|
| `deterministic` | `tool_span_git_log` | a `git commit` Bash tool call inside the session, within 30 seconds of the commit's own timestamp |
| `deterministic` | `trailer_session` | the commit body carries `TokenJam-Session: <id>` or `Claude-Session: <url>` naming a session tj has ingested |
| `deterministic` | `git_note` | a note on the commit under `refs/notes/ai`, `refs/notes/tokenjam`, or one other third-party attestation ref, names a session tj has ingested |
| `inferred` | `trailer_window` | an AI co-author trailer, on the session's own branch, inside the session window, with no stronger evidence pointing elsewhere |

The confidence travels with the row. It is stored per `(session, commit)` pair, and every surface that renders a commit renders the label with it. Nothing in a local database is ever written at `estimated`; that value belongs to an allocation with no session evidence behind it.

An inherited trailer ranks below the tool call that ran the commit. A subagent inherits its parent's session id, so a commit the subagent's own Bash tool produced still carries the parent's trailer. When a trailer names a different session than a tool span does, the row is stored at `inferred`. Without that rule a parent session would absorb its subagents' commits at the top confidence.

`refs/notes/tokenjam` is written only when you opt in with `tj init --notes`. The other two refs are read and never written.

## The five states

Derived at read time from the join plus an index of the default branch that the same pass keeps current.

| State | Meaning |
|---|---|
| `shipped` | a joined commit is reachable from the default branch and was not reverted |
| `committed` | joined commits exist, none of them on the default branch yet |
| `reverted` | every joined commit was reverted by a `Revert "..."` commit naming its sha |
| `unshipped` | no joined commit at all |
| `no_repo` | the session had no repo context and was never analysed |

`committed` and `reverted` are states in their own right, and neither is counted as shipped. A session whose work landed and was then backed out is a different fact from a session that produced no commit.

Where that distinction survives, and where it does not, matters when you read a number. The per-session state is reported as `reverted` everywhere a single session is rendered: `shipped_state` on the API, the Sessions view and the session page in Lens. The window rollup has no `reverted` counter, so a reverted session is counted in `sessions_unshipped` and its cost in `cost_unshipped_usd`. Rows in `top_unshipped` each carry their own `state`, which is where you can tell the two apart.

## What the finding carries

`sessions_total` and the three counts that add up to it: `sessions_shipped`, `sessions_committed` and `sessions_unshipped`, each with its measured cost. `sessions_no_repo` sits outside that total, counted on its own, because those sessions were never analysed and rolling them into any state would claim a result nobody produced.

Then `commits_joined` and `commits_on_default`, the largest unshipped sessions, and two figures that need their own basis line:

- **`cost_rework_usd`**: the cost of sessions where at least half of the lines their commits added were deleted again by other work inside 14 days. Measured from git numstat on the commit's own files; a commit's own session is excluded from the deletions counted against it. The finding carries a `rework_basis` string naming how many sessions had line counts to measure and how many qualified.
- **`cost_loop_usd`**: consecutive Edit or Write calls on one path with identical content, priced at the model turn that issued each repeat. When `[capture] tool_inputs` is off the content cannot be compared, so repeats are counted on path alone and `loop_basis` says so.

Both are `None` rather than zero when there is no evidence yet. Zero would claim the analyzer looked and found nothing.

## Coverage qualifies every figure

`coverage` is the share of the default-branch commits in the window, on the repos this window's sessions ran in and authored by those sessions' developers, that carry a `deterministic` or `inferred` row. Reverts are excluded from the pool. When there is no such commit to cover, coverage is `None`.

Read every figure on this finding through that number. At 40% coverage, "12 of 30 sessions shipped" describes the joins tj could make, not the work your team did. The hooks that raise it are `tj init --hooks`, which writes a `TokenJam-Session` trailer onto commits made from a plain shell while a session is open, and `tj init --notes`, which adds the post-commit note.

## This is not a saving

Unshipped cost is **measured** spend on two kinds of session: the ones that left no joined commit, and the ones whose commits were all reverted. The rollup sums them and publishes no split between them. It is a fact about output, not money to claw back. The finding publishes no `past_overspend_*` field and sits outside the recoverable-waste rollup on purpose: pulling it in would price research, review and operations work as waste.

A session can ship real value with no commit. Reading a codebase before a design decision, reviewing someone else's branch, working through a failing test: none of those produce a commit, and each one lands in `unshipped` when it ran inside a repo. The same work done outside a repo lands in `sessions_no_repo`, outside the total.

The caveat is carried as a dataclass default so no surface can drop it, and it reads:

> Unshipped is measured cost of sessions with no joined commit; a session can ship value without a commit (research, review, ops). Review before acting.

## Where it shows up

`tj optimize shipped`, and `tj optimize shipped --json` for the machine-readable form. The `tj status --agent <id>` card carries a "Shipped N of M sessions" line. Over HTTP it is `GET /api/v1/shipped`, plus `commits` and `shipped_state` on `GET /api/v1/sessions/{id}`. Those are routes on the local API `tj serve` runs, `http://127.0.0.1:7391` by default, not this website's public content API at the same path shape. Its read routes are unauthenticated until you set `[api.auth] enabled = true` and an `api_key`; see [Local API auth](/docs/configuration#local-api-auth). In Lens it appears as the shipped column and filter in the Sessions view, the commit chips with their confidence glyph on a session page, the "Shipped this week" tile on the Dashboard, and the Shipped card on the Optimize page.

## See also

- [How the analyzers work](/docs/optimize-overview) — the full registry and how findings are ranked
- [Cost visibility](/docs/optimize-cost-visibility) — the spend-facing half of the same window
- [CLI reference](/docs/cli) — `tj init --hooks` and `tj init --notes`