Add witness artifact bundle contract (topic_95053275-0a5a-4b05-806e-671dc5cff4d1)

This commit is contained in:
Meta Vens 2026-05-20 23:41:13 +01:00
commit 4ddf30e545
5 changed files with 419 additions and 0 deletions

View file

@ -0,0 +1,120 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/GameCult/cultnet-ts/contracts/cultnet.witness-artifact-bundle.schema.json",
"title": "CultNet Witness Artifact Bundle",
"type": "object",
"required": [
"bundleId",
"witnessKind",
"capturedAt",
"subject",
"contracts",
"artifacts",
"timingWitnesses",
"provenance"
],
"additionalProperties": false,
"properties": {
"bundleId": { "type": "string", "minLength": 1 },
"witnessKind": { "type": "string", "minLength": 1 },
"capturedAt": { "type": "string", "minLength": 1 },
"subject": { "$ref": "#/$defs/subject_pin" },
"contracts": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/contract_pin" }
},
"artifacts": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/artifact_entry" }
},
"timingWitnesses": {
"type": "array",
"items": { "$ref": "#/$defs/timing_entry" }
},
"provenance": { "$ref": "#/$defs/provenance" }
},
"$defs": {
"subject_pin": {
"type": "object",
"required": [
"documentType",
"subjectId"
],
"additionalProperties": false,
"properties": {
"documentType": { "type": "string", "minLength": 1 },
"subjectId": { "type": "string", "minLength": 1 },
"schemaVersion": { "type": "string", "minLength": 1 },
"schemaId": { "type": "string", "minLength": 1 },
"contentHash": { "type": "string", "minLength": 1 }
}
},
"contract_pin": {
"type": "object",
"required": [
"role",
"schemaId"
],
"additionalProperties": false,
"properties": {
"role": { "type": "string", "minLength": 1 },
"schemaId": { "type": "string", "minLength": 1 },
"schemaVersion": { "type": "string", "minLength": 1 },
"contentHash": { "type": "string", "minLength": 1 }
}
},
"artifact_entry": {
"type": "object",
"required": [
"role",
"uri",
"mediaType"
],
"additionalProperties": false,
"properties": {
"role": { "type": "string", "minLength": 1 },
"uri": { "type": "string", "minLength": 1 },
"mediaType": { "type": "string", "minLength": 1 },
"contentHash": { "type": "string", "minLength": 1 },
"byteLength": { "type": "integer", "minimum": 0 },
"producedAt": { "type": "string", "minLength": 1 }
}
},
"timing_entry": {
"type": "object",
"required": [
"stage",
"startedAt",
"completedAt",
"latencyMs"
],
"additionalProperties": false,
"properties": {
"stage": { "type": "string", "minLength": 1 },
"startedAt": { "type": "string", "minLength": 1 },
"completedAt": { "type": "string", "minLength": 1 },
"latencyMs": { "type": "number", "minimum": 0 },
"witnessArtifactUri": { "type": "string", "minLength": 1 }
}
},
"provenance": {
"type": "object",
"required": [
"pipelineId",
"runId",
"runtimeId"
],
"additionalProperties": false,
"properties": {
"pipelineId": { "type": "string", "minLength": 1 },
"runId": { "type": "string", "minLength": 1 },
"runtimeId": { "type": "string", "minLength": 1 },
"agentId": { "type": "string", "minLength": 1 },
"agentRole": { "type": "string", "minLength": 1 },
"toolVersion": { "type": "string", "minLength": 1 }
}
}
}
}

View file

@ -116,6 +116,10 @@ namespace GameCult.Networking
/// </summary>
public const string SimulationConsensusCandidate = "cultnet.simulation_consensus_candidate.v0";
/// <summary>
/// witness artifact bundle document contract identifier.
/// </summary>
public const string WitnessArtifactBundle = "cultnet.witness_artifact_bundle.v0";
/// <summary>
/// CultMesh Verse catalog request contract identifier.
/// </summary>
public const string VerseCatalogRequest = "cultmesh.verse_catalog_request.v0";

View file

@ -375,6 +375,14 @@ namespace GameCult.Networking
SchemaVersion = "ghostlight.agent_state.v0",
DocumentType = "ghostlight.agent-state",
WireContracts = new[] { CultNetWireContracts.SchemaV0 }
},
new SchemaResourceSpec
{
FileName = "cultnet.witness-artifact-bundle.schema.json",
Kind = "document_payload",
SchemaVersion = CultNetSchemaVersions.WitnessArtifactBundle,
DocumentType = "cultnet.witness-artifact-bundle",
WireContracts = new[] { CultNetWireContracts.SchemaV0 }
}
};
}

View file

@ -0,0 +1,254 @@
using System;
using GameCult.Caching;
using MessagePack;
namespace GameCult.Networking
{
/// <summary>
/// Portable manifest for one inspectable pipeline witness bundle.
/// </summary>
[MessagePackObject]
[CultDocument("cultnet.witness_artifact_bundle", CultNetSchemaVersions.WitnessArtifactBundle)]
public sealed class CultWitnessArtifactBundle
{
/// <summary>
/// Gets or sets the stable bundle identifier.
/// </summary>
[Key(0)]
[CultName]
public string BundleId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the witness family or pipeline kind.
/// </summary>
[Key(1)]
public string WitnessKind { get; set; } = string.Empty;
/// <summary>
/// Gets or sets when the manifest was captured.
/// </summary>
[Key(2)]
public string CapturedAt { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the pinned subject this witness bundle describes.
/// </summary>
[Key(3)]
public CultWitnessSubjectPin Subject { get; set; } = new CultWitnessSubjectPin();
/// <summary>
/// Gets or sets the schema and contract pins required to interpret the witness.
/// </summary>
[Key(4)]
public CultWitnessContractPin[] Contracts { get; set; } = Array.Empty<CultWitnessContractPin>();
/// <summary>
/// Gets or sets the inspectable artifact pointers carried by the bundle.
/// </summary>
[Key(5)]
public CultWitnessArtifactEntry[] Artifacts { get; set; } = Array.Empty<CultWitnessArtifactEntry>();
/// <summary>
/// Gets or sets stage timing witnesses associated with the bundle.
/// </summary>
[Key(6)]
public CultWitnessTimingEntry[] TimingWitnesses { get; set; } = Array.Empty<CultWitnessTimingEntry>();
/// <summary>
/// Gets or sets provenance metadata for the bundle producer.
/// </summary>
[Key(7)]
public CultWitnessProvenance Provenance { get; set; } = new CultWitnessProvenance();
}
/// <summary>
/// Pins the document or payload instance the witness is about.
/// </summary>
[MessagePackObject]
public sealed class CultWitnessSubjectPin
{
/// <summary>
/// Gets or sets the subject document type or domain identifier.
/// </summary>
[Key(0)]
public string DocumentType { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the persisted record key or equivalent stable subject id.
/// </summary>
[Key(1)]
public string SubjectId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the pinned subject schema version.
/// </summary>
[Key(2)]
public string? SchemaVersion { get; set; }
/// <summary>
/// Gets or sets the pinned subject schema identifier when one exists.
/// </summary>
[Key(3)]
public string? SchemaId { get; set; }
/// <summary>
/// Gets or sets the pinned subject content hash when one exists.
/// </summary>
[Key(4)]
public string? ContentHash { get; set; }
}
/// <summary>
/// Pins one schema or contract dependency for the witness bundle.
/// </summary>
[MessagePackObject]
public sealed class CultWitnessContractPin
{
/// <summary>
/// Gets or sets the contract role inside the bundle.
/// </summary>
[Key(0)]
public string Role { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the stable schema identifier.
/// </summary>
[Key(1)]
public string SchemaId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the pinned schema version.
/// </summary>
[Key(2)]
public string? SchemaVersion { get; set; }
/// <summary>
/// Gets or sets the canonical schema content hash when one exists.
/// </summary>
[Key(3)]
public string? ContentHash { get; set; }
}
/// <summary>
/// Points at one inspectable artifact emitted by the witnessed pipeline.
/// </summary>
[MessagePackObject]
public sealed class CultWitnessArtifactEntry
{
/// <summary>
/// Gets or sets the artifact role inside the witness bundle.
/// </summary>
[Key(0)]
public string Role { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the portable URI for the artifact.
/// </summary>
[Key(1)]
public string Uri { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the artifact media type.
/// </summary>
[Key(2)]
public string MediaType { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the artifact content hash when one exists.
/// </summary>
[Key(3)]
public string? ContentHash { get; set; }
/// <summary>
/// Gets or sets the artifact size in bytes when known.
/// </summary>
[Key(4)]
public long? ByteLength { get; set; }
/// <summary>
/// Gets or sets when the artifact was produced.
/// </summary>
[Key(5)]
public string? ProducedAt { get; set; }
}
/// <summary>
/// Records timestamp and latency evidence for one stage in the witnessed pipeline.
/// </summary>
[MessagePackObject]
public sealed class CultWitnessTimingEntry
{
/// <summary>
/// Gets or sets the pipeline stage name.
/// </summary>
[Key(0)]
public string Stage { get; set; } = string.Empty;
/// <summary>
/// Gets or sets when the stage started.
/// </summary>
[Key(1)]
public string StartedAt { get; set; } = string.Empty;
/// <summary>
/// Gets or sets when the stage completed.
/// </summary>
[Key(2)]
public string CompletedAt { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the observed stage latency in milliseconds.
/// </summary>
[Key(3)]
public double LatencyMs { get; set; }
/// <summary>
/// Gets or sets the URI for the timing witness artifact when one exists.
/// </summary>
[Key(4)]
public string? WitnessArtifactUri { get; set; }
}
/// <summary>
/// Carries provenance metadata for the witness bundle producer.
/// </summary>
[MessagePackObject]
public sealed class CultWitnessProvenance
{
/// <summary>
/// Gets or sets the stable pipeline identifier.
/// </summary>
[Key(0)]
public string PipelineId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the pipeline run identifier.
/// </summary>
[Key(1)]
public string RunId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the runtime identifier that produced the bundle.
/// </summary>
[Key(2)]
public string RuntimeId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the producing agent identifier when one exists.
/// </summary>
[Key(3)]
public string? AgentId { get; set; }
/// <summary>
/// Gets or sets the producing agent role when one exists.
/// </summary>
[Key(4)]
public string? AgentRole { get; set; }
/// <summary>
/// Gets or sets the pipeline or tool version when one exists.
/// </summary>
[Key(5)]
public string? ToolVersion { get; set; }
}
}

View file

@ -2286,6 +2286,39 @@ namespace GameCult.Networking.Tests
Assert.That(ghostlight.Kind, Is.EqualTo("document_payload"));
}
[Test]
public void CultWitnessArtifactBundle_Has_Stable_CacheContract()
{
var descriptor = new CultCache().Registry.GetRequired<CultWitnessArtifactBundle>();
Assert.That(descriptor.SchemaName, Is.EqualTo("cultnet.witness_artifact_bundle"));
Assert.That(descriptor.SchemaVersion, Is.EqualTo(CultNetSchemaVersions.WitnessArtifactBundle));
Assert.That(descriptor.NameMember, Is.EqualTo(nameof(CultWitnessArtifactBundle.BundleId)));
Assert.That(descriptor.CanonicalSchemaJson, Is.EqualTo(
"{\"schemaName\":\"cultnet.witness_artifact_bundle\",\"schemaVersion\":\"cultnet.witness_artifact_bundle.v0\",\"members\":[{\"slot\":0,\"name\":\"BundleId\",\"type\":\"System.String\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":true},{\"slot\":1,\"name\":\"WitnessKind\",\"type\":\"System.String\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":false},{\"slot\":2,\"name\":\"CapturedAt\",\"type\":\"System.String\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":false},{\"slot\":3,\"name\":\"Subject\",\"type\":\"GameCult.Networking.CultWitnessSubjectPin\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":false},{\"slot\":4,\"name\":\"Contracts\",\"type\":\"GameCult.Networking.CultWitnessContractPin[]\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":false},{\"slot\":5,\"name\":\"Artifacts\",\"type\":\"GameCult.Networking.CultWitnessArtifactEntry[]\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":false},{\"slot\":6,\"name\":\"TimingWitnesses\",\"type\":\"GameCult.Networking.CultWitnessTimingEntry[]\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":false},{\"slot\":7,\"name\":\"Provenance\",\"type\":\"GameCult.Networking.CultWitnessProvenance\",\"isReference\":false,\"many\":false,\"targetSchemaName\":null,\"indexAlias\":null,\"isName\":false}]}"));
}
[Test]
public void CultNetSchemaRegistry_BuiltInCatalog_Advertises_WitnessArtifactBundle()
{
var response = CultNetSchemaRegistry.BuiltIn.CreateCatalogResponse(
new CultNetSchemaCatalogRequestMessage
{
MessageId = "catalog-witness",
IncludeSchemaJson = true
});
var witnessBundle = Array.Find(response.Schemas,
schema => schema.SchemaVersion == CultNetSchemaVersions.WitnessArtifactBundle);
Assert.That(witnessBundle, Is.Not.Null);
Assert.That(witnessBundle!.DocumentType, Is.EqualTo("cultnet.witness-artifact-bundle"));
Assert.That(witnessBundle.Kind, Is.EqualTo("document_payload"));
Assert.That(witnessBundle.WireContracts, Is.EqualTo([CultNetWireContracts.SchemaV0]));
Assert.That(witnessBundle.SchemaJson, Does.Contain("\"bundleId\""));
Assert.That(witnessBundle.SchemaJson, Does.Contain("\"timingWitnesses\""));
Assert.That(witnessBundle.SchemaJson, Does.Contain("\"witnessArtifactUri\""));
}
[Test]
public void Server_Separates_Connection_And_Auth_RateLimits()
{