diff --git a/.agents/skills/doc-writer/SKILL.md b/.agents/skills/doc-writer/SKILL.md index 2c3392ab8..68c0591ae 100644 --- a/.agents/skills/doc-writer/SKILL.md +++ b/.agents/skills/doc-writer/SKILL.md @@ -259,13 +259,13 @@ A few rules to follow: - Do **not** wrap a Bash + PowerShell pairing in bare `` — convert it to `OsAwareTabs` instead. This is the canonical pattern used across the dashboard, install-cli, container-networking, and AKS deployment guides. - Always set `syncKey="terminal"` unless there is a specific reason to scope the persistence differently. The site-wide convention is a single shared key so a reader who picks PowerShell once continues to see PowerShell on every page that offers the choice. - Keep the leading and trailing blank lines around the inner code fences (as shown above). MDX requires the blank lines so the fenced code block is parsed correctly inside the slotted `
`. -- `OsAwareTabs` is **only** for the Bash + PowerShell pairing. Continue to use bare `` / `` for non-OS choices such as C#/TypeScript AppHost samples (`syncKey='aspire-lang'`), CLI vs IDE, deployment targets, or package managers. +- `OsAwareTabs` is **only** for the Bash + PowerShell pairing. Use `AppHostTabs` for AppHost-language choices. Continue to use bare `` / `` for other concerns such as CLI vs IDE, deployment targets, or package managers. #### Pivot/PivotSelector Use `Pivot` and `PivotSelector` sparingly, only for **key landing-page-style articles** where the choice should persist across page navigations and where sharing the page through a URL should land the reader on a specific variant. Pivots support query string values to set the selected option (for example, `?aspire-lang=typescript`). Examples in use today include the [Build your first Aspire app](/get-started/first-app/) and [Deploy your first Aspire app](/get-started/deploy-first-app/) tutorials. -For most pages — including AppHost C# and TypeScript code samples within a guide — prefer synced `Tabs` / `TabItem` blocks at the snippet level instead. See [AppHost Language Parity (C# and TypeScript)](#apphost-language-parity-c-and-typescript). +For most pages — including AppHost code samples within a guide — prefer `AppHostTabs` at the snippet level instead. See [AppHost Language Parity](#apphost-language-parity). ```mdx - +import AppHostTabs from '@components/AppHostTabs.astro'; + + + TypeScript example content here. - + - + C# example content here. - - + + ``` #### CardGrid and LinkCard @@ -445,27 +447,32 @@ For client/library packages: ``` -## AppHost Language Parity (TypeScript and C#) +## AppHost Language Parity -Aspire supports both **TypeScript AppHosts** (`apphost.mts`) and **C# AppHosts** (`AppHost.cs`). Documentation must treat both languages as first-class citizens. **Always show both TypeScript and C# code samples for AppHost code unless the feature is genuinely language-specific or TypeScript support does not exist yet.** Never write AppHost or hosting-integration documentation with a C#-only bias. +Aspire supports AppHost authoring in **TypeScript**, **C#**, **Python**, **Go**, **Java**, and **Rust**. The central registry at `src/frontend/src/data/apphost-languages.json` controls which languages are visible. Documentation must be ready for every registry language before its `enabled` bit is turned on. ### Core Principles -1. **Always show both languages**: Every AppHost-focused example, walkthrough, and AppHost code sample must include both TypeScript and C# variants unless the feature is genuinely language-specific. -2. **Show implementations, not availability notes**: When a TypeScript AppHost API exists, demonstrate it in a complete TypeScript tab beside the C# example. A note or callout that only names the available TypeScript methods does not satisfy language parity. -3. **Use neutral framing**: Write prose that applies to both languages. Say "In your AppHost" not "In your C# project". Say "Add a Redis resource" not "Call `builder.AddRedis()`". -4. **Default to TypeScript**: Put the TypeScript tab first so `apphost.mts` is on the left and selected for readers without a saved preference. Keep C# as an equal peer and preserve the reader's explicit language selection. -5. **Verify TypeScript APIs exist**: Before writing a TypeScript example, confirm the API exists in the TypeScript AppHost SDK. Do not invent TypeScript samples — if you are unsure whether an API is available, flag it for review. +1. **Account for every language**: Every AppHost-focused example must provide a verified slot or an explicit operation-level limitation for every enabled registry language. +2. **Show implementations, not availability notes**: When a generated API exists, demonstrate it in a complete language slot. A note that merely names generated methods does not satisfy parity. +3. **Use neutral framing**: Write prose that applies across languages. Say "In your AppHost" not "In your C# project". Say "Add a Redis resource" not "Call `builder.AddRedis()`". +4. **Use registry order**: Do not order or label tabs manually. `AppHostTabs` keeps TypeScript as the default and preserves the reader's explicit selection. +5. **Verify generated APIs**: Check the exact generated SDK for each language. Naming, optional arguments, unions, callbacks, fluent returns, and errors differ by generator; never infer one language from another. ### AppHost tabs pattern for AppHost content -Use synced `Tabs` for AppHost-specific content that changes between TypeScript and C#. Each AppHost code snippet should provide its own language tabs, list TypeScript first, and use `syncKey='aspire-lang'` so the user's language choice stays synchronized across snippets on the page. +Use `AppHostTabs` for AppHost-specific content. The component synchronizes the selected language and suppresses fallback code when the selected language has an explicit limitation. ````mdx -import { Tabs, TabItem } from "@astrojs/starlight/components"; +import AppHostTabs from "@components/AppHostTabs.astro"; - - + + ```typescript title="apphost.mts" import { createBuilder } from "./.aspire/modules/aspire.mjs"; @@ -480,8 +487,8 @@ await api.withReference(cache); await builder.build().run(); ``` - - + + ```csharp title="AppHost.cs" var builder = DistributedApplication.CreateBuilder(args); @@ -494,27 +501,24 @@ builder.AddProject("api") builder.Build().Run(); ``` - - + + ```` -Use the same synced tabs pattern for more than code blocks when needed. Entire paragraphs, lists, asides, or multi-step sections can live inside the `csharp` and `typescript` tab items when the workflows differ. - -Use different `syncKey` values for other concerns such as CLI vs IDE, deployment targets, platform choices, or package managers. For AppHost language tabs, use exactly `syncKey='aspire-lang'`. +Replace each limitation with a language slot when the generated API is available. Entire paragraphs, lists, asides, or multi-step sections can live inside language slots when workflows differ. -If a section heading should appear in the **On this page** table of contents, keep that heading outside `Tabs`. Headings inside `TabItem` content may be skipped by the TOC generator, so the recommended pattern is a shared heading followed by tabs containing only the language-specific body content. +If a section heading should appear in the **On this page** table of contents, keep that heading outside `AppHostTabs`. The recommended pattern is a shared heading followed by language-specific body content. ### Conventions -| Aspect | TypeScript | C# | -| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | -| File title | `title="apphost.mts"` | `title="AppHost.cs"` | -| Tab wrapper | Shared `` container | Shared `` container | -| Tab item | `` | `` | -| Builder creation | `import { createBuilder } from './.aspire/modules/aspire.mjs';` then newline for space followed by `await createBuilder();` | `DistributedApplication.CreateBuilder(args)` | -| Method casing | camelCase (`addRedis`) | PascalCase (`AddRedis`) | -| Async pattern | `await` each builder call | Synchronous fluent calls | -| Build & run | `await builder.build().run()` | `builder.Build().Run()` | +| Language | File title | Typical method style | Runtime/error style | +| ---------- | --------------- | -------------------- | ------------------- | +| TypeScript | `apphost.mts` | camelCase | async/`await` | +| C# | `AppHost.cs` or `apphost.cs` | PascalCase | synchronous fluent calls | +| Python | `apphost.py` | `snake_case` | exceptions and context manager | +| Go | `apphost.go` | PascalCase | returned errors and fluent `Err()` | +| Java | `AppHost.java` | camelCase | synchronous runtime exceptions | +| Rust | `apphost.rs` | `snake_case` | `Result`, `Option`, and `?` | ### Prose Guidelines @@ -523,23 +527,27 @@ When writing narrative text around AppHost examples: - ✅ "Add a Redis resource to your AppHost" - ✅ "The following example shows how to configure a PostgreSQL resource" - ❌ "Call `builder.AddRedis()` in your _Program.cs_" (C#-specific) -- ❌ "Add the following C# code to your AppHost" (when both languages should be shown) +- ❌ "Add the following C# code to your AppHost" (when multiple AppHost languages are supported) -When a concept differs between languages (e.g., configuration files, async patterns), explain both within the AppHost language tabs or in language-neutral prose above the tabs. +When a concept differs between languages (for example, configuration files, async patterns, options, or errors), explain each relevant behavior inside `AppHostTabs` or in language-neutral prose above it. -### When TypeScript Is Not Yet Supported +### When a Language Is Not Supported -If a hosting integration does not yet have TypeScript AppHost support, show only the C# example without language tabs and add a note: +If a generated AppHost SDK does not expose an operation, omit that language's code slot and add an operation-level limitation: ```mdx - + + ... + ... + ... + ... + ... + ``` -Do **not** wrap a single language in a single-language `` component — that creates a misleading UI suggesting another option exists. - -Use this exception at the operation level, not as a shortcut for the whole page. If some APIs are exported to TypeScript and others are not, provide synchronized C# and TypeScript tabs for every supported operation and place the limitation beside only the unsupported operation. +Use limitations at the operation level, not as a shortcut for the whole page. The selected unsupported language displays its limitation without silently falling back to another language. ## Integration Documentation @@ -570,7 +578,8 @@ title: [Technology] integration description: Learn how to use the [Technology] integration with Aspire. --- -import { Aside, Tabs, TabItem } from "@astrojs/starlight/components"; +import { Aside } from "@astrojs/starlight/components"; +import AppHostTabs from "@components/AppHostTabs.astro"; import InstallPackage from "@components/InstallPackage.astro"; import { Image } from "astro:assets"; @@ -594,8 +603,13 @@ Brief description of the technology and what the integration enables. ### Add [Technology] resource - - + + ```typescript title="apphost.mts" import { createBuilder } from "./.aspire/modules/aspire.mjs"; @@ -607,8 +621,8 @@ const tech = await builder.addTechnology("tech"); await builder.build().run(); ``` - - + + ```csharp title="AppHost.cs" var builder = DistributedApplication.CreateBuilder(args); @@ -619,8 +633,8 @@ var tech = builder.AddTechnology("tech"); builder.Build().Run(); ``` - - + + ### Configuration options @@ -643,8 +657,13 @@ Include both hosting and client sections: ### Add [Technology] resource - - + + ```typescript title="apphost.mts" import { createBuilder } from "./.aspire/modules/aspire.mjs"; @@ -659,8 +678,8 @@ await api.withReference(tech); await builder.build().run(); ``` - - + + ```csharp title="AppHost.cs" var builder = DistributedApplication.CreateBuilder(args); @@ -673,8 +692,8 @@ builder.AddProject("api") builder.Build().Run(); ``` - - + + ### Hosting integration health checks diff --git a/.agents/skills/whatsnew/references/02-research.md b/.agents/skills/whatsnew/references/02-research.md index b39a82d90..ac1e6f2ae 100644 --- a/.agents/skills/whatsnew/references/02-research.md +++ b/.agents/skills/whatsnew/references/02-research.md @@ -47,8 +47,8 @@ This is the phase that turns the skeleton into a real, reviewable page. the section (next step). 7. **Author the draft.** Populate the scaffolded MDX from the dossier: write the lede, the "This release introduces" bullets (1:1 with the `##` sections, same order), and - each section body with impact-first prose, `LearnMore` deep-links, and TypeScript/C# - `` with TypeScript first where a feature spans AppHost languages. Credit each + each section body with impact-first prose, `LearnMore` deep-links, and registry-backed + `AppHostTabs` where a feature spans AppHost languages. Credit each merged community PR by `@handle`. **Only include sections that apply** — delete any standard section with no content. In particular, when the release has **no breaking changes**, remove the "⚠️ Breaking changes" section *and* the breaking-changes diff --git a/.agents/skills/whatsnew/references/03-critique.md b/.agents/skills/whatsnew/references/03-critique.md index b945bb0f7..2f557eadf 100644 --- a/.agents/skills/whatsnew/references/03-critique.md +++ b/.agents/skills/whatsnew/references/03-critique.md @@ -28,7 +28,7 @@ actionable, severity-ranked findings report. **Makes no edits** — {polish} act - `