Runtime Ledger
Track SOIT execution through runs, tasks, run steps, artifacts, costs, and traces, and the versioned contract in which they leave SOIT.
The runtime ledger is the execution record for SOIT. It makes agent turns, workflow runs, tool calls, retrieval, and every other executable operation inspectable. The four concepts below are platform bedrock — modules iterate fast, but Run / Step / Artifact / Cost semantics do not change.
Core records
| Record | Meaning |
|---|---|
| Run | One end-to-end execution and the single trace root — a chat turn, an agent run, a workflow run, a batch job |
| Step | One atomic, observable unit inside a run — LLM call, retrieval, rerank, tool call, workflow node, memory write |
| Artifact | Any large payload produced or consumed by a run or step, stored in object storage and referenced by key |
| Cost | Normalized accounting attached to a run and optionally a step |
| Task | Asynchronous or long-running work state with its own status language |
| Trace | The assembled timeline and evidence powering observe and replay views |
Run invariants
run_idis generated once and is immutable; every externally visible execution path returns it.- Runs store bounded summaries only (
input_summary,output_summarycapped at roughly 8 KB). - Run status is one of
queued | running | succeeded | failed | canceled. - Runs record their execution kind (
chat | workflow | agent | tool | batch) and the exact subject version executed.
Step invariants
- Every step references its parent
run_idand is append-only — history is never rewritten; re-execution creates a new step. - Step types are a stable enum:
llm | retrieval | rerank | tool | workflow_node | agent_plan | memory_write | io | other. - Steps store bounded summaries plus
metrics_json(tokens, latency, HTTP status, vector counts); large payloads go to artifacts.
A step must be created for every LLM call, retrieval or rerank, tool call (HTTP / function / MCP), workflow node execution, agent planning or memory write, and any external egress boundary crossing — even when the result is cached.
Artifact and cost invariants
- The database stores artifact metadata and references only (
kind,mime,size_bytes,storage_key,sha256); content lives in object storage behind the storage port, scoped by key prefixartifacts/<tenant>/<workspace>/<run_id>/<artifact_id>/. - Artifacts are immutable — a new version means a new artifact id.
- Costs are written for any step that touches external resources or metered compute, in a stable queryable form:
currency,amount,unit,quantity,provider/model_ref/tool_ref, plus LLM token breakdowns. - A cost entry without an amount is unpriced, not zero: its pricing snapshot says why. An amount of
0with a currency is an explicit zero price. - New in v1.6.0. Each cost entry reads as one pricing status,
priced,estimated,free, orunpriced; unpriced entries are never counted as zero, and amounts in different currencies are never added together. The ledger can be set beside a provider's bill throughGET /api/v1/runs/costs/entriesandGET /api/v1/runs/costs/reconciliation. Cost entries of model calls also keep the provider's own ids, when it gave them:upstream_id(the response's id) andupstream_request_id(the request id from its response headers), for OpenAI, Anthropic, Gemini, and LiteLLM. Neither is unique. See Checking costs against a bill.
Ledger contract
Ledger records leave SOIT in a versioned contract (server/app/kernel/specs/v1/ledger_spec.schema.json), through ledger exports and run evidence bundles, each record wrapped in an envelope naming the contract version it was written in. Adding an optional field is a minor version; removing, renaming, or retyping one is a major version.
New in v1.6.0. The contract is version 1.1, which adds the optional upstream_id and upstream_request_id to cost records. Evidence bundles and ledger exports declare 1.1, so a consumer that rejects unknown fields must accept the two. See Evidence and Exports.
Lifecycle
A compliant execution path follows this order:
- Resolve the request context (tenant, workspace, user).
- Create a run (
queuedorrunning). - Append a step for each unit of observable work.
- Store large payloads as artifacts and link them from the run or step.
- Write cost entries for metered boundaries.
- End the run with a terminal status and timestamps.
On failure, previous steps, artifacts, and costs are preserved; the run records the last error summary and the id of the failed step.
The invariant behind it all
Execution paths must not silently succeed. If work crosses a model, retrieval, tool, workflow node, memory, or external boundary, it creates inspectable runtime evidence — this is what makes the Observe pillar possible.
Related pages
- Data Model — the tables behind these records.
- Outbox Events — how runtime changes dispatch side effects.
- Tasks and Runs — the product view of the same ledger.
- Evidence and Exports — the versioned contract in which ledger records leave SOIT.