Skip to content

Develop - #60

Open
namedgraph wants to merge 101 commits into
masterfrom
develop
Open

namedgraph wants to merge 101 commits into
masterfrom
develop

Conversation

@namedgraph

Copy link
Copy Markdown
Member

No description provided.

namedgraph and others added 30 commits August 28, 2026 17:22
- Line items table on order pages via forward ldh:view (detail gallery)
- Order status (OrderDelivered/OrderProcessing/OrderProblem) derived
  from shipped vs required date; shipped date and freight re-emitted
- Line item extended prices (schema:totalPrice) computed from the
  existing quantity/unitPrice/discount columns
- Orders gallery first on the container page, newest-first, with
  customer/broker/date/status columns; narrative blocks rewritten;
  buggy sales-by-region chart (duplicate ldh:seriesVarName) removed
- Inverse views upgraded to TableMode with richer columns; new
  "Orders shipped by this shipper" panel; showWhenEmpty false
- KPI row (orders/revenue/avg order value) on the root dashboard
- Fix unbound ?deliveryLocation triple in orders.rq

The source CSVs stay stock Northwind - no orderTotal or productName
columns are added. An order total is the sum of its line items and a
product name lives in products.csv, so neither belongs in the source
data; the row-at-a-time importer cannot aggregate or join, but that is
a constraint to design around rather than to denormalise away.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C17WzuUXBuyAxZ3nQASCNg
Page structure and copy:
- Drop the frontpage block advertising LinkedDataHub's features and the
  low-code build story; an app does not explain its own toolchain
- Lead the dashboard with the monthly sales trend instead of a two-value
  table stretched over the 400px chart canvas
- Record lists come before analytics in each container, so a section opens
  on its data; narrative blocks run between sections throughout
- Give the four reference containers (suppliers, shippers, regions,
  territories) the intros the others already had
- Drop the revenue-by-category chart duplicated in products.ttl; it belongs
  to categories.ttl

Labelling:
- dct:title on every ldh:Object block. Without it the block heading renders
  the fragment identifier, so pages showed "kpi-block", "select-suppliers"
- Northwind's own vocabulary in the ontology: Sales rep (not Broker),
  Reports to (not Sponsor), Territory, Region, Company name, Required by
- Use .ldh-section for section headers; page-header/lead are Bootstrap 2
  leftovers with no rules in the current design system

Correctness:
- Revenue charts are now net of schema:discount. They summed quantity x price
  and overstated by 6.7% (1,297,141 gross against 1,215,813 net), so no chart
  reconciled with the order totals
- No hardcoded counts in prose. "all 830 orders" was also wrong: 19 rows are
  dropped at import because shipPostalCode is empty and the mapping requires it
- Exclude the final, incomplete month from the trend. The data stops on
  1998-05-06, which rendered as a collapse rather than a truncated month; the
  cutoff is derived from MAX(?orderDate), not pinned to a date
- Order-scoped schema:totalPrice is no longer required by any query, following
  its removal from the import mapping

Charts:
- Add revenue by carrier to Orders, the first chart here with multiple series.
  Each ldh:seriesVarName names its own measure column, so the query pivots with
  conditional aggregation; the removed sales-by-region chart passed a label
  column plus a value column, which is why it never rendered

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C17WzuUXBuyAxZ3nQASCNg
The checker maps every .ttl to <path minus .ttl>/, which is right for
containers but wrong for root.ttl: install.sh PUTs it at $base itself, not
at <dir>/root/. So a relative link in a root.ttl resolved one level too
deep, and demo/northwind-traders/root.ttl's href="orders/" was reported as
demo/northwind-traders/root/orders.ttl.

No root.ttl carried a relative link until now, which is why this went
unnoticed. The link is correct at runtime; the checker's base was not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C17WzuUXBuyAxZ3nQASCNg
Several schema.org properties were carrying values outside their declared
domains. Each is moved to the term the vocabulary actually provides:

- Order lines are now schema:OrderItem *and* schema:Offer. schema:OrderItem
  has no price properties at all, so schema:price/schema:priceCurrency were
  off-domain; typing the line as the offer that was accepted for it makes
  them legal without splitting the line across two nodes.
- schema:Offer's own schema:price is the price of the offer as a whole, so it
  replaces schema:totalPrice (a Reservation/Ticket property) as the line
  total. The historical unit price moves to a schema:UnitPriceSpecification
  with schema:priceType schema:ListPrice.
- schema:discount is dropped. It is an Order-level property in schema.org,
  and the list price, quantity and line total already pin the per-line rate
  exactly - unlike a stored rate, they cannot disagree with each other.
- Product prices move from schema:Product to a schema:Offer reached through
  schema:offers. A product is not an offer, so this one needs its own node.
- Freight moves off schema:ParcelDelivery, which has no price property, onto
  a schema:DeliveryChargeSpecification reached via schema:priceSpecification
  from a shipping schema:Offer.
- The dispatch date moves off schema:availableFrom (a DeliveryEvent property
  borrowed onto a ParcelDelivery, and meaning "available for pickup" rather
  than "dispatched") onto a real schema:DeliveryEvent hanging off
  schema:deliveryStatus. Orders never dispatched get no event at all.
- schema:orderedItem loses its rdfs:domain/rdfs:range. schema.org allows
  Order|OrderItem -> OrderItem|Product|Service, which a single domain/range
  pair cannot express; the narrowed pair entailed every OrderItem is a
  Product.

Also fixes a pre-existing import bug: shipPostalCode sat in the required BGP
but 19 orders have none, so those orders never entered the graph at all.
Making it OPTIONAL restores them, taking the import from 811 to 830 orders
and revenue from 1,215,812 to 1,265,792 - the canonical Northwind figure.

Consuming queries follow the new structure. The six revenue queries drop
their ?quantity * ?price * (1 - ?discount) recomputation for the materialised
line total, which also settles a 0.1 discrepancy between the carrier chart
and the rest. SelectProductsFromSupplier traverses schema:offers/schema:price.

Verified by running all eleven import queries over the real CSVs and all 28
view and chart queries against the resulting graph: 830 orders, 2155 lines,
freight totalling 64,943, and top product/employee/customer/category all
matching canonical Northwind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYifJzEkGsTcJsuB4fGRAc
…and orders pages

The schema.org remodelling added facts the content blocks were not using:
per-order status, a materialised line total, the catalogue price each line
was struck against, and a DeliveryEvent carrying the dispatch date. Four new
charts read them, each with a narrative block ahead of it.

Front page gains a discounting section. Revenue is what was billed; the
ListPrice specification on each line is what was listed, so the gap is what
was given away to win the deal. As a share of gross it runs 3.6%-11.2% and is
decorrelated from volume, which a revenue chart cannot show.

Orders page gains a fulfilment section - average days from order to dispatch,
and the share of orders that beat the date the customer was promised - plus a
late-shipments-by-carrier bar alongside the existing revenue-by-carrier line,
so carriers are compared on reliability as well as volume.

"Orders by country" is dropped to make room. The front page already tells the
geographic story in revenue rather than order count, which is the more useful
of the two measures.

Both rate charts are normalised on purpose. Raw monthly status counts squash
the late series (0-4 a month) against a delivered series climbing 21 to 69,
and the 21 still-open orders are a dataset-cutoff artifact confined to the
last two months - they are filtered out of the on-time and late rates, since
not yet dispatched is not the same as delivered late.

Also corrects the orders intro, which still promised unit prices in the
line-items table; those moved to a nested UnitPriceSpecification.

Verified against the full graph built from the real CSVs: 31 view and chart
queries, none failing to parse, none returning no rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYifJzEkGsTcJsuB4fGRAc
customers.rq required <#postalCode> in its main BGP, but HUNGO — Hungry Owl
All-Night Grocers, Cork — has none. Ireland had no national postcode system
until Eircode in 2015, so a 1996-98 Irish address legitimately has no postal
code; this is correct data, not dirty data.

The row processor emits no triple for an empty cell, so the BGP failed to
match and the entire customer was silently discarded - company, contact,
address, phone and coordinates alike. Its 19 orders kept their
schema:customer link, which then resolved to nothing.

Moving the column to an OPTIONAL, alongside region, fax, lat and long in that
same query, restores the customer and takes dangling customer references from
19 to 0. HUNGO imports with its city and no postal code, rather than a
fabricated placeholder.

Same failure mode as the 19 orders dropped on an empty shipPostalCode. An
audit of all eleven mappings against their CSVs confirms these were the only
two required-but-sometimes-empty columns.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYifJzEkGsTcJsuB4fGRAc
- command-line-interface: rewritten for the ldh executable — build and
  PATH setup, PKCS12 keystore auth, LDH_* environment defaults, stdout
  and exit code conventions, the command table (#scripts is now
  #commands) and a deprecation note for the bin/ HTTP API scripts
- every page that invoked a script now invokes a command: upload-file,
  create-documents, create-content (--fragment is --uri), import-csv/rdf,
  change-model (create-construct.sh etc. never existed — they are
  ldh admin ontologies add-constructor/add-class/add-property-constraint)
  and the data-model CLI tables
- manage-packages / administration/packages: packages are installed by
  declaring one ldh:import triple, composed at request time; drop the
  Actions dropdown, the packages/install endpoints and the docker-compose
  restart step
- http-api: drop the removed package endpoints, document the settings
  endpoint, and correct the container hierarchy predicate to
  sioc:has_parent
- admin app URLs use the admin. subdomain, not an /admin path; examples
  use localhost:4443 and a single example agent URI throughout
- get-an-account: the CLI reads the .p12 keystore directly, PEM is only
  needed for curl

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
The multi-stage Docker example built the WAR overlay on maven:3.8.4-openjdk-17,
which cannot compile against a Java 21 platform - and the same page already told
you to target Java 21 ten lines further down. The build stage now matches the
platform's own Dockerfile.

The pinned versions were stale in the same way: the base image at 5.3.2 and the
overlay dependency at 5.5.3, both now 5.9.1, the current release.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Tgf5DjbYiX3SPnCFSgCzd
Present Northwind as a business application, and fix its schema.org modelling
Merge the duplicated pages into their parents (content blocks, documents,
resources, browse data), collapse the static ToC to the active trail, and
fix every broken anchor.

Add the versioning reference and the version-history guide; document the
Memento options of ldh get, federation through the Linked Data proxy, and
UI language negotiation. Sync the reskinned UI descriptions, the ORCID
login, the fuseki/ data directory and the make up/drop targets.

Import the tutorial from the docs-tutorial branch and migrate all of its
examples from the deprecated bin/ scripts to the ldh CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WK1ssk2A67MYseeH3hWnhf
The generated pages link the vendored design-system stylesheets (fonts,
tokens, components, m3 skin, ldh-bridge) plus a docs-specific skin, and
drop Bootstrap 2 and jQuery. The shell mirrors the app's: the sticky
ldh-header carries the landing page's navbar content (version picker,
sample applications, GitHub and social buttons), and the ldh-footer
matches the app's footer.

ttl-to-html.sh copies the CSS and fonts from a local LinkedDataHub
checkout (LDH_SRC) or fetches them from GitHub (LDH_REF, default
develop - flip to master once 5.11.0 ships). The UI/CLI tab strips run
on vanilla JS; the rsync excludes now keep *.xsl and *.css out of the
published files/ tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WK1ssk2A67MYseeH3hWnhf
docs/ and packages/ sit one level deep in the repository, but their
Makefiles defaulted the owner certificate three levels up - pointing
outside the sibling LinkedDataHub checkout, so accepting the make
install defaults failed on realpath. The demo apps sit two levels deep
and their three-level default is correct.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WK1ssk2A67MYseeH3hWnhf
One dataset runs through the whole documentation now: the examples that
used SKOS concepts, countries CSV, Places, Friends and DBpedia all draw
on the Northwind Traders demo instead, sourced from the demo app where
an exhibit exists and linked to its file on GitHub and the live
resource on northwind-traders.demo.linkeddatahub.com.

The tutorial conforms to the demo: the revenue query sums the line
totals the order-details mapping already computed (the old one
multiplied quantity in twice), the composition sequence matches
root.ttl, employees link portraits with foaf:img, and the dangling
query and block references are defined.

The demo grows the two pieces the docs lean on: a schema:Product
constructor and missing-name constraint in ns.ttl, and
categories/unesco-mappings.ttl mapping the eight categories to UNESCO
Thesaurus concepts - the RDF import example and the seed of the
cross-dataspace federation thread.

The exhibit-starved pages get their first exhibits: an
acl:Authorization and access request, the PATCH accepted/rejected pair,
the PROV-O TimeMap with its link-format and Memento exchanges, the
northwind-traders dataspace quoted from config/*.trig, and the
stylesheets reference gains the full global parameter table, the
author-facing function tables, and an input/output template pair. Every
runnable SPARQL exhibit was executed against the live demo endpoints;
screenshots are placeholder comments pending capture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WK1ssk2A67MYseeH3hWnhf
The screenshot comments and the remaining Bootstrap-era media - six
YouTube embeds, four webm recordings and eleven screenshots - become
visible placeholder blocks: a dashed box with an image or play_circle
glyph and a caption typed "Screenshot:" or "Screen recording:" that
describes what the capture should show. The placeholder blocks are the
shot list, in place; the orphaned media files are removed.

Also corrects the Condiments pictureHash in the tutorial's CSV excerpt
to the file's real content hash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WK1ssk2A67MYseeH3hWnhf
The Resources reference and the Query data guide each carry an actual
query block - an ldh:Object over an sp:Select bound through ldh:service
to an sd:Service pointing at the Northwind Traders demo's SPARQL
endpoint. Rendered in a LinkedDataHub instance, the block hydrates with
the service picker and editor, and Run executes against the remote
dataspace; the documentation demonstrates the platform inside itself.

The static site renders such blocks as an "Interactive block" stub in
the placeholder style instead of dropping them silently (the children
view stays suppressed - containers list their children separately).
These two pages' media placeholders from the preceding sweep ride
along.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WK1ssk2A67MYseeH3hWnhf
…ioms, a fuller function reference

Tab groups move from the dead Bootstrap tabbable markup to the design
system's ldhc-tabs component with ARIA roles and hidden-gated panels, on
all eight pages that use them; the static site's tab script follows. The
customization guides drop retired forms - $ac:uri patterns become
ac:absolute-path(ldh:base-uri(.)) - and the stylesheets reference gains
ac:uri() and eight more public functions (label helpers, sort companions,
ldh:link-targets, ldh:hash-code, value-set utilities) grouped into a new
Utility table, with the closing note honestly covering everything left
internal. Editing docs describe the dirty-gated autosave (no edits, no
save, no version) and the tab-group limitation: panels edit in place,
stacked while editing, but creating a group still takes the HTTP API.
Guides move from user-guide/ to extending/, and prose gets a wording pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1UnYpbW5aYLj7LbwtyUo1
Seventeen docs pages that presented mutually exclusive paths as sequential
headings now use the design system's tab groups: the reference Management
sections (UI/CLI pairs in data-model and versioning), the user-guide pages
whose CLI content trailed as a final heading (add-data's three sources,
version-history, create-resources), the triplestore authentication methods
(HTTP Basic / Bearer token), the authorization example in RDF and CLI form,
and the eight tutorial stages' in-the-browser/command-line pairs (insight's
CLI path is its chart+view walkthrough, kept whole as one panel).

The copenhagen and unesco-thesaurus demo apps shed their dead Bootstrap
markup (page-header/lead, and unesco's collapsed row-fluid/span grid) for
the ldh-section idiom, and gain tabs where their content branches: the
copenhagen feature run-on becomes a five-tab tour with links into the app,
unesco's usage-path dl becomes Browser/Editor/RDF Import (the import panel
now names skos-import.rq), and the concepts intro's relationship list
becomes Hierarchical/Associative/Matches with the live Northwind category
mappings as the matches example.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1UnYpbW5aYLj7LbwtyUo1
The tutorial's ten stages, setup's make targets, the Data stage's four
BIND variables and the annotation dialog's fields were all parallel
term-dash-description list items; each is now a striped table in the
reference pages' idiom (the dialog's advanced fields keep their own
table under the expander sentence, replacing a double-nested list).
See-also lists and prose-heavy routes stay lists - they enumerate
reading paths, not facts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1UnYpbW5aYLj7LbwtyUo1
The Resources reference contrasted two SELECT queries by padding them
into a single pre with spaces, so the pair read across the page instead
of down it and neither query could be copied on its own. They are now
two pre elements, each introduced by the sentence that says what it
does; the "# renders category documents" / "# renders nothing" comments
go, since the prose already carried them and their absence leaves the
queries paste-ready.

A sweep of the corpus for pre blocks with a second column of content
turned up no other side-by-side pair - the remaining multi-column
blocks are directory trees, aligned PREFIX declarations and end-of-line
comments, all single examples.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YxhcqMPK1kZR1pxxJqoge
Twenty-eight rdf:XMLLiteral lexical forms across the docs were not
exclusive-canonical XML. Forty-four screenshot-placeholder spans wrote
class before aria-hidden, and the stylesheets reference's wrapped
function signatures used self-closing <br/>; canonical form wants
attributes in alphabetical order and explicit end tags.

RDF 1.1 makes canonical form the datatype's lexical space, and Jena
4.7.0 - which the Check Docs syntax check pins - enforces it while
validating. The workflow runs under bash -e, so it aborted on the first
offending file and reported only about.ttl. Jena 5 and later dropped
the canonicalisation check and validate well-formedness alone, which is
why make validate stayed green locally the whole time.

Each literal is now its own xmllint --exc-c14n serialisation, so the
change is confined to attribute order and empty-element tags; no
whitespace, text or namespace moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YxhcqMPK1kZR1pxxJqoge
check-xmlliterals.sh extracts every """..."""^^rdf:XMLLiteral lexical
form, canonicalises it with xmllint --exc-c14n and diffs the result,
reporting file:line and the offending lines. Run against the tree as it
stood before the preceding commit, it returns the same verdict as Jena
4.7.0 on all twenty-eight offenders, with no false positives - so the
rule survives independently of which Jena is installed.

validate-documents.sh calls it after the riot pass, giving make validate
a check that can actually fail. Check Docs runs it as its own step,
which lets the syntax check move from the action's implicit 4.7.0
default to Jena 6.1.0 without losing the rule; Jena 6 needs Java 21 and
the runner defaults to 17, hence the setup-java step.

The check assumes each literal is a single-rooted fragment, which all
ninety-five are, and compares the Turtle-escaped text - sound here
because the corpus's only escape is \\, which canonicalisation leaves
untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YxhcqMPK1kZR1pxxJqoge
The matches tab pointed at concept3683/, concept1812/ and concept3495/
relative to the concepts container. Those documents are real once the
app is installed - skos-import.rq constructs them from the thesaurus
RDF - but they are never files in the repository, so check-links.sh,
which resolves relative links to checked-in .ttl files, could only
report them broken.

The three now address the public demo instance, in the same form the
Northwind mappings and the reference docs already use, and carry
target="_blank" like the app's other absolute links.

This surfaced only now: the RDF syntax check used to abort the Check
Docs job before the link step ever ran.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YxhcqMPK1kZR1pxxJqoge
Faceted search and parallax are rewritten around the view's control
header: facet pills in a toolbar at the top of the view, loading their
values with result counts on first open, and a chip row below it whose
pivots are discovered by querying the whole result set in both
directions - outgoing properties of the results and properties pointing
at them. Applied steps read via/linked by next to the facets, and
removing one rewinds the view to that step.

The action bar section describes the three zones: Create and Add on the
left, breadcrumb pills with document-type icons and the last-modified
timestamp in the middle, and the Actions, layout-mode and export
dropdowns on the right. The settings menu moves to the navigation bar
section, where the button now lives. The guides that pointed at retired
placements follow: Edit/Delete/Save as go through the Actions dropdown,
File/Query/Import creation goes through the creation bar at the bottom
of the page (the action bar's Create only offers Container and Item),
and the editor toolbar mounts below the action bar.

The right sidebar section becomes Backlinks: every block whose resource
has a URI carries its own links popover, loaded on first open - in
Properties mode that is one per resource in the document. The block
data model sheds the left/main/right column convention for the card
description, and the left sidebar is documented as the flyout it is,
with the stale narrow-screen claim dropped.

Graph-scoped updates state the PATCH contract: a single update
operation, in the DELETE/INSERT-with-WHERE form or the DELETE WHERE
shorthand, everything else answering 422 - matching
DocumentHierarchyGraphStoreImpl. The hand-written "On this page" lists
in the HTTP API and stylesheets references are dropped along the way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WK1ssk2A67MYseeH3hWnhf
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1UnYpbW5aYLj7LbwtyUo1
$ldt:ontology, $ac:forClass, $ac:query and $ac:googleMapsKey survive in
client.xsl solely as Web-Client compatibility leftovers, and $ac:method
and $ac:httpHeaders are server-side layout internals - none of them is
part of the contract a custom stylesheet writes against. The bs2:Form
entry drops its $ac:forClass condition along with them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011j6GapYTmxZXEq3YK2Z53D
The dataset holds the current version of every document and nothing
else, which the versioning reference now states next to the read-only
notice rather than leaving readers to infer it from the Git-backed
storage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011j6GapYTmxZXEq3YK2Z53D
namedgraph and others added 30 commits September 18, 2026 00:22
… property or datatype wins.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ts ldh push, --primary-topic, the FILE argument of put and post and the release archive; the tutorial's app-as-a-repository stage is written around ldh push and the .ldhignore rules the CLI applies; the resource example creates its document with --primary-topic instead of a second post; the HTTP API lists sitemap.xml and robots.txt per dataspace; the dataspace reference stops requiring the end-user service to federate with the admin service, which nothing does any more and the egress proxy would refuse; the stylesheets reference drops $ldt:base and renames ldt:base() to lapp:base(); the configuration reference gains the sef-compiler service; and the packages reference describes the open-mode contract a package stylesheet is composed into, dropping an ac:Stylesheets override a package can no longer make.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lic, the namespace ontology install and ldh push, then the imports: import-csv.sh runs one ldh import csv per manifest row, the UNESCO app runs ldh import rdf into the document the push created, ldh packages add and ldh admin create authorization; import-ns.sh is ldh patch, ldh post and ldh admin clear ontology. The update-folder.sh copies go, and with them the root.ttl entry every .ldhignore carried for a walker that could not place the root document - under ldh push a listed root.ttl is skipped and the root never written. The ignore files list *.sh instead, since the CLI excludes nothing but hidden entries, the docs list the non-RDF .xml files the CLI would parse as RDF/XML, and the CSV demos list *.csv because ldh import csv uploads the file itself. Single-command installs are Makefile targets rather than scripts: the packages Makefile exports the LDH_* variables and runs ldh push on its own folder. Certificates are the PKCS12 keystore the CLI reads, not the PEM the scripts fed to curl.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…et the package SEF is composed from, the platform's unless a deployment bootstraps its own, in which case it must import the platform's client.xsl by an href that resolves from its webapp path, and its layout must prefer the advertised SEF over its own or the site's rules vanish from a package dataspace on the first client-side navigation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ors declare rdf:langString.

Syncs the copy with the platform's bundled one. Two changes ride together: the six hierarchy and
membership views gain ldh:showWhenEmpty false beside :OrphanConcepts, which already had it, so a
leaf or root concept no longer renders empty Narrower and Broader blocks; and every
natural-language value the constructors construct - labels, definitions, title, description - is
declared rdf:langString rather than xsd:string, so the form renders a language control and a value
added through it lands tagged beside the tagged ones already in the data.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An imported package's ontology is now the application's own document under admin/ontologies/, and
the docs said nothing about it: the packages reference described the ontology joining the closure
from the package and the stylesheet composed with no file system mutation, the ontologies reference
described Import ontology persisting an annotation ontology typed owl:Ontology, and the HTTP API
said /clear needs an ontology to clear.

The references now describe materialization, the once-only stylesheet copy served from the
application's origin, store-first resolution, and the import-ontology document shape with
foaf:primaryTopic. The manage-packages guide gains a section on editing the package ontology and the
delete-and-clear recipe for picking up a newer package, since the copy is taken once. The clear
endpoint and its CLI row say the URI is optional.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A write to a document that already exists is now refused unless it says which state it was written against, so the HTTP API reference needs to say what to send and why. The why is the part worth writing down: every write reads the document's graph, changes it in memory and writes the whole graph back, so two writers naming no precondition overwrite each other with nothing to show that anything was lost.

A new Conditional writes section covers the two ways to satisfy the rule - If-Match with the current tag, or If-None-Match: * to create - that creating a document needs neither, and that a 412 hands back the current tag so a client can retry without a second read. It also warns about the trap that costs the most time: an entity tag identifies a negotiated representation, so a tag read as Turtle will not satisfy a write negotiating RDF/XML.

The methods table keeps its shape rather than growing four near-identical rows; its existing "any method may answer 403" note gains 428 and 412, which apply to every write method alike. The caching section drops the claim that tags are hashes of content - they are digests of the document URL and its content - and says to treat the value as opaque, since nothing about the document can be read from it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ontology import section describes the form's effect - the vocabulary kept in the target document, the derived constructors beside it, foaf:primaryTopic naming it - without showing the form the reader is being told to fill in. A placeholder marks the spot, naming the two fields that matter: the vocabulary URI as Source and the target document as Graph.

Same screenshot-placeholder convention the user guide and tutorial already use.

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

LinkedDataHub 6.0 moves dh: off www.w3.org, retires the Linked Data Templates namespace
outright, and renames the application ontology to the dataspace one - lapp: becomes lds:,
lapp:Application becomes lds:Dataspace, ldt:ontology becomes lds:ontology. This repository
is data the platform reads, so the rename reaches it the same way: a dh:Container typed in
the old namespace is not a container any more, and an authorization granting access to
ldt/document-hierarchy#Item grants access to nothing.

Mechanical throughout. The demos and the packages change prefix declarations and the two
places a namespace is written out in full - the UNESCO ACL script's --to-all-in class and
the taxonomy package's lds:Package descriptor. The docs change the same declarations plus
every place a namespace appears as documentation of itself: the prefix table in the
stylesheets reference loses its ldt: row, the ontologies reference points its links at the
new URIs, and the package descriptor examples are rewritten against lds:.

ttl-to-html.xsl matches dh:Item and dh:Container by full URI in eight patterns and sorts,
so the static docs build stops finding any document at all until it moves with them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d not.

The docs manifest carried these as blocked, each with a reason that had stopped being
true or had never been the real one. Taking them needed three fixes in the rig itself.

The sign-up form is served by the dataspace's admin application, and every shot was taken
against the end-user origin, so the URL answered 404. shoot.mjs derives the admin origin
from the base the same way the platform does - an admin. label on the same hostname - and
a shot asks for it with admin: true, so the manifest names no hostnames.

A view's mode toggle lives in .ldh-view-toolbar, which is display: none until the block
head's tune button opens it. The toggle is in the DOM the whole time, so switching modes
waited the full 30s on a control that could never be clicked - that is what timed out a
layout-modes clip. modes.showControls presses tb-controls first and is safe to call when
the toolbar is already open or the block has no such button.

The Create menu was recorded as hiding every constructor but Container and Item. It does
not: there are two controls, and both toggles read "Create". The action bar's offers
Container and Item; the create dock at the foot of the document offers the platform's own
creatable types - Instance, File, the import and query types, View, the charts, thirteen
entries measured on a document in Properties mode. A locator taking the first match in the
document gets the action bar's and reports the other one's entries missing.
constructors.createFromDock scopes to .ldh-create-dock, and matches entry text at its end
because each runs a Material icon ligature straight into its word: "categoryInstance".

The same reading corrects what the dock is: a fixed parameter of the platform, not the
ontology's class list. A domain class such as schema:Product is never an entry - an
instance of one is made by creating an Instance and setting its Type.

fill.mjs compares a caption as text, because a caption may carry inline markup - the
ontologies reference names its controls in <samp> - while the manifest's caption is the
plain sentence that becomes @alt. Captions without markup compare exactly as before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s not there.

The placeholder convention marks a slot and names what belongs in it; every one of these
had been waiting for the shot to become takeable. The rig can take them now, so the divs
become the images and clips they described, and the captions they carried survive as @alt
and @aria-label - the text a reader who cannot see the image gets, and the key fill.mjs
finds the slot by when a shot is retaken.

Each element points at uploads/<sha1 of the published file>, the content-addressed name
the docs dataspace serves it under, so the media ship beside the documents: seventeen new
files and twenty-four re-shot against the design-system UI, which the older captures
predate.

One correction rides along, because taking the shot is what found it. The change-model
guide said Product appears in the Create dropdown once its class is declared. It does not:
the dropdown offers the platform's own creatable types, and a domain class is reached by
creating an Instance and setting its Type - which is the route the form and its constraint
are demonstrated through, and now what the guide describes.

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

The manage-dataspaces guide said the end-user/admin pairing is inferred and you need not
declare lds:endUserApplication or lds:adminApplication by hand. The advice is right and the
properties are gone: they were lapp:endUserApplication and lapp:adminApplication until
inference replaced them, and LDS declares neither under the new prefix nor any successor.
Retiring the lapp: namespace then gave two dead names the live prefix, which is worse than
leaving them stale - a reader looking them up in the dataspace ontology finds nothing and
cannot tell whether the ontology or the guide is wrong.

The claim survives without them, because it never depended on them. An admin. origin is
recognized as an administration application and paired with the end-user application on the
bare host - AdminDataspaceImpl strips the label to derive one, EndUserDataspaceImpl prepends
it to derive the other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…o it.

Four pages name Apache Jena Fuseki as the backend LinkedDataHub ships with and leave it as
plain text, while about.ttl and the dataset reference have linked it all along. The worst of
them is the manage-dataspaces guide, which links QLever and Tentris in the same sentence that
names Fuseki - the alternatives are one click away and the default is not.

Each page's first mention now carries the link, the convention the two pages that already had
it follow. Later mentions in the same page stay plain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The build-apps guide pins the release a reader copies: the Docker base image tag and two Maven
coordinates, all reading 5.10.0, plus the sentence telling them to substitute their own. Those
are the current release and move to 6.0.0.

The HTTP API reference said "Unlike earlier versions, LinkedDataHub 5.x manages the document
hierarchy automatically" - a closed range that excludes the version it now ships with, and that
would need editing again at every major. It becomes "Since LinkedDataHub 5", the form the
dataspace reference and the packages pages already use, which asserts the same floor the old
sentence did and stops going stale. "Unlike earlier versions" goes with it, since "Since"
already says that.

Untouched: the four statements that are history rather than the current release - packages
introduced in 5.2 and declarative since 5.10, the admin subdomain since 5.1.0, and Linked Data
Templates removed in 3.0.3. Bumping those would make them false.

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

An uploaded file is content-addressed in the uploads/ namespace, a flat namespace anchored at
the dataspace base URI and outside the document hierarchy - the dataset reference says so:
it "lives at uploads/<sha1> no matter which document references it". Its depth from the root
is fixed at one. So in ../../../uploads/<sha1> every ../ describes the REFERRING document,
and the reference carries nothing about the file it names. Move a document one level and all
of its images 404, having said nothing wrong about the file.

The 43 references on 35 img and 8 video elements become /uploads/<sha1>, one string
regardless of where the document is written or later moved. fill.mjs stops counting path
segments and emits it directly.

That absolute form cannot survive into the static build, which is published four path
segments deep on GitHub Pages, so ttl-to-html.xsl relativizes rather than reusing the prefix
it used to inherit from the source: local:relativize('/files/' || name, $base-path), the same
helper every generated nav link and child card already uses, with $base-path arriving on the
tunnel the document templates already set. The output is unchanged - 43 references, the same
../../ and ../../../ depths as before, all resolving.

The rewrite is anchored on src=" followed by ../, so the five places uploads/ appears as prose
or as an absolute URI are untouched: the namespace description itself, the uploaded-file URL
in the upload guide, and three ldh:file / foaf:depiction values in Turtle examples.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…elow the fold.

The "Change the layout" tutorial tells the reader that the overriding template puts a
computed total under the order's description, and pointed at a screenshot of an order page
with no total on it. The page had one; the shot ended before it. The document's own
metadata - creator, dates, container, primary topic - renders above the order, and a
viewport-framed capture spends its height on that and stops.

So the shot is framed on its subject rather than the viewport: the union of the order's
description block and the paragraph carrying the total, with the heading between them
falling inside the box. That is the sentence the caption makes, and nothing else.

Re-taken with the tutorial's stylesheet deployed in its AUGMENT-ONLY state - the suppression
half disabled - because the three shots in this sequence are three deployment states and
shot 3 is the one where the line items are gone. With both halves live this shot would have
been shot 3 again, which is what the runner's duplicate check exists to catch.

The want predicate that would have caught the original was already here; it postdates the
image it would have rejected. It passes now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s only filmed browsers.

Everything docs/shoot.mjs records is a page, so the clip at the top of
get-started/setup — the whole Docker procedure, clone to running container —
was the one slot it could not take. A `terminal` shot kind now branches before
any browser context exists: lib/terminal.mjs drives VHS over a committed tape,
render/pace.mjs produces the master at the same clip encoding as a browser clip,
and the record written is the same shape, so fill.mjs needed no change.

The tape runs the page's steps for real against a throwaway clone. It keeps the
page's default ports, and that is load-bearing rather than incidental:
config/dataspaces.trig hardcodes https://localhost:4443 and
https://admin.localhost:4443, so an instance brought up on any other port exits
at startup with

    Root end-user app with origin <https://localhost:PORT> not found. Exiting...

The page's advice to pick different ports when 81/4443 are taken does not work
today. The tape says so at its head, and diverges only in
COMPOSE_PROJECT_NAME, which has to differ or Compose adopts the stack already
on the machine.

The shot's readiness assertion is that the instance the tape built answers, and
it runs while the recording does, because the tape ends with the stack still
attached to the shell VHS is about to take away. It used fetch(), which has no
per-request way to accept the certificate server-cert-gen.sh had just made, and
so reported every healthy instance as one that never answered; it now goes
through node:https.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
a68997a43 removed it from the platform: every application's base URI is
origin-rooted, lds:origin has nowhere to put a path, the webapp deploys as the
ROOT context and the nginx locations are literal, so the variable had exactly
one working value. The setup page's .env block still listed it, and the
configuration page documented it as a field with a choice in it -- with an AWS
example whose URI carried a /linkeddatahub/ sub-path that was never reachable.
Both now say what the platform README says: a dataspace serves from the root of
its origin, so the base URI is the origin followed by /, and several dataspaces
on one instance is a matter of subdomains.

The clip had to be shot again, because it films that block being typed. It
clones --branch develop for as long as master is 5.10.0: that version passes
ABS_PATH=${ABS_PATH} through docker-compose.yml, which resolves to the empty
string and overrides the Dockerfile's ENV ABS_PATH=/, so its entrypoint bails
on [ -z "$ABS_PATH" ] and the recording would film an instance that could not
come up. The page's step 1 is prose -- fork the repository and clone the fork --
so it prints no command the flag contradicts, and the flag goes when 6.0 does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
develop is 6.0, so the docs it publishes are v6 and the v5 pages become an
archived version like v3 and v2 before them. The summary names the version the
reader is on and the list names the ones they can leave for, so v5 moves into
the list and v6 takes the summary.

The link points at linkeddatahub/docs/v5/, which is where a snapshot built from
master's docs has to be published on the gh-pages branch of AtomGraph/LinkedDataHub
-- publish-docs.yml keeps existing files, so an archived version can sit beside
the current one, but nothing creates one. Until that snapshot is pushed the link
is a 404.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The platform's ontology had already been renamed -- lds.ttl carries Dataspace,
AdminDataspace and EndUserDataspace and the word "application" appears nowhere
in it -- but the documentation still described the old model, in which a
dataspace was a pair of applications and "application" was the thing with a base
URI, a service, an ontology and a stylesheet.

An end-user dataspace and an administration dataspace are each a dataspace,
standalone. They are associated by origin rather than declaration: the admin
one's host is the end-user one's host with admin. prefixed, and the platform
derives the counterpart in either direction. So the dataspace reference loses its
Applications section entirely -- those were dataspace traits, documented twice --
and the end-user and admin subsections describe two kinds of dataspace instead of
two halves of one. #applications, #end-user-apps, #admin-apps, #lapp-ont and
#application-and-access-functions go with it, and every inbound link follows.

What stays is the word in its other senses: application/* media types, the
Knowledge Graph application platform, the JAX-RS application class, the
application shell that serves static assets, and the application a reader builds
in a dataspace by following the tutorial. A dataspace is what you configure; an
application is what you build in one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…, Copenhagen only installed on its own production host, and five scripts were copies of each other.

create-authorizations.sh hashed layout.xsl, deleted when the taxonomy package took over the stylesheet; under set -euo pipefail that ended install.sh before ldh push. The one authorization it still had reason to create now lives in install.sh.

Copenhagen's nine mapping queries pinned https://copenhagen.demo.linkeddatahub.com/ns# while import-ns.sh installed the ontology at {base}ns#, so on any other base the data and the model never met. The class is now derived from the $base constant the platform binds, as Northwind already did. The root chart runs on the <ns> endpoint, which re-parses queries against its own URL, so it uses <ns#> and <sparql>; the parking chart runs on the data endpoint, which parses with no base, so it matches the class by name instead.

Makefile, install.sh, import-csv.sh, import-ns.sh and patch-ontology.ru were byte-identical between the CSV demos, and the certificate password went through three argv layers with a note asking the user to escape it. lib/install.mk is the interactive make install for every app: it exports LDH_BASE, LDH_CERT_FILE, LDH_CERT_PASSWORD and LDH_PROXY, which the ldh CLI already reads, and runs install.sh in that environment. lib/ldh-app.sh carries the shared steps; each install.sh is its app's own sequence. The packages registry gets an install.sh so it installs like the rest.

CLAUDE.md, the package README, the root README, two tutorial pages and the screencast README described paths that no longer exist, a script that was deleted, the relative upload reference the fill script stopped writing, and a package install mechanism the platform does not implement. They now describe the tree as it is, and say per step what a re-run does instead of calling the install idempotent in one place and not in another.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
LinkedDataHub bfd873e0a renamed it to -c/--cert and widened it to the PEM beside
the keystore, so fixture.mjs -- the only place here that passes the credential on
the command line rather than through LDH_CERT_FILE -- follows it in the four
invocations that reset a document, a container, a block list or delete by title.
The install path needs nothing: ldh-app.sh and install.mk hand the CLI its
credential as LDH_CERT_FILE, and that name has not changed.

The harness keeps its own --cert-file. It feeds Playwright's pfx, which is PKCS12
and nothing else, so there the name is accurate and the widening does not apply.

The CLI reference follows the surface it documents: both option tables, the
prose that said the credential is read from a keystore, and the captured --help,
regenerated from the CLI rather than edited.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…t-level debug output; it ends at 33, once the platform has loaded its ontologies. Same codec, size and frame rate, 5.9 MB instead of 13, and setup.ttl points at the new content hash.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Since f226cef26 a file dropped on any writable document is imported as data when it
is RDF and uploaded to the document otherwise, so the user-interface tab leads with
the drop and its two outcomes, and keeps the Create menu's File form as the
alternative. The clip shows the drop: the overlay with its RDF and File lanes, then
the portrait as a resource of the document. The shot builds the DataTransfer in the
page, as the platform's own file-drop tests do, because Playwright cannot pick a file
up off the desktop; the drag is held long enough that the pacer's third of it still
reads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… a dataspace.

77cd0f1 renamed the model in the reference pages; the README, the package notes, the
demo root documents, the tutorial and guide landing pages and the screencast
harness's comments kept the old word. They say dataspace now: a dataspace has the
settings, the stylesheet, the imports closure and the origin, and the menu that lists
them is the Dataspaces menu, as the platform labels it. The about page keeps the
distinction the earlier commit drew, in one sentence: LinkedDataHub serves RDF
Knowledge Graph dataspaces, and applications are built on top of them.

What stays is the word in its other senses: application/* media types, the Knowledge
Graph application platform, the JAX-RS application class, the application shell, a
public web application, WebID's distributed applications, and the CLI reference's
"Base URI of the application", which quotes the CLI's own help text and changes with
it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…now, text in the left column and the linked logo in the right, the same grid the site's front page uses from its layout stylesheet, which every dataspace on the deployment shares through ac:stylesheet. No design-system change: per-dataspace layout belongs in that stylesheet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… kilobytes each; they are upscaled now. Uploads are content-addressed, so every reference to the old bytes follows: the photoHash column in employees.csv, which the import maps to foaf:img, and the example ldh add file output in the upload-file guide, which shows Nancy Davolio's hash.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… than the 8-colour GIFs. Both sets are encoded for the web: quality 85, 4:2:0 chroma, metadata stripped, pixel size kept, which takes the 17 images from 17.5 MB to 4.9 MB; the portraits committed at quality 98 in the previous commit are re-encoded the same way. Uploads are content-addressed, so every hash follows the bytes: the pictureHash and photoHash columns, the category rows and conversion output quoted in the tutorial and the CSV import reference, the Beverages example in the dataset reference, Nancy Davolio's example upload in the upload-file guide, and the ldh:file hash of categories.csv itself in the import example, since the CSV changed with its column. The repository tree in the app-as-repository tutorial lists *.jpg under categories/ now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s actually writes.

The Northwind constructors claimed to, but were never diffed against the mapping queries. Person
lacked foaf:img, schema:identifier, the wgs84 coordinates, and the schema:name and faxNumber the
customer and supplier contacts carry; ProductGroup lacked foaf:depiction; Product lacked dct:title,
schema:description and the schema:offers link to its list price. OrderItem offered a discount the
import deliberately drops and lacked schema:itemOffered; ParcelDelivery offered a price that lives
on the delivery charge. City typed its coordinates as strings while every other pair is a float,
so the territories query casts them now. The image properties are typed nfo:FileDataObject, the
way the platform's own ldh:file constructor points at an upload. Enumeration constants the imports
stamp on, such as order status, stay out, as decided when the constructors went in.

Copenhagen had no constructors at all: nine classes, nine mapping queries, no create form. Each
class gets one with the fields its query writes. Three queries were out of step with the other
six and are aligned: parking facilities wrote schema:identifier where the rest write
dct:identifier, and bicycle parkings and charging stations left capacity a string where parking
facilities cast it to an integer. What a form field cannot express stays out: a place's category
and a playground's area and type are untyped blank nodes, and a parking facility's owner points
back at it from a separate resource.

The taxonomy package's constructors were checked against the predicates the UNESCO source data
uses per class. Concept lacked skos:scopeNote, on over three thousand concepts, and
skos:topConceptOf, which the import keeps while dropping its inverse. Collection lacked altLabel,
inScheme and notation, which the micro-thesauri carry.

The ontologies reference quotes the Product constructor verbatim and follows it; the change-model
tutorial's list of the controls the form offers follows too. Its screenshot still shows the
four-control form and needs a reshoot.

Co-Authored-By: Claude Fable 5.1 <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