Skip to content

[Swagger Linter Migration][Superseded by #5652] XMSLongRunningOperationProperty -> lro-missing-polling-metadata - #5709

Closed
Yuchao Yan (msyyc) wants to merge 7 commits into
mainfrom
promote-lintdiff-xms-long-running-operation-property
Closed

Yuchao Yan (msyyc) wants to merge 7 commits into
mainfrom
promote-lintdiff-xms-long-running-operation-property

Conversation

@msyyc

@msyyc Yuchao Yan (msyyc) commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Superseded by the combined proposal in #5652

The official-destination policy and unique regression coverage from this PR at c5e07710 are now incorporated into #5652 in 3ca178be4.

The combined PR is titled [Swagger Linter Migration] LroExtension + XMSLongRunningOperationProperty -> lro-missing-polling-metadata. Please continue review of both policies there rather than introducing a second public rule with the same name.

#5652 preserves the union: explicit 202 for PATCH/marked provider-or-collection POST, or qualifying Location/Azure-AsyncOperation headers on PUT/PATCH/POST/DELETE 200/201/202/204 responses. It performs one metadata lookup and emits at most one ARM warning per eligible operation, including operations matching both triggers. The combined warning and approved suppression target the authored operation, replacing this PR's proposed header-property target.

The immutable source remains #5704 at 2f7e1d5c. No source repair, branch deletion or wholesale branch merge was performed. The combined rule retains one disabled central resource-manager entry; ARM library all includes the registered rule.

Fresh combined local validation passed 543 tests across 73 ARM/rulesets files, affected builds, repository format/lint and one-time published-snippet compilation. These results do not claim new GitHub CI or maintainer approval.

This PR remains open but superseded, preserving its branch and review history. No review thread was resolved. Acceptance and merging of the combined proposal are still required; supersession does not mark either migration Done.

Historical #5709 promotion notes before consolidation (superseded where the combined contract above differs)

Summary

Add the opt-in ARM rule lro-missing-polling-metadata. A polling URL alone does not establish a polling contract: ARM operations with qualifying Location or Azure-AsyncOperation response headers need native polling metadata so generated clients can recognize asynchronous behavior.

This promotes the reviewed development outcome in #5704. The resource manager ruleset registers the new rule as false, so this PR does not enable new diagnostics for existing specs.

Original Swagger linter

The original check, associated with RPC-Async-V1-15, performs these checks:

  • Select ARM PUT, PATCH, POST, and DELETE operations.
  • Accept an operation whose x-ms-long-running-operation value is exactly true.
  • Otherwise inspect responses 200, 201, 202, and 204.
  • Look for a case-insensitive Location or Azure-AsyncOperation response header.
  • Report the first qualifying header when the operation lacks a true LRO marker.
  • Leave other methods, status codes, and header names outside this check.

How the Swagger linter works

For each selected operation object, the function checks the operation-level extension and traverses the listed response objects and their header maps. A finding points to the qualifying header's JSON path, not the operation as a whole. The legacy ARM check is an error; the reviewed native source uses a warning, which this promotion preserves.

The native implementation must not reproduce extension-only compliance. An output-only x-ms-long-running-operation: true override can satisfy the Swagger check without supplying supported TypeSpec polling metadata. That is an intentional parity difference, not a reason to read emitted OpenAPI or infer emitter behavior in production.

Source TypeSpec lintdiff rule

  • Validator ID: XMSLongRunningOperationProperty.
  • Local rule name: xms-long-running-operation-property.
  • Canonical validator slug: xms-long-running-operation-property.
  • Source repository: Azure/typespec-azure.
  • Source branch: feature/lintdiff-xms-long-running-operation-property.
  • Immutable reviewed source SHA: 2f7e1d5c3d04230666f7524235bd25eff364eaa8.
  • Source worktree: C:\dev\worktrees\lintdiff-xms-long-running-operation-property.
  • Original source file: xms-long-running-operation-property.ts.

The source was assumed done for this promotion and its development GitHub review terminated clean on the exact SHA above. Its index and working tree were clean. No source rule, fixture, snapshot, package manifest, or migration document was modified during promotion.

Destination analysis

The agent-selected destination is @azure-tools/typespec-azure-resource-manager. The catalog applicability is ARM, the policy comes from ARM RPC guidance, and the selected polling headers are the ARM header set.

Azure Core is a plausible alternative for a general LRO rule, but its long-running-polling-operation-required check concerns Operation-Location and a linked polling operation. It does not cover this ARM response-header predicate. ARM's existing lro-location-header checks the other direction: a 202 response must include a Location header. It does not require metadata when a header exists.

The implementation uses the existing ARM-to-Azure-Core dependency direction. It adds no TCGC, emitter, or OpenAPI dependency.

How the promoted TypeSpec linter works

The public rule name is lro-missing-polling-metadata.

  1. listServices and getHttpService expose the program's HTTP operations.
  2. The rule selects PUT/PATCH/POST/DELETE and success codes in order 200, 201, 202, 204.
  3. It selects the first matching Location or Azure-AsyncOperation header property, ignoring header-name casing.
  4. getLroMetadata determines whether the operation has supported native polling semantics.
  5. A missing metadata result produces the preserved warning message on that header property.

The diagnostic unit is the operation. A visited-operation set prevents duplicate findings through overlapping services. Different operations sharing the same header declaration can each produce a finding there. The rule uses the current program's native metadata; it does not introduce version projection or claim all-version Swagger parity.

The only behavioral audience adaptation removes the source's documented provider-namespace guard. That guard isolates ARM from data-plane services in lintdiff's mixed runner; it is not a polling requirement. Explicitly selecting this official ARM rule supplies its intended audience. The new no-provider-metadata regression proves this adaptation. Neighboring ARM rules use the same direct native HTTP approach.

Other adaptations are destination metadata, the concise official name/export, linter registration, native docs/tests, and the disabled ruleset entry. The qualifying predicate, status/header ordering, diagnostic target/unit, warning message/severity, and visited-operation deduplication match the pinned source. No source defect was found.

Standard ARM examples and final snippet proof

These are the exact final authored rule examples, compiled by native tests in a configured ARM service with the package's existing tester setup.

Incorrect: plain polling URL on a synchronous provider action

model Response {
  @statusCode statusCode: 202;
  @header("Location") location?: string;
}

@armResourceOperations
interface ProviderActions {
  start is ArmProviderActionSync<Request = void, Response = Response>;
}

The enabled rule reports exactly one warning on Response.location.

Correct: preserve semantic polling headers from the asynchronous template

@armResourceOperations
interface ProviderActions {
  start is ArmProviderActionAsync<Request = void, Response = void>;
}

The enabled rule reports no warning. Tests read both examples directly from the final authored Markdown and check the incorrect diagnostic code, message, count, and exact property position. Both use supported standard ARM templates; no handwritten-route fallback or template limitation is needed.

Fixture-to-native test mapping

The links below resolve to original source main.tsp files on the recorded canonical source branch. Native case strings are exact test titles, not snapshot equivalence claims. The immutable source for all mappings remains 2f7e1d5c3d04230666f7524235bd25eff364eaa8.

Original lintdiff fixture Native vitest case Coverage note
XMSLongRunningOperationProperty/missing-lro-extension it("reports a synchronous resource action with a Location header") Supported sync resource action with 200 Location; exact authored header target.
XMSLongRunningOperationProperty/missing-azure-asyncoperation-header it("reports a synchronous resource action with an Azure-AsyncOperation header") Supported sync resource action with 204 Azure-AsyncOperation; exact authored header target.
XMSLongRunningOperationProperty/no-async-headers it("accepts a synchronous ARM action without polling headers") Qualifying POST/204 without a selected polling header.
XMSLongRunningOperationProperty/compliant-with-template it("accepts standard asynchronous resource operations") Standard asynchronous PUT/PATCH/DELETE resource operations retain polling metadata.
LroExtension/provider-async-plain-location it("rejects an async template whose semantic headers were replaced with plain headers") Plain header customization removes native polling metadata.
LroExtension/provider-semantic-location it("accepts the corrected documentation example") Semantic polling headers provide metadata; the final async provider-action example is read directly from authored docs.
LroExtension/with-lro-extension Not ported (emitter-only comparison) Output-only override and no-openapi suppression remain pinned comparison evidence, not native polling metadata or a reason to import OpenAPI into native tests.
ConsistentPatchProperties/async-get-fallback Not ported (emitter-only comparison) Neighboring PATCH-property fallback/output override is comparison evidence; the native matrix separately covers missing-metadata PATCH.
N/A (promotion adaptation) it("reports qualifying headers without provider metadata") Proves removal of mixed-runner isolation without retaining an unnecessary provider guard.
N/A (source native regression) it("reports each operation sharing a header declaration") One diagnostic per operation, even with a shared property target.
N/A (source native regression) it("reports overlapping services only once per operation") Preserves rule-owned visited-operation deduplication and exact target/count.
N/A (source native regression) it("reports only the first qualifying header in success-code order") Preserves success-code preference and the first matching property.
N/A (final documentation example) it("reports the incorrect documentation example") Exact final standard-template snippet produces one authored-header diagnostic.

The source's nested-provider traversal/isolation cases are not copied mechanically: ordinary HTTP traversal is framework-owned after the isolation guard is removed, and mixed ARM/data-plane filtering does not belong in this explicitly selected ARM rule. The native no-provider regression covers the intentional adaptation instead. No validator snapshots, emitted OpenAPI, or corpus artifacts are copied.

The native parameterized matrix also covers all four qualifying verbs/statuses, casing, excluded methods/statuses/headers, and semantic header customizations in both directions.

Migration evidence

See the immutable migration evidence and its raw document for the declared focused comparisons, real-service findings, full corpus populations, selected-version attribution, all compile exclusions, and intentional native/Swagger differences.

That evidence belongs to the reviewed source, not a fresh promotion corpus run. Matching observed project counts do not prove universal equivalence. In particular, a true output-only Swagger extension is deliberately not accepted as native polling metadata.

Validation

All required targeted checks passed on the exact reviewed promotion inputs:

Required check Outcome
One-time missing ARM dependency-closure build Passed; no dependency reinstall or prepared-layer replay
Final focused native rule suite 22/22 passed, including final authored incorrect/correct snippets
Full ARM package suite 496/496 tests, 67/67 files passed, fixed maxWorkers2
Rulesets build and tests Passed; 5/5 tests
ARM package build and lint Passed, including oxlint deny-warnings
ARM regen-docs and generated-reference inspection Passed; official name, paths, links and manual ARM matrix checked
Scoped source formatting and four-doc empty-ignore write/check Passed; output explicitly lists README, authored rule docs, generated linter reference and ignored rule page
Separate Chronus entries and plain chronus status Passed; ARM feature and rulesets internal disabled registration, LF-only entries
Diff/ownership/staged-content checks Exact approved 10-file scope; generated rule page remains ignored and is not force-added
Independent persistent local precommit review Clean, one round; reviewer independently passed 22/22, including final snippet proof

Per-test timeout and hook defaults were not changed. The first focused attempt exposed a CRLF-only documentation-reader mismatch; the test-only portable-reader correction passed the original22 scope, final-doc22, and full496. A later evidence catalog self-capture hash was reconciled after producer quiescence; all27 raw receipt hashes and all10 frozen file hashes were independently reverified. No required failure remains unresolved.

Validation limitations

Validation and independent review occurred on 2026-10-14, bound to prepared promotion base e0140fa5ad88b34dddd69b4042040f12639432f6, core 62f1d298cac7ee3360ec0f6ba84f2bf1e299dba0, and the exact approved inputs.

Canonical main was fetched at d5ea3d93c264b5bed837d670b5a792f859a5217c but was not integrated. The only task-file intersection adds an unrelated Core no-fixed-enum-discriminator matrix row, separate from the new ARM row. The reviewer also checked the incoming HTTP membership-scan-to-Set change and found equivalent behavior, with no relevant API/predicate change or additional required gate. This is not a claim of validation against an integrated current-main/newer-core tree.

No supplemental monorepo wrapper or website build was warranted for this isolated rule scope. CI's dedicated Website job remains authoritative for the Astro build. Existing diagnostic-documentation warnings from regen-docs are retained in the validation receipts; the new rule page and references were generated successfully.

Promotion sync policy

The reviewed source SHA remains immutable during promotion. A confirmed source-semantic gap blocks promotion and returns to the queue's bounded authorized source-repair flow. Only a newly clean source review can authorize refreshing this promotion's provenance and native copy. Promotion-only adaptation fixes must not hide a source-semantic change.

Review follow-up: documentation-test scope (2026-10-16)

Removed the bespoke Markdown-parsing test in cf9785b, without moving or replacing the parser. All 22 retained literal tests pass, preserving the rule-owned positive/negative and exact authored-target controls. The dependency-closure build and repository format/lint passed.

The actual published incorrect/correct snippets were separately compiled once, with exactly one expected warning versus zero diagnostics and hash/target receipts. Literal tests do not continuously validate Markdown. This supersedes historical description claims about direct Markdown coverage; a standard documentation-testing system is outside this promotion.

Promote XMSLongRunningOperationProperty as lro-missing-polling-metadata
from reviewed source 2f7e1d5 (PR #5704).
Preserve the native predicate and targets while removing mixed-runner isolation.
Register the official ARM rule as disabled by default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8e9d80e2-2e85-4bd4-9baa-eb937b5c60f1
@microsoft-github-policy-service microsoft-github-policy-service Bot added int:azure-specs Run integration tests against azure-rest-api-specs lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library meta:website TypeSpec.io updates linter Issues related to linter rules labels Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • ✅ @azure-tools/typespec-azure-resource-manager
  • ✅ @azure-tools/typespec-azure-rulesets
Show changes

@azure-tools/typespec-azure-resource-manager - feature ✏️

Add the lro-missing-polling-metadata lint rule to require native polling metadata for ARM operations with Location or Azure-AsyncOperation response headers.,> ,> Enable this opt-in rule explicitly:,> ,> yaml,> linter:,> enable:,> "@azure-tools/typespec-azure-resource-manager/lro-missing-polling-metadata": true,>

@azure-tools/typespec-azure-rulesets - internal ✏️

Register the ARM lro-missing-polling-metadata lint rule as disabled in the resource manager ruleset.

@pkg-pr-new

pkg-pr-new Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@azure-tools/typespec-azure-resource-manager@5709
npm i https://pkg.pr.new/@azure-tools/typespec-azure-rulesets@5709

commit: c5e0771

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

📦 Package size report

✅ No notable package size changes compared to the base branch.

14 package(s) with no notable change
Package Packed (base → head) Δ Packed Unpacked (base → head) Δ Unpacked
@azure-tools/typespec-azure-resource-manager 186.65 KB → 186.72 KB +67 B (+0.0%) 1.12 MB → 1.12 MB +651 B (+0.1%)
@azure-tools/typespec-azure-rulesets 5.57 KB → 5.59 KB +25 B (+0.4%) 34.21 KB → 34.41 KB +207 B (+0.6%)
@azure-tools/azure-http-specs 143.52 KB → 143.52 KB — 1.20 MB → 1.20 MB —
@azure-tools/typespec-autorest 87.87 KB → 87.87 KB — 424.17 KB → 424.17 KB —
@azure-tools/typespec-autorest-canonical 7.42 KB → 7.42 KB — 26.00 KB → 26.00 KB —
@azure-tools/typespec-azure-core 136.55 KB → 136.55 KB — 733.30 KB → 733.30 KB —
@azure-tools/typespec-azure-examples 80.15 KB → 80.15 KB — 383.36 KB → 383.36 KB —
@azure-tools/typespec-azure-portal-core 36.26 KB → 36.26 KB — 242.62 KB → 242.62 KB —
@azure-tools/typespec-client-generator-core 243.98 KB → 243.98 KB — 1.30 MB → 1.30 MB —
@azure-tools/typespec-go 263.76 KB → 263.76 KB — 1.35 MB → 1.35 MB —
@azure-tools/typespec-java 20.22 MB → 20.22 MB — 22.51 MB → 22.51 MB —
@azure-tools/typespec-metadata 15.91 KB → 15.91 KB — 62.26 KB → 62.26 KB —
@azure-tools/typespec-python 42.54 KB → 42.54 KB — 165.94 KB → 165.94 KB —
@azure-tools/typespec-ts 530.77 KB → 530.77 KB — 2.67 MB → 2.67 MB —

Packed = gzipped .tgz published to npm. Unpacked = total extracted size. 🆕 added, 🗑️ removed. Packages from the core/ submodule are not included.
🔴 grew · 🟢 shrank — only changes of at least 512 B and 0.5% are marked.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation matches the documented scope, is disabled by default, and has comprehensive targeted coverage.

0 open findings

What changed in this PR

Adds an opt-in ARM lint rule requiring native polling metadata when polling headers are present.

Changes:

  • Implements and registers lro-missing-polling-metadata.
  • Adds comprehensive tests and documentation.
  • Registers the rule as disabled by default with Chronus entries.
File Description
.chronus/​changes/​lro-missing-polling-metadata.md Records the ARM package feature.
.chronus/​changes/​register-lro-missing-polling-metadata.md Records disabled ruleset registration.
packages/​typespec-azure-resource-manager/​README.md Lists the new rule.
packages/​typespec-azure-resource-manager/​src/​linter.ts Registers the rule.
packages/​typespec-azure-resource-manager/​src/​rules/​lro-missing-polling-metadata.md Documents behavior and examples.
packages/​typespec-azure-resource-manager/​src/​rules/​lro-missing-polling-metadata.ts Implements polling-header validation.
packages/​typespec-azure-resource-manager/​test/​rules/​lro-missing-polling-metadata.test.ts Covers qualifying, excluded, template, and deduplication cases.
packages/​typespec-azure-rulesets/​src/​rulesets/​resource-manager.ts Keeps the rule opt-in.
website/​src/​content/​docs/​docs/​howtos/​ARM/​arm-rules.md Adds the Swagger-linter mapping.
website/​src/​content/​docs/​docs/​libraries/​azure-resource-manager/​reference/​linter.md Adds the generated rule reference.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@azure-sdk-automation

Copy link
Copy Markdown
Contributor

You can try these changes here

🛝 Playground 🌐 Website

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Yuchao Yan (msyyc) added a commit that referenced this pull request Oct 15, 2026
## Summary

- Record verified absolute focused-fixture source paths during queue
preparation and reuse them in workers.
- Identify the pinned specs common-types source as
`specification\common-types`.
- Guard optional corpus archive progress directories and use exact
process status for inventory progress.
- Accept CRLF and LF fences when native tests extract actual authored
documentation snippets.
- Hash receipt captures after their producing commands complete; exclude
an inventory command's own live output.

## Observed evidence

The XMSLongRunningOperationProperty queue reused its existing
development and specs worktrees. Fixture import readiness passed, but a
worker guessed a root-level common-types source. That command failed
before validation because the actual source is
`specification\common-types`.

The same run completed its native suite, 52 focused fixture cases, and a
full corpus run. During cleanup-plan inventory, a progress probe
enumerated `plan\objects` before the archive directory existed. The
probe failed, the worker stopped the plan, and exhausted finite
command-correction budgets prevented unattended continuation.

The user later explicitly accepted the named request for one additional
corrective attempt. The coordinator preserved the original failures and
counters, verified the retained state, and authorized that one cleanup
correction. Cleanup restored the corpus baseline, and the unchanged rule
inputs completed their remaining required publication and review gates.
User-wait time remained separate from active execution. This recovery
does not grant future unattended retries.

During promotion, an LF-only Markdown fence reader found no examples in
a CRLF checkout: 20 focused tests passed and two failed. A
test-reader-only correction accepted both line endings. The original 22
tests, exact final documentation snippets, and all 496 ARM package tests
then passed.

An evidence inventory also hashed its own live stdout capture before its
final output was written. A quiescent audit found exactly one mismatch
among 27 receipts. Reprocessing the same completed receipt set restored
zero mismatches without changing any of the ten frozen promotion files
or the index.

Development draft #5704 and promotion draft #5709 now exist with
independently verified clean Copilot reviews. This PR remains separate
from both rule changes and preserves all retry, validation, publication,
and immutable-source requirements.

## Scope and validation

Only five Markdown instruction files under `.github/skills` changed. The
existing canonical branch and PR are retained; no second
process-improvement PR was created. The base remains
`Azure/typespec-azure:feature/lintdiff-migration-new`.

Before the final additions, the coordinator fetched the current
migration base and inspected newer edits in both affected instructions.
The added requirements are not already present and do not replace those
incoming edits.

Focused Prettier write/check and `git diff --check` passed. A formatter
attempt with the uninitialized worktree's configuration could not load
its config-relative TypeSpec plugin and made no changes. The corrected
invocation used the existing built formatter and its source
configuration, whose bytes match the process worktree's configuration.
No dependency installation, submodule initialization, builds, package
tests, generated output, or release metadata were required for this
documentation-only update.

---------

Copilot-Session: 8e9d80e2-2e85-4bd4-9baa-eb937b5c60f1
@msyyc
Yuchao Yan (msyyc) marked this pull request as ready for review October 15, 2026 07:45
Retain literal rule fixtures and package-isolation regressions. Published examples were independently executed and recorded as one-time session evidence; no continuous Markdown coverage is claimed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@catalinaperalta catalinaperalta left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. Some potential test additions for greater test coverage that can be done at a later time in a follow up PR:

A couple of smaller addition would strengthen the edge coverage:

  • The most useful missing regression is the intentional difference from Swagger: an operation with plain polling headers and  @extension("x-ms-long-running-operation", true)  should still warn. The implementation currently ignores that override, but no test locks down this explicitly advertised behavior.
  • A qualifying header later in a header map, after an unrelated header, to guard against accidentally stopping at the first nonmatching header.

@catalinaperalta catalinaperalta left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before merging need to check if this rule is the same name/similar logic as: #5652

@msyyc Yuchao Yan (msyyc) changed the title [Swagger Linter Migration] XMSLongRunningOperationProperty -> lro-missing-polling-metadata [Swagger Linter Migration][Superseded by #5652] XMSLongRunningOperationProperty -> lro-missing-polling-metadata Oct 20, 2026
@msyyc

Copy link
Copy Markdown
Member Author

Following up on your coverage suggestions: thanks! Both regressions are now in the canonical rule suite in #5652: an output-only LRO extension does not satisfy native polling metadata, and a qualifying polling header is recognized even after an unrelated header. Included in the consolidation commit.

@msyyc

Copy link
Copy Markdown
Member Author

Thanks for flagging the overlap with #5652. I consolidated both migrations into #5652 under the existing lro-missing-polling-metadata name. It retains both trigger policies, with one operation-level warning and one metadata lookup per eligible operation. Both PR descriptions/titles and the tracking issue now point to #5652; this PR is marked superseded and remains open for review history.

@msyyc
Yuchao Yan (msyyc) deleted the promote-lintdiff-xms-long-running-operation-property branch October 20, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

int:azure-specs Run integration tests against azure-rest-api-specs lib:azure-resource-manager Issues for @azure-tools/typespec-azure-core library linter Issues related to linter rules meta:website TypeSpec.io updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants