Skip to content

All rules

One row per rule, every package, generated from each plugin’s exported metadata. Each rule links to its page, which has the options it takes and the Incorrect and Correct examples that run in that package’s test suite. Use the site search (⌘ K) or your browser’s find on this page. The same index is served as JSON at /eslint-plugins/rules.json for tools and agents; the shape is documented below.

113 rules across 11 ESLint plugins and 1 lint-meta catalog. Of the 85 ESLint rules, 66 are switched on by a recommended preset, 9 ship registered but off, and 10 are left out of every preset and enabled one by one. The 28 lint-meta rules run under @noctcore/harness, not ESLint. 2 rules read type information: noctcore-contracts/translation-key-exists (optional), noctcore-prisma/mutation-entry-must-reach-audit (required).

RuleWhat it reportsPresetFixTypes
noctcore-react/component-props-namingA function component's first-param props type must be named <Component>Props. Autofixes by renaming the in-file type declaration and its references when that is safe.error🔧
noctcore-react/context-value-must-be-memoizedA context Provider value must not be an inline object literal — a fresh reference each render re-renders every consumer. Memoize it with useMemo and pass the stable reference.error
noctcore-react/max-hook-return-surfaceAn exported use* hook in a hook file may return object literals (top-level or nested one level) of at most max (default 20) members. Wider returns are god-controllers in the making.error
noctcore-react/max-hooks-per-fileA hook/query/mutation file (default *.hooks.ts / *.queries.ts / *.mutations.ts) may export at most max (default 4) use* hooks. Split larger files.error
noctcore-react/max-props-per-componentA *Props interface/type-literal may declare at most max (default 12) local members. extends clauses are not counted.error
noctcore-react/no-effect-derived-stateAn effect whose entire body is setState calls with values derived purely from its dependencies is the "you might not need an effect" anti-pattern — compute the value during render (or with useMemo) instead.error
noctcore-react/no-jsx-computationDisallow array methods, arithmetic, and chained logical expressions directly inside JSX {...}. Lift them to a const above the return or into the hook.error
noctcore-react/no-jsx-in-hooksA use*-named function must not return JSX — that is a component wearing a hook costume. Rename it to a PascalCase component, or return values instead of elements.error
noctcore-react/no-prop-drillingA bundle of maxForwarded+ (default 4) props each forwarded unchanged (name={name}) from the *Props param to the same child component is prop drilling. Compose instead.error
noctcore-react/no-state-in-component-bodyState/effect/query hooks must live in the colocated hook file (<Name>.hooks.ts), not in the component .tsx body. The component is a thin shell.error
noctcore-react/no-unguarded-web-storageA localStorage / sessionStorage call must sit inside a try block: the access itself throws when storage is disabled, partitioned or full.error💡
noctcore-react/prefer-lazy-state-initA useState initializer that is an expensive call (default JSON.parse / localStorage.getItem / sessionStorage.getItem, or a configured builder) must be wrapped in a function so it runs once on mount, not every render.error🔧
noctcore-react/props-must-be-visualComponent props must be visual. Auth/business-identity and credential prop names (userId, currentUser, *token*, *jwt*, *secret*, apiKey, ...) are disallowed. A live password input is a legitimate visual concern and is intentionally allowed.error
noctcore-react/require-effect-cancellationA setState/dispatch that runs after an await or .then() inside a useEffect must be guarded against a unmounted/re-run effect (AbortController, a cancelled flag, or a cleanup return).error
noctcore-architecture/barrel-purityA barrel (index.ts / index.tsx) must contain only re-exports — never local declarations, side effects, or default-exported values.error
noctcore-architecture/colocated-test-requiredA source file matching an include glob must have a colocated *.test.* / *.spec.* sibling on disk.off
noctcore-architecture/component-folder-structureA component <Name>/<Name>.tsx under <componentRoot>/<feature>/... must have its sibling set (.hooks.ts, .types.ts, .stories.tsx, .test.tsx, index.ts) present on disk.error
noctcore-architecture/filename-matches-exportA file's basename must match its primary export (a default export, or the sole named export).error💡
noctcore-architecture/index-must-reexport-defaultA component folder's index.ts must re-export the component default (export { default as <Name> } from './<Name>').error
noctcore-architecture/max-import-depthA relative import may not climb more than max parent levels (default 3). Autofixed to a path alias when one is configured.error🔧
noctcore-architecture/no-cross-feature-importsA file in one feature may not import runtime code from another feature. Move shared code to a shared module or a shared feature.error
noctcore-architecture/single-semantic-moduleRequire each module to export only one semantic concern (types, constants, functions, classes, components, hooks, schemas or enums).off
noctcore-monorepo/no-deep-package-importsWorkspace packages in the configured scopes must be consumed through their package barrel only — never via a deep subpath into package internals.off
noctcore-monorepo/no-unexported-subpath-importImporting a @scope/pkg/<subpath> that the target workspace package's exports map does not expose. Reads the target package.json from disk.off
noctcore-contracts/env-var-schema-parityRequire every process.env.FOO / import.meta.env.FOO key to be declared in a schema file (.env.example or a zod-env module), so config access and config declaration cannot drift apart.off
noctcore-contracts/fetch-must-check-okRequire a fetch response to be checked with .ok or a status comparison before .json() parses its body.error
noctcore-contracts/money-must-be-decimalDisallow monetary values typed as the JS primitive number. Money-named fields explicitly typed : number lose precision to float rounding; use a Decimal money type instead.error
noctcore-contracts/no-direct-process-envDisallow direct process.env access. Force every consumer through a typed, validated config accessor so a missing variable fails at boot, not at use.error
noctcore-contracts/no-error-stringifyDisallow stringifying an error with bare ${error} interpolation, error.toString(), or error + "". These drop the cause chain. Use error instanceof Error ? error.message : String(error) instead.error
noctcore-contracts/require-error-causeRequire re-thrown errors inside a catch to forward the caught error as { cause }. A throw new SomeError(...) that omits the cause severs the chain to the original failure.error🔧
noctcore-contracts/require-registered-keysRequire the key/name argument of configured sink APIs (storage, event channels, cache keys) to be an imported constant from a registry module, not a raw string literal.off
noctcore-contracts/require-schema-parse-at-boundaryDisallow asserting external boundary data with as T instead of parsing it at runtime. Flags casts of JSON.parse, res.json(), web storage, URL search params, message-event data and LLM tool input, directly or through a const; use a zod/valibot parse.off
noctcore-contracts/restrict-throw-to-taxonomyRestrict throw to an approved error taxonomy. Flags throwing a non-allowlisted error class and throwing a non-Error value (string, object, number, ...).error
noctcore-contracts/schema-enum-field-consistencyDisallow a zod field that is an enum in one object schema of a module from being z.string() in another, which widens the wire type every consumer then narrows by hand.error
noctcore-contracts/translation-key-existsRequire every static i18next / react-i18next translation key (t(...), i18n.t(...), <Trans i18nKey>) to exist in the catalog of the namespace in scope.offoptional
noctcore-contracts/wire-message-namingA message-schema const ending in a role suffix (default Event/Command/Query) whose zod object declares type: z.literal(...) must set that literal to kebab-case(const name minus its role suffix).error🔧
noctcore-contracts/zod-schema-namingEvery exported zod schema is a PascalCase const suffixed Schema, paired with a same-named inferred type (export type Foo = z.infer<typeof FooSchema>).error
noctcore-code-quality/fake-timers-must-be-restoredA test file that calls useFakeTimers() must also call useRealTimers(), so fake timers do not leak into later tests.error
noctcore-code-quality/interface-prefix-iInterface names must be prefixed with I followed by an uppercase letter. Module/global augmentations are exempt.not listed
noctcore-code-quality/no-bare-date-nowDisallow bare Date.now() / new Date() in business logic. Read wall-clock time through a shared clock util (nowMs() / now()) so time is mockable.error
noctcore-code-quality/no-conditional-expectDisallow expect() inside a branch, catch or loop that may not run: a skipped assertion lets a broken test pass.error
noctcore-code-quality/no-elided-code-commentsDisallow comments that stand in for elided code ('// ... existing code ...', '// rest of the function unchanged', '// your code here'). They are what an agent leaves when it rewrites a file from an abbreviated draft, and the code they replaced has usually been deleted.error
noctcore-code-quality/no-focused-testsBan focused tests (it.only / describe.only / test.only, fdescribe / fit / ddescribe) so a focused test never silently lands in CI.error
noctcore-code-quality/no-historical-commentsDisallow comments that frame code relative to what it used to do or to a past incident ('before the fix', 'after the refactor', 'we used to', 'no longer'). Source comments describe the current invariant; history belongs in the commit message or PR description, where it does not rot when the code changes again.error
noctcore-code-quality/no-narration-commentsDisallow narrative comments like 'Here we...', 'Now we...', 'First, we...'. These read as step-by-step prose and add no information a future reader cannot get from the code itself. Often a tell that the comment was generated by an agent describing its own changes.error
noctcore-code-quality/no-pr-reference-commentsDisallow PR/issue references in comments. They belong in commit messages and PR descriptions, where they do not rot when the repo moves, the issue tracker migrates, or the numbering changes.error
noctcore-code-quality/no-process-exitDisallow process.exit() outside bootstrap/shutdown paths and standalone CLIs. Application and service code must throw or reject so the lifecycle can shut down gracefully.error
noctcore-code-quality/no-real-network-in-unit-testsUnit tests must not perform real network I/O: mock the HTTP client, or move the test to an integration suite.error
noctcore-code-quality/no-swallowed-assertionDisallow assertions inside a try whose catch neither rethrows nor asserts, and .catch() handlers that swallow an expect(...).rejects/.resolves failure: the assertion fails, the error is dropped, and the test passes.error
noctcore-code-quality/no-template-trim-empty-ternaryDisallow inline <template>.trim() === '' ? fallback : <template>.trim() patterns. Extract to a named utility so the expression is built once and is unit-testable in one place.not listed
noctcore-code-quality/no-vacuous-expectDisallow vacuous expects (typeof checks, literal tautologies, a sole toBeDefined/toBeTruthy): a test must assert behaviour that a real regression would break.error
noctcore-code-quality/prefer-early-returnPrefer guard clauses (early return) over wrapping the whole function body in a multi-statement if without an else.error
noctcore-code-quality/skipped-tests-need-trackingSkipped tests (.skip / .fixme / xit / xdescribe) must carry a tracking marker (an issue URL or TODO(@owner)) on or above the line, so the debt has an owner instead of rotting silently.error
noctcore-async-safety/forward-abort-signalA function that accepts an AbortSignal (param named signal or typed AbortSignal) but awaits a call without ever forwarding it leaves that work uncancellable.error
noctcore-async-safety/no-concurrent-shared-mutationA read-modify-write of an outer-scope binding inside a concurrent Promise.all(arr.map(async …)) callback can lose updates.error
noctcore-async-safety/no-leaky-race-timeoutA setTimeout timeout raced with Promise.race must be cleared — otherwise the timer outlives the race whenever the other promise wins.error
noctcore-async-safety/no-shared-mutable-module-stateA module-scoped mutable binding written inside an exported async/handler function is shared across concurrent requests. Opt in per file via include.error
noctcore-async-safety/prefer-parallel-awaitsConsecutive independent const x = await read() statements can run concurrently via await Promise.all([...]).off💡
noctcore-async-safety/require-client-timeoutA configured network client must be constructed with a timeout option; an unbounded client can hang forever.error
noctcore-async-safety/require-fetch-timeoutA fetch (or configured wrapper) call must carry a signal/timeout in its options — an unbounded request can hang forever.error💡
noctcore-observability/audit-pii-declaredA PII-shaped key written into an audit payload must be declared, either registered for scrubbing on purge or declared non-PII.error
noctcore-observability/no-error-detail-lossA catch block that reports a failure must not reduce the error to only e.message / String(e) / ${e} — log the error itself so its stack and cause survive.error
noctcore-observability/no-sensitive-fields-in-logsA logger call must not reference an identifier, property, or object key whose name matches a sensitive-field denylist (password, token, secret, ...). Redact it before logging.error
noctcore-observability/structured-log-argumentsA logger call must not interpolate dynamic values into the message string (a template literal with expressions). Pass a static message and a structured context object instead.error
noctcore-security/no-shell-interpolationA dynamically-interpolated command string must not flow into a shell runner (exec/execSync, or spawn/execFile with shell: true). Pass the program and arguments separately.error
noctcore-security/no-timing-unsafe-compareAn HMAC digest or signature must not be compared with === / !== / == / != or Buffer#equals; use crypto.timingSafeEqual.error
noctcore-security/no-user-controlled-fetch-urlDisallow HTTP requests whose origin is not fixed at authoring time: a runtime-controlled host enables SSRF.error
noctcore-security/no-user-controlled-redirectDisallow redirects whose target origin is not fixed at authoring time: a user-controlled target is an open redirect.error
noctcore-security/require-path-containmentRequest-shaped input (req.*) passed directly into path.join / path.resolve without a containment guard is a path-traversal sink.not listed
noctcore-security/require-sanitized-htmlHTML reaching dangerouslySetInnerHTML, innerHTML, outerHTML or insertAdjacentHTML must be static markup or pass through a configured sanitizer.not listed
noctcore-security/server-action-through-clientIn a 'use server' module every exported action must be built from a configured action client; a raw exported function bypasses input validation, error shaping and middleware.not listed
noctcore-prisma/mutation-entry-must-reach-auditRequire a mutation entry point whose type-resolved call graph reaches a Prisma write to also reach an audit write. Reports only when the whole graph was read.not listedrequired
noctcore-prisma/no-audit-write-in-transactionDisallow an audit-log write inside a $transaction callback, where it rolls back with the business work. It does not check that mutations are audited at all.error
noctcore-prisma/no-cross-tenant-id-in-whereDisallow a Prisma query or write whose tenant id in where or data is read from client input. Without row-level security, a client-supplied tenant id is a cross-tenant access (IDOR).error
noctcore-prisma/no-raw-sql-outside-allowlistDisallow raw Prisma SQL ($queryRaw, $executeRaw, $queryRawTyped, *Unsafe) that can touch a table outside an allowlist. A raw query has no model, so a tenant-scope extension passes it through unscoped.error
noctcore-prisma/no-request-body-in-writeDisallow a Prisma write whose data (or where) is the raw request body. Passing client input straight through lets a caller set any column (mass assignment) or widen the filter.error
noctcore-prisma/no-unscoped-prisma-outside-allowlistDisallow the unscoped Prisma client and tenant-scope escape-hatch functions outside an allowlist of seeds, migrations, isolation tests, and designated system files.error
noctcore-prisma/prisma-tx-uses-tx-not-clientInside a $transaction(async (tx) => ...) callback, writes must go through the tx parameter, not the outer client, so they participate in the transaction and roll back together.error
noctcore-prisma/prisma-write-in-transactionDisallow two or more Prisma writes in a single function/method body that are not wrapped in a $transaction. A partial failure between writes leaves half-written state.error
noctcore-prisma/restrict-model-writesRestrict Prisma writes to configured models (or to configured columns of them) to the files that own those writes, including nested relation writes. It fences who writes; it does not validate which values or state transitions are legal.not listed
noctcore-prisma/soft-deletable-tables-require-deleted-atRequire a filtered read or bulk write on a soft-deletable Prisma model to exclude soft-deleted rows in its where. Soft delete is convention only, with no Prisma extension injecting the filter, so a query that omits it reads and mutates deleted rows.not listed
noctcore-prisma/tenant-scoped-tables-require-whereRequire every tenant field in the where of a read or bulk write on a tenant-scoped model through the unscoped client, and a scope column in the where of any query on a hand-scoped model.not listed
noctcore-prisma/tenant-write-must-carry-tenant-idRequire every tenant field in the data of a create on a tenant-scoped Prisma model through the unscoped client, which does not inject the tenant scope.not listed
noctcore-rsc/no-navigation-throw-in-tryDisallow redirect(), notFound() and the other throwing next/navigation calls inside a try whose catch swallows the error instead of rethrowing it.error
noctcore-llm/no-llm-output-to-sinkText an LLM SDK call returned must not reach eval, a shell, raw SQL, HTML injection, a fetch origin or an fs path in the same function without being validated or sanitized first.error
lint-meta-rules/agents-doc-presenceAGENTS.md must exist at the repo root, every surface, and every non-opted-out package.harness
lint-meta-rules/canonical-helpers-single-homePure helpers must live in one canonical home (flag the same exported symbol appearing in multiple homes).harness
lint-meta-rules/dockerfile-base-image-digest-pinDockerfile FROM base images must be pinned by @sha256: digest (scratch and earlier build stages exempt).harness
lint-meta-rules/eslint-config-no-warnEvery rule in the RESOLVED ESLint config is "error" or "off", never "warn", including severities a spread preset injects.harness
lint-meta-rules/file-size-ratchetSource files stay at or under 400 raw lines. Today's offenders are grandfathered by .nightcore/lint-meta/baselines/file-size-ratchet.json; a new/grown offender fails, and a stale/shrunk baseline entry demands tightening.harness
lint-meta-rules/github-actions-least-privilege-permissionsGitHub Actions workflows declare a read-only top-level permissions: (no write-all/read-all, no <scope>: write); writes go on the job that needs them.harness
lint-meta-rules/github-actions-no-template-injectionGitHub Actions run: and github-script bodies never expand attacker-controllable ${{ }} context (issue/PR titles, comments, branch names); pass it through env: instead.harness
lint-meta-rules/github-actions-runner-pinnedGitHub Actions jobs must run on a pinned runner image (for example ubuntu-24.04), never a *-latest label.harness
lint-meta-rules/github-actions-sha-pinnedGitHub Actions uses: refs must be pinned to a 40-character commit SHA with a # vN comment (local ./ actions exempt).harness
lint-meta-rules/idempotency-key-parityProcedures carrying the idempotency middleware must have a client caller that sends an idempotency key, or no client caller at all.harness
lint-meta-rules/layer-rankFixed dependency direction by rank: a module imports only strictly-lower-ranked <scope> packages (equal/upward forbidden).harness
lint-meta-rules/no-cloned-component-foldersA component folder name may exist under only ONE feature. Shared surfaces are hoisted; divergent ones get a divergent name. Today’s clones are frozen in a shrinking allowlist.harness
lint-meta-rules/no-warn-severityESLint severity is 'error' or 'off', never 'warn'. A rule that matters is an error; a failure is fixed, not silenced.harness
lint-meta-rules/package-shapeEvery workspace is named <scope>/<dir>; library packages expose a barrel and point main/module/types/exports at the built output.harness
lint-meta-rules/prisma-method-surfaceThe Prisma reads and writes the rules police partition the generated client's <Model>Delegate method surface exactly, so a Prisma upgrade cannot add an unguarded method.harness
lint-meta-rules/security-scanner-version-parityThe gitleaks version pinned in the workflows must equal the one in scripts/ci/pre-push.sh, and the hook must compare a native gitleaks against it at run time.harness
lint-meta-rules/service-image-digest-pinWorkflow service and container images, and docker-compose images, must be pinned by @sha256: digest (a service that builds locally is exempt).harness
lint-meta-rules/session-epoch-capturedEvery call into the sign-in seam must pass the session epoch captured before the credential was read, or a revocation landing during the credential check loses the race.harness
lint-meta-rules/session-kind-stampedEvery call that mints a session must stamp the principal kind onto it, or sit in an allowlisted, provably single-kind flow; a session read without the kind falls back to a default and can silently promote one kind of account into another.harness
lint-meta-rules/session-landing-declaredEvery file that opens a door into a session must declare where it leaves the caller, and a door whose landing demands a return shape (the one that carries the principal kind to the client) must have it.harness
lint-meta-rules/session-mint-callersThe method that mints a session may only be called from allowlisted files; a new sign-in entry point must route through the gate in front of it so the gate cannot be bypassed.harness
lint-meta-rules/tenant-model-registry-parityEvery tenant-bearing Prisma model is scoped by the runtime extension or exempt with a reason, and the tenant lint rules resolve with exactly that registry.harness
lint-meta-rules/test-runner-segregationBun-side packages use 'bun:test'; foreign-side packages use 'vitest'. Never mix runners.harness
lint-meta-rules/test-sibling-enforcementEvery source file matched by include must have a colocated sibling test. Pure helpers must ship a test.harness
lint-meta-rules/test-workspace-enrollmentEvery candidate package with test files must be enumerated in the root 'test:node' script.harness
lint-meta-rules/translation-dead-keysEvery translation catalog key must be reachable from the source: named by a translation call, or spelled by some string in the code.harness
lint-meta-rules/ui-primitive-shapeA folder primitive under the ui root must ship its proof siblings (test, stories); a flat primitive must not carry sibling proof files at the root.harness
lint-meta-rules/workspace-graph-parityImported <scope>/* specifiers must be declared workspace:* deps, and tsconfig references must mirror those deps.harness

Preset: severity in the package's configs.recommended; off means the preset registers the rule switched off, not listed means it leaves the rule out; both are opt-in, so you turn the rule on yourself. harnessmeans the rule is a lint-meta rule with no ESLint preset. ❌ deprecated: the rule still works but is in no preset and will be removed; move to the rule named after it. Fix: 🔧 fixable with--fix, 💡 offers editor suggestions. Types: whether the rule needs a type-checked program (parserOptions.projectService), or uses one when present.

rules.json is generated at build time from the same catalog as the table above, so it cannot disagree with it. llms.txt at the site root points here and at it. The shape is a public contract: fields are added, never renamed or removed, without a schemaVersion bump. A build check asserts the file has exactly one entry per rule doc and that every url resolves to a page on this site.

Top level:

Field Meaning
schemaVersion Integer, currently 1. Bumped only on a breaking change to this shape.
generatedAt ISO timestamp of the build that emitted the file.
site The site root URL.
rulesPage URL of this page.
count Number of entries in rules.
packages One entry per package: name (npm), short, version, description, kind, namespace, url.
rules One entry per rule, in package order then by name.

Each entry in rules:

Field Meaning
id The id a config or a report uses: noctcore-react/no-prop-drilling, or the bare lint-meta id.
name The rule name without its namespace.
package npm name of the package that exports it.
runner eslint, or harness for a lint-meta rule that runs under @noctcore/harness.
description The rule’s meta.docs.description.
url The rule’s page on this site, the same URL as its meta.docs.url.
recommended error or off in the package’s recommended preset; null when the preset leaves it out, and for lint-meta rules.
fixable Whether the rule has an autofix.
hasSuggestions Whether the rule offers suggestions.
typeInfo required, optional or none: whether the rule needs a type-checked program.
deprecated Whether the rule is deprecated. A deprecated rule still works, but no preset enables it.
replacedBy Ids of the rules to use instead of a deprecated one; empty when there is none.
deprecatedSince The package version that deprecated the rule, or null.
category, ciCritical, factory, entry lint-meta rules only: the harness category, whether a violation fails CI by default, the factory export and the entry point that exposes it.