Implementation note. The execution receipt and per-output provenance are implemented (
packages/engine/src/receipt.rs,OutputProvenanceinengine.rs). The CIprovenance-checksjob enforces tag-based$schemaURLs across the corpus.
The engine produces deterministic results: given the same regulation YAML, the same inputs, and the same calculation date, it always produces the same outputs. RFC-006 chose Rust precisely for this guarantee. Determinism within a single execution is not enough. Government agencies must be able to reproduce a specific decision months or years later, with the exact same result.
Dutch administrative law requires this:
Reproducibility requires pinning three things: the regulation YAML, the schema version it conforms to, and the engine version that executed it. Today, the engine stamps none of these on its output. The regulation carries a $schema URL, but the engine ignores it at execution time. The engine version exists as a Cargo constant but is not included in results. There is no “decision receipt” that ties inputs, outputs, and versions together.
RFC-009 (Multi-Organization Execution) adds another dimension: a decision may depend on values accepted from other organizations’ engines, each potentially running a different engine version. Reproducing that decision requires knowing which engine this organization ran, and which engine versions produced the accepted values from other organizations.
The schema defines the regulation format: what fields exist, what operations are valid, what structures are allowed. The engine interprets and executes regulations that conform to the schema. Think SQL (specification) vs PostgreSQL (implementation), or JSON Schema (specification) vs any validator.
This distinction matters because third-party organizations may build their own engine implementations. A municipality (gemeente) might use a different technology stack. The schema must be precise enough that any conformant engine produces identical results for identical inputs. When two engines disagree, one has a bug. The schema is the arbiter.
Coupling schema and engine version numbers would conflate specification with implementation. A schema change (new field, new operation) is a format change. An engine change (bugfix, performance improvement) is an implementation change. They evolve on different timelines for different reasons.
Five decisions establish the reproducibility model.
The schema and engine are versioned independently. Both use semantic versioning.
Schema versions follow the existing convention: immutable directories under schema/ (e.g., schema/v0.5.0/schema.json). A published schema version is never modified. The CI protect-schema job already enforces this.
Engine versions are declared in Cargo.toml and correspond to GitHub Release tags (via release-engine.yml). The engine version is bumped for any change that could affect execution output.
Each engine release declares which schema versions it supports:
The engine validates at startup that loaded regulations reference a supported schema version. A regulation referencing an unsupported schema version is a hard error; the engine does not silently fall back to serde-only validation.
The mapping of which schema version introduced which construct is maintained in the Schema Reference version history, which is the single source of truth. RFCs that add a construct reference that table rather than asserting a version independently.
Every execution produces an Execution Receipt: an output envelope that contains everything needed to reproduce the result.
The receipt records:
valid_from, and content hash), and which jurisdiction scopes were activeThe scope section is what makes the receipt fully reproducible. The engine’s output depends on all loaded regulations, not just the one being evaluated. IoC resolution (RFC-003) pulls in implementing regulations. Cross-law references resolve against whatever is loaded. Priority rules (RFC-010) determine which version of a regulation wins when multiple sources provide it. Two engines with different loaded regulations can produce different results for the same parameters.
The loaded_regulations array lists every regulation the engine had available during execution, each with a SHA-256 content hash. The sources array lists each corpus source with its Git ref and tree hash. Together, these allow exact reconstruction of the execution environment.
The scopes array records which jurisdiction scopes were active (per RFC-010). An engine running with scope GM0363 (Amsterdam) loads different municipal regulations than one running with GM0384 (Diemen), which changes IoC resolution for national laws that delegate to municipal ordinances.
The accepted_values section captures the cross-organizational provenance chain. When engine A accepts a value from engine B, A records B’s provenance metadata. To reproduce the full execution, you need A’s receipt (which tells you B’s engine version and regulation hash) and B’s receipt (which B stores in its own Logboek Dataverwerkingen per RFC-009).
Regulation YAML files currently reference schemas via GitHub main branch URLs:
This URL is mutable. A force-push or file edit on main would silently change what the URL resolves to. For a system that must reproduce decisions years later, that is unacceptable.
Schema URLs switch to Git tag references:
When a schema version is published, a Git tag schema-vX.Y.Z is created. GitHub tag protection rules prevent deletion or modification.
The engine embeds schema files at compile time (via include_str! in validate.rs) and validates by content, not by URL fetch. The URL is a pointer for humans and tooling. Integrity comes from the content hash in the Execution Receipt.
RFC-009 defines two resolution modes: Execute (compute locally) and Accept (take another organization’s authoritative determination). For reproducibility, the Accept path must capture enough provenance to reconstruct the full decision chain.
The inter-engine protocol (RFC-009 §5) is a public API contract between organizations. It must be versioned independently from the engine. An explicit api_version field in every request and response lets organizations running different engine versions communicate:
Within a major API version, backward compatibility is guaranteed. An engine at v0.5.1 and an engine at v0.8.0 both speak api_version: "1.0". Breaking protocol changes require a major API version bump and a migration period where engines support both versions.
Value representation on the wire is pinned: currency values are integers in cents, dates are ISO 8601 strings, booleans are JSON booleans. Internal representation of Value may differ between engine implementations.
This RFC extends the RFC-009 §5 response with engine provenance:
New fields: engine, engine_version, schema_version, regulation_hash, and has_upstream_accepts. The last signals whether this value depended on accepted values from yet another organization, meaning the provenance chain is deeper than two hops.
When reproducing a cross-org decision, the engine uses the sealed accepted values stored in the Execution Receipt. It does not call other organizations’ engines again.
Why not re-call? The other organization may now be running a different engine version with different corpus. Re-calling would produce today’s answer; reproduction needs last year’s. And legally, a beschikking (individual decision) stands once issued. The competent authority’s determination at the time is a legal fact. Reproducing Org A’s decision means re-executing A’s computation with A’s engine version, A’s regulation, and the accepted values A received at the time.
Reproduction steps:
scope.loaded_regulations (verify each content hash)For full chain verification (optional, useful for audit or dispute resolution):
trace_id)Step 5 proves A’s computation is reproducible. Steps 6-8 prove B’s determination was correct. Step 5 alone satisfies the legal requirement: A is responsible for A’s computation, using B’s determination as a legal fact.
Different organizations will run different engine versions. This is expected and acceptable if:
Two engine implementations that correctly implement the same schema version must produce identical outputs for identical inputs. When they diverge, one of them has misimplemented the schema. A future RFC will define a language-agnostic conformance test suite that formalizes this contract, providing a shared set of input/output test cases any engine implementation can run to prove correctness per schema version.
The compliance requirements in this RFC (provenance fields, schema immutability) are themselves rules about how the system must behave. In a platform that executes machine-readable law: can these compliance rules be expressed as RegelRecht regulations?
The engine’s operation set is designed for computation over citizen data (arithmetic, comparison, conditional logic). It cannot introspect the execution environment. A compliance regulation could define what fields a Execution Receipt must contain, but the engine cannot verify its own output structure or inspect other regulations’ metadata. The compliance YAML would need external inputs (engine_version_present: true/false supplied by a test harness) to produce a verdict.
This is useful in two ways:
Runtime self-enforcement (the engine checking its own compliance during execution) would require engine context variables ($engine.version, $regulation.hash) in the execution scope. That is a future extension.
The results section of the Execution Receipt includes an output_provenance map that tags each output with how it was produced:
This serves two purposes:
See RFC-007 for the hook provenance model.
ArticleResult without changing execution logic. Schema enforcement and CI checks can be rolled out phase by phase.refs/heads/main to refs/tags/schema-vX.Y.Z) requires a one-time migration of all corpus YAML files.Alternative 1: Coupled schema + engine versions
$schema field implicitly identifies the engine.Alternative 2: Engine backward compatibility (support all schema versions forever)
Alternative 3: Content-addressed schema references (SRI hashes in YAML)
$schema_integrity: sha256-<hash> alongside the $schema URL.Phase 1: Execution Receipt and engine version stamping
engine_version (from CARGO_PKG_VERSION) and schema_version (from the regulation’s $schema field) to ArticleResultregulation_hash (SHA-256 of YAML content) to ArticleResultengine_version, schema_version, regulation_hash, has_upstream_accepts, and api_versionExecutionReceipt struct as the top-level output envelopePhase 2: Schema version enforcement
supported-schemas metadata to Cargo.toml$schema references a supported versionvalidate.rs (fix the current silent fallback)$schema URLs from refs/heads/main to refs/tags/schema-vX.Y.Zschema-vX.Y.Z Git tags for all published schema versionsschema/latest symlink pointing at the highest released schema version (currently v0.5.2)Phase 3: CI enforcement
Cargo.toml version between PR and main)schema/vX.Y.Z/ directory is registered in validate.rs$schema URL in corpus YAML references an existing schema versionschema/latest symlink points to the highest semver directoryrust-toolchain.toml with pinned stable version for reproducible builds--locked to all CI cargo commands (currently only release builds use it)Affected components
| File | Change |
|---|---|
packages/engine/src/engine.rs | ArticleResult gains provenance fields |
packages/engine/src/service.rs | LawExecutionService populates provenance on execution |
packages/engine/src/article.rs | from_yaml_str computes and stores content hash |
packages/engine/src/lib.rs | Re-export ExecutionReceipt type |
packages/engine/src/bin/validate.rs | Error on unknown $schema, verify all schemas registered |
packages/engine/Cargo.toml | supported-schemas metadata, version bump |
packages/shared/src/provenance.rs | New: ExecutionReceipt, ExecutionReceiptProvenance types |
corpus/regulation/**/*.yaml | $schema URL migration to tag-based refs |
schema/latest | Symlink tracks the highest released schema version (now v0.5.2) |
.github/workflows/ci.yml | Version bump check, schema registration check, --locked |
rust-toolchain.toml | New: pinned Rust toolchain version |
open_terms and implementsAn exploration by Bureau Architectuur of the Dutch Ministry of the Interior into the possibilities of transparent, executable legislation.
Bureau Architectuur
Ministry of the Interior and Kingdom Relations