SOIT Docs
Architecture

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

RecordMeaning
RunOne end-to-end execution and the single trace root — a chat turn, an agent run, a workflow run, a batch job
StepOne atomic, observable unit inside a run — LLM call, retrieval, rerank, tool call, workflow node, memory write
ArtifactAny large payload produced or consumed by a run or step, stored in object storage and referenced by key
CostNormalized accounting attached to a run and optionally a step
TaskAsynchronous or long-running work state with its own status language
TraceThe assembled timeline and evidence powering observe and replay views

Run invariants

  • run_id is generated once and is immutable; every externally visible execution path returns it.
  • Runs store bounded summaries only (input_summary, output_summary capped 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_id and 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 prefix artifacts/<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 0 with a currency is an explicit zero price.
  • New in v1.6.0. Each cost entry reads as one pricing status, priced, estimated, free, or unpriced; 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 through GET /api/v1/runs/costs/entries and GET /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) and upstream_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:

  1. Resolve the request context (tenant, workspace, user).
  2. Create a run (queued or running).
  3. Append a step for each unit of observable work.
  4. Store large payloads as artifacts and link them from the run or step.
  5. Write cost entries for metered boundaries.
  6. 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.

On this page