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

# Briefing rows

> How briefing rows and row items work — what counts toward your per-row item budget, how source groups expand, and how rows are reordered.

Rows are the unit of cross-source AI analysis inside a briefing. Each row holds one or more **row items** (a Source or a Source Group), and the AI writes commentary that synthesises just the items in that row. The page-level [Overall review](/analysis/briefings/overall-review) then synthesises across all rows.

## Row items

A row item is a pointer to an existing thing in your workspace:

| Kind             | What gets pulled in                                                                                                       |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Source**       | The latest chart image + AI short / long summary from that source's most recent Pulse run.                                |
| **Source group** | The latest cross-source meta-analysis run for that group, including its representative chart and the per-child summaries. |

<Note>Briefings never re-fetch data themselves. They reuse whatever the latest Pulse / Source-group analysis produced — so freshness depends on your Pulse cadence. If a source hasn't run recently, the row item shows "no recent data" and the AI commentary notes the gap.</Note>

## Item budget per row

Each plan caps how many items can sit inside a single row:

| Tier       | Items per row |
| ---------- | ------------- |
| Free       | 0             |
| Starter    | 4             |
| Pro        | 6             |
| Enterprise | unlimited     |

### How source groups count

A source group does **not** count as one item — it counts as the **number of sources inside it**. The composer expands the group's child charts visually, so a group with 3 sources fills 3 of your row's item slots.

<Tip>If a source group is too big to fit into a row, the composer surfaces an inline "this would exceed your row's item budget" message before you confirm. Either split the group or move some items to another row.</Tip>

## Row titles

Rows have two titles:

* **`title`** — what you typed. When set, this is what the user sees and it stays put.
* **`aiTitle`** — a 2–5 word title generated by Haiku on every save, based on the row's items. Shown when `title` is empty.

The composer falls back to `title ?? aiTitle ?? "Untitled row"`. If you've manually edited the title, the row is locked (`titleLockedByUser`) and the AI no longer overwrites it. You can choose **Reset to AI title** from the row menu to release the lock.

The same pattern applies at the briefing level — `Briefing.name` vs. `Briefing.aiTitle`. Both regenerate in a single batched AI call on save.

## Reordering

Rows and items reorder via arrow-up / arrow-down buttons on each card. There's no drag-and-drop in v1 — given the small numbers (8 rows × 6 items), the keyboard / click affordance was the simpler ship.

Reordering only changes the row's `position` field — it does **not** retrigger AI analysis. Order only matters for how the briefing renders (composer, PDF) and for the cache hash that decides whether a row needs re-analysis on next save.

## When a row's analysis is reused

Each row stores an `aiInputsHash` — a hash of its sorted item refs + their latest Pulse content IDs + the row title. On save, the orchestrator recomputes the hash:

* **Hash matches** → the prior row commentary is reused. No LLM call, no token spend. The row gets a new `BriefingRowContent` row pointing back at the prior one via `aiReusedFromBriefingContentId`.
* **Hash differs** → the row is re-analysed from scratch.

This is why adding a brand-new row is cheap (only that row runs), and why renaming a row title or moving an item between rows triggers a re-analysis on the affected rows.

## Failure handling

If a single row's AI commentary fails, the run does **not** abort. The failed row's `BriefingRowContent` is written with `aiError` set, a warning is appended to the run, and the orchestrator continues. The run is marked `SUCCESS` as long as at least one row produced commentary AND the overall summary call succeeded.

The composer surfaces failed rows with a "row failed — retry" affordance on the snapshot detail page.
