# CI

The toolkit's contract with your pipeline is its exit codes. They are
stable across releases, they distinguish a negative verdict from a
crash, and they need no wrapper: `npx epcis.dev validate` is already a
CI step. Everything runs in-process on the runner — no service, no key,
no network beyond fetching the package.

## The two exits that gate a merge

Real runs against the fixtures shipped inside the tarball:

```
$ npx epcis.dev validate \
    node_modules/epcis.dev/golden-corpus/valid-standard/\
object-event-shipping.json
valid  schema=epcis-json-schema.json  version=2.0.1
$ echo $?
0

$ npx epcis.dev validate \
    node_modules/epcis.dev/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
```

The full taxonomy, stable across releases: `0` ok · `1` fail (negative
domain verdict) · `2` usage · `3` not-found (unreadable input) · `4`
internal. Gate on `0`; treat `1` as "the document is wrong", and `2–4`
as "the pipeline is wrong". Error strings on stderr carry stable `code`
values — match on code, never on prose. The whole contract is on the
[CLI reference](/docs/reference/cli/).

## The six-line workflow

```yaml
on: [push]
jobs:
  epcis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx -y epcis.dev@0.1.0 validate events/outbound.json
```

That is the whole gate: the step fails on any nonzero exit, so an
invalid document blocks the merge with the per-path schema errors in
the job log. Pin the version, as shown — the verdict is then
reproducible byte-for-byte, because the official GS1 schema the verdict
is against ships inside that exact package with a sha256 pin
([pins](/docs/conformance/pins/)).

## Gate more than the schema

Add lines for the other verbs where they earn their keep:

```yaml
      - run: npx -y epcis.dev@0.1.0 hash events/outbound.json
      - run: npx -y epcis.dev@0.1.0 conformance run --self
```

`hash` exits 0 with the CBV 2.0 §8.9 identity of every event — diff the
ni URIs against yesterday's to catch silent edits. `conformance run
--self` proves the advisory 18-check suite on the runner; a run is
advisory and unsigned, and can never back an attestation — the boundary
is stated in full on [run & rejudge](/docs/conformance/run-rejudge/).

Use the shipped corpus as your own test suite — the corpus tiers and their
expected exits are on [the golden corpus](/docs/conformance/golden-corpus/).

---

**Proof, not adjectives.** This build could not verify the gateway's test suite green, so no sentence on this page claims a passing run — the last verified run is recorded in the repository's own CI, not here. Live on this origin, no key: POST /translate, /validate, /hash. No conformance attestation has ever been issued. The dated ledger is /what-ships-today/.
