@flashyos/shiplog
v0.1.0
Published
shipped/1 — the past tense of a record. One sealed entry per thing that shipped, emitted per repository and merged into one cross-repository calendar, changelog and attribution report. Verifiable offline; the past is append-only.
Maintainers
Readme
@flashyos/shiplog
shipped/1 — the past tense of a record.
The work layer already requires evidence: an initiative task cannot be
completed without an evidenceUrl, and the settlement receipt seals it. But
the evidence is a string somebody pasted. It is not attributed, not hashed,
and not checkable by the counterparty being asked to believe it. A receipt that
seals an unverifiable URL proves that a URL was pasted.
shipped/1 is the substrate that evidence was always supposed to point at. One
sealed entry per thing that reached the branch that deploys — dated, attributed
to the agents and humans who did it, emitted by the repository that shipped it,
merged across a portfolio.
npm i @flashyos/shiplogTwo things fall straight out of the shape
The calendar is a projection, not a service. A merged shiplog sorted by
at is the cross-repository calendar. There is nothing to integrate with: a
portfolio company publishes one file and appears in it.
Agent authorship becomes a measured fact. by carries Directory node ids,
and an agent id is as first-class as a person's. The share of shipping done by
agents stops being a claim in a deck and becomes a number anyone can recompute
from published documents — which is the only form of that number worth quoting.
Use it
import { seal, verifyEntry, merge, view, calendar, changelog, attribution, fromCommits } from '@flashyos/shiplog'
// Derive from the history you already have. Everything lands private.
const { entries, unmapped } = fromCommits(parseGitLog(gitOutput), {
repo: 'repo/flashyos',
assertedBy: 'agent/flashyos-ci',
defaultAuthor: 'person/michael',
authors: { '[email protected]': 'agent/flashyos-builder' },
prBase: 'https://github.com/FlashyLabs/flashyos/pull/',
})
unmapped // git emails no mapping covered — fix these, don't ignore them
// Many repositories, one record.
const { shiplog, problems } = merge([fragmentA, fragmentB, fragmentC])
calendar(shiplog.entries, { grain: 'week', includeEmpty: true }) // the calendar
changelog(shiplog.entries, { from: '2026-07-01T00:00:00Z' }) // the changelog
attribution(shiplog.entries).agentShare // 0.62, and recomputable
verifyEntry(entry) // { ok, claimed, recomputed }The seal
An entry's digest is sha256 over its own canonical form with digest
removed, through @flashyos/verify — the same canonicalisation that seals
every settlement in the estate. Never a second one: a second canonicalisation
is a way to make honest records look forged.
Change the title, the date, the authors or the refs and the digest moves. That is what makes an entry quotable: a settlement receipt can carry the digest, and anyone with the published fragment recomputes it offline without asking us anything.
The past is append-only. A wrong entry is superseded by a new one pointing
back at it, never edited — an edited entry has a different digest, and every
receipt that quoted the old one stops verifying. current() is the view that
hides superseded entries; the log keeps them.
The asymmetry with backlog/1
A backlog item decays and is never sealed. A ship entry is sealed and never decays.
That is not an inconsistency, it is the difference between the two tenses. An intention stops being evidence of anything once nobody has restated it; a fact about the past does not become less true because time passed. What the past needs is not a decay window but a hash, so nobody can revise it quietly.
Saying what a commit was
A derived log is only as good as what it can read off a subject line, and most
good repositories do not write feat:. The precedence chain, most
authoritative first — every step above the last is something somebody said:
.shiplog/kinds.json— a person's decision, by shaKind: docsin the commit body — the author saying it outright- A conventional-commit type
"lexicon": "imperative"in the config — your own declared conventionother— we could not tell
The lexicon is opt-in on purpose. Declare nothing and anything unprefixed stays
other, because reading a convention nobody declared is guessing. Ambiguous
verbs go to infra, never feature: a chore filed as a feature inflates the
only number most readers look at.
Merges of another branch into your line are dropped — that is not a thing that
shipped — and a project prefix like Phase 3: is looked past to the verb
behind it.
For the entries nothing can read:
npx flashy-shiplog unread shiplog.fragment.json --draft .shiplog/kinds.jsonEvery value in that draft is other, never a guess. A draft pre-filled with
plausible answers is one that gets accepted unread.
The command line
flashy-shiplog check <file|dir> validate and re-verify every seal
flashy-shiplog merge <file|dir> [--tier T] [--out F]
flashy-shiplog calendar <file|dir> [--grain day|week|month] [--from D] [--to D]
flashy-shiplog changelog <file|dir> [--from D] [--to D]
flashy-shiplog velocity <file|dir> [--grain G] [--periods N]
flashy-shiplog who <file|dir> who did the shipping
flashy-shiplog summary <file|dir>
flashy-shiplog unread <file|dir> [--draft F] what the classifier could not read
flashy-shiplog pilot [--minutes N] the four numbers, from this repositorypilot reads this repository's own fragment and config and prints a report:
how much history the format saw, how much of it no convention could read, the
share carrying an agent author, whether it is reachable, and how much is
published. Nothing is sent anywhere — it is yours to keep or paste back.
Paths, not a base URL. Every number it prints is recomputed from the documents in front of it — a reader who does not trust us runs the same command.
Adopt it in one command
npx @flashyos/shiplog adoptReads the repository — its remote, its AAO charter if it has one, and which
directory it actually serves from — then writes .shiplog/config.json, vendors
the dependency-free emitter, and adds a workflow that keeps the log current.
It tells you every decision it made and why, including the ones you should
check.
Adopting publishes nothing. Everything derived lands private, so the
served fragment is empty until somebody decides otherwise. That decision is one
field in the config, and it is meant to be a decision.
The served copy is always the public projection. The emitter writes it, not
a copy step in a workflow — a step can be forgotten, and forgetting this one
would publish private entries at a URL. partner is excluded too: a
partner-tier entry behind no gate is a public entry with a misleading label.
| Repository shape | Where the public projection lands |
|---|---|
| Next app, or a static builder with a public/ | public/.well-known/shiplog.json |
| Monorepo with a marketing app | apps/marketing/public/.well-known/shiplog.json |
| Serves its dot-directory through a rewrite | well-known/shiplog.json — and you add the rewrite |
| No site at all | nowhere; the fragment sits at the repository root and is merged from a checkout |
No toolchain? vendor-shiplog.mjs
A single dependency-free ESM file. Copy it into any repository — including one
with no package.json — and it derives, seals and emits a valid fragment from
git log with node alone.
node vendor-shiplog.mjs emit --since 2026-01-01 # → shiplog.fragment.json
node vendor-shiplog.mjs verify shiplog.fragment.jsonIt reads --first-parent, so an entry is a merge to the deploy branch rather
than every commit: a log of every keystroke is technically accurate, unreadable,
and turns a velocity chart into a measure of typing. It lands everything
private, so a first backfill over a decade of branch names is never an
unreviewed disclosure.
src/vendor.test.ts asserts the vendored canonicalisation produces byte-identical
digests to this package. A copy that seals almost the same way produces
entries that fail verification for everyone else — which would surface as "your
changelog is forged" rather than as a bug in a script.
Licence
Apache-2.0. The spec and the clients are permissive on purpose — a standard nobody can embed is not a standard.
