--- 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.