Design Rules & Invariants
PurRDF must stay fast, deterministic, and boring: one engine, one behavior, carried verbatim into Rust, Python, WebAssembly, and C. That promise is kept by a small set of hard invariants, each enforced by CI rather than by convention. The canonical statement is AGENTS.md in the repository; this chapter explains the why.
No semantic Cargo features, ever
The workspace has zero semantic feature flags. Its sole feature declaration
is the empty purrdf-capi:capi = [] compatibility marker that cargo-c
unconditionally enables when building the C ABI. The marker gates no code and
must never appear in cfg(feature = ...); CI verifies its exact empty shape and
scans Rust sources for feature-gated behavior. PurRDF is a data carrier, and
optionality changes semantics per consumer — two builds of “the same version”
that parse or serialize differently would defeat the whole point. No other
[features], no optional dependencies, and no feature-gated behavior. Every
consumer gets the same byte-identical semantics.
PurRDF is NOT an ontology — it mints no vocabulary IRIs
Every vocabulary the library reads or writes — slice manifests,
statement-metadata downcast, box roles, SPARQL extension-function namespaces,
JSON-Schema namespaces — is caller-supplied configuration with no
fabricated default. A feature exercised without its vocabulary hard-errors
or stays inactive. The library never hardcodes a vendor namespace (the GMEOW
ontology is a consumer; the dependency arrow never points from purrdf to
it), and test fixtures use example.org. Consumer-config types (SliceVocab,
Namespaces, StatementMetadataVocab) are unified behind an
OntologyProfile a downstream builds once.
Byte determinism
Serializers and the GTS writer are byte-deterministic. No iteration-order,
time, or RNG dependence is permitted in any output path; hot maps use
fixed-key ahash for this reason. Changes that alter emitted bytes must
update the affected golden files, visibly.
The kernel ring-fence
purrdf-core must never depend on oxigraph or PyO3 — the whole workspace is
oxigraph-free, and a hygiene gate asserts the dependency tree. The three
foundation leaves (purrdf-iri, purrdf-xsd, purrdf-events) keep zero
runtime dependencies. Diagnostics stay structured and SARIF-free in the
kernel; the SARIF boundary is the purrdf-validate leaf.
Everything is wasm-able
Every release crate must build for wasm32-unknown-unknown, and CI
hard-fails otherwise. No dependency may drag in threads, the filesystem, C
toolchains, or wall-clock/RNG syscalls on the wasm path — cryptography stays
pure Rust for exactly this reason. This is what makes the JavaScript package
the same engine rather than a port.
Hard-fail, never wrong
Across the toolkit, out-of-scope input is a typed error, never a partial
answer: malformed RDF is an RdfDiagnostic, an unsupported SPARQL builtin is
EvalError::Unsupported, a malformed ShEx schema is a ShexError, an exhausted
evaluation ceiling is EntailError::Evaluate rather than a truncated closure, and
an unsupported results projection is a typed format error. Lossy-by-design
projections are permitted but loud, via the
loss ledger.
A hard-fail is owed to input the toolkit cannot handle — never to a value its own
signature accepts. purrdf-entail::materialize used to refuse OWL-Direct and
RIF because a Regime value carries neither the query’s class expressions nor a
rule set; that was a partial function wearing a total signature, and the fix was to
change the parameter rather than to document the hole. It takes a Materialization
now, which carries each regime’s own input, and there is no unsupported-regime
error left to name.
The reasoning side adds a second discipline on top of hard-fail: where a run
succeeds but is bounded, it says so. materialize returns a
ReasoningReport with every closure — never a bare dataset — carrying the
regime’s completeness, the rules that did and did not fire, the boundaries met,
and a contract hash of the calculus that ran. A correct-but-incomplete answer
delivered silently is the same failure mode as a wrong one.
Conformance corpora are the contract
The W3C and community test suites are vendored, byte-frozen, and SHA-256-verified; harnesses assert exact counts and enforce XPASS discipline on their expected-failure ledgers. See Conformance & Testing.
Supporting rules
- Measured performance — perf claims require a criterion bench, not an adjective (Performance).
- One version, lockstep releases — crates.io, PyPI, and npm ship one workspace version (Versioning & Releases).
- Nightly-free source, stable MSRV — there are no
#![feature(...)]attributes anywhere in the workspace and the MSRV floor (currently 1.96, on the stable channel) is enforced by a dedicated CI job that builds on exactly that compiler. Contributors and the CI gates run a dated nightly pinned inrust-toolchain.tomlfor its sharper clippy and rustdoc lints; release artifacts are built on stable. - Brand — the project is PurRDF in prose and
purrdfin identifiers (docs/BRAND.md).