Develop - #358
Draft
namedgraph wants to merge 7 commits into
Draft
Conversation
* Client-side constructor instantiation: one prototype per instance An edited resource is one rdf:Description with N rdf:types, so its constructor template must be one rdf:Description with those N types and the union of their constructor properties - the template mirrors the instance. /ns?forClass= minted a fresh bnode prototype per class, and every consumer downstream assumed the single-prototype shape: document-order-dependent property picks (the intermittently vanishing app-settings Description field), parent-type match misses, ldh:SetResourceID orphaning N-1 prototypes in EDIT flows, and xs:anyURI? cardinality errors on multi-range predicates. Instead of asking the server to construct, the client fetches the constructor queries with one SPARQL SELECT (rdfs:subClassOf* closure, DISTINCT - cycle-safe, shared constructors dedup) and expands the CONSTRUCT templates onto a single instance: - ldh:constructor-query builds the SELECT; ldh:construct-instance parses each sp:text with SPARQL.js (no execution - constructors must have an empty WHERE clause; others are skipped with a warning), substitutes ?this with one shared instance label, prefixes marker bnodes per constructor, collapses same-(predicate, range) duplicates, and serializes through ldh:triples-to-descriptions - ldh:load-constructed-doc/set-constructed-doc rewired to the SELECT + instantiation; all EDIT/violation/CREATE chains covered unchanged - ldh:construct-forClass split by use-when: SAXON keeps /ns?forClass= (SSR), SaxonJS instantiates client-side - covers the tunnel-less bs2:FormControl default (SSR EditMode, ldh:DocumentForm, ldh:AppSettingsForm) - add-value flow tokenizes multi-type @typeof (was a latent 500) - forClass cardinalities relaxed to xs:anyURI* where a predicate can have several ranges across the type set (spin:query - sp:Query vs sp:Select); bs2:FormControlTypeLabel iterates all ranges like its marker-bnode sibling - GET-ns-constructors.sh pins the constructor SELECT server contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Remove /ns?forClass= - constructor instantiation is client-side only The client-side instantiation left server-side rendering as the last consumer of /ns?forClass=. SSR's constructor controls were transient paint - the client re-render replaces the form on load - so the SAXON ldh:construct-forClass becomes an empty-document stub (the ac:construct stub pattern): server-rendered edit forms show data properties, the client supplies the constructor controls. - BREAKING: Namespace no longer serves ?forClass= constructed instances; it serves SPARQL queries and the raw ontology graph only. Removes the Constructor usage and the unmapped-IRIException 500 on malformed class URIs - GET-namespace-forClass-rdfs.sh removed with the feature; the constructor SELECT contract is covered by GET-ns-constructors.sh - CacheInvalidationFilter's forClass ban stays: it serves the typeahead ?forClass= filtering on document URLs, not the removed construct - $ac:forClass in client.xsl stays: the shadowed Web-Client templates compiled into the SEF still reference it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Read SPARQL.js parse output via JSON round-trip SaxonJS does not marshal plain JS arrays for ixsl:get() access - the 'where' array came back as an empty sequence and the next ixsl:get() failed with a cardinality error. Serialize the parse tree with JSON.stringify + json-to-xml (the form.xsl SELECT-builder idiom) and read the CONSTRUCT template as json:map elements instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Keep violation machinery types out of the row-form instance type set ldh:row-form-submit-violation swept rdf:types from every non-document Description in the violation response, including the spin:ConstraintViolation / http:Response ones. With per-class prototypes that was harmless (junk classes just yielded no prototype), but the union-typed single prototype inherits those types and then fails bs2:FormControl's every-type-satisfies subset test against the edited resource - constructor controls vanished from the violation re-render. Filter the swept Descriptions with the same type list the bs2:RowForm/bs2:Form suppression templates use. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Single-source the violation machinery type list as $system-types One global drives the bs2:RowForm/bs2:Form suppression matches and the row-form violation type sweep, so the list cannot silently diverge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Drop base-uri override from the modal violation re-render ldh:render-modal-form-violation forced base-uri to the violation response's base URI, a carry-over from before the render dispatchers gained the about-fallback (both from 197ab9c). In the creation/edit flows the two values coincide, but in the app-settings flow the subject is a urn: outside the response's HTTP space, so the override flipped bs2:FormControl's $show-subject default and exposed the URI control on constraint violations while the initial render kept it hidden. The submit-handler context carries no base-uri, so the dispatchers now fall back to $ctx('about') on the violation path exactly as on the initial render. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
#359) Remaining leftovers of the 197ab9c shared-chain refactor, sibling fixes to the base-uri override drop: - property-uris harvested from everything except the edited resource (inverted predicate, contradicting the function's own comment and ldh:load-edited-resource) - property labels degraded to the local-name fallback on modal violation re-renders while metadata was fetched for spin:/sh: machinery terms instead. Flip to @Rdf:about = $about. - object-uris (modal + row-form chains) and the row-form property-uris swept the whole response body including the violation machinery Descriptions, causing junk label lookups. Exclude $system-types-typed Descriptions, mirroring the instance type harvest. - the shared violation render-ctx stamped the Container/Item required function for all flows while the app-settings initial chain passes always-true; masked today by ldh:AppSettingsForm's hardcoded required=true(). Stamp 'required' per flow in the response handlers alongside 'render-fn', and correct the stale 'only used for Container/Item instances' comment that hid this. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Self-federation: origin validators through the proxy, foreign write targets, federation suite The federation test (First Principles ch. 18): one instance's client browses, queries and writes against another, the wire carrying only spec-terms. Most of the machinery already existed - the proxy forwards arbitrary methods with bodies and delegated agent identity, panes carry data-base/data-endpoint from forwarded Link headers so ldt:base() and sd:endpoint() (and with them the ns-bound constructor SELECT and metadata fetches) already resolve against the remote app on remote panes. What was missing: - the proxy's Model branch stamped re-serialization validators, so a client editing a proxied document sent an If-Match the origin had never issued - every cross-instance write 412ed. The origin's ETag/Last-Modified now forward (replacing locally stamped values); the ResultSet branch keeps builder validators. - the Add-data flow rejected foreign targets with a guard written when the remote end refused proxied writes; with delegation the target instance's ACL is the arbiter, so the guard is gone and a remote 403 surfaces as the form error. Import-ontology keeps its local-target guard for a different reason, now stated: the constructor derivation is scoped to the local /sparql endpoint. - nothing proved any of it. New http-tests/federation/ suite runs the browse/query/write triad between the root and test dataspaces at two origins: Link-header endpoint discovery, SPARQL against the discovered endpoint, constructor SELECT against the remote ns, ETag parity between proxied and direct reads, a graph-scoped SPARQL Update PATCH under the origin's If-Match (stale precondition 412s), and the unauthenticated negative. The dataspaces share a triplestore below the HTTP surface in the test config; the meeting surface exercised is the full HTTP stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Forward conditional request headers through the proxy; fix federation negative CI caught two gaps in the cross-instance write path: - the proxy set only Accept and User-Agent on the outbound request, so If-Match never reached the origin - a proxied preconditioned write ran unconditionally (stale If-Match got 204 instead of 412, losing optimistic concurrency). Forward the conditional request headers (If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since); identity stays delegated (not via Authorization/Cookie) and Range is excluded (byte offsets don't survive Model re-serialization). - the negative test used an anonymous request, which is not actually anonymous at the origin: a proxied request with no user certificate rides the server's own credential on the proxy->origin leg. Replace it with a delegated write by the signed-up agent, which is a known federation identity not authorized on B - so B's ACL refuses it, which is the property the test means to assert. Both PATCH tests now echo the actual status codes (debug-first). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Relay proxied 4xx/5xx error responses verbatim The header-forwarding fix made the origin actually evaluate proxied write preconditions - and exposed that the proxy could not relay the resulting error responses. A 4xx/5xx from the origin carries a diagnostic body; getResponse routed it through the Model branch, where readEntity(Model.class) throws on the non-RDF/error body, and the catch turned it into 502. So a proxied stale If-Match (origin 412) and an unauthorized delegated write (origin 403) both surfaced as 502. Relay CLIENT_ERROR/SERVER_ERROR responses raw - status, Content-Type and origin validators forwarded, body piped - skipping the re-serialization branches that only make sense for negotiable success representations. Existing proxy error tests assert status only and are unaffected; the acl#agent leak checks are on direct (non-proxied) requests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Match Accept on the conditional PATCH to the read's variant The valid If-Match returned 412 because LDH ETags are variant-specific (getVariantEntityTag folds the negotiated media type into the tag): the test read the ETag with Accept: application/rdf+xml but the conditional PATCH sent none, so the origin computed the precondition tag for a different variant. The local edit flow already sends Accept: application/rdf+xml on its PATCH for this reason. Align the test's conditional PATCHes with the read. Not a proxy defect - the proxy forwards Accept and If-Match faithfully (stale correctly 412s). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Declarative packages: ldh:import in dataspace settings replaces install endpoints An application imports a package with a single <app> ldh:import <package-uri> triple - in config/dataspaces.trig (permanent, applied on restart) or live via PATCH /settings. Package components are discovered from the package's Linked Data description; the stylesheet is loaded from its source URL and composed into the application stylesheet in memory at compile time, per dataspace, with no restart and no webapp file mutation. - XsltExecutableFilter composes the app stylesheet document with package xsl:imports (SecureXML-parsed, import-set-derived cache key, fallback to the plain stylesheet on compile failure) - LocalStylesheetResolver serves app-origin /static/ xsl:import URLs from the webapp instead of HTTPS round-trips through nginx - packages/install and packages/uninstall endpoints, the admin packages/ container and ACL entries, the package Actions UI, CLI scripts and their tests removed; XSLTMasterUpdater and Package.getStylesheetPath() deleted - Package catalog is data at https://packages.linkeddatahub.com/ (bundled one-entry copy served via the proxy's mapped-URI resolution) - Application settings modal lists the packages with Installed checkboxes; Save applies the change set as one DELETE/INSERT/WHERE PATCH on /settings - ac:stylesheet values in dataspaces.trig are absolute per-origin URLs - Known limitation: package ontologies are not yet auto-imported from ldh:import (manual owl:imports for now) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Package checkboxes join the settings form as RDF/POST inputs - one Save The packages fieldset moves inside the application settings form: each row's Installed checkbox is a pu/ou input pair that serializes an ldh:import triple, so the form's single Save submits settings and package imports as one PATCH. The separate packages Save button, the change-set computation and the SPARQL-generating handlers are removed. - ldh:parse-rdf-post skips unchecked checkboxes (successful-controls rule of HTML form submission, which client-side DOM serialization bypasses) - the ldh:import hidden-input round-trip is suppressed in the settings form - the checkboxes are the sole representation, so unchecking uninstalls - the settings form response reloads the page on success: the composed stylesheet only manifests through a full server-side render Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Keep package checkboxes across the settings violation re-render The modal violation chain had no package-catalog load pair, so the re-rendered settings form lost the package table - and since the checkboxes are the sole ldh:import representation (the hidden-input round-trip is suppressed), a Save after fixing a validation error would submit no ldh:import triples and uninstall every package. ldh:settings-form-response stamps the package-catalog load pair as 'load-pairs' alongside 'render-fn'/'required', and ldh:modal-form-submit-violation joins stamped pairs onto its shared list. Every pair must bake a request, so optional fetches ride per flow rather than sitting in the shared list. The re-rendered checkboxes reflect the submitted state: PackageList's installed set comes from the echoed ldh:import triples in the violation body. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Package ontologies join the imports closure, derived from ldh:import The ontology half of declarative packages, mirroring the stylesheet composition: when OntologyFilter assembles an application's ontology closure, each imported package's ontology (from ldt:ontology in the package description) is assembled as its own owl:imports closure and added as a union member. Nothing is materialized - no owl:imports triple is written anywhere, and the raw ontology graph served by /ns stays untouched. A package ontology that fails to load is skipped so a broken package cannot take the application ontology down. Invalidation stays on the plain ontology-URI key that ClearOntology already removes and rebuilds (now package-aware): a /settings PATCH - the only way the ldh:import set changes live - evicts the assembled closure, and the next request re-derives it. The trig-file route implies a restart, which starts from empty caches anyway. getPackage moves from XsltExecutableFilter to the system Application (the filter delegates) so both composition halves share the package description resolution. New http-test asserts the skos:Concept constructor appears on /ns after the ldh:import PATCH and disappears after removal - closure derivation and eviction in one round trip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Instrument package-ontology removal path (temporary diagnostics) PATCH-settings-package-ontology.sh fails only on removal: the package ontology's skos:Concept constructor stays in the closure after the ldh:import is deleted (adding works). Eviction fires and updateApp persists to the live context dataset, so static reading can't explain it. Log what getPackageOntologies actually sees after the DELETE, and have the test report whether /settings still carries the ldh:import triple - one CI run then tells us persist-vs-cache. To be reverted once the cause is fixed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Settings PATCH evicts the ontology from the repository too, not just the closure Root cause of PATCH-settings-package-ontology.sh failing only on removal, confirmed by the CI diagnostics: after the DELETE, /settings carries 0 ldh:import triples and getPackageOntologies returns [] - the derivation input is correct - yet the skos:Concept constructor stays on /ns. The OWL2 imports build materializes the package's terms into the cached base graph in the OntologyRepository; Settings evicted only the closure union (getOntologyGraphs), leaving that polluted graph cached, so the remove-phase rebuild read skos straight back. Adding worked because the first build ran over a clean cache. Clear both caches, matching ClearOntology (repository graph + fragment- stripped document alias + closure union), minus the proxy purge since settings responses are not cached. Revert the temporary getPackageOntologies logging. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Settings delegates ontology invalidation to ClearOntology via ResourceContext Replaces the inline in-memory eviction with a call-through to the shared ClearOntology logic (the pattern the old InstallPackage used): getResourceContext().getResource(ClearOntology.class).post(uri, null). This gets the full invalidation - repository graph + closure union + varnish BAN/XKEY-PURGE + eager reload - so a settings-driven package add/remove no longer leaves stale /ns SPARQL responses in the proxy caches (the in-memory-only eviction fixed the closure but not varnish; the http-test masked it with a manual purge). ClearOntology becomes context-agnostic: it resolved the end-user app by assuming the current app is admin (getApplication().as(AdminApplication)), which threw for the end-user-scoped /settings. It now resolves both apps from whichever the request matched and derives the admin backend from the resolved admin app rather than the current one, so both backends are purged either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Import ontology persists annotations only - vocabulary via scratch document (#362) The flow's output becomes the artifact packages ship statically: an annotation ontology holding the derived class constructors plus owl:imports of the canonical vocabulary URI. The fetched vocabulary is scaffolding: PUT into a scratch document (UUID-slugged, under the app root) that scopes the construct-constructors CONSTRUCT, then deleted - on the error paths too (client: error wrapper armed once the scratch exists; CLI: EXIT trap). The vocabulary resolves live through the graph repository (bundled mapping or HTTP), identically to package ontologies. This also fixes constructor derivation for bundled vocabularies: previously the shipped file shadowed the local copy that held the constructors, so they never reached the closure (the old test's TO-DO documented it); now the annotation document enters the closure under its own URI and imports the vocabulary. Wiring changes accordingly: add-ontology-import.sh --import <annotation-document>, not the source. ontologyQuery in web.xml is untouched - the describing-document branch keeps resolving own-origin ontologies; this flow simply stops minting shadowing copies. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The bs2:Row GraphMode branch applied bs2:Graph without the required canvas-id param, crashing the block render with a cardinality error. Pass it, and initialize the 3D force graph after the row is rendered by reusing ldh:InitDocumentGraph3D, fed the whole loaded document to match the bs2:Graph rendering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.