Repository navigation
[Swagger Linter Migration] DescriptionMustNotBeNodeName + DescriptiveDescriptionRequired -> documentation-required - #5663
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
All changed packages have been documented.
Show changes
|
📦 Package size report2 packages changed size, +8.05 KB (+0.0%) packed overall.
12 package(s) with no notable change
Packed = gzipped |
commit: |
There was a problem hiding this comment.
🟡 Changes recommended
Inferred HTTP verbs and encoded property names currently escape checks performed by the legacy validator.
2 open findings
What changed in this PR
Adds an opt-in Azure Core lint rule migrating DescriptionMustNotBeNodeName.
Changes:
- Implements and tests uninformative-documentation detection.
- Registers the rule disabled by default in both Azure rulesets.
- Adds documentation and Chronus entries.
| File | Description |
|---|---|
website/.../reference/linter.md |
Lists the rule. |
packages/typespec-azure-rulesets/test/validate-rules-defined.test.ts |
Verifies opt-in status. |
packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts |
Registers disabled rule. |
packages/typespec-azure-rulesets/src/rulesets/data-plane.ts |
Registers disabled rule. |
packages/typespec-azure-core/test/rules/no-uninformative-doc.test.ts |
Adds rule tests. |
packages/typespec-azure-core/src/rules/no-uninformative-doc.ts |
Implements the rule. |
packages/typespec-azure-core/src/rules/no-uninformative-doc.md |
Documents behavior. |
packages/typespec-azure-core/src/linter.ts |
Exports the rule. |
packages/typespec-azure-core/README.md |
Lists the rule. |
.chronus/changes/register-no-uninformative-doc-2026-10-10.md |
Describes ruleset registration. |
.chronus/changes/no-uninformative-doc-2026-10-10.md |
Describes the Core feature. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
You can try these changes here
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve authored and default HTTP part names independently of JSON naming, preserve repeated-part metadata and port reviewed native regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Original Swagger linter - linter code: [DescriptionMustNotBeNodeName](https://github.com/Azure/azure-openapi-validator/blob/main/packages/rulesets/src/native/legacyRules/DescriptionMustNotBeNodeName.ts) - linter doc: [description-must-not-be-node-name.md](https://github.com/Azure/azure-openapi-validator/blob/main/docs/description-must-not-be-node-name.md) Specific checks: - [x] Select objects containing a `description` field recursively. - [x] Ignore non-string descriptions. - [x] Normalize description/name text by trimming whitespace, removing periods, and lowercasing. - [x] Reject a description matching an explicit string `name`. - [x] Otherwise reject a description matching the containing node's string path key. - [x] Otherwise reject the literal placeholder `description`. - [x] Report at the offending `description` field. ## How the Swagger linter works The legacy ARM rule recursively selects description-bearing Swagger objects and compares their text with an explicit name or the object's path key. Consequently it can inspect schema/property names, HTTP verbs, parameters, and `x-ms-enum.values` metadata. It emits at most one finding per selected object using the ordered conditions above. Its executable ARM registration is the audience source of truth despite the documentation's broader applicability wording. The validator counts emitted occurrences, not unique authored declarations. SQL's repeated root files and AlertsManagement's inline parameter copies therefore multiply findings. Generated or qualified schema names, client compatibility types, unused source declarations, and literal route parameters also make output nodes differ from editable TypeSpec targets. Those differences are investigated rather than recreated through emitter-dependent production logic. ## How the migrated TypeSpec linter works Mapping: `DescriptionMustNotBeNodeName` → `tsp-lintdiff-local-linter/description-must-not-be-node-name`. The native rule checks explicit documentation obtained through compiler `getDoc` on named models, scalars, enums, unions, enum members, union variants, model properties, and operations. HTTP path/query/header names take precedence over compiler-resolved ordinary JSON property names. `getHttpOperation` supplies the effective operation verb, including inferred GET without a body and POST with a body. Missing or normalized-empty documentation remains outside this rule; status-code properties remain exempt. Earlier changes closed missing enum-member/union-variant checks and removed the blanket key-property exemption. Enum members are iterated from the enum listener, following official `documentation-required` prior art because the installed compiler walker does not dispatch enum-member events. Named union variants compare their name; unnamed string variants compare their literal value, while other unnamed variants are checked only for placeholder text. Cycle1 repairs two additional native semantic omissions confirmed during [promotion review](#5663 (review)): - `@route("/widgets") @doc("get") op read(): string;` now warns using its inferred GET verb; a body-bearing operation documented only as `post` likewise warns. - `@encodedName("application/json", "wire-name") @doc("wire-name") sourceName: string;` now warns using its JSON wire name. Documentation matching only `sourceName` is compliant when JSON serialization renames it. These are supported compiler/HTTP semantics, not emitter overrides. Explicit verbs, normalization, placeholder handling, HTTP parameter-name precedence, and authored diagnostic targets are preserved. Each violation targets its semantic declaration. The rule does not deduplicate distinct semantic instances merely because they share a source location. It does not project versions, invoke emission, predict generated schema names, or depend on OpenAPI/emitter/SDK APIs. Valid authored warnings from older versions remain native findings; selected-version attribution is separate research evidence. ## Migration evidence and validation See [migration.md](https://github.com/Azure/typespec-azure/blob/lintdiff-description-must-not-be-node-name/packages/typespec-lintdiff/test/fixtures/DescriptionMustNotBeNodeName/migration.md) for the native contract, supported-shape evidence, regenerated full corpus, verified selected-version attribution reuse, all one-sided projects, six compilation exclusions, concrete outlier explanations, and preserved research limitations. The unchanged corpus diagnostic multiset did not expose the two repaired shapes; focused native tests and emitted comparison fixtures establish those fixes. Required cycle1 validation passed: explicit maintained-file Prettier write/check, package build, changed-TypeScript oxlint with denied warnings, 41 native predicate tests, 13 strict rule fixtures, the affected 14-case PATCH fixture group, representative and full corpus runs, and diff hygiene. Independent persistent local precommit review was clean on the complete branch diff and verified all supplied content/receipt hashes, with nine additional native probes. The documented native contract is covered; arbitrary executable Swagger equivalence remains partial, not claimed from project overlap or raw count equality. All 1,005 generated corpus changes were archived and restored through the exact approved manifest plan. Corpus/coverage outputs and temporary fixture junctions are excluded from this PR. ## Cycle2 multipart source repair Confirmed promotion review thread `PRRT_kwDOKoCXvs6rCcjD` (comment4237005053) exposed a missed authored multipart part name. The native rule now uses an authored `getHttpPart(program, type)?.options.name` when present and otherwise the source property name, including repeated parts via compiler array metadata; JSON encoded names remain limited to ordinary non-part properties. Path/query/header precedence and status-code exemption remain intact. Renamed-part violation, source-name-only compliance, default-name JSON-isolation, meaningful-documentation, and supported scalar/array/repeated-part regressions preserve earlier inferred-verb and ordinary JSON-name fixes. Production uses public compiler/HTTP APIs only. Validation:47 native tests,15 strict target-rule fixtures and all five genuinely affected fixture groups pass. The full current468-project corpus has462 successful projects and six preserved compiler exclusions. Complete8,499 diagnostic multisets match prior archived results, allowing conservative selected-version attribution reuse:7,501 findings/142projects. Generated corpus output was archived and exactly1,005 planned paths restored; none is included in this PR. Detailed native contract, parity limits and current corpus evidence remain in the linked migration document above. Historical Formdata required-gate failure is preserved; explicit user authorization prospectively excluded its unchanged unmapped data-plane group without retry or edit. A separately authorized single snapshot correction only reordered seven identical warnings for preexisting listener-phase drift; no unrelated semantics changed. Outer persistent independent local review `f235a949` is clean, with actual follow-up capability and additional multiple-operation/shared/spread/repeated-part probes verified. SourceRepairCycles2/3, development ordinary corrections3/3 and task-wide reserve2/3 remain unchanged. Fresh GitHub review of this exact repair head is required before promotion. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary Update five lint development/promotion skills to prevent the recurring review feedback in the supplied 58-comment retrospective across 17 migration PRs. Target `feature/lintdiff-migration-new`, the branch current when this work was requested. | Review theme | Comments | Prevention | | --- | ---: | --- | | Redundant internal change entries | 28 | Inspect and reuse task-owned unreleased feature/fix coverage before creating entries; preserve genuinely needed coverage for other packages. | | Multiple behavior suites for one public rule | 14 | Extend the canonical suite, found by its imported rule object even when its historical filename differs from the public name. | | Repetitive test headings | 13 | Use file-scope cases or meaningful scenario groups, preserving scoped setup rather than repeating the rule/filename. | | Private fixture APIs | 2 | Use supported public imports/decorators; redesign or remove artificial private-only cases without changing production scope. | | Bespoke Markdown parser | 1 | Enforce the existing prohibition without relocating the parser or losing distinct semantic/example evidence. | These are five recurring lessons, not 58 independent design judgments: 55 comments explicitly disclose Copilot generation, and three have no automation disclosure. Original report IDs 37-39 on #5591 and #5654 remain outside the selection. ## Changes - `typespec-lint-validate`: define canonical suite ownership and meaningful grouping; clarify genuine public-entry/package tests versus duplicate rule execution under import guards; require public fixture APIs. - `typespec-lint-implement`: centralize unreleased change-entry reconciliation and include the shared test policies in implementation/final review. - `lintdiff-rule-promote`: replace unconditional creation of a test file/change entry with discovery and reuse; reconcile acceptance-evidence and review instructions with the shared policies. - `develop-lintdiff-rule`: reference the shared policies during test planning and independent review; preserve migration/corpus requirements. - `create-linter-rule`: apply the same guidance to scaffold cleanup, existing-rule extensions, public fixtures, and changelog coverage. ## Evidence and boundaries - [Changelog guidance, report 22](#5502 (comment)) - [Private APIs, report 23](#5502 (comment)) - [One behavior suite per rule, report 36](#5556 (comment)) - [Historical canonical filename, report 48](#5663 (comment)) - [Isolation-suite duplication, report 53](#5665 (comment)) - [Meaningful scenario grouping, report 55](#5673 (comment)) - [Markdown parser removal, report 33](#5551 (comment)) Keep independent utility tests, genuine package/registration evidence, rule-owned diagnostic regressions, and actual published-example validation. Keep separate rulesets-package internal coverage when needed; do not delete all internal entries. Existing production semantics, audience/enablement, source immutability, parity evidence, and required validation remain unchanged. Skills only: no generator, rule implementation/test, package, dependency, release entry, or CI changes. The scaffolder's redundant wrapper is explicitly handled by cleanup guidance; changing the generator is a separate follow-up. ## Validation - Scoped Prettier write/check on the five changed Markdown files, using the repository's scalar formatting settings without optional plugins because the configured TypeSpec plugin has no built output. No dependencies or submodules were initialized for this documentation update. - Relative Markdown file/heading reference checks and full five-file diff review. - `git diff --check` and explicit staged-file scope verification. No package builds, corpus runs, docs regeneration, or repository-wide formatting/source linting are needed for this Markdown-only change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Run explicit documentation checks from native callbacks, retain missing-doc exemptions, and exercise the public rule without diagnostic filtering. Prune redundant fixtures while preserving multipart boundaries and precise targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Copilot resolve the merge conflicts in this pull request |
…ription-must-not-be-node-name # Conflicts: # packages/typespec-azure-rulesets/test/validate-rules-defined.test.ts Co-authored-by: msyyc <70930885+msyyc@users.noreply.github.com>
Resolved in 3612bc1 by merging the latest |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… use-standard-response-codes (Azure#5556) ## Documentation example cleanup (2026-10-21) Addressed review **5556-R01** in Azure@a35e14e: shortened both published examples to the `ArmResourceRead<Widget, Error = NotFoundResponse>` mistake and standard `ArmResourceRead<Widget>` fix, assuming `Widget` is defined. No full specification or shared setup section is published. Rule behavior, enablement, canonical semantic fixtures and the two existing package changelogs are unchanged. The earlier standalone compilation receipt below applies to the old full examples, not these intentionally contextual excerpts. The actual current Markdown snippets (SHA-256 `4ea418badf18d7e5d04ddb45f1a12e51c32ef8ba2efd3060a86772558a7ad372`) were compiled once with an explicit public ARM service/Widget context using `mise exec -- node --input-type=module` and the compiler `compile`/`NodeHost` API: exactly one expected rule warning for the incorrect excerpt and zero diagnostics for the correct excerpt. This is a one-time contextual validation receipt, not standalone or continuous Markdown coverage; no parser or testing system was added to the repository. Fresh local validation: all **40 canonical rule tests** passed; ARM documentation regeneration completed with existing unrelated missing-documentation warnings; repository `pnpm format` and `pnpm lint`, explicit source/generated Markdown formatting checks, and diff hygiene passed. Unrelated formatter output was excluded. Final-head CI is tracked separately and is not claimed passed here. ## Review fixes and current-main integration (2026-10-20) Published in Azure@c8ae20e. Merged Azure main `2cd8ba15594de155ae4c79719d7e796577ccdcd7` normally and retained both the migration rule and main's new ARM rule registrations in the conflict resolutions. Trimmed duplicate POST 201, providerless POST 404, GET 200, header-bearing 202 and literal/range fixtures in the canonical suite. One standalone native ARM positive/negative pair remains, along with singleton-200, broad-range, service-boundary and diagnostic-cardinality regressions. Rule behavior and enablement are unchanged. This supersedes earlier fixture-title mappings where cases were consolidated. Fresh local validation on the integrated tree: ARM dependency-closure build and production typecheck; **69 tests across four response/helper/merged-rule suites**; **7 ruleset tests**; ARM documentation regeneration; root `pnpm format` and `pnpm lint`; Chronus status and diff checks. The actual published examples were compiled once as unwrapped imported files: one expected warning at the incorrect read operation versus zero diagnostics for the correct example. This receipt is not continuous Markdown coverage. Documentation regeneration retains existing unrelated missing-documentation warnings. Fresh final-head CI is tracked separately; local results do not claim CI approval. ## Summary Consolidate **NoErrorCodeResponses** and **GetResponseCodes** into **`@azure-tools/typespec-azure-resource-manager/use-standard-response-codes`**. This PR absorbs Azure#5585, including its GET implementation and regression coverage, instead of introducing a second public `use-standard-get-response-codes` rule. Implemented in merge commit [`a17c998ab9a4e271e9bc812b083ed29db6d062a2`](Azure@a17c998), preserving both promotion histories. This follows the combined-migration presentation used in Azure#5294, Azure#5423, and Azure#5663/Azure#5674, and addresses the [consolidation request on Azure#5585](Azure#5585 (comment)). Further review of both response-code checks should continue here. Closing Azure#5585 as superseded does not imply CI or review approval; its branch and review history are retained, and existing review threads are not resolved by this consolidation. ## Combined behavior and legacy-rule mapping | Swagger validator | Native coverage | | --- | --- | | [NoErrorCodeResponses](https://github.com/Azure/azure-openapi-validator/blob/main/packages/rulesets/src/spectral/functions/no-error-code-responses.ts) | Concrete HTTP operations may only use `200`, `201`, `202`, `204`, or default. This general policy still checks operations without service or provider metadata, including operations outside declared services. | | [GetResponseCodes](https://github.com/Azure/azure-openapi-validator/blob/1198225afecbb818c3050d4d2a91da92e14e56ce/packages/rulesets/src/spectral/functions/get-response-codes.ts) | GET operations inside a service namespace or its descendants must include `200` and use only `200`, `202`, or default. Missing/empty responses and disallowed statuses produce one warning per invalid GET operation. | The rules are related, not interchangeable: GET `202 + default`, `200 + 201`, and `200 + 204` pass the general allowlist but fail the GET contract. Both contracts are now implemented by one rule and one operation listener. - **Scope is preserved, not silently unified.** The general policy remains service-independent; the stricter GET policy retains Azure#5585's service ancestry boundary. A GET returning `204` outside services remains valid under the general policy, while an explicit `404` outside services is still diagnosed. Provider namespace metadata is not required. - **Diagnostics are deliberately deduplicated.** An invalid service GET receives one GET-specific warning, even when `200` is missing and several statuses are disallowed. It does not additionally receive the general policy's warnings. Other operations retain one warning per disallowed response status, with their existing wording and authored operation targets. - Numeric statuses and singleton ranges are equivalent; broader ranges remain disallowed. Concrete aliases remain independent endpoints, and existing template guards are retained. - A GET with only `200` is valid. Default errors and `Location` headers are not required by either migrated validator function; their separate rules retain ownership. Response payload/schema correctness is also outside this rule. - The central resource-manager ruleset retains a single **disabled (`false`)** registration for `use-standard-response-codes`. There is no new data-plane registration or automatic enablement. Explicitly enabling the combined rule enables both policies in their documented scopes. - The standalone GET export, source/docs rule files, registration, generated index entries, ARM matrix row, and duplicate changelog entries are removed. The absorbed tests are retained as `use-standard-response-codes-get.test.ts`; one ARM feature changelog and the existing ruleset internal entry describe the combined migration. The native rule does not reproduce Swagger emission multiplicity, SDK-scoped emitter filtering, the general validator's omission of `x-ms-paths`, or historical API-version projections. The temporary lintdiff GET rule uses provider ancestry in its mixed ARM/data-plane runner; the official GET policy retains service ancestry. Exact emitted diagnostic parity is not claimed. ## Consolidation and provenance | Check | Immutable reviewed lintdiff source | Promotion history incorporated here | | --- | --- | --- | | NoErrorCodeResponses | Azure#5555 at [`e4f6aadb`](Azure@e4f6aad) | Azure#5556 at [`d6aea03d3`](Azure@d6aea03). | | GetResponseCodes | Azure#5584 at [`99231260`](Azure@9923126) | Azure#5585 at [`264dc6a0a`](Azure@264dc6a). | The immutable lintdiff source implementations and their existing migration evidence are unchanged. This is an explicitly requested consolidation of the official destination rules, not an unreviewed repair of either temporary source. The existing `C:\dev\worktrees\promote-lintdiff-no-error-code-responses` worktree was reused. ## Fixture-to-native test mapping after consolidation The original NoErrorCodeResponses fixtures retain their native coverage in `use-standard-response-codes.test.ts`. General-policy literal/range and per-status diagnostics now use POST so they do not accidentally exercise the stricter GET branch; the standard ARM read and documentation-example expectations use the combined rule's GET diagnostic. The GetResponseCodes fixtures retain their native coverage in `use-standard-response-codes-get.test.ts`: | Original GET fixture | Native test | | --- | --- | | get-200-and-default | `accepts the standard ARM resource read template` | | get-200-only | `accepts an ARM GET with only a 200 response` | | get-202-with-location | `accepts an additional 202 response with a Location header` | | get-missing-200 | `warns when an ARM GET only returns an error response` | | get-extra-201 | `warns once when an ARM GET adds a 201 response` | | get-extra-response-code | `warns once when an ARM GET adds a 204 response` | | get-nested-missing-200 | `warns for a GET in a nested ARM namespace` | Additional combined regressions cover an unfiltered program exercising both policies without duplicate diagnostics, missing `200` together with multiple disallowed statuses, GET-only `202 + default`, optional `Location`, the outside-service general-policy boundary, and the removal of the standalone GET registration. Existing ARM template/customization and singleton/broad-range coverage is retained. ## Validation for this consolidation - Combined native rule and shared-response-helper suites: **56 passed across 3 files**. - ARM package build, including TypeSpec library lint: passed. - ARM documentation regeneration: passed, with existing unrelated missing-documentation warnings. Generated README, linter reference, and combined rule page were inspected. - Azure rulesets build, lint, and tests: passed (**6 tests**), including one disabled combined rule and no standalone GET registration. - Required repository `pnpm format` followed by `pnpm lint`: passed. Six unrelated Astro-only formatting changes were removed from the final diff. - Explicit formatting checks for the rule Markdown and ignored generated rule page, Chronus status, and diff hygiene: passed. The Chronus-generated duplicate was consolidated into the existing feature entry. - The first focused run exposed duplicate GET routes in a new outside-service test fixture; distinct routes fixed the fixture, and the complete focused rerun passed. These are local results, not a claim that fresh GitHub CI or review has completed. The full ARM suite, full monorepo build, website build, and corpus were not rerun for this consolidation. Historical results below do not represent new runs. <details> <summary>Historical Azure#5556 promotion notes before consolidation (superseded where noted above)</summary> ## Test-responsibility cleanup Applied the guidance merged in Azure#5572 from the [Azure#5507 review](Azure#5507 (comment)). Removed the manual suppression pipeline, unused-template case, and namespace-only permutations. Simplified body variants while retaining exact per-status counts and concrete endpoint targets. Kept the concrete inherited-interface regression against the rule's previously overbroad template guard; this is not an ordinary inheritance-only test. No production code changed. Current cleanup validation: `mise exec -- pnpm --filter @azure-tools/typespec-azure-resource-manager exec vitest run test\rules\use-standard-response-codes.test.ts` passed **29 tests**. Repository `pnpm format`, `pnpm lint`, and `git diff --check` passed. Earlier validation counts below describe historical revisions, not this cleanup; the full test suite was not rerun. ## Summary Promote `NoErrorCodeResponses` from the local `no-error-code-responses` rule to `@azure-tools/typespec-azure-resource-manager/use-standard-response-codes`. The rule is available but **disabled by default** in the central resource manager ruleset. No immediate enablement was requested. ## Original Swagger linter - linter code: [NoErrorCodeResponses](https://github.com/Azure/azure-openapi-validator/blob/main/packages/rulesets/src/spectral/functions/no-error-code-responses.ts) - linter doc: [openapi-authoring-automated-guidelines.md](https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/openapi-authoring-automated-guidelines.md) The original ARM rule: - Selects response keys using `$.paths.*.*.responses.*~`. - Defensively ignores empty or non-string input. - Allows exactly `200`, `201`, `202`, `204`, and `default`. - Reports every other response key, including redirects, other success codes, explicit error codes, and grouped status keys, at that key's JSON path. ## How the Swagger linter works The Spectral rule runs with `resolved: false` and compares each selected response key against its allowlist; response payload schemas do not affect the decision. Its selector omits `x-ms-paths`. AutoRest can also remove SDK-scoped endpoints before Swagger validation. Neither emitter selection nor the `x-ms-paths` omission is reproduced in this native rule. ## Source TypeSpec lintdiff rule - Validator: `NoErrorCodeResponses`; canonical slug: `no-error-code-responses`. - Local rule: `no-error-code-responses`. - Source: [original lintdiff implementation](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-no-error-code-responses/packages/typespec-lintdiff/src/rules/no-error-code-responses.ts). - Verified canonical repository/branch: `Azure/typespec-azure`, `feature/lintdiff-no-error-code-responses`, development PR Azure#5555. - Pinned reviewed/pushed commit: `e4f6aadbfd16ebc702a3fd2048bb0ae7a2edbb46`. - Source worktree: `C:\dev\worktrees\lintdiff-no-error-code-responses`, clean at promotion. The named source rule is assumed done for this promotion. It was not modified. Development GitHub review completed clean after two rounds at the pinned SHA. The reviewed wording identifies a **disallowed response status**, rather than incorrectly calling every rejected success/redirect status an error. ## Destination analysis The agent selected ARM under the default recommendation policy. The validator catalog and fixtures are ARM-only, and the exact allowed-status policy applies across ARM HTTP methods. It is not generated-SDK naming policy, so TCGC is not the owner. Core is a plausible shared-response-policy alternative, but its existing `no-error-status-codes` checks only numeric 4xx/5xx responses in `Azure.*` namespaces. Extending that data-plane rule to this stricter ARM allowlist would change another audience's contract. Existing ARM verb-specific response rules also enforce different constraints; none supplies this all-method allowlist. The native name `use-standard-response-codes` describes the preferred ARM set, including statuses outside 4xx/5xx. Imports are compiler/HTTP and a local ARM rule helper only; no reverse ARM-to-TCGC dependency or new package dependency is introduced. ## How the promoted TypeSpec linter works An `operation` listener calls `getHttpOperation` to inspect resolved response status metadata. The implementation preserves the reviewed allowlist, singleton/ranged status handling, warning severity, exact diagnostic wording, and one warning per concrete operation and disallowed response status. Following review, this replaces the initial `getAllHttpServices` traversal. Direct enablement checks operations without `@service`, in nested namespaces, and outside declared services. This service-independent scope is intentional and covered by regression tests; no provider namespace decorator is required either. Like ARM's `use-model-request-body`, direct enablement supplies the intended audience. Library diagnostic filtering remains the compiler linter context's responsibility; the redundant rule-level imported-library filtering test has been removed. Concrete aliases retain independent diagnostics and authored locations. Shared payload variants do not duplicate an HTTP response, but distinct offending statuses remain separate findings. `isTemplateDeclarationOrInstance` and the existing `isTemplatedInterfaceOperation` helper exclude generic operation sources and templated-interface declarations while retaining concrete inherited interface operations. The concrete-interface guard regression is retained because an earlier rule-owned `isTemplateInstance` guard incorrectly skipped it; generic suppression behavior is no longer re-tested through a manual compiler pipeline. No OpenAPI extensions, emitter/SDK metadata, reference-string inference, or unsafe compiler mutation participates in the decision. The rule does not construct historical API-version projections. ## Fixture-to-native test mapping The fixtures are source evidence; their snapshots are not copied. | Original lintdiff fixture | Native vitest case | Coverage note | | --- | --- | --- | | [explicit-error-codes](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-no-error-code-responses/packages/typespec-lintdiff/test/fixtures/NoErrorCodeResponses/explicit-error-codes/main.tsp) | `it("rejects an explicit error response on a standard ARM action")` | Standard `ArmResourceActionSync` with named `Error = NotFoundResponse`; exactly one 404 finding. | | [default-error-response](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-no-error-code-responses/packages/typespec-lintdiff/test/fixtures/NoErrorCodeResponses/default-error-response/main.tsp) | `it("accepts a standard ARM action with the default error response")` | Standard action with omitted default error customization. | | [nested-provider-namespace](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-no-error-code-responses/packages/typespec-lintdiff/test/fixtures/NoErrorCodeResponses/nested-provider-namespace/main.tsp) | `it("rejects a redirect response in a nested provider namespace")` | Nested concrete action with 302 response. | | [status-code-range](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-no-error-code-responses/packages/typespec-lintdiff/test/fixtures/NoErrorCodeResponses/status-code-range/main.tsp) | `it("rejects a status range on a standard ARM action")` | Concrete action with 300–399 response range. | | N/A (source review regression) | `it("reports concrete endpoints, not the shared template instance")` | Exact diagnostic count and authored targets for two aliases, without namespace-traversal scaffolding. | | N/A (framework-only coverage removed) | Not ported (framework-only) | Suppression belongs to the compiler/linter framework. | | N/A (source native matrix) | `it("reports each disallowed response code once across body variants")` | Shared 404 payload variants and independent 500 response, without inheritance/spread scaffolding. | | N/A (source native matrix) | `it("checks concrete interface endpoints despite template exclusion guards")` | Retains the supported-input regression against an overbroad rule-owned template guard. | | N/A (framework-only coverage removed) | Not ported (framework-only) | Uninstantiated-template traversal belongs to the framework. | | N/A (promotion adaptation) | `it("checks ordinary namespaces without provider metadata")` | Direct enablement without redundant provider guard. | | N/A (framework-only coverage removed) | Not ported (framework-only) | Namespace traversal adds no distinct rule-owned predicate. | | N/A (review adaptation) | `it("checks operations without service metadata")` | Minimal operation without `@service`; no redundant namespace permutations. | | N/A (review adaptation) | `it("checks operations outside a declared service")` | An operation outside a declared service is checked independently. | | N/A (documentation validation) | `it("diagnoses the incorrect documentation example")` | Verbatim standard `ArmResourceRead` incorrect example compiles and warns. | | N/A (documentation validation) | `it("accepts the correct documentation example")` | Verbatim compliant example compiles without this warning. | The remaining native cases cover the literal allowlist, rejected literals, multiple range families, singleton ranges, standard default errors, and `ArmResourceRead` response customization. ## Migration evidence See the source [migration evidence](https://github.com/Azure/typespec-azure/blob/e4f6aadbfd16ebc702a3fd2048bb0ae7a2edbb46/packages/typespec-lintdiff/test/fixtures/NoErrorCodeResponses/migration.md) for source fixtures, real-service overlap, count-gap explanations, six compile exclusions, and projection limitations. Development review's wording fix reran all required checks and the full corpus with unchanged semantic counts; it did not rewrite the earlier run's explicitly timestamped evidence. The review follow-up here does not claim a new full-corpus run. ## Validation Review follow-up (`cbde67a32`): - Focused native suite: **34 passed**, including concrete aliases, inherited interfaces, suppression targets, service-independent operations, and both compiled documentation examples. Command: `pnpm --filter @azure-tools/typespec-azure-resource-manager test test/rules/use-standard-response-codes.test.ts --hookTimeout 60000 --maxWorkers 1`. - ARM package build and TypeSpec library lint: passed. - Required repository `pnpm format` followed by `pnpm lint`: passed. - ARM documentation regeneration: passed with existing unrelated missing-documentation warnings. - `pnpm change status` and `git diff --check`: passed. - The full ARM and rulesets suites below are historical promotion results, not reruns of this follow-up. Initial promotion validation: - Missing ARM/compiler/formatter outputs built once using the prepared dependency installation; no dependency changes or reinstall. - Focused native suite: **31 passed**, including both compiled docs examples. - ARM package build and package lint: passed. - ARM docs regeneration: passed; generated README, linter index, rule page, and manually maintained ARM matrix inspected. - Explicit formatting/checks: passed, using `--ignore-path NUL` for the generated Markdown scope. Ignored generated rule pages are not force-added. - Rulesets build, package lint, and tests: passed (**6 tests**), including explicit disabled ARM registration and no data-plane registration. - Full ARM package suite: **505 passed across 67 files** with checked-in test settings and no timeout/concurrency overrides. - Chronus status: passed; separate ARM `feature` and rulesets `internal` entries. ## Validation limitations and recovered attempts For the review follow-up, three new service-scope regressions failed against the old traversal as expected. The first operation-listener implementation used `isTemplateInstance`, which also excluded a concrete inherited interface operation. The existing regression caught this; using `isTemplateDeclarationOrInstance`, as HTTP operation enumeration does, restored that endpoint. All 34 focused cases then passed. The 60-second hook timeout and single worker were used for local library setup; no checked-in timeout or concurrency settings were changed. During initial promotion, formatting could not load the unbuilt workspace Prettier plugin; building the missing outputs resolved it. The first focused run passed 27 cases and exposed four test-scaffolding errors (missing service metadata and injected import/using conflicts). Only the promotion tests were corrected; all 31 then passed independently and in the full package run. The initial Chronus add commands were successful no-ops on the uncommitted branch; explicit LF change entries were added without staging, then validated by Chronus. Docs regeneration reports documentation-missing warnings for existing library diagnostics and unrelated rules, not the new rule. Repository-wide `validate:pr` and the website build were not run; the dedicated CI Website job remains authoritative for Astro validation. ## Promotion sync policy The pinned lintdiff source remains immutable during promotion. A confirmed source-semantic defect blocks this PR and returns evidence to the owning queue for its bounded, authorized return-to-development workflow. Refresh promotion only after the repaired source is pushed and reviewed clean; never repair source semantics solely in this official copy. The operation-listener change above is an explicitly requested destination review adaptation; the pinned source was not changed. </details> ## Review follow-up (2026-10-16) Exported a shared `CompilerTester` with `libraries: []`, without ARM imports/usings, and retained typed First/Second extraction and response ambiguity controls. Completed the file-body fixture's required HTTP metadata fields without changing its model identity. Current helper/general/absorbed-GET suites: **56 passed**. Scoped helper-source typecheck and repository format/lint passed. The full test-project typecheck failed in other existing tests/configuration; that broader command is not reported as passing. ## Current-main compatibility (2026-10-16, 3df7c4a) Merged main `88d691bb5` normally in [`3df7c4aaa`](Azure@3df7c4a). The ruleset-test resolution retains both the response-status opt-in/no-standalone-GET assertions and main's redundant-summary assertion; rule IDs and enablement are unchanged. After merging: dependency-closure build, **56 focused ARM tests and 7 ruleset tests**, repository format/lint and uncached formatting checks passed. Scoped production/helper/tester typecheck passed. Full ARM test-project tsc still fails on pre-existing test assertions/type-only imports and Vitest configuration; that broader command is not reported as passing. Fresh required CI checks at this head are pending; the historical checks above do not establish current-head readiness. ## Review cleanup (October 19, 2026) Moved GET coverage into `use-standard-response-codes.test.ts` with scoped setup and deleted the separate GET file. Differing service boundaries, status/range controls and one-warning GET deduplication remain. Removed redundant ARM internal entries; combined feature and separate rulesets coverage remain. Published in Azure@1064192. Fresh focused validation: **51 tests passed**, package production typecheck, root `pnpm format`, root `pnpm lint`, and diff hygiene passed. The full test TypeScript project retains pre-existing diagnostics (38 before, 38 after); the exact-head baseline comparison found no new diagnostics, accounting explicitly for moved tests and any removed duplicate fixture. This is not a claim that the full test TypeScript project is error-free or that pending CI has passed. These current file-layout/coverage statements supersede earlier descriptions of the removed test files; historical validation remains historical. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use authored property names and native enum-member events; consolidate rule helpers, tests, guidance, and feature release notes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…+ DeleteInOperationName -> use-correct-verb (#5673) ## Summary Consolidate **PutInOperationName**, **PatchInOperationName**, and **DeleteInOperationName** into **`@azure-tools/typespec-client-generator-core/use-correct-verb`**, as agreed in [the review discussion](#5673 (comment)). This PR absorbs the PATCH promotion #5665 and replaces the already-merged PUT rule from #5457. Both open promotion histories are preserved by consolidation merge commit [`d12175650`](d12175650). Following the combined-migration presentation in #5663, #5556, #5294, and #5423, further review of these three SDK naming checks belongs here. Superseding #5665 does not imply CI or review approval; its branch and review history remain intact. ## Combined behavior and legacy-rule mapping | Legacy Swagger validator | Previous native rule/promotion | Combined requirement | | --- | --- | --- | | [PutInOperationName](https://github.com/Azure/azure-openapi-validator/blob/main/docs/put-in-operation-name.md) | `use-create-for-put`, merged #5457 | PUT common SDK names start with `create`. | | [PatchInOperationName](https://github.com/Azure/azure-openapi-validator/blob/main/docs/patch-in-operation-name.md) | Proposed `use-update-for-patch`, #5665 | PATCH common SDK names start with `update`. | | [DeleteInOperationName](https://github.com/Azure/azure-openapi-validator/blob/main/docs/delete-in-operation-name.md) | Proposed `use-delete-for-delete`, this PR | DELETE common SDK names start with `delete`. | - A native operation listener resolves HTTP metadata once per eligible concrete endpoint, using one common SDK-name resolver. Nested services produce one warning per offending operation. Explicit-service ancestors and the nonrecursive global fallback are preserved. Prefix matching remains case-insensitive; GET, POST, and HEAD are not checked. - `getLibraryName(..., AllScopes)` honors unscoped `@clientName`, exact names, and the supported friendly-name fallback. Language-specific overrides and explicit OpenAPI operation IDs do not replace the common name. - Diagnostics target original operations. Both ARM and data-plane service endpoints are supported without requiring provider metadata. Client-only declarations outside HTTP services are not checked. - The rule does not mutate namespaces, select emitters, project API versions, reconstruct historical renamed names, or import AutoRest. - The single `client-sdk` registration is explicitly **disabled by default**. Data-plane/resource-manager rulesets and C# defaults are unchanged. - Separate rule implementations, their single-consumer traversal helper, and duplicate per-verb documentation/test files are replaced by one canonical rule, documentation page, and parameterized behavior suite. The unnecessary package-level AutoRest import isolation test has been removed; the canonical rule suite owns behavior coverage. ## Compatibility and migration **`use-create-for-put` is replaced, not retained as a second public alias.** Update linter configuration and `#suppress` diagnostic codes to `use-correct-verb`. Anyone who had explicitly enabled PUT naming guidance will now opt into PATCH and DELETE checks as well. The proposed PATCH/DELETE rule names are likewise replaced. Renaming an already-shipped SDK method can be breaking. The diagnostic and documentation retain this warning; approved compatibility exceptions can use operation-level suppression with a justification. This change does not itself rename generated methods. The package has one feature Chronus entry describing the consolidated behavior and configuration migration, plus a separate rulesets registration entry. Generated linter references, the ARM migration table, and TCGC documentation knowledge use the combined name. ## Consolidation and immutable source provenance | Check | Recorded lintdiff source | Native promotion incorporated | | --- | --- | --- | | PUT | #5455, `feature/lintdiff-put-in-operation-name`, recorded source `99cead8b6584f5cc1b0db3bf7810b53aa0eb221b` | Merged #5457; implementation already present on main. | | PATCH | #5664, source [`e009a4f53cc01eaa5c6a9ce85654fb1c4a8a6f4f`](e009a4f) | #5665 at [`8710828adc010571458235df8ff50e4019ce3107`](8710828). | | DELETE | #5670, source [`a0b5e8d9a9edee978219c2490c72eac7cc79845b`](a0b5e8d) | This PR at [`5fc361c18b79cfdf8460565fc7e3123669cc394d`](5fc361c), before consolidation. | The lintdiff sources, fixtures, snapshots, and migration evidence are not changed. The PUT source SHA is recorded historical provenance; its GitHub availability is not newly claimed. The approved native SDK-name adaptation remains intentional: a valid unscoped SDK override can fix an invalid authored name, or invalidate an otherwise compliant authored name. Serialized `operationId` underscore/group-prefix/missing-ID exemptions are not reproduced. Historical source corpus counts are not fresh combined-rule results, and exact Swagger diagnostic/count parity is not claimed. The naming policy remains in TCGC rather than introducing SDK dependencies into Azure Core or ARM. ## Regression coverage The canonical suite keeps valid/invalid mappings for all three verbs and exercises shared casing/prefix boundaries, common/scoped/exact/friendly-name precedence, explicit OpenAPI IDs, authored targets, group/underscore contrasts, multiple endpoints, and service selection once. The redundant naming/verb Cartesian product and response-status matrix have been removed. Standard ARM create/patch/delete aliases remain literal fixtures, including both common-name override directions. A mixed-verb regression requires three findings together while allowing GET/POST/HEAD. Nested explicit services require exactly one warning on the original operation; scope controls cover explicit-service descendants, excluded external declarations, nonrecursive global operations/direct interfaces, and concrete aliases versus templates. Public package registration/C# defaults have a separate registration suite; central ruleset coverage retains disabled-by-default activation and legacy-ID absence. The bespoke Markdown reader/fence parser and raw suppression test are deleted, not relocated. Documentation uses short standard-template before/after snippets and one focused common-name fix, without a setup appendix. All four actual final snippets were compiled once in an explicit public ARM context with the rule enabled: incorrect **3 warnings**, correct/common-name/suppression **0 diagnostics**. This receipt is one-time evidence, not continuous documentation coverage. Released-ID migration and SDK compatibility guidance remain intact. ## Validation (selected review fixes, October 21, 2026) - `mise exec -- pnpm --filter @azure-tools/typespec-client-generator-core build`: passed, including production TypeScript and TypeSpec library lint. - `mise exec -- pnpm --filter @azure-tools/typespec-client-generator-core test test/rules/use-correct-verb.test.ts test/linter-registration.test.ts --maxWorkers=1`: **30 tests passed in 2 files**, unchanged test/hook timeouts. - `mise exec -- pnpm --filter @azure-tools/typespec-azure-rulesets test test/validate-rules-defined.test.ts --maxWorkers=1`: **6 tests passed**. - TCGC docs regeneration, explicit native/generated Markdown formatting checks, root `pnpm format` and `pnpm lint`, Chronus status, and diff hygiene passed. - Docs regeneration retains existing missing-diagnostic-documentation and unused-parameter warnings on untouched APIs. - Initial focused-test collection found the admitted head's new examples dependency missing. Frozen-lockfile restoration linked JS dependencies but exited nonzero in unrelated Python preparation (pythonhosted TLS handshake failure); the dependency closure was then built sequentially. An initial target annotation error was corrected and the final target build passed. The install command is not reported as passing. These are local results for the selected findings, not a claim that fresh GitHub CI or review is complete. No new full-package/full-monorepo tests, website build, full test-project typecheck, or lintdiff corpus comparison is claimed. New human documentation feedback outside the selected findings remains for separate review; this update does not claim whole-PR readiness. <details> <summary>Original DELETE promotion description before consolidation (historical)</summary> ## Original Swagger linter - linter code: [DeleteInOperationName](https://github.com/Azure/azure-openapi-validator/blob/main/packages/rulesets/src/spectral/functions/delete-in-operation-name.ts) - linter doc: [delete-in-operation-name.md](https://github.com/Azure/azure-openapi-validator/blob/main/docs/delete-in-operation-name.md) Checks: - [x] Inspect DELETE operations selected from `paths` and `x-ms-paths`. - [x] Read `operationId`, ignoring absent, empty or non-string values. - [x] Ignore IDs without an underscore. - [x] Accept the case-sensitive `Delete` prefix before the underscore or after a word-character group prefix. - [x] Report a warning on the offending `operationId` when neither accepted form matches. ## How the Swagger linter works The Spectral function checks selected DELETE operation objects and applies serialized-ID regular expressions. It does not inspect SDK metadata or require ARM provider metadata. Its underscore/group-prefix exemptions can accept a Delete-prefixed group with a non-delete method, or ignore a standalone invalid method ID. Those serialization-dependent exemptions are intentionally not copied. Native endpoint selection also avoids warnings on declarations used only for client customization. The source migration evidence documents emitted-name overrides, historical version selection, one-sided projects and compilation failures rather than treating equal totals as proof of equivalence. ## Source TypeSpec lintdiff rule - Validator ID: `DeleteInOperationName`; canonical slug: `delete-in-operation-name`. - Local rule: `tsp-lintdiff-local-linter/delete-in-operation-name`. - [Original lintdiff source rule](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/src/rules/delete-in-operation-name.ts). - Canonical source repository: `Azure/typespec-azure`; source branch: `feature/lintdiff-delete-in-operation-name`. - Source draft PR: #5670. - Immutable source commit: `a0b5e8d9a9edee978219c2490c72eac7cc79845b`; development GitHub review clean after two Balanced rounds on this final pushed commit. - Source worktree: `C:\dev\worktrees\lintdiff-delete-in-operation-name`; clean, no uncommitted source changes. The source was assumed done for this promotion run and was not modified. It checks authored names on concrete HTTP service endpoints, DELETE only, with a case-insensitive `delete` prefix and warning targets on the original operation. Initial source commit `8af7a71258c41b8ca10950023c62cc4633bb24fc` is historical; the reviewed source above additionally fixes the harness's stale coverage-kind assertion without changing production behavior. ## Destination analysis The agent selected `@azure-tools/typespec-client-generator-core` because the approved naming-family promotion contract governs common SDK method names. Existing `use-create-for-put` and supported `getLibraryName(..., AllScopes)` establish that ownership and implementation pattern. The new public name is `use-delete-for-delete`. `Both` applicability and compiler/HTTP-only authored-name semantics make Azure Core a plausible authored-domain alternative. Core's `use-standard-names` already checks authored DELETE names in data-plane rulesets, but not the common SDK override domain. An SDK resolver must not introduce TCGC dependencies into Core or ARM. ARM fixture origin alone does not establish ARM-only semantics; this rule requires neither provider metadata nor ARM helpers in production. Existing TCGC `get-operation-name` is GET-specific and is not equivalent DELETE coverage. ## How the promoted TypeSpec linter works The root listener uses `getAllHttpServices` and checks concrete service operations with verb `delete`. A TCGC context created with `{ mutateNamespace: false }` resolves the common SDK name using `getLibraryName(context, operation, AllScopes)`. This honors unscoped client names, exact-name normalization and supported friendly-name fallback, independently of positive language-specific overrides and explicit OpenAPI operation IDs. No emitter or API-version projection is selected, and historical renamed names are not reconstructed. The prefix remains case-insensitive, severity remains warning and each finding targets the original operation, including when an override supplies the displayed SDK name. Group names do not mask an invalid method name. The authorized adaptation changes only the authored-name domain to common SDK names; source selection, DELETE condition, prefix policy and diagnostic targets remain intact. There is no hand-rolled resolver, serialized-reference inference, namespace mutation or emitter coupling. The `client-sdk` ruleset registers the new rule with plain `false`; ARM/data-plane rulesets and C# default rules are unchanged. Separate Chronus entries classify destination availability as `feature` and disabled-only ruleset registration as `internal`. ## Fixture-to-native test mapping | Original lintdiff fixture | Native vitest case | Coverage note | | --- | --- | --- | | [compliant-with-template](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/compliant-with-template/main.tsp) | `it("reports a customized standard ARM delete endpoint")` | Actual source fixture contains a compliant delete and a violating remove despite its directory name. Replace legacy custom action/suppression with supported ArmResourceDeleteSync aliases; preserve prefix contrasts. | | [compliant-with-template](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/compliant-with-template/main.tsp) | `it("accepts a standard ARM delete endpoint")` | Actual source fixture contains a compliant delete and a violating remove despite its directory name. Replace legacy custom action/suppression with supported ArmResourceDeleteSync aliases; preserve prefix contrasts. | | [remove-endpoint](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/remove-endpoint/main.tsp) | `it("reports an invalid HTTP SDK name")` | Concrete remove endpoint warns; reduced generic HTTP operation preserves the method-name predicate. | | [delete-endpoint](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/delete-endpoint/main.tsp) | `it("accepts a valid HTTP SDK name")` | Concrete delete-prefixed endpoint complies. | | [common-name-fixes-id](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/common-name-fixes-id/main.tsp) | `it("accepts a common SDK name override")` | Authorized domain adaptation: unscoped valid SDK name fixes authored remove; source authored rule intentionally still warns. | | [common-name-invalidates-id](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/common-name-invalidates-id/main.tsp) | `it("reports an invalid unscoped client name on the authored operation")` | Authorized domain adaptation: invalid common override warns despite authored deleteWidget; target remains original declaration. | | [ungrouped-remove](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/ungrouped-remove/main.tsp) | `it("reports an invalid HTTP SDK name")` | Same minimal standalone endpoint proves no underscore/group exemption; shared native case is deliberate. | | [delete-group-remove](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/delete-group-remove/main.tsp) | `it("does not let a Delete-prefixed group mask an invalid method name")` | Group prefix cannot hide invalid common method name. | | N/A (documentation regression) | `it("reports the published incorrect template alias")` | Compile actual Markdown setup and incorrect ArmResourceDeleteSync alias; require one warning on remove. | | N/A (documentation regression) | `it("accepts the published correct template alias")` | Compile actual Markdown correct alias with no target-rule diagnostics. | | N/A (documentation regression) | `it("accepts the published template client name override")` | Compile actual Markdown common-name override with no target-rule diagnostics. | | N/A (documentation regression) | `it("accepts the published template suppression")` | Compile actual Markdown suppression with the rule explicitly enabled through the compiler linter; require no diagnostics. | Additional native cases prove SDK override directions, language-specific and mixed names, exact names, friendly fallback, explicit-ID independence, original targets, multiple endpoint findings, non-DELETE controls, case-insensitive prefixes, source endpoint-selection regression, and a common client name on a supported standard ARM deletion alias. Framework-only import exclusion, ordinary inheritance/traversal, uninstantiated-template and suppression tests were not mechanically copied. No validator snapshots, fixtures or corpus payloads are included. ## Migration evidence See [source migration.md](https://github.com/Azure/typespec-azure/blob/feature/lintdiff-delete-in-operation-name/packages/typespec-lintdiff/test/fixtures/DeleteInOperationName/migration.md) for focused tests, representative services, full-corpus counts, one-sided project explanations, retained compile failures and uncertainty. Those counts describe the immutable authored-name source, not fresh SDK-domain equivalence or predicted impact. Promotion did not rerun the lintdiff harness or corpus. ## Validation - Required target dependency-closure build, TCGC package build/lint: exit 0. - Final emitter-free native rule suite: 28/28, including four tests that read and compile the actual published Markdown examples; target rule also passes all 28 cases in the full package run. - Required TCGC docs regeneration and explicit formatting/check of all four native/generated Markdown paths with `NUL` ignore override: exit 0; generated ignored rule page remains unstaged. - Following template-first skill update #5678 (created before this documentation update), the incorrect/correct, common-name override, and suppression examples use `ArmResourceDeleteSync<Widget>` instead of raw routes. The standalone shared setup includes all imports/usings. Incorrect `remove` produces exactly the target warning; correct `delete`, common-name override, and compiler-enabled suppression produce none. Existing generic HTTP behavior tests remain intact. - Rulesets build/lint: exit 0; discovery tests: 5/5. - Separate feature/internal Chronus entries verified LF; plain `pnpm chronus status`: exit 0. Before commit, both `change add` attempts reported no changed package, so the documented fallback created only these two entries. - Full TCGC initial required run: 1660 passed, one 10000ms per-test timeout in `arm client with sub clients`, two existing skips; 1663 tests in 102 files, natural exit 1. - Single coordinator-granted diagnostic, exactly `mise exec -- pnpm --filter @azure-tools/typespec-client-generator-core test --maxWorkers=1`: 1661 passed, zero failures, same two skips and 1663 tests/102 files, natural exit 0. Same code, assertions, dependencies and 10000ms test/hook limits; no pool change. Timeout recovered with UNPROVEN original cause. The task-wide diagnostic allowance is exhausted (1/1); the scoped concurrency profile may persist for matching later package validation without granting another retry. - Latest full package run after template documentation/test changes: 1665 passed, two unchanged existing skips, 1667 total tests in 102 files, exit 0 using the same `--maxWorkers=1` profile and unchanged test/hook timeouts. The four added tests account for the population increase. - Scoped TypeScript formatting/check and diff hygiene pass. The initial nine-file promotion patch previously passed read-only cached application check with an empty index; subsequent review/doc updates are scoped to the maintained rule documentation and native/rulesets tests. ## Validation limitations Required docs regeneration exits 0 with existing documentation warnings for `unnecessary-emitter-option` and unused API `@param` tags on untouched APIs; these unrelated warnings were retained, not repaired. The optional repository-wide wrapper was not warranted for this isolated rule and registration change. No local website/dependency-closure build was run; CI's Website job remains authoritative. The original full-suite timeout remains retained evidence, not an environmental diagnosis. ## Promotion sync policy The source remains immutable during promotion. A verified source-semantic gap blocks this PR and returns evidence to the owning queue, which alone may authorize its bounded source-repair cycle. Refresh native provenance incrementally only after a new clean reviewed source head; do not repair the source or silently alter its semantics in the promoted copy. The expressly approved common SDK naming adaptation is not source repair. ## Review cleanup (October 19, 2026) Renamed the groups to `HTTP SDK names` and `standard ARM operations`, preserving their separate SimpleTester and ARM/standalone setup. Removed the extra DELETE behavior file; focused package-level public-entry coverage retains the throwing AutoRest guard, now outside the PUT behavior suite as well. Removed redundant internal entries; feature and rulesets coverage remain. Published in 50c4b74. Fresh focused validation: **68 tests passed**, package production typecheck, root `pnpm format`, root `pnpm lint`, and diff hygiene passed. The full test TypeScript project retains pre-existing diagnostics (15 before, 15 after); the exact-head baseline comparison found no new diagnostics, accounting explicitly for moved tests and any removed duplicate fixture. This is not a claim that the full test TypeScript project is error-free or that pending CI has passed. These current file-layout/coverage statements supersede earlier descriptions of the removed test files; historical validation remains historical. </details> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Mark Cowlishaw <1054056+markcowl@users.noreply.github.com> Copilot-Session: 8bf3420c-d859-4ac1-81e8-2081d1fded84 Copilot-Session: 832854b7-de67-4f1f-8fe1-6a3e9897aeee
Keep the single-use messages alongside the rule definition without changing behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Summary
Consolidate DescriptionMustNotBeNodeName and DescriptiveDescriptionRequired into the existing
@azure-tools/typespec-azure-core/documentation-requiredrule. This absorbs #5674 without introducing separate public rule IDs.The October 22 review fixes simplify the implementation, tests, and published guidance:
require-docs.ts; remove the two helper modules.enumMembercallback, preserving existing missing-doc exemptions and explicit-documentation checks on exempt members.Behavior and legacy mapping
description, ignoring surrounding whitespace, periods, and case. Operations compare both authored names and effective HTTP verbs.HTTP path/query/header properties use wire names; multipart properties use authored part names with source-name fallback, including repeated parts. Enum members and named union variants use names; unnamed string variants use values. Status-code properties are exempt from name comparisons, not blank checks. Namespaces, interfaces, scalars, inline properties, and otherwise exempt declarations receive explicit-documentation checks without newly requiring absent documentation. Uninstantiated template docs unavailable from the compiler remain outside coverage.
Native-policy adaptation: ordinary properties deliberately use authored names even when
@encodedName("application/json", ...)differs. This replaces the previous JSON-name comparison following review; it is not exact Swagger emitted-name parity. One renamed-property regression proves the new source-name behavior; the prior JSON-precedence matrix is removed. The immutable lintdiff sources below are unchanged.The already-enabled data-plane and resource-manager rulesets intentionally acquire stronger documentation-quality checks. This is not a disabled-by-default opt-in migration. Compiler metadata and emitted Swagger occurrences differ; exact diagnostic population parity and the legacy truthy selector's empty-string discrepancy are not reproduced.
Consolidation and provenance
3753a319105eb1be8's sample CI fix.72ab22398a3c53e2c.Merge
e4e52dac5preserves both histories. #5674 is superseded; its branch/history and review threads are retained. The existing PR title follows the combined-migration convention used in #5294 and #5423.Current local validation (October 22, 2026)
mise exec -- pnpm -r --filter "@azure-tools/typespec-azure-rulesets..." build: passed, including Azure Core, ARM, and their dependency closure.mise exec -- pnpm test --pool=threads --maxWorkers=1 --project=@azure-tools/typespec-azure-core --project=@azure-tools/typespec-azure-rulesets: 60 files passed, 522 tests passed, 2 existing skips. The canonical documentation suite has 32 cases.test/rules/require-docs.test.tsusingtsc --noEmit --ignoreConfig --module NodeNext --moduleResolution NodeNext --target esnext --strict --skipLibCheck --verbatimModuleSyntax true --types node: passed. The earlier undefined-location errors in this suite are eliminated. Full package test-project type-check is not clean: unchanged casing-style/type-only-import tests and Vitest project boundaries still report errors.pnpm formatandpnpm lint, scoped CSpell,chronus status, andgit diff --check: passed.createTesterwithlibraries: [], no imports/usings/wrapper, and the real linter: incorrect snippets produce 2 / 2 / 1 diagnostics; correct snippet produces zero. Document SHA-256:e6405852d08b4128d365c582840dbbd3e923b31200e25539ba91969e488534d6. This is a one-time receipt, not continuous Markdown coverage; no per-rule Markdown parser was added.These are local results, not a claim of GitHub CI success or merge approval. Final-head CI is inspected separately without waiting for completion. Historical validation counts and behavior claims below are superseded by this section.
Historical PR description and earlier validation (superseded above)
Focused test cleanup (October 21, 2026)
b5ade3c56 removes one redundant unfiltered documentation-rule case while retaining the broader mixed missing/blank/punctuation/name/verb regression. No production behavior, public documentation, or enablement changed, and existing release-note coverage is unchanged.
The canonical suite passed 79/79 cases using the existing threads/single-worker profile with unchanged timeouts. An initial run hit a 10-second setup-hook timeout; its cause is unproven. The package/dependency-closure build and root formatting/lint passed. Focused strict TypeScript checking has the same nine pre-existing undefined-location errors as the exact-head baseline, shifted only by the deleted test; no new errors were introduced. The full test project is not claimed error-free. Earlier counts below are historical.
Summary
Consolidate DescriptionMustNotBeNodeName and DescriptiveDescriptionRequired into the existing
@azure-tools/typespec-azure-core/documentation-requiredrule. This PR absorbs #5674, including its implementation and regression coverage, rather than adding separateno-uninformative-docorno-empty-docpublic rules.Implemented in merge commit
e4e52dac5, preserving the histories of both promotion branches.This follows the combined-migration presentation used in #5294 and #5423. Further review of both documentation-quality checks should continue here. Closing #5674 as superseded does not imply CI or review approval; its branch and review history are retained, and existing review threads are not resolved by this consolidation.
Combined behavior and legacy-rule mapping
description, ignoring surrounding whitespace, periods, and case. Operations are checked against both source names and effective HTTP verbs.documentation-required. This intentionally strengthens documentation quality for existing users; it is not a disabled-by-default opt-in migration.The native contract uses compiler documentation metadata, not emitted Swagger summary/description occurrences. The legacy truthy selector's empty-string discrepancy is intentionally not reproduced, and exact emitted diagnostic parity is not claimed.
Consolidation and provenance
3753a319105eb1be8, including the sample CI fix.72ab22398a3c53e2c.The source lintdiff implementations are unchanged. Explicit-documentation checks run directly from the existing native semantic callbacks; one quality-check helper preserves the broader authored-documentation scope and the missing-doc exemptions without listener remapping or validator factories. The combined tests now expect name-only and placeholder descriptions to produce
uninformative, notempty, findings; an unfiltered regression verifies the combined rule reports blank and uninformative documentation together.The user-facing rule documentation has one combined legacy-coverage table, and Azure Core has one consolidated fix changelog entry. Widget Manager colors and the ARM library provisioning state have meaningful descriptions, with their OpenAPI snapshots regenerated.
Validation recorded at consolidation (historical)
These are local results, not a claim that fresh GitHub CI or review has completed. The separate REST API Specs integration check has adoption failures from strengthening an already-enabled rule; no downstream specification changes or diagnostic suppressions are included here.
Historical #5663 promotion notes before consolidation (superseded where noted above)
Current implementation after review
Uninformative-documentation checks are consolidated into existing
documentation-required. Operations are compared against both their source name and HTTP verb; emitted-name, multipart and member behavior is retained. Existing missing-doc exemptions remain unchanged. The separate opt-in rule is removed: this intentionally strengthens documentation quality. Compatibility with the matching #5674 consolidation was verified.Implemented in 5e8dc5927. Review threads are left unresolved.
Historical promotion notes and earlier validation (superseded where noted above)
Summary
Promote Swagger
DescriptionMustNotBeNodeNameto@azure-tools/typespec-azure-core/no-uninformative-doc.The rule warns when explicit documentation repeats the name it describes or uses the placeholder
description. It is registered in Azure Core and explicitly disabled by default in both the data-plane and resource-manager rulesets, so existing specs do not acquire new warnings automatically.This PR includes the native rule, regression tests, documentation, generated linter references, ruleset registration, and separate Chronus entries for the Core feature and internal ruleset registration.
Original Swagger linter
The legacy rule recursively examines objects containing string descriptions. It trims whitespace, removes periods, and compares case-insensitively against an explicit name or the object's path key; it also rejects the placeholder
description. Its executable registration is ARM.Source TypeSpec lintdiff rule
description-must-not-be-node-name3753a319f32f3926005536029f9ff32b94f3c905The promoted
create(context)implementation and normalization function are identical to the reviewed source. Promotion changes the public rule/export name, documentation metadata, package registration, native testing setup, and rulesets. The source was not modified during promotion.Destination analysis
Azure Core owns this shared documentation-quality concern: the implementation uses compiler and HTTP APIs, without ARM resource metadata or SDK-name overrides. Core's existing
documentation-requiredrule checks missing documentation, not repetitive or placeholder text. ARM would unnecessarily restrict the native rule's audience; TCGC is not appropriate because this is not an SDK-naming policy.How the promoted TypeSpec linter works
@doctext and doc comments through compilergetDoc; normalizes surrounding whitespace, periods, and case.HttpPartnames, including repeated-part arrays. Unnamed parts use the source property name; JSON encoding does not rename multipart parts.For example,
@doc("Ready") Ready: "available"warns, while@doc("available") Ready: "available"does not.The production rule does not emit or read Swagger, predict generated schema names, project API versions, or add emitter, OpenAPI, ARM, or TCGC dependencies.
Fixture-to-native test mapping
All 15 source fixtures are represented in the 40 native test cases. Source fixtures are pinned under DescriptionMustNotBeNodeName; native tests exercise the compiler/HTTP predicates directly rather than copying snapshots or ARM template scaffolding.
description-matches-namedescriptive-docsliteral-descriptionmodel-description-matches-model-nameoperation-description-matches-http-verboperation-description-matches-typespec-name-onlyparameter-description-matches-emitted-name,parameter-description-matches-source-name-onlyproperty-description-matches-property-namemember-and-key-documentationinferred-http-verbsjson-encoded-property,json-encoded-source-name-onlymultipart-authored-names,multipart-source-name-onlyAdditional native coverage includes scalar/enum/union names, doc comments, query/header metadata, unnamed variants, missing/empty docs, status-code exemption, exact diagnostic locations, multipart JSON-name isolation, and both published documentation examples.
Migration evidence
The source validation passed 47 native tests, 15 strict fixtures, and five affected fixture groups. The final full corpus run attempted 468 projects, successfully compiled 462, and retained six documented compilation exclusions. The final target-rule diagnostic multiset was unchanged at 8,499; generated corpus outputs were restored and are not included in this PR.
The pinned migration note records project overlap, selected-version attribution, one-sided findings, outliers, and limitations. Swagger counts emitted occurrences; TypeSpec diagnostics describe authored semantic declarations and instances. Repeated roots/versions, inline copies, qualified schema names, compatibility or unused types, version selection, and constant route parameters explain population differences.
The native documentation-quality contract is covered. Corpus overlap and counts are not proof of arbitrary executable Swagger parity.
Validation and review
Required promotion checks passed:
The two existing skips are the unrelated
@exampleand@useRefgroups inno-openapi.test.ts. Documentation regeneration retains existing missing-diagnostic-documentation warnings outside this rule. No new tests are skipped. A website build/full-repository wrapper was not part of the targeted promotion checks.Fresh independent local reviews were clean. The final source review completed after 2 Balanced Copilot rounds at
3753a319; the final promotion review completed after 1 Balanced round at2c5214be. Independent refetches confirmed zero review-specific comments and zero unresolved Copilot threads on both final heads. The inferred-verb, JSON-name, and multipart findings are implemented and resolved.Promotion sync policy
Source semantics remain immutable during promotion. Any future confirmed source-semantic defect must be repaired and reviewed in lintdiff first, then promoted from that exact reviewed commit; it must not be fixed only in the official copy.
heavy
Historical #5674 promotion notes before consolidation (superseded where noted above)
Current implementation after review
Empty/whitespace explicit documentation is checked by existing
documentation-required, preserving its missing-doc exemptions. The separateno-empty-docrule/registration is removed and semantic coverage is transferred. This intentionally strengthens documentation quality. Compatibility with the matching #5663 consolidation was verified.Implemented in 20683492f. Review threads are left unresolved.
Historical promotion notes and earlier validation (superseded where noted above)
Original Swagger linter
The original rule:
trim()result is empty, at their selected JSON paths.How the Swagger linter works
Spectral recursively selects objects using
$..[?(@object() && @.description)].description, then applies the blank-string check. This truthy selector skips missing, empty-string, null, and numeric descriptions before the function runs; whitespace strings are selected and rejected. That executable discrepancy is not copied into the native rule. Emitted occurrences and semantic TypeSpec declarations are different diagnostic populations, so exact Swagger diagnostic parity is intentionally partial.Source TypeSpec lintdiff rule
DescriptiveDescriptionRequired.descriptive-description-required.descriptive-description-required.Azure/typespec-azure; source draft #5672.lintdiff-descriptive-description-required.72ab22394704fce1a1555c879ff7335da8debf42.C:\dev\worktrees\lintdiff-descriptive-description-required, clean at the pinned source head.The source rule was assumed done for this promotion invocation. It was not modified during promotion. The development review completed two rounds with no new comments at the pinned head. The earlier development publication commit was
2423e379680a19091a1b86ffd5c240daa3d4089e; the reviewed source includes subsequent test/evidence fixes without changing production rule behavior.Destination analysis
The agent selected
@azure-tools/typespec-azure-core: the rule uses compiler documentation metadata only, applies to both ARM and data-plane authoring, and needs neither ARM resource semantics nor SDK-name resolution. ARM would unnecessarily narrow a shared authoring requirement; TCGC is not appropriate because this is not a generated SDK API policy. No ARM or TCGC dependency is introduced.Core's existing
documentation-requiredaddresses missing documentation and tests documentation truthiness. It does not reject nonempty whitespace strings and has a different declaration scope. This rule complements that policy rather than duplicating it.How the promoted TypeSpec linter works
The official rule is
@azure-tools/typespec-azure-core/no-empty-doc.It reads
getDoc(program, target)on namespaces, interfaces, operations, models, model properties, scalars, enums, enum members, unions, and union variants. Undefined documentation is compliant; defined documentation with an empty trimmed value produces a warning targeted at the offending declaration. Any non-whitespace character is sufficient.The enum callback explicitly iterates
enum.membersbecause the compiler does not dispatch enum-member listener callbacks. Tests preserve exact property and enum-member diagnostic locations. Standard linter traversal owns library filtering; there is no custom HTTP reachability, provider, version/projection, emitted-reference, or OpenAPI-extension filter.The native rule's
create(context)implementation matches the immutable source after line-ending normalization. Promotion adapts only the official name/export, native documentation metadata, and documentation URL. The source's unprojected native authoring population remains unchanged.The rule is explicitly disabled (
false) in both data-plane and resource-manager rulesets. The Core feature change and disabled-only rulesets internal change have separate Chronus entries.Fixture-to-native test mapping
Snapshots and validator output are evidence only; they were not copied into the official package. Reduced compiler-native snippets exercise the same rule-owned branches.
it("rejects a whitespace-only property description")it("rejects a whitespace-only parameter description")it("accepts nonblank descriptions on models, properties, and operations")it("targets offending enum members rather than their containing enum")it("targets offending properties rather than their containing model")it("targets offending enum members rather than their containing enum")it("validates the documented incorrect and correct examples")The 24 native cases also cover registered declaration kinds, absent and nonblank documentation, empty and Unicode-whitespace content, documentation comments, and diagnostic targeting. Fresh asynchronous tester initialization follows the Core test lifecycle.
Migration evidence
See the checked-in migration.md for focused comparisons, representative and full real-service corpus evidence, one-sided project explanations, compile exclusions, and remaining uncertainty. Those counts describe the immutable source's comparison population, not a fresh official-library corpus run or universal executable equivalence.
Validation recorded at consolidation (historical)
All required targeted checks are complete:
Core prerequisite closure and affected package builds.
Focused native rule tests: 24 passed, including the actual authored documentation examples.
Core package lint.
Core documentation regeneration, inspection, and explicit formatting/checking of the README, authored rule, generated linter reference, and ignored generated rule page.
Maintained-file formatting check and
git diff --check.LF Chronus entries and
pnpm chronus status.Rulesets dependency closure/build, package lint, and five tests passed.
Full Core native suite: 58 files passed, 458 tests passed, two original skips, 460 total, using the approved package-scoped profile:
Independent persistent local precommit review of the exact ten-file scope: no significant issues.
Validation limitations
The original full Core run had two per-test 5,000ms timeouts (override-api-version and casing-style); the authorized single-worker diagnostic retained one casing-style timeout. Both failed receipts remain preserved. A coordinator-authorized bounded comparison ran the exact remaining casing test once against the baseline registration and once against the restored draft using threads/single-worker; both passed. The subsequent single authorized full suite passed with the identical 460-test population and the same two original skips.
The original timeout cause is unproven. This is not a claimed environmental diagnosis. Vitest 5,
isolate: false, the configured 5,000ms test and 10,000ms hook limits, assertions, dependencies, and skips were unchanged. The passing profile does not grant further retries. Baseline isolation restored the exact draft registration and all 404 archived compiled outputs.Repository-wide
validate:prand the website build were not run; the required targeted plan above is complete, and CI's dedicated Website job remains authoritative for the Astro build. The generated rule page was inspected/formatted but remains ignored and is not force-added.Promotion sync policy
The lintdiff source remains immutable during promotion. A confirmed source-semantic defect discovered in promotion review blocks this PR and returns evidence to the owning queue; only that coordinator may authorize a bounded return-to-development repair cycle. This PR would then be refreshed incrementally only after clean source review at a new exact pinned commit, with native regressions and provenance updated. Promotion itself never edits the source rule or silently changes its semantics.
Historical review cleanup (October 19, 2026; superseded below)
Consolidated empty/effective-name behavior into
require-docs.test.tsand deleted the two additional behavior files. Authored-documentation filtering stays local; combined/missing-documentation checks remain unfiltered. HTTP metadata spies are scoped to the naming-quality subgroup and restored after each case; diagnostic targets and call-count checks remain. Removed the redundant Core internal entry, retaining the fix and separate samples-package entries.Published in 57eb47e. Fresh focused validation: 86 tests passed, package production typecheck, root
pnpm format, rootpnpm lint, and diff hygiene passed. The full test TypeScript project retains pre-existing diagnostics (19 before, 19 after); the exact-head baseline comparison found no new diagnostics, accounting explicitly for moved tests and any removed duplicate fixture. This is not a claim that the full test TypeScript project is error-free or that pending CI has passed. These current file-layout/coverage statements supersede earlier descriptions of the removed test files; historical validation remains historical.Review cleanup (October 21, 2026)
Implemented selected review IDs 32–36 in 85ac77b. Removed the unreachable uninstantiated-template success case, HTTP resolver spy/call-list oracle, and test-only missing-diagnostic filters. All canonical fixtures now instantiate the shipped public rule with minimally documented scaffolding or assert the real missing-doc warning. Mixed missing/blank/name controls and exact authored targets remain.
Documentation-quality checks now run directly in native callbacks, with explicit enum-member iteration and a small effective-name resolver. Missing-doc exemptions, broader explicit-doc coverage, blank-diagnostic deduplication, public rule ID and existing enablement are unchanged. Added instantiated-model and explicit discriminator regressions. Reduced multipart checks to a normal named part and a focused repeated outer-array part; retained authored/source/JSON precedence, fallback and placeholder controls, and combined path/query/header source-name compliance with JSON-precedence cases.
Fresh local validation: 80 canonical rule tests passed; the complete Azure Core and Azure rulesets run passed 60 files, 568 tests, with 2 existing skips. Azure Core package build (including production TypeScript compilation and TypeSpec library lint), root
pnpm format, rootpnpm lint, Chronus status and diff hygiene passed. Reused existing production release coverage without adding internal test-history notes. An initial diagnostic-order expectation and an intermediate TypeScript narrowing error were corrected before the passing validation. No compiler timeout settings were changed.Available final-head CI was inspected once after publication: only
license/clahad reported (success); build/test CI was not yet reported. CI pending - not awaited. These local results do not claim fresh CI passed or whole-PR review/merge readiness. The earlier consolidation and promotion validation above remains historical. Existing review threads are untouched.