---
description: |
  [TOPIC] Figure Provenance & Verification
  [DETAILS] The universal rule that a publication figure is not an image but a generative, verifiable artefact: every figure is produced by code through figrecipe (recipe YAML + DATA csv/npz + rendered media, separating WHAT-to-show from HOW-to-show), the paper consumes only the rendered media via symlink, and each figure is registered as a Clew claim so its Source->...->Figure DAG is hash-verified and re-derivable. Covers the verification-boundary concept (expensive upstream steps may sit OUTSIDE the Clew boundary as hashed Source leaves) and the load-bearing semantic distinction: "Clew-unverified" means *the steps cannot be machine-checked*, NOT *the values are wrong*. Use when wiring any figure pipeline, auditing a manuscript's figures for reproducibility, or deciding how much of a pipeline to bring under Clew.
tags: [scitex-scientific-figures-provenance]
---

# Figure Provenance & Verification (universal principles)

Companion to [01_figures_01_standards.md](01_figures_01_standards.md) (which
governs how a figure should *look*). This leaf governs how a figure is
*produced and verified*. Both apply to every manuscript figure.

## A figure is a generative artefact, not an image

Never hand-draw, hand-edit, or hand-place a figure in the paper. Every figure
is generated by code through **figrecipe**:

```
Python code  ->  Recipe (YAML: structure + plot calls)  ->  Figure (PNG/PDF/SVG)
```

with a **separation of concerns**:

- **DATA** (csv / npz) — *what* to show. The numbers behind the figure.
- **STYLE** (presets) — *how* to show it. Geometry, colours, typography.

So each figure emits three coupled outputs: `recipe.yaml` + `DATA.csv` +
rendered media. A bare `plt.savefig()` is an anti-pattern — it throws away the
recipe and the DATA, leaving an unverifiable image.

### Where the artefacts live

- The `recipe.yaml` + `DATA.csv` live **with the script output**, next to the
  code that produced them — never in the paper's caption directory.
- The paper consumes only the **rendered media via symlink**; captions are
  text files alongside the symlink.
- A recipe `.yaml` sitting in the paper's figure/caption dir is misplaced
  cruft — remove it; it is build-invisible and confuses provenance.

### Source location, in order of preference

1. The **actual analysis script that owns the data** — the figure is a
   byproduct of the analysis, so it cannot drift from the numbers it depicts.
2. Fallback: a dedicated `scripts/for_paper/build_fig_*.py` — ONLY when
   composition across several analyses is too awkward for one script. Do not
   let this become a permanent paper-only drawing layer.

## Every figure is a Clew claim

Register each figure as a **Clew claim** so its provenance is a hash-verified
DAG: `Source -> ... -> Figure`. Re-derivation must reproduce the figure (hash
match). This is what lets a reviewer trace a plotted value back to source data
and confirm nothing changed underneath it. See
[04_clew_01_dag-as-map-and-evidence.md](04_clew_01_dag-as-map-and-evidence.md).

## Reproduction must be COMPLETE and never GAMED

Re-derivation (above) must capture *everything that affected the pixels* — the
loaded style/rcParams, every parameter (including style-inherited ones), and the
data — and re-render identically from the recipe alone. Reject two failure modes
explicitly:

- **Silent degradation.** A replay that cannot match must **FAIL LOUD with the
  specific divergence** (style? ticks? size?) — never silently resize + restyle
  and report success. A size mismatch must be reported as a SIZE mismatch. (The
  figure-replay instance of fail-fast / fail-loud / no-silent-fallback.)
- **Gaming the check.** Never mutate the *figure* to dodge a replay gap — forcing
  explicit ticks, hard-coding fontsizes, or pinning a figsize purely to make the
  validator pass. That hides a real recorder gap and corrupts the figure to
  satisfy a test. If a required element (colorbar, log axis, style-driven font)
  does not yet reproduce, **keep the correct figure and fix the recorder** —
  reproduction is a property the tooling must earn, not one the figure is
  mutilated to fake.

## The verification boundary (expensive upstream steps)

Not every step can be re-run under Clew. A heavy, one-off computation (a
multi-TB GPU pass, a month-long cluster job) may have been produced *before*
Clew was connected, and re-running it solely to instrument it is not always
justified.

The correct treatment is **not** to abandon verification — it is to draw a
**boundary**:

- The expensive step's outputs (e.g. a results database) become the
  **Source / leaf nodes** of every downstream figure's Clew DAG, ingested as
  **hashed inputs**.
- Clew then verifies *everything downstream of the boundary*
  (source-db -> features -> models/stats -> figrecipe figure), even though it
  does not verify the upstream genesis.

State the boundary explicitly in the manuscript's reproducibility / data-
availability section.

### Semantics: unverified is not wrong

This is the load-bearing distinction. **"Clew-unverified" means the steps
cannot be *machine-checked*, NOT that the values are *wrong*.** Word any
limitation as a limitation of *verifiability*, not of *correctness*. This is
the figure-pipeline instance of the ecosystem-wide honest source-grounding
norm — *ground every claim you can, include the ungroundable with an explicit
reason, never silently omit* (see
[06_scitexification/00_playbook.md](06_scitexification/00_playbook.md)).

## Tables follow the same contract

A table is not hand-typed markup — it is a **CSV generated by code**,
symlinked into the paper, and hash-chained by Clew exactly like a figure:

- The analysis script that owns the data writes the table as `*.csv`
  (alongside its figure outputs).
- The paper consumes it via **symlink** into the tables caption/media dir
  (`tables/caption_and_media/*.csv`); the LaTeX/markdown table is rendered
  from that CSV, never typed by hand.
- The CSV is a Clew Source/intermediate node, so the table's numbers trace
  back to source data by hash — a changed value is caught by re-derivation.

The figure rules above (generated-by-code, symlink consumption, no hand-edit,
the verification boundary, unverified≠wrong) apply verbatim to tables.

## Anti-patterns

- Bare `plt.savefig()` that discards the recipe and DATA — produces an
  unverifiable image.
- Committing a rendered image with no recipe / DATA / Clew claim behind it.
- Putting the figrecipe `.yaml` or DATA `.csv` in the paper's caption dir
  instead of with the script output.
- Hand-editing a figure (in an image editor / GUI) after generation — breaks
  reproducibility silently.
- Treating an unverifiable upstream step as a *correctness* defect, or
  silently dropping it — instead, draw the boundary and disclose it.
- A replay that silently resizes/restyles to "match" and reports success —
  it must fail loud with the specific divergence (style? ticks? size?).
- Mutating a figure (forcing ticks, hard-coding fontsize, pinning figsize)
  just to make a replay/validator pass — hides a recorder gap; fix the
  recorder instead.
