Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .chronus/changes/tcgc-docs-fix-comments-2026-04-07.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@azure-tools/typespec-client-generator-core"
---

Fix inaccurate doc comments: @clientApiVersions param description, @deserializeEmptyStringAsNull example, @markAsLro typo, @client empty example, @override malformed comment.
7 changes: 7 additions & 0 deletions .chronus/changes/tcgc-docs-spector-specs-2026-04-07.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@azure-tools/azure-http-specs"
---

Add Spector test specs for @convenientAPI/@protocolAPI, @scope, @responseAsBool, and @clientDoc decorators.
53 changes: 53 additions & 0 deletions eng/scripts/doc-updater/knowledge/tcgc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# TCGC Documentation Knowledge Base

## Decorator Namespace Conventions in Spector Specs

In Spector specs (`packages/azure-http-specs/specs/`), TCGC decorators must use fully qualified names with the `@global.Azure.ClientGenerator.Core.` prefix (e.g., `@global.Azure.ClientGenerator.Core.convenientAPI(false)`). The `using Azure.ClientGenerator.Core;` import alone is NOT sufficient for decorator resolution in Spector spec compilation. The `@@` augmentation syntax also requires the `@@global.Azure.ClientGenerator.Core.` prefix.

## Doc Comment Source for Reference Docs

The auto-generated reference docs at `website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md` are generated from doc comments in `packages/typespec-client-generator-core/lib/decorators.tsp` and `lib/legacy.tsp`. Run `pnpm regen-docs` from `packages/typespec-client-generator-core/` after editing doc comments. Never edit the generated `decorators.md` directly.

## Access Decorator Syntax

The `@access` decorator takes `Access.internal` or `Access.public` enum values, NOT string literals. The previous documentation incorrectly showed `@@access(Target, "internal")` — the correct syntax is `@@access(Target, Access.internal)`.

## Howto Doc File Ownership

Each howto file owns specific topics:

- `01setup.mdx` — setup, language scoping (`@scope`)
- `02package.mdx` — `@service`, `@clientNamespace`, licensing
- `03client.mdx` — `@client`, `@clientInitialization`, `@paramAlias`, `@clientLocation`, client hierarchy
- `04method.mdx` — `@protocolAPI`, `@convenientAPI`, `@access`, `@usage`, `@override`, `@responseAsBool`, `@clientDoc`, transformation functions
- `05pagingOperations.mdx` — paging patterns, `@nextLinkVerb`, `@markAsPageable`, `@disablePageable`
- `06longRunningOperations.mdx` — LRO patterns, `@markAsLro`
- `08types.mdx` — type mappings, `@alternateType`, `@clientDefaultValue`, `@flattenProperty`, `@deserializeEmptyStringAsNull`
- `09renaming.mdx` — `@clientName`, `@encodedName`
- `10versioning.mdx` — `@versioned`, `@apiVersion`, `@clientApiVersions`
- `11hierarchyBuilding.mdx` — `@hierarchyBuilding` (legacy)
- `12clientOptions.mdx` — `@clientOption`

## pnpm Availability

In CI environments, `pnpm` may not be on PATH. Use `corepack pnpm` or create a wrapper script at `/tmp/gh-aw/agent/bin/pnpm` that invokes `node /home/runner/.cache/node/corepack/v1/pnpm/<version>/bin/pnpm.cjs`. Add `/tmp/gh-aw/agent/bin` to PATH.

## Spector Validation Sequence

From `packages/azure-http-specs`, run in this exact order:

1. `pnpm build` (compiles TypeScript and validates scenarios)
2. `pnpm validate-mock-apis` (verifies mock API implementations)
3. `pnpm cspell` (check spelling — run from repo root)
4. `pnpm format:dir packages/azure-http-specs` (run from repo root)
5. `pnpm regen-docs` (regenerate spec-summary.md)

Note: `pnpm lint` and `pnpm format` scripts don't exist in the azure-http-specs package — use root-level `pnpm format:dir`.

## Legacy Decorators Still Active

All decorators in `Azure.ClientGenerator.Core.Legacy` namespace (`lib/legacy.tsp`) are still functional and used in production: `@hierarchyBuilding`, `@flattenProperty`, `@markAsLro`, `@markAsPageable`, `@disablePageable`, `@nextLinkVerb`, `@clientDefaultValue`. They should be documented with caution warnings but NOT ignored.

## Decorators Without Spector Coverage (remaining)

The following decorators still need Spector specs: `@useSystemTextJsonConverter`, `@clientApiVersions`, `@clientOption`, `@markAsLro`, `@markAsPageable`, `@disablePageable`, and the transformation functions (`replaceParameter`, `removeParameter`, `addParameter`, `reorderParameters`).
115 changes: 115 additions & 0 deletions packages/azure-http-specs/spec-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,38 @@ Expected response body:
}
```

### Azure_ClientGenerator_Core_ClientDoc_AppendDoc

- Endpoint: `get /azure/client-generator-core/client-doc/appendDoc`

This scenario tests @clientDoc with append mode.
The operation's original doc is "Gets a resource."
After applying @clientDoc in append mode, the client documentation should be:
"Gets a resource. Returns null if the resource is not found."

Expected query parameter: name="sample"
Expected response body:

```json
{ "name": "sample" }
```

### Azure_ClientGenerator_Core_ClientDoc_ReplaceDoc

- Endpoint: `get /azure/client-generator-core/client-doc/replaceDoc`

This scenario tests @clientDoc with replace mode.
The operation's original doc is "Gets a resource."
After applying @clientDoc in replace mode, the client documentation should be:
"Fetch the named resource from the service."

Expected query parameter: name="sample"
Expected response body:

```json
{ "name": "sample" }
```

### Azure_ClientGenerator_Core_ClientInitialization_DefaultClient_HeaderParam

- Endpoints:
Expand Down Expand Up @@ -839,6 +871,45 @@ Expected client structure:
- Interface ResourceOperations should contain only operation `getResource`
- Root client should contain operation `getHealthStatus` (moved from ResourceOperations)

### Azure_ClientGenerator_Core_ConvenientApi_Both

- Endpoint: `get /azure/client-generator-core/convenient-api/both`

This scenario tests both @convenientAPI and @protocolAPI enabled (default behavior).
Both protocol and convenience methods should be generated.
Expected query parameter: name="sample"
Expected response body:

```json
{ "name": "sample" }
```

### Azure_ClientGenerator_Core_ConvenientApi_ConvenientOnly

- Endpoint: `get /azure/client-generator-core/convenient-api/protocolOnly`

This scenario tests @convenientAPI(false) on an operation, meaning only a protocol method
should be generated. The convenience method should NOT be generated.
Expected query parameter: name="sample"
Expected response body:

```json
{ "name": "sample" }
```

### Azure_ClientGenerator_Core_ConvenientApi_ProtocolOnly

- Endpoint: `get /azure/client-generator-core/convenient-api/convenientOnly`

This scenario tests @protocolAPI(false) on an operation, meaning only a convenience method
should be generated. The protocol method should NOT be generated.
Expected query parameter: name="sample"
Expected response body:

```json
{ "name": "sample" }
```

### Azure_ClientGenerator_Core_DeserializeEmptyStringAsNull_get

- Endpoint: `get /azure/client-generator-core/deserialize-empty-string-as-null/responseModel`
Expand Down Expand Up @@ -1205,6 +1276,50 @@ param2: param2

Expected response: 204 No Content

### Azure_ClientGenerator_Core_ResponseAsBool_Exists

- Endpoint: `head /azure/client-generator-core/response-as-bool/exists/found`

This scenario tests @responseAsBool on a HEAD operation that returns 200 (exists).
The generated method should return `true` for 2xx responses.
Expected request: HEAD /azure/client-generator-core/response-as-bool/exists/found
Expected response: 200

### Azure_ClientGenerator_Core_ResponseAsBool_NotExists

- Endpoint: `head /azure/client-generator-core/response-as-bool/exists/notFound`

This scenario tests @responseAsBool on a HEAD operation that returns 404 (not found).
The generated method should return `false` for 404 responses.
Expected request: HEAD /azure/client-generator-core/response-as-bool/exists/notFound
Expected response: 404

### Azure_ClientGenerator_Core_Scope_NegatedScopeOperation

- Endpoint: `get /azure/client-generator-core/scope/negatedOp`

This scenario tests that an operation scoped with negation is generated for all
languages except those excluded. The 'negatedOp' operation should NOT be generated
for C# but should be generated for all other languages.
Expected response body:

```json
{ "name": "sample" }
```

### Azure_ClientGenerator_Core_Scope_ScopedOperation

- Endpoint: `get /azure/client-generator-core/scope/scopedOp`

This scenario tests that an operation scoped to a specific set of languages is only
generated for those languages. The 'scopedOp' operation should only be generated for
Python and Java. Other languages should not include this operation.
Expected response body:

```json
{ "name": "sample" }
```

### Azure_ClientGenerator_Core_Usage_ModelInOperation

- Endpoints:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import "@typespec/http";
import "@typespec/spector";
import "@azure-tools/typespec-client-generator-core";

using Http;
using Spector;
using Azure.ClientGenerator.Core;

@doc("Test for @clientDoc decorator to override or append client documentation.")
@scenarioService("/azure/client-generator-core/client-doc")
@global.Azure.ClientGenerator.Core.clientNamespace("azure.clientgenerator.core.clientdoc", "java")
namespace _Specs_.Azure.ClientGenerator.Core.ClientDoc;

@scenario
@scenarioDoc("""
This scenario tests @clientDoc with append mode.
The operation's original doc is "Gets a resource."
After applying @clientDoc in append mode, the client documentation should be:
"Gets a resource. Returns null if the resource is not found."

Expected query parameter: name="sample"
Expected response body:
```json
{ "name": "sample" }
```
""")
namespace AppendDoc {
model Resource {
name: string;
}

/** Gets a resource. */
@route("/appendDoc")
@get
op getResource(@query name: string): Resource;
}

@@global.Azure.ClientGenerator.Core.clientDoc(AppendDoc.getResource,
"Returns null if the resource is not found.",
global.Azure.ClientGenerator.Core.DocumentationMode.append
);

@scenario
@scenarioDoc("""
This scenario tests @clientDoc with replace mode.
The operation's original doc is "Gets a resource."
After applying @clientDoc in replace mode, the client documentation should be:
"Fetch the named resource from the service."

Expected query parameter: name="sample"
Expected response body:
```json
{ "name": "sample" }
```
""")
namespace ReplaceDoc {
model Resource {
name: string;
}

/** Gets a resource. */
@route("/replaceDoc")
@get
op getResource(@query name: string): Resource;
}

@@global.Azure.ClientGenerator.Core.clientDoc(ReplaceDoc.getResource,
"Fetch the named resource from the service.",
global.Azure.ClientGenerator.Core.DocumentationMode.replace
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { json, MockApiDefinition, passOnSuccess, ScenarioMockApi } from "@typespec/spec-api";

export const Scenarios: Record<string, ScenarioMockApi> = {};

function createMockApiDefinition(route: string): MockApiDefinition {
return {
uri: `/azure/client-generator-core/client-doc/${route}`,
method: "get",
request: {
query: {
name: "sample",
},
},
response: {
status: 200,
body: json({ name: "sample" }),
},
kind: "MockApiDefinition",
};
}

Scenarios.Azure_ClientGenerator_Core_ClientDoc_AppendDoc = passOnSuccess([
createMockApiDefinition("appendDoc"),
]);

Scenarios.Azure_ClientGenerator_Core_ClientDoc_ReplaceDoc = passOnSuccess([
createMockApiDefinition("replaceDoc"),
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import "@typespec/http";
import "@typespec/spector";
import "@azure-tools/typespec-client-generator-core";

using Http;
using Spector;
using Azure.ClientGenerator.Core;

@doc("Test for @convenientAPI and @protocolAPI decorators.")
@scenarioService("/azure/client-generator-core/convenient-api")
@global.Azure.ClientGenerator.Core.clientNamespace(
"azure.clientgenerator.core.convenientapi",
"java"
)
namespace _Specs_.Azure.ClientGenerator.Core.ConvenientApi;

model Resource {
name: string;
description?: string;
}

@scenario
@scenarioDoc("""
This scenario tests @convenientAPI(false) on an operation, meaning only a protocol method
should be generated. The convenience method should NOT be generated.
Expected query parameter: name="sample"
Expected response body:
```json
{ "name": "sample" }
```
""")
namespace ConvenientOnly {
@route("/protocolOnly")
@get
@global.Azure.ClientGenerator.Core.convenientAPI(false)
op protocolOnlyOp(@query name: string): Resource;
}

@scenario
@scenarioDoc("""
This scenario tests @protocolAPI(false) on an operation, meaning only a convenience method
should be generated. The protocol method should NOT be generated.
Expected query parameter: name="sample"
Expected response body:
```json
{ "name": "sample" }
```
""")
namespace ProtocolOnly {
@route("/convenientOnly")
@get
@global.Azure.ClientGenerator.Core.protocolAPI(false)
op convenientOnlyOp(@query name: string): Resource;
}

@scenario
@scenarioDoc("""
This scenario tests both @convenientAPI and @protocolAPI enabled (default behavior).
Both protocol and convenience methods should be generated.
Expected query parameter: name="sample"
Expected response body:
```json
{ "name": "sample" }
```
""")
namespace Both {
@route("/both")
@get
op bothOp(@query name: string): Resource;
}
Loading
Loading