# ORCHID literature data: schema

Release: `orchid-literature 2026-10-23 (record store db-v2.1: invariant/general v2.1.19r, text+context fixup-20261022)`. Generated by `tools/export_literature_data.py` (deterministic; `--check` verifies the files).

Derived data extracted from published papers (tables and running text) by independent machine readers
(Gemini Flash / Pro, Claude Opus; text-layer reading for tables) and accepted only on reader agreement plus
deterministic checks. **Licence:** derived data CC BY 4.0 with attribution to ORCHID and to the cited source
papers; each `quote` is a short excerpt kept to make the value verifiable, which remains under the publisher's
copyright and is not relicensed. The source
papers, page images, crops and full text are **not** redistributed.

**Status.** Version 1, machine-extracted: values were read by language-model readers constrained to quote their source and checked automatically, and the release was then audited on the printed pages by adversarial audits, an independent second review of fit-facing records and a preliminary human review of a frozen sample. Flags such as accepted and ESPEI-ready record screening results, not certification; known issues are listed in the release notes.

## Files

| file | content |
|---|---|
| `index.json` | release info, totals, per-system counts by evidence class and property group, file list |
| `systems/<SYS>.json` | `{system, elements, records[], artifacts{}, models{}, papers{}, contexts{}}` for one chemical system. To keep files small, a record's `locator` is `{ref, row}` with `ref` a key of `artifacts` (table/passage label, caption, page) and its `model` is a key of `models`; `elements` is stored once per file. The `.jsonl.gz` dump carries the full records; `<SYS>` = elements sorted alphabetically, uppercase, joined by `-` (as in `data/phase-diagrams`). Large systems are split into `<SYS>.json`, `<SYS>.part2.json`, ... (listed in `index.json`) |
| `papers.json` | paper id -> bibliographic record |
| `contexts.json` | paper id -> experimental-context items (samples, treatments, measurements), each with its quote and page |
| `orchid-literature.jsonl.gz` | all records, one JSON object per line (same fields as `systems/*.json`) |
| `orchid-literature.csv.gz` | all records flattened; nested fields are compact JSON strings |
| `CITATION.txt`, `CITATION.cff` | how to cite this dataset (the CSV carries no metadata; a leading comment line would break CSV readers). `index.json` and every `systems/*.json` also carry a top-level `cite_as` object: text citation, BibTeX and Zenodo DOI of this dataset and of the umbrella record, and the sources to cite as well |
| `QUALITY_FIXES.md`, `quality_fixes.json` | data-hardening report: duplicates merged, vocabulary maps, garbled fields, DFT-parity outlier classes (every changed record with before / after / evidence) |

## Record fields

| field | meaning |
|---|---|
| `id` | stable compact ORCHID id (`ORC-...`) |
| `pid` | persistent locator id: paper / page / table or passage / row |
| `chemical_system`, `paper_id`, `fold_lso`, `fold_lpo` | machine-learning folds: `chemical_system` (sorted elements, = `system`), `paper_id` (stable source id), `fold_lso` = leave-chemical-system-out fold int(sha256(chemical_system), 16) mod 5, `fold_lpo` = leave-paper-out fold int(sha256(paper_id), 16) mod 5 (tools/ml_folds.py). Subsystems are their own systems (a held-out ternary may have its binaries in training); drop subsystems of held-out systems for strict chemistry extrapolation |
| `system`, `elements` | chemical system of the record's own state (sorted uppercase elements joined by `-`): a binary compound's formation enthalpy or lattice parameter, or a pure element's transition, printed in a ternary / multi-component paper is filed under its binary / unary subsystem (audit round 2, A1 C4: `corrections` rule `system_of_the_state`, flag `system_is_state_subsystem`) |
| `source_system` | the chemical system of the source table / paper the record was read from (equal to `system` unless the state is a lower-order subsystem) |
| `record_kind` | `invariant_table` (invariant-reaction table), `general_table` (thermochemical / phase-equilibrium / structure table), `running_text` (value stated in a sentence) |
| `observation_type` | `invariant_reaction`, `phase_transition`, `equilibrium_property`, `structural_property`, `phase_region_vertex_set`, `phase_assemblage_node`, `property_expression` |
| `property` | quantity kind: `INVARIANT_TEMPERATURE`, `TRANSITION_TEMPERATURE`, `PHASE_COMPOSITION`, `PHASE_ASSEMBLAGE`, or a thermodynamic / structural quantity (`HM_FORM`, `HM_MIX`, `CPM`, `SM`, `GIBBS_ENERGY`, `EMF`, `ACR_<EL>`, `LATTICE_A`, ...); a controlled code (see Controlled vocabularies). `property_code_raw`: the code as the record builder wrote it, when it was malformed and cleaned |
| `property_group` | coarse filter group (invariant reaction, phase transition, phase equilibrium, formation, mixing / partial, heat capacity / entropy / enthalpy, activity / EMF / Gibbs energy, crystal structure, model expression, other property) |
| `property_printed` | the quantity symbol or name as printed |
| `value`, `unit` | value in the normalized unit (temperatures: K; formation enthalpies and Gibbs energies of formation, mixing enthalpies, Gibbs energies of mixing and partial molar enthalpies: kJ/mol-atom when the per-atom basis is explicit, otherwise kJ/mol (basis unstated); a formation value whose unit could not be normalized (no unit read, eV per formula unit, per-defect energies, a per-mole-of-atoms label without an energy unit) keeps its unit as read, is never ESPEI-ready, and is counted by unit in quality_fixes.json `counts.formation_values_unit_not_normalized` (audit round 3, H9); lattice lengths a, b, c (`LATTICE_A`, `LATTICE_B`, `LATTICE_C`, `LATTICE_A0`, `LATTICE_C0`, `LATTICE_UNSPECIFIED_AXIS`, `LATTICE_A_R`): angstrom (Å) when the stored unit is nm, pm or Å, unconverted when no unit was printed (`not_printed`, `unknown`), flag `lattice_length_outside_typical_range` outside 1-100 Å; other properties (e.g. Gibbs energies, which include the coefficients B of printed expressions A + BT in J/mol/K) keep the unit as read). `value_stored`, `unit_stored`: the value before that conversion. `value_printed`, `unit_printed`: as printed (tables) |
| `unit_basis` | how the per-atom / per-formula basis of an energy was established: `printed_unit` (in the printed unit), `column_header`, `caption`, `reviewer`, `state_completion_<where>` (a quoted header / caption / footnote / sentence), `eV_per_atom_printed`, `magnitude_heuristic` (inferred by the record builder from the magnitude; formation values flagged `basis_inferred_from_magnitude`), `assumed_per_mole_of_atoms` (solution phase, not printed), `not_printed`, `unstated`. For mixing / partial quantities only an explicit basis gives kJ/mol-atom. `per_formula_unit_divisor`: the record builder divided a per-formula-unit value by this number of atoms |
| `value_before_quality_fix`, `basis_correction`, `sign_heading_printed` | set when a DFT-parity outlier was corrected (QUALITY_FIXES.md): the value before the fix; `{formula, atoms_per_formula_unit, operation}` for a per-formula value that was labelled per atom; the printed heading that carries a minus sign |
| `corrections` | literature-level gate corrections (QUALITY_FIXES.md section 7): `[{gate, field, before, after, rule, evidence, visual_check}]`; `visual_check` = `{page, pdf_sha256_prefix, image_sha256, verdict, seen, checked}` (the PDF page was rendered and inspected; no image or page text is published). The value before a correction is kept in `value_stored`, `temperature_K_stored`, `unit_stored`, `system_stored`, `property_code_before_gates`, `evidence_class_before_gates`, `phases_stored` (with `phases_dropped` [{label, reason}]), `pressure_basis_stored`, `overall_composition_stored`, and per phase composition `composition_stored` / `basis_stored` (A1 audit round 2: fractions under an at.% header, header naming the wrong element, compositions assigned to the wrong phase; gate `A1`) |
| `gate_failures` | `[{gate, code, effect, detail, visual_check}]`: a gate demoted (`effect` `not_accepted`: `flags.extraction_accepted` false) or flagged (`not_espei_ready`: `espei_ready` and `espei_certified` false; `flag_only`) the value; codes also in `flags.gate_failures`; see QUALITY_FIXES.md section 7 |
| `phase_compositions_dropped` | compositions printed under a label that the page check found not to be a phase of the row (a column header); kept, not used |
| `transition_kind_stored` | the reaction type before a page-checked correction to the printed type |
| `uncertainty_stored`, `reference_state_stored`, `temperature_basis_stored` | values retained before a correction. For records with between-source scatter or composition-offset estimates, the reported uncertainty is also retained in the primary `uncertainty` field; the estimate and its derivation are kept in `uncertainty_budget.quantities.value.estimate_provenance`. Reference-state conversions retain the original value and conversion in `corrections[].evidence.conversion`. |
| `property_expression` | (audit round 2) `{form, term, printed, temperature_range_K}` when the value is one coefficient of a printed expression in T (`A + B T`, a Cp polynomial); the property code then ends in `_EXPRESSION_TERM` and the record has no single temperature (`temperature_K_stored` keeps the one stored before) |
| `calorimeter_temperature_K`, `reference_temperature_K` | (audit round 2) the calorimeter temperature of a standard enthalpy of formation stored at 298.15 K (direct-synthesis / solute-solvent drop calorimetry); the reference temperature of a heat content H(T) - H(298) stored at its row temperature T. Property codes `HM_FROM_COMPOUNDS` / `GM_FROM_COMPOUNDS` / `SM_FROM_COMPOUNDS`: formation quantities whose printed reference is compounds (e.g. oxides), not the elements; `flags.non_crystalline_state`: liquid / amorphous / glassy state printed at room temperature |
| `related_records` | ids of records of the same measurement listed in another paper (a compilation), or of the independent measurements behind a scatter-aware sigma |
| `merged_ids`, `merged_pids` | ids (and persistent locators) of records that were exact duplicates of this one and were merged into it |
| `value_range` | `{min,max}` when a range was printed; `value_expression`: a printed expression (model parameters) |
| `uncertainty` | `{value, basis, note}`; `basis: printed` is the source's own, in the record unit; between-source scatter and composition-offset estimates do not replace it; `stated_default` / `reading_resolution` are ORCHID placeholders (e.g. "5% default", "10% placeholder; none printed") and are **not** measured uncertainties |
| `evidence_identity` | Canonical observation ID, alias IDs, role and source evidence. Repeated extractions, later reports of the same calculation and alternate printed unit representations remain separate literature records but represent one observation for calibration and fitting. Source-link discrepancies are retained in the identity review and are not silently treated as verified citations. Available in JSON and CSV. Decisions: `DFT_EVIDENCE_IDENTITIES.json`, `MGPB_EVIDENCE_REVIEW.json` and `SRSI_EVIDENCE_REVIEW.json`. The DFT review retains the initial source group at the top level and subsequent groups under `additional_reviews`. The canonical ID selects a retained database record. When only secondary reports are available, `source_doi` identifies the original calculation and `canonical_reporting_source` identifies the report selected for fitting. `source_context_discrepancy` documents differences in reported conditions without overwriting them. |
| `source_evidence_review` | Source pages, prior classification and state metadata, unresolved source discrepancies and fitting status. Mg2Pb experimental rows from a mixed DFT table retain both unit representations; their measurement temperature is null pending primary-source review and they are excluded from calibration and fitting. The original table heading remains in `temperature_printed` and the prior numeric interpretation in `temperature_K_stored`. The Sr–Si review records corrections from model to DFT classification and repairs citation links from the printed reference. Repeated GGA/LDA reports retain their reported quantities and are linked to canonical records for calibration and fitting. The boride review corrects three conflicting experimental/DFT labels using the original CeB4 calorimetry paper and the reporting paper's source attributions. Reported values, uncertainties and conditions remain intact. These rows are excluded from fitting and calibration until their primary-source states are fully verified; the review distinguishes verified primary numerical evidence from method-only verification. Two LuB12 rows with conflicting source attributions are separately retained and excluded from fitting and calibration until independent source identities are established. Equality of the reported values is not treated as proof of duplication. |
| `uncertainty_budget` | uncertainty budget of the record's measured quantities, components kept separate (PI decision 2026-10-24; tools/uncertainty_budget.py; calibration in UNCERTAINTY_CALIBRATION.json / .md): `{calibration (version and sha256 of UNCERTAINTY_CALIBRATION.json), sigma_rule, quantities{value, composition, temperature_K}}`; `value` = the record's value, `composition` = its printed phase compositions (mole fraction), `temperature_K` = the equilibration temperature of a phase-composition / assemblage observation. Per quantity, in its `unit`: `sigma_printed` `{value, kind, taken_as}` is the reported component expressed on the stated sigma convention (with `printed` retaining the interval when a coverage conversion is applied; null if none; ORCHID placeholders are not printed sigmas), `sigma_reading` `{value, basis}` half of the last printed digit (`last_printed_digit`; compositions: `last_stored_digit`), `sigma_method` `{class, value, floor, floor_unit, basis (calibrated / prior / calibrated vs experiment), n_pairs, ci95, entry}` the method-class floor (robust between-study scatter of independent same-method measurements in ORCHID, or a documented prior; DFT: calibrated sigma_DFT; model values: model-vs-experiment deviation), `sigma_composition` (value measured at a historical formula: `{value, dx, slope}`), `sigma_scatter` (page-checked mutually inconsistent group: `{value, n_sources, group}`), `sigma_conversion` (reference-state conversion), then `sigma_effective` = max(sqrt(max(printed, reading, method)^2 + composition^2 + conversion^2), scatter) (`sigma_effective_reason` when none applies). `estimate_provenance` retains the separately estimated uncertainty, its basis and derivation when an earlier estimate occupied the primary field. These estimates do not replace the source-reported uncertainty. The effective sigma and the fitting weights are unchanged by this separation. Null = the component does not apply. CSV: the budget as JSON plus `sigma_effective` and `sigma_method_class` of the value |
| `temperature_K` | equilibrium / measurement temperature in K; `temperature_printed` as printed (unit as printed, often °C); `temperature_basis` how it was obtained (`printed`, `quantity_label`, `caption`, `assumed_room_temperature`, `not_printed`, ...); `temperature_bound` `{op, value_K, printed}` for a printed bound (`op` `<` upper bound, `>` lower bound), `temperature_max_K` / `temperature_range_K` for ranges; `temperature_uncertainty` `{value,basis}` |
| `pressure_Pa`, `pressure_basis` | pressure and how it was set: `printed`, or `assumed_default_1_atm_not_printed` (1 atm assumed, no pressure printed; the record builder's label kept in `pressure_basis_stored`) |
| `phases` | `[{label, model_phase, mapping_confidence, pearson_symbol, prototype, label_variants, label_source}]`: printed phase label and, where mapped, the CALPHAD model phase; `label_variants`: other readers' spellings of the same printed label (kept when duplicate readings were merged, e.g. 'NiAl (B2)' beside 'B2'); `label_source: page_check` for a phase added from the printed reaction on a page check; `structure_basis: mapped_model_phase`: the Pearson symbol / prototype are those of the mapped model phase, not printed; `model_phase_rejected` {model_phase, reason, pearson_symbol, prototype}: a corpus mapping the label cannot carry (tools/phase_label_check.py); `model_phase_basis: system_alias:<id>`: the model phase comes from (or is confirmed by) the per-system alias table of conventional phase names (tools/phase_aliases.json, e.g. Cu-Sn ε -> CU3SN_L, Cu-Zn β -> BCC_A2, Cu-Si κ -> HCP_A3), applied when the printed composition / temperature agree (`model_phase_alias_check`: composition, temperature, composition+temperature or label_only); `structure_basis: system_alias`: Pearson symbol / prototype are the handbook structure of that alias |
| `phase_compositions` | `[{phase, composition{EL: x}, basis, composition_source}]`. `basis` is `mole_fraction` (possibly "converted from atomic/mass percent") or `unresolved` (printed basis disputed/mixed, or values outside 0..1: do not use as mole fractions). `composition_source`: `printed`, `stoichiometry`, `formula_nominal`, `congruent_same_composition`, `printed_range`, `printed_bound`, ... |
| `overall_composition`, `overall_composition_basis` | alloy / sample composition (mole fraction) where given |
| `reference_state` | reference state of a thermodynamic quantity, always an object: per element `{EL: {phase, fixed_state_variables}}`, or `{printed: "..."}` when only free text was read |
| `reaction` | `{printed, type}`: reaction as printed and its class (`type` = `transition_kind`) |
| `transition_kind`, `transition_kind_printed` | controlled reaction / transition class (see Controlled vocabularies) and the class as printed or read |
| `evidence_class` | `experimental`, `model` (value from a CALPHAD/thermodynamic model), `dft` (first-principles), `unspecified` (origin could not be settled). Never mixed or averaged |
| `origin` | `{class, basis, measured_in, measured_in_basis, own_work_of_paper, attribution_note}`: how the evidence class was decided (e.g. `printed_source_experimental`, `cited_work_before_1980_assumed_experimental`), the paper the value was measured/calculated in, and whether that is the containing paper itself |
| `model` | for `model` values linked unambiguously to a TDB: `{tdb, tdb_path, tdb_sha256, catalog_entry, link_basis, model_citation, model_doi, reproduced_by_calculation}`; `model_unlinked_reason` otherwise. `model_doi` is null with `model_doi_status: dropped_mismatch` and `model_doi_correction` when the TDB header citation's DOI is a mismatch (`tools/doi_corrections.json`) |
| `method` | row-level method only when printed (table cell/column, or inside the value's quoted sentence); `method_basis` says which |
| `conditions` | `{kind: paper_context, paper}`: the containing paper's own experimental context applies (see `contexts`); `paper_context_not_extracted`; `cited_source` ("conditions in the cited source"): the value is cited from another work and the containing paper's context does **not** apply; `not_applicable_own_calculation` |
| `conditions_summary` | (CSV only) accepted context items of that paper, condensed |
| `cited_sources` | the value's own source(s) as printed in the paper's reference list: `{label, reference, doi, first_author, year, kind, status, paper}`; `year` is re-derived from the printed `reference` (a parenthesised year first, never a page number; tools/literature_quality.py `cited_year`), and where this changed the reader's year that year is kept in `year_read` with the rule in `year_basis` (`printed_parenthesised_year`, `printed_year_not_page`, `page_number_not_year`, `ambiguous_several_years`, `after_citing_paper`; a null year means no year could be read safely); a DOI dropped as a mismatch is null with `doi_status: dropped_mismatch` and `doi_correction` (the printed `reference` is kept). A source-page-verified replacement has `doi_status: corrected_from_printed_reference`, the prior link in `doi_original`, and a `doi_correction` identifier in `SOURCE_REFERENCE_CORRECTIONS.json`. The corresponding `origin.measured_in` uses the verified source, while `origin.measured_in_original` preserves the prior link. This correction does not change the reported value, uncertainty, or evidence class. |
| `citations_printed` | (text) citation labels printed in the sentence |
| `paper` | id of the containing paper (`doi:<doi>` or `sha256:<pdf prefix>`) -> `papers.json`; CSV adds `paper_citation`, `paper_doi` |
| `quote`, `quote_page` | (running text) the short verbatim excerpt backing the value (usually the sentence that states it; at most 500 characters: a longer excerpt is clipped at export to a contiguous substring, word boundaries, no ellipsis inserted, that still contains the printed value, with `flags.quote_clipped` = true and the original length in `flags.quote_chars_original`; the clipped text is verbatim on the same page) and its page (`null` with `flags.page_unreadable` when the page read was not a page number); for tables `locator` gives table label, caption, page and row instead |
| `locator` | `{artifact: table|text_passage, label, caption, page, row, asset}` |
| `flags` | `extraction_accepted` (reader agreement + checks passed; the explorer hides `false` by default), `row_state` (`settled`, `awaiting_reader`, `disputed_in_review_queue`), `review_state`, `espei_ready` (the value CAN be used in ESPEI: complete state, phases mapped to a model, phase-rule / unit checks, proper format, and the physical phase rule of QUALITY_FIXES.md section 6; `espei_ready_upstream` true and `espei_phase_rule` {result, reason, detail} when only the phase rule turned it false), `espei_certified` (`espei_ready` AND agreement with a CALPHAD prediction: `plausibility.result` = pass), `espei_certified_basis` (`pycalphad_invariant_agreement`; later bases may be added, e.g. `espei_driving_force`), `calphad_check` (`agrees`, `disagrees`, `not_available`), `state_fully_specified`, `specification_gaps`, `plausibility` (CALPHAD cross-check: result, model, dT_K, dx {phase: mole-fraction difference}, calculation, tolerance {dT_K, dx}, `fail_reason` for fails with |dT| < 5 K), `plausibility_before_correction` (the cross-check of a value whose temperature a gate corrected; computed on the old temperature), `source_disputed`, `judge_adjudicated`, `magnitude_suspect`, `system_mismatch`, `value_in_quote`, `quote_clipped` / `quote_chars_original` / `quote_original_sha256` (the quote was clipped to 500 characters at export; the printed value is inside the clipped text; the hash, 16 hex characters of the SHA-256 of the original quote, is shared by the values read from the same long sentence), `unsigned_in_print` (minus sign absent in print, value as printed), `sign_pdf_page_verified` (sign corrected from the PDF page image; `value_first_reader` kept), `has_dropped_fields` / `dropped_field_count` (fields removed because they were not printed in the quote; content not published), `review_flags` (codes only), `second_reader`, reader conflicts; data-hardening flags (QUALITY_FIXES.md): `page_unreadable`, `reference_state_dropped_not_in_quote`, `basis_inferred_from_magnitude`, `basis_corrected_from_formula`, `sign_from_heading`, `dft_outlier_unexplained` |
| `link_rule` | (text) the rule that linked the value to its quote: the quote is verbatim on one page/passage and the printed value is inside it |
| `reader_agreement`, `reader_agreement_label` | (tables) how many independent visual readers carry the value: `multi_reader` ("confirmed (two or more readers)"), `single_reader` ("unconfirmed (single reader)": one visual reader read the row and no second reader has confirmed it; the explorer shows a "single reader" badge), `single_reader_text_layer` ("single reader + PDF text layer": one visual reader, every value also printed in the PDF text layer). Two blind, independent Claude reads (`claude-opus-5.5` and `claude-opus-5.5#2` in `readers`) count as two readers. Where the record builder did not classify the value (invariant tables, running text) it is derived from `readers` (`reader_agreement_basis: derived_from_readers`, settled rows only) |
| `readers`, `reader_tier`, `tool_release` | extraction provenance: the readers whose readings agreed (`text_layer` = the PDF text layer), the reader tier, a pipeline status of the table or passage (which reader passes ran), not an agreement claim (`preliminary_flash+claude`: two independent model readers agree; `confirmed_pro`: confirmed by a third reader; `+fable_judge`: a judge chose between disagreeing readers; `preliminary_flash` for text: first reader, with the second reader's agreement in `flags.second_reader`), the record-builder release |

## Controlled vocabularies

`transition_kind`: `eutectic`, `peritectic`, `monotectic`, `syntectic`, `eutectoid`, `peritectoid`, `monotectoid`, `quasi-peritectic`, `congruent melting`, `congruent transformation`, `polymorphic`, `magnetic`, `order-disorder`, `critical point`, `metatectic/catatectic`, `other`. Mapping (deterministic, `tools/literature_quality.py`): typos repaired (Peritetic,
Peritectiod, ...); keywords (eutectoid before eutectic, magnetic before order-disorder, metatectic and catatectic
together); Rhines letters (E/e eutectic, P/p peritectic, U and class II quasi-peritectic; ternary class I eutectic);
"C.M.", "melting point", "congruent melting" congruent melting; a bare "congruent" (point, reaction) is congruent
melting when a liquid phase is among the record's phases and congruent transformation otherwise; "polymorphic",
"allotropic", "solid state transition" polymorphic; printed alternatives ("eutectic (or peritectic)"), liquidus /
solidus points, extrema (max, min, saddle), "C" and anything else are `other`. Every printed variant, its term and
the rule are listed in QUALITY_FIXES.md.

Property codes: malformed builder codes are cleaned (`ACR_` -> `ACTIVITY`, `LATTICE_4F_Z_1_` -> `LATTICE_4F_Z_1`, `LATTICE_4F_Z_2_` -> `LATTICE_4F_Z_2`, `LATTICE_4F_Z_FIRST_` -> `LATTICE_4F_Z_1`, `LATTICE_4F_Z_SECOND_` -> `LATTICE_4F_Z_2`, `LATTICE_A_` -> `LATTICE_A`, `LATTICE_A_FE50NI50_` -> `LATTICE_A`, `LATTICE_A_FE_` -> `LATTICE_A`, `LATTICE_A_NI_` -> `LATTICE_A`, `LATTICE_B_` -> `LATTICE_B`, `LATTICE_COMMENTS_UNLABELLED_VALUE_` -> `LATTICE_UNLABELLED_VALUE`, `LATTICE_C_` -> `LATTICE_C`, `LATTICE_LATTICE_PARAMETER` -> `LATTICE_UNSPECIFIED_AXIS`, `LATTICE_LATTICE_PARAMETERS` -> `LATTICE_UNSPECIFIED_AXIS`, `LATTICE_LATTICE_PARAMETERS_ERROR_5_` -> `LATTICE_UNSPECIFIED_AXIS`, `LATTICE_LATTICE_PARAMETER_A` -> `LATTICE_A`, `LATTICE_LATTICE_PARAMETER_ALPHA` -> `LATTICE_ALPHA`, `LATTICE_LATTICE_PARAMETER_B` -> `LATTICE_B`, `LATTICE_LATTICE_PARAMETER_BETA` -> `LATTICE_BETA`, `LATTICE_LATTICE_PARAMETER_C` -> `LATTICE_C`, `LATTICE_LATTICE_PARAMETER_GAMMA` -> `LATTICE_GAMMA`, `LATTICE_VAT` -> `LATTICE_V_ATOM`, `LATTICE_V_AT` -> `LATTICE_V_ATOM`; `LATTICE__` -> `LATTICE_UNSPECIFIED_ANGLE` for
angles, `LATTICE_UNSPECIFIED_AXIS` otherwise; other `LATTICE_LATTICE_PARAMETER_*` and trailing / doubled
underscores stripped). The raw code is kept in `property_code_raw`.

## Data hardening (`QUALITY_FIXES.md`, `quality_fixes.json`)

Duplicates merged (`merged_ids`), vocabularies, garbled pages / bounds / reference states, DFT-parity outlier
classes and energy-unit conversions, with every changed record (id, before, after, evidence) for pages,
reference states and outliers, and the complete variant tables for the vocabularies.

## Paper fields (`papers.json`)

Ids are `doi:<doi>` (corpus papers and cited works with a DOI), `sha256:<16 hex>` (corpus papers without DOI),
`ref:<hash>` (a cited work without DOI, citation = the printed reference), `nodoi:<hash>` (a cited work whose matched
DOI was dropped as a mismatch, same id as in the bibliography) or `unresolved:...` (a reference label the
citing paper's list could not resolve). Fields: `id`, `doi`, `doi_status` / `doi_correction` (`dropped_mismatch` and
the entry id of `tools/doi_corrections.json` when the DOI an upstream matcher assigned was dropped because its Crossref
record describes a different work; `doi` is then null and the citation is the printed one), `doi_url`, `citation` (formatted from local Crossref metadata, the TDB header, or the printed reference),
`title`, `authors`, `year`, `container`, `kind` (`corpus_paper`: a paper the values were read from; `cited_work`:
a work cited for a value), `roles` (`reporting_paper`, `cited_source`), `systems`, `metadata_source`,
`pdf_sha256_prefix` (first 16 hex of the source PDF's SHA-256, an identifier only; the PDF is not distributed).

## Context items (`contexts.json`, `systems/*.json` -> `contexts`)

`{paper, context_id, section_basis, reader_tier, readers, items[]}`; each item has `type` (`sample`, `treatment`,
`measurement`), its fields (`purity`, `starting_materials`, `synthesis`, `nominal_composition`; `temperature_printed`,
`duration`, `atmosphere_or_container`, `cooling`, `purpose`; `method`, `instrument`, `heating_or_cooling_rate`,
`atmosphere`, `crucible`, `calibration`, `stated_uncertainty`, `applies_to`), its `quote` and `page`, and
`extraction_accepted`, `second_reader_agrees`, `has_dropped_fields`, `review_flags`. A context is attached only to values
whose origin is the paper's own experimental work.
