Skip to content

Develop - #341

Draft
namedgraph wants to merge 7 commits into
masterfrom
develop
Draft

Develop#341
namedgraph wants to merge 7 commits into
masterfrom
develop

Conversation

@namedgraph

Copy link
Copy Markdown
Member

No description provided.

namedgraph and others added 7 commits August 6, 2026 12:09
* Serve raw ontology graphs without RDFS inference

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>

* Fix ScopedGraphRepository.contains() dropping resolvable imports

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>

* Alias only repository-held closure ids under their document URIs

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>

* Fix GET-proxied-ontology-ns.sh assertions for closure DESCRIBE semantics

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>

* Remove Linked Data proxy DESCRIBE-over-closure fallback

The proxy is document-keyed transport; term lookups over the ontology
closure belong on /ns (SPARQL), not on a DESCRIBE synthesized from the
proxy target URI. Drop the fallback and its ParameterizedSparqlString/
QueryExecution imports; the isCached closure-cache branch (raw per-doc
graphs, no inference) still serves closure documents.

Realign tests: delete GET-proxied-ontology-ns.sh (it only exercised the
removed DESCRIBE path and dereferenced ontology terms via the admin doc
URI, which was never a supported path). Add GET-ns-no-query.sh (raw /ns
ontology graph, no rdfs:Resource leak) and GET-proxied-mapped-vocab.sh
(dct:title, foaf:Person, skos:Concept served from the static prefix
mapping; skos also covers fragment-strip + xml:base resolution).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Make the Linked Data proxy dumb: drop ontology-closure serving

Remove the getOntology()/isCached branch that answered ?uri= requests from
the app ontology owl:imports closure cache, plus the now-unused ontology
injection, getOntology(), and PrefixGraphRepository/Application/EndUserApplication/
OntModel imports. The proxy is now dumb transport: bundled-vocab file cache
(isMapped) + SSRF-checked external fetch. Ontology terms are served by /ns.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix GET-proxied-mapped-vocab SIGPIPE on large vocab responses

The assertions piped the whole vocabulary graph through `echo | grep -q`.
`grep -q` exits on first match and closes the pipe while `echo` is still
writing; with `set -o pipefail` the SIGPIPE'd `echo` (write error: broken
pipe, exit 141) fails the pipeline whenever the response exceeds the ~64 KiB
pipe buffer — so a *successful* match killed the test. Only this test trips
it, being the only one that returns entire vocabulary documents (40-113 KiB).

Read from a here-string instead (temp file, no pipe to break), and match the
full language-tagged label literal the bundled documents actually carry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
)

The four view controls (order-by dropdown, facet headers, parallax
properties, rdf:type facet values) resolved predicate/class labels by
proxying each term's vocabulary document (GET ?uri=<ac:document-uri(term)>),
then applying ac:label to the response. With the proxy no longer serving
ontology terms, route these through the app's /ns SPARQL endpoint instead.

- view-results chain: add a property-metadata load step (mirroring the
  existing object-metadata step) that DESCRIBEs the result predicates over
  /ns before ldh:render-view, threading $property-metadata through to the
  order-by and facet-header rendering.
- order-by + facet headers: render labels synchronously from
  $property-metadata via ac:label (local-name fallback for terms absent
  from the closure); drop the per-predicate proxy promises and the
  ldh:order-by-response / ldh:facet-filter-response handlers.
- parallax + rdf:type facet values: swap each per-term proxy GET for a
  POST /ns DESCRIBE $Type VALUES { <term> }; the response is still
  application/rdf+xml so the existing handlers are unchanged.

Verified against the unesco-thesaurus demo: on /concepts/concept1002/ the
redundant vocab-document proxy fetches drop (skos/core 9->5, rdf 2->1,
prov 2->1), the SKOS view predicates now resolve via /ns, and the 429/502
the redundant load triggered are gone.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
POST-proxied-external-query.sh proxied a live query to dbpedia.org/sparql,
whose public endpoint intermittently returns 502 in CI. Rename it to
POST-proxied-cross-origin-query.sh and point it at the admin app's SPARQL
endpoint (a different origin than the end-user app), so it still exercises
the ProxyRequestFilter remote-fetch + SPARQL-results re-serialization path
but runs deterministically against a local target. Use the owner cert since
the admin endpoint is ACL-protected, and assert the response is actual
SPARQL results rather than only a 200 status.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Replace /add and /generate endpoints with client-orchestrated graph writes

Move the "Add data" and "Generate containers" orchestration to the client
over the uniform Graph Store Protocol interface, removing the server-side
fetch/SSRF surface (pen-test LNK-002) and collapsing two special-purpose
endpoints into standard per-document writes.

- Add data: the submit handler splits by the presence of a spin:query input.
  The add/clone variant fetches dct:source through the same-origin ?uri=
  proxy as RDF/XML (CORS + Jena format conversion) and POST-appends it to the
  sd:name target (ldh:add-data-source-response / ldh:add-data-form-error). The
  import-ontology variant is unchanged and still RDF/POSTs to /transform.
- Generate containers: builds one container document per checked class and
  PUTs them via parallel ixsl:all fan-out (ldh:generate-containers-fanout ->
  ldh:generate-containers-join), seeded by ixsl:resolve so the requests run in
  an active promise context. The view block is now correctly wrapped as
  ldh:Object -> rdf:value -> ldh:View (the endpoint's bare ldh:View bypassed
  ldh:InvalidContentBlockType validation).
- Delete Add.java, Generate.java and their Dispatcher locators. /transform is
  retained until a client-side SPARQL engine lands.
- http-tests: drop the obsolete /add and /generate tests (the system/ suite is
  unregistered); add add/GET-proxied-source-POST-append.sh and
  add/PUT-generate-container.sh. CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix generate-containers dialog: parent init, disabled Generate, async schema load

- Initialise the parent typeahead with the current container (ldh:LoadTypeaheads
  from ldh:base-uri, matching btn-save-as), which was lost when the form stopped
  carrying a source param.
- Disable the Generate button until the schema is loaded; enable it in
  ldh:endpoint-classes-response once the class list is populated.
- Make Load schema fully async: the service-endpoint resolution used a blocking
  document() fetch (the "TO-DO: asynchronous request"). Replaced with an
  ixsl:resolve -> ldh:load-schema-endpoint -> ldh:load-schema-results promise
  chain, so the request no longer blocks and the progress cursor shows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add RDF data: seed target graph from the local dataspace, reject remote targets

The "Add RDF data" dialog seeded its target graph typeahead from
ldh:base-uri(.), which resolves to the proxied remote resource when viewing
one (ac:document-uri(ac:uri())). So adding data while viewing a proxied
document defaulted the write target to that remote and proxied the append to
it (403). The source is still ldh:base-uri (the remote being imported).

- btn-save-as now seeds the graph typeahead from ac:absolute-path(ldh:request-uri())
  (the local browser location), which is always a local dataspace document.
- Guard the append: a cross-origin target renders an inline error instead of
  proxying the write. ldh:add-data-form-error now tolerates a missing response
  so pre-fetch validation can reuse it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Condense Unreleased CHANGELOG entries to one line each

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Merge /ns ontology labels into client-side object-metadata

Object labels for rdf:type (class) objects were missing because
$object-metadata-query only queried the instance SPARQL endpoint
(sd:endpoint() -> /sparql); class labels live in the ontology served
at /ns (in-memory OntModel), not in instance data. So class objects
fell through ac:object-label to their URI fragment.

Object-metadata is now the union of labels from /sparql (instances)
and /ns (ontology/classes), merged client-side into the single
$object-metadata document ac:object-label already consumes. No SERVICE
federation and no Java: ARQ's SERVICE bypasses LDH's proxy/truststore
and a query to /sparql is executed by an isolated Fuseki container that
can't reach the app's public /ns, so a two-request merge is used
instead, mirroring the existing property-metadata -> /ns pattern.

- client.xsl: add $object-metadata-ns-query (the label CONSTRUCT
  without the GRAPH ?graph wrapper, since /ns serves the ontology in
  the default graph); wire the /ns fetch + merge into the document-load
  chain.
- merge-rdfxml.xsl: add ldh:merge-metadata(), delegating to the
  existing ldh:MergeRDF mode (guards an absent side with empty rdf:RDF).
- block.xsl: ldh:load-object-metadata now builds both /sparql and /ns
  requests; add ldh:set-object-metadata-ns + ldh:merge-object-metadata;
  wire the ontology-view chain.
- view.xsl: wire the view-results chain.
- form.xsl / modal.xsl: add the /ns tuple + merge step to all five
  ldh:fire-load-set-parallel chains.
- object.xsl: build the /ns request, fire it in the metadata thunk,
  merge both bodies in the response handler.

ac:object-label and its $object-metadata tunnel are unchanged; only the
assembly of that document changes. Server-side (SSR) assembly is left
untouched: ldh:rdf-document-response replaces the SSR-rendered
document-body on load, so the visible labels come from the client merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Merge /ns ontology labels into server-side object-metadata (initial paint)

The client render already merges instance labels (/sparql) with
ontology/class labels (/ns) for object URIs, but the initial
server-rendered paint (layout.xsl, the counterpart of client.xsl's
document-load chain) fetched object-metadata from sd:endpoint() only.
So on first paint an rdf:type/class object showed its URI fragment,
snapping to the real label once the client re-rendered the body.

Give the server's first paint the same merged metadata:
- declare $object-metadata-ns-query (the label CONSTRUCT without the
  GRAPH ?graph wrapper, since /ns serves the ontology in the default
  graph), mirroring client.xsl;
- include client/merge-rdfxml.xsl so ldh:merge-metadata() + the
  ldh:MergeRDF mode are available server-side (pure XSLT, no ixsl);
- the initial-paint $object-metadata variable now fires both requests
  (each in its own try/catch) and merges them via ldh:merge-metadata,
  passing the merged document as the bs2:TabBody object-metadata tunnel.

Verified: layout.xsl compiles clean under Saxon-HE (product=SAXON, the
server-side processor) — a full execution plan is produced with no
static errors, matching the pre-edit baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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