# Shipreport

> Write an executive summary of the work actually shipped over a chosen time frame, built from real GitHub contributions and real Claude Code session transcripts, ranked so only what mattered appears, and rendered as a press-styled HTML report. Use when the user says "what did I ship this week", "shipreport", "write my weekly summary", "executive summary of my work", "status report for my manager", "what did I get done last month", "summarize what I shipped in July", or wants their commits, merged PRs, cut releases and coding sessions turned into a report someone who wasn't there can read.

- Skill: `natejswenson/shipreport` (Agent Skill, multi-file: 38 files)
- Install (CLI): `npx skillmds@latest add natejswenson/shipreport`
- Raw SKILL.md: https://api.skillmd.com/api/skills/natejswenson/shipreport/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: natejswenson (https://skillmd.com/u/natejswenson)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/natejswenson/shipreport

---


## Codex runtime

When running in Codex, invoke this skill as `$shipreport`. Resolve scripts, assets,
and references from the directory containing this SKILL.md, regardless of the
current working directory. Existing `~/.claude/` personal-data paths remain valid
and are still used by the bundled scripts; they do not require Claude to run.
Map `Read`/`Write`/`Edit`/`Bash` to the available file and shell tools, and
`WebSearch`/`WebFetch` to available web tools. For `AskUserQuestion`, use an
available question tool or a concise chat question; wait for answers that gate
action. Use Codex's delegation tools for required subagents when available;
otherwise disclose that independent execution is unavailable. Discover connected
apps by capability rather than assuming Claude MCP tool names exist.

In Codex, pass `--transcripts "${CODEX_HOME:-$HOME/.codex}/sessions"` to
`node scripts/shipreport.js index`. The recursive collector accepts Codex rollouts
and Claude transcripts. The default without this flag still reads Claude history;
state which source was indexed. Archived Codex sessions can be indexed separately
with `--transcripts "${CODEX_HOME:-$HOME/.codex}/archived_sessions"`.

# /shipreport — an executive summary of shipped work, where every claim carries a receipt

You are running the **shipreport** skill. It reads a stretch of real GitHub
contributions and real Claude Code sessions, ranks them so only what mattered
appears, and renders a press-styled sheet for someone who was not in the room.

**Announce at start:** "I'm using the shipreport skill to summarise what you shipped."

> Commands below run from the directory containing this `SKILL.md` (`$SKILL_DIR`).
> Resolve it once.

## The one rule

**Every claim in the report carries a receipt — a commit SHA, pull request number, release tag or session event id that resolves against the local corpus — and a ranked item whose receipt does not resolve is dropped from the report, never softened into vague prose.**

The failure this exists to stop is not invention from nothing. It is **drift**:
"fixed the release flow" → "overhauled the release pipeline" → "rebuilt CI/CD",
each step defensible, the last one false. `receipts` is a gate, not a reminder —
it exits non-zero, and `render` runs it again before writing a byte.

## What is code and what is judgment

The split is declared in `skill-invariants.json` and checked — a deterministic
step whose command does not exist fails `skillfactory verify`.

| Deterministic — the machine decides | Command |
|---|---|
| collect GitHub contributions and Claude session transcripts, redact secrets and absolute paths at ingest so nothing unsafe is ever cached, and record a watermark so the second run is cheap | `node scripts/shipreport.js index` |
| score and order every candidate in the requested window, draw the line between what appears in the report and what does not, and say when that line was a tiebreak rather than a ranking | `node scripts/shipreport.js rank` |
| read the artifact behind a receipt — a release's changelog, a pull request's body, a session's shape — from the corpus rather than from the network | `node scripts/shipreport.js show` |
| resolve every citation in the drafted report back to a real artifact, failing the run when one does not resolve | `node scripts/shipreport.js receipts` |
| render the ranked items and approved prose into the press-styled HTML report | `node scripts/shipreport.js render` |

| Model judgment — nothing on disk answers it | Why |
|---|---|
| merge several ranked items into the one outcome a stakeholder would recognise, and choose the headline the time frame was actually about | the score orders candidates but cannot tell that three pull requests were one piece of work, and nothing on disk records which of them a reader would care about |
| write the prose for a reader with no context on the code — plain outcomes, no repository names or commit hashes in the body | translating a diff into why it mattered is judgment, and the audience is a person who was not there |
| decide what an ambiguous session actually accomplished when its commits and its transcript disagree | a session that ends without a commit may have been abandoned or may have been the investigation that made the next day cheap, and only a reader of the transcript can tell |

## The flow

### 1. Resolve the window — never ask what you can read

Default to the last 7 days. The request usually names the window already
("this week", "last month", "since the 1st") — read it, and **never ask about
anything in it**. A confirmation is not a question.

Ask at most one question, and only when the window is genuinely ambiguous.

### Optional audience preferences

Before ranking or composing, use the optional `skill_memory` hook only when a
compatible connected tool and a privately registered subject are available. Read
[the local-memory protocol](references/local-memory.md) first. It defaults off;
missing, disabled or unavailable memory leaves the normal flow unchanged. Recalled
audience/emphasis are presentation guidance only: current instructions, cached
artifacts opened with `show`, deterministic counts and `receipts` remain authoritative.
After delivery, save only an explicitly requested durable preference correction.

### 2. Index

```bash
node scripts/shipreport.js index
```

One line of narration first — this is the slow step. The first run backfills a
year and takes a minute; later runs read the watermark and take seconds.

Report the table it prints. **Do not re-explain the redaction counts** — zero
redactions is the normal case and means nothing matched, not that redaction was
skipped.

### 3. Rank, and read the table

```bash
node scripts/shipreport.js rank --days 7
```

**Paste the table into the conversation.** Not a summary of it — the table. It
shows every item above the line, a few near misses, and the signals behind each
score, which is how the user sees *why* something did or did not make it before a
word of prose exists. A sentence about the ranking is not the ranking.

`rank` says so itself when the cut line falls inside a tie. Repeat that finding;
do not smooth it over. The remedy is to treat the tied items as one body of work
or to raise `--top` — never to let the timestamp choose.

`--kind session` (or `pr`, `release`, `commit`) narrows the table when you want
one source. That flag exists so you never pipe this output through `grep`.

**`rank` also prints the figures the sheet will show** — released, merged,
commits, sessions, repos. Read them and never restate them: they exist here so
the prose is written with the real numbers visible, not so the prose can repeat
them. The strip prints them itself.

### 4. Read what you are about to describe

This is the step that separates a real report from a plausible one. For each item
you intend to feature, read the artifact behind it and write the sentence **from**
it:

```bash
node scripts/shipreport.js show <receipt> <receipt> …
```

One call, every artifact, no network — the bodies were cached and redacted at
index time. **Never run `gh release view` or `gh pr view` here.** Doing so costs a
round trip per item and prints kilobytes of changelog into the conversation, which
this skill's own presentation contract forbids. If `show` reports no body cached,
the corpus predates body caching — run `index --full` once.

**Cite nothing you did not open in this run.** A memory of an artifact from an
earlier run is not a reading of it, and it resolves exactly as well — so the one
rule passes it. `receipts` now names citations that were never opened with
`show`; it reports rather than refuses, because an item read through the ranked
table alone is legitimately cited. Treat the list as a question to answer, not a
warning to dismiss.

`--brief` prints the opening lines of many artifacts at once, for deciding
*which* to read. It deliberately does not count as having read them.

**Never write the sentence first and then hunt for a receipt to attach.** No gate
catches the order you did things in.

### 5. Compose the art — one original scene per card

**Say what you are about to do first**, in one line naming the card count
(`composing 13 card scenes…`). This step takes minutes, and a run that goes
silent for that long has stopped showing its work — the longest gap in the first
real run was nearly four minutes of nothing.

**This is the step that makes the sheet worth looking at, and it is judgment.**
Read `references/illustration.md` first. Each card carries a small line-art scene
composed for *that* item, depicting the concrete mechanism — two versions and a
selector that picked the lower one; a chain whose links all report green and
whose last one is missing; eight near-identical copies collapsing into one
definition.

- **A generic icon is a failure**, however tidy. The title already says what the
  item is; the drawing shows how it worked or failed.
- **No two cards may look alike.** `render` refuses duplicates on a normalised
  fingerprint, but the real test is the eye.
- **Ink only.** The accent budget is already spent on the stamp and the hero
  figure, so a colour literal is refused outright.

### 6. Write the draft

A JSON file — `references/receipts.md` has the shape. Three things to hold:

- **No identifier in any prose field.** No `#412`, no hash, no `owner/repo`.
  The gate fails the draft, and the reason is the audience: an identifier in a
  sentence assumes a reader who already knows your repositories.
- **Do not write a count of shipped things — this is a gate now, not advice.**
  `render` computes the strip, and a count in the headline or standfirst is a
  second copy of a figure printed an inch away. The second real run wrote
  "Eleven components shipped, two of them brand new" above a strip reading 16
  released, with 15 releases cited and 3 of them first — every number wrong,
  and the right ones rendered adjacent. Numbers *inside* an item's prose are
  fine and often the point: they belong to the artifact you are describing, not
  to this window.
- **The line is guidance, not a filter.** Anything in the corpus resolves. If
  the ranking buried something that mattered, cite it anyway — then say which
  weight was wrong.

**Then show what you wrote, and keep going.** Print the headline, the standfirst
and a numbered table (`#`, `section`, `card title`) — then render. Do not wait for
approval: the sheet is a local file, and offering to adjust it afterwards costs
nothing. The point is that the wording reaches the conversation *before* the
browser opens, so it can be argued with rather than discovered.

### 7. Gate it

```bash
node scripts/shipreport.js receipts --draft <file>
```

If it refuses, **fix the draft, never the checker.** A claim that cannot find a
receipt is a claim to delete or shrink — "investigated" is a real, citable
outcome; "fixed" is not, until something merged.

### 8. Render, and show it

```bash
node scripts/shipreport.js render --draft <file> --out <file.html>
```

It opens in the browser on its own. **Never pass `--no-open` in an interactive
session** — the user should see the sheet appear on their own screen, not read
a paragraph about it.

Then report one table (section, items, receipts), one sentence, and stop.
Offer to adjust; do not narrate the design.

## Commands

| Command | Returns |
|---|---|
| `shipreport index` | source, since, seen, new, cached — plus redaction counts and the new watermark |
| `shipreport rank` | rank, kind, item, score, signals, receipt — then the figures the sheet will print, the window/candidates/folds table, and a tie warning when the line is arbitrary |
| `shipreport show` | receipt, kind, when, title — then each artifact's body from the corpus, sharing one total character budget |
| `shipreport receipts` | claim, receipt, resolved — then a verdict with unresolved and prose counts, and any citation never opened with `show`; **exits non-zero on any failure** |
| `shipreport render` | section, items, receipts — then cards, size and window, and the output path |

Useful flags: `--days N` / `--since --until`, `--top`, `--floor`, `--kind
release|pr|commit|session`, `--limit N`, `--near N`, `--all` (print every
candidate), `--chars N` (`show`'s **total** budget, split across the receipts you
ask for), `--full` (force a backfill), `--corpus <dir>`.

**Run every one of these bare.** The output is bounded — `rank` by `--limit`,
`show` by a total character budget that shrinks as you ask for more artifacts.
Piping through `tail` or `head` silently eats the head of a table; `grep` is
never needed, because `--kind` and `--limit` do that job. If some output really
is too long to read, that is a missing bound worth fixing, not a pipeline worth
adding.

## Rules that are not negotiable

- **Every claim in the report carries a receipt — a commit SHA, pull request number, release tag or session event id that resolves against the local corpus — and a ranked item whose receipt does not resolve is dropped from the report, never softened into vague prose.**
- **Never claim a result you did not observe.** Say what you verified and what
  you did not.
- **Never count activity as achievement.** Forty sessions and no releases is a
  report that says exactly that. A thin week produces a short report, and a
  short honest report is the correct output.
- **Never fix a refusal by weakening the gate.** `receipts` is argued with by
  changing the draft. The one exception is a refusal that is simply *wrong* —
  the gate once called the phrase "plus/minus" a repository name — and the fix
  for that is a fix to the checker with a test on both sides, never a reworded
  sentence that was already true.
- **Never shell out for something the corpus holds.** `show` reads bodies that
  `index` already fetched and redacted. A `gh` call in the middle of a run is a
  round trip, a wall of text, and a second unredacted copy of the same data.
- **Never hand-write a brand value.** `assets/report.css`'s `:root` block is a
  press-generated region; change `tokens.json` and re-run `press emit`. Card art
  may paint only `currentColor` or `none` — a hex in a drawing is a brand value
  written down in a second place.
- **Never let a card wear a generic icon.** A scene that would look right on any
  other card has not found the mechanism yet.

<!-- press:runtime -->
In Claude Code, load `/press`; in Codex, load `$press`; then follow the shared PRESS terminal/UI contract from `brand/agent-ui.md`. Do not copy or override that contract here.
<!-- press:runtime -->

