Graph Metadata
Every named graph the pipeline publishes carries a small block of triples in which the graph describes itself — what it is, what it is about, who loaded it, when, and how big it turned out to be. It is the same shape in every graph, which makes it both the easiest thing to get uniformly right and the easiest to get uniformly wrong.
It was uniformly wrong. The description node was typed academic event instead
of textual description — two IRIs one digit apart — and because a description
sits on the right-hand side of denoted by, which requires information rather
than an event, every graph carrying one was logically inconsistent. Nothing
caught it: the IRI was real, it was not deprecated, and the tests asserted the
same wrong term the code wrote.
Visualization
Four kinds of thing appear here, and telling them apart is the whole model:
| Node | Kind | Why it matters |
|---|---|---|
| the graph | data collection, void:Dataset |
a continuant — it persists, it does not happen |
| the description | textual description | also a continuant: information, not an event |
| the load | process | the only occurrent, and so the only thing that may occupy a stretch of time |
| the instants | temporal instant | zero-dimensional; the begin and end of the load |
The rule that does the work
occupies temporal region has
domain process or process boundary, and is functional. It may not be written on
the graph, or on the description, or on a person — only on the load. If you want
to say when a continuant was valid, that is a different property and a
different pattern; see Role Bearer.
What goes wrong
Diff this against the block above: the description is typed NFDI_0000018, and
the instants are typed with the parent class. The first is fatal, the second is
silent, and the pair is a fair summary of what a reasoner can and cannot do for
you.
Fatal — the reasoner catches it. Turn reasoning on and the graph is inconsistent. Every axiom in the chain belongs to BFO, IAO or nfdicore; only the one asserted type is ours:
denoted by |
range information content entity |
| information content entity | ⊑ generically dependent continuant ⊑ continuant |
| academic event | ⊑ event ⊑ process ⊑ occurrent |
| continuant | owl:disjointWith occurrent |
An inconsistent graph is not a graph with one bad triple in it. Under OWL semantics everything follows from a contradiction, so every query against that graph is entitled to return anything at all — which is why the check belongs before publication, not after.
Silent — only the shape catches it. The instants are typed
BFO_0000148 zero-dimensional
temporal region rather than
BFO_0000203 temporal instant.
BFO_0000203 is a subclass of BFO_0000148, so the looser type contradicts
nothing and no reasoner will ever mention it. It simply discards the precision
the range axiom exists to provide. The SHACL shape on this page notices; HermiT
never will.
The same holds for NFDI_0000009,
which is deprecated in favour of
IAO_0001000 data collection.
Deprecation is advice, not an axiom — it cannot make anything inconsistent, so
only a shape will raise it.
Declare your properties, or none of this happens
Worth stating on its own, because it invalidates the obvious way of checking any of the above.
An OWL parser must decide what kind of property a statement uses. If the file being read does not declare it, the fallback is to treat the statement as an annotation — a note, carrying no logical meaning. A reasoner does not evaluate annotations; it does not see them. So a reasoner run over a bare data export returns consistent however broken the data is, and the run looks exactly like a passing one.
Both .ttl files on this page begin with declarations for that reason:
obo:IAO_0000235 a owl:ObjectProperty . # denoted by
obo:BFO_0000199 a owl:ObjectProperty . # occupies temporal region
Delete those two lines from broken-data.ttl and the inconsistency disappears —
the data is just as wrong, and the reasoner has been handed nothing to check.
Before trusting any green reasoner result over pipeline output, confirm the check
was live: inject a known-bad triple and make sure it is still caught.
SHACL Constraints
| Target | Constraint |
|---|---|
void:Dataset |
has a non-empty rdfs:label |
void:Dataset |
denoted by a NFDI_0001018 textual description carrying a non-empty NFDI_0001007 value |
void:Dataset |
output of exactly one BFO_0000015 load process |
void:Dataset |
not typed with a deprecated class |
| load process | at least one RO_0000057 participant |
| load process | exactly one BFO_0000199 region, a BFO_0000038 with first and last instants |
BFO_0000203 |
exactly one time:inXSDDateTimeStamp typed xsd:dateTimeStamp |
Terms
| Term | Name |
|---|---|
IAO_0001000 |
data collection — replaces the deprecated NFDI_0000009 |
IAO_0000235 |
denoted by |
NFDI_0001018 |
textual description |
NFDI_0001007 |
has value |
RO_0002353 |
output of |
BFO_0000015 |
process |
RO_0000057 |
has participant |
BFO_0000199 |
occupies temporal region — functional, processes only |
BFO_0000038 |
one-dimensional temporal region |
BFO_0000222 / BFO_0000224 |
has first / last instant — range is BFO_0000203 |
BFO_0000203 |
temporal instant |
NFDI_0000004 |
person |