Runs and sessions
A run is one top-level execution attempt caused by an activation or operator action.
A run is the control-plane unit of work. It has correlation IDs, status, timestamps, failures, and results. It is what lets an operator ask what happened, where it happened, what it is waiting on, and what to inspect next.
Runs are not the same thing as tasks. A task is planned work. A run is executed work.
A session is the conversational and operational thread inside a run. The narrative surface of execution.
Local vs shared runs
Runs are either local or shared, decided at creation time based on where the activation comes from.
Local runs: repo-local coding work where one agent claims a task, implements it, and commits. These stay in local SQLite. No write amplification, no premature globalization.
Shared runs: work dispatched remotely, work involving shared resources, work involving multiple agents or humans. These are born as substrate records because multiple surfaces need to observe them from the start. There is no promotion — if work crosses a shared boundary, the run starts shared.
An operator can import a local run into the substrate after the fact (arbe run share <id> or equivalent), but that’s an export/import move, not the normal lifecycle.
When arbe do dispatches, it creates a shared run in the substrate and mirrors it locally. For local-only work, only the local timeline exists. The local timeline is the operator’s view — richer, more detailed. The shared record is the collaborative view — coarser, durable.
Session is a room
A session is a scoped narrative with history and permissions. That is room-shaped. It should be a room, not a new record type.
The argument for a separate type was that rooms are “human-facing” and sessions are “execution-facing.” But that’s a cognitive-layer distinction, not a substrate one. The substrate sees a scoped narrative with permissions and a stream. If the substrate needs two types for that, the record model isn’t general enough.
For interactive work, a session is naturally room-like: prompt, reasoning, tool calls, approvals, retries, and outputs. For headless work, it may still exist as a machine-oriented stream even if no human watches it live.
The kind field in room content distinguishes human-facing conversations from execution-facing sessions. Lifecycle differences (sessions can be cleaned up, conversations persist) are handled by the runtime, not by the type system.
A session gets a durable stream when it needs one — interactive sessions always, terse cron sessions maybe a lightweight one. The existing room creation flow (mutation executor + stream hooks) already handles stream provisioning.
See record-types for the run and extended room schemas.
Artifacts
A result is the durable outcome of a run. An artifact is a durable object produced or modified by a run.
Examples include a posted Discord message with remote message ID, a PR URL, a generated file, a screenshot, a build log reference, a commit hash, or a structured incident summary.
The key rule is that terminal text is not enough. A useful system leaves inspectable results behind.
The bytes live elsewhere (git, blob storage, stream revisions). The record is the metadata and identity.
parent_id expresses ownership and lifecycle — artifacts live under a durable scope (house, project) because they outlive the sessions and runs that produced them. Provenance (which run, which session, which resource) goes in content where it can be queried without polluting the ownership tree. This avoids the trap where parent_id tries to mean ownership, creation context, and usage context simultaneously.
See record-types for the artifact schema.
Open question
Should artifact bytes live in the substrate? Probably not always. Metadata and identity belong in records. Bytes may live in git, blob storage, sandbox filesystems, or stream-backed revisions depending on the domain.