<!-- Generated by scripts/gen-docs.ts from the TSDoc in src/. Do not edit by hand. -->

# `measure/export`

Source: [`src/measure/export.ts`](https://github.com/AI-for-dev/combo/blob/main/src/measure/export.ts)

Exporting a run: `runs/<timestamp>/` with one HTML and one JSONL per
subagent, plus a `usage.json`.

Two rules govern this file.

**We reimplement nothing.** The HTML and the JSONL are pi's own
(`AgentSession.exportToHtml` / `exportToJsonl`); `usage.json` is the single
artefact we produce ourselves, because it is the only one pi does not know
about - time is measured here, and attribution per subagent is ours.

**An export never breaks a run.** Every failure comes back as a string in
`error`, never as a throw: an export is an observer of the work, and an
observer that takes the workflow down with it is a bug. That matters most on
the interrupted path, where exporting is precisely what we are trying to
rescue.

## `createRunDir`

*function*

```typescript
export function createRunDir(base = "runs", now = new Date()): string { /* … */ }
```

Creates `<base>/<timestamp>/` and returns its path: a directory of its own
for every call.

The timestamp is to the second, sortable and filesystem-safe, so `ls` shows
the runs in order. Two runs started in the same second would share it, and
a run directory holds one run: the second is `<timestamp>-2`, then `-3`, as
the run's branch is. Each candidate is created exclusively, so two processes
never both take one. {@link newestRunFirst} orders the names.

`<base>` is given a `.gitignore` of its own, because the exports land **inside
the repository the run works on** and git has no reason to know about them.
Measured, both ways round: a delivery that gives its subtasks copies refuses
to put them back, because `runs/` alone makes the tree unclean; and `/build`'s
commit is a `git add -A`, which would sweep a run's transcripts into the
user's history. A file already there is left alone - it is their directory
once they have said anything about it.

## `exportBaseName`

*function*

```typescript
export function exportBaseName(id: string): string { /* … */ }
```

Turns a subagent id into a file name: `reviewer#2` → `reviewer-2`.

`#` is legal in a file name and unusable in a URL, and these files are meant
to be opened in a browser and shared.

## `SessionExport`

*type*

```typescript
export type SessionExport = {
	/** The subagent this transcript belongs to, e.g. `reviewer#2`. */
	id: string;
	/** Path of the HTML page pi rendered. Absent when it could not be produced. */
	html?: string;
	/** Path of the JSONL transcript. Attempted separately from the HTML. */
	jsonl?: string;
	/** Why the export did not happen. Never thrown, always reported. */
	error?: string;
};
```

What one subagent left on disk. Both paths are absent when nothing could be written.

## `usageReport`

*function*

```typescript
export function usageReport(snapshot: RunSnapshot, wallMs: number, exports?: SessionExport[]): UsageReport { /* … */ }
```

Builds the report from a collected snapshot.

A fan-out **aggregates**, it never averages: tokens and cost are sums,
`busyMs` is the sum of the branches, and `wallMs` is how long the run took.
The ratio of the last two is the only honest measure of parallelism.

## `UsageReport`

*type*

```typescript
export type UsageReport = {
	/** When the report was written, ISO 8601. The run's own timestamp is the directory. */
	generatedAt: string;
	/** Wall time of the run itself, not the sum of the subagents. */
	wallMs: number;
	/** One entry per subagent, in tree order: a child follows the parent it hangs under. */
	subagents: UsageReportEntry[];
	/** The sum over every subagent - failures included, because they cost too. Its `wallMs` is the run's. */
	total: UsageTotal;
	/** Busy time over wall time: the parallelism actually achieved. */
	parallelism: number;
	/** Where each transcript landed, and why one is missing when it is. */
	exports?: SessionExport[];
	/** In a flow run: every visit of every life, each life's in plan order. */
	visits?: VisitUsage[];
	/** In a flow run: one entry per node address, in the order first visited. */
	nodes?: NodeUsage[];
	/** In a flow run with a journal: each life, whose sum `total` is. */
	lives?: LifeUsage[];
};
```

The whole `usage.json` document.

## `UsageReportEntry`

*type*

```typescript
export type UsageReportEntry = {
	/** The subagent, e.g. `scout#1` - what the transcript files are named after. */
	id: string;
	/** The agent it was spawned from. Several subagents may share one agent. */
	agent: string;
	/** The lifetime it actually ran with, not the agent's declared default. */
	lifetime: string;
	/** `provider/id` as pi resolved it, when pi could say. */
	model?: string;
	/** Its last known status: what it was doing when the run ended. */
	status: string;
	/** Whether its last turn succeeded. Absent while it is still running. */
	ok?: boolean;
	/** The failure, when there was one. A failed subagent keeps its usage. */
	error?: string;
	/** The last task it was given - a report of ids alone reads like nothing. */
	task: string;
	/** How many tools it called. The cheapest signal that a turn ran away. */
	toolCalls: number;
	/** Present when its definition says `mustCallTool: true` and it called no tool: why it failed. */
	calledNoTool?: true;
	/**
	 * The subagent that had this one spawned. Absent on a root.
	 *
	 * The list stays **flat** and carries the link, rather than nesting: the
	 * total is a sum over the whole tree either way, every reader written
	 * against the flat shape keeps working, and a tree is one pass away for
	 * whoever wants one. The rows are in tree order, so reading it top to bottom
	 * already shows the children under their parent.
	 */
	parentId?: string;
	/** Its {@link Usage}: time measured here, tokens as pi reported them. */
	usage: Usage;
	/** In a flow run: the folder of its transcript, relative to the run directory - its memory scope's path, else its visit's. */
	home?: string;
	/** In a flow run: the life it ran in, counting from 1. */
	life?: number;
	/** In a flow run: every visit it ran, in the order they ended. */
	visits?: string[];
};
```

One subagent's line in `usage.json`.

## `UsageTotal`

*type*

```typescript
export type UsageTotal = Usage & {
	/** Subagents in the run, failures included. */
	subagents: number;
	/** Those that ended with `ok: false`. Counted apart: `2/3 done` hides a crash. */
	failed: number;
};
```

The sum over a whole run, and how many subagents it was spread over.

## `writeUsageReport`

*function*

```typescript
export function writeUsageReport(dir: string, report: UsageReport): string { /* … */ }
```

Writes `usage.json` into `dir` and returns its path.
