Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 in rust-toolchain.toml for its sharper clippy and rustdoc lints; release artifacts are built on stable.
  • Brand — the project is PurRDF in prose and purrdf in identifiers (docs/BRAND.md).