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

# Write a pipeline script

> How pipeline execution actually works, and the minimum you need to know to write one.

A pipeline script is ordinary Python that Braven runs **for you**, once per Experiment, against that experiment's files. This page covers how execution works; for the full function-by-function reference see [Pipeline scripts (SDK reference)](/sdk-reference/pipeline-scripts).

## How execution works

<Steps>
  <Step title="Files are already local">
    The experiment's files are placed in the script's working directory before it runs. Open them with plain relative paths — `pd.read_csv("data.csv")`, `h5py.File("data.h5")` — no download step, no experiment ID plumbing.
  </Step>

  <Step title="Declare dependencies in a header comment">
    ```python theme={null}
    # requirements: numpy scipy>=1.10 matplotlib
    ```

    Must appear in the **leading comment block**, before the first real line of code. Braven `pip install`s these before running your script; already-installed packages are cached, so repeat runs are fast.
  </Step>

  <Step title="Log what you want kept">
    Call `braven.log_config`, `braven.log_summary`, `braven.log_series`, `braven.plot_series`, or `braven.upload` as many times as you like — see the [full reference](/sdk-reference/pipeline-scripts) for every helper.
  </Step>

  <Step title="Everything is written at the end">
    Nothing is saved incrementally while the script runs. A Run's output — Config, Summary, Series, Figures — is written **wholesale** when it finishes, and fully **replaces** that pipeline's previous output for the experiment. There's no partial/incremental state to reason about.
  </Step>
</Steps>

<Note>
  Both plain `.py` scripts and `.ipynb` notebooks (via papermill) are supported as pipelines.
</Note>

## Minimal pipeline

```python theme={null}
import pandas as pd
import matplotlib.pyplot as plt
import braven

df = pd.read_csv("data.csv")

braven.log_config("n_rows", str(len(df)))
braven.log_summary("peak", f"{df['value'].max():.3f}")
braven.log_series("value", df["value"].tolist())

fig, ax = plt.subplots()
ax.plot(df["time"], df["value"])
braven.upload(fig, "trace.png")
```

## What's available at a glance

| Helper                                            | What it does                                                                                                    |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `braven.log_config(key, value)`                   | Log an **input** parameter (string)                                                                             |
| `braven.log_summary(key, value)`                  | Log an **output** metric (string)                                                                               |
| `braven.log_series(key, values)`                  | Log a numeric list as a plottable Series                                                                        |
| `braven.plot_series(name, y, x=..., y_label=...)` | Log a labeled trace as an interactive chart; call again with the same `name` to overlay traces                  |
| `braven.upload(fig_or_path, name)`                | Upload a matplotlib Figure (saved as image *and* auto-extracted as an interactive companion) or a plain file    |
| `braven.device(key, type=None)`                   | Tag values to a specific physical [Device](/concepts/devices) instead of the whole experiment                   |
| `braven.path_params()`                            | Dict parsed from filenames — `30C` → `temperature`, `run_5` → `run_index`, `3v3` → `vdd`, and more              |
| `braven.files`                                    | Dict of the experiment's files (filename → local path)                                                          |
| `braven.params`                                   | Canonical parameter values already extracted for this experiment, if the pipeline has field mappings configured |

<Warning>
  `braven.log()` and `braven.log_plot()` (Plotly) are **removed**. Older stored scripts that still call them raise a clear error with migration guidance — use `log_config`/`log_summary` and `braven.upload(fig, name)` with a matplotlib Figure instead.
</Warning>

## Assigning a script to run automatically

Write and edit the script under a project's **Pipelines** tab, then assign it to a watched folder (**Pipelines → Watch Folders → Assign pipeline**) — see [Ingest data](/guides/ingest-data). Every new experiment in that folder then runs it automatically; watch progress in the **Activity** tab, and re-run any past execution from the pipeline's detail view.

## Continue

<CardGroup cols={2}>
  <Card title="Full function reference" icon="book" href="/sdk-reference/pipeline-scripts">
    Every pipeline helper, with full signatures and edge cases.
  </Card>

  <Card title="Tag a device from your script" icon="microchip" href="/concepts/devices">
    `braven.device(key)` is the recommended way to link a device — no manual step after the run.
  </Card>
</CardGroup>
