Skip to content

fix LD safe mode and open schemas

Steve CAPELL requested to merge mre-tech-fixes into main

v0.7.0 artefact refactor: consolidation, JSON-LD safe-mode fixes, and extension mechanism

Summary

This MR reshapes the v0.7.0 artefacts so that JSON-LD expansion works under safe mode, credentials can be extended without silent property loss, and the context + schema + ontology layers stay aligned via automated checks.

High-level outcomes:

  • Consolidated the CVC artefacts (one schema + one sample hierarchy).
  • Fixed the renderTemplate2024 property shape across all five credential schemas.
  • Added top-level JSON-LD context terms for the five credential classes and the 25 helper classes that were previously unresolvable.
  • Reconciled divergent property-definition forms in the context (one canonical form per term).
  • Introduced a per-class @vocab extension mechanism so implementers can add custom keys without breaking safe-mode expansion.
  • Curated which classes accept extensions (open) vs which remain strict (closed) at both the JSON Schema and JSON-LD layers.
  • Extended the validation script with a safe-mode expansion section that catches silent property drops going forward.
  • Added an audit script that enforces the new @vocab policy and flags context / ontology drift.
  • Documented the extension mechanism in the Extension Development Guide.
  • Resolved upstream issues #650 (closed) (missing type/id keyword aliases) and #651 (closed)-adjacent (typed-object safe-mode failures).

All 73 automated checks pass, 0 failures.

Issues addressed

  • #650 (closed) — v0.7.0 context missing top-level type keyword alias.
  • Safe-mode expansion failure on nested typed objects (e.g. "type": ["Link"] inside conformsTo) — previously dropped the type silently.
  • renderTemplate2024 schema bug — five credential schemas had a single-object field that should have been an array of RenderTemplate2024.
  • Missing credential type IRIs — the five VC types (DigitalProductPassport, DigitalConformityCredential, DigitalFacilityRecord, DigitalIdentityAnchor, DigitalTraceabilityEvent) were not mapped in the context.
  • Characteristics extension point was silently dropping unknown keys.
  • Divergent property definitions in the context (schema: vs schemaorg: prefixes, two different mediaType forms).

Changes by area

Artefacts: schemas

  • CVC consolidation. Removed the separate ConformityProfile.json and Criterion.json schemas. The single root schema is now artefacts/schema/v0.7.0/cvc/ConformityScheme.json, which embeds profiles and criteria as nested structures. The sample instances are consolidated into a single hierarchical ConformityScheme_instance.json.
  • renderMethod rename (DPP, DCC, DFR, DIA, DTE). The renderTemplate2024 property (a single-object field) was renamed to renderMethod and re-typed as an array of RenderTemplate2024, aligning with the W3C VC render-method spec. Updated samples (DigitalIdentityAnchor_instance.json) correspondingly.
  • Characteristics as an explicit extension point. Rewrote the Characteristics $defs entry in dpp/Product.json to document the extension mechanism, allow an optional @context property for prefix declarations, and require additionalProperties: true.
  • Open vs closed classes curated. Flipped additionalProperties: false → true on 17 $defs entries plus 4 root schemas (dpp/Product.json, dfr/Facility.json, dcc/ConformityAttestation.json, cvc/ConformityScheme.json). Classes retaining false are the genuinely closed pure-shape types (Measure, Dimension, Coordinate, Period, Address, Country, Classification, Score, Link, Image, PartyRole, SensorData, StandardAlignment, RegulatoryAlignment, Performance, IdentifierScheme, BitstringStatusListEntry, RenderTemplate2024, EventProduct, MaterialUsage, PerformanceMetric, ConformityTopic).

Artefacts: JSON-LD context (untp-context.jsonld)

  • Top-level keyword aliases. Added "type": "@type" and "id": "@id" so the context works standalone as well as combined with the W3C VCDM v2 context. Resolves issue #650 (closed).
  • Credential type mappings. Added top-level @protected term entries for DigitalProductPassport, DigitalConformityCredential, DigitalFacilityRecord, DigitalIdentityAnchor, and DigitalTraceabilityEvent, each pointing to untp:<Name>. Without these, credentials declaring "type": ["VerifiableCredential", "DigitalProductPassport"] would silently lose the UNTP type on expansion.
  • Helper class hoist. Added top-level @protected term entries with nested @context for 25 helper classes that previously existed only as inline definitions inside parent scopes: Address, Characteristics, Classification, Coordinate, Country, Dimension, Endorsement, EventProduct, FacilityVerification, Image, Link, Location, Material, MaterialUsage, Measure, Package, PartyRole, Performance, Period, ProductVerification, RegulatoryAlignment, Score, ScoringFramework, SensorData, StandardAlignment. A sample with "type": ["Link"] on a nested object now resolves correctly under JSON-LD safe mode (previously: relative @type reference error).
  • RenderTemplate2024 class scope. Added a top-level term with a nested @context that uses W3C-standard IRIs for mediaType (schema:encodingFormat) and digestMultibase (security:digestMultibase) so it doesn't collide with VC v2's protected definitions of those terms.
  • Per-class @vocab extension mechanism. Every open class's nested @context now declares "@vocab": "https://vocabulary.uncefact.org/untp/<ClassName>/". Unknown extension keys resolve into that per-class synthetic namespace instead of being silently dropped. Characteristics uses this pattern as the canonical extension point. Closed classes deliberately omit @vocab.
  • Property-definition reconciliation. 51 term definitions normalised:
    • schemaorg:descriptionschema:description (3 occurrences; same IRI, different prefix).
    • schemaorg:nameschema:name (39 occurrences; same IRI, different prefix).
    • Two divergent mediaType forms (schema:encodingFormat vs untp:mediaType with @vocab: mimetype.io) collapsed to the W3C form schema:encodingFormat everywhere (9 occurrences). This aligns with VC v2 and eliminates a protected-term collision risk inside VerifiableCredential scope.

Artefacts: ontology

  • No additions required. Every untp:* IRI now referenced from the context (715 terms after the refactor) already resolves to an entry in untp-ontology.jsonld. Verified by the validator's context → ontology consistency check.

Artefacts: samples

  • artefacts/samples/v0.7.0/cvc/ reduced to a single ConformityScheme_instance.json hierarchy; four standalone snippet samples removed.
  • artefacts/samples/v0.7.0/dia/DigitalIdentityAnchor_instance.json updated to use the new renderMethod array shape with type: ["RenderTemplate2024"].

Tooling

  • .claude/scripts/validate-artefacts.mjs — added section 7: JSON-LD safe-mode expansion. This catches silent property drops (unmapped terms, relative @type references, dropped extension keys). Also fixed a latent bug where localContexts mapped the wrong URL, causing the script to fetch the published (old) context over HTTP rather than testing local edits. Removed references to the deleted CVC profile/criterion schemas and samples.
  • .claude/scripts/audit-context.mjs (new) — reports on:
    • Class term entries and where each class is registered.
    • Property-definition divergences (form differences of the same property across scopes).
    • Consistent properties (ripe-for-hoist candidates).
    • @vocab policy enforcement on every open class.
    • Schema class names not yet registered in the context.
  • .claude/scripts/hoist-classes.mjs (new, one-off helper) — generated the 25 hoisted class entries from the JSON schemas and the reconciled context. Preserved for auditability.

Documentation

  • website/docs/specification/ConformityVocabularyCatalog.md — artefacts table rewritten to reflect one schema + one sample after CVC consolidation.
  • website/docs/extensions/ExtensionDevelopmentGuide.md — new section Technical Reference: How Extensions Work covering:
    • JSON Schema + JSON-LD two-layer alignment.
    • Open vs closed class lists with rationale.
    • Default auto-namespacing behaviour for extension keys.
    • Preferred pattern for implementers with their own published vocabulary (scoped @context + prefix).
    • Promotion path from extension key to curated UNTP term (version-boundary breaking change rules).
    • Which tooling to run to validate an extension.

Validation

Run from the repo root:

node .claude/scripts/validate-artefacts.mjs
node .claude/scripts/audit-context.mjs

Results after this MR:

  • validate-artefacts.mjs: 73 passed, 0 failed, 2 skipped.
    • 17 samples validated against their JSON schemas.
    • 14 schemas with all $refs resolving.
    • 715 context terms all resolve to ontology entries.
    • All context keys match their ontology local names.
    • 17 samples expand cleanly under JSON-LD normal mode.
    • 17 samples expand cleanly under JSON-LD safe mode (new).
    • Skipped: cvc/ConformityScheme_instance.json (vocabulary artefact, no @context).
  • audit-context.mjs:
    • 0 classes missing from context (down from 25 at start of session).
    • 52 classes registered top-level.
    • 0 property-definition divergences after reconciliation.
    • All 30 open classes declare a correctly-namespaced @vocab.
    • 0 schema class names unregistered.

Extension examples

Simple case — any implementer adding a vendor-specific field:

{
  "type": ["Product"],
  "id": "https://manufacturer.example/p/42",
  "name": "Battery module",
  "partNumber": "BM-18650-3000"
}

Expands to <product> <https://vocabulary.uncefact.org/untp/Product/partNumber> "BM-18650-3000". Extension key preserved, safe-mode clean.

Industry-extension case — using your own vocabulary:

{
  "type": ["Product"],
  "characteristics": {
    "@context": { "battery": "https://example-industry.org/battery/v1/" },
    "type": ["Characteristics"],
    "battery:batteryChemistry": "NMC 811",
    "battery:batteryCategory": "EV"
  }
}

Extension keys expand into the industry body's namespace rather than UNTP's synthetic one.

Notes for reviewers

  • The inline helper-class @context blocks in parent scopes (for example the inline Link/Image definitions inside productImage, trustmark, conformsTo, etc.) were deliberately preserved to support typeless nested complex properties per the spirit of issue #651 (closed). Pruning them was considered and declined because the parent scope's @vocab doesn't propagate into child-node values — only property-scoped @context does. The audit script treats this coexistence as expected, not as a divergence.
  • The @vocab policy check in audit-context.mjs is the regression guard for the extension mechanism — any future edit that removes @vocab from an open class or drifts the URL pattern will be flagged.
  • Per-class synthetic extension IRIs are stable within a given context version but subject to rewrite if the term is later promoted to a curated UNTP property. This is called out in the Extension Development Guide.

Merge request reports

Loading