Run 2026-10-04 · pandoc 3.11

Fidelity dashboard

One dated reading of the grid: which pandoc produced these numbers, what moved since the baseline, and how much of the movement a reader would actually notice. Every figure is read from results/; nothing on this page is typed by hand.

3.11pandoc (the pin)
2026-10-04measured (UTC)
66element probes
34formats scored
37.6%median semantic fidelity
within thresholds0 regressions

Deltas

What moved since the baseline

A cell moved when its verdict changed between two runs. The direction is not a matter of taste: every lane declares an order over its verdicts in src/verdicts.py, and a cell that drops rank is a regression. Thresholds live in thresholds.json and the exit code of make delta is the gate, so a regression stops a build rather than scrolling past in a diffstat.

pandoc 3.11 of 2026-10-04T10:56:21Z against pandoc 3.11 of 2026-09-30T22:08:13Z, profile pin.

Every cell held its verdict.

Severity

Not every lost probe costs the same

Counting probes treats a dropped footnote body and a dropped column width as one event each. They are not. Each probe is classified by what is missing from the page when it does not survive: content means text or a referent is gone with nothing left saying so, structure means every character survived but the document's shape did not, and presentation means styling or a measurement is gone while the words and the shape are intact. The rubric, and the calls that are arguable, are in src/severity.py.

The weighted score below forgives the canonical verdict, because the question it asks is whether the content came home, not whether the writer restyled it on the way. The three per-class columns are the point: two formats reach the same percentage by losing very different things.

formatexpressed/66round‑trip/66exact/66canon./66meta/10semanticcontent/12structure/27presentation/27
native666666-10100122727
markdown595946111088.9112521
html63624433.571.872317
commonmark_x57573921062.462015
muse45433234.5598189
epub62613622.558.152013
rst4745331056.481610
djot565635-055.661811
latex5447247451.36178
markdown_mmd50502836.550.471410
docx5343239448.731910
mediawiki5144253044.44168
docbook4341225443.65148
gfm50502421040.24139
odt4938233040.22177
markdown_github4646223037.641110
markdown_phpextra4848261037.631113
org44432133.537.64128
textile4644212037.63155
asciidoc484721-235.95115
ipynb4766213035.93129
typst534825--35.931111
dokuwiki3835211032.52128
jats544321-431.63108
commonmark565621-029.92109
man3635155229.11127
markdown_strict525218-027.4387
jira3736171026.51116
rtf4026151022.20106
fb2393313-420.5274
haddock3532131020.5185
t2t373513-017.9166
pptx40140101.7010
opml100-20000
kept partly kept lost weights: content 3, structure 2, presentation 1

Carve

The other direction: Carve source in, Carve source out

Every lane above starts from a pandoc AST, which answers how much of a pandoc document a format can hold. It cannot answer the question a Carve author has, because the pandoc AST has no node for an admonition, a task list, a highlight or an editorial mark, so those never appear in the probe set at all.

This lane starts from Carve instead: 42 source files, one construct each, written out through the pandoc-carve bridge and read straight back, and again through each of 13 export formats. exact is character for character, equivalent is a formatter restyling, respelled means the source is spelled differently and renders identically (a [^1] reference footnote coming back as an inline one), and lossy means a reader would see the difference. The three left-hand verdicts preserve rendered content; source-only information such as comments can still be lost and reported in warnings.

42 Carve fixtures · renderer @markup-carve/carve 0.1.9 (revision 04845b7fc316) · pandoc 3.11 · bridge revision c3d85783623c

conversion pathexactequivalentrespelledlossyunparsableerrcame back readable/42
bridge35151--41
bridge-preserve36141--41
markdown29-67--35
gfm16-1016--26
commonmark_x26-79--33
djot23-712--30
html31-65--37
latex14-621-120
typst11-619-617
docx131523--19
odt12-525--17
rst18-618--24
org16-719--23
jats10-328-113
epub27-510--32

These verdicts compare rendered content. A dropped comment can count as respelled because it renders to nothing; warnings record that source loss. Each format row covers writing, reading, bridge conversion, and Carve source serialization. An error may occur at any of those steps; the failure details identify the step.

bridge-preserve enables roundtrip: true metadata on the direct bridge path. Export-format rows use the default bridge options.

Table-span and table-rowspan test continuation cells; table-ragged tests a short row that Pandoc pads with an empty cell.

Preservation-mode warnings: table-ragged.

Carve round-trip measurement: 2026-09-30T22:12:39Z; bridge revision c3d85783623c2498370941e09d757b68b34fb839.

The default bridge lane changes 7 of them: comment (respelled), footnote (respelled), frontmatter (equivalent), ordered-list (respelled), quote-container (respelled), table-align (respelled), table-ragged (lossy).

Failures by step (including bridge failures)
  • jats/raw-html: pandoc export/import: Command failed: pandoc -f jats -t json Invalid XML: Missing root element
  • latex/table-span: pandocToCarve: renderCarve cannot spell table_row: a table row whose every cell is blank has no Carve source spelling
  • typst/heading: pandoc export/import: Command failed: pandoc -f typst -t json (line 1, column 1): unexpected Elt {eltName = Identifier "heading", eltPos = Just (line 1, column 1), eltFields = fromList [(Identifier "body",VContent (fromList [Elt {eltName = Identifier "document", eltPos = Just (line 1, column 1), eltFields = fromList [(Identifier "body",VContent (fromList [Elt {eltName = Identifier "text", eltPos = Just (line 1, column 1), eltFields = fromList [(Identifier "body",VContent (fromList [Txt "A second-level heading"]))]}])),(Identifier "level",VInteger 2)]}]))]}
  • typst/table: pandocToCarve: Cannot read properties of undefined (reading 'flatMap')
  • typst/table-align: pandocToCarve: Cannot read properties of undefined (reading 'flatMap')
  • typst/table-ragged: pandocToCarve: Cannot read properties of undefined (reading 'flatMap')
  • typst/table-rowspan: pandocToCarve: Cannot read properties of undefined (reading 'flatMap')
  • typst/table-span: pandocToCarve: Cannot read properties of undefined (reading 'flatMap')

The bridge says out loud what it dropped on comment, table-ragged.

History

The same probes over time

One line per recorded run, appended by src/delta.py --record. Medians rather than means: a handful of formats cannot be measured on a runner without a TeX engine, and a mean would report the machine instead of pandoc.

datepandocexpressedexactmetasemanticregressionsstatus
2026-09-30T22:08:13Z3.1147.522.02.037.60ok
2026-09-30T00:53:45Z3.1147.522.02.037.60ok
2026-09-30T00:53:45Z3.1147.522.02.037.60ok
2026-09-08T22:04:55Z3.1147.522.02.037.60ok
2026-09-07T12:16:16Z3.10.247.022.02.037.60ok

Gate

What would fail this build

Two profiles, because the two comparisons are not the same question. Against the pinned pandoc nothing may move without someone deciding it should. Against pandoc's nightly, upstream restyles writers routinely, so presentation-class movement is expected and content-class movement is not.

profilemax regressionsper laneper classmax weightwhat it is for
pin0--0the repo's own results against the pinned pandoc: nothing may move without a human deciding it should
nightly12exact 12, matrix 6, meta 2, roundtrip 6content 0, structure 424the watch on pandoc's nightly build: upstream restyles writers all the time, so presentation may move; content may not

exempt: pdf on the matrix lane - the pdf writer needs an external TeX engine, so this row measures whether the runner has one