Skip to content

@noctcore/lint-meta-rules

Whole-repo structure-lock rules for the @noctcore/harness lint-meta runner.

Some invariants are not about one file: every workspace package is named <scope>/<dir>, every imported workspace package is a declared dependency, every GitHub Action is pinned to a commit SHA, no ESLint rule resolves to warn, no source file grows past a line cap. ESLint’s per-file model cannot see those. These rules can.

This is not an ESLint plugin. Each rule is a pure function of a repo context returning violations, implementing the IMetaRule contract from @noctcore/harness, and each is a factory (createXRule(options)) so anything project-specific is an option with a default. The intended consumer is the harness export pipeline, which inlines rule source into a repo’s .nightcore/lint-meta/; nothing requires this package at runtime. It is a bad fit if you are not running the harness, and several defaults (the @nightcore scope, a 400-line cap) are starting points you are expected to override. The /i18n, /prisma and /resolved-config entry points load ESLint and are kept off the main entry for that reason. The /session and /trpc entry points hold fences around one seam each (the call that mints a session; routers and the clients that call them), and are inert until you name that seam’s methods and files.

Install
npm install --save-dev @noctcore/lint-meta-rules
# or: bun add -D / pnpm add -D @noctcore/lint-meta-rules
registry.ts
import { createAllRules, createPackageShapeRule } from '@noctcore/lint-meta-rules';
// Every catalog rule with default options:
const rules = createAllRules();
// Or one rule, retargeted at your repo:
const shape = createPackageShapeRule({ scope: '@acme' });

A source catalog for the @noctcore/harness lint-meta runner, not an ESLint plugin. 28 rules.

Each rule links to its page, with the options it takes and Incorrect and Correct examples. A good first read is github-actions-sha-pinned.

RuleWhat it enforcesCategoryEntry point
agents-doc-presenceAGENTS.md must exist at the repo root, every surface, and every non-opted-out package.source-text@noctcore/lint-meta-rules
canonical-helpers-single-homePure helpers must live in one canonical home (flag the same exported symbol appearing in multiple homes).source-text@noctcore/lint-meta-rules
dockerfile-base-image-digest-pinDockerfile FROM base images must be pinned by @sha256: digest (scratch and earlier build stages exempt).ci@noctcore/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.config@noctcore/lint-meta-rules/resolved-config
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.source-text@noctcore/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.ci@noctcore/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.ci@noctcore/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.ci@noctcore/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).ci@noctcore/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.source-text@noctcore/lint-meta-rules/trpc
layer-rankFixed dependency direction by rank: a module imports only strictly-lower-ranked <scope> packages (equal/upward forbidden).source-text@noctcore/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.source-text@noctcore/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.config@noctcore/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.config@noctcore/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.config@noctcore/lint-meta-rules/prisma
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.ci@noctcore/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).ci@noctcore/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.source-text@noctcore/lint-meta-rules/session
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.source-text@noctcore/lint-meta-rules/session
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.source-text@noctcore/lint-meta-rules/session
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.source-text@noctcore/lint-meta-rules/session
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.config@noctcore/lint-meta-rules/prisma
test-runner-segregationBun-side packages use 'bun:test'; foreign-side packages use 'vitest'. Never mix runners.testing@noctcore/lint-meta-rules
test-sibling-enforcementEvery source file matched by include must have a colocated sibling test. Pure helpers must ship a test.source-text@noctcore/lint-meta-rules
test-workspace-enrollmentEvery candidate package with test files must be enumerated in the root 'test:node' script.testing@noctcore/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.source-text@noctcore/lint-meta-rules/i18n
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.source-text@noctcore/lint-meta-rules
workspace-graph-parityImported <scope>/* specifiers must be declared workspace:* deps, and tsconfig references must mirror those deps.config@noctcore/lint-meta-rules