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

RFD-0007 — Queries and mutations are first-class items; every result carries why-provenance

Committed Opened 2026-05-03 · Committed 2026-05-03

Question

How does Argon expose retrieval and state-transition operations? As external API surfaces wired up in Rust, or as language-native declarable items that ship with the same testing, type-checking, and provenance infrastructure as derive rules?

Decision

Queries and mutations are first-class items in the language. They declare with query <name>(args) :- body => head and mutation <name>(args) { require, do, retract, emit, return } shapes. Both are type-checked against the TBox at compile time. Both compose with patterns, computes, aggregates, and the rest of the expression grammar.

Every query and mutation result carries why-provenance. Derived facts include their derivation chain — which input facts contributed, which rules fired, what the substitutions were. The provenance is a value field (PosBool(M) DNF encoding stored as JSONB) carried alongside the fact. Aggregate expressions carry witness lists. Compute calls carry per-leaf-read justification with transparent combinators.

Query surface uses Prolog-style :- syntax for the body and => head for the projected output. Mutations use singular clause names: require (preconditions), do (assignments), retract (negative facts), emit (events), return (response payload).

Rationale

Provenance is not optional. This system processes hundreds of billions of dollars in financial contracts. Every derived fact must be defensible: what inputs led here, which rule version applied, what substitutions filled in the gaps. Bolting provenance on as a per-feature concern would have left blind spots; making it a structural property of every result means the question never comes up.

First-class items, not bolt-on APIs. A query authored in Argon participates in the type system. The compiler verifies the query’s body atoms resolve against declared concepts, that bound variables are consistent, that the projected head is well-typed. A query expressed as a Rust function with hand-written SQL has none of those guarantees and is the thing that produces the bug we just paid an audit team to find.

Singular mutation clauses. require not requires. do not does. The grammar prefers singular keywords (RFD-0015 framing). Reads as English when the mutation is simple, scales to compound clauses when needed.

PosBool(M) value, not weight semiring. PosBool stores the derivation expression as a value field on each derived fact. This composes with DBSP-style IVM (RFD-0022) where the weight semiring is reserved for Z-set arithmetic. Mixing the two semantics would have constrained provenance encoding to whatever shape DBSP-the-library exposes.

Consequences

  • query and mutation are reserved item kinds with their own AST nodes (Item::Query, Item::Mutation).
  • Result objects from queries carry why: PosBool(M). Result objects from mutations carry the same plus a transition-trace.
  • The compiler enforces that every derived fact has a recoverable provenance encoding; rules that produce facts without traceable derivation are rejected.
  • Aggregate expressions admit where clauses for filtering and emit witness lists alongside the aggregate value.
  • Compute call graphs cannot have cycles (OE0210). Arity must match (OE0208). Tier must be consistent.