fix LD safe mode and open schemas
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
renderTemplate2024property 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
@vocabextension 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
@vocabpolicy and flags context / ontology drift. - Documented the extension mechanism in the Extension Development Guide.
- Resolved upstream issues #650 (closed) (missing
type/idkeyword 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
typekeyword alias. -
Safe-mode expansion failure on nested typed objects (e.g.
"type": ["Link"]insideconformsTo) — previously dropped the type silently. -
renderTemplate2024schema bug — five credential schemas had a single-object field that should have been an array ofRenderTemplate2024. -
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:vsschemaorg:prefixes, two differentmediaTypeforms).
Changes by area
Artefacts: schemas
-
CVC consolidation. Removed the separate
ConformityProfile.jsonandCriterion.jsonschemas. The single root schema is nowartefacts/schema/v0.7.0/cvc/ConformityScheme.json, which embeds profiles and criteria as nested structures. The sample instances are consolidated into a single hierarchicalConformityScheme_instance.json. -
renderMethodrename (DPP, DCC, DFR, DIA, DTE). TherenderTemplate2024property (a single-object field) was renamed torenderMethodand re-typed as an array ofRenderTemplate2024, aligning with the W3C VC render-method spec. Updated samples (DigitalIdentityAnchor_instance.json) correspondingly. -
Characteristics as an explicit extension point. Rewrote the
Characteristics$defsentry indpp/Product.jsonto document the extension mechanism, allow an optional@contextproperty for prefix declarations, and requireadditionalProperties: true. -
Open vs closed classes curated. Flipped
additionalProperties: false → trueon 17$defsentries plus 4 root schemas (dpp/Product.json,dfr/Facility.json,dcc/ConformityAttestation.json,cvc/ConformityScheme.json). Classes retainingfalseare 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
@protectedterm entries forDigitalProductPassport,DigitalConformityCredential,DigitalFacilityRecord,DigitalIdentityAnchor, andDigitalTraceabilityEvent, each pointing tountp:<Name>. Without these, credentials declaring"type": ["VerifiableCredential", "DigitalProductPassport"]would silently lose the UNTP type on expansion. -
Helper class hoist. Added top-level
@protectedterm entries with nested@contextfor 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 referenceerror). -
RenderTemplate2024class scope. Added a top-level term with a nested@contextthat uses W3C-standard IRIs formediaType(schema:encodingFormat) anddigestMultibase(security:digestMultibase) so it doesn't collide with VC v2's protected definitions of those terms. -
Per-class
@vocabextension mechanism. Every open class's nested@contextnow declares"@vocab": "https://vocabulary.uncefact.org/untp/<ClassName>/". Unknown extension keys resolve into that per-class synthetic namespace instead of being silently dropped.Characteristicsuses this pattern as the canonical extension point. Closed classes deliberately omit@vocab. -
Property-definition reconciliation. 51 term definitions normalised:
-
schemaorg:description→schema:description(3 occurrences; same IRI, different prefix). -
schemaorg:name→schema:name(39 occurrences; same IRI, different prefix). - Two divergent
mediaTypeforms (schema:encodingFormatvsuntp:mediaTypewith@vocab: mimetype.io) collapsed to the W3C formschema:encodingFormateverywhere (9 occurrences). This aligns with VC v2 and eliminates a protected-term collision risk insideVerifiableCredentialscope.
-
Artefacts: ontology
- No additions required. Every
untp:*IRI now referenced from the context (715 terms after the refactor) already resolves to an entry inuntp-ontology.jsonld. Verified by the validator's context → ontology consistency check.
Artefacts: samples
-
artefacts/samples/v0.7.0/cvc/reduced to a singleConformityScheme_instance.jsonhierarchy; four standalone snippet samples removed. -
artefacts/samples/v0.7.0/dia/DigitalIdentityAnchor_instance.jsonupdated to use the newrenderMethodarray shape withtype: ["RenderTemplate2024"].
Tooling
-
.claude/scripts/validate-artefacts.mjs— added section 7: JSON-LD safe-mode expansion. This catches silent property drops (unmapped terms, relative@typereferences, dropped extension keys). Also fixed a latent bug wherelocalContextsmapped 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).
-
@vocabpolicy 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
@contextblocks in parent scopes (for example the inline Link/Image definitions insideproductImage,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@vocabdoesn't propagate into child-node values — only property-scoped@contextdoes. The audit script treats this coexistence as expected, not as a divergence. - The
@vocabpolicy check inaudit-context.mjsis the regression guard for the extension mechanism — any future edit that removes@vocabfrom 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.