The tarball contains its own contract.
31 July 2026 · the launch journal
There are two ways a machine caller learns a CLI. It can probe: run the tool, parse whatever comes out, guess at the failure modes, and hope the next release guesses the same way. Or it can read: open a file that states the verb set, the output discipline, and the exit codes as promises, and integrate against the promises. Probing is what agents do to tools that never wrote anything down. This package chose the other path: the contract is a file in the tarball, at node_modules/epcis.dev/AGENTS.md, and its first line of business says why — "grep it where you already are. It is the contract, not marketing."
What the contract promises
The load-bearing clauses, as the shipped file states them:
- Verbs (closed set): validate, hash, translate, capture, mcp, conformance run, conformance rejudge, pins, version. Bare npx epcis.dev prints the orientation. Unknown verbs are a typed USAGE refusal with a hint, exit 2.
- --json on every verb. Deterministic machine JSON — same data and same exit code as text mode. Text mode is token-cheap TSV-style lines: never colored, never wrapped.
- stdout is payload-only. Errors are typed on stderr: {"error":{"code","message","hint"?}} in json mode, error\tcode=… in text mode. JSON.parse(stdout) never sees a half-written object.
- Exit codes (stable): 0 ok · 1 fail (a negative domain verdict: invalid document, failed translation, rejected capture) · 2 usage · 3 not-found · 4 internal.
- Error code strings are stable across releases — match on code, never on prose.
The closed verb set is the quiet promise most tools refuse to make. It means no surprise surface area between versions: the nine verbs an agent persisted in its residency block are the nine verbs that exist, and an unknown verb is a typed refusal rather than an adventure.
Three commands, contract held
Not claims — a transcript, run against the shipped fixtures:
$ npx epcis.dev validate golden-corpus/valid-standard/aggregation-event-packing.json
valid schema=epcis-json-schema.json version=2.0.1
$ echo $?
0
$ npx epcis.dev validate golden-corpus/invalid/object-event-missing-action.json
schema-error /epcisBody/eventList/0 must have required property 'action'
schema-error /epcisBody/eventList/0 must match "then" schema
schema-error / must match "then" schema
invalid errors=3
$ echo $?
1
$ npx epcis.dev frobnicate
error code=USAGE message=unknown verb "frobnicate" hint=run `npx epcis.dev` with no arguments for the verb map
$ echo $?
2
Read the second one carefully, because it is the clause agents get wrong about every other tool: exit 1 is not a crash. The validator did its job perfectly — it found an invalid document and said so, with JSON-Pointer paths on stdout as payload. The fail-closed path working is exit 1; the tool itself breaking is exit 4. A caller that treats every nonzero exit as an error will retry a verdict, and a verdict does not change on retry. (The HTTP side of this contract — typed problem documents an agent can branch on — keeps the same law.) Distinct codes are what let a machine build the correct branch: 0 proceed, 1 route the finding, 2 fix the call, 3 fix the path, 4 escalate.
The same run in --json returns the same verdict as structured payload — including the provenance of the ruler it was measured with:
{ "valid": true,
"schema": { "artefact": "epcis-json-schema.json", "version": "2.0.1",
"sha256": "0f46ff694efffd8d8ce840a33dfde84228add11b516b8b258f3200740ae210af" } }
That sha256 is the pin of GS1's own published schema, checkable against /conformance/ and against ref.gs1.org directly. Even the validation result carries its evidence.
Token economics are contract clauses too
Text mode being TSV-style is not aesthetics. An agent pays for every byte it reads back, so the default output is engineered to be the cheapest faithful representation: one tab-separated line per fact, no color codes to strip, no wrapping to reassemble, no banner. The hash verb answers one line per event — hash 0 ni:///sha-256;e5624abc…?ver=CBV2.0 — and nothing else. When an agent wants structure, --json is there on every verb with identical semantics; when it wants cheap, text mode is already minimal. Both modes keep stdout payload-only, so the pipe never carries prose that has to be recognized and discarded.
Why put the contract in the tarball instead of only on a website? Because the file is versioned with the code it describes. The AGENTS.md an agent reads at node_modules/epcis.dev/AGENTS.md shipped in the same publish as the binary it documents — there is no skew between the docs revision and the tool revision, and no network fetch to a page that may describe a newer release. The contract also carries the sharp edges honestly, in a section titled Hazards: translate in text mode prints the document as line 2 of stdout (use --json when piping); the hash algorithm itself excludes eventID and recordTime per CBV 2.0 §8.9, so do not pre-strip anything; conformance runs are advisory and unsigned, never anyone's attestation. Extension fields, where the corpus carries them, ride under a declared JSON-LD context, and the projection always validates against the pinned official EPCIS 2.0.1 schema — the file states both halves of that sentence and forbids restating either half alone.
The idiom repeats across the family by design. The business-transaction layer's biztx CLI ships the same laws verb for verb — --json everywhere, payload on stdout, typed stderr, exits 0/1/2 — so an agent that learned one door has learned the family.
npx epcis.dev, then read the file. The integration is a read, not a reverse-engineering project.
Sources: node_modules/epcis.dev/AGENTS.md (contract clauses quoted from the shipped file); CLI transcript captured 2026-07-31 against the packaged golden corpus; pinned-schema digests at /conformance/ and ref.gs1.org.
We answer in writing. We take at most five conversations a month, only when you ask for one, and only after you already have the written read.