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 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
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
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:
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.
renderrefuses 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, noowner/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.
rendercomputes 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
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
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.
receiptsis 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.
showreads bodies thatindexalready fetched and redacted. Aghcall 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:rootblock is a press-generated region; changetokens.jsonand re-runpress emit. Card art may paint onlycurrentColorornone— 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.
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.