diff --git a/contracts/cultnet/cultnet.witness-artifact-bundle.schema.json b/contracts/cultnet/cultnet.witness-artifact-bundle.schema.json
index 6dd6971d..936a0a04 100644
--- a/contracts/cultnet/cultnet.witness-artifact-bundle.schema.json
+++ b/contracts/cultnet/cultnet.witness-artifact-bundle.schema.json
@@ -59,6 +59,7 @@
],
"additionalProperties": false,
"properties": {
+ "id": { "type": "string", "minLength": 1 },
"role": { "type": "string", "minLength": 1 },
"schemaId": { "type": "string", "minLength": 1 },
"schemaVersion": { "type": "string", "minLength": 1 },
@@ -74,6 +75,7 @@
],
"additionalProperties": false,
"properties": {
+ "id": { "type": "string", "minLength": 1 },
"role": { "type": "string", "minLength": 1 },
"uri": { "type": "string", "minLength": 1 },
"mediaType": { "type": "string", "minLength": 1 },
@@ -92,6 +94,7 @@
],
"additionalProperties": false,
"properties": {
+ "id": { "type": "string", "minLength": 1 },
"stage": { "type": "string", "minLength": 1 },
"startedAt": { "type": "string", "minLength": 1 },
"completedAt": { "type": "string", "minLength": 1 },
diff --git a/packages/cultnet-py/src/cultnet_py/interop_peer.py b/packages/cultnet-py/src/cultnet_py/interop_peer.py
index ba236686..e6cd4601 100644
--- a/packages/cultnet-py/src/cultnet_py/interop_peer.py
+++ b/packages/cultnet-py/src/cultnet_py/interop_peer.py
@@ -1316,6 +1316,7 @@ def witness_artifact_bundle_schema_json() -> str:
"required": ["role", "schemaId"],
"additionalProperties": False,
"properties": {
+ "id": {"type": "string", "minLength": 1},
"role": {"type": "string", "minLength": 1},
"schemaId": {"type": "string", "minLength": 1},
"schemaVersion": {"type": "string", "minLength": 1},
@@ -1327,6 +1328,7 @@ def witness_artifact_bundle_schema_json() -> str:
"required": ["role", "uri", "mediaType"],
"additionalProperties": False,
"properties": {
+ "id": {"type": "string", "minLength": 1},
"role": {"type": "string", "minLength": 1},
"uri": {"type": "string", "minLength": 1},
"mediaType": {"type": "string", "minLength": 1},
@@ -1340,6 +1342,7 @@ def witness_artifact_bundle_schema_json() -> str:
"required": ["stage", "startedAt", "completedAt", "latencyMs"],
"additionalProperties": False,
"properties": {
+ "id": {"type": "string", "minLength": 1},
"stage": {"type": "string", "minLength": 1},
"startedAt": {"type": "string", "minLength": 1},
"completedAt": {"type": "string", "minLength": 1},
diff --git a/packages/cultnet-py/tests/test_cultnet.py b/packages/cultnet-py/tests/test_cultnet.py
index 475305d3..dc15224b 100644
--- a/packages/cultnet-py/tests/test_cultnet.py
+++ b/packages/cultnet-py/tests/test_cultnet.py
@@ -1870,9 +1870,9 @@ class CultNetTests(unittest.TestCase):
witness_kind="interop-proof",
captured_at="2026-06-13T00:00:03Z",
subject={"documentType": "cultnet.interop-note", "subjectId": "note:python"},
- contracts=[{"role": "payload", "schemaId": "schema-a"}],
- artifacts=[{"role": "log", "uri": "cultcache://bundle-1/log", "mediaType": "text/plain"}],
- timing_witnesses=[{"stage": "roundtrip", "startedAt": "2026-06-13T00:00:03Z", "completedAt": "2026-06-13T00:00:04Z", "latencyMs": 1.0}],
+ contracts=[{"id": "c0e1a2b3c4d5", "role": "payload", "schemaId": "schema-a"}],
+ artifacts=[{"id": "a0e1a2b3c4d5", "role": "log", "uri": "cultcache://bundle-1/log", "mediaType": "text/plain"}],
+ timing_witnesses=[{"id": "e0e1a2b3c4d5", "stage": "roundtrip", "startedAt": "2026-06-13T00:00:03Z", "completedAt": "2026-06-13T00:00:04Z", "latencyMs": 1.0}],
provenance={"pipelineId": "interop", "runId": "run-1", "runtimeId": "python-test"},
)
payload = encode_witness_artifact_bundle_payload(bundle)
@@ -1888,6 +1888,8 @@ class CultNetTests(unittest.TestCase):
self.assertEqual(slots[2], "2026-06-13T00:00:03Z")
self.assertEqual(slots[3]["subjectId"], "note:python")
self.assertEqual(slots[4][0]["schemaId"], "schema-a")
+ self.assertEqual([slots[4][0]["id"], slots[5][0]["id"], slots[6][0]["id"]], ["c0e1a2b3c4d5", "a0e1a2b3c4d5", "e0e1a2b3c4d5"])
+ self.assertEqual(typed_decoded.contracts[0]["id"], "c0e1a2b3c4d5")
self.assertEqual(slots[5][0]["uri"], "cultcache://bundle-1/log")
self.assertEqual(slots[6][0]["latencyMs"], 1.0)
self.assertEqual(slots[7]["runtimeId"], "python-test")
diff --git a/packages/cultnet-ts/test/interop/cultnet-interop.test.ts b/packages/cultnet-ts/test/interop/cultnet-interop.test.ts
index 756ef97d..7eab911f 100644
--- a/packages/cultnet-ts/test/interop/cultnet-interop.test.ts
+++ b/packages/cultnet-ts/test/interop/cultnet-interop.test.ts
@@ -1232,9 +1232,9 @@ test("CultNet TS/Rust/C#/Python peers discover each other and exchange raw state
schemaVersion: INTEROP_SCHEMA_VERSION,
schemaId: interopNoteSchemaId,
},
- [{ role: "payload", schemaId: interopNoteSchemaId, schemaVersion: INTEROP_SCHEMA_VERSION }],
- [{ role: "log", uri: "cultcache://bundle:ts-python-witness/log", mediaType: "text/plain" }],
- [{ stage: "roundtrip", startedAt: "2026-06-13T00:00:03Z", completedAt: "2026-06-13T00:00:04Z", latencyMs: 1 }],
+ [{ id: "c0e1a2b3c4d5", role: "payload", schemaId: interopNoteSchemaId, schemaVersion: INTEROP_SCHEMA_VERSION }],
+ [{ id: "a0e1a2b3c4d5", role: "log", uri: "cultcache://bundle:ts-python-witness/log", mediaType: "text/plain" }],
+ [{ id: "e0e1a2b3c4d5", stage: "roundtrip", startedAt: "2026-06-13T00:00:03Z", completedAt: "2026-06-13T00:00:04Z", latencyMs: 1 }],
{ pipelineId: "interop", runId: "ts-python-witness", runtimeId: "ts-witness", agentId: "ts-agent" },
]),
sourceRuntimeId: "ts-witness",
@@ -1252,6 +1252,7 @@ test("CultNet TS/Rust/C#/Python peers discover each other and exchange raw state
assert.equal(witnessSlots[1], "interop-proof");
assert.equal(witnessSlots[3].subjectId, "note:python-peer");
assert.equal(witnessSlots[7].runtimeId, "ts-witness");
+ assert.deepEqual([witnessSlots[4][0].id, witnessSlots[5][0].id, witnessSlots[6][0].id], ["c0e1a2b3c4d5", "a0e1a2b3c4d5", "e0e1a2b3c4d5"], "element ids cross TS to Python and back");
const expectedPeers = ["csharp-peer", "python-peer", "rust-peer", "ts-peer"];
diff --git a/src/GameCult.Networking/CultWitnessArtifactBundle.cs b/src/GameCult.Networking/CultWitnessArtifactBundle.cs
index 045ebfbb..6baa48bb 100644
--- a/src/GameCult.Networking/CultWitnessArtifactBundle.cs
+++ b/src/GameCult.Networking/CultWitnessArtifactBundle.cs
@@ -104,6 +104,10 @@ namespace GameCult.Networking
[MessagePackObject]
public sealed class CultWitnessContractPin
{
+ [Key(4)]
+ [CultElementId]
+ public string Id { get; set; } = string.Empty;
+
///
/// Gets or sets the contract role inside the bundle.
///
@@ -135,6 +139,10 @@ namespace GameCult.Networking
[MessagePackObject]
public sealed class CultWitnessArtifactEntry
{
+ [Key(6)]
+ [CultElementId]
+ public string Id { get; set; } = string.Empty;
+
///
/// Gets or sets the artifact role inside the witness bundle.
///
@@ -178,6 +186,10 @@ namespace GameCult.Networking
[MessagePackObject]
public sealed class CultWitnessTimingEntry
{
+ [Key(5)]
+ [CultElementId]
+ public string Id { get; set; } = string.Empty;
+
///
/// Gets or sets the pipeline stage name.
///