Record scientific assertions
Your archive holds measurements, but you also need to record conclusions such as “this film is PET” as typed, evidence-linked claims that can later be superseded or retracted without erasing what you first believed.
For the common case, sample.claim commits one operator-entered claim and sample.claims reads its active heads; evidence, typed subjects and the claim lifecycle stay core operations.
This guide assumes: you have finished the tutorial path. The concept page two kinds of identity is helpful background, because assertions lean on both record identity and physical sample identity.
A scientific assertion records one typed property value about the physical sample, one
run, or one exact record. Each property has a property identifier (a namespaced
lower-case token such as polymer.identity), and each assertion names the subject it
describes through a SubjectRef. To make a claim about the physical sample, the container
first needs a sample UID (a portable identity for the physical object, distinct from
the human-facing sample_id). assign_sample_uid mints one for you.
import numpy as np
from mtrl import InstrumentRun, MaterialData, Mtrlfrom mtrl.assertions import AssertionDraft, SubjectRef
application = Mtrl(sample_id="polymer-film-01")sample_uid = application.assign_sample_uid()run = InstrumentRun(run_name="ftir-replicate-01")checkpoint = MaterialData( content=np.array([0.15, 0.21, 0.19]), type="raw", checkpoint_name="spectrum",)run.add_checkpoint(checkpoint)application.add(run)
draft = AssertionDraft( subject=SubjectRef.sample(sample_uid), property_id="polymer.identity", value="polyethylene terephthalate", author="analyst",)assertion = application.assert_properties([draft])[0]print(f"property: {assertion.property_id}; value: {assertion.value}")
for claim in application.list_assertions(property_id="polymer.identity"): print(f"claim id: {claim.claim_id}; status: {claim.status.value}")
corrected = application.supersede_assertion( assertion.assertion_id, value="polypropylene", reason="The reference library match favours polypropylene.",)print(f"supersedes the original: {corrected.supersedes == assertion.assertion_id}")print( f"corrected: {corrected.value}; " f"original: {application.get_assertion(assertion.assertion_id).value}")property: polymer.identity; value: polyethylene terephthalateclaim id: 01a1830d-adf8-7251-abc7-ccf5d3ef0490; status: assertedsupersedes the original: Truecorrected: polypropylene; original: polyethylene terephthalateThe first printed line is property: polymer.identity; value: polyethylene terephthalate.
The loop then prints the claim identifier (a UUID, so yours will differ) and the status
asserted. After the supersede call, the third line is supersedes the original: True:
the new version points back at the old one. The last line is corrected: polypropylene; original: polyethylene terephthalate, because the corrected version heads the claim while
the original version remains readable in the history.
What just happened?
Section titled “What just happened?”assign_sample_uid gave the container a portable physical sample identity, a UUIDv7
string, while leaving sample_id untouched. SubjectRef.sample(sample_uid) then aimed
the draft at the physical film rather than at a run or a record.
assert_properties committed your draft as one revision. The batch form is atomic: if any
draft is invalid, nothing changes. When you have a single claim, assert_property is the
convenience route with the same fields as keyword arguments, for example
application.assert_property(subject=..., property_id=..., value=...); it commits one
claim in one revision.
Now the claim/version distinction, which is the heart of this feature. A claim is the
continuing statement (“the identity of polymer-film-01”), and it keeps one ClaimId
across its whole life. A version is one committed wording of that claim, and each
version gets its own AssertionId. When supersede_assertion corrected your
identification, it did not overwrite anything. It added a second version under the same
ClaimId, marked as superseding the first AssertionId. Both versions stay in the audit
history, which is why get_assertion could still read the original value.
retract_assertion withdraws a claim outright and requires a non-empty reason. Use
list_assertions(active_only=False, include_retracted=True) to see every version,
including retracted ones, and pass as_of_revision to read the claim state as it stood at
an earlier revision. Independent claims about the same subject and property coexist, so a
colleague’s claim never silently overwrites yours.
Where next
Section titled “Where next”- Two kinds of identity — why a record identifier and a sample identifier are different things, and when to use each.
- Revisions: the archive timeline — how each lifecycle call lands as exactly one revision you can revisit.