Add witness artifact bundle contract (topic_95053275-0a5a-4b05-806e-671dc5cff4d1)
This commit is contained in:
parent
ebdd2272f4
commit
4ddf30e545
5 changed files with 419 additions and 0 deletions
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
254
src/GameCult.Networking/CultWitnessArtifactBundle.cs
Normal file
254
src/GameCult.Networking/CultWitnessArtifactBundle.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue