- Status: implemented for the forward-chaining family (
rdfs,owl-rl,owl-rl-ext) ·reason --certificate DIR,onto_reasonwithcertificate_dir, batchreason --certificate· checker inlean/withOOCert.certificate_soundmachine-checked, axioms pinned by#guard_msgs· every shipped ontology certified in CI (tests/lean_certificate_test.rs) · opt-in, default unchanged · theowl-dltableaux path refuses the flag rather than pretending · the SHIQ tableaux reasoner now certifies its POSITIVE answers (Dl.satisfiable_of_checkModel, decision 0006); its negative answers and the parsers remain uncovered - Written: 2026-09-13
- Related: decision 0001 (an inference is not an assertion); issues #131 and #132, fixed the
same day;
tests/reason_rl_ext_soundness_test.rs, the first thing the layer caught
The engine's inferences were trusted because the engine was trusted. The reasoner is a few hundred
lines of Rust applying rules by hand, and on 13 September 2026 two of those rules turned out to be
unsound: cls-svf1 derived membership in a class from membership in its restriction superclass
(the converse of the axiom), and it treated a class IRI in object position as an instance of that
class. Both had been there since the extended profile was written. Both were found not by a test
but by asking, rule by rule, "what semantic condition would make this sound", and finding none.
A test suite catches the bugs someone thought of. What was missing is a check that does not depend on the engine's author having thought of the bug.
- The reasoner emits a certificate, not a claim. With
--certificate DIRevery inferred triple is written with the rule that produced it and the premises the rule read, in a fixed order per rule. The asserted graph goes beside it. Nothing about the engine's internals is needed to read the two files. - The checker re-derives, it does not search.
OOCert.checkStepmatches the premises against the rule's shape, checks each premise is asserted or was concluded by an earlier step, and checks the conclusion is the one the rule yields. No fixpoint, no indices, no search: a few hundred lines that a reader can hold at once. - The checker's soundness is a theorem, not a test.
OOCert.certificate_soundstates that a certificate the checker accepts contains only triples entailed by the asserted graph under the RDF-based semantics of the vocabulary the rules use (lean/OOCert/Semantics.lean). The proof is one lemma per rule plus an induction over the certificate.#guard_msgspins the axioms topropext,Classical.choiceandQuot.sound; asorryor anative_decidefails the build. - The semantic conditions are the weakest the rules need, with fourteen exceptions that were
found later and are now discharged; see the correction at the foot of this record. Each of the
rest is the if direction of the corresponding W3C condition or a consequence of it. Weaker
conditions admit more interpretations, so soundness here implies soundness over the
conditions
lean/OOCert/W3C.leanstates, and since 15 September 2026 that implication is a theorem (OOCert.W3CEntails.of_entails) rather than an argument in a docstring. Since the same day it reaches one layer further out:lean/OOCert/Conforming.leanformalises an OWL 2 RDF-Based interpretation in its own right, from RBS Table 5.1 and RDF 1.1 Semantics section 5's truth clause, proves the reading that used to be prose asOOCert.Conforming.toW3C, and discharges all five of theIPmemberships the old bridge assumed from five quoted axiomatic triples carried as fields.OOCert.certificate_conforming_soundis the resulting sentence. What is still not a theorem is that the field list is a SUBSET of the Recommendation's conditions, which a reader checks cell by cell; that a subset is the safe direction is the reason it is allowed. One modelling decision does not run in the safe direction and is named there:ILis total, per RBS section 4.2, so RDF 1.1 interpretations in which a literal fails to denote are outside the claim.Modelreads a list off the asserted graph throughChain, which is weaker than the specification's semantic sequence relation and so keeps the model class larger;OOCert.Conforming.seq_of_chainproves that comparison rather than asserting it. - The theorem is shown non-vacuous in the same directory. A soundness result about an
unsatisfiable semantics proves nothing, so
lean/OOCert/Witness.leanexhibits a model of an arbitrary graph, exhibits a triple that is not entailed, and proves that the derivationcls-svf1used to make is refuted by a model of its own premises while the half the reasoner still makes is entailed. That last pair turns "we removed a rule we could not justify" into "we removed a rule that was unsound", machine-checked. Those refutations are about this layer's own model class and not about the specification's; the correction below says why, and which one now has a conforming replacement. - Core Lean only. No Mathlib.
Std.HashSetfor membership, with its own lemmas bridging to list membership in the proof. The trust surface is the Lean kernel pluslean/. - The gate is proved able to fail. The test suite appends a forged conclusion, a premise
outside the graph, and the exact line the old
cls-svf1emitted, and requires each to be rejected. A gate that cannot fail is decoration. - The reasoner derives less from malformed lists.
owl:intersectionOfandowl:unionOflists are read only when every node carriesrdf:firstandrdf:restand the chain reachesrdf:nil. The old lenient walk fired the class rules on whatever it recovered; the checker has no rule for a list it cannot walk, so neither does the reasoner. Deriving less from malformed input is the sound direction.
It does not make ontologies "correct". It makes every OWL-RL inference the engine reports
independently checkable against a formal semantics with a proved checker. Whether an ontology says
what its author meant is a different question and no proof answers it. The SHACL validator, the
RDF parsers are outside the theorem. The SHIQ tableaux reasoner is partly inside it now: when it
answers that a class is satisfiable or an ontology consistent it has built a completion graph, that
graph is a model, and Dl.checkModel verifies it. When it answers unsatisfiable or inconsistent it
still carries no certificate, because that needs a refutation rather than a model, and the report
says so rather than leaving the reader to assume symmetry. The differential oracle
against pyshacl (tools/shacl_differential.py) remains the gate for SHACL.
An independent second formalisation in Isabelle/HOL, written from the W3C specifications with the Lean deliberately unread, found two things wrong with decisions 4 and 5 above. Both are recorded here rather than quietly edited, because the original wording is what the project told people.
Fourteen arms were not the weakest condition the rule needs. They were the rule. Twelve of them
(scm-eqc1 and scm-eqp1, two conclusions each, plus scm-svf1, scm-svf2, scm-avf1,
scm-avf2, scm-dom1, scm-dom2, scm-rng1, scm-rng2) are stated by no cell of any
specification table, and rdfs5 and rdfs11 were licensed but redundant. For those arms the
machine-checked content was close to nothing: the theorem said the rule was sound because a field of
the condition record said so. All fourteen are now derived from quoted cells in
lean/OOCert/W3C.lean, and every derivation is a proof term depending on no axiom at all. The cell
that makes them derivable is Table 5.8's connective, which carries rowspan="4" in the
specification's HTML and therefore states an iff, not the if-then that RDFS alone gives.
The claim about the OWL 2 Direct Semantics read through triples is withdrawn. It was never verified, and it is false as written for those twelve arms.
The claim that the W3C reads a list off the graph is withdrawn. The specification's sequence
notation quantifies over IEXT(I(rdf:first)) and IEXT(I(rdf:rest)), never over the graph, and it
says explicitly that no semantic constraint enforces well-formed sequence structures. Chain is
still the right thing to use, but for the opposite reason to the one given: it fires on strictly
fewer lists, so the conditions over it are weaker and the model class is larger.
Non-entailment never transferred outward and the record did not say so. A model of the weaker
conditions need not be a W3CModel, so a refutation built from one is a statement about this
layer's model class unless something restates it. That covered every ¬ Entails, every ¬ Unsat
and every exhibited Model in the repository.
And the first attempt to say how far that goes overstated it, in the other direction. The
branch that added W3C.lean claimed that no Herbrand or saturated witness could ever be a
W3CModel and that "every conforming countermodel has to be a hand-built finite structure". Both
are false. IP is a free parameter of W3C rather than a field of Interp, so the refuter chooses
it, and IP := fun _ => False makes sp_bwd, dom_bwd and rng_bwd vacuous; an empty IC makes
sc_bwd vacuous rather than demanding, which is the opposite of what the claim said. Five of the
eight negative results are now over W3CModel, four of them with their witness interpretations
unchanged, including mix_not_absolutely_w3c_entailed, which is what a report's
entailed_under_supplied_rules verdict rests on. The three that are not carry the failing field as
a decide-checked theorem.
Neither finding is a soundness bug. All of them are the layer claiming more than it had earned, which by this project's own standard is the same category of defect, and a false claim of rigour is worse than the assumption it replaced because the assumption was visible.