What is an Execution Record?
The core primitive everything else in Flux is built on.
Every request Flux handles produces an execution record — a complete snapshot of everything that request did, stored automatically, queryable from the terminal.
This is the single concept that makes the rest of the platform possible: traces, replays, diffs, mutation history, and regression detection all query the same underlying record.
┌─────────────────────────────────────────────────────────────┐
│ Execution Record request_id: 550e8400 │
├─────────────────────────────────────────────────────────────┤
│ INPUT POST /signup { email: "a@b.com" } │
├─────────────────────────────────────────────────────────────┤
│ SPANS gateway 2ms ✔ │
│ create_user 81ms ✔ │
│ ├─ db.insert(users) 4ms ✔ │
│ └─ stripe.charge — ✗ timeout (10s) │
├─────────────────────────────────────────────────────────────┤
│ MUTATIONS users id=42 plan: free → null (rolled back) │
├─────────────────────────────────────────────────────────────┤
│ OUTPUT 500 44ms Error: Stripe API timeout │
└─────────────────────────────────────────────────────────────┘
What an execution record contains
| Field | Description |
|---|---|
request_id | Unique identifier — used in every flux command |
request_input | HTTP method, path, headers, body exactly as received |
span_tree | Ordered tree of spans: gateway auth, function execution, DB queries, tool calls, async hand-offs |
db_mutations | Every INSERT / UPDATE / DELETE during this request — table, row, old value, new value |
response | Status code, body, and latency of the final response |
errors | Any exceptions thrown, including stack trace and the span where they occurred |
timestamps | Wall-clock and monotonic time at every span boundary |
How it is produced
The execution record is produced by the Flux runtime as a side-effect of executing the request — no instrumentation, no SDK, no configuration required. The record is written atomically at the end of the request (or on failure) and stored in the Data Engine.
Client
→ Gateway (records request input, auth span)
→ Runtime (records function spans, tool calls, errors)
→ Data Engine (records DB mutations, links them to request_id)
→ Response (records status, body, total latency)
All spans assembled into execution record → stored by request_id
Querying execution records
All flux debugging commands are queries against execution records:
| Command | What it reads from the record |
|---|---|
flux why <id> | Errors + span tree → root cause + suggestion |
flux trace <id> | Span tree → full execution timeline |
flux trace debug <id> | Span tree → interactive step-through |
flux trace diff <a> <b> | Two span trees → structural diff |
flux state history <table> --id <row> | DB mutations for a row across all records |
flux state blame <table> --id <row> | DB mutations → per-column last-write attribution |
flux incident replay <from>..<to> | Request inputs → re-execute against current code |
flux bug bisect --request <id> | Request input → binary-search git history |
Storage
Execution records are stored in the Data Engine alongside your PostgreSQL data. Span data is written to trace_requests and execution_spans; mutation data to state_mutations. All three tables are indexed by request_id.
You can query them directly with flux explain or via standard PostgreSQL tooling — they are plain tables in your project database.
Retention
| Plan | Execution record retention |
|---|---|
| Free | 14 days |
| Builder | 30 days |
| Pro | 90 days |
| Enterprise | Custom (up to unlimited) |
Performance overhead
Recording an execution record adds:
- Span recording: ~0.1ms per span, written asynchronously after the response is sent
- Mutation logging: one additional write per DB mutation, batched in the same transaction at the Data Engine level — no extra round-trip
- Network overhead: zero — all recording is in-process
In practice, p99 latency overhead is under 2ms for typical functions. The recording path is never on the critical path for the HTTP response.