Temporal Queries
Time is a first-class dimension of the world model — not a log, not an audit table, not a framework to integrate. Every state the graph has ever been in is directly queryable.
For autonomous systems, this is the difference between learning and guessing. A robot that made a wrong navigation decision can reconstruct its exact world model state at the moment of the decision — not a log entry, not a diff, the full spatial-temporal graph as it existed then. That is what makes safety analysis, model debugging, and decision auditing tractable rather than speculative.
Every mutation in ArcFlow is versioned and preserved. The world model at any previous moment is accessible with a single AS OF seq N clause on any MATCH — no schema changes, no separate tables, no event-sourcing pipeline. Point-in-time queries, deterministic replay, temporal comparison, and clock domain merging are built into the query language itself.
-- Current state
MATCH (o:Order {id: 'ORD-001'}) RETURN o.status -- "shipped"
-- State at mutation sequence 100 (before the shipment mutation)
MATCH (o:Order {id: 'ORD-001'}) AS OF seq 100 RETURN o.status -- "confirmed"Real-world queries#
The power of temporal queries is not in the syntax — it's in the questions you can now ask that were previously unanswerable without a separate audit system.
Post-incident reconstruction: who was where when it happened?
-- All players within 3m of the ball, 10 sequences before the goal was scored
MATCH (g:Goal {match_id: 'match-001'})
MATCH (b:Ball) AS OF seq g.seq - 10
WHERE distance(b.position, g.position) < 1.0
MATCH (p:Player) AS OF seq g.seq - 10
WHERE distance(p.position, b.position) < 3.0
RETURN p.name, p.team, p._confidence
ORDER BY p._confidence DESCThe world model at g.seq - 10 is the exact graph state ten mutations before the goal was recorded. No log reconstruction. No event replay pipeline. The same query syntax, a different time.
Fraud investigation: what did we know before we approved it?
-- Account state at the moment the transfer was authorised
MATCH (a:Account {id: $account_id}) AS OF seq $approval_seq
RETURN a.balance, a.risk_score, a.last_verified_at, a._confidence
-- Which facts were predicted (not observed) at that moment?
MATCH (a:Account {id: $account_id})-[r:LINKED_TO]->(b:Account)
AS OF seq $approval_seq
WHERE r._observation_class = 'predicted'
RETURN b.id, r._confidence, r.sourceAutonomous system debugging: what did the world model show before the decision?
-- Robot's view of obstacle positions 500ms before the collision
MATCH (o:Obstacle) AS OF seq $collision_seq - 50
RETURN o.name, o.position, o._observation_class, o._confidence
ORDER BY o._confidence ASC
-- Confidence at the moment versus now — did confidence degrade?
MATCH (o:Obstacle {id: $id}) AS OF seq $collision_seq - 50
RETURN o._confidence AS confidence_then
MATCH (o:Obstacle {id: $id})
RETURN o._confidence AS confidence_nowWorld model staleness: which facts aged past their reliable window?
-- Facts that were current at a past checkpoint but have since been superseded
MATCH (f:Fact) AS OF seq 5000
WHERE f._observation_class = 'observed'
WITH f.id AS fact_id, f._confidence AS old_confidence
MATCH (f:Fact {id: fact_id})
WHERE f._confidence < old_confidence - 0.2
RETURN fact_id, old_confidence, f._confidence AS current_confidence
ORDER BY (old_confidence - f._confidence) DESCThese are not reconstructions from logs. They are direct queries against the versioned world model — same graph, same language, different point in time.
Clock and Ticks#
The engine maintains a logical clock. Each mutation advances the clock tick.
CALL db.clock()| field | value |
|-------|------------|
| tick | 1700000042 |
ArcFlow Clock Domains#
Real-world data arrives from sources that run at different rates — a camera at 25Hz, a LIDAR at 10Hz, an RFID reader at 1Hz. ArcFlow Clock Domains let each source maintain its own independent temporal sequence while the world model merges them into a coherent view. No external time-series middleware, no alignment preprocessing — each domain advances at its own rate and the graph stays consistent.
CALL db.clockDomains()Each domain reports its current tick, last mutation timestamp, and source identity. Observations ingested from different clock domains carry their originating domain in provenance metadata — so a query can distinguish a LIDAR reading from tick 1420 and a camera reading from tick 356, even when both describe the same entity at the same wall-clock instant.
AS OF Queries#
Query the graph as it existed at a specific mutation sequence number. The engine replays the WAL onto a clean snapshot at that sequence and executes the query against it.
-- Preferred: AS OF seq N (exact, WAL-based snapshot)
MATCH (n:Person) AS OF seq 42 RETURN n.name-- Create nodes — each mutation advances the sequence
CREATE (a:Person {name: 'Alice'}) -- seq 1
CREATE (b:Person {name: 'Bob'}) -- seq 2
-- Query the graph at seq 1 — only Alice existed
MATCH (n:Person) AS OF seq 1 RETURN n.name| name |
|-------|
| Alice |
Only mutations up to and including sequence N are visible.
Deprecated:
AS OF <unix_timestamp>is deprecated and will emit a warning. UseAS OF seq Ninstead — it is exact and requires no timestamp-to-sequence mapping.
Nodes As Of#
Procedure form for temporal point-in-time queries:
CALL db.nodesAsOf()Temporal Ingest#
Insert nodes with explicit timestamps, independent of the current clock:
// Ingest a historical fact with an explicit timestamp
db.mutate(
"CREATE (e:Event {name: $name}) AS OF $ts",
{ name: 'Launch', ts: 1609459200 } // 2021-01-01 00:00:00 UTC
)Temporal ingest is used for backfilling historical data, replaying event streams, and merging data from external systems with their original timestamps.
Temporal Compare#
Compare the graph state between two points in time:
CALL db.temporalCompare()Returns the diff: nodes added, removed, and properties changed between two temporal snapshots.
Temporal Replay#
Replay mutations in chronological order from a starting tick:
CALL db.temporalReplay()Useful for auditing, debugging, and reconstructing how the graph evolved over time.
Temporal Gate#
Conditional logic based on temporal state. Gate procedures validate that certain temporal preconditions hold before allowing a mutation:
CALL db.temporalGate()Change Tracking#
Changes Since#
Get all mutations since a given tick:
CALL db.changesSince()Returns the mutation log entries — node creates, property updates, relationship changes — that occurred after the specified tick.
Mutation Log#
View the full mutation history:
CALL db.mutations()Fingerprinting#
Cryptographic fingerprint of the current graph state. Two graphs with identical content produce identical fingerprints regardless of insertion order.
CALL db.fingerprint()| fingerprint |
|--------------------------------------------------|
| sha256:a1b2c3d4e5f6... |
Monotonic Validation#
ArcFlow enforces monotonic clock progression within a single engine instance. Timestamps never go backward. This guarantees causal ordering: if fact A was created before fact B, A's timestamp is strictly less than B's.
External timestamps from temporal_ingest are stored as-is but are clearly marked as externally sourced. The engine's own clock remains monotonic.
Stream Merging#
When merging data from multiple sources with independent clocks:
- Each source's timestamps are preserved in their original clock domain
- Conflicts are detected when two sources assert contradictory facts at overlapping time ranges
- The merge result includes provenance metadata identifying which source contributed each fact
-- View clock domains from different data sources
CALL db.clockDomains()| domain | current_tick | source |
|----------|-------------|---------------|
| local | 1700000042 | engine |
| import_1 | 1609459200 | external |
| import_2 | 1672531200 | external |
See Also#
- Event Sourcing — WAL mutation log, deterministic replay, and audit trail
- Live Queries — combine
AS OFwith standing queries for point-in-time live views - Temporal Reference — full
AS OFsyntax, clock domains, and multi-source time merging - Confidence & Provenance — provenance trails alongside temporal history