// // CvdiagSchema.cs — C# binding for the CVDIAG flap-observability envelope. // CODEGEN'd from the single IR `showcase/harness/src/cvdiag/schema.json` // ($id https://copilotkit.ai/schemas/cvdiag/v1.json, schema_version 1). // Plan unit L0-D; spec §5. Mirrors the canonical TS `schema.ts` / Pydantic / // Java bindings so all five languages share one wire contract. // // Source of truth: schema.json. To regenerate, re-run the .NET codegen step // (see L1-F build-context note in the slot report) — DO NOT hand-edit the // enum members / record fields below; edit schema.ts (→ schema.json) and // re-emit. The 3 enums, the EdgeHeaders record, the CvdiagEnvelope record, // the 29 per-boundary metadata records, and the closed key tables below are // derived 1:1 from schema.json `$defs`. // // System.Text.Json source-generation: `CvdiagJsonContext` carries the // [JsonSerializable] attributes so serialization is AOT/trim-safe and never // uses reflection-based serializers at runtime. // #nullable enable using System.Text.Json; using System.Text.Json.Serialization; namespace Copilotkit.Showcase.Cvdiag; /// Emitting layer (schema.json `$defs.layers`). [JsonConverter(typeof(JsonStringEnumConverter))] public enum CvdiagLayer { [JsonStringEnumMemberName("probe")] Probe, [JsonStringEnumMemberName("backend")] Backend, [JsonStringEnumMemberName("aimock")] Aimock, } /// Event outcome (schema.json `$defs.outcomes`). [JsonConverter(typeof(JsonStringEnumConverter))] public enum CvdiagOutcome { [JsonStringEnumMemberName("ok")] Ok, [JsonStringEnumMemberName("err")] Err, [JsonStringEnumMemberName("timeout")] Timeout, [JsonStringEnumMemberName("info")] Info, } /// /// Closed boundary enum (schema.json `$defs.boundaries`, 33 members). The /// dotted wire values (e.g. "backend.response.complete") are carried by the /// [JsonStringEnumMemberName] attributes; the C# member names are the /// PascalCase'd, dot-stripped equivalents. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum CvdiagBoundary { [JsonStringEnumMemberName("probe.start")] ProbeStart, [JsonStringEnumMemberName("probe.navigate.complete")] ProbeNavigateComplete, [JsonStringEnumMemberName("probe.message.send")] ProbeMessageSend, [JsonStringEnumMemberName("probe.dom.container.mount")] ProbeDomContainerMount, [JsonStringEnumMemberName("probe.dom.firsttoken")] ProbeDomFirsttoken, [JsonStringEnumMemberName("probe.dom.alternate_content")] ProbeDomAlternateContent, [JsonStringEnumMemberName("probe.sse.event")] ProbeSseEvent, [JsonStringEnumMemberName("probe.sse.aborted")] ProbeSseAborted, [JsonStringEnumMemberName("probe.network.error")] ProbeNetworkError, [JsonStringEnumMemberName("probe.network.response")] ProbeNetworkResponse, [JsonStringEnumMemberName("probe.console.error")] ProbeConsoleError, [JsonStringEnumMemberName("probe.exit")] ProbeExit, [JsonStringEnumMemberName("backend.request.ingress")] BackendRequestIngress, [JsonStringEnumMemberName("backend.agent.enter")] BackendAgentEnter, [JsonStringEnumMemberName("backend.llm.call.start")] BackendLlmCallStart, [JsonStringEnumMemberName("backend.llm.call.heartbeat")] BackendLlmCallHeartbeat, [JsonStringEnumMemberName("backend.llm.call.response")] BackendLlmCallResponse, [JsonStringEnumMemberName("backend.sse.first_byte")] BackendSseFirstByte, [JsonStringEnumMemberName("backend.sse.event")] BackendSseEvent, [JsonStringEnumMemberName("backend.sse.aborted")] BackendSseAborted, [JsonStringEnumMemberName("backend.agent.exit")] BackendAgentExit, [JsonStringEnumMemberName("backend.response.complete")] BackendResponseComplete, [JsonStringEnumMemberName("backend.error.caught")] BackendErrorCaught, [JsonStringEnumMemberName("aimock.request.ingress")] AimockRequestIngress, [JsonStringEnumMemberName("aimock.match.decision")] AimockMatchDecision, [JsonStringEnumMemberName("aimock.response.start")] AimockResponseStart, [JsonStringEnumMemberName("aimock.sse.chunk")] AimockSseChunk, [JsonStringEnumMemberName("aimock.response.aborted")] AimockResponseAborted, [JsonStringEnumMemberName("aimock.response.complete")] AimockResponseComplete, [JsonStringEnumMemberName("cvdiag.purge_audit")] CvdiagPurgeAudit, [JsonStringEnumMemberName("cvdiag.collision_detected")] CvdiagCollisionDetected, [JsonStringEnumMemberName("cvdiag.queue_dropped")] CvdiagQueueDropped, [JsonStringEnumMemberName("cvdiag.metadata_dropped")] CvdiagMetadataDropped, } /// /// The closed 9-key edge-header bag (schema.json `edge_headers`). Every field /// is always present on the wire; an absent header serializes as null. The /// hyphenated wire names are carried by [JsonPropertyName]. /// public sealed record EdgeHeaders { [JsonPropertyName("cf-ray")] public string? CfRay { get; init; } [JsonPropertyName("cf-mitigated")] public string? CfMitigated { get; init; } [JsonPropertyName("cf-cache-status")] public string? CfCacheStatus { get; init; } [JsonPropertyName("x-railway-edge")] public string? XRailwayEdge { get; init; } [JsonPropertyName("x-railway-request-id")] public string? XRailwayRequestId { get; init; } [JsonPropertyName("x-hikari-trace")] public string? XHikariTrace { get; init; } [JsonPropertyName("retry-after")] public string? RetryAfter { get; init; } [JsonPropertyName("via")] public string? Via { get; init; } [JsonPropertyName("server")] public string? Server { get; init; } /// Always 9 — the closed allow-list cardinality. public int KeyCount() => 9; /// All 9 field values (for deny-leakage assertions/inspection). public IReadOnlyList AllValues() => new[] { CfRay, CfMitigated, CfCacheStatus, XRailwayEdge, XRailwayRequestId, XHikariTrace, RetryAfter, Via, Server, }; } /// /// The CVDIAG envelope (schema.json root). Field/property names map to the /// snake_case wire keys via [JsonPropertyName]; the two optional `_`-prefixed /// stamp flags are written only when true (DefaultIgnoreCondition handles the /// false/null case via the source-gen context options). /// public sealed record CvdiagEnvelope { [JsonPropertyName("schema_version")] public int SchemaVersion { get; init; } = CvdiagSchema.SchemaVersion; [JsonPropertyName("test_id")] public string TestId { get; init; } = ""; [JsonPropertyName("trace_id")] public string TraceId { get; init; } = ""; [JsonPropertyName("span_id")] public string SpanId { get; init; } = ""; [JsonPropertyName("parent_span_id")] public string? ParentSpanId { get; init; } [JsonPropertyName("layer")] public CvdiagLayer Layer { get; init; } [JsonPropertyName("boundary")] public CvdiagBoundary Boundary { get; init; } [JsonPropertyName("slug")] public string Slug { get; init; } = ""; [JsonPropertyName("demo")] public string Demo { get; init; } = ""; [JsonPropertyName("ts")] public string Ts { get; init; } = ""; [JsonPropertyName("mono_ns")] public long MonoNs { get; init; } [JsonPropertyName("duration_ms")] public long? DurationMs { get; init; } [JsonPropertyName("outcome")] public CvdiagOutcome Outcome { get; init; } [JsonPropertyName("edge_headers")] public EdgeHeaders EdgeHeaders { get; init; } = new(); [JsonPropertyName("metadata")] public Dictionary Metadata { get; init; } = new(); [JsonPropertyName("_metadata_dropped")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool MetadataDropped { get; set; } [JsonPropertyName("_truncated")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Truncated { get; set; } } // ── Per-boundary metadata records (29; schema.json `$defs.boundary_metadata_keys`) ── // // One record per metadata-bearing boundary. These are convenience/typed views // over the boundary's closed key set; the on-wire `metadata` bag stays an open // JSON object (Dictionary) so a single envelope type carries // every boundary. Emitters MAY construct these and pass their property bag to // CvdiagEmitArgs.Metadata. Field names mirror the schema.json metadata keys. public sealed record ProbeStartMeta { public string? Url { get; init; } public string? Viewport { get; init; } } public sealed record ProbeNavigateCompleteMeta { public string? Url { get; init; } public long? NavMs { get; init; } public int? HttpStatus { get; init; } } public sealed record ProbeMessageSendMeta { public int? MessageIndex { get; init; } public int? CharCount { get; init; } public string? Demo { get; init; } } public sealed record ProbeDomContainerMountMeta { public long? DeltaMsFromStart { get; init; } } public sealed record ProbeDomFirsttokenMeta { public long? DeltaMsFromStart { get; init; } public int? TextLength { get; init; } } public sealed record ProbeDomAlternateContentMeta { public Dictionary? ChildTypeHistogram { get; init; } } public sealed record ProbeSseEventMeta { public string? EventType { get; init; } public int? PayloadSizeBytes { get; init; } public int? SequenceNum { get; init; } } public sealed record ProbeSseAbortedMeta { public string? TerminationKind { get; init; } public int? BytesBeforeAbort { get; init; } } public sealed record ProbeNetworkErrorMeta { public string? Url { get; init; } public string? ErrorClass { get; init; } public int? ResponseStatus { get; init; } } public sealed record ProbeNetworkResponseMeta { public string? Url { get; init; } public int? Status { get; init; } public int? ContentLength { get; init; } public long? DurationMs { get; init; } } public sealed record ProbeConsoleErrorMeta { public string? Level { get; init; } public string? MessageScrubbed { get; init; } public string? SourceFile { get; init; } public string? LineCol { get; init; } } public sealed record ProbeExitMeta { public string? TerminalOutcome { get; init; } public long? TotalDurationMs { get; init; } public int? SseEventCount { get; init; } public long? FirstTokenDeltaMs { get; init; } } public sealed record BackendRequestIngressMeta { public string? Method { get; init; } public string? Path { get; init; } public int? ContentLength { get; init; } } public sealed record BackendAgentEnterMeta { public string? AgentName { get; init; } public string? ModelId { get; init; } } public sealed record BackendLlmCallStartMeta { public string? Provider { get; init; } public string? Model { get; init; } public int? PromptTokenCountEstimate { get; init; } } public sealed record BackendLlmCallHeartbeatMeta { public long? ElapsedMsSinceStart { get; init; } } public sealed record BackendLlmCallResponseMeta { public string? Provider { get; init; } public string? Model { get; init; } public int? ResponseTokenCount { get; init; } public long? LatencyMs { get; init; } public string? ErrorClass { get; init; } } public sealed record BackendSseFirstByteMeta { public long? DeltaMsFromIngress { get; init; } } public sealed record BackendSseEventMeta { public string? EventType { get; init; } public int? PayloadSizeBytes { get; init; } public int? SequenceNum { get; init; } } public sealed record BackendSseAbortedMeta { public string? TerminationKind { get; init; } public int? BytesBeforeAbort { get; init; } } public sealed record BackendAgentExitMeta { public string? TerminalOutcome { get; init; } public long? TotalDurationMs { get; init; } } public sealed record BackendResponseCompleteMeta { public int? HttpStatus { get; init; } public int? ContentLength { get; init; } public long? TotalDurationMs { get; init; } public int? SseEventCount { get; init; } } public sealed record BackendErrorCaughtMeta { public string? ExceptionType { get; init; } public string? MessageScrubbed { get; init; } public string? StackBrief { get; init; } public bool? Truncated { get; init; } } public sealed record AimockRequestIngressMeta { public string? Path { get; init; } public int? ContentLength { get; init; } public string? MatchKeys { get; init; } } public sealed record AimockMatchDecisionMeta { public string? FixtureId { get; init; } public double? MatchScore { get; init; } public IReadOnlyList? RejectReasons { get; init; } } public sealed record AimockResponseStartMeta { public long? DeltaMsFromIngress { get; init; } } public sealed record AimockSseChunkMeta { public int? ChunkSizeBytes { get; init; } public int? SequenceNum { get; init; } } public sealed record AimockResponseAbortedMeta { public string? TerminationKind { get; init; } public int? BytesBeforeAbort { get; init; } } public sealed record AimockResponseCompleteMeta { public int? HttpStatus { get; init; } public int? TotalBytes { get; init; } public long? TotalDurationMs { get; init; } public int? ChunkCount { get; init; } } /// /// Static schema tables derived 1:1 from schema.json `$defs`. These back the /// closed-world metadata filter in and the /// codegen-coverage assertions. /// public static class CvdiagSchema { /// schema.json `schema_version` (const 1). public const int SchemaVersion = 1; /// schema.json `$defs.edge_header_keys` (9 keys, wire order). public static readonly IReadOnlyList EdgeHeaderKeys = new[] { "cf-ray", "cf-mitigated", "cf-cache-status", "x-railway-edge", "x-railway-request-id", "x-hikari-trace", "retry-after", "via", "server", }; /// schema.json `$defs.boundaries` (all 33 wire values). public static readonly IReadOnlyList AllBoundaries = new[] { "probe.start", "probe.navigate.complete", "probe.message.send", "probe.dom.container.mount", "probe.dom.firsttoken", "probe.dom.alternate_content", "probe.sse.event", "probe.sse.aborted", "probe.network.error", "probe.network.response", "probe.console.error", "probe.exit", "backend.request.ingress", "backend.agent.enter", "backend.llm.call.start", "backend.llm.call.heartbeat", "backend.llm.call.response", "backend.sse.first_byte", "backend.sse.event", "backend.sse.aborted", "backend.agent.exit", "backend.response.complete", "backend.error.caught", "aimock.request.ingress", "aimock.match.decision", "aimock.response.start", "aimock.sse.chunk", "aimock.response.aborted", "aimock.response.complete", "cvdiag.purge_audit", "cvdiag.collision_detected", "cvdiag.queue_dropped", "cvdiag.metadata_dropped", }; /// /// schema.json `$defs.boundary_metadata_keys` — the closed metadata key /// set per data-plane boundary (29 entries; the 4 `cvdiag.*` accounting /// boundaries carry no closed key set and ride their payload verbatim). /// Keyed by the dotted wire boundary value. /// public static readonly IReadOnlyDictionary> BoundaryMetadataKeys = new Dictionary> { ["probe.start"] = new[] { "url", "viewport" }, ["probe.navigate.complete"] = new[] { "url", "nav_ms", "http_status" }, ["probe.message.send"] = new[] { "message_index", "char_count", "demo" }, ["probe.dom.container.mount"] = new[] { "delta_ms_from_start" }, ["probe.dom.firsttoken"] = new[] { "delta_ms_from_start", "text_length" }, ["probe.dom.alternate_content"] = new[] { "child_type_histogram" }, ["probe.sse.event"] = new[] { "event_type", "payload_size_bytes", "sequence_num" }, ["probe.sse.aborted"] = new[] { "termination_kind", "bytes_before_abort" }, ["probe.network.error"] = new[] { "url", "error_class", "response_status" }, ["probe.network.response"] = new[] { "url", "status", "content_length", "duration_ms" }, ["probe.console.error"] = new[] { "level", "message_scrubbed", "source_file", "line_col" }, ["probe.exit"] = new[] { "terminal_outcome", "total_duration_ms", "sse_event_count", "first_token_delta_ms" }, ["backend.request.ingress"] = new[] { "method", "path", "content_length" }, ["backend.agent.enter"] = new[] { "agent_name", "model_id" }, ["backend.llm.call.start"] = new[] { "provider", "model", "prompt_token_count_estimate" }, ["backend.llm.call.heartbeat"] = new[] { "elapsed_ms_since_start" }, ["backend.llm.call.response"] = new[] { "provider", "model", "response_token_count", "latency_ms", "error_class" }, ["backend.sse.first_byte"] = new[] { "delta_ms_from_ingress" }, ["backend.sse.event"] = new[] { "event_type", "payload_size_bytes", "sequence_num" }, ["backend.sse.aborted"] = new[] { "termination_kind", "bytes_before_abort" }, ["backend.agent.exit"] = new[] { "terminal_outcome", "total_duration_ms" }, ["backend.response.complete"] = new[] { "http_status", "content_length", "total_duration_ms", "sse_event_count" }, ["backend.error.caught"] = new[] { "exception_type", "message_scrubbed", "stack_brief", "truncated" }, ["aimock.request.ingress"] = new[] { "path", "content_length", "match_keys" }, ["aimock.match.decision"] = new[] { "fixture_id", "match_score", "reject_reasons" }, ["aimock.response.start"] = new[] { "delta_ms_from_ingress" }, ["aimock.sse.chunk"] = new[] { "chunk_size_bytes", "sequence_num" }, ["aimock.response.aborted"] = new[] { "termination_kind", "bytes_before_abort" }, ["aimock.response.complete"] = new[] { "http_status", "total_bytes", "total_duration_ms", "chunk_count" }, }; /// The dotted wire value for a boundary enum member. public static string WireValue(CvdiagBoundary boundary) => boundary switch { CvdiagBoundary.ProbeStart => "probe.start", CvdiagBoundary.ProbeNavigateComplete => "probe.navigate.complete", CvdiagBoundary.ProbeMessageSend => "probe.message.send", CvdiagBoundary.ProbeDomContainerMount => "probe.dom.container.mount", CvdiagBoundary.ProbeDomFirsttoken => "probe.dom.firsttoken", CvdiagBoundary.ProbeDomAlternateContent => "probe.dom.alternate_content", CvdiagBoundary.ProbeSseEvent => "probe.sse.event", CvdiagBoundary.ProbeSseAborted => "probe.sse.aborted", CvdiagBoundary.ProbeNetworkError => "probe.network.error", CvdiagBoundary.ProbeNetworkResponse => "probe.network.response", CvdiagBoundary.ProbeConsoleError => "probe.console.error", CvdiagBoundary.ProbeExit => "probe.exit", CvdiagBoundary.BackendRequestIngress => "backend.request.ingress", CvdiagBoundary.BackendAgentEnter => "backend.agent.enter", CvdiagBoundary.BackendLlmCallStart => "backend.llm.call.start", CvdiagBoundary.BackendLlmCallHeartbeat => "backend.llm.call.heartbeat", CvdiagBoundary.BackendLlmCallResponse => "backend.llm.call.response", CvdiagBoundary.BackendSseFirstByte => "backend.sse.first_byte", CvdiagBoundary.BackendSseEvent => "backend.sse.event", CvdiagBoundary.BackendSseAborted => "backend.sse.aborted", CvdiagBoundary.BackendAgentExit => "backend.agent.exit", CvdiagBoundary.BackendResponseComplete => "backend.response.complete", CvdiagBoundary.BackendErrorCaught => "backend.error.caught", CvdiagBoundary.AimockRequestIngress => "aimock.request.ingress", CvdiagBoundary.AimockMatchDecision => "aimock.match.decision", CvdiagBoundary.AimockResponseStart => "aimock.response.start", CvdiagBoundary.AimockSseChunk => "aimock.sse.chunk", CvdiagBoundary.AimockResponseAborted => "aimock.response.aborted", CvdiagBoundary.AimockResponseComplete => "aimock.response.complete", CvdiagBoundary.CvdiagPurgeAudit => "cvdiag.purge_audit", CvdiagBoundary.CvdiagCollisionDetected => "cvdiag.collision_detected", CvdiagBoundary.CvdiagQueueDropped => "cvdiag.queue_dropped", CvdiagBoundary.CvdiagMetadataDropped => "cvdiag.metadata_dropped", _ => throw new ArgumentOutOfRangeException(nameof(boundary), boundary, null), }; } /// /// System.Text.Json source-generation context. Carries the [JsonSerializable] /// roots so serialization is reflection-free (AOT/trim-safe). The snake_case /// policy is NOT applied globally — every field already declares its exact /// wire name via [JsonPropertyName], so we keep the default naming and rely on /// the explicit attributes (avoids double-transforming hyphenated edge keys). /// /// IMPORTANT: we do NOT set a context-wide DefaultIgnoreCondition. The schema /// REQUIRES the nullable fields (`parent_span_id`, `duration_ms`) to appear on /// the wire as `null`, so a global WhenWritingDefault/WhenWritingNull would /// drop them and break the closed-world `required` contract. The only fields /// that should be omitted when default are the two `_`-prefixed stamp flags, /// which carry their own per-property [JsonIgnore(WhenWritingDefault)]. /// [JsonSourceGenerationOptions(WriteIndented = false)] [JsonSerializable(typeof(CvdiagEnvelope))] [JsonSerializable(typeof(EdgeHeaders))] [JsonSerializable(typeof(Dictionary))] public partial class CvdiagJsonContext : JsonSerializerContext { }