Skip to content

Commit 94445d4

Browse files
authored
Merge branch 'main' into vikeshi26/arm-leases-batch-2026-05-22
2 parents 30fd85b + 025f152 commit 94445d4

1,238 files changed

Lines changed: 244855 additions & 144689 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/instructions/armapi-review.instructions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,12 @@ The TypeSpec-required rule applies to all new ARM API versions. The full rule de
505505

506506
- Properties that are arrays **MUST** have plural names (e.g., `scopes`, `rules`, `addresses`) -- not singular (e.g., `scope`, `rule`, `address`). Singular names suggest a single value, not a collection.
507507

508+
### 8.3b Array Item Identifiers (`x-ms-identifiers`)
509+
510+
- Arrays of objects in ARM responses **SHOULD** declare which item property uniquely identifies an item via the `x-ms-identifiers` extension. This is what enables ARM tooling (Resource Graph, Change Analysis, What-If) to track individual items across PUT/PATCH/GET responses without treating the array as opaque.
511+
- **In TypeSpec source, do NOT use `@extension("x-ms-identifiers", ...)`** to control this. `@extension` is forbidden in TypeSpec source for any reason. Use the `@key` decorator on the identity property of the item type, or the `@identifiers(#["<propertyName>"])` decorator on the array property. For arrays whose items have no logical identifier, use `@identifiers(#[])`. See `typespec-review.instructions.md` Section 6.5 (TSP-ARRAY-IDENTIFIERS) for the full rule with examples.
512+
- A `#suppress` on `missing-x-ms-identifiers` is acceptable **only** as a last resort and must include a real technical justification (no `FIXME`/`TODO`/`TBD`, no "matching another resource" — see `typespec-review.instructions.md` Section 4.1 Warning Suppression Policy).
513+
508514
### 8.4 Use Specific Types Instead of Generic Strings (ACTIVELY REVIEW)
509515

510516
> **See also:** [`.github/skills/azure-api-review/references/naming-conventions.md`](../skills/azure-api-review/references/naming-conventions.md) for common property names (e.g., `createdAt`, `lastModifiedAt`, `deletedAt`) and resource identifier naming rules (use `Id` suffix, not `Uri` or `Name`).
@@ -1138,6 +1144,7 @@ When reviewing ARM resource-manager swagger files, verify:
11381144
- ✅ Immutable properties: unchanged values accepted, changed values rejected with 400 (OAPI030, OAPI031)
11391145
- ✅ Fields have clear ownership — server-owned (readOnly) or client-owned (preserved exactly); no server auto-updates of client fields (OAPI034)
11401146
- ✅ Array ordering preserved in PUT/PATCH responses (OAPI024)
1147+
- ✅ Arrays of objects declare `x-ms-identifiers` via TypeSpec `@key` or `@identifiers` (not `@extension`) — see TSP-ARRAY-IDENTIFIERS
11411148
- ✅ Property value casing and formatting preserved — no normalization (OAPI026)
11421149

11431150
### List APIs

.github/instructions/typespec-review.instructions.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ Flag these issues when found:
290290
- **`@flattenProperty` on new APIs** — do not add new `@flattenProperty` decorators. Flattening creates SDK-breaking issues and is discouraged for new resource types and properties. Existing flattened properties may remain for backward compatibility.
291291
- **Spread-only model types as full models** — a model type used only for spreading (`...`) into other types **SHOULD** be declared as an `alias` instead. Using `model` for types that are only spread generates unnecessary types in the output. See [TypeSpec alias documentation](https://typespec.io/docs/language-basics/alias) (TypeSpec-BestPractice-01).
292292
- **Empty model literal `{}` as POST action body** -- when an `ArmResourceActionAsync` or `ArmResourceActionSync` POST action does not need a request body, use `void` instead of `{}`. An empty model literal triggers the `no-empty-model` lint rule, and suppressing it is the wrong fix. Use `void` and remove the suppression.
293+
- **`@extension(...)` in TypeSpec source** -- never add `@extension(...)` decorators to TypeSpec source for any reason. This includes `@extension("x-ms-identifiers", ...)`, `@extension("x-ms-mutability", ...)`, and any other OpenAPI extension. `@extension` bypasses TypeSpec's type system and emitter conventions. Use the appropriate built-in decorator instead: `@identifiers` / `@key` for `x-ms-identifiers`, `@visibility` for mutability, `@secret` for secret data, etc. (see TSP-ARRAY-IDENTIFIERS for the `x-ms-identifiers` case).
293294

294295
---
295296

@@ -320,6 +321,47 @@ Flag these issues when found:
320321
- Properties whose names or doc comments clearly indicate numeric values (e.g., `numberOfCores`, `ram`, `vCpu`, `diskSizeGB`) **SHOULD** use `int32`, `int64`, or `float64` — not `string`.
321322
- If backward compatibility forces a string type for a numeric value, the doc comment **MUST** document the expected format and units.
322323

324+
### 6.5 Array Identifiers — `x-ms-identifiers` (TSP-ARRAY-IDENTIFIERS)
325+
326+
The `missing-x-ms-identifiers` linter rule fires on array-typed properties whose item models do not declare which property uniquely identifies an item. **Do not silence this rule with `@extension("x-ms-identifiers", [])`.** `@extension` is forbidden in TypeSpec source for any reason (see Section 5 anti-patterns).
327+
328+
Use one of the following built-in TypeSpec decorators instead:
329+
330+
- **Preferred — `@key` on the identity property of the item type.** The emitter automatically derives `x-ms-identifiers` from `@key`. Use this when the item model has a single, natural identity property:
331+
332+
```tsp
333+
model MoveRequest {
334+
@key moveId: string;
335+
from: string;
336+
to: string;
337+
}
338+
339+
model MoveCollection {
340+
moves?: MoveRequest[];
341+
}
342+
```
343+
344+
- **Alternative — `@identifiers(#["<propertyName>", ...])` on the array property.** Use this when the identity property cannot be marked with `@key` on the item type (e.g., the item type is shared and `@key` would conflict, or identity is composed of multiple properties):
345+
346+
```tsp
347+
@identifiers(#["moveId"])
348+
moves?: MoveRequest[];
349+
```
350+
351+
- **Items genuinely have no identifier — `@identifiers(#[])` on the array property.** For arrays whose items are ordered result records, primitive values, or otherwise have no stable identity field, declare the empty identifier list explicitly. This satisfies the linter without a suppression and documents the design intent:
352+
353+
```tsp
354+
@identifiers(#[])
355+
testResults?: TestResultRecord[];
356+
```
357+
358+
**Forbidden patterns:**
359+
360+
- `@extension("x-ms-identifiers", [])` — never add `@extension` in TypeSpec source for any reason. Replace with `@identifiers(#[])` (or `@key` on the item type).
361+
- `#suppress "@azure-tools/typespec-azure-resource-manager/missing-x-ms-identifiers" "..."` — suppression is **only** acceptable as a last resort when neither `@key` nor `@identifiers` is technically feasible (extremely rare). The suppression reason must still meet Section 4.1 rules (real technical justification, no `FIXME`/`TODO`/`TBD`, no "matching another resource"). For brand-new operations and models, suppression is never the right fix — use `@identifiers` or `@key`.
362+
363+
When reviewing a new `missing-x-ms-identifiers` suppression, propose `@identifiers` or `@key` as the fix. **Never** suggest `@extension("x-ms-identifiers", ...)`.
364+
323365
---
324366

325367
## 7. `tspconfig.yaml` Additional Validation
@@ -396,6 +438,8 @@ When reviewing TypeSpec files, verify:
396438
- ✅ ARM resource ID properties use `armResourceIdentifier` not `string` (TSP-ARM-RESOURCE-ID)
397439
- ✅ No `@operationId` overrides — restructure interfaces instead
398440
- ✅ URI properties use `url` scalar type, not plain `string`
441+
- ✅ Array-typed properties declare item identity via `@key` (on item type) or `@identifiers` (on array property); use `@identifiers(#[])` when items have no identifier (TSP-ARRAY-IDENTIFIERS)
442+
- ✅ No `@extension(...)` decorators in TypeSpec source — never `@extension("x-ms-identifiers", ...)`, `@extension("x-ms-mutability", ...)`, etc.
399443
- ✅ Standard ARM base types used (no custom/private resource decorators)
400444
- ✅ Client customizations only in `client.tsp`
401445
-`tspconfig.yaml` references correct linter ruleset

.github/skills/evals/arm-api-reviewer/.vally.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ paths:
44

55
suites:
66
all:
7-
description: "Full ARM API Reviewer eval suite (34 stimuli)"
7+
description: "Full ARM API Reviewer eval suite (35 stimuli)"
88
evals: ["evaluate/eval-*.yaml"]

.github/skills/evals/arm-api-reviewer/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ arm-api-reviewer/
2222
│ ├── eval-classification.yaml
2323
│ ├── eval-report-format.yaml
2424
│ └── eval-citation-and-parity.yaml
25-
├── fixtures/ # Test fixtures (32 files)
25+
├── fixtures/ # Test fixtures (34 files)
2626
│ ├── arm-openapi/ # ARM OpenAPI specs with seeded violations
2727
│ ├── examples/ # Example JSON files (good and bad)
2828
│ ├── readme/ # readme.md suppression files
@@ -31,7 +31,7 @@ arm-api-reviewer/
3131
└── README.md # This file
3232
```
3333

34-
## Test Categories (34 test cases across 13 eval files)
34+
## Test Categories (35 test cases across 13 eval files)
3535

3636
| ID | Category | Count | Description |
3737
| ------ | ------------------------ | ----- | -------------------------------------------------------------------- |
@@ -41,7 +41,7 @@ arm-api-reviewer/
4141
| 04xxxx | Breaking changes | 4 | Removed property, type change, enum narrowing, added required |
4242
| 05xxxx | Suppression analysis | 2 | Missing reason, security rule suppressions |
4343
| 06xxxx | Example file validation | 2 | Bad resource ID, realistic secrets |
44-
| 07xxxx | TypeSpec review | 3 | Segment casing, secrets, anti-patterns |
44+
| 07xxxx | TypeSpec review | 4 | Segment casing, secrets, anti-patterns, x-ms-identifiers |
4545
| 08xxxx | Check Name Availability | 1 | Custom CNA models, missing input validation |
4646
| 09xxxx | True negatives | 3 | Clean spec, clean example, clean proxy resource |
4747
| 10xxxx | Classification | 1 | NEW vs EXISTING issue tagging |
@@ -51,15 +51,15 @@ arm-api-reviewer/
5151

5252
## Fixtures
5353

54-
All 33 fixture files live in `fixtures/`. See
54+
All 34 fixture files live in `fixtures/`. See
5555
[`fixtures/README.md`](fixtures/README.md) for the complete catalog with
5656
descriptions, seeded violations, and guidance on reusing fixtures in other
5757
eval suites.
5858

5959
- **15 ARM OpenAPI specs** in `arm-openapi/` -- 2 clean + 12 with seeded violations + 1 TypeSpec-generated
6060
- **3 example JSON files** in `examples/` -- 1 clean + 2 with issues
6161
- **2 readme.md files** in `readme/` -- suppression scenarios
62-
- **3 TypeSpec files** in `typespec/` -- segment/naming, secret/type, anti-pattern violations
62+
- **4 TypeSpec files** in `typespec/` -- segment/naming, secret/type, anti-pattern, x-ms-identifiers violations
6363
- **10 version-pair files** in `version-pairs/` -- 5 pairs for breaking change detection
6464

6565
## Quick Start
@@ -74,7 +74,7 @@ VS Code with GitHub Copilot active.
7474
```powershell
7575
cd .github/skills/evals/arm-api-reviewer
7676
77-
# Run the full suite (34 stimuli, sequential -- safest)
77+
# Run the full suite (35 stimuli, sequential -- safest)
7878
.\run-evals.ps1
7979
8080
# Point to an existing evaluate clone instead of re-cloning
@@ -124,7 +124,7 @@ cd .github/skills/evals/arm-api-reviewer
124124
# (evaluate is a monorepo; the vally binary lives under packages/cli)
125125
export VALLY_CLI="/path/to/evaluate/packages/cli/dist/index.js"
126126

127-
# Run the full suite (all 34 stimuli, 5 concurrent workers)
127+
# Run the full suite (all 35 stimuli, 5 concurrent workers)
128128
node $VALLY_CLI eval --suite all --verbose
129129

130130
# Run a single category

.github/skills/evals/arm-api-reviewer/evaluate/eval-typespec.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,46 @@ stimuli:
116116
- "Flagged ALL_CAPS enum value (FAILED) -- should use PascalCase (Failed)"
117117
- "Flagged default value on ipAllocationMethod that flows into PATCH model (RPC-Patch-V1-10)"
118118
- "Flagged @flattenProperty on new API -- flattening is discouraged for new resource types"
119+
120+
# 070004 -- x-ms-identifiers violations (TSP-ARRAY-IDENTIFIERS)
121+
- name: detect-typespec-x-ms-identifiers-violations
122+
environment:
123+
files:
124+
- src: "../fixtures/typespec/x-ms-identifiers-violations.tsp"
125+
dest: "specification/identifierdemo/resource-manager/Microsoft.IdentifierDemo/main.tsp"
126+
prompt: >
127+
Review the following TypeSpec specification file for compliance with
128+
Azure REST API Guidelines, ARM RPC rules, and TypeSpec-specific review
129+
rules. This is a new service with no previous API version. Report all
130+
findings with rule IDs, line numbers, and fix suggestions. Pay special
131+
attention to how array properties declare x-ms-identifiers.
132+
133+
File: specification/identifierdemo/resource-manager/Microsoft.IdentifierDemo/main.tsp
134+
constraints:
135+
expect_skills:
136+
- azure-api-review
137+
graders:
138+
- type: output-matches
139+
config:
140+
pattern: "(?i)@extension|x-ms-identifiers"
141+
- type: output-matches
142+
config:
143+
pattern: "(?i)@identifiers|@key"
144+
- type: output-matches
145+
config:
146+
pattern: "(?i)TSP-ARRAY-IDENTIFIERS|missing-x-ms-identifiers"
147+
- type: output-matches
148+
config:
149+
pattern: "(?i)FIXME|placeholder"
150+
- type: prompt
151+
rubric:
152+
- 'Flagged the @extension("x-ms-identifiers", []) on preflightResults as forbidden -- @extension MUST NOT be used in TypeSpec source for any reason (TSP-ARRAY-IDENTIFIERS)'
153+
- 'Recommended replacing @extension("x-ms-identifiers", []) with @identifiers(#[]) for preflightResults'
154+
- 'Flagged the @extension("x-ms-identifiers", ["nodeId"]) on nodes as forbidden -- must use @identifiers(#["nodeId"]) or @key on the item type'
155+
- 'Flagged the #suppress reason containing "FIXME" on volumes as a placeholder-text violation (TSP-4.1 / Section 4.1 -- Warning Suppression Policy)'
156+
- 'Flagged the vague "Matching AiGateway pattern" suppression reason on firewallRules -- suppressions must stand on their own with a real technical justification (TSP-4.1)'
157+
- 'Recommended using @identifiers(#["volumeId"]) on volumes or @key on volumeId in the StorageVolume model instead of suppressing the rule'
158+
- 'Did NOT flag the @identifiers(#["moveId"]) on pendingMoves -- this is the correct pattern (positive control)'
159+
- 'Did NOT flag the @identifiers(#[]) on diagnosticSnapshots -- explicit empty identifier list is the correct pattern for arrays with no identity (positive control)'
160+
- 'Did NOT flag customLabels -- the ClusterLabel item type uses @key on labelKey, which automatically derives x-ms-identifiers (positive control)'
161+
- 'Never suggested @extension("x-ms-identifiers", ...) as a fix in any finding'

.github/skills/evals/arm-api-reviewer/fixtures/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,11 @@ Each subdirectory contains a `stable-2024-01-01.json` (previous) and
9494
| `new-vs-existing/` | Mixed classification | `bar` has no description in both versions (EXISTING); `baz` is newly added without description (NEW). |
9595
| `added-required-property/` | Optional becomes required | `sku` property changes from optional to required in WidgetProperties between versions. |
9696

97-
### `typespec/` -- TypeSpec Specification Files (3 files)
97+
### `typespec/` -- TypeSpec Specification Files (4 files)
9898

9999
| File | Violations | Description |
100100
| -------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101101
| `segment-casing-violations.tsp` | @segment casing, naming, enum | @segment uses all-lowercase instead of camelCase (TSP-SEGMENT-CASE); PATCH named "patch" not "update" (TSP-PATCH-NAME); @operationId override; enum instead of union; plain string for ARM resource ID. |
102102
| `secret-and-type-violations.tsp` | Secrets, type constraints | Missing @secret on connectionString/adminPassword/primaryKey (SEC-SECRET-DETECT); #suppress for secret-prop; string instead of utcDateTime; string for numeric diskSizeGB (TSP-NUMERIC-TYPE); plain string for ARM resource ID (TSP-ARM-RESOURCE-ID). |
103103
| `anti-patterns.tsp` | Common TypeSpec anti-patterns | Empty model `{}` instead of void for POST action; #suppress for no-empty-model; @flattenProperty on new API; default value flowing into PATCH; `\| null` on new property; underscore and ALL_CAPS enum values. |
104+
| `x-ms-identifiers-violations.tsp` | x-ms-identifiers / @extension | `@extension("x-ms-identifiers", ...)` on array properties (forbidden -- use `@identifiers` or `@key`); `#suppress` of `missing-x-ms-identifiers` with FIXME placeholder text (TSP-4.1); vague "matching another resource" suppression (TSP-ARRAY-IDENTIFIERS). Also contains positive controls: `@identifiers(#["..."])`, `@identifiers(#[])`, and `@key` on item type. |

0 commit comments

Comments
 (0)