Pure-JSON test vectors. Any implementation — in any language — can pass this
kit by reading only SPEC.md, schema-1.0.json, and this file. No
Node, no Python, no burn-specific code is required to write a runner.
Two runners already exist and MUST stay green in CI:
Both exit 0 when every vector passes and 2 on any failure.
Each vectors/*.json file is one vector:
{
"id": "K06",
"name": "human-readable claim being proven",
"kind": "validate | validate-both | merge",
"bundle": { ... }, // kind=validate / validate-both
"bundles": [ {...}, ... ], // kind=merge
"expectSameReversed": true, // optional, kind=merge only
"expect": { ... } // per-kind expectations, below
}The vectors directory may be overridden with the ULP_VECTORS_DIR env var so
implementations can also run private vectors through the same tooling.
expect.ok is a boolean: does bundle validate against
ulp/schema-1.0.json in strict mode (unknown non-x-* fields are
errors) — matching ULP's current-minor behavior.
expect.strictOk and expect.relaxOk: the same bundle validated strictly and
then with the newer-minor relaxation (unknown non-x-* fields ignored). A
conformant reader answers the version question before the field question —
see SPEC.md §Negotiation.
Merge bundles per ULP §Merge semantics — identity
(deviceId, agent, sessionId, index-within-session), set-union, totals
recomputed from the merged event set (never summed from pre-aggregated
rows) — then compare against expect.totals:
"expect": {
"totals": {
"events": 3, "cost": 20.25, "cacheHit": 0.7272727272727273,
"sources": { "store": 1, "estimate": 1, "unpriced": 1 },
"repos": { "shared-repo": 9.0, "a-only": 11.25 }
}
}Comparison tolerances: exact for counts; absolute difference < 1e-9 for
cost, cacheHit, and each per-repo cost. repos must match in both the
listed costs and the total number of repo buckets (an extra or missing
bucket is a failure even if every listed number matches). sources counts
events by provenance class (store / estimate / unpriced).
If expectSameReversed is true, the merge MUST produce identical totals when
bundles is given in reverse order — agreement is order-free, not
order-tolerant.
K01a minimal bundle is accepted; the schema is satisfiable.K02unknown non-x-*fields fail at the current minor and are ignored only under the newer-minor relaxation.K03x-<impl>-*extension fields are legal without registration.K04absolute paths (dir, non-~/store paths) are rejected — privacy P3.K05a stored cost of0stays provenancestore(billed zero ≠ unpriced).K06merge unions devices, recomputes buckets from events (not sum-of-sums), and is order-free.K07merge(A, A) == A— union identity; re-ingesting cannot double-count.K08unpriced tokens still count; dollars are withheld (provenance rule R3).
- Add
vectors/KNN-short-name.jsonfollowing the format above. - Keep it a claim about the protocol, not about burn's UX.
- Run both runners; they must agree. If they disagree, the protocol — not the implementation — is under-specified; fix SPEC.md first.
