Skip to content

Serve raw ontology graphs without RDFS inference - #337

Open
namedgraph wants to merge 4 commits into
developfrom
raw-ontology-serving
Open

Serve raw ontology graphs without RDFS inference#337
namedgraph wants to merge 4 commits into
developfrom
raw-ontology-serving

Conversation

@namedgraph

@namedgraph namedgraph commented Jul 27, 2026

Copy link
Copy Markdown
Member

Problem

View blocks rendered as generic property lists instead of executing their spin:query. Root cause: OntologyFilter materialized an RDFS-inferred imports closure, and ProxyRequestFilter's namespace short-circuit served proxied ?uri= requests via DESCRIBE over that inferred model. ldh:View a rdfs:Class + rules rdfs7b/rdfs9 type every instance rdf:type rdfs:Resource, so proxied ontology documents carried an extra type on every term — producing multi-token @typeof that the client's exact-equality *[@typeof = '&ldh;View'] match no longer satisfied.

Changes

No RDFS inference over application ontologies. An audit showed the reasoner was vestigial: constructors recurse over direct superclasses, SPIN constraint collection walks rdfs:subClassOf itself, and every client ontology query already uses (rdfs:subClassOf)* property paths. The owl:imports closure is now resolved natively by ontapi (OntModelFactory.createModel over a new ScopedGraphRepository view) as a live union graph — the manual flatten/infer/materialize pipeline is deleted. rdfs:Class terms are promoted to owl:Class in a separate union member (ontapi's OWL2 named-class factory doesn't recognize bare rdfs:Class), so no document graph is polluted. Closure union graphs are cached in a bounded map on Application; per-request OntModel wrapping is preserved.

Two ontapi interaction subtleties surfaced by the HTTP test suite, both pinned by the new OntologyClosureCIReproTest:

  • ontapi consults GraphRepository.contains() before get() when resolving imports; ScopedGraphRepository.contains() now answers resolvability (attempting the load) instead of cache state, otherwise every bundled-mapped/SPARQL/HTTP import was silently replaced by an empty graph — stripping SPIN constraints from the closure
  • ontapi keys imports under their declared ontology IRIs; the doc-URI aliasing loop only touches ids the shared repository actually holds, so a content-addressed upload declaring a foreign ontology IRI no longer triggers an HTTP dereference of that IRI during load

Raw document graphs on the wire, uniformly. The shared repository now only holds raw per-document graphs. ProxyRequestFilter serves closure documents with their raw graphs — asserted triples only, identical to a direct document GET — guarded by isCached so arbitrary external URIs can't trigger repository loading outside the SSRF-validated external-client path. The DESCRIBE fallback remains for terms minted in external namespaces and is now inference-free. Namespace's no-query GET reuses the shared raw cache instead of a per-request createRepository SPARQL round-trip. Invalidation updated in ClearOntology and ValidatingModelProvider.

Also fixed in passing: UninstallPackage no longer reads a materialized closure when a package URI is a loaded app ontology, and admin ontologies importing each other can no longer fold one closure's artifacts into another on the shared system repository.

With the server no longer serving inferred types, LDH-authored blocks carry single-token @typeof again — the client XSL is untouched relative to develop.

Test plan

  • Full unit suite passes (92 tests). OntologyImportsCharacterizationTest rewritten to pin: eager closure resolution, zero inference in the union, raw document graphs untouched (isomorphism), promotion not leaking into raw graphs, getOntClass recognition of rdfs:Class-only terms, import-cycle tolerance. OntologyClosureCIReproTest pins the production-shaped closure (real bundled location mappings) and the mismatched-IRI upload import
  • http-tests/proxy/GET-proxied-ontology-ns.sh extended: proxied ontology-document hash terms served with no rdf:type rdfs:Resource; same negative assertion on the DESCRIBE fallback
  • Full HTTP test suite green in CI
  • Browser: View block on the homepage executes its query and renders the table; single-token typeof in the DOM

🤖 Generated with Claude Code

@namedgraph
namedgraph force-pushed the raw-ontology-serving branch from 7327548 to b8e472d Compare August 2, 2026 12:36
@namedgraph namedgraph changed the title Serve raw ontology graphs without RDFS inference; token-safe @typeof matching Serve raw ontology graphs without RDFS inference Aug 2, 2026
namedgraph and others added 4 commits August 2, 2026 20:34
Resolve the application ontology's owl:imports closure natively via
ontapi (OntModelFactory.createModel over a ScopedGraphRepository view)
instead of manually flattening the closure and materializing an
RDFS-inferred model. No inference is applied anymore — every consumer
(constructor/constraint inheritance, client-side (rdfs:subClassOf)*
queries) traverses hierarchies explicitly. rdfs:Class terms are promoted
to owl:Class in a separate union member so no document graph is
polluted; closure union graphs are cached in a bounded map on
Application, keyed by ontology URI.

The shared repository now only ever holds raw per-document graphs, which
ProxyRequestFilter serves directly for closure documents — asserted
triples only, identical to a direct document GET. The DESCRIBE fallback
over the in-memory closure (terms minted in external namespaces) is
inference-free as well. This fixes inferred rdf:type rdfs:Resource
leaking into proxied namespace documents, where the extra type produced
multi-token @typeof and silently degraded View blocks to a generic
property list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ontapi consults GraphRepository.contains() before get() when resolving
an ontology's imports closure. PrefixGraphRepository.contains() reports
cache state (loaded graphs), not resolvability, so every import that
resolves through a bundled location mapping (dh, sp, spin, foaf, sioc,
sd), SPARQL-first loading or HTTP was answered with false on first
resolution — and ontapi silently substituted an empty ontology graph
for it (its ignoreUnresolvedImports fallback). The closure kept its
shape but lost the content of every such import: SPIN constraints
vanished, so validation enforced nothing (422 tests wrote through,
eventually applying invalid dataspace settings and cascading into
NPEs), and vocabulary term lookups came up empty. This is what failed
the HTTP test suite in CI.

contains() now attempts resolution through the backing repository
(which loads and caches the graph) after the cache checks, reporting
absent only for genuinely unresolvable ids. Adds a production-shaped
regression test: ns# ontology importing the SPARQL-seeded ldh#
vocabulary with its transitive imports resolved through the real
bundled location mappings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ontapi keys imports under their declared ontology IRIs, which need not
be repository entries: a content-addressed upload is cached under its
uploads/ URI while declaring a foreign ontology IRI. The doc-URI
aliasing loop called repository.get() on such declared IRIs, which fell
through to an HTTP dereference of the foreign IRI (e.g.
https://example.org/test) during ontology load — failing the load and
the ontology-import-upload-no-deadlock HTTP test. Guard the loop with
isCached() so only graphs the shared repository actually holds get
aliased. Adds a mismatched-IRI import case to the closure regression
test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The admin ontologies/namespace/ document stores the ontology but the
closure keys it under the <ns#> ontology URI, so a proxied GET of the
document URI is answered by the closure DESCRIBE fallback — the
document's own #-fragment term descriptions — not the raw graph branch.
Assert on a class minted in the document's hash namespace (mirroring
the original #related_View regression) instead of the made-up-namespace
classes, which only appear under their own namespace document URI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@namedgraph
namedgraph force-pushed the raw-ontology-serving branch from 5553543 to 205d102 Compare August 2, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant