# Gate register Every place a gate deliberately checks **less** than it could: a rule switched off or narrowed for a scope, a flag left off, a lane that does not run everywhere, an accepted finding. One row each, with the reason and the number measured when the row was written. **Why this file exists (E32).** The worst defect E32 found was not in any code a reviewer read: a lint rule had been switched off with 64 real hits counted in its own comment, and the "false positives" beside them were the React Compiler skipping 95 functions without a word. The rule since: **a gate with any real hit is never switched off or narrowed** — fix the sites, or suppress each one inline and name its invariant. Whatever is left narrowed is listed here, and [`frontend/src/lib/gates.contract.test.ts`](../frontend/src/lib/gates.contract.test.ts) reads the **effective** configuration and fails when a narrowing is not on this page. A row here is a decision, not an oversight; a narrowing missing from here is a test failure. **The snapshot.** ESLint's effective config for every scope `eslint .` reaches, and the lines that run the lanes (`scripts/check.sh`, `backend/docker-compose.test.yml`, `backend/Dockerfile.test`, the npm scripts, `vitest.config.ts`, `.prettierignore`), are committed in [`frontend/src/lib/gates.snapshot.json`](../frontend/src/lib/gates.snapshot.json). The test fails on ANY difference — a rule, an option, a new ignore, a gate flag. A scope is named by path (`scopeOf` in the test: `src`, `src tests`, `ui`, …) and every file in it must get the same config, so a glob that moves one file into another config fails too, while a new file in a known place changes nothing (until E35 the snapshot listed every file, and 31% of commits regenerated it). So a change to a gate is always a diff of that file in review, never a shape a detector failed to model (E32 review: three rounds of a cleverer detector each found a bypass of the last one's fixes). Regenerate it with `GATE_SNAPSHOT=update npx vitest run src/lib/gates.contract.test.ts` (from `frontend/`), and read its diff as a gate change. A new kind of file (a new config, a new place) needs a line in `scopeOf`. **How the register is matched:** each narrowing must appear in a table row below as its `code-quoted` id, in a row whose **Scope** cell names the scope exactly as the test reports it (a cell may list several, separated by ` · `). For ESLint the scopes are the snapshot's config names, and a narrowing is read off each config against `src`: a rule at off or warn, or one src applies that a scope does not (one id per plugin family), a `no-restricted-*` list that leaves out an entry src's declares, an option that lets something through (`allow…`, `ignore…`, `except…`), an unused-disable report that is not an error, and a file no config lints. Ruff's TOML is parsed and held to a whitelist of key paths, pytest.ini to a whitelist of whole lines. ## ESLint (frontend) `npm run lint` is `eslint . --max-warnings 0`, so a rule at **warn** fails the lane exactly like an error (the test holds that exact script). The warn rows are listed so that claim stays checked. A core rule switched off because its typescript-eslint twin replaces it (`no-unused-vars` → `@typescript-eslint/no-unused-vars`) is not listed separately; neither are the core rules typescript-eslint's compatibility layer switches off for TS files because the type checker reports them itself (`no-undef`, `no-redeclare`, `no-dupe-keys`, …), nor the formatting rules `eslint-config-prettier` switches off (Prettier owns formatting and has its own lane). | Scope | Narrowed | Why | Measured | | --- | --- | --- | --- | | src | `@typescript-eslint/no-unused-vars` — off | tsc owns it (`noUnusedLocals` + `noUnusedParameters` in every project), so a finding is reported once, by one tool. | — | | src | `react-hooks/exhaustive-deps` — warn | Preset level; `--max-warnings 0` makes it fail. Every suppression names its invariant (`eslint-comments/require-description`). | 0 suppressions (E32 S4: a suppression of this rule drops the whole function from the React Compiler, so the two that were left went) | | src | `react-hooks/incompatible-library` — warn | Preset level; fails under `--max-warnings 0`. VirtualGrid (TanStack Virtual) is the one site, suppressed inline. | 1 | | src | `react-hooks/unsupported-syntax` — warn | Preset level; fails under `--max-warnings 0`. | 0 | | src | `react-refresh/only-export-components` — warn | Fails under `--max-warnings 0`; `allowConstantExport` lets a module export constants beside its component. | 0 | | src | `@typescript-eslint/no-unused-expressions` — narrowed | `allowShortCircuit` + `allowTernary`: `cond && f()` / `a ? f() : g()` side-effect statements are an idiom here. | — | | src | `@typescript-eslint/no-floating-promises` — narrowed | `allowForKnownSafeCalls`: React Query's `invalidateQueries`, `refetchQueries`, `cancelQueries`, `resetQueries`, `prefetchQuery`, `prefetchInfiniteQuery` never reject unless asked to (`throwOnError`), so a bare call is safe by name. | 6 names | | e2e | `@typescript-eslint/no-floating-promises` — narrowed | The same `allowForKnownSafeCalls` list (the typed rules are one object for both scopes). | 6 names | | lib/http.ts | `no-restricted-globals` — narrowed | A network owner (`NETWORK_OWNERS` in eslint.config.js): the one module that may call `fetch` (`send`, which `req` and the public pages go through). Every other network global stays restricted there. | 1 global | | lib/api/core.ts | `no-restricted-globals` — narrowed | A network owner: `uploadReq` is the one `XMLHttpRequest` (upload progress). | 1 global | | src tests · ui tests · lib tests | `no-restricted-globals`, `no-restricted-properties` — off | A test stands a double in for the network (`vi.stubGlobal("fetch", …)`) and may reach it directly. | — | | src tests · ui tests · lib tests | `local/literal-a11y-text`, `local/input-primitive`, `local/surface-role`, `local/glyph-link` — off | A test renders throwaway markup (a raw input as a focus target, an untranslated label) that is not product UI. `local/button-primitive` stays on: in a test file (and in a control primitive) it only refuses a raw `