# 3. Nodes A document is a tree of nodes. `nodes.jsonl` holds one node per line, in document order: the root first, then each node before its children, and siblings in order. ## 3.1 The node record | Property | Required | Holds | | --- | --- | --- | | `id` | Yes | The node ID ([4](04-identity.md)). | | `kind` | Yes | One of the kinds in [3.2](#32-kinds-and-children). | | `parent` | Yes | The parent's ID, or null for the root. | | `ord` | Yes | The position among its siblings, from 0. | | `label` | No | The printed label (`2.3`, `Figure 2`, `(1)`, `a`), for kinds that take one. | | `attrs` | No | Structural attributes, by kind ([3.4](#34-attributes)). | | `content` | No | The node's own content, by kind ([3.3](#33-content)). | | `withheld` | No | `true` when the policy withholds the content ([12.5](12-package.md#125-text-policies)). | | `fidelity` | No | For leaves, the level the pipeline established: `F0`, `F1` or `F2` ([8.1](08-fidelity.md#81-levels-of-fidelity)). | | `prov` | No | `{witnesses, backend?, reading?, corroborated_by?}`: the witness IDs the text came from, the backend, and the witnesses that agree. | | `text_hash`, `match_hash`, `align_key`, `own_hash`, `node_hash` | Yes | The five content values ([5](05-hashes.md)). | Optional properties are omitted, never written as null. The schema is `node.schema.json`. ## 3.2 Kinds and children | Kind | May contain | Content | Label | | --- | --- | --- | --- | | `document` | `front`, `section`, `back` | | | | `front` | `title`, `authors`, `abstract`, `keywords`, `notes` | | | | `title` | `sentence` | | | | `authors` | | runs | | | `abstract` | blocks, `section` | | | | `keywords` | | runs | | | `notes` | `footnote`, `paragraph` | | | | `section` | `heading` first, then blocks and `section` | | Yes | | `heading` | `sentence` | | | | `paragraph` | `sentence` | | | | `sentence` | `equation` | runs | | | `equation` | `equation-line` | math | Yes | | `equation-line` | | math | Yes | | `figure` | `panel`, `caption`, `figure-note` | | Yes | | `panel` | | | Yes | | `caption` | `sentence` | | | | `figure-note` | `sentence` | | | | `table` | `caption`, `table-head`, `table-body`, `table-note` | | Yes | | `table-head`, `table-body` | `row` | | | | `table-note` | `sentence` | | | | `row` | `cell` | | | | `cell` | | runs | | | `list` | `item` | | | | `item`, `quote` | blocks | | | | `code`, `algorithm` | | verbatim | Yes | | `theorem` | blocks | | Yes | | `footnote` | `sentence` | | Yes | | `back` | `acknowledgments`, `references`, `appendix`, `notes` | | | | `acknowledgments` | blocks | | | | `references` | `reference` | | | | `reference` | | runs | Yes | | `appendix` | `section` | | | Blocks are `paragraph`, `equation`, `figure`, `table`, `list`, `quote`, `code`, `algorithm`, `theorem` and `footnote`. These rules also hold: 1. Exactly one node is a root, and it is a `document`. 2. A `heading` is a section's first child, and a section has at most one. 3. `front`, `back`, `title`, `authors`, `abstract`, `keywords`, `caption`, `table-head`, `table-body`, `acknowledgments` and `references` occur at most once among their siblings. 4. A `table` has exactly one `table-body`. 5. `title`, `heading`, `paragraph`, `caption`, `figure-note`, `table-note`, `table-head`, `table-body`, `row`, `list`, `item`, `quote`, `theorem`, `footnote` and `appendix` have at least one child. 6. Only the kinds marked in the table take a `label`. 7. A body without headings, as in a letter, is a section with no heading. Kinds outside this list are refused; a new kind is a minor version. Page breaks, running heads and columns are not nodes: they are geometry ([7](07-geometry.md)). ## 3.3 Content | Content | Kinds | Shape | | --- | --- | --- | | Runs | `sentence`, `cell`, `reference`, `authors`, `keywords` | A non-empty list of inline runs ([3.5](#35-inline-runs)). | | Math | `equation`, `equation-line` | `{notation, source, form?}`: `tex` or `typst`, the source as written or as a form spells it, and that form ([3.7](#37-math-forms)). | | Verbatim | `code`, `algorithm` | `{language?, text}`. | Other kinds have no `content`. Derived renderings and readings, such as MathML and a formula's tree, are not content; they live in `derived/` ([12.7](12-package.md#127-derived-files)). ## 3.4 Attributes | Kind | Attribute | Holds | | --- | --- | --- | | `section` | `level` (required) | 1 for a section, 2 for a subsection, and so on. | | `table` | `columns` | The column definitions: `[{id, header_path}]`, where `header_path` lists header cell IDs, outermost first ([9.2](09-figures-tables-equations.md#92-tables)). | | `cell` | `column`, `rowspan`, `colspan`, `role` | The column's ID, spans (default 1), and `data` (default), `column-header`, `row-header` or `row-group-header`. | | `row` | `header_path` | The cell IDs that label the row, outermost first. | | `list` | `ordered` | Whether the list is numbered. | | `item` | `marker` | The printed marker. | | `theorem` | `variant` | The printed kind: Theorem, Lemma, Definition, Proof, Remark. | | `panel` | `image`, `crop`, `figure_text` | The image asset, a crop, and the text found inside the panel ([9.1](09-figures-tables-equations.md#91-figures-and-panels)). | No other kind carries `attrs`, and each kind carries only its own attributes. ## 3.5 Inline runs | Run | Properties | Plain projection | | --- | --- | --- | | `text` | `text`, and optional `style`: a sorted list of distinct values from `em`, `strong`, `sup`, `sub`, `sc`, `mono`, never both `sup` and `sub`. | The text. | | `math` | `notation` (`tex` or `typst`), `source`, and optional `form` ([3.7](#37-math-forms)). | `$` + source + `$` | | `cite` | `refs`: reference node IDs; `text`: the printed form, such as `23`, `[1,2]` or `(Smith, 2019)`. | The printed form. | | `xref` | `target`: a node ID in this document, an address in another, or null when not yet resolved; `text`. | The printed form. | | `fnmark` | `footnote`: a footnote's ID, or null; `text`: the mark. | The mark. | | `link` | `url`, `text`. | The text. | | `equation` | `node`: the ID of an equation child of the sentence. | `$$` + that equation's source + `$$` | A `text`, `link`, `cite`, `xref` or `fnmark` run MAY carry `printed`: its text as the paper prints it, where an `emend` corrected the authors ([11.6](11-corrections.md#116-emendations)). The run's `text` is then the emended text, and only an emend writes `printed`. Runs are canonical: no `text` run is empty, no two adjacent `text` runs have the same style list unless one of them carries `printed`, and no run's `printed` equals its `text`. A sentence's `equation` runs name its `equation` children, each once, in the children's order, and a sentence has no other children. A display equation the sentence runs through is thus part of the sentence, while keeping its own ID and hashes. ## 3.6 The plain projection The plain projection of a node with runs is the concatenation of its runs' projections. That of an equation or an equation line is `$$` + source + `$$`, and that of a verbatim node is its text. That of any other node is the plain projections of its text leaves joined with one U+0020. A node's text leaves are found by walking its subtree in document order and stopping at `sentence`, `cell`, `reference`, `authors`, `keywords`, `code` and `algorithm` nodes, and at `equation` nodes whose parent is not a sentence; an `equation-line` is never a text leaf. Offsets, quotations and the text hash are all defined on the plain projection as stored, before normalization. A node's runs as printed are its runs with each run's `printed` in place of its `text`, and each `text` run that carried one joined with the adjacent `text` runs of the same style list, as the run was before an emend split it. Its projection as printed is the plain projection of its runs as printed; an equation's or a verbatim node's is its plain projection, and any other node's joins its text leaves' projections as printed with one U+0020. It is the text a witness of the paper holds, recovered from `nodes.jsonl` alone; for a node no emend touched it is the plain projection. The strict and lossy projections are taken over the runs as printed ([5.3](05-hashes.md#53-the-strict-projection)), and `projections.jsonl` gives an emended sentence's projections with its projection as printed. ## 3.7 Math forms A math form is a standard spelling of formulas, so that one formula is written alike whichever paper, converter or claim it comes from. An equation's or equation line's content and a `math` run MAY carry `form`, which names the form its `source` is written in; a source without `form` is as the witness wrote it. The one form is `scienting-math/1`, canonical TeX, which [9.4](09-figures-tables-equations.md#94-the-math-form) describes; a node or run with `form` has the notation `tex`. A consumer MAY rely on these properties of a source that carries `form`: 1. It is canonical: it is the form's own spelling of the formula the witness wrote, which renders as that formula did, apart from presentation such as spacing, `\displaystyle` and delimiter sizes. 2. It is idempotent: spelling it in the form again gives it back unchanged, so a tool that rewrites it to check it, or a converter run twice, changes nothing. 3. It carries no label: the form lifts a `\tag` out of the source, and an equation keeps it as its `label`; `\label`, `\nonumber` and `\notag` are gone. 4. Everything else is derived from it: the tree, the symbol keys and the issues of [9.4](09-figures-tables-equations.md#94-the-math-form) are read from the source by a pinned tool into `derived/math.jsonl` ([12.7](12-package.md#127-derived-files)), are never content, and are the same for the same source under the same form. A producer writes `form` only on a source it has spelled in that form. A correction that changes such a source writes the new source in the form too, since the gate does not implement the form and cannot check it. A validator that implements the form reports a source with `form` that is not its own canonical spelling as `doc.math.form`. `form` enters no content value, so adding it to a source already in the form changes no hash, ID or anchor, while respelling a source in the form changes them as any edit of the source does ([5.2](05-hashes.md#52-own-content)).