Engine catalog

See what Fettl actually checks.

Explore the patterns Fettl checks: hidden failures, hollow implementations, duplicated logic, and structural shortcuts.

This catalog follows the current engine source. The checks available in an installed version depend on that release.

167public checks
15categories
30Free scans, fixes included
9deterministic corrections

Public rules

Checks by failure type.

Free includes 30 scans with eligible fixes. Checks marked “automatic fix” can be corrected and verified by Fettl.

AI Prose Artifacts

2 checks
AI021-ts_ignore_without_explanation

Ts ignore without explanation

TypeScript @ts-ignore or @ts-expect-error without explanatory comment

ecosystem
AI022-rust_unwrap_in_production

Rust unwrap in production

Rust unwrap() in non-test, non-entry-point code where ? or .expect() is expected

ecosystem

AI Slop Patterns

20 checks
AI001-typescript_any_abuse

Typescript any abuse

TypeScript `any` type annotations, casts, and generic widening: the most common TypeScript AI slop pattern, indicating the generator gave up on type reasoning

ecosystem
AI002-typescript_double_cast

Typescript double cast

TypeScript `as unknown as TargetType` double-cast pattern: type laundering through `unknown` to bypass TypeScript's type-compatibility check; strongly associated with AI code generation

ecosystem
AI003-typescript_json_parse_cast

Typescript json parse cast

JSON.parse(...) cast directly to a concrete type without runtime validation: AI shortcut that skips Zod, type guards, or field-by-field checks

ecosystem
AI004-console_log_production

Console log production

console.log()/console.debug() in production JS/TS code: unstructured, unfiltered debug instrumentation that AI generators leave in after development

ecosystem
AI005-debug_labeled_console_log

Debug labeled console log

console.log calls with [DEBUG] or DEBUG: prefix: debug instrumentation labeled as temporary by the AI, committed to production

ecosystem
AI006-console_log_bracket_tag_prefix

Console log bracket tag prefix

console.log() calls whose first argument starts with a bracket-tagged domain prefix (e.g. [model], [api]): simulated structured logging that was never wired to a real logger

ecosystem
AI007-hardcoded_async_sleep

Hardcoded async sleep

await new Promise(resolve => setTimeout(resolve, N)): blind polling sleep instead of event-driven readiness; reliable AI-slop signal

ecosystem
AI008-duplicate_adjacent_comments

Duplicate adjacent comments

Consecutive identical /// doc comment lines in Rust: copy-paste artifact where an AI emits the same documentation string twice for adjacent items

ecosystem
AI009-formulaic_rustdoc_sections

Formulaic rustdoc sections

Mechanical # Returns / # Arguments / # Errors rustdoc sections in Rust that restate the type signature rather than explaining behavior

ecosystem
AI010-optional_service_field_setters

Optional service field setters

Rust structs with 3+ Option<Arc<T>> fields and set_* injector methods: bolted-on dependency injection instead of constructor injection or a builder

ecosystem
AI011-inline_require

Inline require

require() calls inside JavaScript function or method bodies: each function importing its own dependencies instead of sharing top-of-file declarations

ecosystem
AI012-same_value_fallback_constant

Same value fallback constant

Top-level JS/TS const named with a fallback keyword but sharing an identical initializer with another const: fake resilience: two names, one value, zero distinction

ecosystem
AI013-global_serverless_state

Global serverless state

declare global { var ... } and global.X = assignments in Next.js API route files: mutable global state that silently evaporates on every cold start in serverless deployments (Vercel, AWS Lambda, Netlify)

ecosystem
AI014-redundant_hasattr_self_assign

Redundant hasattr self assign

`if hasattr(obj, 'attr'): obj.attr = obj.attr`: vacuous hasattr guard with a self-assignment body; always a no-op and a strong AI slop signal

ecosystem
AI015-brittle_llm_prefix_parsing

Brittle llm prefix parsing

LLM response parsed line-by-line with `.startswith('Prefix:')`: brittle custom text protocol instead of structured output (JSON schema / function calling)

ecosystem
AI016-function_local_stdlib_import

Function local stdlib import

`import X` / `from X import Y` inside a function or method body when X is a stdlib module: AI models generate each function in isolation with its own imports instead of lifting them to module level

ecosystem
AI017-mutable_default_argument

Mutable default argument

Mutable container literal (`[]`, `{}`, `{...}`) as a function parameter default: the same object is shared across all calls; classic Python footgun common in AI-generated code

ecosystem
AI018-excessive_defensive_null_checking

Excessive defensive null checking

Stacked null guards where the second check repeats the first: AI-generated defensive stacking

ecosystem
AI019-unhelpful_error_messages

Unhelpful error messages

Verbose, vague, or non-actionable error messages that don't help diagnose the problem

ecosystem
AI020-synthetic_values

Synthetic values

Detects fabricated data, placeholder prices, synthetic API keys, dummy URLs, and placeholder return values

ecosystem

Code Hygiene

30 checks
CH001-absolute_paths

Absolute paths

Hardcoded absolute filesystem paths that break portability across machines

general
CH002-redundant_map_err

Redundant map err

Redundant .map_err(Type::from)? chains where ? would do the same From conversion

ecosystem
CH003-generic_names

Generic names

Poor variable names (temp, data, x)

general
CH004-manual_conversion_methods

Manual conversion methods

Ad-hoc pub fn as_str(&self) or pub fn from_str(...) inherent methods that should be impl Display or impl FromStr instead

ecosystem
CH005-nonstandard_constructors

Nonstandard constructors

Non-standard constructor names (build_with_*, create, make); new_* variants, type-discriminating names, mode-named operational constructors, and factory families with no new() are exempt

ecosystem
CH006-primitive_obsession

Primitive obsession

Using primitives where types belong (magic numbers, string/int pattern matching, bool proliferation)

general
CH007-print_alongside_logger

Print alongside logger

print() calls in Python files that already use a proper logger: the deferred-work pattern of reaching for print() when logging infrastructure is present

ecosystem
CH008-summary_litter

Summary litter

AI-generated status/summary/report files

general
CH009-random_scripts

Random scripts

Scattered/unorganized shell scripts (.sh, .bash, .zsh) or .py scripts

general
CH010-over_abstraction

Over abstraction

Thin wrapper functions that add no value

general
CH011-outdated_setup

Outdated setup

Outdated edition/version in setup files (Cargo.toml, pyproject.toml)

general
CH012-copy_paste_detection

Copy paste detection

Duplicated code within/across files

general
CH013-prefer_match

Prefer match

If/else chains that should use match/pattern matching

ecosystem
CH014-implicit_state_machine

Implicit state machine

Detects implicit state machines that should use enums

general
CH015-trivial_type_aliases

Trivial type aliases

Type aliases that are trivial wrappers around standard library types

ecosystem
CH016-unhelpful_expect

Unhelpful expect

Unhelpful .expect() messages that don't explain what went wrong

ecosystem
CH017-conditional_wrapper

Conditional wrapper

Functions starting with conditional early-return that hide control flow from callers

general
CH018-reexport_stubs

Reexport stubs

Rust files that contain only `pub use` re-exports with no implementation (pass-through stubs)

ecosystem
CH019-duplicate_entry_points

Duplicate entry points

Rust modules with conflicting entry points: foo/mod.rs and foo.rs coexisting, or lib.rs + main.rs both independently declaring the same module names

ecosystem
CH020-direct_exit

Direct exit

Direct process termination (process::exit, sys.exit, process.exit) called outside designated entry points: bypasses cleanup and prevents testing

general
CH021-public_test_modules

Public test modules

Test infrastructure leaked into the public API surface via pub mod tests or pub helper functions inside cfg(test)

ecosystem
CH022-pub_visibility

Pub visibility

pub items in library crates with no cross-crate consumers: over-broad visibility that should be pub(crate) or private

ecosystem
CH023-borrow_checker_evasion

Borrow checker evasion

Excessive .clone(), Arc<Mutex<T>> overuse, and dyn Trait overuse: density-based detection of borrow-checker evasion patterns common in AI-generated Rust

ecosystem
CH024-print_only_server_logging

Print only server logging

print() calls in Python server/application code (routes/, api/, agent/, handlers/, views/, endpoints/, middleware/ paths or FastAPI/Flask/Starlette/aiohttp/Django/Tornado imports) with no logging framework imported: the AI slop pattern of using print() as the sole logging mechanism in production server code

ecosystem
CH025-manual_serde

Manual serde

Functions with 5+ consecutive .get("literal_key").and_then(...) calls: manual struct field extraction that should use #[derive(serde::Deserialize)] instead

ecosystem
CH026-floating_string_statement

Floating string statement

String literal expression statements inside function bodies at non-docstring positions: evaluated and silently discarded; strong AI slop signal (misplaced LangChain-style tool descriptions)

ecosystem
CH027-bad_coding_practices

Bad coding practices

Language-specific anti-patterns: glob re-exports, fat main() (Rust); star imports, broad/bare except, type() comparisons, global mutable state, os.path.join mixed with string concat (Python); var declarations, loose equality, Function() constructor, document.write, setTimeout with string (JS/TS)

general
SS001-env_var_multi_read

Env var multi read

Environment variable names read in 3+ files without a central config module

ecosystem
SS002-duplicated_constants

Duplicated constants

Constants with identical values declared with different names across files

ecosystem
SS003-config_source_duplication

Config source duplication

Config file values hardcoded in source code instead of read from config

ecosystem

Code Smells

1 checks
CS001-type_name_dispatch

Type name dispatch

type(x).__name__ == '...' or x.__class__.__name__ == '...' string comparisons used for type dispatch: use isinstance() instead

ecosystem

Content Quality

15 checks
CQ001-hyperbolic_language

Hyperbolic language

Detects promotional language (amazing, revolutionary)

generalautomatic fix
CQ002-dead_code_markers

Dead code markers

Comments marking code as deprecated/unused

generalautomatic fix
CQ003-deferred_work

Deferred work

TODO/FIXME/hack markers

generalautomatic fix
CQ004-test_excuses

Test excuses

Excuse-making in tests (known issue, will fix later)

generalautomatic fix
CQ005-verbose_comments

Verbose comments

Overly verbose AI-generated comments

generalautomatic fix
CQ006-speculative_generality

Speculative generality

YAGNI violations (future use, placeholder, stub)

generalautomatic fix
CQ007-agent_scaffolding

Agent scaffolding

Agent workflow markers (Phase 1, Step 3, etc.)

generalautomatic fix
CQ008-copy_paste_acknowledgment

Copy paste acknowledgment

Comments that explicitly admit code is copied or duplicated from another location

general
CQ009-ai_generation_header

Ai generation header

File-level AI-generation disclaimer header (GPT, Claude, Copilot, 'generated by AI', etc.). High-precision, low-recall: fires rarely in practice but is a reliable signal when it does.

general
CQ010-decorative_separator_comments

Decorative separator comments

Decorative separator comments (── box-drawing or 20+ repeated = or - chars) used as visual section dividers instead of module decomposition

general
CQ011-cross_file_separator_density

Cross file separator density

Project-level separator density: fires when separator comments are dense relative to source file count, detecting structural AI slop invisible to per-file checks

general
CQ012-documentation_links

Documentation links

Broken relative links in Markdown documentation files

general
CQ013-hardcoded_years

Hardcoded years

Hardcoded year literals (2024–2029) that may be stale AI-generated timestamps or copyright headers

general
CQ014-hyperbolic_language_project_density

Hyperbolic language project density

Project-level hyperbolic language density: fires when a high fraction of all tracked files contain CQ001 violations, detecting systematic AI writing style invisible to per-file checks

general
CQ015-verbose_comments_project_density

Verbose comments project density

Verbose comments saturation across source files

general

Database

2 checks
DB001-json_column_abuse

Json column abuse

Structured data serialized as JSON into a single database column instead of proper relational tables: a performance and maintainability disaster

ecosystem
DB002-unsafe_sql_construction

Unsafe sql construction

SQL queries built with string formatting or concatenation (f-strings, %, .format(), +): SQL injection vector; use parameterized queries instead

general

Dead Code Detection

9 checks
DC001-dead_local_variables

Dead local variables

Wildcard bindings (let _ =) and underscore variables that suppress warnings

general
DC002-dead_fields_invariant_bool

Dead fields invariant bool

Private bool fields always initialized true, never set false, with if/else branches (the else is unreachable)

ecosystem
DC003-dead_methods

Dead methods

Methods/functions with zero callers

general
DC004-dead_structs

Dead structs

Struct/enum/class definitions that are never constructed in production code

general
DC005-dead_variants

Dead variants

Rust enum variants (match-arm-only use is dead) and Python enum members (attribute-access-only) defined but never used in production code

general
DC006-unused_trait_parameters

Unused trait parameters

Trait parameters unused in ALL implementations

ecosystem
DC007-versioned_source_file_names

Versioned source file names

Source files with a _v{N} suffix (e.g. foo_v2.py) that coexist with the base file (foo.py): AI-generated versioned duplicates instead of editing the original

general
DC008-vestigial_code

Vestigial code

Functions with stub bodies (todo!(), unimplemented!(), pass, ..., empty block) that silently do nothing when called

general
DC009-semantic_stubs

Semantic stubs

Functions with non-empty bodies that look implemented but do no meaningful work: Ok(()) returns, logging-only bodies, pass-through wrappers, hardcoded expression bodies, empty match arms, stub constructors

general

Design Slop

39 checks
DS001-blue_purple_gradient

Blue purple gradient

Blue-to-purple or purple gradients in CSS/Tailwind: the single most recognizable AI slop color move

general
DS002-default_google_fonts

Default google fonts

Overused Google Fonts (Inter, Space Grotesk, Sora, Syne, Archivo, Cormorant, Fraunces, etc.) loaded as the brand typeface

general
DS003-em_dashes

Em dashes

Em dash in body copy, headlines, and descriptions: a classic tell of AI writing

general
DS004-glowy_pill_buttons

Glowy pill buttons

Fully rounded pill buttons (border-radius: 999px) with gradient fill and soft glow or blurred drop shadow

general
DS005-gradient_headline_text

Gradient headline text

Headline words clipped to a multi-color gradient via background-clip: text

general
DS006-background_glow

Background glow

Soft radial blob of accent color bleeding from a corner or center of a dark section for 'atmosphere'

general
DS007-grid_graph_background

Grid graph background

Faint thin grid lines (often with radial mask) layered behind hero or section to look 'technical'

general
DS008-hover_boop

Hover boop

Button that lifts (translateY) or scales up on hover: a default template reflex

general
DS009-fixed_background

Fixed background

Background layer pinned with position: fixed that trails behind the whole page under everything

general
DS010-mix_blend_blobs

Mix blend blobs

Big blurred radial-gradient blobs with mix-blend-mode: multiply drifting behind content (candy aurora)

general
DS011-cool_blue_charcoal

Cool blue charcoal

Default 'serious dark product' base: cool blue-charcoal or slate-indigo ink (#0c0e15 family)

general
DS012-cream_beige_background

Cream beige background

Warm cream, bone, or beige as the 'tasteful premium' background: overused to the point of slop

general
DS013-slop_gray

Slop gray

Default UI-kit neutral gray (#f3f4f6 / #eceef2 family) as footer band, section divider, or card fill

general
DS014-underline_fill_hover

Underline fill hover

Link or button underline that grows, wipes, or travels in on hover: a reflexive 'look, it is interactive' flourish

general
DS015-default_shadow

Default shadow

Soft shadow bloomed evenly on every side of an element: the 'float everything on a fluffy cloud' look

general
DS016-inner_glow_box

Inner glow box

Bordered pill, chip, badge, or box with a glowing tinted fill inside, or a pulsing glow behind a status dot

general
DS018-countdown_timer

Countdown timer

Row of small boxes with big numbers and unit labels (DAYS/HRS/MIN/SEC) to fake urgency

general
DS019-floating_cards

Floating cards

Small cards layered over a hero that bob or float with a looping animation

general
DS020-accent_bar_card

Accent bar card

Plain dark box with a single bright accent line running down one edge

general
DS021-hairline_border_boxes

Hairline border boxes

Every card, stat box, or tile wrapped in a faint 1px light border with soft inner highlight

general
DS022-pastel_candy_gradient

Pastel candy gradient

Soft multi-stop wash of butter-yellow into peach into strawberry-milk pink, or mint-to-lavender

general
DS023-lucide_react_imports

Lucide react imports

lucide-react icon pack imported and used: the uniform thin-stroke look is a giveaway on every project

ecosystem
DS024-letterspaced_caps_everywhere

Letterspaced caps everywhere

Single letterspaced uppercase treatment used for eyebrow, button text, figure numbers, nav, and footer colophon

general
DS025-monospace_house_voice

Monospace house voice

Monospace font used reflexively for copyright lines, eyebrows, captions, and labels to signal 'technical and premium'

general
DS140-hairline_border_wide_shadow

Hairline border wide shadow

Hairline border paired with a wide diffuse shadow: a recurring generated-UI signature. Commit to one: a defined edge or a soft elevation, not both at once

general
DS141-repeating_gradient_stripes

Repeating gradient stripes

Repeating-gradient stripes used as surface decoration: a recurring generated-UI signature. Reach for a deliberate texture, or leave the surface plain

general
DS142-extreme_border_radius_cards

Extreme border radius cards

Over-rounding cards, sections, and inputs (24px and up on a small card) rounds everything into the same soft blob. Cards top out around 12 to 16px

general
DS143-all_caps_body_text

All caps body text

Long passages of uppercase body text: hard to read because all-caps removes word shape. Reserve uppercase for short labels and headings

general
DS144-gray_text_on_colored_bg

Gray text on colored bg

Gray text looks washed out on colored backgrounds. Use a darker shade of the background color, or white/near-white for contrast

general
DS145-marketing_buzzword

Marketing buzzword

Generic SaaS phrases (streamline, empower, supercharge, world-class, enterprise-grade) are instant AI tells. Pick a specific verb and noun that says what the product literally does

general
DS146-broken_or_placeholder_image

Broken or placeholder image

<img> tags with empty src, missing src, or placeholder values ship as broken-image boxes. Use real images, generated assets, or remove the tag

general
DS147-justified_text

Justified text

Justified text without hyphenation creates uneven word spacing (rivers of white). Use text-align: left for body text

general
DS148-tight_line_height

Tight line height

Line height below 1.3x the font size makes multi-line text hard to read. Use 1.5 to 1.7 for body text

general
DS149-tiny_body_text

Tiny body text

Body text below 12px is hard to read, especially on high-DPI screens. Use at least 14px for body content, 16px is ideal

general
DS150-wide_letter_spacing_body

Wide letter spacing body

Letter spacing above 0.05em on body text disrupts natural character groupings and slows reading. Reserve wide tracking for short uppercase labels only

general
DS151-bounce_elastic_easing

Bounce elastic easing

Bounce and elastic easing on interface elements feels dated and tacky. Reserve spring physics for things that are actually physical; ease interface motion out smoothly

general
DS152-layout_property_animation

Layout property animation

Animating width, height, padding, or margin causes layout thrash and janky performance. Use transform and opacity instead

general
DS153-image_hover_transform

Image hover transform

Scaling or rotating an image on hover is a recurring generated-UI signature. Let imagery sit still, or use a subtler, purposeful interaction

general
DS154-theater_framing_copy

Theater framing copy

Dismissing something as 'theater' or 'performative' is a recurring generated-copy tic. Say plainly what the thing does or does not do

general

Error Handling

2 checks
EH001-assert_runtime_validation

Assert runtime validation

assert statements used as runtime guards: silently removed by Python's `-O` flag

ecosystem
EH002-duplicate_error_display

Duplicate error display

thiserror enum variants sharing an identical display template: ambiguous log messages make errors indistinguishable in production

ecosystem

Evasion Detection

19 checks
EV001-lint_suppression

Lint suppression

Lint suppressions without fixes (# noqa, #[allow])

generalautomatic fix
EV002-manifest_lint_suppression

Manifest lint suppression

Lint rules disabled at manifest level (Cargo.toml, pyproject.toml, ESLint config)

general
EV003-test_skip_evasion

Test skip evasion

Skipped tests (@pytest.mark.skip)

ecosystemautomatic fix
EV004-redundant_error_handling

Redundant error handling

Empty/bare error handlers that do nothing

general
EV005-defensive_error_silencing

Defensive error silencing

Silent error handling that hides bugs instead of failing fast

general
EV006-silent_fallbacks

Silent fallbacks

Converting errors to defaults (.ok(), .unwrap_or_default(), catch-all match)

ecosystem
EV007-error_message_dispatch

Error message dispatch

Branching on error message text via .to_string().contains("...") instead of using typed error variants

ecosystem
EV008-map_err_to_string

Map err to string

.map_err(|e| Wrapper(e.to_string())) closures that silently erase structured error types

ecosystem
EV009-discarded_errors

Discarded errors

Error-handling code that discards original error context: .map_err(|_| ...), bare except without chaining, catch without using error

general
EV010-single_use_helpers

Single use helpers

Functions called from only one place: review for reflexive extraction to dodge complexity lints

general
EV011-empty_test_bodies

Empty test bodies

Test functions whose bodies cannot fail: empty, comment-only, let-only, trivially-true assertions, JS callbacks with no assertions

general
EV012-always_passing_tests

Always passing tests

Test functions with no assertion indicators: always pass regardless of code behavior

general
EV013-hidden_env_config

Hidden env config

Runtime env vars used as hidden behavioral toggles: possibly undocumented switches invisible to the CLI/config

general
EV014-hollow_test_density

Hollow test density

Project-level hollow test density: fires when the combined count of empty and excused test functions exceeds a threshold, exposing systematic test scaffolding

general
EV015-lint_suppression_project_accumulation

Lint suppression project accumulation

Fires when the total count of lint suppressions exceeds the configured threshold, OR when the suppression density (suppressions per KLOC) exceeds the density threshold: catching both large low-density accumulations and small dense repos

general
EV016-dict_get_complex_default

Dict get complex default

Python dict.get(key, complex_default) where default is a function call or non-trivial expression

ecosystem
EV017-nullish_coalescing_fn_fallback

Nullish coalescing fn fallback

TypeScript value ?? fn() where fallback is a function call masking absence

ecosystem
EV018-try_catch_default_return

Try catch default return

try/catch blocks returning default values on any exception type without distinguishing failure modes

ecosystem
EV019-if_let_else_hides_none

If let else hides none

if let Some/Ok with else branch returning computed default that hides why the value was absent

ecosystem

Framework Hygiene

21 checks
FH001-missing_key_in_map

Missing key in map

JSX elements rendered via .map() without a key prop: React can't track item identity for reordering, addition, or deletion

ecosystem
FH002-array_index_as_key

Array index as key

Array index used as key prop when items can reorder: index-based keys break React's reconciliation when list order changes

ecosystem
FH003-empty_deps_stale_closure

Empty deps stale closure

useEffect with empty dependency array but referencing state or props inside: captures stale values from first render

ecosystem
FH004-missing_effect_cleanup_listener

Missing effect cleanup listener

addEventListener in useEffect with no cleanup return function: event listener leak that survives unmount

ecosystem
FH005-missing_effect_cleanup_timer

Missing effect cleanup timer

setTimeout or setInterval in useEffect with no cleanup return function: timer survives unmount, causing stale callbacks

ecosystem
FH006-setstate_during_render

Setstate during render

setState called directly during render phase (not in event handler or effect): causes infinite re-render loops

ecosystem
FH009-context_value_not_memoized

Context value not memoized

Context Provider value created inline (new object every render) without useMemo: causes all consumers to re-render on every provider render

ecosystem
FH010-dialog_without_aria_label

Dialog without aria label

Dialog or role='dialog' element without aria-label or aria-labelledby: screen readers can't announce the dialog's purpose

ecosystem
FH011-input_without_label

Input without label

input element without associated label (label tag, aria-label, or aria-labelledby): inaccessible to screen readers

ecosystem
FH012-locale_independent_date

Locale independent date

Date.toLocaleDateString() or toLocaleString() called without explicit locale/timezone options: produces non-deterministic output across environments

ecosystem
FH013-suppress_hydration_warning

Suppress hydration warning

suppressHydrationWarning used as a band-aid instead of fixing the underlying SSR/client mismatch

ecosystem
FH017-incomplete_effect_deps

Incomplete effect deps

useEffect/useLayoutEffect with a dependency array that omits state or props referenced in the effect body: stale values when deps change

ecosystem
FH018-async_effect_without_guard

Async effect without guard

Async operation (fetch, await, .then) in useEffect without AbortController or is-mounted guard: state updates after unmount cause warnings and bugs

ecosystem
FH019-ssr_unsafe_window_access

Ssr unsafe window access

window.innerWidth, matchMedia, or navigator.userAgent accessed during render phase (not in useEffect): causes SSR/hydration mismatch

ecosystem
FH020-nondeterministic_key

Nondeterministic key

Key prop derived from Math.random(), Date.now(), nanoid(), or crypto.randomUUID(): key changes every render, breaking React reconciliation

ecosystem
FH021-aria_ref_nonexistent_id

Aria ref nonexistent id

aria-describedby, aria-labelledby, aria-controls, or aria-owns pointing to an ID that doesn't exist in the same file

ecosystem
FH022-error_message_without_role

Error message without role

Element with error-indicating className (error, alert, warning) without role='alert': screen readers won't announce the error dynamically

ecosystem
FH023-derived_state_in_usestate

Derived state in usestate

useState initialized from a prop or computation, but the setter is never called in the component: should be computed during render instead

ecosystem
FH024-focus_in_effect_without_guard

Focus in effect without guard

.focus() called at the top level of a useEffect body without a conditional guard: clobbers user focus on every effect run

ecosystem
FH025-incomplete_callback_deps

Incomplete callback deps

useCallback or useMemo with a dependency array that omits state or props referenced in the callback body: stale values when deps change

ecosystem
FH026-hardcoded_jsx_id

Hardcoded jsx id

JSX element with hardcoded id='stringLiteral': collides when the component renders multiple instances. Should use useId() or accept an id prop

ecosystem

Infrastructure

2 checks
IF001-linter_configuration

Linter configuration

Proper linter configuration (Clippy/Ruff) with complexity checking

ecosystem
IF002-version_consistency

Version consistency

Version strings out of sync across config files (Cargo.toml workspace members, pyproject.toml/setup.cfg/__version__, package.json monorepo)

general

Performance

3 checks
PF001-wasteful_roundtrip

Wasteful roundtrip

Transform-then-inverse patterns that waste CPU and memory (format! then slice, str then int)

general
PF002-inline_regex_compilation

Inline regex compilation

Regex::new() with a static string literal inside a function body (compiled on every call)

ecosystem
PF003-inline_glob_compilation

Inline glob compilation

GlobSetBuilder::new() or GlobBuilder::new() inside a function body: glob compiled on every call instead of once

ecosystem

Resource Loading

1 checks
RL001-module_level_open

Module level open

Module-level open() calls with cwd-relative paths: breaks at import time when the process is not started from the project root

ecosystem

Security

1 checks
SE001-security_practices

Security practices

Dangerous API scanner: unsafe Rust (with missing/vague SAFETY comment detection), eval/exec/pickle in Python, innerHTML/eval/child_process in JavaScript

general

Try Fettl

Run the checks where your code already lives.

The Free CLI stays local and requires no account. Pro adds ongoing commercial use and supported local agent hooks.

Choose what you share. Privacy notice