Skip to content

Data model

The question this answers: what is a Chemical Record made of, and what is deliberately kept apart?

What follows is the spec-level model — the entities the Specification names and the rulings that fix their shape. Every term in the diagram is a glossary term; CONTEXT.md holds the definitions the rulings use, and the properties the physical schema has to satisfy are listed in Specification, Seam 1.

A physical schema exists, and not in this repository. It lives in the api gitlink under app/chemical-safety-assistant-api/prisma/models/, where seventeen Corpus models are declared, and ten migrations describe how it got there. None of those migrations has ever been applied to a live Postgres: the api's test suite runs without a database, because the snapshot serialiser is a pure function over rows. So the model below is still the one to read for shape, and the schema is the one to read for columns — and the two are kept in step by Specification, Seam 1 rather than by either document quoting the other.

The entities

Three Corpus artifacts sit outside this diagram because they belong to the Corpus rather than to any one record: the Routing Threshold, the Escalation Trigger phrase list and the Routing Fixture. They are listed in Curation and ruled on in ADR-0031.

Record identity

QuestionRuling
Whether a record is keyed on a substance or on the document it came fromADR-0013 — A Chemical Record is keyed on its SDS document, not on a substance (D-18)
What the identifying fields are, and what two revisions of the same product amount toADR-0013
What identity becomes when the document states no revision dateADR-0024 — An SDS with no revision date is admitted, and the absence is shown
Whether such a document may be refused admission insteadADR-0024, rejected options
Which identifier Identification reads first, and what it falls back toADR-0013 (D-19), corrected by ADR-0018 (D-35)

The ruling that made CAS Number the primary key is kept and marked dead: ADR-0004 — superseded by ADR-0030; read order preserved, ranking replaced. The three peer routes that replaced its ranking are in Identification and Routing.

Optional attributes, and absence as a value

QuestionRuling
Whether CAS Number is required, and what a Formulated Product carries insteadADR-0013
Whether a missing revision date may be filled from the Curation date or a file timestampADR-0024
What a PPE field holds when the SDS names no glove materialADR-0025 — Generic PPE text is displayed as generic, and the glossary rule is softened
Whether an empty safety field is an error or an answerADR-0002 (D-03)
Whether an empty device is the same value as an empty fieldADR-0032 — see The Corpus lifecycle
Whether a document records how many pages it has, what a document with no readable count means, and what the verification gate then checksADR-0074, superseding ADR-0059

Formulated Product, Pure Substance, Not Stated and Corpus Unavailable are all defined in CONTEXT.md, which also records that the per-item PPE definition is an aspiration rather than a guarantee.

How a row stops being displayed

A Corpus row is evidence with Provenance, so nothing in this model is deleted and a correction never rewrites what a row says (ADR-0084). A selection stops being displayed by releasing its position: the ordinal every selection row carries is nullable, and a row with none is one nothing may lay out. That is the whole of what "live" means, and it is the same filter the snapshot serialiser and the curator's record read both use.

QuestionRuling
What a correction replaces, and why it is the attachment row rather than the Source SpanADR-0084 (D-169)
How a position is freed without narrowing any unique constraint, and why a partial index lostADR-0084 (D-172)
Whether a retracted position is renumbered, and whether a gap in a sequence is a legal recordADR-0084 (D-170)
What happens to a superseded span's Restriction, Translation and ReconciliationsADR-0084 (D-171)
Which two mutations of a stored row the Corpus permits, and why the list is closedADR-0084 (D-177)
Why a Spill Response branch is not an attachment row at allADR-0086 (D-179)

Two consequences worth stating in the model's own terms. A correction chain is linear — one predecessor has at most one replacement, enforced by a unique on the link rather than by a rule something has to remember — so a position holds exactly one live row at every instant. And because ordinal is nullable while the uniques stay as written, many released rows coexist at one position: NULLs are distinct, which is what makes a chain representable at all.

What travels with a displayed span

QuestionRuling
Whether a safety field may exist without a Source SpanADR-0002
Whether the SDS Section reference travels with the span wherever it is shownADR-0002, Specification §7
What laying spans out as labelled fields is permitted to change about their wordsADR-0012
Whether a Curated Translation replaces its original or hangs off itADR-0009 (D-11)
Whether the stored text of a span may be an unverified extractionADR-0027
Whether a Spill Response branch may be chosen for the user, and what its step order meansADR-0026
What Routing is allowed to attach to a span it points atADR-0031 (D-65)
In what shape the SDS Section reference crosses to the device, and why a rendered string was not enoughADR-0072
Where the sixteen Section headings themselves are held, and in which languagesADR-0060
Whether a published version's bytes may be reshaped by a later schema changeADR-0056, read together with ADR-0072

What is kept apart

QuestionRuling
Whether two suppliers' records for the same chemical may be merged, or one designated canonicalADR-0023 — Conflicting Chemical Records are never merged; the product in hand governs (D-45)
What a name search matching several products presents insteadADR-0023 (D-46)
Whether the Corpus may be deduplicated by chemical nameADR-0023, "This ruling may not be re-decided"
Whether safety content may enter a record from any source other than its own SDSADR-0025 (D-53)
Whether a Restriction from elsewhere in the document may be attached to a span at display timeADR-0022 — the mechanics are in Curation
Whether generated prose may occupy a safety fieldADR-0002, ADR-0012
Whether a term may carry a second Curated Explanation, and what a curator who wrote the wrong one can do about itADR-0073, superseding ADR-0061
Whether a stored copy of an admitted document can be removed once a span, a record or a published payload may reference itADR-0058
Whether the Reconciliation gaps read's answer is a closed union the two repositories key off, or an implementation detailADR-0071, on ADR-0068 and ADR-0069

ADR-0023 works the case that motivated it — two suppliers, the same solvent, opposite advice about the same glove material — and states what the conservative-looking merge would have produced.

Where the shape is asserted

PropertyWhere
Every Safety-Critical element carries a Source Span naming its SDS SectionSpecification, Seam 1
Every document has complete Provenance, and every Curated Translation a reachable originalSpecification, Seam 1
No record carries a revision date its document does not stateSpecification, Seam 1
Every record traces to exactly one SDSSpecification, Seam 1
A search matching several products returns all of them rather than collapsing to oneSpecification, Seam 3
What a record renders when a field is silent, or a date or material is absentSpecification, Seam 2
The schema the client validates on sync is the one the checker asserts againstSpecification, Seam 1

Where the rest of this is