Time-travel: as_of reads the past
Area: Bitemporal time Teaches: the surface
as_ofclause on apub query— the same query, snapshotted at different transaction-times, returns different answers. Every mutation stamps a monotonic transaction-time, andas_of <tx>reads the state visible at that instant. Prerequisites: refinement (iff), andmutatebodies. 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_ofargument (a raw transaction-time stamp). Forms likeas_of now() - 1 dayor 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.