# Pipeline Run Inspector

Pipeline Run Inspector [#pipeline-run-inspector]

The **Pipeline Run Inspector** renders one card per tracked pipeline run: the run name, a status badge (running pulses on the primary color, completed goes emerald, failed goes destructive), a progress bar derived from `completed`/`total`, the current step while running, and the total duration once done. When a run carries an optional `steps[]` array, an expandable per-step timing section renders each step's name, status, and duration; when absent, the aggregate view alone renders. With no runs, an empty state points at `createDevToolsProgressCallback()` as the instrumentation hint. The `runs` prop mirrors the `@localmode/devtools` `PipelineSnapshot` record (keyed by pipeline name), so `useDevToolsPipelineRuns()` output feeds it directly — no mapping layer.

Preview [#preview]

```tsx
'use client';

import {
  PipelineRunInspector,
  type PipelineRunLike,
} from '@/components/pipeline-run-inspector';

/**
 * Static fixture shaped like real `@localmode/devtools` pipeline snapshots:
 * a running run mid-embed with live step timings, a completed run with full
 * per-step durations, and a failed run (aggregate-only view — no `steps`).
 */
const FIXTURE_RUNS: Record<string, PipelineRunLike> = {
  'rag-ingest': {
    currentStep: 'embed',
    completed: 2,
    total: 4,
    status: 'running',
    startedAt: '2026-07-03T09:12:04.000Z',
    steps: [
      { name: 'load', durationMs: 84, status: 'completed' },
      { name: 'chunk', durationMs: 112, status: 'completed' },
      { name: 'embed', status: 'running' },
      { name: 'store', status: 'idle' },
    ],
  },
  'semantic-search': {
    currentStep: 'rerank',
    completed: 3,
    total: 3,
    status: 'completed',
    startedAt: '2026-07-03T09:11:40.000Z',
    durationMs: 1834,
    steps: [
      { name: 'embed', durationMs: 412, status: 'completed' },
      { name: 'search', durationMs: 96, status: 'completed' },
      { name: 'rerank', durationMs: 1326, status: 'completed' },
    ],
  },
  'batch-caption': {
    currentStep: 'caption',
    completed: 1,
    total: 3,
    status: 'failed',
    startedAt: '2026-07-03T09:10:12.000Z',
    durationMs: 3261,
  },
};

/**
 * Demo for PipelineRunInspector. Renders three fixture runs — one running
 * (primary pulse badge + current step), one completed (emerald badge +
 * duration), one failed (destructive badge) — with expandable per-step timing
 * rows where the fixture carries `steps`. No models, no network. Wire `runs`
 * to `useDevToolsPipelineRuns()` from `@localmode/devtools/react` in your app.
 */
export default function PipelineRunInspectorDemo() {
  return <PipelineRunInspector runs={FIXTURE_RUNS} />;
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/devtools/pipeline-run-inspector
```

Data source & dependencies [#data-source--dependencies]

**Data source:** renders whatever `runs` record you pass — works with any backend. Recommended producer: `useDevToolsPipelineRuns()` from `@localmode/devtools/react` (on-device, optional); its snapshot record spreads straight into the `runs` prop.

* `lucide-react` — icons
* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `pipeline-run-inspector.tsx` — the `PipelineRunInspector` component
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**PipelineRunInspector**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `runs` | `Record<string, object>` | — | **Required.** Pipeline run snapshots keyed by pipeline name. |

**PipelineRunLike**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `currentStep` | `string` | — | **Required.** Current step name. |
| `completed` | `number` | — | **Required.** Number of steps completed. |
| `total` | `number` | — | **Required.** Total number of steps. |
| `status` | `PipelineRunStatus` | — | **Required.** Pipeline status. |
| `startedAt` | `string` | — | **Required.** When the pipeline started (ISO timestamp string). |
| `durationMs` | `number` | — | Total duration in milliseconds (set on completion). |
| `steps` | `array` | — | Optional per-step timings; per-step rows render only when present. |

**PipelineStepLike**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` | — | **Required.** Step name. |
| `durationMs` | `number` | — | Step duration in milliseconds (set once the step finished). |
| `status` | `PipelineRunStatus` | — | **Required.** Step status. |

Examples [#examples]

Bound to the devtools bridge [#bound-to-the-devtools-bridge]

```tsx
import { enableDevTools } from '@localmode/devtools';
import { useDevToolsPipelineRuns } from '@localmode/devtools/react';
import { PipelineRunInspector } from '@/components/pipeline-run-inspector';

enableDevTools(); // once, at app init

function PipelineTab() {
  const runs = useDevToolsPipelineRuns();
  return <PipelineRunInspector runs={runs} />;
}
```

Runs appear once a pipeline is instrumented with a devtools-aware progress callback:

```tsx
import { createDevToolsProgressCallback } from '@localmode/devtools';

await pipeline.run(input, {
  onProgress: createDevToolsProgressCallback('rag-ingest'),
});
```

Per-step timings [#per-step-timings]

The bridge snapshot is aggregate-only today (`currentStep`, `completed`/`total`, `durationMs`), and the card renders exactly that when `steps` is absent. If your wiring tracks individual step durations, attach them and the card grows an expandable "Step timings" section:

```tsx
<PipelineRunInspector
  runs={{
    'semantic-search': {
      currentStep: 'rerank',
      completed: 3,
      total: 3,
      status: 'completed',
      startedAt: '2026-07-03T09:11:40.000Z',
      durationMs: 1834,
      steps: [
        { name: 'embed', durationMs: 412, status: 'completed' },
        { name: 'search', durationMs: 96, status: 'completed' },
        { name: 'rerank', durationMs: 1326, status: 'completed' },
      ],
    },
  }}
/>
```

Customization [#customization]

Status styling is table-driven: `STATUS_BADGE_STYLES`, `PROGRESS_BAR_STYLES`, and `STEP_DOT_STYLES` map each status (`running` | `completed` | `idle` | `failed`) to utility classes — swap the emerald success accent or the running pulse there. Durations format via the local `formatDuration()` (ms → s → m s). Styled entirely with shadcn/ui CSS-variable utilities so it inherits your theme — because you own the copied file, every class and threshold is yours to change.