Found by the #328 identifier scan.
build_aas_from_passport takes the asset identity as a bare gtin: &str and emits it twice under a GS1 label:
// crates/dpp-aas/src/builder.rs:89
SpecificAssetId { name: "gtin".into(), value: gtin.to_owned() }
// :124
global_asset_id: format!("urn:odal-node:product:{gtin}")
The doc comment on the function already says so, and calls it pre-existing.
Why it is worth a ticket now rather than staying a documented quirk
#295 made it reachable. While every product group payload carried a mandatory gtin, the label was always true — a caller had nothing else to pass. EN 18219 schemes 2 and 3 are self-issuing and carry no GTIN, so the label is now false for exactly the passports the identifier work exists to enable.
That is the same shape as dpp-engine #376, where a GTIN was scraped out of the carrier URI and the internal UUID substituted when there was none: a scheme-1 assumption that was harmless only because scheme 1 was universal.
It matters more here than in an internal struct because an AAS Environment is an interop document. A consuming toolchain reads specificAssetIds by name; one asking for gtin and receiving did:web:acme.example.com:b:1 has been told something untrue by a document it treats as authoritative. globalAssetId carries the same value into a urn:odal-node:product: URN.
The tension to resolve, which is why this is a decision and not a patch
The parameter is deliberately not read off the passport — the unsold-goods mapper's module note records why: the asset identity is the caller's, and a file export or an AASX package assembled for a reporting authority may legitimately key on something that is not the passport's product identifier at all. So "just use product_identifier()" is not the answer; it would break the case the parameter exists for.
Options, roughly in increasing cost:
- Name the slot from the value. Take
&ProductIdentifier where the caller has one and derive the specificAssetId name per scheme — reusing dpp_registry::{SCHEME_GTIN, SCHEME_IDENTIFICATION_LINK, SCHEME_DID}, which already state those three strings once and record all three as RegistryBasis::Assumed.
- Take a
(name, value) pair and keep the identity fully the caller's, which makes the mislabel impossible to produce by accident but pushes the choice out.
- Keep
&str, rename the slot to something scheme-neutral (productIdentifier) — cheapest, but silently changes a key an existing AAS consumer may match on.
Option 1 is the one that matches what dpp-registry already does for the same question on the registry wire.
Check while fixing
crates/dpp-tests/fixtures/aas/environments/*.json carry "gtin" as a specificAssetId name in all twelve environments; whichever option is taken, those goldens move with it.
Related: #328, #295, #322.
Found by the #328 identifier scan.
build_aas_from_passporttakes the asset identity as a baregtin: &strand emits it twice under a GS1 label:The doc comment on the function already says so, and calls it pre-existing.
Why it is worth a ticket now rather than staying a documented quirk
#295 made it reachable. While every product group payload carried a mandatory
gtin, the label was always true — a caller had nothing else to pass. EN 18219 schemes 2 and 3 are self-issuing and carry no GTIN, so the label is now false for exactly the passports the identifier work exists to enable.That is the same shape as dpp-engine #376, where a GTIN was scraped out of the carrier URI and the internal UUID substituted when there was none: a scheme-1 assumption that was harmless only because scheme 1 was universal.
It matters more here than in an internal struct because an AAS Environment is an interop document. A consuming toolchain reads
specificAssetIdsby name; one asking forgtinand receivingdid:web:acme.example.com:b:1has been told something untrue by a document it treats as authoritative.globalAssetIdcarries the same value into aurn:odal-node:product:URN.The tension to resolve, which is why this is a decision and not a patch
The parameter is deliberately not read off the passport — the unsold-goods mapper's module note records why: the asset identity is the caller's, and a file export or an AASX package assembled for a reporting authority may legitimately key on something that is not the passport's product identifier at all. So "just use
product_identifier()" is not the answer; it would break the case the parameter exists for.Options, roughly in increasing cost:
&ProductIdentifierwhere the caller has one and derive thespecificAssetIdname per scheme — reusingdpp_registry::{SCHEME_GTIN, SCHEME_IDENTIFICATION_LINK, SCHEME_DID}, which already state those three strings once and record all three asRegistryBasis::Assumed.(name, value)pair and keep the identity fully the caller's, which makes the mislabel impossible to produce by accident but pushes the choice out.&str, rename the slot to something scheme-neutral (productIdentifier) — cheapest, but silently changes a key an existing AAS consumer may match on.Option 1 is the one that matches what
dpp-registryalready does for the same question on the registry wire.Check while fixing
crates/dpp-tests/fixtures/aas/environments/*.jsoncarry"gtin"as aspecificAssetIdname in all twelve environments; whichever option is taken, those goldens move with it.Related: #328, #295, #322.