feat(openlabel): add structural LinkML schema for ASAM v1 JSON format#81
Open
jdsika wants to merge 7 commits into
Open
feat(openlabel): add structural LinkML schema for ASAM v1 JSON format#81jdsika wants to merge 7 commits into
jdsika wants to merge 7 commits into
Conversation
jdsika
force-pushed
the
feat/openlabel-v2-schema
branch
from
June 15, 2026 20:23
1ff9a15 to
c3100a5
Compare
- Add openlabel-v2-schema.yaml: 12 classes, 4 enums, 227 TagTypeEnum values - Generate JSON Schema artifact (openlabel-v2.schema.json) via gen-json-schema - Add sync_tag_type_enum.py script to keep vocabulary in sync with ontology - Add convert_openlabel_v1_to_v2.py script for v1-to-v2 format conversion - Add 19 pytest tests covering schema generation, validation, sync, and converter - Wire pre-commit hook for automatic TagTypeEnum synchronization - Extend Makefile generate target to produce JSON Schema when schema model exists - Document unified modeling strategy in SCHEMA_MODELING.md Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>
jdsika
force-pushed
the
feat/openlabel-v2-schema
branch
from
June 15, 2026 20:40
c3100a5 to
629d50c
Compare
Make the LinkML structural schema faithfully validate the ASAM OpenLABEL v1.0.0 scenario tagging file format and its full tag vocabulary. Structural format (openlabel-v2-schema.yaml): - Accept numeric vec.val arrays (ranges/sets such as [3.4, 3.7] and [2, 3]) via standard LinkML any_of[float, string]; items were previously string-only and rejected the spec's own section 8.2.5 examples. Removing the schema-wide default_range lets any_of generate a clean, draft-independent anyOf (no helper class, no post-processing). - Allow tag_data as object OR string, matching ASAM oneOf[object, string]. - Constrain schema_version to "1.0.0", boolean/text type to "value", and drop the fabricated "set" vec type so the schema matches the ASAM schema exactly. - Generate the JSON Schema deterministically with gen-json-schema --deterministic --indent 3 (consistent with the OWL/SHACL/context targets), so output is stable and the pretty-format-json hook is a no-op. Tag vocabulary (openlabel-v2.yaml, scripts/sync_tag_type_enum.py): - Add 15 intermediate ODD/Behaviour category classes (SceneryJunction, EnvironmentWeather, BehaviourMotion, DrivableArea*, Geometry*, ...) and 10 mid-level enum values (RoundaboutLarge/Mini/Normal/Compact/Double and the uniform/variable signs) so every ASAM v1 tag class is a valid tag.type. - Derive TagTypeEnum from all openlabel_v2-namespaced classes; the vocabulary grows 227 -> 256 and now covers all 243 ASAM tag classes (0 missing). - New classes carry ISO 34503:2023 comments and v1 PAS 1883 notes, matching the model's ISO-migration citation convention. Tests and tooling: - Validate spec chapter-8 examples against BOTH the authoritative ASAM schema and the LinkML schema, and assert the vocabulary covers every ASAM tag class (replaces a tautological count test). Suite grows 19 -> 44 tests. - Correct the equivalence/stricter claims and add a scope note in SCHEMA_MODELING.md. Regenerated OWL/SHACL/JSON-LD context/JSON Schema/PROPERTIES; SHACL conformance suite and 44 unit tests pass; full pipeline is idempotent. Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>
jdsika
force-pushed
the
feat/openlabel-v2-schema
branch
from
June 16, 2026 11:03
bb363db to
7ddb7f7
Compare
4 tasks
Addresses all findings from the PR #81 review. Blocking (CI red): - Reflow `extract_tag_value` signature in convert_openlabel_v1_to_v2.py so `ruff format` no longer rewrites the file (line exceeded the 88-char limit, which failed the Standards & Syntax "unexpected modified file" gate). Medium: - Add `encoding="utf-8"` to all file reads in sync_tag_type_enum.py and convert_openlabel_v1_to_v2.py (and the schema-reading test). The semantic model contains em-dashes; bare open() crashed on Windows (cp1252) — breaking the sync-tag-type-enum pre-commit hook and the converter for Windows users. - Disclose the stricter `tag.ontology_uid` requirement in SCHEMA_MODELING.md (added equivalence-table row + note) and back it with a LINKML_STRICTER test. Low / nits: - Converter: accumulate repeated class+slot values into a list instead of silently overwriting (last-wins data loss); add coverage. - Converter: collapse the identical if/else and four identical class-building branches. - Converter: make `--pretty` a real toggle via argparse.BooleanOptionalAction (`--no-pretty`); previously it could never be False. - Add a SHACL-conformance test that validates converter output against the openlabel-v2 shapes (the converter's stated purpose), not just @context/@type. - sync_tag_type_enum.py: rewrite the TagTypeEnum end-detection to be position-independent (no longer relies on TagTypeEnum being the last enum). - Docs: fix `gen_tag_type_enum.py` -> `sync_tag_type_enum.py` reference and document the structural-schema `$id` namespace choice in SCHEMA_MODELING.md. No generated artifacts change; TagTypeEnum sync remains idempotent. Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
Add an ontology-independent harness that programmatically proves the
LinkML-generated JSON Schema faithfully re-models the normative ASAM
OpenLABEL v1.0.0 JSON Schema, within the scenario-tagging scope.
The proof tests a falsifiable refinement relation L = A|τ refined by Δ,
relaxed by Λ, via three pillars:
- scope projection by $ref reachability (10 in-scope defs, 36 out of scope,
derived not asserted);
- a structural gap table classifying every (def, aspect) as
EQUIVALENT / REFINEMENT / OUT_OF_SCOPE / LOOSER / UNMAPPED;
- a differential oracle over a mutation + property-based corpus
(hypothesis-jsonschema) that fails the soundness gate on any instance L
accepts but A rejects for a reason outside the declared, bounded Λ.
The oracle surfaced two real, bounded LinkML-codegen relaxations the curated
example test had missed: dict-key naming (open additionalProperties vs ASAM
patternProperties) and explicit-null on optional fields (type ["t","null"]).
The null relaxation is now ELIMINATED by generating this domain's JSON Schema
with --no-include-null (scoped per-domain); dict-key naming remains the sole
declared relaxation Λ = {key_pattern}.
- add scripts/schema_refinement_prover.py (reusable across migrations)
- add linkml/openlabel-v2/proof_spec.yaml + proof_seeds/ (chapter-8 examples)
- add tests/unit/test_schema_refinement.py (CI gate; globs linkml/*/proof_spec.yaml)
- generate linkml/openlabel-v2/REFINEMENT_PROOF.md (verdict: PROVEN)
- add scripts/gen_json_schema.py wrapper exposing include_null, and
linkml/openlabel-v2/jsonschema.genopts (--no-include-null) for per-domain opts;
wire the Makefile JSON-Schema step through the wrapper
- regenerate artifacts/openlabel-v2/openlabel-v2.schema.json without null unions
(null_optional dropped from allow_looseness so the gate guards against regress)
- correct SCHEMA_MODELING.md: point to the machine-checked proof; reclassify the
key-pattern difference from "cosmetic" to declared relaxation Λ1; record Λ2 as
resolved; add the toolchain note (regeneration needs the submodule-pinned linkml)
- add hypothesis + hypothesis-jsonschema to the dev extra
- pin the dev linkml dependency to the exact submodule commit (b2b3dba5) instead
of the branch, so CI and the submodule cannot drift and regenerate divergent
artifacts (e.g. dropped format:uri)
- register a 'slow' pytest marker and mark the proof suite, so a fast lane
(pytest -m 'not slow') can skip the ~5-min property-based run without
weakening the gate
Verified: make generate DOMAIN=openlabel-v2 reproduces the artifact through the
wrapper with no collateral changes to OWL/SHACL/context; 58 tests pass.
Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
6 tasks
…efinement Bumps linkml to the feat/envited-x-pipeline SHA carrying the two upstreamed jsonschemagen features (ASCS-eV/linkml #15 --include-null/--no-include-null, #16 propertyNames from inlined-dict key-slot constraints) and uses them to close two of the three disclosed relaxations in the openlabel-v2 structural model. The refinement proof now reports L = A|τ refined by Δ, relaxed by Λ = {simple_dict_object_form} (down from {key_pattern}); 0 undisclosed looseness, 0 structural LOOSER rows. Closed relaxations (now regression-guarded, omitted from allow_looseness): - key_pattern: `pattern` on the uid identifier slots -> propertyNames, plus `--closed` (root additionalProperties:false). - null_optional: native `--no-include-null`. Model correctness fix: - tag.resource_uid was modeled as a string id-ref; ASAM defines it as an id->string map. Remodeled as an inlined simple-dict (ResourceUid: uid + identifier_in_resource) with a patterned key. Remaining declared relaxation (Λ3): inlined simple-dict always also accepts the object form for its value, where ASAM allows only the bare string. A LinkML expressivity limit, not a model defect; disclosed and recognized by the prover. Changes: - pyproject.toml + submodule: pin linkml to 97e73d0f (feat/envited-x-pipeline). - linkml/openlabel-v2/openlabel-v2-schema.yaml: uid patterns on TagEntry/OntologyEntry; resource_uid remodeled as an inlined id->string map. - linkml/openlabel-v2/jsonschema.genopts: `--no-include-null --closed`. - Makefile: JSON-Schema generation back through native gen-json-schema + per-domain genopts; delete scripts/gen_json_schema.py (no longer needed — native CLI flag). - regenerate artifacts/openlabel-v2/openlabel-v2.schema.json (closed root, no null, propertyNames on dict containers). - scripts/schema_refinement_prover.py: recognize simple_dict_object_form looseness. - linkml/openlabel-v2/proof_spec.yaml: allow_looseness=[simple_dict_object_form]; document closed relaxations. - tests/unit/test_schema_refinement.py: assert key_pattern/null closed. - regenerate REFINEMENT_PROOF.md; update SCHEMA_MODELING.md. Signed-off-by: jdsika <carlo.van-driesten@vdl.digital>
…type - Add `--no-use-native-uris` to the `gen-owl` invocation in the Makefile so `QuantitativeValue`, `minValue`/`maxValue` and `hasLowerBound`/`hasUpperBound` are emitted with their declared mapped IRIs (`schema:` / `cmns-q:`) instead of native `openlabel_v2:` IRIs. openlabel-v2 is the only LinkML domain, so the change is scoped to it. - Regenerate `artifacts/openlabel-v2/openlabel-v2.owl.ttl`, `shacl.ttl`, `context.jsonld`, `schema.json` and `PROPERTIES.md` with the pinned linkml (`97e73d0`). - Remove redundant nested `@type` (AdminTag/Behaviour/Odd/RoadUser) from all openlabel-v2 valid and invalid test data. The container types are now inferred via RDFS `rdfs:range` on blank nodes (leveraging blank-node RDFS inference). - Convert `schema:QuantitativeValue` nodes to the bare `QuantitativeValue` context term and simplify each instance `@context` to the single base context string, keeping the inline `xsd` prefix only where typed literals still require it (fail02, fail04). - Add `domain: QuantitativeValue` to the `minValue`/`maxValue` LinkML slots so `gen-owl` emits `schema:minValue`/`schema:maxValue rdfs:domain schema:QuantitativeValue`. This lets RDFS infer the `QuantitativeValue` type from the presence of `minValue`/`maxValue`, so the redundant `@type: QuantitativeValue` is removed from every valid node that carries a range. `fail09` (a QuantitativeValue with no min/max, testing the missing-required violation) intentionally keeps its explicit `@type`. - Regenerate the `fail10`/`fail12` `.expected` fixtures (the other ten are unchanged; fail10 loses only noisy inferred `owl:Restriction` dumps). - Add `fail13_quantitativevalue_inferred_missing_max` — an invalid instance whose `QuantitativeValue` carries `minValue` but no `@type` and no `maxValue`. It proves the type is recovered purely by RDFS inference (from `minValue`) and that the shape's required-`maxValue` constraint still fires (the `.expected` shows exactly `maxValue (QuantitativeValue)`, not a "not a QuantitativeValue" error). - Replace `trafficAgentTypeValue`'s `any_of: [RoadUserHumanEnum, RoadUserVehicleEnum]` with a single combined `TrafficAgentTypeEnum` (explicit union of all human + vehicle permissible values). With a single enum range, `gen-jsonld-context` restores the `@type: @vocab` coercion and `gen-shacl` emits one `sh:in (...)`, so the data drops the verbose `[{"@id": "openlabel_v2:VehicleCar"}, ...]` form for bare terms `["VehicleCar", "HumanPedestrian"]` — consistent with every other enum slot. `fail11` keeps its two invalid terms (now bare) and still fails on the enum. - Dedupe `TagTypeEnum` in `scripts/sync_tag_type_enum.py`: the structural `TagTypeEnum` is a set of unique tag-type names, but a permissible value may appear in several enum families (the combined `TrafficAgentTypeEnum` repeats the `RoadUser*Enum` values). Emit each value once — otherwise `gen-json-schema` aborts with a duplicate-key error. The synced `openlabel-v2-schema.yaml` gains only the `trafficAgentTypeValue` category slot, and `schema.json` lists it as a valid tag type. Validation: `python -m src.tools.validators.validation_suite --domain openlabel-v2` passes — all valid instances conform and all 13 invalid instances fail as expected against the regenerated fixtures. docs: add the LinkML fork & upstream-PR workflow guide (`.github/instructions/linkml-fork-workflow.md`) and reference it from `AGENTS.md`, `CLAUDE.md` and `.github/copilot-instructions.md`. Signed-off-by: Carlo van Driesten <carlo.van-driesten@bmw.de>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a structural LinkML schema (
openlabel-v2-schema.yaml) that models theASAM OpenLABEL v1.0.0 scenario tagging JSON file format and generates a JSON
Schema that validates both the file structure and the tag vocabulary —
something the original ASAM schema does not do (it accepts any string as
tag.type).This complements the existing semantic model (
openlabel-v2.yaml, which alreadygenerates OWL/SHACL/JSON-LD context). The two models share a single source of
truth via
TagTypeEnum, the list of validtag.typevalues derived from theontology vocabulary.
The schema and vocabulary were hardened against an adversarial audit that
validated outputs directly against the authoritative ASAM JSON Schema and
scenario tagging ontology (see "Audit hardening" below).
Architecture
openlabel-v2.yamlopenlabel-v2-schema.yamlThe bridge is
TagTypeEnum— 256 validtag.typevalues derived from theontology's classes, slots, and enum values. Changes to the ontology flow into the
JSON Schema when the enum is regenerated (
scripts/sync_tag_type_enum.py, wiredas a pre-commit hook).
Changes
New files
linkml/openlabel-v2/openlabel-v2-schema.yaml— structural model (12 classes,5 enums) of the
openlabelroot,metadata,ontologies,tags,tag_data,and the generic
boolean/num/text/vecdata containers.artifacts/openlabel-v2/openlabel-v2.schema.json— generated JSON Schema artifact.linkml/openlabel-v2/SCHEMA_MODELING.md— architecture, scope, and afunctional-equivalence comparison against the ASAM schema.
scripts/sync_tag_type_enum.py— regeneratesTagTypeEnumfrom the semanticmodel (all
openlabel_v2tag classes + admin tags + enum/boolean slots).scripts/convert_openlabel_v1_to_v2.py— converts plain ASAM v1 tagging JSONinto JSON-LD using the v2 context (for SHACL validation / semantic search).
tests/unit/test_openlabel_schema.py— 48 pytest tests (generation, validation,cross-schema equivalence, vocabulary faithfulness, sync, converter).
Modified files
linkml/openlabel-v2/openlabel-v2.yaml— added 15 intermediate ODD/Behaviourcategory classes and 10 mid-level enum values (see Audit hardening, B2); all new
tags carry ISO 34503:2023
comments:and v1 PAS 1883notes:, matching themodel's ISO-migration convention. Also adds
domain: QuantitativeValueto theminValue/maxValueslots and the combinedTrafficAgentTypeEnumrange ontrafficAgentTypeValue(see "JSON-LD data simplification" below).tests/data/openlabel-v2/**— valid and invalid instances simplified (nested@typeremoved,@type: QuantitativeValuedropped, bare enum terms);fail10/fail12.expectedregenerated; newfail13regression test + catalog entry.artifacts/openlabel-v2/*— regenerated OWL, SHACL, JSON-LD context, JSONSchema, and PROPERTIES.md.
Makefile— newgen-json-schemastep (auto-detects a*-schema.yamlmodel)using
--deterministic --indent 3and the same trailing-newline normalizationas the OWL/SHACL/context targets.
.pre-commit-config.yaml—sync-tag-type-enumhook; registry/properties/readmetriggers extended to
schema.json.submodules/asam-openx-standards— bumped to include the extracted ASAMOpenLABEL v1.0.0 spec/ontology/JSON-schema used as the normative reference.
Audit hardening
The original structural schema was reviewed against the ASAM OpenLABEL v1.0.0
spec, JSON Schema, and tagging ontology. The following findings were fixed:
vec.valwas string-only; rejected the spec's own numeric ranges/sets ([3.4, 3.7],[2, 3])any_of[float, string](clean, draft-independentanyOf)RoundaboutLarge("large roundabout"),SceneryJunction, all mid-hierarchy classesvectest masked B1 with string valuestag_datastring form (ASAMoneOf[object,string]) was droppedany_of[TagData, string]schema_version,boolean/texttypeunconstrained; fabricatedsetvec typeconst: "1.0.0",ValueOnlyTypeEnum, removedsetgen-json-schema --deterministic --indent 3(consistent with OWL/SHACL/context; LinkML is pinned, sometamodel_versionis stable)SCHEMA_MODELING.mdontology_uidis intentionally kept required on tags: every scenario tagreferences its ontology (spec 8.2.1), all spec examples include it, and making it
optional triggers a LinkML simple-dict shorthand (bare-string tags) that ASAM
forbids.
Functional equivalence (verified)
11 spec-valid chapter-8 examples validate against both the authoritative ASAM
JSON Schema and the LinkML-generated schema (minimal,
RoundaboutLarge, numericvecrange/set, stringvecset,numvalue/min,tag_datastring, ontology-as-string, boundary include, admin text). The LinkML schema is stricter only where
it adds value: it rejects unknown
tag.type, non-1.0.0schema_version, thefabricated
vecsettype, bare-string tags, and non-number/stringvec.valitems — all of which the ASAM schema accepts. The
vec.valnumber|stringconstraint was verified to hold under both JSON Schema draft-07 and 2019-09.
Refinement proof (programmatic)
The equivalence claims above are additionally backed by a falsifiable,
programmatic proof that the LinkML-generated JSON Schema faithfully re-models
the normative ASAM OpenLABEL v1.0.0 JSON Schema within the scenario-tagging
scope — plus a reusable harness for any future JSON-Schema → LinkML migration.
Full JSON-Schema equivalence is undecidable, so instead of cherry-picked
examples we prove a refinement relation:
A— normative ASAM schema;A|τ— its tagging projection, derived by$refreachability from
metadata/ontologies/tags(10 in-scope defs, 36 out ofscope — computed, not asserted).
L— the LinkML-generated schema.Δ— enumerated intended strengthenings (256-value vocabulary enum, requiredontology_uid, closed objects,schema_versionconst).Λ— enumerated, disclosed relaxations whereLis knowingly looser.Three pillars, all in
scripts/schema_refinement_prover.py:(def, aspect)classifiedEQUIVALENT / REFINEMENT / OUT_OF_SCOPE / LOOSER / UNMAPPED, each justified.
(
hypothesis-jsonschema) validated against both schemas; the soundness gatefails on a single instance
Laccepts thatArejects for a reason outsideΛ.Closing the gaps the oracle found
The oracle surfaced two real relaxations the example-based tests missed. Both
are now closed using two
jsonschemagenfeatures upstreamed to the pinnedlinkml fork (ASCS-eV/linkml #15
--include-null/--no-include-null,#16
propertyNamesfrominlined-dict key-slot constraints), and are regression-guarded: they are
intentionally not listed in
allow_looseness, so the gate fails if eitherever reappears.
null_optional— LinkML codegen emitted optional fields astype: ["t","null"], soLaccepted explicitnullwhere ASAM forbids it.Closed natively with
--no-include-null, scoped to this domain vialinkml/openlabel-v2/jsonschema.genopts.key_pattern— ASAM restricts dict keys (tag/ontology uids) tonumeric/UUID patterns;
Laccepted any key. Closed bypatternon the uididentifier slots (emitted as
propertyNames) plus--closed(root
additionalProperties: false).A model-correctness fix fell out of the same pass:
tag.resource_uidwasmodeled as a string id-ref, but ASAM defines it as an id → string map. It is
remodeled as an inlined simple-dict (
ResourceUid:uid+identifier_in_resource) with a patterned key.The single remaining declared relaxation is Λ = {simple_dict_object_form}:
a LinkML inlined simple-dict always also accepts the object form
{"identifier_in_resource": ...}for its value, where ASAM allows only thebare string. This is a LinkML expressivity limit (an inlined simple-dict cannot
be restricted to scalar-only values), not a model defect; it affects only the
optional
resource_uidvalue form, is disclosed inproof_spec.yaml, and isrecognized as a category by the prover.
Result:
linkml/openlabel-v2/REFINEMENT_PROOF.md— PROVEN (sound refinementmodulo declared relaxations), Λ = {simple_dict_object_form}, 0 undisclosed
looseness, 0 structural LOOSER rows.
CI hardening: the dev
linkmldependency is pinned to the exact submodulecommit (
97e73d0f) rather than a branch, so CI can't install a newer linkmlthan the submodule and regenerate divergent artifacts. A
slowpytest markeris registered for the proof suite (
pytest -m 'not slow'for the fast lane).JSON-LD data simplification via RDFS inference
Building on the recent blank-node RDFS-inference support, the openlabel-v2 test
data is simplified so instances carry only the information a real producer would
write — nested container types are now inferred rather than declared.
gen-owl --no-use-native-uris(Makefile): emitsQuantitativeValue,minValue/maxValueandhasLowerBound/hasUpperBoundwith their declaredmapped IRIs (
schema:/cmns-q:) instead of nativeopenlabel_v2:IRIs, sothe shared context can resolve the
QuantitativeValuevalue type. Scoped toopenlabel-v2 (the only LinkML domain).
@typeremoval: redundant@typeonAdminTag/Behaviour/Odd/RoadUsercontainer blank nodes is dropped from every valid and invalidinstance; the type is recovered from each parent property's
rdfs:rangeunderRDFS inference.
domain: QuantitativeValueon theminValue/maxValueslots makesgen-owlemitschema:minValue/schema:maxValue rdfs:domain schema:QuantitativeValue, so RDFS infers theQuantitativeValuetype from themere presence of a range. The redundant
@type: QuantitativeValueis removedfrom every valid node that carries a range;
fail09(no min/max) intentionallykeeps its explicit
@type.TrafficAgentTypeEnum:trafficAgentTypeValuemoves fromany_of: [RoadUserHumanEnum, RoadUserVehicleEnum]to a single combined enum.A single enum range restores the
@type: @vocabcontext coercion (LinkML dropsit for
any_ofslots) and yields onesh:in, so the data replaces verbose[{"@id": "openlabel_v2:VehicleCar"}, ...]objects with bare terms["VehicleCar", "HumanPedestrian"], consistent with every other enum slot.sync_tag_type_enum.pydedup: the structuralTagTypeEnumis a set ofunique tag-type names, but the combined
TrafficAgentTypeEnumrepeats theRoadUser*Enumvalues. Each value is now emitted once — otherwisegen-json-schemaaborts on a duplicate key. The syncedopenlabel-v2-schema.yamlgains only thetrafficAgentTypeValuecategory slotand
schema.jsonlists it as a valid tag type.fail13_quantitativevalue_inferred_missing_max: anuntyped
QuantitativeValuewithminValueonly (no@type, nomaxValue). Itproves the type is recovered purely by RDFS inference and that the required
maxValueconstraint still fires — the.expectedshows exactlymaxValue (QuantitativeValue), not a "not a QuantitativeValue" error, so aregression in inference would change the message and fail the test.
Versioning & Compatibility
Type of change
How Has This Been Tested?
python scripts/schema_refinement_prover.py --spec linkml/openlabel-v2/proof_spec.yaml→ exit 0, verdict PROVEN, Λ = {simple_dict_object_form}.
pytest tests/unit/test_schema_refinement.py— 10/10 passed (soundnessgate, coverage gate, scope-projection pins, bounded-looseness check asserting
key_pattern/null_optionalstay closed).pytest tests/unit/test_openlabel_schema.py -v— 48/48 passed (schemageneration, ASAM/LinkML cross-validation, vocabulary faithfulness covering all
243 ASAM tag classes, sync correctness, v1→v2 converter).
python -m src.tools.validators.validation_suite --domain openlabel-v2— SHACLconformance passes with nested types removed (valid instances conform via RDFS
inference); all 13 invalid fixtures (incl. the new
fail13_quantitativevalue_inferred_missing_max) fail as expected against their.expectedfiles.make generate DOMAIN=openlabel-v2is idempotent (CI "verify no changes");regeneration reproduces the committed
openlabel-v2.schema.jsonbyte-identically.ruff checkclean on changed Python.Checklist