---
id: 03-no-undeclared-rate-limit-eslint-rule
epic: security-headers-rate-limit-sbom
title: "`no-undeclared-rate-limit` ESLint rule"
type: technical-story
status: done
feature: core-eslint
depends-on: [01-rate-limit-type-primitives]
blocks: [05-auth-signin-rate-limit-backfill]
created: 2026-05-20T00:00:00Z
updated: 2026-05-20T08:45:03.170Z
---

## Goal

Add the `no-undeclared-rate-limit` ESLint rule (warn severity) that catches `rateLimit.consume("X", _)` calls where `"X"` is not declared in the manifest's `rateLimit` array, and declared budget names that are never consumed in the use-case body — giving AI agents and developers lint-time enforcement of rate-limit drift.

## Why

Without a lint rule, an agent could add a `consume("foo", ...)` call that has no corresponding manifest declaration, or declare a budget that silently goes unused. The ESLint rule closes this gap at the same latency as the existing `no-undeclared-audit` and `no-undeclared-consent-check` rules, making the rate-limit channel structurally consistent with the rest of the conformance system.

## Done when

- `packages/core-eslint/rules/no-undeclared-rate-limit.js` is registered at warn severity in `plugin.js` + `base.js`.
- `packages/core-eslint/rules/_manifest-ast.js` parser extracts the `rateLimit` field from a feature manifest.
- RuleTester fixtures cover: matching `budgetName` in call and manifest (pass), `budgetName` in call absent from manifest (warn), declared budget never consumed in use-case body (warn), non-use-case file (no-op).
- Prior-art shape mirrors `no-undeclared-audit.js` and `no-undeclared-consent-check.js`.
- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass.

## In scope

- `no-undeclared-rate-limit.js` rule implementation.
- `_manifest-ast.js` extension for `rateLimit` field extraction.
- Rule registration in `plugin.js` + `base.js` at warn severity.
- RuleTester fixtures (declared/undeclared/unused/non-use-case).

## Out of scope

- CLAUDE.md + `conformance-quickref.md` rule-count bump (12 → 13) — Story 11.
- `withRateLimit` wrapper — Story 04.
- Auth backfill applying the rule — Story 05.

## Tasks

- [x] Add `no-undeclared-rate-limit` rule in `packages/core-eslint/rules/no-undeclared-rate-limit.js` (warn severity); extend `packages/core-eslint/rules/_manifest-ast.js` to extract the `rateLimit` field; register the rule in `plugin.js` + `base.js`; add RuleTester fixtures: declared budget + matching call (pass), undeclared budget name in call (warn), declared budget never consumed (warn), non-use-case file (no-op); all gates pass.
