From 7646be66f084babc64561d1d195a9aacc9c4bb4b Mon Sep 17 00:00:00 2001 From: Abhitej John Date: Tue, 29 Sep 2026 15:20:02 -0700 Subject: [PATCH 1/2] Add lightweight telemetry skill for .NET 11 tools Import the reviewed five-file patch by @qapdex-maker from https://github.com/dotnet/skills/pull/1036 onto current main without unrelated README links. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/CODEOWNERS | 2 + README.md | 2 +- plugins/dotnet11/README.md | 3 +- .../skills/lightweight-telemetry/SKILL.md | 210 +++++++++++++++++ .../dotnet11/lightweight-telemetry/eval.yaml | 222 ++++++++++++++++++ 5 files changed, 437 insertions(+), 2 deletions(-) create mode 100644 plugins/dotnet11/skills/lightweight-telemetry/SKILL.md create mode 100644 tests/dotnet11/lightweight-telemetry/eval.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3fdaecf7a0..6caa79b2e6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -143,3 +143,5 @@ /plugins/dotnet11/skills/system-text-json-net11/ @dotnet/skills-csharp-language-reviewers /tests/dotnet11/system-text-json-net11/ @dotnet/skills-csharp-language-reviewers +/plugins/dotnet11/skills/lightweight-telemetry/ @dotnet/skills-csharp-language-reviewers +/tests/dotnet11/lightweight-telemetry/ @dotnet/skills-csharp-language-reviewers diff --git a/README.md b/README.md index 6208d89b42..e1b9547c09 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Plugin support is component-specific: | [dotnet-test-migration](plugins/dotnet-test-migration/) | Skills and a GitHub Copilot orchestrator agent for migrating .NET test frameworks and platforms: MSTest and xUnit version upgrades, xUnit-to-MSTest conversion, and VSTest to Microsoft.Testing.Platform. | | [dotnet-aspnetcore](plugins/dotnet-aspnetcore/) | ASP.NET Core web development skills including middleware, endpoints, real-time communication, and API patterns. | | [dotnet-blazor](plugins/dotnet-blazor/) | Skills for Blazor development: component authoring, interactivity, and web application patterns. | -| [dotnet11](plugins/dotnet11/) | Skills for new .NET 11 APIs and language features. | +| [dotnet11](plugins/dotnet11/) | Skills for .NET 11 targets, including new APIs and established platform features. | ## Agentic Workflows diff --git a/plugins/dotnet11/README.md b/plugins/dotnet11/README.md index 7a85a71559..9c7aa60196 100644 --- a/plugins/dotnet11/README.md +++ b/plugins/dotnet11/README.md @@ -1,7 +1,8 @@ # dotnet11 -Skills focused on new APIs and language features introduced in .NET 11. +Skills for .NET 11 targets, including new APIs and use of established APIs. ## Skills - system-text-json-net11 +- lightweight-telemetry (uses stable metrics APIs in a `net11.0` console tool) diff --git a/plugins/dotnet11/skills/lightweight-telemetry/SKILL.md b/plugins/dotnet11/skills/lightweight-telemetry/SKILL.md new file mode 100644 index 0000000000..e0112bb7c3 --- /dev/null +++ b/plugins/dotnet11/skills/lightweight-telemetry/SKILL.md @@ -0,0 +1,210 @@ +--- +name: lightweight-telemetry +description: Emit structured metrics from a .NET 11 console app, CLI, or build tool using the built-in System.Diagnostics.Metrics API with no OpenTelemetry, APM, or collector dependency. Use when asked to "report how long it took", "count how many times it ran", expose queue depth or a running total, pick between a counter, gauge, and histogram, split one metric by a tag/dimension, keep the measurement path cheap when nothing is listening, or print readings as JSON lines from a short-lived process. Do not use for apps targeting before net11.0, distributed tracing across services (use configuring-opentelemetry-dotnet), cloud ingestion into Application Insights or Azure Monitor, or shipping log lines to Seq/Elasticsearch. +license: MIT +--- + +# Lightweight telemetry in .NET 11 + +A minimal, dependency-free way to expose operational metrics from a CLI or tool. +No OpenTelemetry SDK or external collector is required. These APIs predate .NET 11; +this skill applies them to tools targeting `net11.0`. A listener can write JSON +lines to stdout for a local consumer. + +## When to use + +- A build tool, CLI, or local agent needs to report timing/counts. +- You want structured telemetry without an APM vendor SDK. +- The host may be resource-constrained (no background collector). + +## When not to use + +- The project targets an earlier .NET version; these metrics APIs also work + there, but this skill is for the .NET 11 plugin. +- You need distributed tracing across services → use the + `configuring-opentelemetry-dotnet` skill instead. +- You need cloud ingestion (Application Insights) → use the vendor SDK. + +## Pick the instrument first + +The instrument type is the decision that is most often wrong, and it is not +recoverable downstream — a consumer cannot turn a gauge back into a rate. + +| The value is | Use | Never use | Because | +|---|---|---|---| +| A total that only grows (bytes processed, runs) | `CreateCounter` | a gauge | the consumer derives the rate from the increasing total; a gauge that resets destroys it | +| The value right now (queue depth, open handles) | `CreateGauge` | a counter | a cumulative sum misrepresents a level that goes down again | +| A per-operation duration or size you want percentiles for | `CreateHistogram` | a counter | summing durations loses the distribution | +| A level you can only sample when asked | `CreateObservableGauge` | recording in a hot loop | the callback runs at collection time | + +Set the unit and description when defining the instrument. Do not rely only on +a `.ms` name suffix to tell consumers which unit the value uses: + +```csharp +meter.CreateHistogram("tool.step.duration", "ms", "Duration per build step"); +``` + +## Split a metric by a dimension, not by name + +One instrument plus a tag, never one instrument per value: + +```csharp +stepDuration.Record(elapsedMs, new TagList { { "step", "restore" } }); +``` + +Tag **values** must come from a bounded set (step names, status codes). Never tag +with a user id, path, or timestamp — each distinct value is a separate time +series downstream. + +## Keep the hot path cheap + +`Record`/`Add` are cheap, but building the tags and formatting values is not. +Guard the expensive part when nothing is collecting: + +```csharp +if (stepDuration.Enabled) // false when no listener is attached + stepDuration.Record(elapsedMs, new TagList { { "step", step } }); +``` + +Use `TagList` (a struct) rather than allocating a `KeyValuePair[]` per iteration. + +## Lifetime: set up the listener before the first measurement + +A `MeterListener` only sees measurements recorded **after** `Start()`. In a +short-lived CLI this is the difference between output and silence: + +```csharp +using var meter = new Meter("MyTool"); +using var listener = new MeterListener(); +listener.InstrumentPublished = (instrument, l) => +{ + if (ReferenceEquals(instrument.Meter, meter)) + l.EnableMeasurementEvents(instrument); +}; +listener.Start(); // before any measurements +// Record counters, histograms, and synchronous gauges after Start(). +listener.RecordObservableInstruments(); // only if observable instruments are used +``` + +Also register `SetMeasurementEventCallback` before calling `Start()` to consume +recorded values. Synchronous counter, histogram, and gauge callbacks run when +the measurement is recorded; they cannot be flushed later. Observable instruments +emit only when a listener calls `RecordObservableInstruments()`. Dispose the +listener before its meter; do not make a listener dispose a caller-owned meter. + +## The pattern + +For a `net11.0` console project, this complete `Program.cs` prints one JSON line +per measurement. The framework `MeterListener` needs no external package: + +```csharp +using System.Diagnostics; +using System.Diagnostics.Metrics; +using System.Text.Json; + +using var meter = new Meter("MyTool", "1.0.0"); +var runs = meter.CreateCounter("tool.runs", "{run}", "Number of executions"); +var duration = meter.CreateHistogram("tool.step.duration", "ms", "Duration per step"); +int queueDepth = 3; +meter.CreateObservableGauge("tool.queue.depth", () => queueDepth, "{item}", "Items queued"); + +TimeProvider clock = TimeProvider.System; +using var listener = new MeterListener(); +listener.InstrumentPublished = (instrument, l) => +{ + if (ReferenceEquals(instrument.Meter, meter)) + l.EnableMeasurementEvents(instrument, clock); +}; +listener.SetMeasurementEventCallback(WriteReading); +listener.SetMeasurementEventCallback(WriteReading); +listener.Start(); // start before the first measurement + +await RecordStepAsync(clock, duration); +runs.Add(1); +listener.RecordObservableInstruments(); // poll the observable gauge once before exit + +static async Task RecordStepAsync(TimeProvider clock, Histogram duration) +{ + long start = clock.GetTimestamp(); + await Task.Delay(TimeSpan.FromMilliseconds(10), clock); + if (duration.Enabled) + duration.Record(clock.GetElapsedTime(start).TotalMilliseconds, + new TagList { { "step", "compile" } }); +} + +static void WriteReading( + Instrument instrument, T value, ReadOnlySpan> tags, + object? state) where T : struct +{ + var dimensions = new Dictionary(tags.Length); + foreach (var tag in tags) + dimensions[tag.Key] = tag.Value; + + var reading = new + { + meter = instrument.Meter.Name, + instrument = instrument.Name, + unit = instrument.Unit, + description = instrument.Description, + value, + tags = dimensions, + timestamp = ((TimeProvider)state!).GetUtcNow().ToString("O") + }; + Console.WriteLine(JsonSerializer.Serialize(reading)); +} +``` + +`RecordStepAsync` takes a `TimeProvider`, so a test can pass a controlled clock +(for example `FakeTimeProvider`) without changing this method. A bare local +assignment to `TimeProvider.System` is not itself an injection seam. + +## Output contract + +Each reading is exactly one JSON object on its own line — no banner, no summary +line, nothing else on stdout. A consumer can `tail -f` and parse every line. + +```json +{"meter":"MyTool","instrument":"tool.step.duration","unit":"ms","description":"Duration per step","value":58.6,"tags":{"step":"compile"},"timestamp":"2026-08-29T18:32:07+00:00"} +``` + +Contract, in order: + +1. `meter` — the fixed `Meter` name, never per-run or per-environment +2. `instrument` — the stable instrument name +3. `unit` — from the instrument metadata, never only a name suffix +4. `description` — so a scraped reading is self-describing +5. `value` — the measurement +6. `tags` — object of the bounded dimensions +7. `timestamp` — ISO 8601, UTC + +Note that a non-standard unit is written in UCUM annotation form: a counter +measures `{run}` or `{item}`, not `runs` or `items`. That is the correct +convention, and it is what a consumer expects to see — do not "fix" it to a +plain noun. + +The listener must be constructed and `Start()`ed **before** the first `Record`/ +`Add`. When the program uses an observable instrument, call +`RecordObservableInstruments()` before exit. That call polls observables; it +does not flush counters, histograms, or synchronous gauges. Measurements taken +before `Start()` produce no line. + +## Verify it works + +1. Put the complete program above in a console project that targets `net11.0`. +2. Run `dotnet run -c Release` from that project. +3. Confirm the output has three JSON lines (duration, run count, queue depth), + with numeric `value` fields, and no extra lines from the application. + +If the application prints nothing, check that the listener is started, has +registered callbacks, and has enabled the instruments before recording. + +## Notes + +- `Meter`/`Counter`/`Histogram` are built into `System.Diagnostics.DiagnosticSource` + (no extra NuGet package for the API itself). +- For production scraping or OTLP export, use an exporter. This skill only + handles local consumption without an SDK or collector. +- Keep the meter name stable — it becomes the metric namespace downstream. The + meter *version* string is safe to bump; the name is not. +- One instrument + a tag beats one instrument per value, but keep tag values + bounded — unbounded values (ids, paths) create a time series each. diff --git a/tests/dotnet11/lightweight-telemetry/eval.yaml b/tests/dotnet11/lightweight-telemetry/eval.yaml new file mode 100644 index 0000000000..b09b86850c --- /dev/null +++ b/tests/dotnet11/lightweight-telemetry/eval.yaml @@ -0,0 +1,222 @@ +name: lightweight-telemetry +description: Evaluates the dotnet11/lightweight-telemetry skill +type: capability +defaults: + timeout: 6m + runs: 1 +stimuli: + - name: Structured metrics from a console tool without an APM SDK + prompt: | + I'm building a .NET 11 console tool and I want it to report how many times + it ran and how long each run took, as structured telemetry. I do NOT want to + pull in OpenTelemetry or any external APM SDK — the host has no collector and + is resource constrained. Show me a minimal `net11.0` program that reports a + count and a duration distribution and prints one structured line per + measurement. + graders: + - type: exit-success + - type: output-contains + config: + substring: System.Diagnostics.Metrics + - type: output-matches + config: + pattern: (Json|Serialize|serializ) + - type: output-not-matches + config: + pattern: PackageReference.*OpenTelemetry + - type: prompt + rubric: + - Solves the problem with the metrics API that ships in the framework rather than an added OpenTelemetry or vendor APM package + - Provides both a cumulative count and a duration distribution, not just one of the two + - Emits machine-readable (structured/JSON) output per measurement rather than only a plaintext log line + - Enables an in-process consumer before recording, so the reported measurements produce output + + - name: Elapsed time measured through the framework clock abstraction + prompt: | + In a .NET 11 tool I need to measure how long an operation takes and record it + as a metric, and I want the timing to be testable — I don't want DateTime.Now + scattered through the code. Show a small `net11.0` snippet that takes the + start timestamp, computes the elapsed milliseconds, and records it. + graders: + - type: exit-success + - type: output-not-matches + config: + pattern: DateTime\.Now + - type: prompt + rubric: + - Obtains the elapsed time from an injectable clock abstraction (or an equivalent testable seam) instead of DateTime.Now + - Records the resulting duration as a metric measurement + - The timing code can be driven by a fake/controlled clock in a test without changing production code + + - name: Metric identity stays stable across releases + prompt: | + I'm adding metrics to several .NET 11 tools and I plan to scrape the emitted + values with an external system later. Give me a `net11.0` example of setting + up the metric source, and tell me what I must be careful about so my + dashboards and queries don't break when I ship the next version. + graders: + - type: exit-success + - type: output-not-matches + config: + pattern: Guid\.NewGuid + - type: prompt + rubric: + - Uses an explicit, fixed metric source name rather than a generated or environment-derived one + - Explains that the source and instrument names form the identity downstream consumers query, so renaming them breaks existing dashboards + - Distinguishes the version string (safe to change) from the name (not safe to change) + + - name: Consume own measurements in-process with no extra packages + prompt: | + I have a .NET 11 tool that already records metrics with the built-in metrics + API, but nothing consumes them so I see no output. Without adding any NuGet + package, how do I subscribe to my own measurements in the same process and + write each reading to the console as a JSON line? Show a `net11.0` example. + graders: + - type: exit-success + - type: output-matches + config: + pattern: (Json|Serialize|serializ) + - type: output-not-matches + config: + pattern: PackageReference.*OpenTelemetry + - type: prompt + rubric: + - Subscribes to measurements in-process using a framework-provided listener, adding no NuGet package + - Explicitly opts the instruments in so callbacks actually fire (subscription alone produces nothing) + - Serializes each reading to JSON and writes it to the console + + - name: Instrument choice for an instantaneous value + prompt: | + I have a .NET 11 tool and I want to expose the number of items currently + queued as telemetry. A running total is wrong here — I need whatever the + value is right now. Show me a `net11.0` snippet that picks the right + instrument for that and publishes the value. + graders: + - type: exit-success + - type: prompt + rubric: + - Chooses an instrument that reports the current value rather than a monotonically increasing sum + - Explains why a cumulative counter would misrepresent a queue depth + - Shows the value being published/observed, not merely the instrument being created + + - name: Instrument choice for a monotonic total + prompt: | + My .NET 11 tool processes files and I want to report the total number of + bytes it has processed since start, so an external system can compute a rate + from it. Show me a `net11.0` snippet with the right instrument for that and + explain why it is the right one. + graders: + - type: exit-success + - type: prompt + rubric: + - Chooses a monotonically increasing cumulative instrument for the byte total + - Explains that the consumer derives the rate from the increasing total, so the app must not reset or gauge it + - Does not model the running total as a distribution/percentile instrument + + - name: Split one metric by a dimension + prompt: | + In a .NET 11 tool I'm recording how long each build step takes. Right now all + the durations land in one bucket and I cannot tell "restore" from "compile". + I don't want a separate metric per step name. Show me a `net11.0` example + that fixes this. + graders: + - type: exit-success + - type: prompt + rubric: + - Attaches a key/value dimension to each measurement instead of creating one instrument per step + - Keeps a single instrument and passes the step name as the dimension value + - Warns (or by construction avoids) unbounded dimension values that would explode cardinality + + - name: Metadata that makes readings interpretable downstream + prompt: | + I'm defining metrics in a .NET 11 tool and a colleague scraping them cannot + tell whether a duration value is seconds or milliseconds, or what a metric + means. Show me a `net11.0` snippet that fixes that at the point where the + metrics are defined. + graders: + - type: exit-success + - type: prompt + rubric: + - Declares the unit and a human-readable description on the instruments themselves rather than documenting them elsewhere + - Puts the unit in the metadata instead of relying only on a suffix in the metric name + - Shows the metadata reaching the consumer/reading output + + - name: Keep the measurement path cheap when nothing is listening + prompt: | + I want metrics in a hot loop in my .NET 11 tool, but most of the time nobody + is collecting them and I don't want to pay for building tag arrays and + formatting values on every iteration. How do I keep that path cheap? Show a + `net11.0` snippet. + graders: + - type: exit-success + - type: prompt + rubric: + - Checks whether the instrument is actually being collected before doing the expensive work of assembling the measurement + - Keeps the always-executed path allocation-light (no per-iteration allocation of tag collections or strings) + - Still records correctly when a consumer is attached + + - name: Observable snapshot is collected before the tool exits + prompt: | + My short-lived .NET 11 CLI exposes its current queue depth as an observable + metric. Its in-process listener writes readings to JSON lines, but the CLI + exits before the listener collects the queue depth. How do I collect a final + reading before termination without changing the metric to a running total? + graders: + - type: exit-success + - type: prompt + rubric: + - Ensures the consumer/listener is set up before the first measurement is recorded, not after + - Polls enabled observable instruments explicitly before the process exits + - Disposes the listener and meter after collecting the observable reading + - Explains that synchronous measurements run callbacks when recorded and cannot be flushed by polling observables + + - name: Cross-service tracing request stays out of scope + prompt: | + My .NET 11 service needs distributed tracing across several microservices, + with spans, context propagation, and a Jaeger backend so I can follow one + request end to end. How should I set that up? + expect_activation: false + graders: + - type: output-matches + config: + pattern: (OpenTelemetry|Activity|ActivitySource) + - type: prompt + rubric: + - Answers with a distributed tracing solution (spans, context propagation, an OTLP/Jaeger exporter) + - Does not answer a tracing question with a dependency-free in-process metrics recipe + - Does not claim that console-printed metrics give end-to-end request correlation + - Treats a dependency-free in-process metrics recipe as out of scope for this request + + - name: Cloud ingestion request stays out of scope + prompt: | + I want my .NET 11 app's telemetry to land in Application Insights so my team + gets hosted dashboards, retention, and alerting without running anything + ourselves. What should I use? + expect_activation: false + graders: + - type: output-matches + config: + pattern: (Application Insights|ApplicationInsights|Azure Monitor|OpenTelemetry) + - type: prompt + rubric: + - Recommends the hosted ingestion path (the vendor/Azure Monitor SDK or an OTLP exporter pointed at it) + - Does not propose printing measurements to the console as a substitute for hosted dashboards and alerting + - Addresses that the data must leave the process to reach the cloud service + - Treats a console-only, dependency-free metrics recipe as out of scope for this request + + - name: Log shipping request stays out of scope + prompt: | + I just want my .NET 11 app to ship its existing info/warn/error log lines to + a central place like Seq or Elasticsearch so I can search them. How do I do + that? + expect_activation: false + graders: + - type: output-matches + config: + pattern: (ILogger|Serilog|Seq|Elasticsearch|logging) + - type: prompt + rubric: + - Answers with a logging pipeline (a logger plus a sink for the target system) + - Does not convert the request into numeric instruments, which would discard the log message text + - Keeps the searchable log lines intact rather than replacing them with aggregated values + - Treats a numeric metrics recipe as out of scope for a log-shipping request From 0d2f9c91b9974cb5fe64e92960068d3f2b320328 Mon Sep 17 00:00:00 2001 From: Fixture Tests Date: Tue, 6 Oct 2026 16:16:41 -0700 Subject: [PATCH 2/2] Add telemetry evaluation result tags Add the current capability, risk, and journey metadata required by the evaluation quality gate without changing prompts, graders, or rubrics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d3836dec-316f-497c-bfca-3875014f94b2 --- .../dotnet11/lightweight-telemetry/eval.yaml | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/dotnet11/lightweight-telemetry/eval.yaml b/tests/dotnet11/lightweight-telemetry/eval.yaml index 11591bcad3..b1ca5d174b 100644 --- a/tests/dotnet11/lightweight-telemetry/eval.yaml +++ b/tests/dotnet11/lightweight-telemetry/eval.yaml @@ -13,6 +13,10 @@ stimuli: is resource constrained. Show me a minimal `net11.0` program that reports a count and a duration distribution and prints one structured line per measurement. + tags: + capability: structured-metrics-emission + risk: external-sdk-dependency + journey: add-console-tool-metrics graders: - type: exit-success - type: output-contains @@ -37,6 +41,10 @@ stimuli: as a metric, and I want the timing to be testable — I don't want DateTime.Now scattered through the code. Show a small `net11.0` snippet that takes the start timestamp, computes the elapsed milliseconds, and records it. + tags: + capability: testable-elapsed-time + risk: untestable-wall-clock + journey: record-operation-duration graders: - type: exit-success - type: output-matches @@ -58,6 +66,10 @@ stimuli: values with an external system later. Give me a `net11.0` example of setting up the metric source, and tell me what I must be careful about so my dashboards and queries don't break when I ship the next version. + tags: + capability: stable-metric-identity + risk: dashboard-query-breakage + journey: define-stable-metrics graders: - type: exit-success - type: output-matches @@ -79,6 +91,10 @@ stimuli: API, but nothing consumes them so I see no output. Without adding any NuGet package, how do I subscribe to my own measurements in the same process and write each reading to the console as a JSON line? Show a `net11.0` example. + tags: + capability: in-process-metric-consumption + risk: silent-unobserved-measurements + journey: consume-local-metrics graders: - type: exit-success - type: output-matches @@ -103,6 +119,10 @@ stimuli: queued as telemetry. A running total is wrong here — I need whatever the value is right now. Show me a `net11.0` snippet that picks the right instrument for that and publishes the value. + tags: + capability: current-value-instrument-selection + risk: cumulative-value-misrepresentation + journey: report-current-queue-depth graders: - type: exit-success - type: output-matches @@ -121,6 +141,10 @@ stimuli: bytes it has processed since start, so an external system can compute a rate from it. Show me a `net11.0` snippet with the right instrument for that and explain why it is the right one. + tags: + capability: monotonic-total-instrument-selection + risk: incorrect-rate-semantics + journey: report-processed-byte-total graders: - type: exit-success - type: output-matches @@ -139,6 +163,10 @@ stimuli: the durations land in one bucket and I cannot tell "restore" from "compile". I don't want a separate metric per step name. Show me a `net11.0` example that fixes this. + tags: + capability: bounded-metric-dimensions + risk: metric-cardinality-explosion + journey: separate-build-step-durations graders: - type: exit-success - type: output-matches @@ -157,6 +185,10 @@ stimuli: tell whether a duration value is seconds or milliseconds, or what a metric means. Show me a `net11.0` snippet that fixes that at the point where the metrics are defined. + tags: + capability: metric-unit-description-metadata + risk: ambiguous-measurement-semantics + journey: define-interpretable-metrics graders: - type: exit-success - type: output-matches @@ -175,6 +207,10 @@ stimuli: is collecting them and I don't want to pay for building tag arrays and formatting values on every iteration. How do I keep that path cheap? Show a `net11.0` snippet. + tags: + capability: disabled-instrument-fast-path + risk: hot-path-allocation-overhead + journey: optimize-unobserved-measurements graders: - type: exit-success - type: output-matches @@ -193,6 +229,10 @@ stimuli: sometimes see no output at all for the last operations. Show me how to structure a `net11.0` program so the recorded values are all accounted for before it terminates. + tags: + capability: short-lived-listener-lifetime + risk: dropped-final-measurements + journey: flush-short-lived-tool-metrics graders: - type: exit-success - type: output-matches @@ -210,6 +250,10 @@ stimuli: My .NET 11 service needs distributed tracing across several microservices, with spans, context propagation, and a Jaeger backend so I can follow one request end to end. How should I set that up? + tags: + capability: tracing-boundary + risk: incorrect-skill-activation + journey: route-distributed-tracing expect_activation: false graders: - type: output-matches @@ -227,6 +271,10 @@ stimuli: I want my .NET 11 app's telemetry to land in Application Insights so my team gets hosted dashboards, retention, and alerting without running anything ourselves. What should I use? + tags: + capability: cloud-ingestion-boundary + risk: incorrect-skill-activation + journey: route-hosted-telemetry expect_activation: false graders: - type: output-matches @@ -244,6 +292,10 @@ stimuli: I just want my .NET 11 app to ship its existing info/warn/error log lines to a central place like Seq or Elasticsearch so I can search them. How do I do that? + tags: + capability: log-shipping-boundary + risk: incorrect-skill-activation + journey: route-centralized-logging expect_activation: false graders: - type: output-matches