# NarrativeTrace .NET > Code is the log. Auto-generate human-readable execution narratives from method names, parameter names, and return values — no manual logging. ## Install and first trace (copy this) ```bash dotnet new console -n Hello && cd Hello dotnet add package NarrativeTrace.Proxy ``` ```csharp using NarrativeTrace.Core; using NarrativeTrace.Proxy; using NarrativeTrace.Runtime; // snippet:begin fixedTraceparent // A fixed W3C traceparent, seeded through NarrativeTraceConfig so this page's embedded output // always names the same trace. A real run adopts nothing here (or a real inbound request header, // via NarrativeTraceMiddleware) and gets a fresh, randomly generated trace id every time. const string DemoTraceparent = "00-a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4-a1b2c3d4a1b2c3d4-01"; // snippet:end fixedTraceparent var context = new SyncNarrativeContext( new NarrativeTraceConfig(initialTraceparent: Traceparent.Parse(DemoTraceparent))); var orders = NarrativeTraceProxy.Create(new OrderService(), context); orders.PlaceOrder("cust-1", "book-123", 2); Console.WriteLine(IndentedTextRenderer.Render(context.CaptureTrace())); public interface IOrderService { string PlaceOrder(string customerId, string productId, int quantity); } public sealed class OrderService : IOrderService { public string PlaceOrder(string customerId, string productId, int quantity) => $"confirmed:{customerId}:{productId}:{quantity}"; } ``` ```bash dotnet run ``` ```text trace: loose hook parks (a1b2c3d) └── IOrderService.PlaceOrder(customerId: "cust-1", productId: "book-123", quantity: 2) → "confirmed:cust-1:book-123:2" — 8ms ``` Then to your logger, same tree, two lines: ```csharp using var loggerFactory = LoggerFactory.Create(b => b.AddConsole()); TraceLogExporter.ExportToLogger(context.CaptureTrace(), loggerFactory.CreateLogger("NarrativeTrace")); ``` ## Before you start - `NarrativeTrace.Proxy` pulls in `.Runtime`+`.Core`. - `NARRATIVETRACE_OUTPUT` defaults **on**. - `new ProxyOptions(Redaction: ...)` reaches the proxy, and `NarrativeTracingDiOptions.Redaction`/`NarrativeTraceOptions.Redaction` reach DI auto-wrap and ASP.NET Core auto-wrapped proxies too. - No compiler flag needed for parameter names — unlike the JVM's `-parameters`. - The `.nt` structural artifact is a last-green baseline with per-invocation identity, `manifest.json` and approval mode: `NARRATIVETRACE_APPROVAL=true` fails a passing test whose structure differs from its committed `*.approved.nt`, and `./build.sh Approve` promotes a reviewed `*.received.nt`. See [Structural Trace Format](https://github.com/narrativetrace/narrativetrace-dotnet/blob/v0.1.5/documentation/structural-trace-format.md). - A trace has a name (`trace: bold elk soars (a1b2c3d)`, above), and a test-suite run has one too — the console footer, `manifest.json`'s `run` object, the Markdown frontmatter's `run:` field, and `nt.runName` in the logging bridge's scope. Neither ever reaches the `.nt` text. See [Configuration Guide §7](https://github.com/narrativetrace/narrativetrace-dotnet/blob/v0.1.5/documentation/guides/configuration.md#7-logging-bridge-microsoftextensionslogging). - Every `NARRATIVETRACE_*` variable — what it sets, its default, and a runnable example of the effect — is tabulated in [Configuration Guide §10](https://github.com/narrativetrace/narrativetrace-dotnet/blob/v0.1.5/documentation/guides/configuration.md#10-environment-variable-worked-examples). ## Field notes from AI agents (self-reported, not a benchmark) We asked AI agents to build a claims service with NarrativeTrace and write up the experience for another engineer, naming what was awkward. Prompted, one run each, unedited, model named; we wrote the library. The reports so far are from the Java runtime; the same exercise runs for .NET next, and the principle they describe — the trace as evidence, redaction by parameter name, output on by default — is the same here. All six reports, the exact prompt and the task: https://narrativetrace.ai/#agents-saying - "…the release call is present, generated from a real run, not asserted from inside the process being tested." — Claude Sonnet, 2026-09-11 - "The always-on name-based deny-list is a genuinely nice piece of design: naming a parameter `paymentToken` gets you redaction for free, and I could prove it in a test." — Claude Sonnet, 2026-09-11 - "The trace of a failed payment made missing compensation immediately visible, and approval testing caught an extra call that ordinary value assertions accepted." — GPT-6, 2026-09-08 ## Docs - [User Guides](https://github.com/narrativetrace/narrativetrace-dotnet/blob/v0.1.5/documentation/guides/README.md): installation, configuration, annotations, ASP.NET Core, clarity - [Complete Reference](https://narrativetrace.ai/dotnet/llms-full.txt): full API surface, data model, integration paths, CLI/MSBuild - [Repository README](https://github.com/narrativetrace/narrativetrace-dotnet/blob/v0.1.5/README.md): value proposition and quick start - [Agent Skills](https://github.com/narrativetrace/narrativetrace-dotnet/blob/v0.1.5/documentation/agent-skills.md): the `narrativetrace-doctor` and `add-narrative-tracing` Claude Code skills ## Target frameworks Libraries multi-target `net10.0` and `netstandard2.0` (the `Legacy` package adds `net48`). No compiler flag is needed — .NET retains parameter names in metadata by default. ## Packages (NuGet id = project name) - `NarrativeTrace.Core` — trace model, `INarrativeContext`, config, redaction, text/Markdown/prose renderers; namespace `NarrativeTrace.Core.Annotation` holds `[Narrated]`/`[OnError]`/`[NotTraced]`/`[NarrativeSummary]` - `NarrativeTrace.Runtime` — capture engine (`SyncNarrativeContext`, `AsyncNarrativeContext`), JSON/chapter exporters - `NarrativeTrace.Proxy` — `NarrativeTraceProxy.Create`, `[Traced]` - `NarrativeTrace.DependencyInjection` — `AddNarrativeTracing` namespace auto-wrap - `NarrativeTrace.AspNetCore` — per-request middleware, `ITraceExporter` - `NarrativeTrace.Logging` — `Microsoft.Extensions.Logging` bridge - `NarrativeTrace.Observability` — OpenTelemetry (`ActivitySource` "NarrativeTrace", batch + live) - `NarrativeTrace.Diagrams` — Mermaid / PlantUML sequence renderers - `NarrativeTrace.Clarity` — naming-clarity analysis and reporting - `NarrativeTrace.Cli` — `dotnet-narrativetrace` global tool (`clarity-scan`, `clarity-check`) - `NarrativeTrace.MSBuild` — build-only shim wiring the CLI into `dotnet build`/`test` - `NarrativeTrace.Testing.Xunit` / `.NUnit` — per-test context + failure-narrative printing ## Core API - `new SyncNarrativeContext(new NarrativeTraceConfig(TracingLevel.Detail))` - `NarrativeTraceProxy.Create(target, context)` → traced proxy - `context.CaptureTrace()` → `TraceTree`; `context.Reset()` - `IndentedTextRenderer.Render(tree)` / `MarkdownRenderer.Render(tree)` / `ProseRenderer.Render(tree)` - `MarkdownRenderer.RenderDocument(tree, metadata)` — frontmatter + `## Trace:` header + call flow (what per-test `.md` artifacts contain) - `TracingLevel`: `Off`, `Errors`, `Summary`, `Narrative`, `Detail` ## Configuration - Env: `NARRATIVETRACE_LEVEL`, `NARRATIVETRACE_OUTPUT`, `NARRATIVETRACE_OUTPUT_DIR`, `NARRATIVETRACE_FORMAT`, `NARRATIVETRACE_APPROVAL`, `NARRATIVETRACE_APPROVED_DIR` (`ConfigResolver`, invalid values degrade to defaults) - DI: `services.AddNarrativeTracing(o => o.Namespaces("MyApp.Services"))` - ASP.NET Core: `services.AddNarrativeTrace(configuration)` + `app.UseMiddleware()`; trace services via `HttpContext.GetNarrativeContext()` - MSBuild: `NarrativeTrace.MSBuild` package → `ClarityScan`/`ClarityCheck` targets ## Clarity - `ClarityScanner.Scan([typeof(T)])` (reflection-only) or `ClarityAnalyzer.Analyze(tree)` - Five weighted components: Method 30%, Parameter 25%, Class 20%, Structural 15%, Cohesion 10% - CI gate: `dotnet-narrativetrace clarity-scan --assembly X.dll` then `clarity-check --results clarity-results.json --min-score 0.8 --max-high-issues 0` ## Examples - `examples/NarrativeTrace.Examples.ECommerce` — nested order-flow narrative (success, declined payment, out of stock) + clear-vs-cryptic naming comparison