Testing
This is the org-wide guide for test contributions. The ajar spec repo is the current source for schemas, examples, seed vectors, and validation tools.
This is the org-wide guide for test contributions. The ajar spec repo is the
current source for schemas, examples, seed vectors, and validation tools.
Test Taxonomy
-
Schema validation
Owner:
ajar/schemas/*.schema.jsonandajar/examples.Valid examples live under
ajar/examples/manifests,ajar/examples/implementer-manifests,ajar/examples/views,ajar/examples/policies,ajar/examples/errors, andajar/examples/scenario-tickets. Invalid examples live underajar/examples/invalidand are indexed byajar/examples/invalid/index.json. -
Conformance vectors
Owner:
ajar/test-vectors, graduating toconformance.Current vector files are
core-vectors.json,runtime-vectors.json,scope-vectors.json,extension-vectors.json,manifest-check-vectors.json,http-signature-vectors.json, andcrypto-signing.json. Every spec MUST maps to at least one vector throughtest-vectors/must-coverage.json, enforced bytools/check_must_coverage.py. Sub-clause gaps remain visible as Follow-up markers intest-vectors/must-coverage.md. -
Adversarial cases
Owner: vectors and examples derived from
docs/04-SECURITY-MODEL.md.The threat catalogue calls out prompt injection, forged manifests, expired offers, over-cap commits, rollback, and injection corpora. Adversarial parity is required for compatibility claims. Passing these cases is release-blocking.
-
End-to-end scenarios
Owner: full protocol walks such as
ajar/examples/scenario-tickets.The canonical scenario is a 50-ticket purchase. It covers mandate -> simulate -> offer -> commit -> receipt with real signing vectors. Under T1.14, these scenarios become executable harness runs in
conformance.
Author A Conformance Vector
-
Choose the spec MUST.
-
Use
ajar/test-vectors/must-coverage.jsonto find the requirement id or add one when the MUST is new. -
Pick the vector file by surface:
core-vectors.jsonfor schema-backed protocol artifacts.runtime-vectors.jsonfor executable decision rules.scope-vectors.jsonfor mandate-scope matching.extension-vectors.jsonfor version and extension policy.manifest-check-vectors.jsonfor implementer-facing manifest checks.http-signature-vectors.jsonfor signed agent HTTP requests.
-
Follow the existing
kind,input,data, andexpectedshape for that file. Do not invent a parallel format. -
Wire every reject verdict to a registry error code from
ajar/registries/error-codes.md. -
Add the vector id to
test-vectors/must-coverage.json. -
Add the human-readable row or vector id to
test-vectors/must-coverage.md. -
Run:
make validate
make must- If vector counts change, update the expected-count lines in
ajar/README.mdandajar/tools/check_phase0.py. - Vectors may be added. They must not be weakened.
Runtime vectors are checked by runtime_verdict in
ajar/tools/validate_examples.py. Current kind values are http_surface,
domain_binding, kid_resolution, client_action_sequence,
commit_offer_state, mandate_required, mandate_status,
simulate_equivalence, simulation_offer_binding, view_provenance,
fallback_operation, version_change, and aep_requirement.
Worked runtime vector:
{
"id": "client-r3-propose-without-simulate",
"spec_section": "6",
"kind": "client_action_sequence",
"input": {
"action_risk": "R3",
"attempted_mode": "propose",
"prior_simulation": false
},
"expected": {
"verdict": "reject",
"error_code": "AJAR-SIMULATE-REQUIRED"
}
}Core vectors use a schema-backed artifact path:
{
"id": "offer-expired-at-issue",
"profile": "ACT",
"spec_section": "7",
"schema": "offer.schema.json",
"input": "../examples/invalid/expired-offer/offer.json",
"rule": "offer-expired-at-issue",
"expected": {
"verdict": "reject",
"error_code": "AJAR-OFFER-EXPIRED"
}
}Author An Invalid Example
Invalid examples are declared in ajar/examples/invalid/index.json.
Each case has:
idpathschemaexpectedrulewhen the failure is semanticerror_codewhy
expected is either schema-fail or semantic-fail.
Current semantic rules in tools/validate_examples.py are
offer-expired-at-issue, offer-exceeds-mandate-cap,
manifest-sequence-rollback, manifest-expired-at-issue, and
manifest-lifetime-over-180d.
Example index entry:
{
"id": "expired-offer",
"path": "expired-offer/offer.json",
"schema": "offer.schema.json",
"expected": "semantic-fail",
"rule": "offer-expired-at-issue",
"error_code": "AJAR-OFFER-EXPIRED",
"why": "expires_at is earlier than issued_at."
}The invalid artifact must pass its JSON Schema before a semantic failure is checked. Schema-fail cases must fail the named schema directly.
Produce Signed Fixtures
Use ajar/tools/signing_profile.py.
The helper implements draft canonicalization for these fixtures: UTF-8 JSON,
sorted keys, no insignificant whitespace, and removal of the artifact's own
signature field before signing. Receipts remove site_signature and
agent_signature before signing.
Use obviously fake seeds. Never use real-looking keys.
python3 tools/signing_profile.py public-key 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
python3 tools/signing_profile.py sign --type offer --seed-hex 404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f examples/scenario-tickets/offer.json
python3 tools/signing_profile.py verify --type offer --public-key JUO5L_EJVRFHatyDadtt3JM2ZaEZeN2hQE7hBmypVZ0 --signature JxqIuUE8oAKHT44Kh8500yq9Jn2IFhzifut29kCSRmrY8k8hYIOQUHHRpT-wfuRyYoDKXDRpSQkAcA1vaTXGCw examples/scenario-tickets/offer.jsonCanonical bytes, public key, SHA-256 digest, and signature land in ajar/test-vectors/crypto-signing.json. Signature changes are crypto-change
PRs and require human review.
Contribute An End-To-End Scenario
Start from ajar/examples/scenario-tickets.
Current files:
mandate.jsonsimulation.jsonoffer.jsonreceipt.json
A new scenario must include:
- Every artifact schema-valid under
tools/validate_examples.py. - Signatures verifiable by
tools/signing_profile.pyand represented intest-vectors/crypto-signing.jsonwhen canonical bytes are part of the fixture. - At least one deliberate failure branch with its registry error code.
- A README stating which spec sections the scenario exercises.
Use the real protocol order: manifest -> simulate -> offer -> commit -> receipt.
Adversarial Contributions
Adversarial PRs are welcome when they encode documented threats from
docs/04-SECURITY-MODEL.md.
Do not use a public PR to disclose a new vulnerability. Follow SECURITY.md
first. Add the adversarial case after the fix or after maintainers confirm that
the threat is already documented.
Implementation Tests
Gateway and Kernel tests are forward-looking but the rule is already fixed: logic needs unit tests, and the conformance harness must stay green.
Anything touching the docs/04-SECURITY-MODEL.md catalogue needs an
adversarial test in the same PR.
Do not weaken, skip, or mark-flaky a conformance or adversarial case to pass CI. End-to-end Definition of Done evidence belongs in the PR: command output, harness report, or documented trace.
Running Everything
For ajar work:
python3 -m venv .venv
. .venv/bin/activate
python3 -m pip install -r requirements-dev.txt
make validate
make phase0
make manifest-checkTargets:
make examples: runstools/validate_examples.py.make must: runstools/check_must_coverage.py.make links: runstools/check_links.py.make markdown: runstools/check_markdown.py.make validate: runsexamples,must,links, andmarkdown.make phase0: runstools/check_phase0.py.make manifest-check: checksexamples/manifests/blog-core.jsonas served from/.well-known/ajar.json.
Expected green output:
Validated 12 valid examples, 8 invalid examples, 4 signing vectors, 2 HTTP signature vectors, 5 extension vectors, 6 manifest check vectors, 11 core vectors, 25 runtime vectors, and 12 scope vectors.
MUST coverage OK: 24 requirements mapped.Contributing to Ajar
> Org path convention: this file applies org-wide (lives in the .github repo). References like docs/03-PROTOCOL-SPEC.md, GLOSSARY.md,...
AGENTS.md — Instructions for AI Coding Agents
> Org path convention: this file and ENGINEERING.md apply org-wide (live in the .github repo). References like docs/03-PROTOCOL-SPEC.md,...