CR

create-linter-rule

Standard procedure for authoring TypeSpec linter rules.

Install

mkdir -p .claude/skills/create-linter-rule && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10163" && unzip -o skill.zip -d .claude/skills/create-linter-rule && rm skill.zip

Installs to .claude/skills/create-linter-rule

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Create a new TypeSpec linter rule, lint diagnostic, or design guideline checker with a TDD approach, including implementation, tests, documentation, ruleset registration, and changeset. Use this skill when asked to create, add, implement, or write a new linter rule, lint warning, validation rule, or design guideline enforcement for TypeSpec Azure libraries.
359 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Implement linter rules
  • Write validation tests
  • Add documentation
  • Register rulesets

How it works

Follows a TDD approach to scaffold, implement, test, and register new TypeSpec linter rules.

Inputs & outputs

You give it
Linter rule requirements
You get back
Implemented linter rule and tests

When to use create-linter-rule

  • Implement new linter rule
  • Write TypeSpec validation guideline
  • Add lint diagnostic check

About this skill

Create a TypeSpec Linter Rule

Follow these steps in order to create a complete, high-quality linter rule.

Step 1: IDENTIFY

Determine the rule metadata before writing any code:

  • Package: typespec-azure-core (data-plane rules), typespec-azure-resource-manager (ARM rules), or typespec-client-generator-core (client SDK generation rules)
  • Rule name: Must follow these naming conventions:
    • Use kebab-case (lowercase letters, numbers, hyphens)
    • DO NOT include the package name in the rule ID (the package is already part of the fully-qualified diagnostic code)
    • Use no-<thing> when the rule bans a construct or usage (e.g., no-nullable, no-enum, no-format)
    • Use use-<preferred-thing> when the rule points users to a standard/preferred TypeSpec pattern (e.g., use-standard-operations, use-extensible-enum)
    • Keep names short and concise — prefer no-enum over no-enum-type-usage
  • Severity: All linter rules are warnings; no severity choice is needed
  • Target ruleset(s): data-plane, resource-manager, or both
  • Description: One-line explanation of what the rule enforces

Choose the package by scope:

  • Decorators from typespec-client-generator-core, or rules only about client SDK generation → typespec-client-generator-core
  • Rules specific to ARM APIs → typespec-azure-resource-manager
  • Rules that apply to both data-plane and ARM, or only data-plane → typespec-azure-core

Step 2: SCAFFOLD

Generate all required files using the repo's scaffolding tool:

pnpm create:linter-rule < rule-name > --package < azure-core | azure-resource-manager | client-generator-core > --description "<description>"

This creates:

  • packages/<pkg>/src/rules/<rule-name>.ts — rule skeleton
  • packages/<pkg>/test/rules/<rule-name>.test.ts — test skeleton
  • packages/<pkg>/src/rules/<rule-name>.md — docs skeleton (referenced from the rule via docs: fileRef.fromPackageRoot(...))
  • Updates packages/<pkg>/src/linter.ts — registers the rule

Step 3: WRITE FAILING TESTS FIRST (TDD)

Edit packages/<pkg>/test/rules/<rule-name>.test.ts:

  1. Write tests for valid code that should produce no diagnostics (.toBeValid())
  2. Write tests for invalid code that should produce specific diagnostics (.toEmitDiagnostics())
  3. Create equivalence classes for the input and write tests covering at least one instance of each class:
    • Group inputs by how the rule handles them (e.g., for a rule targeting ModelProperty):
      • Simply defined properties
      • Properties defined using spread or is
      • Properties inherited from a base class
    • Add boundary conditions specific to the rule logic (e.g., for a name-prefix rule):
      • Properties with the forbidden prefix
      • Properties with the prefix text in the middle or end of the name
      • Properties with names shorter than the prefix
  4. Always include at least one test verifying that library types in Azure.Core and Azure.ResourceManager are not subject to the rule

Test API reference:

// No diagnostics expected
await tester.expect(`model Foo {}`).toBeValid();

// Specific diagnostic expected
await tester.expect(`model foo {}`).toEmitDiagnostics([
  {
    code: "@azure-tools/typespec-<pkg>/<rule-name>",
    severity: "warning",
    message: "Expected message text",
  },
]);

// Test code fix (if rule provides one)
await tester
  .expect(`enum Color { red }`)
  .applyCodeFix("fix-id")
  .toEqual(`union Color { string, red: "red" }`);

Verify tests fail before implementing:

pnpm --filter "@azure-tools/typespec-<pkg>..." build
pnpm --filter "@azure-tools/typespec-<pkg>..." test

Step 4: IMPLEMENT THE RULE

Edit packages/<pkg>/src/rules/<rule-name>.ts:

  • Implement visitor logic in the create(context) function
  • Return a SemanticNodeListener with the appropriate hooks:
    • model — for model-level checks
    • modelProperty — for property-level checks
    • operation — for operation-level checks
    • enum — for enum checks
    • namespace — for namespace-level checks
    • interface — for interface checks
    • union / unionVariant — for union checks
  • Use context.reportDiagnostic({ target }) to report violations
  • Use paramMessage from @typespec/compiler for interpolated messages
  • Add codefixes array to reportDiagnostic() if a fix is possible

Step 5: VERIFY TESTS PASS

pnpm --filter "@azure-tools/typespec-<pkg>..." build
pnpm --filter "@azure-tools/typespec-<pkg>..." test

All tests should pass. If not, fix the implementation until they do.

Step 6: REGISTER IN RULESETS

Add the rule to the appropriate ruleset(s):

  • Data-plane rules: Edit packages/typespec-azure-rulesets/src/rulesets/data-plane.ts
  • ARM rules: Edit packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts

TCGC rules generally go in both rulesets. Rules in typespec-azure-core that apply to both ARM and data-plane specs also go in both rulesets. Only rules that are truly ARM-specific go exclusively in resource-manager.ts; otherwise, add the rule to both resource-manager.ts and data-plane.ts as appropriate.

Add an entry: "@azure-tools/typespec-<pkg>/<rule-name>": true,

Every rule MUST be explicitly listed (enabled or disabled). The validate-rules-defined.test.ts test will fail otherwise.

Verify:

pnpm --filter "@azure-tools/typespec-azure-rulesets..." build
pnpm --filter "@azure-tools/typespec-azure-rulesets..." test

Step 7: WRITE DOCUMENTATION AND REGENERATE DOCS

Edit packages/<pkg>/src/rules/<rule-name>.md:

  • Replace all placeholder text
  • Write a clear description of what the rule checks and why
  • Provide realistic ❌ Incorrect and ✅ Correct examples using actual TypeSpec patterns
  • Add a required ## Impact section: a bulleted **Area:** line (e.g. API, SDK, Emitters) followed by a short paragraph describing what breaks or degrades when the rule is violated
  • Add a required ## Suppression section: state whether suppression is acceptable and why, and point to the correct fix. Suppressions use the #suppress "<rule-id>" "<justification-string>" directive placed on the line above the target — NOT a // suppress comment
  • Add a ## LintDiff Equivalent section when an equivalent LintDiff (azure-openapi-validator) rule exists, deep-linking to the specific rule id in the OpenAPI authoring automated guidelines. Omit only when no equivalent exists
  • This file holds only the extended documentation body — the page title, rule id, and short description are generated from the rule definition. tspd renders the page at website/src/content/docs/docs/libraries/<pkg>/rules/<rule-name>.md
  • For ARM rules, also update the alphabetized rule table in website/src/content/docs/docs/howtos/ARM/arm-rules.md with the documentation URL, LintDiff equivalent, and impact. This table is authored manually and is not updated by regen-docs.

Then regenerate the library's reference docs (updates the rule listing):

pnpm --filter "@azure-tools/typespec-<pkg>..." build
pnpm --filter "@azure-tools/typespec-<pkg>" regen-docs

Step 8: CREATE CHANGESET

pnpm change add

When prompted:

  • Select change kind: feature (new rule) or fix (bugfix to existing rule)
  • Select affected package: @azure-tools/typespec-<pkg>
  • Write a concise description: "Add <rule-name> linter rule that <what it does>"

Step 9: FINAL VALIDATION

Run the general pre-PR validation to ensure everything is ready:

pnpm validate:pr

This checks: branch is up to date, build passes, tests pass, lint passes, format is clean, spelling is clean, regen-docs is clean, changeset exists, and diff only contains expected files.

If any check fails, fix the issue and re-run. Use pnpm validate:pr --fix to auto-fix formatting and lint issues.

Step 10: EXTERNAL INTEGRATION CHECK

New linter rules MUST NOT break existing Azure service specs. After pushing your PR:

  1. Apply the int:azure-specs label to the PR to trigger the External Integration check: gh pr edit --add-label "int:azure-specs"
  2. If the agent cannot apply the label automatically, tell the user to apply the int:azure-specs label manually in the GitHub UI
  3. After applying the label, the External Integration workflow will start. Monitor it via gh run list --workflow=external-integration.yml
  4. This workflow packages your changes and runs TypeSpec validation against all specs in Azure/azure-rest-api-specs
  5. Wait for the check to pass before requesting review

If the check fails, your rule produces diagnostics on existing specs. To resolve:

  1. Apply an API-neutral fix to the spec (preferred): If the fix doesn't change API behavior, submit a PR to Azure/azure-rest-api-specs on the main branch.
  2. Suppress the rule: If the spec cannot be fixed without changing API behavior, add a #suppress "<rule-id>" "<justification-string>" directive on the line above the target (NOT a // suppress comment). Suppressions always go to the main branch.
  3. Fix on typespec-next: If the fix requires unreleased TypeSpec APIs or behavior, submit to the typespec-next branch (uses nightly builds).
  4. Link your spec fix PR: Always link the spec fix PR in your linter rule PR description.

The External Integration workflow:

  • Builds and packs all typespec-azure packages from your PR
  • Checks out Azure/azure-rest-api-specs (main branch)
  • Patches in your packaged changes
  • Runs tsp-integration azure-specs --stage validate
  • Checks for unexpected git changes

Important Notes

  • Import extensions: Always use .js extensions in imports (e.g., from "./rules/my-rule.js")
  • Rule URL: Must match https://azure.github.io/typespec-azure/docs/libraries/<pkg>/rules/<rule-name>

Content truncated.

When not to use it

  • General TypeSpec development
  • Non-Azure libraries

Prerequisites

TypeSpec

Limitations

  • Requires TypeSpec environment
  • Strict registration rules

How it compares

It provides a specific scaffolding and TDD workflow for Azure-specific TypeSpec libraries.

Compared to similar skills

create-linter-rule side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
create-linter-rule (this skill)02moReviewAdvanced
python-testing-patterns772moReviewIntermediate
dependency-upgrade265moReviewIntermediate
test-cases577moNo flagsBeginner

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry