# Activix data formats (conceptual)

This document describes **what kinds of structured values** Activix deals with, at a high level. Field names below follow **current defaults**; many top-level names are **configurable** per collection (primary key, status, timestamps, correlation field, purge marker). Normative detail lives in [activix.spec.md](./activix.spec.md), [activity-structure.md](./activity-structure.md), and [run-context-object.md](./run-context-object.md).

---

## 1. Encoding

- **Carrier**: each persisted **activity** is one **document** (object) in a store—JSON-shaped in practice.
- **Time**: lifecycle timestamps on the row use **epoch milliseconds** (numbers). Step-level timing inside optional nested steps uses **ISO 8601 strings** where required by that sub-format.
- **Phased writes**: the same logical record is often written **more than once**; later writes **merge** into the same document rather than replacing unspecified parts wholesale.

---

## 2. Top-level format: activity record

An **activity record** is a **composite** value. Conceptually it has four roles (not necessarily four physical groupings in older rows):

| Role | Idea |
|------|------|
| **Identity of the row** | A stable primary key for this activity instance (library-generated unless you supply one). |
| **Lifecycle** | Status, start/end/duration (and optional “still running” markers), plus fields the library owns for completion, failure, staleness, and soft-delete. |
| **Correlation** | A nested object that places this activity in your **work graph** (session, job, task chain, optional executor metadata). Default field name: `runContext`. |
| **Observed I/O** | What crossed the **activity boundary** from the outside world (`outer`), plus optional **internal steps** (`inner`). |

Integrators usually **supply** correlation + I/O (partially at first). The library **owns** lifecycle machinery and the primary key (see [activity-structure.md](./activity-structure.md)).

---

## 3. Sub-format: run correlation (`runContext`)

**Purpose**: answer “**where** in the orchestrated run does this activity sit?”—not “who is the end user?” (that belongs to your auth layer) and not “how is Activix configured?”.

**Shape**: an **extensible associative map** (string keys → JSON-compatible values). A few keys are **conventional** for queries and tooling (e.g. `sessionId`); the rest are **domain-defined** ids and tags (jobs, tasks, parent links, leaf call ids, etc.).

**Storage name**: default BSON/document key is **`runContext`**. The field name is configurable (`runContextField`). Older stacks or deliberate compatibility may still use the legacy key **`identity`** for the same *role* of object—see [identity-object.md](./identity-object.md) and [MIGRATION-v5.md](./MIGRATION-v5.md).

---

## 4. Sub-format: boundary tier (`outer`)

**Purpose**: describe **one** activity as a black box: what went **in**, what came **out**, and **annotating metadata** (and optional **cost**).

**Shape**: a fixed **schema of slots**:

- **Input** — arbitrary JSON value (the stimulus).
- **Output** — arbitrary JSON value; may be **absent or null** until the activity finishes (normalized on write).
- **Metadata** — plain object (annotations: type labels, model names, tenant hints, etc.).
- **Cost** (optional) — a **nested sub-format** (see §6).

This is the **coarsest** I/O view: one pair per activity row.

---

## 5. Sub-format: step tier (`inner[]`)

**Purpose**: optional **refinement** of the same activity: ordered (or at least list-shaped) **sub-steps** inside the boundary, each with its own input/output/metadata/cost and **per-step clocks**.

**Shape**: array of objects. Each element mirrors the **I/O slots** of `outer`, plus **timing fields** (start/end, optional derived duration). Optional **step identity** keys allow merges on patch instead of blind appends.

Omit `inner` entirely when you do not need intra-activity tracing.

---

## 6. Sub-format: cost object

**Purpose**: attach **structured economic or usage** data without forcing a global enum of providers.

**Shape**: small plain object with **optional** numeric and string fields (e.g. currency amount, token breakdown, provider/model labels) plus an optional **`details`** bag for provider-specific extensions. Validated when present; not required for the record to exist.

---

## 7. Legacy shapes (same logical record)

Older persisted rows may still carry:

- **`structure: { outer, inner? }`** instead of root-level `outer` / `inner` — see [MIGRATION-v6.md](./MIGRATION-v6.md).
- Top-level **`fullRequest` / `fullOutput`** sidecars in some local/playground layouts — prefer `outer` / `inner` I/O fields for new data.

Readers that must handle **mixed ages** should treat these as **alternate encodings** of the same conceptual tiers.

---

## 8. Mental model

```mermaid
flowchart TB
  subgraph record["Activity record"]
    PK[Primary key + lifecycle]
    RC[Run correlation envelope]
    OUT[Boundary tier outer]
    INN[Optional step list inner]
  end
  RC -->|scopes| OUT
  OUT -->|may refine| INN
```

**Run correlation** scopes the row in your system. **`outer`** is what you show when summarizing “this activity.” **`inner`** is how you explain *how* it got there, when you need that fidelity.

---

## 9. Where to read next

| Topic | Doc |
|--------|-----|
| `outer` / `inner` keys, phased writes, merge rules | [activity-structure.md](./activity-structure.md) |
| `runContext` meaning, hierarchy, examples | [run-context-object.md](./run-context-object.md) |
| `sessionId` expectations | [session-id-usage.md](./session-id-usage.md) |
| v5 rename `identity` → `runContext` | [MIGRATION-v5.md](./MIGRATION-v5.md), [identity-object.md](./identity-object.md) |
| Full package contract | [activix.spec.md](./activix.spec.md) |
