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

Time-travel: as_of reads the past

Area: Bitemporal time Teaches: the surface as_of clause on a pub query — the same query, snapshotted at different transaction-times, returns different answers. Every mutation stamps a monotonic transaction-time, and as_of <tx> reads the state visible at that instant. Prerequisites: refinement (iff), and mutate bodies. Run: ox build examples/temporal_as_of_surface && ox run-scenario examples/temporal_as_of_surface

A database that only knows “now” cannot answer “what did we believe last Tuesday”. Argon’s substrate is bitemporal: each mutation emits events with a monotonically-increasing transaction-time stamp, and a query can read as of any past stamp. The history is queryable, not overwritten.

What to read in chronicle.ar

A Person carries a mutable age, and Adult is the iff-refinement that classifies anyone 18 or older:

pub type Person { mut age: Int }
pub type Adult <: Person iff { self.age >= 18 };

Two declared queries pin the same Adult extent to two past transaction-times — the surface as_of <int> clause is the whole point:

pub query current_adults() -> Adult;          // latest snapshot
pub query adults_at_2()    -> Adult as_of 2;   // right after the hire
pub query adults_at_4()    -> Adult as_of 4;   // right after set_age

The driver routes a query carrying as_of through the time-travel read path automatically; the modeler never touches the runtime API.

Running it

The demo.toml scenario hires alice as a minor (age 12), then later sets her age to 25. Each mutation advances transaction-time, so the same Adult query gives different answers at different stamps:

query chronicle::adults_at_2:   0 row(s)        — alice is 12 here, not an Adult
query chronicle::adults_at_4:   1 row(s)  alice — alice is 25 here, now an Adult
query chronicle::current_adults: 1 row(s) alice — latest state agrees with tx=4

The decisive contrast is adults_at_2 vs adults_at_4: it is one query against one individual, but the answer flips from empty to {alice} purely because the read snapshot moved past the set_age event. The refinement is re-evaluated against the field value as of each instant.

Honest caveats (what runs today)

  • v0 admits only integer literals as the as_of argument (a raw transaction-time stamp). Forms like as_of now() - 1 day or a parameterized argument are reserved for a later RFD.
  • This is the transaction-time axis (when the system learned a fact). The valid-time axis — when a fact is true in the modelled world — is shown in effective_dated_tax_v0, which reads as_of <#date#>.

This example is compiled and run in CI; its as_of snapshot behaviour is pinned by a corpus test (oxc-runtime/tests/examples_corpus.rs), so the time-travel surface can’t drift from the language.