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

# Building Source Spans for a Verifiable Evidence Layer

> Learn how to build SourceSpan objects from PDF pages, sections, tables, and free text to create a stable, verifiable evidence layer.

A `SourceSpan` is the atomic unit of evidence in CL SDK. Every extracted field, cited answer, and PCE change request traces back to one or more spans. Building spans correctly — stable IDs, accurate page ranges, meaningful section metadata — is the single most important step in producing trustworthy outputs from any CL SDK workflow.

## SourceSpan Schema

```typescript Full Interface theme={"system"}
interface SourceSpan {
  id: string;
  documentId: string;
  sourceKind?: "policy_pdf" | "application_pdf" | "email" | "attachment" | "manual_note";
  kind: "pdf_text" | "pdf_image" | "html" | "markdown" | "plain_text" | "structured_field";
  text: string;
  hash: string;
  textHash?: string;
  pageStart?: number;
  pageEnd?: number;
  sectionId?: string;
  formNumber?: string;
  sourceUnit?: "page" | "section" | "table" | "table_row" | "table_cell" | "key_value" | "text";
  parentSpanId?: string;
  table?: {
    tableId?: string;
    rowIndex?: number;
    columnIndex?: number;
    columnName?: string;
    rowSpanId?: string;
    tableSpanId?: string;
    isHeader?: boolean;
  };
  bbox?: SourceSpanBBox[];
  location?: SourceSpanLocation;
  metadata?: Record<string, string>;
}
```

> [!NOTE]
>
> The `hash` field is computed automatically by CL SDK builders from the span's `text` content. You do not need to set it manually. Span IDs should be derived from `documentId + pageNumber + sectionId + hash` so that identical text on the same page always produces the same ID.

## Builder Functions

CL SDK provides four builder functions that handle ID generation, hashing, and metadata normalization for you.

```typescript theme={"system"}
import {
  buildPageSourceSpans,
  buildSectionSourceSpans,
  buildTextSourceSpans,
  chunkSourceSpans,
} from "@claritylabs/cl-sdk";
```

## Building Page Spans

Page spans are the best default for PDF documents. Each span covers one full page of extracted text, giving the LLM a predictable context window and making page citations exact.

```typescript theme={"system"}
const spans = buildPageSourceSpans([
  {
    documentId: "policy-123",
    pageNumber: 12,
    sourceKind: "policy_pdf",
    text: pageText,
    sectionId: "definitions",
    formNumber: "CG 00 01",
  },
]);
```

> [!TIP]
>
> Pass `sectionId` and `formNumber` whenever you can parse them from the PDF. These fields dramatically improve retrieval precision for coverage queries against multi-form commercial policies.

## Building Section Spans

Section spans split page text at insurance heading boundaries. Use them when your PDF parser produces reliable headings and you want finer-grained retrieval without the overhead of chunking.

```typescript theme={"system"}
const spans = buildSectionSourceSpans(pages, {
  minSectionChars: 120,
});
```

**pages** `PageInput[]` *required*

Array of page objects with `documentId`, `pageNumber`, `text`, and optional `sourceKind`.

**options.minSectionChars** `number` (default: 120)

Minimum character length for a section span. Sections shorter than this threshold are merged into the preceding span.

## Building Text Spans

Use `buildTextSourceSpans` for long free-text sources that don't have natural page breaks — emails, manual notes, attachments, and HTML extracts. The builder chunks the text into overlapping windows.

```typescript theme={"system"}
const spans = buildTextSourceSpans(
  {
    documentId: "email-456",
    sourceKind: "email",
    text: emailBody,
  },
  {
    maxChars: 4000,
    overlapChars: 250,
  },
);
```

**input.documentId** `string` *required*

Stable identifier for the source document.

**input.sourceKind** `string`

One of `"email"`, `"attachment"`, `"manual_note"`, `"html"`, or `"plain_text"`.

**options.maxChars** `number` (default: 4000)

Maximum characters per span window.

**options.overlapChars** `number` (default: 250)

Character overlap between adjacent spans to avoid cutting evidence across boundaries.

## Chunking for Retrieval

After building spans, chunk them into retrieval windows for vector search. Chunks are larger than spans and respect span boundaries, so a single chunk never splits a table row or section heading mid-sentence.

```typescript theme={"system"}
const chunks = chunkSourceSpans(spans, { maxChars: 6000 });
```

Store chunks alongside spans in your `SourceStore`. The query agent and PCE agent use both: chunks for initial retrieval, spans for final citation.

## Table Span Hierarchy

When you process documents with Docling, table content is represented as a three-level span hierarchy:

**1. Table span (sourceUnit: 'table')**

Contains the full markdown rendering of the table. Used when the LLM needs the complete table structure for context.

```
| Coverage | Limit | Retention |
|----------|-------|-----------|
| E&O      | $5,000,000 Each Claim | $25,000 |
```

**2. Row span (sourceUnit: 'table\_row')**

One header-aware row rendered as a readable key-value string. This is the **canonical evidence span** for table-derived policy facts.

```
Coverage: E&O | Limit: $5,000,000 Each Claim | Retention: $25,000
```

**3. Cell span (sourceUnit: 'table\_cell')**

A single cell value with `columnName` and `rowIndex` metadata. Points back to its parent row span via `parentSpanId`. Available for UI highlighting and precise bounding-box rendering.

> [!WARNING]
>
> Row spans are the canonical evidence for table-derived policy facts. When the extractor quotes a limit from a schedule, it should cite the row span — not the cell span or the full table span — so the quoted text is human-readable and verifiable.

## Span Metadata

Use the `metadata` field to attach arbitrary key-value pairs without polluting the core schema. Common uses:

```typescript theme={"system"}
const spans = buildPageSourceSpans([
  {
    documentId: "policy-123",
    pageNumber: 5,
    text: pageText,
    metadata: {
      carrierName: "Acme Casualty",
      policyNumber: "GL-2024-001",
      endorsementEffectiveDate: "2024-01-01",
      ocr_confidence: "0.97",
    },
  },
]);
```

Metadata values are indexed by `SourceStore` implementations and available as filters in `SourceRetrievalQuery`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.