feat(telemetry): track which rules users disable and suppress - #1016
Merged
Conversation
Rule rejection — turning a rule off in config or silencing a finding inline — is the strongest false-positive signal we have, and until now no rule identity ever rode telemetry for it: the wide event carried only counts (scan.rulesDisabled, scan.ignoredTagCount) and the pipeline silently nulled every suppressed diagnostic. Two complementary counters, both keyed by canonicalized rule + source: - rule.disabled: once per scan per config off-switch (rules: "off" / ignore.rules). Load-bearing on its own because "off" rules are stripped from the generated oxlint config upstream and never fire, so per-diagnostic counting can never see them. - rule.suppressed: findings the diagnostic pipeline dropped per user intent (config off-switch, per-path ignore.overrides entry, inline react-doctor-disable comment), tallied inside buildDiagnosticPipeline and rolled up on the wide event as diag.suppressed*. Suppression tallies thread InspectOutput -> CachedScanPayload (schema v2 -> 3) so cache hits replay them; the public InspectResult and JSON report are untouched. Engine-owned drops (test-file auto-suppression, the library gate, ignore.files, the warnings hide) are deliberately not counted — they say nothing about the user rejecting a specific rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
commit: |
There was a problem hiding this comment.
Pull request overview
This PR adds telemetry to measure rule rejection by tracking (1) which rules users disable via config and (2) which findings get suppressed by user intent during the diagnostic filtering pipeline, while keeping the public InspectResult and JSON report unchanged.
Changes:
- Introduces
rule.disabledandrule.suppressedcounters (keyed by canonical rule + source) and threads suppression tallies through the scan payload/cache for replay on cache hits. - Extends the core diagnostic pipeline to tally user-intent suppression sites and exposes
summarizeSuppressions()viaInspectOutput. - Adds canonicalization for user-provided rule keys (
canonicalizeUserRuleKey) plus tests for canonicalization, suppression attribution, and wide-event rollups.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/react-doctor/tests/scan-result-cache.test.ts | Updates cache test expectations for new cached payload fields. |
| packages/react-doctor/tests/record-scan-metrics.test.ts | Adds tests for summarizeDisabledRules canonicalization/dedup behavior. |
| packages/react-doctor/tests/build-run-event.test.ts | Adds tests for diag.suppressed* rollups and absent-vs-zero semantics. |
| packages/react-doctor/src/inspect.ts | Threads suppressedRuleCounts into cached payload, scan metrics, and run events. |
| packages/react-doctor/src/cli/utils/scan-result-cache.ts | Persists suppression tallies in cached scan payload (schema bump support). |
| packages/react-doctor/src/cli/utils/record-scan-metrics.ts | Emits rule.disabled and rule.suppressed metrics; adds disabled-rule summarizer. |
| packages/react-doctor/src/cli/utils/constants.ts | Bumps scan cache schema version; adds new metric names. |
| packages/react-doctor/src/cli/utils/build-run-event.ts | Rolls suppression tallies into wide-event diag.suppressed* attributes. |
| packages/core/tests/merge-and-filter-diagnostics.test.ts | Adds tests validating suppression-source attribution and non-counted boundaries. |
| packages/core/src/types/index.ts | Re-exports SuppressedRuleCount type. |
| packages/core/src/types/diagnostic.ts | Introduces SuppressedRuleCount telemetry-only type. |
| packages/core/src/run-inspect.ts | Adds suppressedRuleCounts to InspectOutput and populates it from the pipeline. |
| packages/core/src/rule-key-aliases.ts | Adds canonicalizeUserRuleKey for config-key canonicalization. |
| packages/core/src/build-diagnostic-pipeline.ts | Adds suppression tallying + summarizeSuppressions() implementation. |
| .changeset/rule-ignore-telemetry.md | Publishes the telemetry feature as a react-doctor patch changeset. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Merged
rayhanadev
added a commit
that referenced
this pull request
Jul 2, 2026
Resolved one conflict in build-run-event.test.ts — both main (#1016 suppressed-rule telemetry) and this branch added an independent test after lint.droppedFileCount; kept both. Auto-merged files that both sides touched (check-reduced-motion.ts, has-ignored-path-segment.ts, neutralize-disable-directives.ts, run-inspect.ts, spawn-batches.ts, inspect.ts) verified: all --max-duration / discovery fixes and Bugbot fixes survived intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Rule rejection — turning a rule off in config or silencing a finding with an inline comment — is the strongest false-positive signal we have, and none of it was measurable: no rule identity ever rode telemetry for silenced rules.
Before:
After:
Group either counter by
rulein Sentry for the rule-rejection leaderboard; thediag.suppressed*dims make per-scan suppression rates queryable in Trace Explorer.What changed
rules: "off"entries are stripped from the generated oxlint config upstream (runners/oxlint/config.ts) — those rules never run, so only the scan-levelrule.disabledcounter can see them.ignore.rules, per-pathignore.overrides, and inline disables are enforced post-lint in the pipeline, whererule.suppressedtallies them per finding.buildDiagnosticPipelinenow counts drops at exactly the four user-intent sites via asuppress(diagnostic, source)helper and exposessummarizeSuppressions(). Engine-owned drops (test-file auto-suppression, the library gate,ignore.filespatterns, the warnings hide,textComponents/runtimeGlobalsknobs) are deliberately not counted — they say nothing about the user rejecting a specific rule.InspectOutput→CachedScanPayload(SCAN_RESULT_CACHE_SCHEMA_VERSION2 → 3) so cache hits replay the same telemetry. PublicInspectResultand the JSON report are untouched.canonicalizeUserRuleKey(inrule-key-aliases.ts, wrapping the existing canonicalizer) soreact/jsx-key, barejsx-key, andreact-doctor/jsx-keygroup under oneruleattribute.rule.fired— and pass throughbeforeSendMetricscrubbing like every other metric.Test plan
pnpm typecheck— 14/14 tasks green.ignore.filesboundary (packages/core/tests/merge-and-filter-diagnostics.test.ts),summarizeDisabledRulescanonicalization/dedup (record-scan-metrics.test.ts), wide-event rollup incl. absent-vs-zero semantics (build-run-event.test.ts)..env*gitignore (pre-existing environment trap, unrelated).pnpm lint/pnpm format:checkclean.🤖 Generated with Claude Code
Note
Low Risk
Low risk: telemetry-only paths and cache schema bump; diagnostic filtering behavior is unchanged aside from counting at existing drop sites.
Overview
Adds anonymized telemetry so rule rejection (config off-switches, ignores, overrides, inline disables) is measurable per canonical rule key.
The diagnostic pipeline now tallies user-intent drops via
suppress()andsummarizeSuppressions()(config/override/inline), while engine-owned filters stay untracked.rule.suppressedcounters anddiag.suppressed*wide-event rollups carry those tallies;rule.disabledrecords once per scan forrules: "off"andignore.rules(including rules stripped from oxlint before they fire).canonicalizeUserRuleKeygroups legacy and short rule spellings in metrics.Tallies flow through
InspectOutput,CachedScanPayload(cache schema v3), scan metrics, and run events. PublicInspectResult/ JSON reports are unchanged.Reviewed by Cursor Bugbot for commit eb41a4d. Bugbot is set up for automated code reviews on this repo. Configure here.