---
name: extraction-field-provenance
description: "Make every extracted field carry where it came from — the page/section it was read from and the verbatim span, so an auditor can confirm each value without re-opening the document, and mark derived or defaulted fields as inferred instead of giving them a fake location. Use when pulling structured fields out of a document (invoice, contract, form, report, clinical note) into JSON. Do NOT use for answering a question from sources, translating, or designing the extraction schema itself."
metadata:
  decimalai-skill-id: "58b4b774-3758-47b6-a593-0acade40fb30"
  decimalai-version-id: "9bf786d0-8230-4d41-9bfe-db946d7e4551"
  decimalai-version: "1"
  decimalai-body-sha256: "8c11e5266a140862d19b9c35af125d40fccc2c4ab10e757603a8d8a409abe2fa"
  decimalai-name: "extraction-field-provenance"
  decimalai-format: "1"
  decimalai-invocation: "model"
  decimalai-category: "documentation"
  decimalai-source: "https://app.decimal.ai/s/extraction-field-provenance@1/SKILL.md"
---
# Extraction field provenance

## What this does

When you extract fields from a document into JSON, this skill makes every field carry **where it came
from**: the page or section it was read on, and the **exact span of text** it was read from. A value
that wasn't literally in the document — something you computed, derived, or filled in by default — is
marked **inferred** instead of being handed a location it doesn't have.

The point is auditability. A month later someone disputes a number. With provenance, the reviewer
reads the `source` off the field — "page 2, Header, span `3rd of April, 2024`" — and confirms or
refutes it in seconds, without re-opening and re-scanning the whole document. Without it, every
dispute is a full re-read, and there is no way to tell a value that was *read* from one that was
*guessed*.

It targets the default failure of a bare model asked to "extract these fields": it returns **flat
values** — `{"invoice_date": "2024-04-03"}` — with no trace of where each came from, and it silently
mixes values it read off the page with values it inferred, so a downstream reviewer can't tell which
is which or check either.

## When to use / when NOT to

**Use when:**
- You're pulling a set of fields out of a **document** (invoice, contract, form, receipt, report,
  clinical note, statement) into structured JSON.
- The output will be **reviewed, audited, or disputed**, and someone may need to trace a value back
  to its source.
- It matters whether a value was **read from the document** or **derived/assumed**.

**Do NOT use when:**
- You're **answering a question** from sources and citing support for the answer — that's grounded
  question-answering, not field extraction.
- You're **translating** a document (the output isn't a field set with sources).
- You're **designing the schema** or the canonical mappings themselves — that's schema design, not
  running an extraction with provenance.

## The output shape (this is the contract)

Every extracted field is an **object with a `value` and a `source`**, never a bare value.

```json
{
  "invoice_date": {
    "value": "2024-04-03",
    "source": { "page": 2, "section": "Header", "span": "3rd of April, 2024" }
  },
  "total_due": {
    "value": 1240.00,
    "source": "inferred"
  },
  "po_number": {
    "value": null,
    "source": null
  }
}
```

Three states, and every field is in exactly one of them:

1. **Located** — the value was read off the document. `source` is an object with:
   - `page` — the page number it appears on (or `null` if the document has no pages, e.g. a markdown
     file — then rely on `section`).
   - `section` — the heading, label, table, or region it sits under (`"Header"`, `"Line Items"`,
     `"Section 7.2 Termination"`). Give the most specific locator available.
   - `span` — the **verbatim text** it was read from, copied character-for-character.
2. **Inferred** — the value is *not* literally in the document. You computed it (summed line items),
   derived it (concluded the contract auto-renews from a clause), or filled a default (currency
   assumed USD because none was printed). `source` is `"inferred"`. Do **not** give it a page or span.
3. **Absent** — the field isn't in the document and can't be inferred. `value` is `null` and `source`
   is `null`. Never fabricate a value or a location to fill a slot.

## The rules

1. **Every field carries a `source`.** No bare values. If you can't say where a value came from, it
   is either inferred or absent — decide which; there is no fourth option.
2. **The `span` is verbatim.** Copy the exact characters as they appear in the document — original
   wording, punctuation, casing, symbols. `span` is *what you read*; `value` is *what you extracted
   from it*. They are allowed to differ (see next rule).
3. **Normalize the `value`, never the `span`.** The `value` may be cleaned up — a date reformatted to
   `2024-04-03`, money parsed to `1240.00`, a status mapped to an enum. The `span` still shows the raw
   source text (`"3rd of April, 2024"`, `"$1,240.00"`, `"PAID IN FULL"`). This is exactly what makes
   the extraction checkable: the reviewer sees both the source text and your reading of it.
4. **A located field must actually be located.** Only attach a `page`/`section`/`span` to a value
   whose source text is genuinely on the page. If you can point to the characters, it's located. If
   you can't, it is **not** located — mark it inferred or absent. Never invent a span or a page to
   make a value look sourced.
5. **Mark inferred fields inferred.** Anything computed, derived, reasoned, or defaulted gets
   `source: "inferred"`, not a location. A total you added up, a currency you assumed, a boolean you
   concluded from prose — all inferred, even if the *inputs* were located.
6. **Absent is `null`/`null`, not a guess.** A field with no source in the document and no basis to
   infer gets `value: null`, `source: null`. Do not fill it, and do not give the null a location.

## The located-vs-inferred decision

For each field, ask: **can I point to the characters this came from?**

- **Yes, the value (or the text I normalized into it) is on the page** → located. Record page,
  section, and the verbatim span.
- **No — I computed / derived / assumed it** → inferred. `source: "inferred"`, no location.
- **No, and I have no basis for it** → absent. `value: null`, `source: null`.

The failure to avoid is the **dressed-up guess**: a value you inferred or assumed, handed a
plausible-looking page and span so it reads as if it were on the document. That is worse than no
provenance at all, because it makes an audit trust something it shouldn't. When in doubt between
located and inferred, choose inferred — under-claiming a source is safe; over-claiming one is not.

## Locating when there are no page numbers

Not every document has pages. Use the most specific locator the format offers, in this order:
`page` → `section`/heading → table + row/column → paragraph or line reference. A markdown report has
no `page` but does have headings; a spreadsheet export has sheet + cell; an email has header fields
vs. body. Fill `page: null` and put the real locator in `section`. The span is still verbatim
regardless of format.
