> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quadrillion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Task variables (qualia)

> Pass variables between tasks in notebook and script workflows with the qualia package.

When you have multiple agents working on a research problem, they often need to share information — one task produces a result that another task uses as input. Qualia's task-variable evidence system passes those values between tasks through the `qualia` package, which is available in every notebook kernel and in every script an agent runs.

<Info>
  This system was previously called **Q\_VARS**. The in-kernel global `Q_VARS` has been replaced by the `qualia` package (Python), `library(qualia)` (R), and `Qualia` (Julia) — same flow, a real per-language client instead of an injected blob.
</Info>

Qualia handles capture automatically, but it's useful to know how the system works.

## How it works

The basic flow:

1. **Declare required variables** on a task
2. **Assign values** in a notebook cell (just use the variable name)
3. **Capture** happens automatically when the cell runs
4. **Other agents read values** in downstream tasks with `qualia.get()`

```mermaid theme={null}
flowchart LR
    task1["Task 1: Train model"]
    capture["Capture: best_accuracy = 0.94"]
    task2["Task 2: Summarize results"]
    read["Read: qualia.get(variable_names=['best_accuracy'])"]

    task1 --> capture
    capture --> task2
    task2 --> read
```

## Reading captured evidence

In a downstream task's notebook, read values captured by upstream tasks. The `qualia` namespace is already in scope — no import needed:

```python theme={null}
# Python — pandas DataFrame indexed by (notebook_id, variable_name)
df = qualia.get(variable_names=["accuracy", "best_model"])
```

```r theme={null}
# R — data.frame
df <- qualia_get(variable_names = c("accuracy", "best_model"))
```

```julia theme={null}
# Julia — vector of records
records = Qualia.get_evidence(variable_names = ["accuracy", "best_model"])
```

To recover a value scoped to one task (for example after a kernel reset, or in a fresh subagent), use the typed read:

```python theme={null}
rows = (await qualia.get_evidence(task_id="T-1", variable_names=["accuracy"]))["rows"]
```

## Capturing variables

In a notebook, variables are captured from cells automatically:

1. Define **required variables** when creating a task
2. Run code that assigns values to those variable names
3. Qualia captures the values when the cell executes

For example, if a task requires `best_model` and `accuracy`:

```python theme={null}
best_model = "random_forest"
accuracy = 0.94
```

After this cell runs, both values are captured and available to downstream tasks.

You can also be explicit, from the cell that produced the value:

```python theme={null}
await qualia.submit_variable(variable_name="accuracy", value=accuracy)
```

This fans out across every in-progress task on the session that declared the slot name — the same semantics as autocapture.

For an interesting value the task did **not** declare, record it as evidence without filling a slot:

```python theme={null}
await qualia.add_evidence(
    "class_counts",
    description="Counts per class in the training set",
)
```

Pass `recall_priority` (`"low"` by default, up to `"global"`) when the value is
something later agents should be shown rather than have to go looking for. See
[recall priority](/knowledge#recall-priority).

<Info>
  Captured variables become **runtime-captured claims** in the [Knowledge System](/knowledge), creating a documented trail of data flow.
</Info>

## Capturing from a script

Scripts use the same package. There is no cell boundary, so there is no automatic capture — the script submits before it exits — and it imports the client itself.

The `qualia` methods are async. Notebook cells can `await` them directly because IPython enables autoawait, but a script has to run them itself: top-level `await` is a `SyntaxError` outside a cell.

```python theme={null}
import asyncio
import qualia

async def main():
    accuracy = train_and_eval(...)
    await qualia.submit_variable(variable_name="accuracy", value=accuracy)

asyncio.run(main())
```

<Warning>
  Calling `qualia.submit_variable(...)` without `await` does not capture anything. It builds a coroutine that is never run, and Python only reports it as a `RuntimeWarning` when the object is garbage collected.
</Warning>

R and Julia work the same way, with one difference worth knowing. Julia cannot read a calling function's local variables by name, so `add_evidence` there is a macro:

```julia theme={null}
using Qualia
Qualia.@add_evidence class_counts description="Counts per class"
```

A script claim points at the line of the script that produced the value, the way a notebook claim points at its cell. That line is re-checked whenever the claim is read, so editing the script past it marks the source as changed.

## Provenance: values come from a runtime, full stop

There's no way to set a variable from outside the running code. Values are captured from real Python/R/Julia assignments, or from `submit_variable` / `add_evidence` calls made inside a notebook cell or a running script. `add_evidence` takes the variable name and reads that variable from the live runtime; agents cannot pass a separate literal evidence value through tool arguments.

This is why a computed value is better recorded from the runtime than written to a file and quoted: the runtime holds the actual value, with its real precision and type, while a file only holds however it was formatted on the way out.

To leave a declared slot permanently unfilled, the agent skips it with `update_task(skip_variables=...)` — a state decision, never a value.

## When tasks use variable evidence

Most useful when:

* **Chaining experiments**: One task trains a model, another evaluates it
* **Aggregating results**: Multiple parallel tasks produce metrics, a final task compares them
* **Parameterized workflows**: Pass configuration between stages

Example workflow:

```
T-1: Load and clean data → cleaned_rows
T-2: Train model A → model_a_accuracy (depends on T-1)
T-3: Train model B → model_b_accuracy (depends on T-1)
T-4: Compare models (depends on T-2, T-3)
     → qualia.get(variable_names=["model_a_accuracy", "model_b_accuracy"])
```

## Exporting notebooks

When you export a notebook for standalone use, Qualia replaces `qualia.get()` calls (and the R/Julia equivalents) with their actual values. The exported notebook runs without Qualia — all variable references are resolved to concrete data, so it works in other IDEs such as JupyterLab and VS Code.

## Technical details

The `qualia` package is shipped by the platform as a path overlay — nothing is installed into your environment. In a notebook it is pre-loaded by the kernel attach hook; in a script it is on the path for you to import. It communicates with the backend via authenticated API calls configured when the kernel or the command starts.

The system supports:

* **Python, R, and Julia**, in notebook kernels and in scripts, with one shared wire contract across all of them
* **Primitive values**: numbers, strings, booleans, and homogeneous lists of those. Dicts, DataFrames, and other complex objects are rejected — capture separate scalar variables instead
* **Session-scoped fan-out**: a captured value fills the matching declared slot on every in-progress task in the session
