diff --git a/Directory.Build.props b/Directory.Build.props
index 3b37331..e8508a1 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,14 +7,12 @@
-
- 1.0.0
+
+ 4.2.0
$(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix)
diff --git a/README.md b/README.md
index 35ffef4..7cf925c 100644
--- a/README.md
+++ b/README.md
@@ -22,15 +22,14 @@ dotnet add package Qyl.Telemetry.SemanticConventions
```
The three supported packages are published on
-[nuget.org](https://www.nuget.org/profiles/ANcpLua) at `1.0.0`. `.Analyzers` is
+[nuget.org](https://www.nuget.org/profiles/ANcpLua); this change targets `4.2.0`. `.Analyzers` is
preview-only and is not among them. Incubating APIs intentionally track unstable
upstream conventions and may change between minor releases.
**Coming from `Qyl.OpenTelemetry.SemanticConventions`?** These are new package IDs, not
new versions of the old ones. The `Qyl.OpenTelemetry.SemanticConventions*` IDs stop at
-`4.0.0` and receive no further releases; change the ID and take `1.0.0`. The lower number
-is not a downgrade — the new family starts its own version line, and the generated
-namespace ships as its birth ABI rather than as a break to anything already published.
+`4.0.0` and receive no further releases; change to the `Qyl.Telemetry.*` family. The new
+family began at `1.0.0` and now follows its own monotonic release line.
## Choosing between the compiled packages and the source generator
diff --git a/Version.props b/Version.props
index 5f38b56..abe0b49 100644
--- a/Version.props
+++ b/Version.props
@@ -13,7 +13,7 @@
3.2.2
9.4.0
- 7.6.0
+ 7.9.0
10.0.10
diff --git a/eng/semconv/attributes.lock.sha256 b/eng/semconv/attributes.lock.sha256
index d2cc2ed..1fa2cd2 100644
--- a/eng/semconv/attributes.lock.sha256
+++ b/eng/semconv/attributes.lock.sha256
@@ -1 +1 @@
-61af43292e106cab4cf587603c2c9cbb6da60b8ad4f17daef88a393e9a8333b9
+de15808eede6e525c3deaf61b5709204dc0bf68ee6b5f05c426559843494ca8a
diff --git a/src/Qyl.Telemetry.SemanticConventions.Analyzers/SemconvRegistryFacts.g.cs b/src/Qyl.Telemetry.SemanticConventions.Analyzers/SemconvRegistryFacts.g.cs
index 932cccf..f8afce0 100644
--- a/src/Qyl.Telemetry.SemanticConventions.Analyzers/SemconvRegistryFacts.g.cs
+++ b/src/Qyl.Telemetry.SemanticConventions.Analyzers/SemconvRegistryFacts.g.cs
@@ -793,6 +793,15 @@ internal static class SemconvRegistryFacts
["process.vpid"] = SemconvAttributeValueKind.Integer,
["process.working_directory"] = SemconvAttributeValueKind.String,
["profile.frame.type"] = SemconvAttributeValueKind.String,
+ ["qyl.agent.diagnostic.check.count"] = SemconvAttributeValueKind.Integer,
+ ["qyl.agent.diagnostic.check.failed_count"] = SemconvAttributeValueKind.Integer,
+ ["qyl.agent.diagnostic.extension.id"] = SemconvAttributeValueKind.String,
+ ["qyl.agent.diagnostic.format.version"] = SemconvAttributeValueKind.Integer,
+ ["qyl.agent.diagnostic.outcome"] = SemconvAttributeValueKind.String,
+ ["qyl.agent.diagnostic.phase"] = SemconvAttributeValueKind.String,
+ ["qyl.agent.diagnostic.probe.id"] = SemconvAttributeValueKind.String,
+ ["qyl.agent.diagnostic.snapshot.id"] = SemconvAttributeValueKind.String,
+ ["qyl.agent.diagnostic.variable.count"] = SemconvAttributeValueKind.Integer,
["qyl.capability.id"] = SemconvAttributeValueKind.String,
["qyl.capability.kind"] = SemconvAttributeValueKind.String,
["qyl.exception.source"] = SemconvAttributeValueKind.String,
@@ -806,6 +815,11 @@ internal static class SemconvRegistryFacts
["qyl.mcp.test_case.id"] = SemconvAttributeValueKind.String,
["qyl.mcp.tool.name"] = SemconvAttributeValueKind.String,
["qyl.project.id"] = SemconvAttributeValueKind.String,
+ ["qyl.workflow.agent.id"] = SemconvAttributeValueKind.String,
+ ["qyl.workflow.attempt.id"] = SemconvAttributeValueKind.String,
+ ["qyl.workflow.event.id"] = SemconvAttributeValueKind.String,
+ ["qyl.workflow.run.id"] = SemconvAttributeValueKind.String,
+ ["qyl.workflow.tool_call.id"] = SemconvAttributeValueKind.String,
["qyl.workspace.id"] = SemconvAttributeValueKind.String,
["rpc.connect_rpc.error_code"] = SemconvAttributeValueKind.String,
["rpc.grpc.status_code"] = SemconvAttributeValueKind.String,
@@ -1137,6 +1151,8 @@ private static readonly (string Prefix, SemconvAttributeValueKind Kind)[] Templa
["process.paging.fault_type"] = new[] { "major", "minor" },
["process.state"] = new[] { "defunct", "running", "sleeping", "stopped" },
["profile.frame.type"] = new[] { "beam", "cpython", "dotnet", "go", "jvm", "kernel", "luajit", "native", "perl", "php", "ruby", "rust", "v8js" },
+ ["qyl.agent.diagnostic.outcome"] = new[] { "fail", "not_evaluated", "pass", "unknown" },
+ ["qyl.agent.diagnostic.phase"] = new[] { "checkpoint", "error", "input", "output" },
["rpc.connect_rpc.error_code"] = new[] { "aborted", "already_exists", "cancelled", "data_loss", "deadline_exceeded", "failed_precondition", "internal", "invalid_argument", "not_found", "out_of_range", "permission_denied", "resource_exhausted", "unauthenticated", "unavailable", "unimplemented", "unknown" },
["rpc.grpc.status_code"] = new[] { "0", "1", "10", "11", "12", "13", "14", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9" },
["rpc.message.type"] = new[] { "RECEIVED", "SENT" },
@@ -1190,7 +1206,7 @@ private static readonly (string Prefix, SemconvAttributeValueKind Kind)[] Templa
new(new[] { "Qyl.Collector", "Qyl.Instrumentation.ErrorCapture", "Qyl.OpenTelemetry.AutoInstrumentation", "Qyl.OpenTelemetry.AutoInstrumentation.Database", "Qyl.OpenTelemetry.AutoInstrumentation.NServiceBus", "System.Runtime" }, StringComparer.Ordinal);
private static readonly HashSet KnownEventNames =
- new(new[] { "app.crash", "app.jank", "app.screen.click", "app.widget.click", "az.resource.log", "azure.resource.log", "browser.web_vital", "db.client.operation.exception", "device.app.lifecycle", "exception", "faas.invocation.exception", "feature_flag.evaluation", "gen_ai.client.inference.operation.details", "gen_ai.client.operation.exception", "gen_ai.evaluation.result", "http.client.request.exception", "http.server.request.exception", "messaging.create.exception", "messaging.process.exception", "messaging.receive.exception", "messaging.send.exception", "messaging.settle.exception", "qyl.http.client", "qyl.rpc.grpc", "rpc.client.call.exception", "rpc.message", "rpc.server.call.exception", "session.end", "session.start" }, StringComparer.Ordinal);
+ new(new[] { "app.crash", "app.jank", "app.screen.click", "app.widget.click", "az.resource.log", "azure.resource.log", "browser.web_vital", "db.client.operation.exception", "device.app.lifecycle", "exception", "faas.invocation.exception", "feature_flag.evaluation", "gen_ai.client.inference.operation.details", "gen_ai.client.operation.exception", "gen_ai.evaluation.result", "http.client.request.exception", "http.server.request.exception", "messaging.create.exception", "messaging.process.exception", "messaging.receive.exception", "messaging.send.exception", "messaging.settle.exception", "qyl.agent.diagnostic.snapshot", "qyl.http.client", "qyl.rpc.grpc", "rpc.client.call.exception", "rpc.message", "rpc.server.call.exception", "session.end", "session.start" }, StringComparer.Ordinal);
internal static readonly SemconvSpanRule[] SpanRules =
{
diff --git a/src/Qyl.Telemetry.SemanticConventions.Incubating/Attributes/Qyl/QylAttributes.g.cs b/src/Qyl.Telemetry.SemanticConventions.Incubating/Attributes/Qyl/QylAttributes.g.cs
index 59ca8f7..f551c76 100644
--- a/src/Qyl.Telemetry.SemanticConventions.Incubating/Attributes/Qyl/QylAttributes.g.cs
+++ b/src/Qyl.Telemetry.SemanticConventions.Incubating/Attributes/Qyl/QylAttributes.g.cs
@@ -13,6 +13,103 @@ namespace Qyl.Telemetry.SemanticConventions.Incubating.Attributes.Qyl;
/// Qyl Attributes (incubating).
public static class QylAttributes
{
+ ///
+ /// Number of machine-readable checks in an agent diagnostic state frame.
+ ///
+ public const string AgentDiagnosticCheckCount = "qyl.agent.diagnostic.check.count";
+
+ ///
+ /// Number of failed checks in an agent diagnostic state frame.
+ ///
+ public const string AgentDiagnosticCheckFailedCount = "qyl.agent.diagnostic.check.failed_count";
+
+ ///
+ /// Stable discriminator of the machine-readable agent diagnostic extension.
+ ///
+ public const string AgentDiagnosticExtensionId = "qyl.agent.diagnostic.extension.id";
+
+ ///
+ /// Version of the machine-readable agent diagnostic state-frame format.
+ ///
+ public const string AgentDiagnosticFormatVersion = "qyl.agent.diagnostic.format.version";
+
+ ///
+ /// Deterministically computed aggregate outcome of an agent diagnostic state frame.
+ ///
+ public const string AgentDiagnosticOutcome = "qyl.agent.diagnostic.outcome";
+
+ ///
+ /// Values for the AgentDiagnosticOutcome attribute.
+ ///
+ public static class AgentDiagnosticOutcomeValues
+ {
+ ///
+ /// At least one evaluated diagnostic check failed.
+ ///
+ public const string Fail = "fail";
+
+ ///
+ /// The state frame contained no diagnostic checks.
+ ///
+ public const string NotEvaluated = "not_evaluated";
+
+ ///
+ /// Every evaluated diagnostic check passed.
+ ///
+ public const string Pass = "pass";
+
+ ///
+ /// No failed check was observed, but at least one check could not be evaluated.
+ ///
+ public const string Unknown = "unknown";
+ }
+
+ ///
+ /// Program boundary at which an agent diagnostic state frame was captured.
+ ///
+ public const string AgentDiagnosticPhase = "qyl.agent.diagnostic.phase";
+
+ ///
+ /// Values for the AgentDiagnosticPhase attribute.
+ ///
+ public static class AgentDiagnosticPhaseValues
+ {
+ ///
+ /// State captured at an explicit verification checkpoint.
+ ///
+ public const string Checkpoint = "checkpoint";
+
+ ///
+ /// State captured on an error path.
+ ///
+ public const string Error = "error";
+
+ ///
+ /// State captured before the probed operation.
+ ///
+ public const string Input = "input";
+
+ ///
+ /// State captured after the probed operation.
+ ///
+ public const string Output = "output";
+ }
+
+ ///
+ /// Bounded machine identifier of the diagnostic probe that captured the state frame.
+ ///
+ public const string AgentDiagnosticProbeId = "qyl.agent.diagnostic.probe.id";
+
+ ///
+ /// Stable identifier of one immutable agent diagnostic state frame.
+ ///
+ public const string AgentDiagnosticSnapshotId = "qyl.agent.diagnostic.snapshot.id";
+
+ ///
+ /// Number of dynamic variables described by an agent diagnostic state frame.
+ ///
+ public const string AgentDiagnosticVariableCount = "qyl.agent.diagnostic.variable.count";
+
///
/// Stable identifier of the qyl capability that produced the telemetry.
///
@@ -78,6 +175,31 @@ public static class QylAttributes
///
public const string ProjectId = "qyl.project.id";
+ ///
+ /// Stable workflow agent identifier mirrored into telemetry for journal correlation.
+ ///
+ public const string WorkflowAgentId = "qyl.workflow.agent.id";
+
+ ///
+ /// Stable workflow attempt identifier mirrored into telemetry for journal correlation.
+ ///
+ public const string WorkflowAttemptId = "qyl.workflow.attempt.id";
+
+ ///
+ /// Stable immutable workflow journal event identifier mirrored into telemetry for correlation.
+ ///
+ public const string WorkflowEventId = "qyl.workflow.event.id";
+
+ ///
+ /// Stable workflow run identifier mirrored into telemetry for journal correlation.
+ ///
+ public const string WorkflowRunId = "qyl.workflow.run.id";
+
+ ///
+ /// Stable workflow tool-call identifier mirrored into telemetry for journal correlation.
+ ///
+ public const string WorkflowToolCallId = "qyl.workflow.tool_call.id";
+
///
/// qyl workspace identifier the telemetry belongs to.
///
diff --git a/src/Qyl.Telemetry.SemanticConventions.Incubating/Names/QylTelemetryNames.g.cs b/src/Qyl.Telemetry.SemanticConventions.Incubating/Names/QylTelemetryNames.g.cs
index 66caf75..8685d70 100644
--- a/src/Qyl.Telemetry.SemanticConventions.Incubating/Names/QylTelemetryNames.g.cs
+++ b/src/Qyl.Telemetry.SemanticConventions.Incubating/Names/QylTelemetryNames.g.cs
@@ -55,17 +55,22 @@ public static class Scopes
}
///
- /// Names of the qyl-owned DiagnosticSource bridge events.
+ /// Names of qyl-owned Activity and DiagnosticSource events.
///
public static class Events
{
///
- /// The qyl.http.client bridge event.
+ /// The qyl.agent.diagnostic.snapshot telemetry event.
+ ///
+ public const string QylAgentDiagnosticSnapshot = "qyl.agent.diagnostic.snapshot";
+
+ ///
+ /// The qyl.http.client telemetry event.
///
public const string QylHttpClient = "qyl.http.client";
///
- /// The qyl.rpc.grpc bridge event.
+ /// The qyl.rpc.grpc telemetry event.
///
public const string QylRpcGrpc = "qyl.rpc.grpc";
}
diff --git a/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/Resources/qyl-registry.json b/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/Resources/qyl-registry.json
index a7f834f..f7a93be 100644
--- a/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/Resources/qyl-registry.json
+++ b/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/Resources/qyl-registry.json
@@ -1,6 +1,12 @@
{
- "description": "qyl-owned vocabulary, first-class next to the pinned upstream registries (qyl architecture, loop 1). Attributes here are qyl.* names the platform emits and the collector must recognize; scope_names are the ActivitySource/Meter names qyl constructs; event_names are qyl-owned DiagnosticSource bridge event names. Consumed by emit_analyzer_registry.py (QYL0200/QYL0201 allowlists). Entries under qyl.mcp.* are the experimental staging namespace: deletion-targeted on every upstream registry bump that lands an equivalent.",
+ "description": "qyl-owned vocabulary, first-class next to the pinned upstream registries (qyl architecture, loop 1). Attributes here are qyl.* names the platform emits and the collector must recognize; scope_names are the ActivitySource/Meter names qyl constructs; event_names are qyl-owned Activity or DiagnosticSource event names. Consumed by emit_analyzer_registry.py (QYL0200/QYL0201 allowlists). Entries under qyl.mcp.* are the experimental staging namespace: deletion-targeted on every upstream registry bump that lands an equivalent.",
"attributes": [
+ {
+ "key": "qyl.agent.diagnostic.extension.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Stable discriminator of the machine-readable agent diagnostic extension."
+ },
{
"key": "qyl.capability.id",
"type": "string",
@@ -25,6 +31,138 @@
"stability": "development",
"brief": "qyl auto-instrumentation domain that emitted the span or metric."
},
+ {
+ "key": "qyl.agent.diagnostic.snapshot.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Stable identifier of one immutable agent diagnostic state frame."
+ },
+ {
+ "key": "qyl.agent.diagnostic.format.version",
+ "type": "int",
+ "stability": "development",
+ "brief": "Version of the machine-readable agent diagnostic state-frame format."
+ },
+ {
+ "key": "qyl.agent.diagnostic.probe.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Bounded machine identifier of the diagnostic probe that captured the state frame."
+ },
+ {
+ "key": "qyl.agent.diagnostic.phase",
+ "type": {
+ "members": [
+ {
+ "id": "input",
+ "value": "input",
+ "stability": "development",
+ "brief": "State captured before the probed operation."
+ },
+ {
+ "id": "output",
+ "value": "output",
+ "stability": "development",
+ "brief": "State captured after the probed operation."
+ },
+ {
+ "id": "error",
+ "value": "error",
+ "stability": "development",
+ "brief": "State captured on an error path."
+ },
+ {
+ "id": "checkpoint",
+ "value": "checkpoint",
+ "stability": "development",
+ "brief": "State captured at an explicit verification checkpoint."
+ }
+ ]
+ },
+ "stability": "development",
+ "brief": "Program boundary at which an agent diagnostic state frame was captured."
+ },
+ {
+ "key": "qyl.agent.diagnostic.outcome",
+ "type": {
+ "members": [
+ {
+ "id": "pass",
+ "value": "pass",
+ "stability": "development",
+ "brief": "Every evaluated diagnostic check passed."
+ },
+ {
+ "id": "fail",
+ "value": "fail",
+ "stability": "development",
+ "brief": "At least one evaluated diagnostic check failed."
+ },
+ {
+ "id": "unknown",
+ "value": "unknown",
+ "stability": "development",
+ "brief": "No failed check was observed, but at least one check could not be evaluated."
+ },
+ {
+ "id": "not_evaluated",
+ "value": "not_evaluated",
+ "stability": "development",
+ "brief": "The state frame contained no diagnostic checks."
+ }
+ ]
+ },
+ "stability": "development",
+ "brief": "Deterministically computed aggregate outcome of an agent diagnostic state frame."
+ },
+ {
+ "key": "qyl.agent.diagnostic.variable.count",
+ "type": "int",
+ "stability": "development",
+ "brief": "Number of dynamic variables described by an agent diagnostic state frame."
+ },
+ {
+ "key": "qyl.agent.diagnostic.check.count",
+ "type": "int",
+ "stability": "development",
+ "brief": "Number of machine-readable checks in an agent diagnostic state frame."
+ },
+ {
+ "key": "qyl.agent.diagnostic.check.failed_count",
+ "type": "int",
+ "stability": "development",
+ "brief": "Number of failed checks in an agent diagnostic state frame."
+ },
+ {
+ "key": "qyl.workflow.run.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Stable workflow run identifier mirrored into telemetry for journal correlation."
+ },
+ {
+ "key": "qyl.workflow.event.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Stable immutable workflow journal event identifier mirrored into telemetry for correlation."
+ },
+ {
+ "key": "qyl.workflow.attempt.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Stable workflow attempt identifier mirrored into telemetry for journal correlation."
+ },
+ {
+ "key": "qyl.workflow.agent.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Stable workflow agent identifier mirrored into telemetry for journal correlation."
+ },
+ {
+ "key": "qyl.workflow.tool_call.id",
+ "type": "string",
+ "stability": "development",
+ "brief": "Stable workflow tool-call identifier mirrored into telemetry for journal correlation."
+ },
{
"key": "qyl.project.id",
"type": "string",
@@ -96,6 +234,7 @@
"System.Runtime"
],
"event_names": [
+ "qyl.agent.diagnostic.snapshot",
"qyl.http.client",
"qyl.rpc.grpc"
]
diff --git a/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/emit_attributes.py b/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/emit_attributes.py
index 2969ba4..02b7c90 100644
--- a/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/emit_attributes.py
+++ b/src/Qyl.Telemetry.SemanticConventions.SourceGeneration/scripts/emit_attributes.py
@@ -637,7 +637,7 @@ def attr_ident(a):
# --------------------------------------------------------------------------- #
-# qyl-owned scope + bridge event names #
+# qyl-owned scope + telemetry event names #
# --------------------------------------------------------------------------- #
NAMES_NAMESPACE = "Qyl.Telemetry.SemanticConventions.Incubating.Names"
NAMES_CLASS = "QylTelemetryNames"
@@ -700,7 +700,7 @@ def emit_names_file(qyl_registry) -> str:
w.line(f"{pad}}}")
w.line()
- w.summary(pad, ["Names of the qyl-owned DiagnosticSource bridge events."])
+ w.summary(pad, ["Names of qyl-owned Activity and DiagnosticSource events."])
w.line(f"{pad}public static class Events")
w.line(f"{pad}{{")
first = True
@@ -708,7 +708,7 @@ def emit_names_file(qyl_registry) -> str:
if not first:
w.line()
first = False
- w.summary(ipad, [f"The {escape_attribute(value)} bridge event."])
+ w.summary(ipad, [f"The {escape_attribute(value)} telemetry event."])
w.line(f"{ipad}public const string {ident} = {csharp_string(value)};")
w.line(f"{pad}}}")
diff --git a/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/AgentDiagnosticVocabularyTests.cs b/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/AgentDiagnosticVocabularyTests.cs
new file mode 100644
index 0000000..9e9f33b
--- /dev/null
+++ b/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/AgentDiagnosticVocabularyTests.cs
@@ -0,0 +1,70 @@
+using AwesomeAssertions;
+using Qyl.Telemetry.SemanticConventions.Incubating.Attributes.Qyl;
+using Qyl.Telemetry.SemanticConventions.Incubating.Names;
+using Xunit;
+
+namespace Qyl.Telemetry.SemanticConventions.Pipeline.Tests;
+
+public sealed class AgentDiagnosticVocabularyTests
+{
+ [Fact]
+ public void Agent_diagnostic_snapshot_uses_one_fixed_event_and_fixed_summary_keys()
+ {
+ QylTelemetryNames.Events.QylAgentDiagnosticSnapshot.Should()
+ .Be("qyl.agent.diagnostic.snapshot");
+
+ (string Actual, string Expected)[] summaryKeys =
+ [
+ (QylAttributes.AgentDiagnosticExtensionId, "qyl.agent.diagnostic.extension.id"),
+ (QylAttributes.AgentDiagnosticFormatVersion, "qyl.agent.diagnostic.format.version"),
+ (QylAttributes.AgentDiagnosticSnapshotId, "qyl.agent.diagnostic.snapshot.id"),
+ (QylAttributes.AgentDiagnosticProbeId, "qyl.agent.diagnostic.probe.id"),
+ (QylAttributes.AgentDiagnosticPhase, "qyl.agent.diagnostic.phase"),
+ (QylAttributes.AgentDiagnosticOutcome, "qyl.agent.diagnostic.outcome"),
+ (QylAttributes.AgentDiagnosticVariableCount, "qyl.agent.diagnostic.variable.count"),
+ (QylAttributes.AgentDiagnosticCheckCount, "qyl.agent.diagnostic.check.count"),
+ (QylAttributes.AgentDiagnosticCheckFailedCount, "qyl.agent.diagnostic.check.failed_count"),
+ ];
+
+ summaryKeys.Select(static item => item.Actual).Should().OnlyHaveUniqueItems();
+ summaryKeys.Should().AllSatisfy(static item => item.Actual.Should().Be(item.Expected));
+ }
+
+ [Fact]
+ public void Agent_diagnostic_phase_and_outcome_values_are_machine_tokens()
+ {
+ string[] phases =
+ [
+ QylAttributes.AgentDiagnosticPhaseValues.Input,
+ QylAttributes.AgentDiagnosticPhaseValues.Output,
+ QylAttributes.AgentDiagnosticPhaseValues.Error,
+ QylAttributes.AgentDiagnosticPhaseValues.Checkpoint,
+ ];
+ string[] outcomes =
+ [
+ QylAttributes.AgentDiagnosticOutcomeValues.Pass,
+ QylAttributes.AgentDiagnosticOutcomeValues.Fail,
+ QylAttributes.AgentDiagnosticOutcomeValues.Unknown,
+ QylAttributes.AgentDiagnosticOutcomeValues.NotEvaluated,
+ ];
+
+ phases.Should().Equal("input", "output", "error", "checkpoint");
+ outcomes.Should().Equal("pass", "fail", "unknown", "not_evaluated");
+ }
+
+ [Fact]
+ public void Workflow_correlation_uses_fixed_qyl_keys()
+ {
+ (string Actual, string Expected)[] correlationKeys =
+ [
+ (QylAttributes.WorkflowRunId, "qyl.workflow.run.id"),
+ (QylAttributes.WorkflowEventId, "qyl.workflow.event.id"),
+ (QylAttributes.WorkflowAttemptId, "qyl.workflow.attempt.id"),
+ (QylAttributes.WorkflowAgentId, "qyl.workflow.agent.id"),
+ (QylAttributes.WorkflowToolCallId, "qyl.workflow.tool_call.id"),
+ ];
+
+ correlationKeys.Select(static item => item.Actual).Should().OnlyHaveUniqueItems();
+ correlationKeys.Should().AllSatisfy(static item => item.Actual.Should().Be(item.Expected));
+ }
+}
diff --git a/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/Qyl0200TelemetryNameAnalyzerTests.cs b/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/Qyl0200TelemetryNameAnalyzerTests.cs
index 4ff8186..74d46ba 100644
--- a/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/Qyl0200TelemetryNameAnalyzerTests.cs
+++ b/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/Qyl0200TelemetryNameAnalyzerTests.cs
@@ -36,6 +36,15 @@ public async Task Qyl0200_accepts_registry_attribute_keys_including_qyl_owned_en
"""
activity?.SetTag("http.request.method", "GET");
activity?.SetTag("qyl.instrumentation.domain", "http");
+ activity?.SetTag("qyl.agent.diagnostic.extension.id", "qyl.agent.diagnostic.snapshot");
+ activity?.SetTag("qyl.agent.diagnostic.format.version", 1);
+ activity?.SetTag("qyl.agent.diagnostic.snapshot.id", "snapshot-1");
+ activity?.SetTag("qyl.agent.diagnostic.probe.id", "vcs.root.resolve");
+ activity?.SetTag("qyl.agent.diagnostic.phase", "checkpoint");
+ activity?.SetTag("qyl.agent.diagnostic.outcome", "pass");
+ activity?.SetTag("qyl.agent.diagnostic.variable.count", 3);
+ activity?.SetTag("qyl.agent.diagnostic.check.count", 2);
+ activity?.SetTag("qyl.agent.diagnostic.check.failed_count", 0);
"""));
diagnostics.Should().BeEmpty();
@@ -119,7 +128,11 @@ public async Task Qyl0200_checks_activity_event_names()
{
var clean = await AnalyzerHarness.RunAsync(
new Qyl0200TelemetryNameAnalyzer(),
- ActivitySink("activity?.AddEvent(new ActivityEvent(\"exception\"));"));
+ ActivitySink(
+ """
+ activity?.AddEvent(new ActivityEvent("exception"));
+ activity?.AddEvent(new ActivityEvent("qyl.agent.diagnostic.snapshot"));
+ """));
clean.Should().BeEmpty();
var flagged = await AnalyzerHarness.RunAsync(
diff --git a/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/RegistryDerivedAnalyzerTests.cs b/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/RegistryDerivedAnalyzerTests.cs
index 2b49ca1..355d6ed 100644
--- a/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/RegistryDerivedAnalyzerTests.cs
+++ b/tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/RegistryDerivedAnalyzerTests.cs
@@ -37,6 +37,48 @@ public async Task Qyl0013_accepts_registry_array_and_schema_governed_any_types()
diagnostics.Should().BeEmpty();
}
+ [Fact]
+ public async Task Qyl0013_enforces_agent_diagnostic_summary_types()
+ {
+ var diagnostics = await AnalyzerHarness.RunAsync(
+ new Qyl0013IncorrectAttributeTypeAnalyzer(),
+ SinkSource(
+ """
+ SetTag("qyl.agent.diagnostic.extension.id", "qyl.agent.diagnostic.snapshot");
+ SetTag("qyl.agent.diagnostic.format.version", "1");
+ SetTag("qyl.agent.diagnostic.snapshot.id", "snapshot-1");
+ SetTag("qyl.agent.diagnostic.probe.id", "probe-1");
+ SetTag("qyl.agent.diagnostic.phase", "checkpoint");
+ SetTag("qyl.agent.diagnostic.outcome", "pass");
+ SetTag("qyl.agent.diagnostic.variable.count", 3);
+ SetTag("qyl.agent.diagnostic.check.count", 2);
+ SetTag("qyl.agent.diagnostic.check.failed_count", 0);
+ """));
+
+ diagnostics.Should().ContainSingle();
+ diagnostics[0].Id.Should().Be("QYL0013");
+ diagnostics[0].GetMessage(CultureInfo.InvariantCulture).Should().Contain("integer");
+ }
+
+ [Fact]
+ public async Task Qyl0012_enforces_agent_diagnostic_phase_and_outcome_tokens()
+ {
+ var diagnostics = await AnalyzerHarness.RunAsync(
+ new Qyl0012InvalidAttributeValueAnalyzer(),
+ SinkSource(
+ """
+ SetTag("qyl.agent.diagnostic.phase", "CHECKPOINT");
+ SetTag("qyl.agent.diagnostic.outcome", "PASS");
+ """));
+
+ diagnostics.Should().HaveCount(2);
+ diagnostics.Select(static diagnostic => diagnostic.Id).Should().OnlyContain(static id => id == "QYL0012");
+ diagnostics.Select(static diagnostic => diagnostic.GetMessage(CultureInfo.InvariantCulture))
+ .Should().Contain(static message => message.Contains("checkpoint", StringComparison.Ordinal));
+ diagnostics.Select(static diagnostic => diagnostic.GetMessage(CultureInfo.InvariantCulture))
+ .Should().Contain(static message => message.Contains("pass", StringComparison.Ordinal));
+ }
+
[Fact]
public async Task Qyl0012_reports_only_noncanonical_spelling_of_known_enum_values()
{