Conformance
Three implementations of the Agent Receipt Protocol — the Go, Python, and TypeScript SDKs, separate codebases that share no code — verify one another’s receipts. Every conformance vector is signed once with a single shared Ed25519 keypair; each SDK then re-verifies the signatures, canonical JSON (RFC 8785), and receipt hashes the other two produced. Verification is exercised in both directions: a positive corpus every implementation must accept, and a MUST-reject corpus of tampered receipts and broken chains every implementation must refuse. The frozen vectors span four pinned spec versions (v0.2.0 through v0.5.0), so a receipt emitted by any SDK at any of those versions is provably readable by the other two.
What this cross-language check does and does not prove
Section titled “What this cross-language check does and does not prove”The three SDKs share no code: each is a separate implementation of the same specification in a different language (Go, Python, TypeScript). They must agree byte-for-byte on canonical JSON (RFC 8785) and receipt-hash output, and agree on which signatures are valid. Interop is wired through a single shared Ed25519 keypair: the TypeScript SDK is the canonical source of the keypair (sdk/py/tests/fixtures/ts_vectors.json); the Go and Python SDKs generate their own vectors with that same key. Because the key is shared, any SDK can verify a receipt any other SDK signed — the cross-language property the tests assert.
What this proves. The three language implementations do not diverge in serialisation. A canonicalization, number-formatting, string-escaping, or signature-encoding difference between Go, Python, and TypeScript would produce mismatched bytes or a failed signature and turn a test red. Cross-language byte-level drift is the bug class this suite is built to catch.
What it does not prove. These SDKs are written and maintained by a single author, so they are not independent interpretations of the specification. A single misreading of an ambiguous spec requirement can be encoded identically into all three and still pass every check here — the vectors agree with each other, not with an independent reading of the spec. This suite is a serialisation-drift detector, not a spec-ambiguity detector; catching spec-interpretation ambiguity would require implementations from independent parties, which do not exist yet.
Results matrix
Section titled “Results matrix”Each row is a frozen vector set. The Go / Py / TS columns mark which implementations consume and verify that set in their test suites. Vector counts are generated from the committed JSON by scripts/conformance_matrix/count.py, and a CI reconciliation test asserts every count in this table — and the figures in the callout below — equal that script’s output. It re-runs whenever the vector corpus, the spec, this page, or count.py changes, so an edit that outdates a number here turns the check red before merge.
| Vector set | Purpose | Go | Py | TS | Spec version(s) | Vectors |
|---|---|---|---|---|---|---|
| canonicalization | RFC 8785 canonical JSON + receipt-hash agreement | ✓ | ✓ | ✓ | version-independent | 44 |
| emit-failure | emit-failure outcome contract (ADR-0025) | ✓ | ✓ | ✓ | version-independent | 2 |
| malformed (MUST-reject) | negative corpus every verifier MUST reject | ✓ | ✓ | ✓ | version-independent | 11 |
| v0.2.0 | frozen v0.2.0 receipts + chains | ✓ | ✓ | ✓ | 0.2.0 | 3 |
| v0.3.0 | frozen v0.3.0 receipts + chains | ✓ | ✓ | ✓ | 0.3.0 | 3 |
| v0.4.0 | frozen v0.4.0 receipts + chains | ✓ | ✓ | ✓ | 0.4.0 | 2 |
| v0.5.0 | frozen v0.5.0 receipts + chains | ✓ | ✓ | ✓ | 0.5.0 | 3 |
| did:key resolution | did:key v0.7 resolution wire shape (ADR-0007) | ✓ | ✓ | ✓ | did:key v0.7 | 3 |
| disclosure envelope | parameter-disclosure envelope: pinned ciphertext | ✓ | ✓ | ✓ | envelope v1 | 2 |
| rotation event | key-rotation event verifies under the outgoing key | ✓ | ✓ | ✓ | 0.2.1 | 1 |
Each linked vector set above is pinned to an immutable commit, so a citation stays valid as the corpus grows.
The rotation event row is pinned at spec v0.2.1 rather than 0.2.0 because, per its vector README, every field is exactly what a 0.2.1 receipt already requires with ADR-0015’s keyRotation field layered on and nothing else changed — so its wire shape tracks the 0.2.1 schema baseline it extends.
Clause traceability
Section titled “Clause traceability”Every vector in the MUST-reject corpus names the normative spec clause it enforces, so a reviewer can trace each negative case to the requirement it guards. The Enforces column is generated from each vector’s clause field by count.py — the same generator that emits the counts — and pinned to this page by the reconciliation test; it is never hand-typed. Each entry links to the published spec section it enforces. (The source spec’s §7.3.5 and §7.8 fold into the published chain-integrity-verification section, which has no finer on-page anchor.)
CI enforcement
Section titled “CI enforcement”Cross-language verification is enforced in continuous integration, not run by hand. Every pull request and every push to main that touches an SDK, the shared vectors, or the specification runs that SDK’s cross-language suite:
sdk-go.yml— runs the GoCrossLanguageintegration tests, which verify TypeScript- and Python-signed receipts in Go and reject the malformed corpus.sdk-py.yml— runstests/test_cross_language_go.pyandtests/test_cross_language_ts.py, verifying Go- and TS-signed receipts in Python.sdk-ts.yml— runs the TypeScript cross-language suite, verifying Go- and Python-signed receipts in TypeScript.cross-sdk-tests.yml— runs the shared spec-example and version-pinned vector checks, the CLI↔WASM verifier gate, and the reconciliation test that pins this page’s vector counts tocount.py.
Path filtering scopes each run to the changed surface: a change to the shared vectors or the spec re-runs all three SDK suites; a change confined to one SDK re-runs at least that SDK’s cross-language verification. A regression that breaks interop — a canonicalization drift, a signature-encoding change, a verifier that stops rejecting a malformed receipt — turns a required check red before merge.
Run it yourself
Section titled “Run it yourself”The frozen vectors and the suites that consume them live in the repository. Run each line from the repository root; the per-suite commands use a subshell so the directory change does not leak into the next line:
# Go: verify TS- and Python-signed receipts, reject the malformed corpus(cd sdk/go && go test -tags=integration -v -run CrossLanguage)
# Python: verify Go- and TS-signed receipts(cd sdk/py && uv run pytest tests/test_cross_language_go.py tests/test_cross_language_ts.py -v)
# TypeScript: verify Go- and Python-signed receipts(cd sdk/ts && pnpm test -- cross-language)
# Shared spec-example and version-pinned vector checks(cd cross-sdk-tests && go test -tags=integration -v)
# did:key resolution vectors (ADR-0007) — one command per SDK(cd sdk/go && go test -tags=integration -v -run TestDIDKeyVectors ./did/...)(cd sdk/py && uv run pytest tests/test_did_key_vectors.py -v)(cd sdk/ts && pnpm exec vitest run did-key-vectors)
# Regenerate the vector counts in the matrix abovepython3 scripts/conformance_matrix/count.py --format md
# Regenerate the "Enforces" clause-traceability tablepython3 scripts/conformance_matrix/count.py --format enforcesSee cross-sdk-tests/README.md for how the shared vectors are generated and consumed.