Inside Aspire
The Architecture Behind the AI Loop
Ground decisions. Observe outcomes. Ship intent.
Start presentation
Wisconsin .NET UG
November 17, 2026 at 6pm Central
Aspire makes AI
more efficient.
Reviewed facts guide the next move.
Observe with an AppHost or without.
Take intent to a configured target.
Evidence and application intent travel together.
Three areas. One payoff: precise, inspectable decisions, not invented savings.
AI loops
A confident answer isn't evidence.
We use AI to build Aspire and aspire.dev. Reviewed, published facts then ground the developer's next move.
Make browser behavior observable.
WithBrowserLogs
Console output, exceptions, network events, and screenshots become evidence.
Configure the tracked browser at runtime. Follow the reviewed guide into the CLI and the developer's next decision.
Experimental ASPIREBROWSERLOGS001 in Aspire.Hosting.Browsers. A documented extra capability, not wired into this weather app.

- microsoft/aspire#16531Configure tracked browser command
- pr-docs-check run 25092695241Successful existing workflow run
- microsoft/aspire.dev#775Bot draft, maintainer refinement, merge
- browser-logs.mdPublished guide, retrieved by the CLI
Different skills. Visible human review.

doc-writer is a distinct skill.gh aw status "pr-docs-check" \
--repo "microsoft/aspire" --jsondoc-writer guides documentation authoring. aspire-orchestration and aspire-monitoring guide precise app operations.
Actual refinement commit: moved the guide, corrected scope, configuration precedence, and browser sharing. Nothing is dispatched on stage.
Review and publication supply current facts.
Human review and deployment are barriers. Published context feeds the CLI, not model weights.
pr-docs-check.mdgh aw compilepr-docs-check.lock.yml
on:
pull_request:
types: [closed]
branches:
- main
- release/*
safe-outputs:
create-pull-request:
title-prefix: "[docs] "
draft: true
target-repo:
"microsoft/aspire.dev"
protected-files: blocked{
"number": 775,
"state": "closed",
"merged": true,
"merged_by": {
"login": "davidfowl"
}
}Facts feed the next loop.
Correct today can be stale tomorrow.
Reviewed narrative
Release-published Markdown supplies current narrative context.
Versioned API facts
Assemblies generate per-package-version API models and references.
API references are separate from the narrative corpus. These captures use current published context, not automatic CLI-semver matching. API generator / corpus boundaries.
Start with a healthy app.
Find current guidance.
aspire docs search "Configure tracked browser" --format Json[
{
"title": "Browser logs",
"slug": "browser-logs",
"content": "Learn how to capture and configure browser console logs in Aspire using the BrowserLogs resource.",
"section": "Add browser logs to a resource",
"score": 172.43004
},
{
"title": "Browser app configuration",
"slug": "browser-app-configuration",
"content": "Configure generic JavaScript browser apps to send OpenTelemetry data to the Aspire dashboard with OTLP HTTP exporters.",
"section": "Example browser telemetry code",
"score": 119.291626
},
{
"title": "Enable browser telemetry",
"slug": "enable-browser-telemetry",
"content": "Enable browser telemetry in the Aspire dashboard to capture client-side OpenTelemetry logs, traces, and metrics from front-end JavaScript apps.",
"section": "Dashboard configuration",
"score": 101.06361
},
{
"title": "Set up Grafana k6 in the Aspire AppHost",
"slug": "set-up-grafana-k6-in-the-aspire-apphost",
"content": "Configure the Community Toolkit k6 resource in Aspire, including scripts, browser testing, endpoints, health checks, and OpenTelemetry.",
"section": "Use a fixed port or browser-enabled image",
"score": 73.46021
},
{
"title": "Compiler Error ASPIREBROWSERLOGS001",
"slug": "compiler-error-aspirebrowserlogs001",
"content": "Learn what causes the Aspire compiler error ASPIREBROWSERLOGS001 and how to fix it so your AppHost builds cleanly.",
"section": "To correct this error",
"score": 71.18428
}
]
Captured 2026-10-12. Illustrative replay timing, not a performance claim. Complete public stdout / stderr / capture proof.
Read the actual guidance.
aspire docs get browser-logs --section "Configure browser logs" --format Json{
"title": "Browser logs",
"slug": "browser-logs",
"summary": "Learn how to capture and configure browser console logs in Aspire using the BrowserLogs resource.",
"content": "## Configure browser logs\n\nThe **Configure tracked browser** command opens an interactive settings dialog directly in the Aspire dashboard. Use it to adjust the browser session at runtime without restarting the AppHost.\n\nThe dialog presents the following options:\n* **Scope** — Apply changes to the current resource only, or globally to all browser log resources in the AppHost.\n* **Browser** — Choose a detected Chromium browser (Edge or Chrome) or specify a custom executable path.\n* **User data mode** — `Shared` (cross-AppHost persistent data directory) or `Isolated` (per-AppHost persistent data directory).\n* **Profile** — Select a discovered Chromium profile from the Aspire-managed browser hive (available in `Shared` mode only).\n* **Save to user secrets** — Persist the selected settings to the AppHost’s user secrets so they’re restored on the next run. This option is only shown when the AppHost project has a user secrets ID configured. Run [`aspire secret set`](https://aspire.dev/reference/cli/commands/aspire-secret-set/) once in the AppHost project to initialize a user secrets store; subsequent **Save to user secrets** clicks then write the browser configuration into it.\n\nNote\n\nThe **Configure tracked browser** command is only available when the Aspire dashboard’s interaction service is active (that is, when the dashboard is connected to the AppHost). It is disabled in standalone dashboard mode.\n\nAfter confirming the dialog, the new configuration takes effect immediately: the current browser session restarts with the updated settings.",
"sections": [
"Prerequisites",
"Install the package",
"Add browser logs to a resource",
"Optional parameters",
"Configure via app settings",
"Configure browser logs",
"Browser sessions, tabs, and process sharing",
"How browser logs are captured",
"See also"
]
}
Captured 2026-10-12. Illustrative replay timing, not a performance claim. Complete public stdout / stderr / capture proof.
Find an exact C# member.
aspire docs api search WithBrowserLogs --language csharp --format Json[
{
"id": "csharp/aspire.hosting.browsers/browserlogsbuilderextensions/methods#withbrowserlogs",
"name": "WithBrowserLogs(IResourceBuilder<T>, string?, string?, BrowserUserDataMode?)",
"language": "csharp",
"kind": "member",
"parentId": "csharp/aspire.hosting.browsers/browserlogsbuilderextensions/methods",
"memberGroup": "methods",
"summary": "Adds a child resource that can open the application's primary browser endpoint in a tracked browser session, surface browser diagnostics, and capture screenshots.",
"score": 59
}
]
Captured 2026-10-12. Illustrative replay timing, not a performance claim. Complete public stdout / stderr / capture proof.
Inspect the member, not a guess.
aspire docs api get "csharp/aspire.hosting.browsers/browserlogsbuilderextensions/methods#withbrowserlogs" --format Json{
"id": "csharp/aspire.hosting.browsers/browserlogsbuilderextensions/methods#withbrowserlogs",
"name": "WithBrowserLogs(IResourceBuilder<T>, string?, string?, BrowserUserDataMode?)",
"language": "csharp",
"kind": "member",
"url": "https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browserlogsbuilderextensions/methods#withbrowserlogs-iresourcebuilder-t-string-string-browseruserdatamode",
"parentId": "csharp/aspire.hosting.browsers/browserlogsbuilderextensions/methods",
"memberGroup": "methods",
"content": "# BrowserLogsBuilderExtensions Methods\n\n- Package: [Aspire.Hosting.Browsers](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers.md)\n- Type: [BrowserLogsBuilderExtensions](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browserlogsbuilderextensions.md)\n- Kind: `Methods`\n- Members: `1`\n\nExtension methods for adding tracked browser log resources to browser-based application resources.\n\n<a id=\"withbrowserlogs\"></a>\n<a id=\"withbrowserlogs-iresourcebuilder-t-string-string-browseruserdatamode\"></a>\n\n## WithBrowserLogs(IResourceBuilder<T>, string?, string?, BrowserUserDataMode?)\n\n> **Experimental:** ASPIREBROWSERLOGS001 - [Learn more](https://aspire.dev/diagnostics/aspirebrowserlogs001/)\n\n- Name: `WithBrowserLogs(IResourceBuilder<T>, string?, string?, BrowserUserDataMode?)`\n- Modifiers: `extension`\n- Returns: `IResourceBuilder<T>`\n- Source: [GitHub](https://github.com/microsoft/aspire/blob/f4c27f2d43ddc1cacd0dd083b30d1fea1cee7a62/src/Aspire.Hosting.Browsers/BrowserLogsBuilderExtensions.cs#L122-L289)\n\nAdds a child resource that can open the application's primary browser endpoint in a tracked browser session, surface browser diagnostics, and capture screenshots.\n\n```csharp\npublic static class BrowserLogsBuilderExtensions\n{\n public static IResourceBuilder<T> WithBrowserLogs<T>(\n this IResourceBuilder<T> builder,\n string? browser = null,\n string? profile = null,\n BrowserUserDataMode? userDataMode = null)\n {\n // ...\n }\n}\n```\n\n## Parameters\n\n- `builder` (`IResourceBuilder<T>`)\n The resource builder.\n- `browser` (`string?`) `optional`\n The browser to launch. When not specified, the tracked browser uses the configured value from `Aspire:Hosting:BrowserLogs` and otherwise prefers an installed `\"msedge\"` browser in shared user data mode, an installed `\"chrome\"` browser in isolated user data mode, and finally falls back to `\"chrome\"`. Supported values include logical browser names such as `\"msedge\"` and `\"chrome\"`, or an explicit browser executable path.\n- `profile` (`string?`) `optional`\n Optional Chromium profile name or directory name to use. Only valid when the effective user data mode is [BrowserUserDataMode.Shared](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browseruserdatamode/fields.md). When not specified, the tracked browser uses the configured value from `Aspire:Hosting:BrowserLogs` if present.\n- `userDataMode` ([BrowserUserDataMode?](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browseruserdatamode.md)) `optional`\n Optional [BrowserUserDataMode](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browseruserdatamode.md) that selects whether the tracked browser launches against a persistent Aspire-managed user data directory shared across all AppHosts on the machine ( [BrowserUserDataMode.Shared](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browseruserdatamode/fields.md), the default) or a per-AppHost persistent user data directory ( [BrowserUserDataMode.Isolated](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browseruserdatamode/fields.md)). Both modes use Aspire-managed paths under `%LocalAppData%\\Aspire\\BrowserData` on Windows (or platform equivalents); the user's normal browser profile is never used. When not specified, the tracked browser uses the configured value from `Aspire:Hosting:BrowserLogs` and otherwise defaults to [BrowserUserDataMode.Shared](https://aspire.dev/reference/api/csharp/aspire.hosting.browsers/browseruserdatamode/fields.md).\n\n## Returns\n\n`IResourceBuilder<T>` -- A reference to the original `ApplicationModel.IResourceBuilder`1` for further chaining.\n\n## Remarks\n\nThis method adds a child browser logs resource beneath the parent resource represented by `builder`. The child resource exposes a dashboard command that launches a Chromium-based browser in a tracked mode, attaches to the browser's debugging protocol, forwards browser console, error, exception, and network output to the child resource's console log stream, and can capture screenshots as command artifacts.\n\nThe tracked browser session uses the [Chrome DevTools Protocol (CDP)](https://chromedevtools.github.io/devtools-protocol/) to subscribe to browser runtime, log, page, and network events.\n\nThe parent resource must expose at least one HTTP or HTTPS endpoint. HTTPS endpoints are preferred over HTTP endpoints when selecting the browser target URL.\n\nBrowser, profile, and user data mode settings can also be supplied from configuration using `Aspire:Hosting:BrowserLogs:Browser`, `Aspire:Hosting:BrowserLogs:Profile`, and `Aspire:Hosting:BrowserLogs:UserDataMode`, or scoped to a specific resource with `Aspire:Hosting:BrowserLogs:{ResourceName}:Browser`, `Aspire:Hosting:BrowserLogs:{ResourceName}:Profile`, and `Aspire:Hosting:BrowserLogs:{ResourceName}:UserDataMode`. Explicit method arguments override configuration.\n\n## Examples\n\nAdd tracked browser logs for a web front end:\n\n```csharp\nvar builder = DistributedApplication.CreateBuilder(args);\n\nbuilder.AddProject<Projects.WebFrontend>(\"web\")\n .WithExternalHttpEndpoints()\n .WithBrowserLogs();\n```\n\n## ATS metadata\n\n### ATS export\n\n- Available to Polyglot AppHosts through the Aspire Type System.\n"
}
Captured 2026-10-12. Illustrative replay timing, not a performance claim. Complete public stdout / stderr / capture proof.
An exact member, not a plausible guess.
{
"id": "csharp/aspire.hosting.browsers/browserlogsbuilderextensions/methods#withbrowserlogs",
"name": "WithBrowserLogs(IResourceBuilder<T>, string?, string?, BrowserUserDataMode?)",
"language": "csharp",
"kind": "member"
}return markdownResponse(
renderCSharpMemberKindMarkdown(
routeProps.pkg,
routeProps.type,
routeProps.memberKind,
routeProps.allTypes,
base
)
);The response identifies the member and returns its reference content. The renderer preserves that identity in published Markdown.
Actual returned member / reference renderer. Selected fields, not fabricated output.
Choose scope before action.
Writing code isn't operating a system.
The next move needs an exact target, readiness, and a result you can inspect.
Wait for the actual native API.
aspire wait api --apphost 'demo\host\apphost.cs' --status healthy --timeout 120 --non-interactiveWaiting for resource 'api' to be healthy...
✅ Resource 'api' is healthy. (0.0s)
Captured 2026-10-12. Illustrative replay timing, not a performance claim. Complete public stdout / stderr / capture proof.
Inspect what is running.
aspire describe --apphost 'demo\host\apphost.cs' --format Json --non-interactive{
"resources": [
{
"name": "api-kewwzmak",
"displayName": "api",
"resourceType": "Executable",
"state": "Running",
"creationTimestamp": "2026-10-12T11:24:41+00:00",
"startTimestamp": "2026-10-12T11:24:41.8+00:00",
"source": "Weather.Api.exe",
"healthStatus": "Healthy",
"dashboardUrl": "[redacted dashboardUrl]",
"relationships": [
{
"type": "Reference",
"resourceName": "open-meteo"
},
{
"type": "Reference",
"resourceName": "reverse-geocoding"
},
{
"type": "Reference",
"resourceName": "geocoding"
},
{
"type": "Reference",
"resourceName": "open-meteo"
},
{
"type": "Reference",
"resourceName": "reverse-geocoding"
},
{
"type": "Reference",
"resourceName": "geocoding"
}
],
"urls": [
{
"name": "http",
"url": "http://localhost:62869"
}
],
"volumes": [],
"properties": {
"executable.args": null,
"executable.path": "[redacted local filesystem path]",
"executable.pid": 40208,
"executable.workDir": "[redacted local filesystem path]",
"resource.appArgs": [],
"resource.appArgsSensitivity": []
},
"environment": "[redacted environment]",
"healthReports": {
"api_http_/health_200_check": {
"status": "Healthy"
}
},
"commands": {
"restart": {
"displayName": "Restart",
"description": "Restart resource",
"state": "Enabled",
"sortOrder": 2
},
"stop": {
"displayName": "Stop",
"description": "Stop resource",
"state": "Enabled",
"sortOrder": 1
}
}
},
{
"name": "frontend-installer-trmtsfbm",
"displayName": "frontend-installer",
"resourceType": "Executable",
"state": "NotStarted",
"source": "npm",
"dashboardUrl": "[redacted dashboardUrl]",
"relationships": [
{
"type": "Parent",
"resourceName": "frontend-quwryuxr"
}
],
"urls": [],
"volumes": [],
"properties": {
"executable.args": null,
"executable.path": "npm",
"executable.pid": null,
"executable.workDir": "[redacted local filesystem path]",
"resource.appArgs": null,
"resource.appArgsSensitivity": null,
"resource.parentName": "frontend-quwryuxr"
},
"environment": "[redacted environment]",
"healthReports": {},
"commands": {
"start": {
"displayName": "Start",
"description": "Start resource",
"state": "Enabled",
"sortOrder": 0
}
}
},
{
"name": "frontend-quwryuxr",
"displayName": "frontend",
"resourceType": "Executable",
"state": "Running",
"creationTimestamp": "2026-10-12T11:24:42+00:00",
"startTimestamp": "2026-10-12T11:24:42.205+00:00",
"source": "npm",
"healthStatus": "Unhealthy",
"dashboardUrl": "[redacted dashboardUrl]",
"relationships": [
{
"type": "WaitFor",
"resourceName": "frontend-installer-trmtsfbm"
},
{
"type": "Reference",
"resourceName": "api-kewwzmak"
},
{
"type": "WaitFor",
"resourceName": "api-kewwzmak"
},
{
"type": "Reference",
"resourceName": "api-kewwzmak"
}
],
"urls": [
{
"name": "http",
"url": "http://localhost:62871"
}
],
"volumes": [],
"properties": {
"executable.args": null,
"executable.path": "npm",
"executable.pid": 14900,
"executable.workDir": "[redacted local filesystem path]",
"resource.appArgs": [
"run",
"dev",
"--",
"--port",
"11338"
],
"resource.appArgsSensitivity": [
0,
0,
0,
0,
0
],
"resource.launchConfigurationType": "node"
},
"environment": "[redacted environment]",
"healthReports": {
"frontend_http_/_200_check": {}
},
"commands": {
"restart": {
"displayName": "Restart",
"description": "Restart resource",
"state": "Enabled",
"sortOrder": 2
},
"stop": {
"displayName": "Stop",
"description": "Stop resource",
"state": "Enabled",
"sortOrder": 1
}
}
},
{
"name": "geocoding",
"displayName": "geocoding",
"resourceType": "ExternalService",
"state": "Running",
"source": "geocoding-api.open-meteo.com",
"healthStatus": "Healthy",
"dashboardUrl": "[redacted dashboardUrl]",
"relationships": [],
"urls": [
{
"name": "default",
"url": "https://geocoding-api.open-meteo.com/v1/search"
}
],
"volumes": [],
"properties": {
"resource.source": "geocoding-api.open-meteo.com"
},
"environment": "[redacted environment]",
"healthReports": {
"geocoding_external_/v1/search?name=Seattle&count=1&language=en&format=json_200_check": {
"status": "Healthy"
}
},
"commands": {}
},
{
"name": "open-meteo",
"displayName": "open-meteo",
"resourceType": "ExternalService",
"state": "Running",
"source": "api.open-meteo.com",
"healthStatus": "Healthy",
"dashboardUrl": "[redacted dashboardUrl]",
"relationships": [],
"urls": [
{
"name": "default",
"url": "https://api.open-meteo.com/"
}
],
"volumes": [],
"properties": {
"resource.source": "api.open-meteo.com"
},
"environment": "[redacted environment]",
"healthReports": {
"open-meteo_external_/v1/forecast?latitude=0&longitude=0¤t=temperature_2m_200_check": {
"status": "Healthy"
}
},
"commands": {}
},
{
"name": "reverse-geocoding",
"displayName": "reverse-geocoding",
"resourceType": "ExternalService",
"state": "Running",
"source": "api.bigdatacloud.net",
"healthStatus": "Healthy",
"dashboardUrl": "[redacted dashboardUrl]",
"relationships": [],
"urls": [
{
"name": "default",
"url": "https://api.bigdatacloud.net/data/reverse-geocode-client"
}
],
"volumes": [],
"properties": {
"resource.source": "api.bigdatacloud.net"
},
"environment": "[redacted environment]",
"healthReports": {
"reverse-geocoding_external_/data/reverse-geocode-client?latitude=0&longitude=0&localityLanguage=en_200_check": {
"status": "Healthy"
}
},
"commands": {}
}
]
}
Captured 2026-10-12. Illustrative replay timing, not a performance claim. Complete public stdout / stderr / capture proof.
See the request trace come back.
aspire otel traces api --apphost 'demo\host\apphost.cs' --format Json --non-interactive[
{
"traceId": "7030bd343ae293c5941aec96764b60a5",
"durationMs": 13,
"title": "GET /health",
"spans": [
{
"traceId": "7030bd343ae293c5941aec96764b60a5",
"spanId": "049b066236a5e0d2",
"kind": "Server",
"name": "GET /health",
"source": "api",
"destination": "localhost:62869",
"durationMs": 13,
"timestamp": "2026-10-12T11:24:42.1521661Z",
"attributes": {
"http.request.method": "GET",
"server.address": "localhost",
"server.port": "62869",
"url.scheme": "http",
"url.path": "/health",
"network.protocol.version": "1.1",
"http.route": "/health",
"http.response.status_code": "200 OK"
}
}
],
"hasError": false,
"timestamp": "2026-10-12T11:24:42.1521661Z",
"dashboardUrl": "[redacted dashboardUrl]"
},
{
"traceId": "7e37e007d193c9d60fa813ee5f4df1ca",
"durationMs": 523,
"title": "GET /api/weather",
"spans": [
{
"traceId": "7e37e007d193c9d60fa813ee5f4df1ca",
"spanId": "d5cdad39eba8b8fe",
"kind": "Server",
"name": "GET /api/weather",
"source": "api",
"destination": "localhost:62869",
"durationMs": 523,
"timestamp": "2026-10-12T11:24:44.6316734Z",
"attributes": {
"http.request.method": "GET",
"server.address": "localhost",
"server.port": "62869",
"user_agent.original": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.26200; en-US) PowerShell/7.6.6",
"url.scheme": "http",
"url.path": "/api/weather",
"url.query": "?lat=Redacted&lon=Redacted",
"network.protocol.version": "1.1",
"http.route": "/api/weather",
"http.response.status_code": "200 OK"
}
},
{
"traceId": "7e37e007d193c9d60fa813ee5f4df1ca",
"spanId": "e2d52233242d655c",
"parentSpanId": "d5cdad39eba8b8fe",
"kind": "Internal",
"name": "weather.forecast",
"source": "api",
"durationMs": 521,
"timestamp": "2026-10-12T11:24:44.6323495Z",
"attributes": {}
},
{
"traceId": "7e37e007d193c9d60fa813ee5f4df1ca",
"spanId": "2ebb083081915206",
"parentSpanId": "e2d52233242d655c",
"kind": "Client",
"name": "GET",
"source": "api",
"destination": "api.bigdatacloud.net:443",
"durationMs": 209,
"timestamp": "2026-10-12T11:24:44.6331319Z",
"attributes": {
"http.request.method": "GET",
"server.address": "api.bigdatacloud.net",
"server.port": "443",
"url.full": "https://api.bigdatacloud.net/data/reverse-geocode-client?*",
"http.response.status_code": "200 OK",
"network.protocol.version": "1.1"
}
},
{
"traceId": "7e37e007d193c9d60fa813ee5f4df1ca",
"spanId": "5da9f92c5b2662bb",
"parentSpanId": "e2d52233242d655c",
"kind": "Client",
"name": "GET",
"source": "api",
"destination": "api.open-meteo.com:443",
"durationMs": 520,
"timestamp": "2026-10-12T11:24:44.6331352Z",
"attributes": {
"http.request.method": "GET",
"server.address": "api.open-meteo.com",
"server.port": "443",
"url.full": "https://api.open-meteo.com/v1/forecast?*",
"http.response.status_code": "200 OK",
"network.protocol.version": "1.1"
}
}
],
"hasError": false,
"timestamp": "2026-10-12T11:24:44.6316734Z",
"dashboardUrl": "[redacted dashboardUrl]"
}
]
Captured 2026-10-12. Illustrative replay timing, not a performance claim. Complete public stdout / stderr / capture proof.
Guidance chooses. The CLI executes.
Skill
File-based instructions that guide workflow and tool choice.
aspire-monitoring/SKILL.mdA skill selects the observation; it does not replace the tool.
CLI tool
Executable operations that return current evidence.
aspire wait apiaspire describeaspire otel traces api
Public CLI 13.6.1. Exact operations and evidence.
Skills choose the next move.
Route and create
aspireaspire-initaspireifyaspire-project-v2-migrationRun and inspect
aspire-orchestrationaspire-monitoringaspire-deploymentaspire-orchestration selects the exact start/wait target. aspire-monitoring selects the logs and traces that ground the next decision.
Compiler facts become exact CLI references.
DLL + XML + available PDB → Roslyn canonical model → C# reference Markdown → separate API lookup.
Fetch current text. Reuse unchanged work.
using var request = new HttpRequestMessage(
HttpMethod.Get, url);
if (!string.IsNullOrEmpty(cachedETag))
{
request.Headers.IfNoneMatch.Add(
new EntityTagHeaderValue(cachedETag));
}var currentFingerprint =
SourceContentFingerprint.Compute(
content, IndexSchemaVersion);
await _docsCache.SetIndexAsync(
[.. documents], cancellationToken)
.ConfigureAwait(false);The narrative-docs path obtains the cached ETag, reuses matching fingerprints, and parses only when needed. Default corpus: llms-full.txt.
ETag source / cache/index source. Selected lines, wrapped for display.
The parser knows fences.
var span = content.AsSpan();
var codeBlocks = FindCodeBlockRegions(span);
// Find all document boundaries (line indices where H1 headers start)
var docBoundaries = FindDocumentBoundaries(span, codeBlocks);
// Find all section headings (H2+) using the pre-computed fence regions
// passed down from ParseAsync.
var sectionStarts = FindSectionHeadings(docSpan, codeBlocks);Headings inside code fences must not split the document.
Inspect the actual C# parser excerpt in docs/code. The rehearsal uses Aspire's parser, not a clone.
Find the relevant narrative section.
private const float TitleWeight = 10.0f;
private const float SummaryWeight = 8.0f;
private const float HeadingWeight = 6.0f;
private const float CodeWeight = 5.0f;
private const float BodyWeight = 1.0f;Configure browser logs
Rank prose and sections, then retrieve the content. This is not the separate compiler-derived API index.
Pinned narrative weights. Local lexical ranking, not embeddings or model training.
Know which process is native.
Native CLI and API. Managed C# AppHost. Browser React. Node only for frontend development/build.
Wire an executable, not a fallback.
var api = builder.AddExecutable("api", executable, nativeDirectory)
.WithHttpEndpoint(env: "PORT")
.WithHttpHealthCheck("/health")
.WithExternalHttpEndpoints()
.WithOtlpExporter(OtlpProtocol.HttpProtobuf);
builder.AddViteApp("frontend",
Path.Combine(builder.AppHostDirectory, "..", "frontend"))
.WithNpm(install: false)
.WithReference(api.GetEndpoint("http")).WaitFor(api)
.WithEnvironment("API_HTTP", api.GetEndpoint("http"))
.WithEnvironment("API_HTTPS", "");Actual AppHost: excerpt omits surrounding configuration. Dynamic ports, readiness, references, and OTLP are explicit.
Reason. Act precisely. Observe.
Advance to trace skills → CLI → resources → evidence → next decision.
Aspire dashboard
A running process isn't the whole story.
See resource state and the request's actual path. Share that evidence with the next human or agent decision.
Observe a running AppHost.
OTLP adds structured logs, traces and metrics.
Official dashboard overview and image source. Starting a C# or TypeScript AppHost launches the integrated dashboard; emitting telemetry still requires instrumentation.
Same viewer. No AppHost required.
aspire dashboard run --non-interactivedocker run --rm `
-p "127.0.0.1:18888:18888" `
-p "127.0.0.1:4317:18889" `
-p "127.0.0.1:4318:18890" `
mcr.microsoft.com/dotnet/aspire-dashboard:latestAny instrumented OTLP emitter can send telemetry. A standalone viewer does not orchestrate your processes.
Documented startup options, not recorded executions. Container ports map UI / gRPC / HTTP; loopback binding is a safety adaptation. Browser authentication remains enabled. The CLI command is foreground/blocking.
Instrumentation makes the evidence.
What happened, with structured context.
Where this request went across boundaries.
How behavior changes over time.
Instrument code, export OTLP, then inspect actual results.
Integrated resource service / independent telemetry service. Designed for development and short-term diagnostics, not unlimited production retention.
Aspire deployment
Application intent should outlive your laptop.
Keep relationships explicit. Publish target artifacts, deploy configured resources, and verify the actual outcome.
Start from deployable intent.
AppHost modelTarget integrationEnvironment resource
aspire add docker --non-interactivevar environment = builder
.AddDockerComposeEnvironment("env")
.WithDashboard();Declare deployable application resources in that model.
Explicitly labeled Docker Compose example, not a user-chosen target or the weather AppHost. AddDockerComposeEnvironment / WithDashboard. No integration or graph was changed here.
Publish describes the target.
aspire publish --apphost 'path\to\deployment\AppHost.cs' `
--output-path 'publish-output' --non-interactiveCompose publishing generates docker-compose.yaml and .env.
It does not build images or apply the deployment.
Documented publish pipeline. Public CLI 13.6.1 syntax; substitute a configured, deployment-ready AppHost. No generated output is presented as a capture.
Deploy applies the model.
aspire deploy --apphost 'path\to\deployment\AppHost.cs' `
--environment '<environment>' --non-interactiveDeploy works from the AppHost model, not an old publish folder.
Compose fills parameters, builds images, and runs Compose up.
Documented deploy pipeline. It deploys declared resources, not the AppHost process. No provisioning or deployment was performed for this presentation.
Same intent. Explicit boundaries.
Choose the supported integration and environment.
Resolve parameters, secrets, authentication and allowed side effects.
Inspect the deployed resources and observed health.
aspire-deployment guides the workflow; the CLI and target provider perform it.
Documented target setup / unchanged shipped deployment skill. Same model does not mean identical target behavior.
Aspire makes AI
more efficient.
AI loops
Aspire dashboard
Aspire deployment
Earlier architecture.
Current surface.
This rehearsal
llms-full.txt, CLI docs commands, and a separate API-reference index.
Optional appendix. Reusable fetching, parsing, indexing, and retrieval outlive a particular tool surface.
Reported documentation outcomes
82feature-docs PRs merged
44.8hmedian docs time-to-merge
Reported window: May 3-June 2, 2026.
Historical human-reviewed data, not a fresh SLA.
Optional appendix. Source and measurement context