Skip to content

chore: refresh ENVITED-X integration with reviewed generator features - #14

Draft
jdsika wants to merge 17 commits into
mainfrom
feat/envited-x-pipeline
Draft

jdsika wants to merge 17 commits into
mainfrom
feat/envited-x-pipeline

Conversation

@jdsika

@jdsika jdsika commented May 7, 2026 •

Copy link
Copy Markdown

Purpose

Feature integration branch for downstream ENVITED-X projects: every fork feature PR at its reviewed head, on upstream main a1593d16c (2026-10-23). This PR remains a draft and is not an upstream submission.

Rebuilt 2026-10-23 from upstream main instead of rebased: 15 cherry-picked PR heads plus two integration-only commits, all signed and signed off, each keeping its author. The previous history is kept as the branch backup/envited-x-pipeline-2026-10-15 (416efa809).

Snapshot

Tagged as envited-x/2026.10.23 (signed, on 88e3a8085). This snapshot changes linkml-runtime too (rdf_terms, the canonicalizer), so pin both packages; pinning only packages/linkml pulls linkml-runtime 1.12.1 from PyPI and fails to import:

linkml-runtime @ git+https://github.com/ASCS-eV/linkml.git@envited-x/2026.10.23#subdirectory=packages/linkml_runtime
linkml[diff-stable] @ git+https://github.com/ASCS-eV/linkml.git@envited-x/2026.10.23#subdirectory=packages/linkml

Carried PRs

Fork PR Head Upstream Feature
#16 dcbaabe21 linkml#3716 (approved) propertyNames from inlined-dict key slot constraints
#29 e6a40da1c linkml#4028 class-level boolean expressions alike in SHACL and JSON Schema
#30 baff32593 linkml#4169 (draft) range_expression on each value
#34 796fbfe7e linkml#4170 (draft) has_member
#19 f5cfa5991 linkml#3989 presence-implies-value rules to SHACL-SPARQL
#20 5a673d4c8 linkml#3990 compositional rule translation
#23 170b2fbd8 linkml#3991 rule docs
#31 1aca1173c linkml#4171 instantiates as rdf:type
#32 88e231b42 linkml#4156 (draft) one RDF term kind for URI-like types
#58 0fa8f658b linkml#4157 (draft) declared annotation terms
#59 596bb1dd1 linkml#4158 (draft) annotation property roles
#56 ca8dd1dc1 linkml#4144 "@version": 1.1 in contexts
#27 baa237351 linkml#3993 deterministic-output docs
#4 29098c800 linkml#3446 (draft) --normalize-prefixes
#26 d9d930b98 fork only canonicalization delegated to diffable-rdf

No longer carried: #13 and #24 (merged upstream as linkml#3485 and linkml#3985), #33 and #36 (re-cut as #58/#59), and the old reconciliation for the slot-level sh:pattern (on upstream main now).

Integration reconciliation

Validation (head 88e3a8085)

Known limitations

@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch 9 times, most recently from a705c35 to 3d3a52a Compare May 12, 2026 16:35
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch 3 times, most recently from cab84ad to b2b3dba Compare June 10, 2026 19:42
@rmessaou
rmessaou force-pushed the feat/envited-x-pipeline branch from 97e73d0 to ef7ad85 Compare July 8, 2026 09:02
@jdsika jdsika self-assigned this Jul 11, 2026
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch from 6aa7702 to 233adc3 Compare September 11, 2026 14:23
@jdsika jdsika changed the title chore: consolidated feature branch for ENVITED-X pipeline chore: feature integration branch for the ENVITED-X pipeline Sep 11, 2026
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch from 233adc3 to b8a388e Compare September 11, 2026 14:44
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch from 3026d81 to abd2032 Compare October 2, 2026 12:10
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch from e3081ef to d75f782 Compare October 8, 2026 14:35
@jdsika jdsika changed the title chore: feature integration branch for the ENVITED-X pipeline chore: refresh ENVITED-X integration with reviewed generator features Oct 8, 2026
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch 2 times, most recently from 0ac558f to 536d6f1 Compare October 14, 2026 07:58
jdsika and others added 2 commits October 23, 2026 14:43
…nstraints

For an inlined-as-dict slot whose range class has an identifier/key slot, render the
key slot's string-applicable constraints onto JSON Schema propertyNames (draft-06+)
instead of dropping them. In the inlined-dict form the mapping key is the identifier
value, so the key slot's constraints constrain the keys. JSON object keys are always
strings, so only pattern, enum (equals_string_in) and a string const (equals_string)
are emitted; numeric minimum/maximum, numeric const (equals_number) and allOf are
excluded -- a numeric const would otherwise reject every key. structured_pattern is
honored when materialize_patterns is enabled, consistent with value patterns.
Backward compatible: emitted only when a string-applicable key constraint applies.

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

Includes the documentation for the emitted propertyNames constraint.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
…SHACL and JSON Schema

gen-shacl dropped class-level any_of, all_of, exactly_one_of and none_of,
so a class stating "a code or a name is required" generated shapes that
accepted everything.  gen-json-schema translated them, but only for the
declaring class, and read an absent slot differently depending on where
an expression was nested.

Both generators now give class expressions one semantics, shared in
linkml.generators.common.class_expression:

- A class expression constrains every instance of its class, so a class
  carries the expressions of its ancestors and mixins, translated in its
  own context.
- The specification does not say what a slot condition means for an
  absent slot.  As an SQL CHECK constraint (ISO/IEC 9075) reads a
  condition on null, a condition that doesn't decide whether its slot
  may be absent is unknown for an absent slot.  An instance is invalid
  only when an expression is definitely false, and exactly_one_of holds
  when exactly one member is definitely true.  The operators compose,
  and gen-json-schema no longer rejects an instance meeting only the
  first member of an exactly_one_of while accepting one meeting none.

gen-shacl emits sh:or, sh:and, sh:xone and sh:not (the operators'
exact_mappings, SHACL 4.6) over anonymous member shapes, each in its
"not false" form or, under sh:not, its "definitely true" form, where
such a condition also has sh:minCount 1.  Conditions translate required,
value_presence, the cardinalities, minimum/maximum_value, pattern,
equals_string(_in), equals_number and range; a parameter SHACL allows
once per shape that one condition needs twice moves into an sh:and
member.  An operator whose members use anything else is skipped whole,
with one warning naming the shapes it is missing from.

gen-json-schema builds the same forms with anyOf, allOf, oneOf and not,
resolves conditions on the induced slot, and also enforces cardinalities
(minItems / maxItems, or minProperties / maxProperties for a slot inlined
as a dict, whose values are the values of the dict) and range in
class-level conditions.
value_presence: ABSENT on several slots of one expression now requires
each to be absent rather than not all of them present, in rules too.

An is_a member names a class that every instance of the class carrying
the expression belongs to (the class or an ancestor), that only some
belong to (through a descendant), or that none belongs to, since an
instance belongs to its class and that class's ancestors. Only the
second case checks the instance: sh:class in SHACL, and in JSON Schema,
which carries no type, a reference to the class's definition. Before,
a class inherited its parent's any_of over its own subclasses as sh:class
and $ref to itself, which recursed without end in JSON Schema and, in
SHACL, rejected a node typed only as a subclass.

The slot loop's range dispatch and sh:path computation move into
_add_range and _slot_iri; output for schemas without class expressions
is unchanged.  The compliance tests test_class_any_of and
test_class_any_of_with_required run for SHACL.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
jdsika and others added 4 commits October 23, 2026 14:43
…L and JSON Schema

gen-shacl ignored a slot's range_expression, and gen-json-schema read it
with the two-valued builder of rules, which ignores cardinalities, the
range of a condition, and the slot_usage and aliases of the value class.

Both generators now read it as a class-level expression: each value must
satisfy it as an instance of the range class satisfies a class-level
expression. In SHACL it is an sh:node next to the range's sh:class. On a
class alternative of any_of it applies to that alternative, and in a
slot condition it takes the form of the condition. SHACL skips an
expression it cannot express with a warning, as it skips a class-level
expression.

gen-json-schema applied the value constraints of a slot inlined as a
dict to the dict instead of to each value, which only a range_expression
showed. The compact forms of such a dict, {id: value} and {id: null},
stay out of its reach.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
has_member asks for at least one value of a multivalued slot satisfying
an expression. gen-shacl ignored it. gen-json-schema translated it as
contains, which an absent slot passes but an empty one fails, applied
only the value operators of the expression, and ignored it in
class-level expressions.

Both generators now read a member as one value, which must satisfy the
value operators, range and range_expression of the expression. A slot
without a value has no member, so has_member decides presence. In SHACL
it is an sh:qualifiedValueShape with sh:qualifiedMinCount 1; in JSON
Schema, contains on a required slot, or some value of a dict inlined by
key. In a slot condition the member takes the form of the condition.
SHACL skips a has_member it cannot express with a warning, and both
generators warn about one on a slot that is not multivalued.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
The rules-to-SHACL-SPARQL converter recognised two named patterns.  This
adds the presence-implies-value pattern: a precondition asserting
`value_presence: PRESENT` on one slot, and a postcondition constraining
another slot with `equals_string` or `equals_string_in`.  It reads as
"if the guard slot is present, the target slot must be present and hold
one of the allowed values", and generalises the boolean guard to
arbitrary enum values.  The boolean guard becomes the case
`equals_string: "true"` of this pattern on an xsd:boolean-typed flag.

`equals_string` / `equals_string_in` compare strings.  The metamodel
defines them for slots of range string; enums and types with datatype
xsd:string are treated alike, and a slot without a range takes the
schema's default_range.  On an xsd:boolean-typed slot each value must be
a lexical form of xsd:boolean (XML Schema 1.1 Part 2 section 3.3.2.2:
true, false, 1, 0) and denotes that boolean.  A rule applying them to
any other range is skipped with a warning: its RDF values are typed
literals or IRIs that equal no string.  Built-in type names resolve
without importing linkml:types, as in the main slot loop; the built-in
name of `curie` was misspelt there and is corrected.

Values are compared with `=`, spelled out as the disjunction SPARQL 1.1
section 17.4.1.9 defines `IN` to be, so the RDF 1.1-identical plain and
xsd:string literal forms both match and booleans compare by value;
rdflib evaluates `IN` and triple-pattern constants by term identity.
COALESCE turns the type error that RDFterm-equal raises for
incomparable literals into false, so a spec-conformant engine reports
such a value instead of dropping the row.  Queries are DISTINCT and
project ?path (and ?value where there is one), so each result names the
property and the offending value.  A value containing a backslash
followed by "u" or "U" is split with CONCAT, since codepoint escapes are
replaced before a query is parsed.

The exclusive-value pattern takes the precondition `has_member:
{equals_string: V}`, which states "V is one of the values". A bare
`equals_string: V` applies to every value of the slot, as the
specification applies a slot constraint to all members of a collection
and as gen-json-schema reads it, so it no longer matches this pattern:
such a rule is skipped with a warning naming has_member. The pattern
gets the same range check and value comparison, and its
maximum_cardinality > 1 form tests HAVING on the COUNT aggregate:
expressions projected by SELECT are not visible to HAVING (SPARQL 1.1
section 18.2.4.2), so `HAVING (?count > N)` never held and the
constraint never fired.

A rule applies to all members of its class, so a class shape now
carries the rules of its ancestors and mixins, as the JSON Schema
generator applies them.  Each rule is translated in the inheriting
class's context, where slot_usage may refine the slots it references.
Classes sharing a class_uri share one shape, which carries each rule
once.  With `open_world: true` "the postconditions may be omitted in
instance data", so an absent target is no violation.

Skip warnings name the rule by its position and its title, or its
description without one.

A rule whose conditions carry any operator beyond what a pattern
translates, or name an unknown or identifier slot, is skipped rather
than partially translated.  Each problem with a rule is logged once as a
warning naming the declaring class, the rule's position and the class
shapes it affects.  The metadata fields the operator accounting ignores
are derived from the metamodel.  Slot resolution goes through induced
slots, so slot_usage overrides, slot_uri overrides and alias-form keys
resolve to the IRI sh:path emits.

Behaviour changes for existing schemas: an exclusive-value rule with a
bare equals_string precondition is skipped (write has_member:
{equals_string: V}); the boolean guard rejects a string "true" on an
xsd:boolean flag and skips a flag whose type has another datatype IRI
(main compared with str()); exclusive-value rules with
maximum_cardinality > 1 now fire; inherited rules are now enforced on
subclass shapes; problems with rules are logged at WARNING instead of
DEBUG; rule results carry sh:resultPath and sh:value; sh:message follows
the rule's in_language or default_language.

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

A rule that no named pattern matches is now composed from the operators
it uses: each precondition becomes SPARQL filters on $this, and each
postcondition slot gets its own constraint, the union of the ways to
violate that slot's condition, binding the offending value to ?value
(SHACL sections 5.3.1 and 5.3.2).  A rule P -> A and B is violated
exactly when P and not A, or P and not B, holds.

A condition may use value_presence, required, equals_string,
equals_string_in, equals_number, pattern, minimum_value, maximum_value,
the slot-level any_of, all_of, exactly_one_of and none_of over those
value tests, a range_expression on the slot's class (nested to any
depth) and has_member.  The semantics follow the JSON Schema
generator's if/then:

- presence: value_presence decides, then required, then a default: a
  precondition requires its slot, a postcondition unless the rule is
  open_world, an inner condition of a nested expression never;
- value operators and range_expression apply to every value of the slot
  (05validation.md: "all members"), has_member to some value;
- equals_string(_in) only on string, enum and boolean slots (the named
  patterns' range check); equals_number and the bounds only on the
  numeric datatypes of SPARQL 1.1 section 17.1, guarded with isNumeric,
  and only finite numbers; pattern as REGEX on the value's string form,
  as sh:pattern reads it (SHACL section 4.4.2);
- a value is one term, so each member of a slot-level boolean is true or
  false, and the operators are those of 05validation.md: exactly_one_of
  counts the members that hold.

Any other operator, or a rule with empty pre- or postconditions, skips
the rule with one warning, so a rule is never partially translated.  A
rule skipped part-way no longer reports the notes of the abandoned
translation.

The compliance suite now runs SHACL in the rule tests, and every rule of
test_preconditions and test_preconditions_combos is translated, so each
instance is checked.

Co-authored-by: jdsika <carlo.van-driesten@vdl.digital>
Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch from 416efa8 to d6ef9ea Compare October 23, 2026 12:53
jdsika and others added 11 commits October 23, 2026 15:13
The SHACL generator translates LinkML rules into sh:sparql constraints,
but its documentation did not mention them.

Describe the two named patterns and the composed translation with the
operators it supports and one constraint per postcondition slot, the
semantics shared with the JSON Schema generator (presence, every value
versus has_member, value comparison, numeric bounds), inheritance,
open_world, --no-emit-rules and the skip warnings, with an example
generated by the generator.  Replace the dangling "See above for
implementation status" in the rules section of advanced.md with links
to the JSON Schema and SHACL generator pages.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Translate explicit instantiates declarations to rdf:type on the emitted
schema resource through one shared helper. Apply the mapping consistently
to schemas, classes, slots, types, enums, and non-literal permissible values.
Literal permissible values cannot be RDF subjects and produce a warning.

Membership types the schema element itself, not its ordinary data instances,
and does not become subclass inheritance. Existing enum representation and
OWL class/individual punning remain intact. No additional flag is needed
because the schema already declares the relationship.

Cover CURIEs, absolute and minted IRIs, literal values, representation modes,
and the distinction between gen-rdf metamodel serialization and gen-owl
ontology assertions. Document the LinkML and OWL specification references.

A resource in the OWL 2 reserved vocabulary (Structural Specification
section 2.4), such as a type emitted as xsd:integer without type objects,
cannot be an OWL 2 DL individual; its instantiates is not asserted, with
a warning. A value that is neither a CURIE nor an IRI would become a
relative IRI and is skipped with a warning too.

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

A value of a type is an IRI, a blank node or a literal according to one rule,
linkml_runtime.utils.rdf_terms.type_term, which gen-jsonld-context, gen-owl,
gen-shacl, gen-shex, gen-yarrrml and the RDF dumper and loader now share:

- uri, uriorcurie and the types derived from them are IRIs;
- objectidentifier is an IRI, and nodeidentifier and types with
  uri: rdfs:Resource are IRIs or blank nodes;
- every other type is a literal of its datatype, including a type derived
  from string with uri: xsd:anyURI, which keeps URI references as text.

The rule resolves the inherited, expanded datatype, so its spelling does not
matter, and recognises uri and uriorcurie by name and definition rather than
by from_schema, which merging imports rewrites. A datatype with an undeclared
prefix is an error (xsd, shex and rdfs are known). The dumper writes the
inherited datatype of a derived type and rejects relative values of IRI types
(RDF 1.1 Concepts §3.2); the loader accepts IRIs for node-kind ranges in
strict mode, keeps uri values complete, compacts uriorcurie values to CURIEs
and keeps blank-node labels. gen-shacl gives sh:in values and sh:defaultValue
the slot's term kind.

The --xsd-anyuri-as-iri option of gen-owl and gen-jsonld-context is
deprecated in 1.13.0, has no effect, and is scheduled for removal in 1.14.0.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
…rm of their range

An annotation whose tag a metaclass declares through instantiates gets the RDF
term that the slot's range gives a slot value (linkml_runtime.utils.rdf_terms):
nodeidentifier, uri and uriorcurie values are IRIs or blank nodes, string
values are text with the resolved language, curie values are xsd:string
literals, and an enum-ranged slot writes the permissible value's text. gen-owl
and gen-shacl share the rule through linkml.generators.common.annotations;
without a declaration both keep their earlier behaviour, and metaclasses that
declare a tag with different terms are an error.

gen-owl applies the same rule to native metadata, so see_also, source and the
mappings are IRIs by the metamodel's ranges rather than by a name check, and
created_on is an xsd:dateTime literal. A link value that is neither an IRI
nor a CURIE, such as a bare element name in see_also, is written as a plain
literal with a warning: a relative IRI would make the whole graph
non-standard RDF and push it onto the canonicalizer's rdflib fallback. OWL 2 §10.1 admits IRIs, anonymous
individuals and literals as annotation values.

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

A slot that implements owl:AnnotationProperty, or whose property IRI is one of
the built-in annotation properties of OWL 2 (Structural Specification §5.5), is
an annotation property: it is declared as such, produces no class restriction
or cardinality axiom (annotations have no logical meaning, §10.1), and its
domain, datatype or class range and super-properties are emitted as annotation
axioms (§10.2). Built-in properties get neither a declaration nor axioms. The
predicate of a declared annotation whose metaclass slot has the role is
declared too.

A slot that implements two roles is an error (§5.8.1), as is an inverse or a
property characteristic, a unique key, a type designator, or a rule or
class-expression condition on an annotation property.

A super-property must have the role of its sub-property: a sub-property
axiom relates two annotation properties (§10.2.1) or two logical
properties (§9.2.1, §9.3.1), so an annotation property under a data or
object property, or the reverse, is an error.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Generated contexts use JSON-LD 1.1 constructs: scoped contexts on enum
slots and class-level range overrides, and @Prefix. A JSON-LD 1.0
processor ignores them silently, so datatype coercions are lost. Emit
"@Version": 1.1 first in every generated context. It makes a JSON-LD 1.0
processor stop (JSON-LD 1.1 §1.7), and a JSON-LD 1.1 processor then
requires 1.1 processing (JSON-LD 1.1 API §4.1.2). gen-jsonld embeds the
declaration with the generated context.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Describe in the gen-owl documentation what RDFC-1.0 canonicalization
guarantees for standard RDF, why its sequential blank-node numbering can
still produce noisy diffs, and that --diff-stable reduces that churn
without guaranteeing minimal diffs. Note that the option also exists on
gen-rdf, gen-shacl and gen-shex --format rdf, and that graphs that are not
standard RDF, such as gen-shacl --include-annotations output with a
non-CURIE annotation tag, are serialized by rdflib with a warning, where
--diff-stable has no effect.

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

Add an opt-in --normalize-prefixes flag to the OWL, SHACL, JSON-LD
context and JSON-LD generators that renames non-standard prefix aliases
to their well-known names. The names come from the prefixmaps
linked_data context (curated) layered over its merged context, and where
rdflib binds a namespace by default, from rdflib, which serializes the
output (geo, schema, wgs where prefixmaps says geosparql, sc, WGS84).

- Both http://schema.org/ and https://schema.org/ map to 'schema'.
- OWL and SHACL share normalize_graph_prefixes(): phase 1 renames
  schema-declared prefixes, phase 2 cleans up runtime-injected bindings
  without overwriting HTTPS bindings with HTTP variants.
- A standard name already declared for another namespace is skipped
  with a warning.

The flag defaults to off, preserving existing behaviour, and only the
generators that honour it offer it: gen-rdf writes the schema's class_uri
and prefix declarations as literal data and gen-shex's RDF uses rdflib's
names already, so neither emits an alias to rename. The documentation
for the flag is in docs/generators/owl.rst.

test_csv_limit set csv.field_size_limit(20) without restoring it, so any
later test on the same worker that read prefixmaps' CSV data failed; it
now restores the limit.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
The RDF canonicalizer in linkml_runtime was extracted into diffable-rdf,
and the two copies diverged. Calling the library fixes nine defects of
the in-tree copy, each asserted against both entry points in
test_rdf_canonicalize_defects.py:

- a base IRI ending in # no longer rewrites every IRI sharing its prefix
- a shared rdf:List tail is no longer duplicated
- N-Triples refuses a relative IRI instead of writing a file its own
  parser rejects; N-Triples allows only absolute IRIs
- literals containing U+2028 and other line separators survive sorting
- degraded RDF/XML, degraded Turtle and JSON-LD are byte-identical
  across processes
- every format ends with exactly one newline
- a Dataset is refused instead of silently flattened

diffable-rdf becomes a required dependency of linkml-runtime. It needs
only rdflib and pyoxigraph, which the runtime already requires. The
diff-stable extras remain as aliases. Turtle output of the four RDF
generators is unchanged.

The library logs degraded output; the runtime re-emits those records as
RDFCanonicalizationWarning so they stay visible without logging
configuration.

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

Consumer checks for features that meet on the integration branch: an any_of
branch keeps its pattern next to a nested range_expression after RDF
serialization, in both RDF label modes; and one OWL resource keeps its
metaclass membership (instantiates) next to its declared IRI-valued
annotation, while a type keeps its membership (gen-owl writes no annotations
on types).

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
A separate job resolves the pinned OWLAPI 5.5.0 tree with Maven and runs a
Java probe on gen-owl output: OWL 2 DL profile, no unparsed triples, and the
expected annotations kept through a round trip. It checks the documented
DCMI example in four modes and a schema where metaclass membership, a
declared annotation, annotation properties with a super-property and a
built-in IRI, and an ordinary data property meet.

Both profile tests are strict expected failures: gen-owl writes metadata
annotations (dcterms:title, skos:definition, skos:inScheme,
linkml:permissible_values) without declaring their annotation properties,
and asserts membership in external classes without declaring them, which
upstream main does for any schema too. They fail loudly once the
declarations exist.

Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
@jdsika
jdsika force-pushed the feat/envited-x-pipeline branch from d6ef9ea to 88e3a80 Compare October 23, 2026 13:13
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.

2 participants