This was generated by AI during triage.
Summary
Sync publish and async publish appear to use different root/subject canonicalization semantics. That makes the same domain payload produce different RDF subjects depending on whether it goes through agent.publish() or agent.publishAsync(), which breaks stable IRI linking and graph visibility assumptions for integrations.
The async lift path rewrites requested roots to generated canonical roots:
packages/publisher/src/async-lift-validation.ts:38-47 builds canonicalRootMap and canonicalizes public/private quads.
packages/publisher/src/async-lift-validation.ts:73-98 rewrites matching subjects and object IRIs.
packages/publisher/src/async-lift-validation.ts:101-105 generates roots like dkg:<cg>:<namespace>:<scope>/<name>-<hash>.
packages/agent/src/dkg-agent-publish.ts:1054-1091 uses the validated/canonicalized async payload when building the async lift seal.
Sync publish, by contrast, takes the caller-provided quads/JSON-LD IDs into _publish() and then through the normal canonical publish payload path without this async-lift root rewrite:
packages/agent/src/dkg-agent-publish.ts:1172-1283 passes caller quads/privateQuads to publisher.publish().
packages/publisher/src/canonical-publish-payload.ts canonicalizes the publish payload for hashing, but does not generate the async-lift dkg:<cg>:<namespace>:<scope>/... subject IRIs.
Why this matters
For domain integrations, caller-provided IRIs are the graph model. Example shape:
- tenant root:
urn:dmaast:tenant:tenant-a
- device root:
urn:dmaast:device:tenant-a:device-a
- source root:
urn:dmaast:data-source:tenant-a:device-a:source-a
- device links to tenant by IRI
- source links to tenant and device by IRI
With sync publish, these stable IRIs remain visible as the subjects/objects and the VM graph renders connected.
With async publish, the lift validator rewrites roots to generated dkg:.../<name>-<hash> roots. That means the same input no longer has the same external root identity across sync and async paths. It can also split graph links and make downstream lookups harder unless the caller knows and follows the async-generated canonical root mapping.
Expected behavior
Sync and async publish should have consistent root/subject semantics for identical inputs, or the SDK/API should expose a clear compatibility contract.
Possible fixes:
- Preserve caller-provided root IRIs in async publish, matching sync publish.
- If async needs generated DKG roots internally, persist explicit alias/equivalence metadata and make query/UI/link traversal resolve source IRIs and canonical IRIs consistently.
- Provide an opt-in/opt-out option for async root canonicalization, with the default matching sync publish for JSON-LD
@id payloads.
Suggested acceptance tests
- Publish the same JSON-LD hierarchy through sync and async:
- Tenant has stable
@id.
- Device has stable
@id and links to tenant by IRI.
- Data source has stable
@id and links to both tenant and device by IRI.
- The finalized VM graph has the same root entity semantics in both paths.
- The UI graph renders connected tenant -> device -> source clusters in both paths.
- SPARQL lookups by caller-provided root IRI work in both paths, or a documented alias query works equivalently.
- UAL/root metadata exposes enough mapping for downstream integrations to fetch by the original domain IRI.
Observed downstream workaround
The DMAAST Kafka endpoint registration integration switched to sync publish by default because it needed stable tenant/device/data-source IRIs and visible connected graph clusters for the demo.
Summary
Sync publish and async publish appear to use different root/subject canonicalization semantics. That makes the same domain payload produce different RDF subjects depending on whether it goes through
agent.publish()oragent.publishAsync(), which breaks stable IRI linking and graph visibility assumptions for integrations.The async lift path rewrites requested roots to generated canonical roots:
packages/publisher/src/async-lift-validation.ts:38-47buildscanonicalRootMapand canonicalizes public/private quads.packages/publisher/src/async-lift-validation.ts:73-98rewrites matching subjects and object IRIs.packages/publisher/src/async-lift-validation.ts:101-105generates roots likedkg:<cg>:<namespace>:<scope>/<name>-<hash>.packages/agent/src/dkg-agent-publish.ts:1054-1091uses the validated/canonicalized async payload when building the async lift seal.Sync publish, by contrast, takes the caller-provided quads/JSON-LD IDs into
_publish()and then through the normal canonical publish payload path without this async-lift root rewrite:packages/agent/src/dkg-agent-publish.ts:1172-1283passes caller quads/privateQuads topublisher.publish().packages/publisher/src/canonical-publish-payload.tscanonicalizes the publish payload for hashing, but does not generate the async-liftdkg:<cg>:<namespace>:<scope>/...subject IRIs.Why this matters
For domain integrations, caller-provided IRIs are the graph model. Example shape:
urn:dmaast:tenant:tenant-aurn:dmaast:device:tenant-a:device-aurn:dmaast:data-source:tenant-a:device-a:source-aWith sync publish, these stable IRIs remain visible as the subjects/objects and the VM graph renders connected.
With async publish, the lift validator rewrites roots to generated
dkg:.../<name>-<hash>roots. That means the same input no longer has the same external root identity across sync and async paths. It can also split graph links and make downstream lookups harder unless the caller knows and follows the async-generated canonical root mapping.Expected behavior
Sync and async publish should have consistent root/subject semantics for identical inputs, or the SDK/API should expose a clear compatibility contract.
Possible fixes:
@idpayloads.Suggested acceptance tests
@id.@idand links to tenant by IRI.@idand links to both tenant and device by IRI.Observed downstream workaround
The DMAAST Kafka endpoint registration integration switched to sync publish by default because it needed stable tenant/device/data-source IRIs and visible connected graph clusters for the demo.