Skip to content

Develop - #60

Open
namedgraph wants to merge 72 commits into
masterfrom
develop
Open

namedgraph wants to merge 72 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 12, 2026 18:50
Mirrors the bundled copy. A label added through the form came out untagged beside the tagged values
already in the data, because the constructor declared [ a xsd:string ] and the added field is built
from the constructor rather than from the data. A SKOS label is natural language, and SKOS's own rule
of at most one prefLabel per language is not expressible without a tag.

Requires the platform's rdf:langString form control (LinkedDataHub 48f2d7479, Web-Client 7abb022f);
without it a langString marker renders as a resource picker.
…oncept you are reading.

Mirrors the SKOS package stylesheet from the platform repository, where the change is described in full. In short: the tree roots at the concept scheme rather than at the document's own topic, and on a concept document it climbs to the scheme one hop per request - a constant-size SELECT over a frontier set, both assertion directions per hop, each hop in its own GRAPH because a property path cannot leave one - then descends expanding every child on the path, so a concept with two broader concepts appears under each of its parents. ReadMode only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BSRUJBNKKjdgJeP3qRZTPd
…d in.

Every hierarchy and membership view joined a prefLabel and filtered it to English or untagged. The
label is the ORDER BY key and is never projected, so the filter selected nothing - it only dropped
concepts labelled in another language, which is every concept created through a UI whose language
control follows the browser. Which language a reader wants is decided at render time, where the label
chain already picks per value; a stored query cannot know it.

The join stays as the sort key; what is given up is the order among one concept's own labels.

Synced from the platform's bundled copy
(src/main/resources/com/linkeddatahub/packages/skos/ns.ttl), which this file is kept byte-identical
to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BSRUJBNKKjdgJeP3qRZTPd
106 p.lede become p.ldh-body-lg in 57 documents, and docs.css drops the
.docs-main p.lede rule that carried them.

The class was documentation-local: it resolved only under the static site's
.docs-main wrapper, so the same rdf:XMLLiteral bodies rendered a lead paragraph
on the generated site and plain body text inside LinkedDataHub, which links no
docs.css and has no delivery path for one. .ldh-body-lg is defined in
colors_and_type.css, which both contexts link, so one class now serves both.

It also corrects a size drift. docs.css set the lead to --fs-lg, which the
scale's own role map reserves for headings; --fs-md is the size that map
assigns to lead paragraphs, and the m3 skin maps .ldh-body-lg to Material
Body Large 16/24.

The hero's .lede stays as it is: app.css scopes that rule to .ldh-hero, where
linkeddatahub.com's homepage nests it correctly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BSRUJBNKKjdgJeP3qRZTPd
…ulary it speaks.

packages/skos/ becomes packages/editor/taxonomy/, so the package URI is now
https://packages.linkeddatahub.com/editor/taxonomy/#this, and layout.xsl becomes skos.xsl.

The old name was the vocabulary, not the capability. What the package ships is a taxonomy editor:
a concept tree beside the content, hierarchy views that read both assertion directions, and
constructors and constraints for concepts, schemes and collections. SKOS is what it speaks. The
directory now says the first and the stylesheet says the second, matching the platform's own rule
that vocabulary-typed templates live in a module named for the prefix. editor/ opens a namespace
for the editors that follow.

update-folder.sh derives the published URI from the filesystem path, so the move IS the URI change;
packages/editor.ttl is the container document that makes https://packages.linkeddatahub.com/editor/
exist for taxonomy/ to be created under. Nothing else about identity is a convention: the ontology
is found through ldt:ontology and the stylesheet through ac:stylesheet, which is why the stylesheet
could be renamed at all. Both READMEs and the packages reference page said layout.xsl was part of
the layout contract and drew a single-segment packages/<package-name>/; both claims are corrected.

skos.xsl is also brought up to the platform's bundled copy, which had been ahead by the concept
tree's heading wrapper and its ReadMode hrefs (ldh:conceptree-href, ldh:tree-node-uri). The copies
are meant to be byte-identical, and ac:stylesheet points at this one on develop, so the drift was
what a non-bundled install would have fetched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EptZ6w7rd883q8N4CMorR
…m now dresses.

docs.css loses its content-typography layer — headings, code/samp/kbd, pre, dfn
and the reference-table skin — because those are element rules in the kit now
(colors_and_type.css, surfaces.css), which both this site and LinkedDataHub link.

The layer existed because the design system covered app surfaces and prose not at
all. That meant the same rdf:XMLLiteral body rendered correctly here and undressed
in the product, which links no docs.css and has no delivery path for one: about
2,500 elements, the same defect that moved p.lede to p.ldh-body-lg, two orders of
magnitude larger. A rule keyed off an element rather than off .docs-main is the
only kind that can reach both, since neither stylesheet can know the other's
wrapper.

What stays is genuinely site-only: the layout grid, nav, child cards, version
dropdown, header and footer chrome, the media rules, and the relaxed line height
this column wants. The table pair stays too — flow spacing and topped-out cells
are what a table in PROSE needs and an app surface does not — but it keys off the
element now, because the content carries no class: the 37 tables have dropped
class="ac-table dn-comfy" and are plain <table> again, which returns every one of
those files to the state it was committed in.

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

The 37 reference tables gain a visually-hidden <caption> and all 204 <th> gain a
scope. The 43 alerts put their running text in the component's own .ac-alert-text
slot, and the two package documents trade class="lede", which resolves in neither
context, for the .ldh-body-lg role that resolves in both.

A table is the design system's DataTable, and that component requires a caption:
a table needs a name, and an aria-label on a grid is less reliable. The captions
say what each table lists rather than repeating the heading above it, because a
caption is read on its own. scope is native table markup — it is what survived
when the component dropped every role= attribute — and the reference tables need
all three kinds: col in the head, row for the term column, rowgroup for the
colspan bands that open a tbody in the CLI and HTTP API matrices.

Cell decorations (.num, .iri, .mono, .var) are deliberately NOT applied. They
dress cells holding bare text, and this corpus marks its tokens up as <code> and
<samp>, which the kit's element layer already dresses; adding them would put mono
on the cell and a chip inside it. The one numeric column, the tutorial's stage
number, is an ordinal row label rather than a quantity, so right-aligning it
would be wrong. The finding dissolved on inspection.

Alert titles are left alone: the component makes the title optional, the pass
that harvested the obvious ones already ran, and inventing 38 more would be
writing, not conforming.

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

Three pages disagreed. reference/command-line-interface.ttl described the
`packages` group in prose while omitting it from its own command table; both
reference/administration/packages.ttl and user-guide/manage-packages.ttl asserted,
word for word, that "packages have no command of their own" and that `ldh patch`
on the settings document is the whole interface.

The commands exist, and the corpus already documents the release they belong to —
it marks the `bin` scripts deprecated and records the retirement of the
packages/install endpoints, both of which land in the same version. So the CLI
page's prose was right and the other two were stale. The table gains a Packages
group, and both pages now install with `ldh packages add` and uninstall with
`ldh packages remove`, keeping what is still true: the declaration IS the
installation, it takes effect on the next request without a restart, and the
permanent form is the same triple in config/dataspaces.trig.

The READMEs were further behind than the pages they link to. packages/README.md
still documented "installation-time composition via JAX-RS endpoints" with a
tick-and-cross list claiming no dynamic loading at request time — the opposite of
what the reference page says — plus install-package.sh, the master-stylesheet
mechanism, POST /packages/install and /uninstall, an instruction to restart
Docker, and an unresolved TODO. The root README pointed at the deprecated bin
scripts as the prerequisite. Both now describe the mechanism that exists.

Also corrected: create-resources.ttl posted schema:category at a document URI
where every other example in the corpus uses the #this fragment, teaching the
exact confusion between a document and its topic that the data model is careful
about. The demo's own import mints categories as <graph>#this.

Not fixed here, because it is a migration rather than a correction:
demo/unesco-thesaurus/install.sh still calls install-package.sh, and the install
tooling has 24 call sites on deprecated bin scripts and none on ldh. Converting
one line would leave a single ldh call among them, and would fail on
authentication — ldh takes a PKCS12 keystore where these scripts pass a .pem.

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

Copenhagen and UNESCO were written in a register the design system names as its
anti-example: "Copenhagen is renowned as one of the world's most bicycle-friendly
cities", libraries as "a cornerstone of community life", concepts as "a distinct
unit of thought or terminology". The system asks for confident, technical and
unmarketed copy that states the mechanism rather than the outcome, and Northwind's
own blocks already do — each paragraph earns the chart it introduces.

Rewritten from the data. The bicycle racks carry a capacity, a street address and
a district, so the map reads as supply rather than as points. The libraries carry
a postal district and a link out to the city's own page. The toilets carry opening
hours and step-free access, which is what makes their map answer availability and
not just location. Counts are the real row counts of the imports.

Both apps also told the reader to "explore the map below" and to "use the
interactive map below", which the block underneath was already showing.

Structurally, seven of Copenhagen's ten containers had no prose at all while two
had hand-written intros — charging stations, parking, places, playgrounds,
toilets, schools and sports centres opened straight into a map with nothing said
about it. All ten have one now, and every demo block opens at h2 inside
.ldh-section, the shape Northwind's 23 blocks use; the five that opened at h3 with
no h2 above them are gone. 37 blocks, 37 h2, 37 .ldh-section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EptZ6w7rd883q8N4CMorR
… that were really sentences become sentences.

Converted, because each term carried the same positional facts and the list was
hiding a grid: the triplestore service properties (property, endpoint, required,
example — exactly three dd per dt, in that order every time), the dataspace
service fields (required-ness parenthesised into every description), and the
shared CLI parameters, whose entries carried two dt each for the short and long
form and sat beside an environment-variable table doing the same job.

Dissolved into prose, because a list of one is a sentence:
ENABLE_LINKED_DATA_PROXY, JAVA_OPTIONS, the applications' Stylesheet property,
the Title step in create-documents, and the two byte-identical Value steps in
create-content.

Deliberately NOT converted. The twelve environment-variable lists in
configuration.ttl stay lists: most terms carry several dd — google_client_id has
two, credentials has three — and their defaults live inside the prose rather than
in a column, so a two-column table would flatten notes into one cell. The layout
modes in user-interface.ttl stay too: one description each, of wildly unequal
length, where a table would make one enormous cell. With the kit now dressing dl
in both contexts, appearance is no longer a reason to convert anything — only
information shape is, and these are shaped like definition lists.

The ten <pre> blocks flagged as "not code" are all left as they are. Every one
holds a literal artifact and is framed as such: the CSV samples and the imports
manifest are the contents of named files, each with a Source link to that file
directly beneath, so a table would lose the commas the reader needs in order to
write the mapping; the rest are directory trees, a shell session and response
bodies whose line structure is the content.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EptZ6w7rd883q8N4CMorR
tutorial/insight.ttl put <h2 id="chart"> and <h2 id="view"> — 78 of the page's
110 content lines — inside the "From the command line" panel, which is not the
one selected on load, while the default panel held a single paragraph. A reader
arriving at the stage saw that paragraph and then "What you now see", which
refers to a chart and a view they were never shown; a link to #chart landed on a
heading inside a hidden panel.

The nesting was inverted. Those two headings name what the stage BUILDS, which
both routes build, so they belong to the page and not to one route. The sections
own the tabs now, one strip each, and the browser and command-line routes sit
inside the section they are routes through.

The other three pages with headings inside tab panels are left alone, and the
difference is what the headings name. get-an-account's tabs are three mutually
exclusive ways to get an account, and its headings are the steps of one of them;
version-history and create-resources tab a user-interface route against a command
-line one, and their headings — "In the History dialog, click Restore" — belong
to the route they sit in. In all three the headings are in the DEFAULT panel, so
the page reads correctly on arrival. Inverting them would also have meant writing
command-line content that does not exist: version-history's CLI panel is one
paragraph covering all three Memento roles and does not cover restore at all.

Five sections whose only child was another section are merged: "Management" over
a lone "Command line interface" in the ACL, ontologies, packages, CSV import and
RDF import references. Nothing linked #management-cli; #management keeps its id,
which five pages do link.

The action bar's reference said its controls fall into three zones and then spent
four paragraphs on them, because the timestamp — "next to the breadcrumbs" — had
a paragraph of its own. It joins the middle zone, so the three paragraphs now
open with "On the left", "In the middle" and "On the right".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EptZ6w7rd883q8N4CMorR
…ally too.

reference/dataspace.ttl introduced its Northwind block as "the Northwind Traders
dataspace, as declared in config/dataspaces.trig" — a claim about shipped
configuration. The entry it was quoting exists, but as a testing leftover: the
out-of-the-box stack populates nothing behind that origin, and the tutorial does
not create it, because the tutorial writes everything to $LDH_BASE, which is the
root dataspace at localhost:4443.

So the block is reframed as what it is and what the corpus needs it to be: the
worked template that manage-dataspaces#create-dataspace asks the reader to follow
and never shows. The Turtle is unchanged, origins included — a reader adding a
dataspace to the default stack really would write localhost:4443, and the
<app>.demo.<host> shape is the one the demo site deploys, so the same declaration
carries from local to public.

The tutorial deliberately keeps building into the root dataspace. Requiring two
TriG edits and a restart before a first document would put configuration in front
of someone whose goal is to run the thing out of the box.

reference/http-api.ttl keeps its federation example on the public demo hosts,
which are reachable with no setup and make the cross-INSTANCE case concrete, and
gains a sentence saying the mechanism is about origins rather than instances: two
dataspaces on one LinkedDataHub federate the same way once both are declared.
Going local there would have meant importing the UNESCO thesaurus first, which is
a heavy prerequisite for one example.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EptZ6w7rd883q8N4CMorR
…ote behind.

Scripted Playwright scenes against a running instance, plus the tooling to pace,
review and still them. A scene is a workflow rather than a tour: it opens on a
question somebody would ask, each step follows from the one before, and it ends by
answering it — by writing the finding into a ContentMode document, so what survives
the recording is an artefact and not a memory. The five here build their pages out
of interleaved prose and evidence, which is how the Northwind pages are built
already, so the result belongs where it lands.

Five rules turned out to matter more than any amount of scripting, and every one of
them came from a scene doing something a person would not. A URI is never known in
advance: to reference a resource the scene navigates to it, presses the app's own
copy control and pastes, because a value appearing in a field from nowhere is a
step the audience cannot reproduce — synthesising a ?mode= URL is the same
violation with the address bar. The copy takes the inner resource and not the
ldh:Object wrapping it, or the embed nests and renders two stacked headers for one
piece of content. No gesture is performed whose state is already current, so a
view already in Grid is not switched to Grid for the camera. Presence in the DOM is
never availability — a collapsed details, the ContentMode-gated add buttons, the
inert hover-opened drawer, canvas-drawn map pins and hover-revealed copy controls
each need a real gesture first. And every locator is scoped to the active pane,
since inactive tabs stay in the document and an unscoped one will happily drive a
tab nobody is looking at; that single mistake accounted for more failures than the
rest together, and it hid well because it only bit where a comma selector let one
alternative escape the scope.

Pacing is measured rather than declared. A uniform speed-up scales pointer travel
and typing along with the waiting and reads as fast-forward, so the transcode
detects static stretches with freezedetect and speeds only those, taking 163s of
capture down to 65s with every gesture still at 1x. The marks sidecar each scene
emits — an edit list keyed by beat — is remapped onto that timeline piecewise,
which is also what lets the reviewer pull frames from the paced file without every
late beat landing past its end.

The reviewer exists because three defects in a row were invisible to the beat log.
A page whose prose said twenty territories beside an evidence block rendering nine
employees, since facet state is not persisted and the embedded view reverts. A run
that died after two of sixteen beats and reported green, because the beats that ran
had succeeded. And an embed that nested an Object in an Object while every step
reported success. So it now requires the terminal beat, counts the document's
blocks, and resolves each embedded value against its source to say whether it
reached the resource or its wrapper.

FINDINGS.md records what the scenes ran into that is the product's rather than
theirs: facet values that never load on large containers, where the whole cost is a
label lookup over an unbound graph that cannot match; a chart axis bound to a
URI-valued variable rendering raw escaped markup; and the parallax step chip's ×,
which is a dead control that throws.
…her than where it showed.

A form control is found by the property name beside it and never by position. LDH's
inline forms carry no label elements — each field is a .ldh-prop-group holding its
own name — so filtering anything coarser matches the container that holds every
name and then takes its first control. Three call sites got away with it and the
fourth did not: on the View form the first control is the query combobox, so filling
"Title" typed the title into the middle of the URI that had just been pasted, leaving
a corrupt reference that still looked plausible. field() is now the only way in, and
fill() reports which control it wrote to, so a mistargeted write shows up in the beat
instead of only in the video.

The mode guard never once fired. currentViewMode matched \bGrid\b against a toggle
that reads "grid_viewGrid" — a Material ligature running straight into the label, so
there is no word boundary in front of it — and quietly returned unknown every time,
which meant the "already in this mode" branch was dead code and every switch went
ahead. It compares against the tail of the string now. The toggle text had been
sitting in an early probe's output the whole time.

A query typed on camera is multi-line and shaped the way somebody writes SPARQL,
because the query is the artefact the scene is making: if it looks machine-generated
the claim that a person could have written it goes with it. It is typed deliberately
unindented, since the editor auto-indents and supplying our own would double it, and
closing braces are typed normally because CodeMirror types over the ones its
auto-closing inserted. All of which fails silently, so the text is read back from the
editor and compared — the beat says "11 lines, read back and matching" or it says
nothing of the sort.

And the copy takes the inner resource rather than the ldh:Object wrapping it, with
an optional name when a page holds several. Two scenes had been embedding block URIs
instead of view URIs, which nests an Object in an Object and renders two stacked
headers for one piece of content; N2 was copying the same URI for its query and its
view, so the embed showed the query. The reviewer now resolves every embedded value
against its source and says whether it reached the resource or the wrapper, because
every beat passed while this was wrong.

FINDINGS gains a fourth: YASQE asks prefix.cc for its prefix list over http from an
https page, so the browser blocks it as mixed content and the editor comes up without
prefix autocompletion, telling nobody.
…of the bytes that ship.

Every shot is cropped to its subject: a chart block is a picture of that chart block, not
of a page with a chart somewhere in it. A page-level shot stays page-level — the
application layout, the landing page, the unauthenticated app — and everything else names
the element it is about.

Uploads are content-addressed, so a reference is only valid for the bytes that are served.
Stills are published as WebP at 2240px, twice the docs' content column, which stays sharp
on a retina display at a quarter of the PNG's weight; the clips already carried CRF 20,
yuv420p and a leading moov atom, so they are copied rather than re-encoded.

A clip is a <video> and never an <object>. Item.java serves every upload with
default-src 'none'; sandbox, the LNK-011 fix — an <object> loads the file as a nested
document, so that CSP governs its own media load and Chrome blocks it while Firefox does
not. A <video> is a subresource of the page and loads normally.

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

The runner took viewport screenshots, so a picture captioned "a chart block" spent half
its pixels on the navbar and the block was cut off at the fold. A shot now names its
subject — a selector, a locator, or a pair whose union it spans — and the frame is that
element's box.

Two things that cost three attempts each are written down in the code. Sticky chrome
paints OVER an element capture, because Playwright scrolls the subject into view and the
header is still on top of it; every fixed or sticky element that is not the subject, inside
it, or containing it is hidden for the shot. And a union has to measure its boxes in page
coordinates atomically: scrolling to the second subject moves the first one's viewport
rect, so boxes read at different scroll positions cannot be combined.

Shots that document an act of authoring — annotating a word, composing blocks — opt in
with writes: true, and the run provisions a scratch container, uses it and removes it. It
resets rather than creates, so a run that dies before teardown does not poison the next
one, and nothing depends on a document some earlier session left behind.

fill.mjs is the step that was missing: it writes the published files into the .ttl sources
and rewrites them when they are re-shot. The caption is the key — it is already in the
markup as @alt or @aria-label — so a slot is found either by its placeholder or by the
element carrying it, and a re-shoot just swaps the hash.

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

The rig had no reader-facing account of the docs shoot: what the manifest is, why a shot
asserts a want before it captures, why masters are not web assets, and why optimisation
has to happen before hashing when the hash is the address.

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

A view without a title is only addressable by URI, which means a scene or a shot has to
know the URI in advance — and copying one out of band is the thing the rig is not allowed
to do. With a title, the same view is found in any combobox by typing what it is called.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…arn what the app actually does.

Each scene is a workflow answering a question, so every step has to be one a person could
take: a resource is found by the name it carries, a mode is switched with the control that
switches it, and a block's URI is copied with the button that copies it. The helpers grew
where the app turned out not to work the way the scenes assumed — collapsed disclosures,
hover-gated controls, inactive panes still in the DOM.

Adds annotating a word with a statement, which is how an arbitrary triple is written
through the UI: the resource form is ontology-driven and offers only what the class
declares, while prose annotation is not constrained by the subject's class. Adds the
graph helpers, the cold open that lifts a scene's payoff to the front, and a sixth scene.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RULES.md is the set a scene kept breaking and rediscovering: no URI is known in advance,
copy the inner resource rather than the Object wrapping it, presence in the DOM is not
availability, scope every locator to the active pane. FINDINGS.md gains the product
defects these runs walked into — the endpoint that could not render HTML, and a
document-scope Map that draws Null Island when there is nothing to plot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A selector belongs to the app, not to a guess, so each of these dumps some corner of the
running instance — the chart create form, the mode buttons, the typeahead, the query
block, federation, the graph. probe-viewform stops asking about a form it no longer needs.
inventory.mjs lists what a dataspace actually holds, which is what a scene has to consult
before inventing a question about it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ldh:container was deleted from the platform - the property, its constructor input, the ontology
query's binding, the data-container stamp and the RDFa fallback read. The Views reference was the
last place in either repository that still mentioned it: a paragraph presenting it as the way to get
a Create button, and an example carrying the one absolute https://localhost:4443/products/ value that
deletion found. A reader following either would have declared a property nothing reads.

The replacement text corrects two further things the old paragraph got wrong independently of the
property. Inline creation is for views attached to a property in the application's ontology, not for
any view that carries some metadata: the class to construct comes from the attaching property's
rdfs:range, or its rdfs:domain when the attachment is inverse, and a view block authored by hand in a
document has neither. And the container is now derived from the view's own results - a second query
built from its SELECT with LIMIT, OFFSET and ORDER BY removed, projecting the containers of the
documents the existing solutions are described in. One container renders the button, none and more
than one render nothing, and the three cases are spelled out because the two silences are the ones a
reader will otherwise read as a bug.

The showWhenEmpty example keeps its shape minus the deleted line. It stays valid for a
document-authored view, since ldh:showWhenEmpty does have the RDFa fallback read that data-for-class
never had.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The concept tree's three promises passed their failures to the one-argument ldh:promise-failure,
which LinkedDataHub replaced with one that takes the element to report in and a headline (937a215f8):
a refused parents query reports on the tree's root row, a refused step on the row it was stepping
into, and a refused children query in the list the children would have filled. The package bundled in
LinkedDataHub changes identically.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ints at a category, a territory and an employee.

schema:City gets a constructor (title, name, identifier, coordinates, region), so the Create
button on a region's city list produces a form instead of a bare type chip. Three inverse
views join the six that existed: products in this category, employees serving this territory,
and an employee's direct reports.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…a loop where they can.

The scenes are rewritten from the rules: each starts on a document whose loaded render is the
money shot, explains that state in a content block before any work, and moves by visible
clicks from there. Where the app lets a scene write into the data — Houston, created from the
region's own list — the closing beat re-reads the evidence it opened on.

The rig learns what the takes taught it: pins are located by resource URI through the captured
OpenLayers maps, graph nodes are aimed at by the canvas's own hover event rather than a
projected point, queries are typed line by line past the editor's completion popup, the head
of each cut is trimmed to the first beat instead of a cold open being spliced in, and a scene
resolves its inputs over SPARQL at start. Retired scenes and the probes that answered the
questions are kept. FINDINGS gains the empty constructor form, the restart that drops package
imports, and table headers that are not sort controls.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tead of winning the walk.

The platform split ldh:RenderRow into a sealed walk and the open leaf mode ldh:RowHook it asks at
every element, so the rule that reveals the open concept moves to that mode and drops the
priority it needed to outrank the walk. The README now describes where a package stylesheet is
composed - right above hooks.xsl, the open modes' module, and below everything else - and what a
rule in each open mode owes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Person, Corporation, Order, OrderItem, ParcelDelivery, DeliveryEvent, Offer, the three price
specifications, PostalAddress, ContactPoint, ProductGroup and Place join Product and City, each
with the literal fields its instances carry — dates as xsd:date, quantities as xsd:integer,
prices as xsd:float — and typed lookups for what they link to. DeliveryMethod is an
enumeration and stays without one; enumerated values such as order status are set on the
instance afterwards.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…. One fuseki service holds an end-user and an admin dataset for every dataspace, named after the dataspace origin with the deployment host dropped and the role appended (northwind-traders.demo.end-user, and so on; the root dataspace keeps plain end-user and admin), declared in config/fuseki/config.ttl and stored one TDB2 directory per dataset under fuseki/. The configuration reference replaces the fuseki-admin and fuseki-end-user sections with the single fuseki service and documents the new egress forward proxy: outbound SPARQL SERVICE and LOAD from both Fuseki and the platform pass through it, public destinations allowed and loopback, private and link-local refused, so federation with public endpoints works while a query cannot reach another dataset, the cache or the platform. The dataspace reference's service example points at a per-dataspace dataset, the dataspace-management guide gains a step to declare the datasets and bind each application to its own service, the setup tree notes one TDB2 directory per dataset, and the triplestores reference's quad-store example follows the single-fuseki endpoint form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Docs: single Fuseki with a dataset per dataspace, and the egress proxy
… property or datatype wins.

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