Skip to content

feat(ontology)!: generate manifest v6, envited-x v4, georeference v6 and ositrace v7 from LinkML with an ontology-header profile - #106

Draft
jdsika wants to merge 7 commits into
mainfrom
feat/linkml-domain-models
Draft

jdsika wants to merge 7 commits into
mainfrom
feat/linkml-domain-models

Conversation

@jdsika

@jdsika jdsika commented Oct 2, 2026 •

Copy link
Copy Markdown

Description

Switches manifest, georeference, envited-x and ositrace to artifacts generated
from LinkML, and bumps them to manifest v6, georeference v6, envited-x v4 and
ositrace v7
. The hand-written OWL, SHACL and contexts of these four are replaced;
the prior versions stay published under their w3id IRIs and are named by
owl:priorVersion. The importing domains move to the new versions in place.

The models follow what the hand-written files meant: each construct is traced to
its intent and modelled with the LinkML mechanism for it, and the generators are
the only thing that runs — no pre- or post-processing. What LinkML cannot state is
recorded in linkml/GAPS.md.

The generators are the reviewed ENVITED-X pipeline of the ASCS-eV LinkML fork,
pinned to the tag envited-x/2026.10.14 (536d6f194).

Commits

Based on v0.5.1.

  1. fix(validators) — per-resource inference types blank-node objects by rdfs:range, as the default mode does.
  2. fix(report) — the two hash-dependent parts of pySHACL's generic messages (the order of sh:in values; where a shared blank node's rendering is cut off) are rendered in a fixed form, so report snapshots are reproducible. The structured violations take the same normalization.
  3. build(linkml) — linkml pinned to the tag envited-x/2026.10.14; one build script for every LinkML domain; openlabel-v2 rebuilt with it.
  4. ci — the jobs that run just generate check out the pinned Gaia-X sources.
  5. test(harness) — scripts/compare_artifacts.py measures a candidate against the committed artifacts.
  6. feat(ontology) — the ontology-header profile (below).
  7. feat(ontology)! — the switch-over and version bumps; the registry, catalog and properties overviews list the new versions, and the docs describe the generated domains.

Ontology headers: the ontology-header profile

The hand-written ontologies share one header convention: a permanent identifier,
the repository and release they come from, their version and prior version, their
shapes, related resources and their authors, with resources named by IRI. LinkML
gives a schema-level annotation a literal unless a metaclass the schema
instantiates declares its range — the route the LinkML maintainers recommend
(linkml/linkml#2892, #3379) and that the fork implements (ASCS-eV/linkml#33).

linkml/ontology-header/ is that metaclass as a schema profile, the pattern of
LinkML's implements/instantiates guide and of linkml-microschema-profile, which
downstream schemas import:

  • OntologyHeader declares owl:priorVersion, prov:wasDerivedFrom,
    dcterms:references and dcterms:publisher: range nodeidentifier,
    implements: [owl:AnnotationProperty]; the release and the publisher are required.
  • Each domain imports the profile and instantiates: [ontology-header:OntologyHeader].
    Its header values are IRIs on declared annotation properties (OWL 2 requires the
    declaration in the import closure), and its OWL states no logical axioms about them.
  • The domains build with --no-mergeimports / --exclude-imports, so the metaclass
    stays in the profile: a domain's OWL has owl:imports of the profile and types
    the ontology ontology-header:OntologyHeader; no domain class, shape or context
    term is added.
  • The profile's shapes check those headers: every generated ontology conforms, a
    literal publisher or a missing release does not (tests and fixtures included).
  • https://w3id.org/ascs-ev/envited-x/ontology-header/v1 resolves through the
    existing generic w3id rules.

owl:versionIRI is not stated. Every hand-written ontology used its version IRI as
its ontology IRI, and the schema id is that IRI, so the triple only repeated it;
OWL 2 makes the version IRI ontology-header structure, not an annotation, and the
reviewed generator rejects it as one. The registry, docs build and class pages use
the ontology IRI as version IRI, so no published version IRI changes. OWL 2 §3.3,
OBO FP-004 and FOOPS VER1 prefer a permanent ontology IRI with a distinct version
IRI; LinkML has no field for that (GAPS G22, linkml/linkml#768).

Generator features this needs

All in the ASCS-eV LinkML fork, each its own PR, combined on the pipeline branch (ASCS-eV/linkml#14) after review:

Fork PR Feature Upstream
ASCS-eV/linkml#29 class-level any_of / all_of / exactly_one_of / none_of → sh:or / sh:and / sh:xone / sh:not linkml/linkml#4028
ASCS-eV/linkml#30 range_expression enforced on each value (sh:node over an anonymous shape) —
ASCS-eV/linkml#34 has_member → sh:qualifiedValueShape + sh:qualifiedMinCount 1 fixes linkml/linkml#2465
ASCS-eV/linkml#31 instantiates on emitted schema resources (the schema, permissible values) → rdf:type —
ASCS-eV/linkml#32 string-derived xsd:anyURI types stay typed literals —
ASCS-eV/linkml#33 declared annotation terms: an instantiates metaclass gives an annotation its RDF term, implements: [owl:AnnotationProperty] its OWL role; reserved OWL vocabulary such as owl:versionIRI is rejected as an annotation linkml/linkml#3546, #2892
ASCS-eV/linkml#36 DCMI agent annotations (dcterms:creator, publisher, …) with declared ranges linkml/linkml#3543
ASCS-eV/linkml#19, #20 rules → SHACL-SPARQL, with a compositional fallback —
ASCS-eV/linkml#24, #26 --diff-stable output through diffable-rdf —
ASCS-eV/linkml#4 --normalize-prefixes —

gen-shacl --inlined-as-node was rejected in review and is not used: what a nested
value must satisfy is stated with range_expression on the slot that holds it.

Modelling

  • Nested values. A class range gives sh:class; the range class's shape reaches
    a nested value only when the value is typed (in the data, or by rdfs:range under
    the validator's RDFS inference). What a link, its file metadata or its dimensions
    must satisfy regardless of type is a range_expression. The JSON-LD artifact
    condition is a class expression on ArtifactLink and on the artifact properties;
    the license, manifest-reference and ENVITED-X vocabulary narrowings sit on the
    owning properties.
  • openlabel-v2. Built by the same script with --no-use-native-uris: its OWL
    declares the schema.org and Commons IRIs (schema:QuantitativeValue,
    schema:minValue, …) its SHACL and context already use. Rule 17 ("DrivableAreaEdge
    includes EdgeNone") is has_member; a bare equals_string on a multivalued slot
    means every value and the reviewed converter skips it. Rules on a class also
    constrain its subclasses' shapes.
  • Nested OWL restrictions name slots by their slot_uri (skos:note,
    sh:conformsTo, ositrace:identifier), the IRIs the ontologies declare.

Data-contract changes

envited-x v4

  • Every artifact and referenced link uses the ENVITED-X access roles and categories. A referenced artifact stays optional.
  • The simulation data / documentation / metadata / media links are required of a simulation asset's manifest (envited-x:SimulationManifest) only, no longer of software, service and code assets. An external simulation manifest declares the type to be checked; the fixtures do.
  • A license link no longer needs an IRI. The license is gx:license on the resource description — a list, as in Gaia-X.
  • hasFormat is optional on a domain specification, and description on the software, service and code bases, as their shapes, the leaf domains and Gaia-X state. leakage-test, service and survey define no format.
  • hasSoftwareResource, hasServiceOffering and hasCodeArtifact stay sub-properties of hasResourceDescription, whose range now covers all four kinds of description.

manifest v6

  • ArtifactLink, LicenseLink, ManifestLink: the JSON-LD artifact condition applies to the artifact properties, so the self-reference (hasManifestReference) is exempt by structure.
  • The artifact properties have the range manifest:Link plus their conditions, so membership in ArtifactLink no longer follows from the range in OWL.
  • Manifest is defined by subClassOf; the equivalentClass axioms (one with minCardinality 0) made every individual a Manifest.
  • manifest:isMiscellaneous is a MiscellaneousCategory (it was typed with an undefined class).
  • An empty CID is rejected; the pattern no longer needs a lookahead, which SHACL's regex dialect lacks.
  • width, height and depth are unbounded: the metamodel has no exclusive bound (GAPS G8).

Nested values (all four) — without RDFS inference, an untyped nested value fails its sh:class check; OMB's default validation infers the type from rdfs:range and accepts it.

All four headers — owl:imports of the ontology-header profile and rdf:type ontology-header:OntologyHeader; owl:priorVersion, prov:wasDerivedFrom (releases/tag/v0.6.0), dcterms:references and dcterms:publisher are IRIs; no owl:versionIRI. dcterms:identifier (DCMI range: literal), dcterms:license and dcterms:conformsTo are literals. Contributors are GitHub account IRIs (GAPS G16).

🏷️ Versioning & Compatibility

  • Major (Breaking change that makes existing Self-Descriptions invalid)

manifest v5→v6, envited-x v3→v4, georeference v5→v6, ositrace v6→v7; ontology-header v1 is new. A self-description against the old versions keeps validating against them. The importing domains are repointed without bumps of their own, as when hdmap moved to v6.

🏗️ Type of change

  • New type (non-breaking change which adds a type)
  • Breaking change (Change that would cause existing Self Descriptions not to be accepted anymore by a Federated Catalogue)

🧪 How Has This Been Tested?

just generate                        # all 18 generated artifacts reproduce byte for byte (against the pinned Gaia-X sources)
just validate                        # all checks pass: valid data of 22 domains conforms; 52 negative fixtures behave as recorded
pytest tests/                        # 683 passed, 1 skipped, 1 xfailed
mkdocs build --strict                # after properties_updater and class_page_generator, as cd-docs runs it: no warnings
  • Every valid fixture of every domain conforms, including the 12 repointed importing domains and the new ontology-header domain.
  • Every negative snapshot still fails on its root violation (the missing hasContent, the wrong CID, MIME type, file size or name, the bad lat/lon and bounding box, the missing topic, the manifest-vocabulary role or category, the literal publisher, the missing release). Without --inlined-as-node's sh:node, a holding node reports no "does not conform to shape" violation of its own; the ENVITED-X vocabulary violations report sh:in; manifest fail_04 reports the sh:or instead of the rule's sentence (GAPS G21).
  • The negative fixtures of manifest, envited-x, ositrace, hdmap and openlabel-v2 match their snapshots under different PYTHONHASHSEED values.
  • tests/integration/test_linkml_models.py checks every header against the profile (imports, type, no redeclared metaclass, conformance), the header terms (IRIs on declared annotation properties, no owl:versionIRI), the JSON-LD artifact condition, license categories, MCAP channels, geodetic alternatives and the documented dimension gap with real SHACL validation.
  • The fork features: their PRs' CI and the pipeline branch's suite.

Docs and registry

docs/registry.json, docs/ontologies/catalog.md and docs/ontologies/properties.md are committed with the change, as for every ontology change: the documentation site is built from them, and with the previous registry mkdocs build --strict stops on 59 links to the replaced versions. The LinkML page (formerly under Future) states which domains are generated and how they are built; the contribute guide sends a change to a generated domain to its schema; the Gaia-X pages name envited-x v4.

Next steps

Before v0.6.0 — these change manifest v6 / envited-x v4, which are not released yet:

  • G8 — width, height and depth greater than 0 again: minimum_value: 0 plus a class none_of that excludes maximum_value: 0. A probe accepts 1.5 and 1e-9 and rejects 0, -1 and NaN in SHACL; the OWL stays weaker (G19).
  • R5 — decide whether manifest and envited-x keep separate isPublic, isLicense, isManifest and isMiscellaneous individuals.
  • D5 — decide which URI-valued properties are IRI nodes and which are xsd:anyURI literals.

One fork round and one re-pin, for generator bugs visible in these artifacts:

  • G18 — the ontology-header context maps nodeidentifier to @id.
  • G17 — gen-owl leaves foreign predicates undescribed.
  • G21 — anonymous shapes get an sh:message.
  • G19 — OWL anonymous class expressions keep their presence conditions.

In this repository, not necessarily in this PR:

  • R2 — --per-resource validation loads fixtures and the ontology's named individuals; needed before the comparison harness measures the next domain.
  • cd-release — its push of the registry to main is declined by the branch rules.
  • Mark this PR ready for review and request reviewers.

At release

  • The release must be v0.6.0: the generated headers name releases/tag/v0.6.0 as prov:wasDerivedFrom.
  • The v0.6.0 version-bump PR regenerates the registry for v0.6.0 before the tag (docs/ecosystem/release-process.md); until then the registry names v0.5.1.
  • After the docs deploy, https://w3id.org/ascs-ev/envited-x/ontology-header/v1 resolves through the generic w3id rules.

Known gaps after merge

Recorded in linkml/GAPS.md; none of them blocks the switch-over:

  • D5 — which URI-valued properties are resources (IRI nodes) and which are data (xsd:anyURI literals).
  • D11 — the renamed envited-x classes lose their bare context terms.
  • G8 — no exclusive numeric bounds (manifest dimensions).
  • G16 — contributors by IRI only; native license / conforms_to are literals although DCMI recommends URIs (gen-owl emits rdfs:isDefinedBy and dcterms:license as string literals instead of URIs linkml/linkml#3546); non-linguistic annotations carry @en.
  • G17 — gen-owl describes a reused foreign predicate (label, comment, skos:inScheme).
  • G18 — the ontology-header context coerces its terms to shex:nonLiteral instead of @id.
  • G19 — OWL omits presence conditions inside anonymous class expressions.
  • G20 — OpenLABEL minValue ≤ maxValue is not enforced.
  • G21 — anonymous shapes carry no sh:message.
  • G22 — no version IRI in the metamodel: no permanent ontology IRI with a version IRI per release.
  • R2 — --per-resource validation merges neither fixtures nor ontology individuals.
  • R5 — manifest and envited-x each define isPublic, isLicense, isManifest, isMiscellaneous.
  • R6 — the generated ontologies state no owl:versionIRI; the registry, docs build and class pages use the ontology IRI, and registry_updater logs a warning for each.

✅ Checklist

  • My code follows the modelling guidelines of this project
  • I have performed a self-review of my own changes
  • I have updated the version number in the ontology file (if applicable)
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@jdsika jdsika changed the title feat(linkml): model manifest, georeference, envited-x and ositrace in LinkML feat(ontology)!: generate manifest v6, envited-x v4, georeference v6 and ositrace v7 from LinkML Oct 2, 2026
@jdsika
jdsika force-pushed the feat/linkml-domain-models branch from bace4ad to 4dba4fa Compare October 2, 2026 18:35
@jdsika
jdsika force-pushed the feat/linkml-domain-models branch from 6606a49 to b5f07cc Compare October 14, 2026 15:00
@jdsika jdsika changed the title feat(ontology)!: generate manifest v6, envited-x v4, georeference v6 and ositrace v7 from LinkML feat(ontology)!: generate manifest v6, envited-x v4, georeference v6 and ositrace v7 from LinkML with an ontology-header profile Oct 14, 2026
@jdsika
jdsika force-pushed the feat/linkml-domain-models branch 2 times, most recently from 5f405e3 to 38224a9 Compare October 14, 2026 16:56
…e inference

The fast ABox inference behind per-resource validation applied rdfs3 only when
the object was an IRI. RDF 1.1 Semantics rdfs3 entails the range type for every
non-literal object, and the canonical apply_rdfs_inference already does so
(FILTER(!isLiteral(?object))); the fast path's docstring claims the same rules.

Inline JSON-LD values are blank nodes, so in per-resource mode they were never
typed through rdfs:range, and any sh:class constraint on them failed only in
that mode. Hand-written shapes mostly use sh:node and never noticed; shapes
generated by LinkML use sh:class and fail immediately.

- apply rdfs3 to every non-literal object in ShaclValidator._abox_inference
- add a unit test asserting the fast and the canonical path type IRI and
  blank-node objects alike and never literals

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
… form

Two parts of pySHACL's generic messages change from one process to the next,
so a report snapshot holding them failed on some runs and passed on others:

- an sh:in message lists the allowed values in the order of a Python set,
  which follows string hashing
- a blank node is rendered to a fixed depth and the rendering cached under
  the node alone; a node reached by several paths is cut off
  (<http://recursion.too.deep>) wherever the first path happened to reach it

The report formatter's normalization now sorts the sh:in values and replaces
a rendering that pySHACL cut off with "[ ... ]". A complete rendering is
sorted already and stays as it is, as does a custom sh:message that merely
mentions a list.

The structured violations applied both steps on their own; they now take
them from the same normalization.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
…main with one script

Pins linkml to the tag envited-x/2026.10.14 of the ASCS-eV fork
(536d6f194, the reviewed ENVITED-X pipeline), with the diff-stable extra.
The branch the tag was cut from is rebuilt by force-push; the tag names
one commit.

- scripts/build_linkml_domain.sh runs gen-owl, gen-shacl and
  gen-jsonld-context for one domain with its linkml/<domain>/gen-flags.env:
  always --no-use-native-uris and --diff-stable; it stages the three
  artifacts in a temporary directory, fails on an empty artifact, on an
  unexpanded IRI and on any rule or class expression gen-shacl skips, and
  prints a failing generator's log
- `just generate` and `just generate-domain` build every LinkML domain with
  that script; artifacts/gx/update-from-submodule.sh takes
  deterministic_turtle from diffable_rdf
- openlabel-v2 is built by the script: --no-use-native-uris makes its OWL
  declare the schema.org and Commons IRIs (schema:QuantitativeValue,
  schema:minValue, ...) its SHACL and context already use, instead of local
  openlabel_v2: stand-ins; rules on a class also constrain its subclasses'
  shapes; the header predicates are declared annotation properties
- openlabel-v2 Rule 17 ("DrivableAreaEdge includes EdgeNone") is
  has_member: a bare equals_string on a multivalued slot means every value,
  and the reviewed rule converter skips it; the SHACL is unchanged by it
- three openlabel-v2 snapshots re-recorded: added result paths and the
  schema:QuantitativeValue IRI; each still fails on the same constraint

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Check out submodules/service-characteristics in the Standards & Syntax and
Generate Artifacts jobs: the envited-x and ositrace LinkML schemas import the
Gaia-X LinkML schemas from it, so `just generate` failed there.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
…acts

scripts/compare_artifacts.py (`just verify-linkml <domain>`) compares a
candidate artifact set with the committed one:

- header: the ontology node's annotations
- vocab: classes, properties, the class and property hierarchies,
  owl:equivalentClass axioms, and no unexpanded IRI
- context: the JSON-LD expansion of every test instance
- shacl: verdict and violations per test instance
- probe: adversarial instances derived in RDF space from every committed
  shape, judged by the violations a mutation adds - changed or removed
  constrained values, a second value where at most one is allowed, and a
  removed optional property, which must add no violation on either side
- --dependents also runs the shacl and probe layers on the instances of
  every domain that imports the measured one

Checked against deliberately broken candidates: a dropped sub-property
axiom, a required hasFormat (caught on the leakage-test, service and survey
fixtures through --dependents) and a required cid (caught by exactly the five
cid absence probes).

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
…y headers

A LinkML schema profile, as LinkML's implements/instantiates guide describes
it: the metaclass ontology-header:OntologyHeader declares the header
annotations whose values are resources - owl:priorVersion,
prov:wasDerivedFrom, dcterms:references and dcterms:publisher - each with
range nodeidentifier and implements: [owl:AnnotationProperty], and requires
the release and the publisher.

A schema that imports the profile and instantiates OntologyHeader gets those
annotations as IRIs on declared annotation properties, with no logical axioms
about them. Built with --no-mergeimports and --exclude-imports, an importing
domain keeps the metaclass out of its own vocabulary: its OWL has
owl:imports of the profile and types the ontology OntologyHeader, and the
profile's shapes check that header.

- linkml/ontology-header: the profile; its own header follows it
- artifacts/ontology-header: generated OWL, SHACL and context,
  https://w3id.org/ascs-ev/envited-x/ontology-header/v1 (the generic
  ascs-ev/envited-x w3id rules already resolve it)
- tests/data/ontology-header: a conforming header, one with a literal
  publisher and one without its release
- docs/registry.json, the catalogs, the catalog and properties overviews
  and the README table list ontology-header v1

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
…and ositrace v7 from LinkML

The four ontologies are generated from linkml/<domain>/<domain>.yaml by
`just generate`; their hand-written artifacts are replaced. The prior versions
stay published under their w3id IRIs and are named by owl:priorVersion. Each
construct is modelled by its intent with the LinkML mechanism for it, and the
generators are the only thing that runs; what LinkML cannot state is recorded
in linkml/GAPS.md.

Modelling (linkml/GAPS.md M1-M14):

- dependencies are imported (manifest and Gaia-X by envited-x; envited-x and
  georeference by ositrace) and kept out of the importing artifacts with
  --no-mergeimports / --exclude-imports; gx classes are extended by is_a and
  gx slots inherited by name
- what a nested value - a link, its file metadata, its dimensions - must
  satisfy is a range_expression on the slot that holds it; a class range
  gives sh:class, and a typed nested node is checked by its class's shape
- the JSON-LD artifact condition is a class expression on ArtifactLink and on
  the artifact properties; the self-reference hangs from
  hasManifestReference, which does not carry it
- license, manifest-reference and ENVITED-X vocabulary narrowings sit on the
  owning properties; ENVITED-X access roles and categories are enums whose
  values instantiate manifest's open classes
- filePath and ositrace:validationReport are UriReference, an xsd:anyURI
  literal; iri and conformsTo are IRI nodes
- headers follow the ontology-header profile: no owl:versionIRI (the schema
  id is the version's ontology IRI, and OWL 2 makes the version IRI header
  structure, not an annotation); owl:priorVersion, prov:wasDerivedFrom,
  dcterms:references and dcterms:publisher are IRIs on annotation
  properties; contributors are their GitHub accounts
- name collisions across the import closure get unique LinkML names, with
  class_uri/slot_uri keeping the IRIs and alias keeping the JSON-LD terms

Data-contract changes:

- envited-x: every artifact and referenced link uses the ENVITED-X roles and
  categories; a referenced artifact stays optional
- envited-x: the simulation data / documentation / metadata / media links are
  required of a simulation asset's manifest (envited-x:SimulationManifest)
  only, no longer of software, service and code assets
- envited-x: a license link no longer needs an IRI; the license is gx:license
  on the resource description, a list as in Gaia-X
- envited-x: hasFormat is optional on a domain specification, and description
  on the software, service and code bases, as their shapes and Gaia-X state
- manifest: Manifest is defined by subClassOf; the equivalentClass axioms made
  every individual a Manifest; isMiscellaneous is a MiscellaneousCategory
- manifest: the artifact properties have the range Link plus their
  conditions, so membership in ArtifactLink no longer follows from the range
- manifest: an empty CID is rejected; width, height and depth are unbounded
  (no exclusive bound in the metamodel, GAPS G8)
- without RDFS inference, an untyped nested value fails its sh:class check;
  the default validation types it by rdfs:range

The importing domains move to the new versions in place, without version
bumps of their own: imports, prefixes, fixtures and snapshots. New negative
fixtures cover the closed link vocabulary, a referenced artifact, a
simulation manifest without media and an empty CID. Each re-recorded
snapshot still fails on its root violation; a holding node reports no
"does not conform to shape" violation of its own.

The registry, docs build and class pages use the ontology IRI as version
IRI where none is stated, so the published version IRIs do not change.
linkml/openlabel-v2/GAP_ANALYSIS.md names the current flags.

docs/registry.json and the catalog and properties overviews list the new
versions; the documentation site is built from them.
The LinkML page states which domains are generated and how they are built,
the contribute guide sends a change to a generated domain to its schema, and
the Gaia-X pages name envited-x v4.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
@jdsika
jdsika force-pushed the feat/linkml-domain-models branch from 38224a9 to 6554dd2 Compare October 15, 2026 10:32
@jdsika jdsika mentioned this pull request Oct 21, 2026
6 of 9 tasks
jdsika added a commit that referenced this pull request Oct 21, 2026
* fix(registry): carry the generated timestamp forward so updates are idempotent

update_registry() refreshes `generated` only when the registry content
changed, but it built the new registry without ever carrying the existing
timestamp over. An unchanged registry therefore lost the key entirely: the
first `just registry-update` added a timestamp and a second one removed it
again, so the command never reached a fixed point and reported a change on
every other run.

Carry the existing timestamp forward when the content is unchanged, which is
what "records when the content last changed" was meant to express. The
registry now regenerates byte-for-byte, so the pre-commit hook and CI agree
with the committed file.

Add three regression tests: the timestamp survives an unchanged regeneration,
it is still refreshed when the release tag changes, and two consecutive
write_registry() runs leave the file untouched. The first and third fail
against the previous behaviour.

Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>

* chore(release): bump version to 0.5.2

- Bump the package version to 0.5.2 in pyproject.toml and in the project's
  own entry in uv.lock (uv lock --check passes).
- Regenerate docs/registry.json and config/ontoenv.toml for v0.5.2. The
  release workflow cannot push the registry to main, and the documentation
  and the W3ID artifacts of a release are built from the registry at its
  tag, so the registry has to name the release before the tag is created.
- Release scope: v0.5.2 ships ValidationSession, which reuses parsed
  schemas across validations (#110). Below 1.0.0 the minor version takes
  the place of the major version, so a backward-compatible addition raises
  the patch version and keeps v0.6.0 free for the breaking ontology
  changes still in review (#106, #107).

---------

Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>

This branch has not been deployed

No deployments
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.

SHACL generator: has_member is ignored

1 participant