Skip to content

[pilot] Migrate OperationsApiSchemaUsesCommonTypes to a native TypeSpec lint - #5095

Draft
msyyc wants to merge 1 commit into
Azure:feature/lintdiff-migrationfrom
msyyc:pilot/lintdiff-operations-api-common-types
Draft

[pilot] Migrate OperationsApiSchemaUsesCommonTypes to a native TypeSpec lint#5095
msyyc wants to merge 1 commit into
Azure:feature/lintdiff-migrationfrom
msyyc:pilot/lintdiff-operations-api-common-types

Conversation

@msyyc

@msyyc msyyc commented Jul 29, 2026

Copy link
Copy Markdown
Member

What this is

A cost pilot, not a production change. It runs one bucket-C rule (validator fires, no TypeSpec lint mapping exists) end-to-end through the documented migration process so we can compare the measured effort against the 0.875–1.25 dev-day/rule estimate in the migration plan.

Rule migrated: OperationsApiSchemaUsesCommonTypes (ARM, severity error, RPC-Operations-V1-01, 84 firings across 450 compiled projects in the ARM coverage breakdown).

Harness result: Covered (TSP lint): 2 · Remaining unresolved gaps: 0 · exit 0. Spot-checked LroErrorContent for regressions — still green.

node --import tsx/esm test/harness/validate.ts OperationsApiSchemaUsesCommonTypes

Changes

  • New rule tsp-lintdiff-local-linter/operations-api-schema-uses-common-types (~65 LOC) + registration in linter.ts. Any ARM GET whose route ends in /providers/<...>/operations must return a model that is named OperationListResult and marked as an ARM common type (isArmCommonType).
  • Two violating fixtures. The pre-existing custom-operations-schema fixture used route /customOperations, which can never match the validator selector /providers/\w+\.\w+/operations$ — it produced zero validator diagnostics and proved nothing. It is retained as the compliance case with reviewed ambient diagnostics.
    • custom-operations-list-result — hand-written /providers/<ns>/operations route returning a service-defined list result. TypeSpec already reports missing-operations-endpoint here, so this shape was arguably covered already.
    • legacy-operations-custom-responseAzure.ResourceManager.Legacy.Operations<ArmResponse<CustomOpListResult>>. This satisfies missing-operations-endpoint and produced no TypeSpec diagnostics at all before this change. This is the clean gap that justifies the native rule.
  • Harness dependencies declared. packages/typespec-lintdiff was missing @microsoft.azure/openapi-validator-core, lodash, yaml, @azure-tools/typespec-azure-rulesets, and @azure-tools/typespec-client-generator-core; the validate harness could not start without them. The lockfile had no entry for this package at all, hence the large pnpm-lock.yaml diff.
  • rule.md rewritten with source-of-truth notes, both authorable shapes, deliberate parity deltas, and the test-case table.

Finding worth discussing

context.reportDiagnostic({ target: operation }) is silently dropped when the operation comes from a template instantiation (interface Operations extends Azure.ResourceManager.Legacy.Operations<...>), because the operation's node lives in library source. The rule was firing internally and emitting nothing; targeting the user-declared interface fixes it.

This may be a systemic cause of the very low real-world parity on several already-migrated rules — XmsResourceInPutResponse (391 validator firings vs 2 lint firings, 0.5%), PaginationResponse (1.2%), TagsAreNotAllowedForProxyResources (5.1%), ConsistentPatchProperties (8.3%) — all concern shapes that normally arrive via ARM templates. Worth auditing before re-estimating the "already migrated" bucket; it could be a cheap bulk fix rather than ~25 re-implementations.

Measured effort

Phase Plan estimate Measured
Environment bring-up not budgeted ~50 min
1 Validator analysis 0.06–0.13 d ~20 min
2 TypeSpec-first design 0.06–0.13 d ~25 min
3 Implementation 0.25 d ~15 min
3b Debugging the dropped diagnostic not budgeted ~35 min
4 Testing + coverage validation 0.25–0.5 d ~30 min
5 Documentation 0.25 d not donerule.md only; website doc + ruleset promotion outstanding

Phases 1–4 ≈ 2.1 h ≈ 0.26 dev-day of hands-on work. This does not refute the 0.875–1.25 dev-day figure: the untested residual is website docs, promotion out of the staging package, PR review rounds and ARM sign-off — and two real costs sat entirely outside the estimate model (environment bring-up, compiler-behaviour debugging).

Other observations

  • A fixture that failed to compile was reported as a passing COMPLIANT case, so coverage totals can be inflated by non-compiling fixtures.
  • The harness needs an external clone of azure-openapi-validator via LINTDIFF_VALIDATOR_ROOT; this is not documented.
  • docs/ONBOARDING.md is stale: it references tests/, scripts/validate.ts, and RULE_ID_MAP/TSP_DIAGNOSTIC_MAP, none of which exist any more (now test/fixtures/, test/harness/validate.ts, and rule.md frontmatter).
  • Of the 37 rules in the coverage gist's "needs migration" bucket, 3 are already classified blocked/suppression-dependent and 6 template-enforced by the repo's own intake artifacts, so that bucket looks overstated.

Not done

Website documentation, promotion into an official ruleset, rule category assignment, and release notes — i.e. the plan's phase 5 and "Review and Integration" steps.

Pilot migration of the ARM validator rule OperationsApiSchemaUsesCommonTypes
(RPC-Operations-V1-01) to a native TypeSpec lint, run end-to-end through the
documented migration process to measure the real per-rule cost.

- Add rule operations-api-schema-uses-common-types and register it.
- Add two violating fixtures. The pre-existing custom-operations-schema fixture
  used route /customOperations, which can never match the validator selector
  /providers/<ns>/operations$, so it proved nothing; it is kept as the
  compliance case with reviewed ambient diagnostics.
- legacy-operations-custom-response is the clean gap: Legacy.Operations<Response>
  with a service-defined response satisfies missing-operations-endpoint and
  previously produced no TypeSpec diagnostics at all.
- Declare harness dependencies that were missing from package.json
  (openapi-validator-core, lodash, yaml, typespec-azure-rulesets,
  typespec-client-generator-core) so the validate harness runs.

Note: diagnostics targeting an operation that comes from a template
instantiation are silently dropped because the operation node lives in library
source, so the rule targets the user-declared interface instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5895e2fa-70fa-42e0-9443-b35905ca584c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant