From 5558640ac16a4282ce21df27a91608e128af122e Mon Sep 17 00:00:00 2001 From: Yuchao Yan Date: Thu, 3 Sep 2026 13:33:41 +0800 Subject: [PATCH 1/3] Promote unsupported PATCH properties rule Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...ed-patch-properties-2026-09-03-13-20-00.md | 8 + .../typespec-azure-resource-manager/README.md | 1 + .../src/linter.ts | 2 + .../rules/no-unsupported-patch-properties.md | 46 ++++ .../rules/no-unsupported-patch-properties.ts | 227 ++++++++++++++++++ .../no-unsupported-patch-properties.test.ts | 210 ++++++++++++++++ .../src/rulesets/resource-manager.ts | 1 + .../reference/linter.md | 1 + 8 files changed, 496 insertions(+) create mode 100644 .chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md create mode 100644 packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.md create mode 100644 packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts create mode 100644 packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts diff --git a/.chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md b/.chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md new file mode 100644 index 0000000000..38f043762a --- /dev/null +++ b/.chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md @@ -0,0 +1,8 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-azure-resource-manager" + - "@azure-tools/typespec-azure-rulesets" +--- + +Add the `no-unsupported-patch-properties` ARM linter rule, which reports writable resource identity, location, and provisioning state properties in PATCH request bodies. Register the rule as disabled by default in the ARM ruleset. diff --git a/packages/typespec-azure-resource-manager/README.md b/packages/typespec-azure-resource-manager/README.md index 7db74ab137..1aac9ad1dd 100644 --- a/packages/typespec-azure-resource-manager/README.md +++ b/packages/typespec-azure-resource-manager/README.md @@ -75,6 +75,7 @@ Available ruleSets: | [`@azure-tools/typespec-azure-resource-manager/no-empty-model`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/no-empty-model) | ARM Properties with type:object that don't reference a model definition are not allowed. ARM doesn't allow generic type definitions as this leads to bad customer experience. | | [`@azure-tools/typespec-azure-resource-manager/no-reserved-resource-property`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/no-reserved-resource-property) | Reserved property names (for example 'billingData') must not be present in a resource's property bag. The property name is matched case-insensitively. | | [`@azure-tools/typespec-azure-resource-manager/no-query-in-point-op`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/no-query-in-point-op) | Point operations must not declare query parameters beyond api-version. | +| [`@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties`](https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/no-unsupported-patch-properties) | ARM PATCH request bodies must not contain writable resource identity, location, or provisioning state properties. | ## Decorators diff --git a/packages/typespec-azure-resource-manager/src/linter.ts b/packages/typespec-azure-resource-manager/src/linter.ts index 2e5ed94ff2..c5635c5383 100644 --- a/packages/typespec-azure-resource-manager/src/linter.ts +++ b/packages/typespec-azure-resource-manager/src/linter.ts @@ -34,6 +34,7 @@ import { noQueryInPointOpRule } from "./rules/no-query-in-point-op.js"; import { noReservedResourcePropertyRule } from "./rules/no-reserved-resource-property.js"; import { deleteOperationMissingRule } from "./rules/no-resource-delete-operation.js"; import { noResponseBodyRule } from "./rules/no-response-body.js"; +import { noUnsupportedPatchPropertiesRule } from "./rules/no-unsupported-patch-properties.js"; import { operationsInterfaceMissingRule } from "./rules/operations-interface-missing.js"; import { patchEnvelopePropertiesRules } from "./rules/patch-envelope-properties.js"; import { resourceNameRule } from "./rules/resource-name.js"; @@ -95,6 +96,7 @@ const rules = [ noEmptyModel, noReservedResourcePropertyRule, noQueryInPointOpRule, + noUnsupportedPatchPropertiesRule, ]; export const $linter = defineLinter({ diff --git a/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.md b/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.md new file mode 100644 index 0000000000..d9856a98c4 --- /dev/null +++ b/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.md @@ -0,0 +1,46 @@ +ARM PATCH request bodies must not make resource identity, location, or provisioning state properties writable. A PATCH operation cannot change top-level `id`, `name`, `type`, or `location`, or `properties.provisioningState`. + +Remove these properties from the PATCH body, mark identity and provisioning state properties with read-only visibility, and mark location as read-only and create-only. + +## Impact + +- **Area:** API + +Writable system-managed properties let generated SDKs offer updates that the service cannot safely apply and violate the ARM RPC contract. + +## ❌ Incorrect + +```tsp +model WidgetPatch { + id?: string; + location?: string; + properties?: WidgetPatchProperties; +} + +model WidgetPatchProperties { + provisioningState?: string; +} +``` + +## ✅ Correct + +```tsp +model WidgetPatch { + @visibility(Lifecycle.Read) + id?: string; + + @visibility(Lifecycle.Read, Lifecycle.Create) + location?: string; + + properties?: WidgetPatchProperties; +} + +model WidgetPatchProperties { + @visibility(Lifecycle.Read) + provisioningState?: ResourceProvisioningState; +} +``` + +## LintDiff Equivalent + +This rule corresponds to the Swagger validator rule [UnSupportedPatchProperties](https://github.com/Azure/azure-openapi-validator/blob/main/docs/un-supported-patch-properties.md). diff --git a/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts b/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts new file mode 100644 index 0000000000..b199869f8f --- /dev/null +++ b/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts @@ -0,0 +1,227 @@ +import { + createRule, + fileRef, + getLifecycleVisibilityEnum, + getLocationContext, + getVisibilityForClass, + isNeverType, + isNullType, + paramMessage, + resolveEncodedName, + type DiagnosticTarget, + type Model, + type ModelProperty, + type Operation, + type Program, + type Type, +} from "@typespec/compiler"; +import { + createMetadataInfo, + getHttpOperation, + resolveRequestVisibility, + Visibility, + type MetadataInfo, +} from "@typespec/http"; + +import { resolveProviderNamespace } from "../namespace.js"; + +const unsupportedPatchProperties = new Set(["id", "name", "type", "location"]); + +export const noUnsupportedPatchPropertiesRule = createRule({ + name: "no-unsupported-patch-properties", + docs: fileRef.fromPackageRoot("src/rules/no-unsupported-patch-properties.md"), + description: + "ARM PATCH request bodies must not contain writable resource identity, location, or provisioning state properties.", + severity: "warning", + url: "https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/no-unsupported-patch-properties", + messages: { + default: paramMessage`PATCH request body property '${"propertyName"}' is not patchable and should be removed or made read-only or immutable.`, + }, + create(context) { + return { + operation: (operation) => { + const namespace = operation.interface?.namespace ?? operation.namespace; + if (resolveProviderNamespace(context.program, namespace) === undefined) { + return; + } + + const [httpOperation] = getHttpOperation(context.program, operation); + if (httpOperation.verb !== "patch" || httpOperation.parameters.body === undefined) { + return; + } + + for (const violation of findViolations( + context.program, + httpOperation.parameters.body.type, + operation, + )) { + context.reportDiagnostic({ + target: violation.target, + format: { + propertyName: violation.propertyName, + }, + }); + } + }, + }; + }, +}); + +type Violation = { + target: DiagnosticTarget; + propertyName: string; +}; + +function findViolations(program: Program, patchBody: Type, operation: Operation): Violation[] { + const patchModel = getModelType(patchBody); + if (patchModel === undefined) { + return []; + } + + const metadataInfo = createMetadataInfo(program, { + canonicalVisibility: Visibility.Read, + canShareProperty: (property) => canSharePropertyUsingReadonlyOrXmsMutability(program, property), + }); + const visibility = resolveRequestVisibility(program, operation, "patch"); + const schemaVisibility = getSchemaVisibility(metadataInfo, patchModel, visibility); + const violations: Violation[] = []; + + for (const { property, jsonName } of getModelProperties(program, patchModel)) { + if (!metadataInfo.isPayloadProperty(property, schemaVisibility) || isNeverType(property.type)) { + continue; + } + + if (unsupportedPatchProperties.has(jsonName) && isWritableProperty(program, property)) { + violations.push({ + target: getDiagnosticTarget(program, property, operation), + propertyName: jsonName, + }); + } + + if (jsonName === "properties") { + collectProvisioningStateViolation( + program, + property.type, + operation, + metadataInfo, + schemaVisibility, + violations, + ); + } + } + + return violations; +} + +function collectProvisioningStateViolation( + program: Program, + type: Type, + operation: Operation, + metadataInfo: MetadataInfo, + visibility: Visibility, + violations: Violation[], +) { + const propertiesModel = getModelType(type); + if (propertiesModel === undefined) { + return; + } + + const schemaVisibility = getSchemaVisibility(metadataInfo, propertiesModel, visibility); + for (const { property, jsonName } of getModelProperties(program, propertiesModel)) { + if ( + jsonName === "provisioningState" && + metadataInfo.isPayloadProperty(property, schemaVisibility) && + !isNeverType(property.type) && + isWritableProperty(program, property) + ) { + violations.push({ + target: getDiagnosticTarget(program, property, operation), + propertyName: `properties.${jsonName}`, + }); + } + } +} + +function getModelType(type: Type): Model | undefined { + if (type.kind === "Model") { + return type; + } + if (type.kind !== "Union") { + return undefined; + } + + const nonNullVariants = [...type.variants.values()] + .map((variant) => variant.type) + .filter((variant) => !isNullType(variant)); + return nonNullVariants.length === 1 && nonNullVariants[0].kind === "Model" + ? nonNullVariants[0] + : undefined; +} + +function getSchemaVisibility( + metadataInfo: MetadataInfo, + model: Model, + visibility: Visibility, +): Visibility { + return metadataInfo.isTransformed(model, visibility) ? visibility : Visibility.Read; +} + +function isWritableProperty(program: Program, property: ModelProperty): boolean { + const lifecycle = getLifecycleVisibilityEnum(program); + const visibility = getVisibilityForClass(program, property, lifecycle); + const read = lifecycle.members.get("Read"); + const update = lifecycle.members.get("Update"); + if (read !== undefined && visibility.size === 1 && visibility.has(read)) { + return false; + } + + const emittedMutability = [...visibility].filter((member) => + ["Read", "Create", "Update"].includes(member.name), + ); + return ( + visibility.size === lifecycle.members.size || + emittedMutability.length === 0 || + (update !== undefined && visibility.has(update)) + ); +} + +function canSharePropertyUsingReadonlyOrXmsMutability( + program: Program, + property: ModelProperty, +): boolean { + const lifecycle = getLifecycleVisibilityEnum(program); + const visibility = getVisibilityForClass(program, property, lifecycle); + if (visibility.size === lifecycle.members.size) { + return true; + } + return ( + visibility.size > 0 && + [...visibility].every((member) => ["Read", "Create", "Update"].includes(member.name)) + ); +} + +function getDiagnosticTarget( + program: Program, + property: ModelProperty, + operation: Operation, +): DiagnosticTarget { + return getLocationContext(program, property).type === "project" ? property : operation; +} + +function getModelProperties( + program: Program, + model: Model, +): { property: ModelProperty; jsonName: string }[] { + const properties = new Map(); + + for (let current: Model | undefined = model; current !== undefined; current = current.baseModel) { + for (const property of current.properties.values()) { + const jsonName = resolveEncodedName(program, property, "application/json"); + if (!properties.has(jsonName)) { + properties.set(jsonName, property); + } + } + } + + return [...properties].map(([jsonName, property]) => ({ property, jsonName })); +} diff --git a/packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts b/packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts new file mode 100644 index 0000000000..cea55e912a --- /dev/null +++ b/packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts @@ -0,0 +1,210 @@ +import { Tester } from "#test/tester.js"; +import { + createLinterRuleTester, + type LinterRuleTester, + type TesterInstance, +} from "@typespec/compiler/testing"; +import { beforeEach, it } from "vitest"; + +import { noUnsupportedPatchPropertiesRule } from "../../src/rules/no-unsupported-patch-properties.js"; + +let runner: TesterInstance; +let tester: LinterRuleTester; + +beforeEach(async () => { + runner = await Tester.createInstance(); + tester = createLinterRuleTester( + runner, + noUnsupportedPatchPropertiesRule, + "@azure-tools/typespec-azure-resource-manager", + ); +}); + +it("reports writable id, name, and type properties", async () => { + await tester + .expect( + ` + @armProviderNamespace namespace Microsoft.Test; + + model WidgetPatch { + id?: string; + name?: string; + type?: string; + } + + @route("/widgets/{widgetName}") @patch + op update(@path widgetName: string, @body body: WidgetPatch): void; + `, + ) + .toEmitDiagnostics([ + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'id' is not patchable and should be removed or made read-only or immutable.", + }, + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'name' is not patchable and should be removed or made read-only or immutable.", + }, + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'type' is not patchable and should be removed or made read-only or immutable.", + }, + ]); +}); + +it("reports writable location and properties.provisioningState on a nullable body", async () => { + await tester + .expect( + ` + @armProviderNamespace namespace Microsoft.Test; + + model WidgetPatch { + location?: string; + properties?: WidgetPatchProperties; + } + + model WidgetPatchProperties { + provisioningState?: string; + } + + @route("/widgets/{widgetName}") @patch + op update(@path widgetName: string, @body body: WidgetPatch | null): void; + `, + ) + .toEmitDiagnostics([ + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'location' is not patchable and should be removed or made read-only or immutable.", + }, + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'properties.provisioningState' is not patchable and should be removed or made read-only or immutable.", + }, + ]); +}); + +it("reports inherited properties by their encoded JSON names", async () => { + await tester + .expect( + ` + @armProviderNamespace namespace Microsoft.Test; + + model PatchBase { + @encodedName("application/json", "name") + resourceName?: string; + } + + model WidgetPatch extends PatchBase { + @encodedName("application/json", "location") + region?: string; + properties?: WidgetPatchProperties; + } + + model PropertiesBase { + @encodedName("application/json", "provisioningState") + state?: string; + } + + model WidgetPatchProperties extends PropertiesBase {} + + @route("/widgets/{widgetName}") @patch + op update(@path widgetName: string, @body body: WidgetPatch): void; + `, + ) + .toEmitDiagnostics([ + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'location' is not patchable and should be removed or made read-only or immutable.", + }, + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'properties.provisioningState' is not patchable and should be removed or made read-only or immutable.", + }, + { + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'name' is not patchable and should be removed or made read-only or immutable.", + }, + ]); +}); + +it("accepts read-only and immutable reserved properties", async () => { + await tester + .expect( + ` + @armProviderNamespace namespace Microsoft.Test; + + model WidgetPatch { + @visibility(Lifecycle.Read) id?: string; + @visibility(Lifecycle.Read) name?: string; + @visibility(Lifecycle.Read) type?: string; + @visibility(Lifecycle.Read, Lifecycle.Create) location?: string; + properties?: WidgetPatchProperties; + } + + model WidgetPatchProperties { + @visibility(Lifecycle.Read) provisioningState?: string; + } + + @route("/widgets/{widgetName}") @patch + op update(@path widgetName: string, @body body: WidgetPatch): void; + `, + ) + .toBeValid(); +}); + +it("accepts a read-only referenced provisioning state", async () => { + await tester + .expect( + ` + @armProviderNamespace namespace Microsoft.Test; + + model WidgetPatch { + properties?: WidgetPatchProperties; + } + + model WidgetPatchProperties { + @visibility(Lifecycle.Read) + provisioningState?: ResourceProvisioningState; + } + + @route("/widgets/{widgetName}") @patch + op update(@path widgetName: string, @body body: WidgetPatch): void; + `, + ) + .toBeValid(); +}); + +it("accepts scalar and multi-model-union bodies", async () => { + await tester + .expect( + ` + @armProviderNamespace namespace Microsoft.Test; + + model FirstPatchBody { + id?: string; + } + + model SecondPatchBody { + name?: string; + } + + @route("/widgets/{widgetName}") @patch + op updateScalar(@path widgetName: string, @body body: string): void; + + @route("/other-widgets/{widgetName}") @patch + op updateUnion( + @path widgetName: string, + @body body: FirstPatchBody | SecondPatchBody + ): void; + `, + ) + .toBeValid(); +}); diff --git a/packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts b/packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts index ce215cf72b..4129e1f7e4 100644 --- a/packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts +++ b/packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts @@ -101,6 +101,7 @@ export default { "@azure-tools/typespec-azure-resource-manager/missing-operations-endpoint": true, "@azure-tools/typespec-azure-resource-manager/patch-envelope": true, "@azure-tools/typespec-azure-resource-manager/arm-resource-patch": true, + "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties": false, "@azure-tools/typespec-azure-resource-manager/resource-name": true, "@azure-tools/typespec-azure-resource-manager/retry-after": false, // Disable https://github.com/Azure/typespec-azure/issues/3351 "@azure-tools/typespec-azure-resource-manager/secret-prop": true, diff --git a/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md b/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md index cf666f029a..387660c0d3 100644 --- a/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md +++ b/website/src/content/docs/docs/libraries/azure-resource-manager/reference/linter.md @@ -69,3 +69,4 @@ Available ruleSets: | [`@azure-tools/typespec-azure-resource-manager/no-empty-model`](../rules/no-empty-model.md) | ARM Properties with type:object that don't reference a model definition are not allowed. ARM doesn't allow generic type definitions as this leads to bad customer experience. | | [`@azure-tools/typespec-azure-resource-manager/no-reserved-resource-property`](../rules/no-reserved-resource-property.md) | Reserved property names (for example 'billingData') must not be present in a resource's property bag. The property name is matched case-insensitively. | | [`@azure-tools/typespec-azure-resource-manager/no-query-in-point-op`](../rules/no-query-in-point-op.md) | Point operations must not declare query parameters beyond api-version. | +| [`@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties`](../rules/no-unsupported-patch-properties.md) | ARM PATCH request bodies must not contain writable resource identity, location, or provisioning state properties. | From 1e1dcfe19fa364eb1c3ff7dc6cf4404c23d4e09f Mon Sep 17 00:00:00 2001 From: Yuchao Yan Date: Thu, 3 Sep 2026 17:40:49 +0800 Subject: [PATCH 2/3] Adapt unsupported PATCH rule to ARM ruleset scope Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../rules/no-unsupported-patch-properties.ts | 7 --- .../no-unsupported-patch-properties.test.ts | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts b/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts index b199869f8f..0b7e57d0d1 100644 --- a/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts +++ b/packages/typespec-azure-resource-manager/src/rules/no-unsupported-patch-properties.ts @@ -23,8 +23,6 @@ import { type MetadataInfo, } from "@typespec/http"; -import { resolveProviderNamespace } from "../namespace.js"; - const unsupportedPatchProperties = new Set(["id", "name", "type", "location"]); export const noUnsupportedPatchPropertiesRule = createRule({ @@ -40,11 +38,6 @@ export const noUnsupportedPatchPropertiesRule = createRule({ create(context) { return { operation: (operation) => { - const namespace = operation.interface?.namespace ?? operation.namespace; - if (resolveProviderNamespace(context.program, namespace) === undefined) { - return; - } - const [httpOperation] = getHttpOperation(context.program, operation); if (httpOperation.verb !== "patch" || httpOperation.parameters.body === undefined) { return; diff --git a/packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts b/packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts index cea55e912a..45ef3cf0d3 100644 --- a/packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts +++ b/packages/typespec-azure-resource-manager/test/rules/no-unsupported-patch-properties.test.ts @@ -55,6 +55,49 @@ it("reports writable id, name, and type properties", async () => { ]); }); +it("reports unsupported properties in nested ARM namespaces", async () => { + await tester + .expect( + ` + @armProviderNamespace + namespace Microsoft.Test { + namespace Nested { + model WidgetPatch { + id?: string; + } + + @route("/widgets/{widgetName}") @patch + op update(@path widgetName: string, @body body: WidgetPatch): void; + } + } + `, + ) + .toEmitDiagnostics({ + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'id' is not patchable and should be removed or made read-only or immutable.", + }); +}); + +it("does not require provider namespace decoration when the ARM rule is enabled", async () => { + await tester + .expect( + ` + model WidgetPatch { + id?: string; + } + + @route("/widgets/{widgetName}") @patch + op update(@path widgetName: string, @body body: WidgetPatch): void; + `, + ) + .toEmitDiagnostics({ + code: "@azure-tools/typespec-azure-resource-manager/no-unsupported-patch-properties", + message: + "PATCH request body property 'id' is not patchable and should be removed or made read-only or immutable.", + }); +}); + it("reports writable location and properties.provisioningState on a nullable body", async () => { await tester .expect( From 100f545c80631782acdd3090d19288d957550a46 Mon Sep 17 00:00:00 2001 From: Yuchao Yan Date: Tue, 8 Sep 2026 10:09:41 +0800 Subject: [PATCH 3/3] Fix promotion changelog classification Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...te-un-supported-patch-properties-2026-09-03-13-20-00.md | 3 +-- .../register-no-unsupported-patch-properties-ruleset.md | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .chronus/changes/register-no-unsupported-patch-properties-ruleset.md diff --git a/.chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md b/.chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md index 38f043762a..597d226076 100644 --- a/.chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md +++ b/.chronus/changes/promote-un-supported-patch-properties-2026-09-03-13-20-00.md @@ -2,7 +2,6 @@ changeKind: feature packages: - "@azure-tools/typespec-azure-resource-manager" - - "@azure-tools/typespec-azure-rulesets" --- -Add the `no-unsupported-patch-properties` ARM linter rule, which reports writable resource identity, location, and provisioning state properties in PATCH request bodies. Register the rule as disabled by default in the ARM ruleset. +Add the `no-unsupported-patch-properties` ARM linter rule, which reports writable resource identity, location, and provisioning state properties in PATCH request bodies. diff --git a/.chronus/changes/register-no-unsupported-patch-properties-ruleset.md b/.chronus/changes/register-no-unsupported-patch-properties-ruleset.md new file mode 100644 index 0000000000..32a0c1b36e --- /dev/null +++ b/.chronus/changes/register-no-unsupported-patch-properties-ruleset.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@azure-tools/typespec-azure-rulesets" +--- + +Register the ARM `no-unsupported-patch-properties` lint rule as disabled in the resource manager ruleset.