Skip to content
Back to skills

Quality Gates

ASecurity

Installs and repairs quality gates (check:fast, verify, lefthook, Claude Code hooks, the guardrail) without weakening them. Use when a gate, hook or verify fails or a gate file changes. Not for a lint error (typescript-and-lint-rules).

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
developmenttypescriptgonodegitperformance

Works with

  • claude code

Security analysis

A100/100

Pro scans all 19 files and shows the line behind each finding

Scanned October 18, 2026

npx -y skills add zakariaf/ReactNative-Skills --skill quality-gates --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Quality Gates?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Quality Gates
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zakariaf-quality-gates/badge)](https://www.skillsdirectory.com/skills/zakariaf-quality-gates)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: quality-gates
description: Installs and repairs quality gates (check:fast, verify, lefthook, Claude Code hooks, the guardrail) without weakening them. Use when a gate, hook or verify fails or a gate file changes. Not for a lint error (typescript-and-lint-rules).
---

# Quality gates

When an agent writes most of the code, the gates are the reviewer: a hook after every edit,
`check:fast` before the agent stops, lefthook on every commit and push, and `npm run verify`, whose
guardrail compares every resolved tool config with `quality-gates.json`. This skill installs those
gates from tested templates, fixes failures the right way, and proves with two checkers that no gate
is missing, weakened or bypassed.

## Rules that must hold

1. **When a gate fails, fix the code, never the gate.** No lowered threshold, raised limit, wider
   exemption glob (a `!` entry in `collectCoverageFrom`, an ignored test path), `eslint-disable`, `@ts-ignore`, `.skip`, `jest.retryTimes`, istanbul or Stryker
   ignore comment. *Why:* quietly relaxing a check to get green is the likeliest failure of an
   unsupervised agent, and nothing else would notice.
2. **Never bypass a hook:** no `--no-verify`, `git commit -n`, `LEFTHOOK=0`, `LEFTHOOK_EXCLUDE`,
   `lefthook uninstall`, `core.hooksPath`, no `skip:`, `only:` or `exclude_tags:` in `lefthook.yml` or
   in a local `lefthook-local.yml` (usually git-ignored), no `disableAllHooks`, no `|| true` on a hook. *Why:* the hooks are the reviewer; a
   local override is invisible to everyone else, which is why the guardrail reads the merged config.
3. **A gate changes only with the developer's agreement,** in one commit that changes the gate and
   `quality-gates.json` together, with a `Gate-Change: <reason>` trailer (git-commits-and-reporting
   owns the trailer format). If a gate looks wrong, stop and send the note in
   `assets/gate-question.md`; never edit `quality-gates.json` to match a weakened config. *Why:* the
   guardrail and `git log --grep Gate-Change` only mean something if every change goes through them.
4. **Use the canonical npm scripts byte for byte** (`templates/package-scripts.json`; `start`,
   `android`, `ios` and `web` stay the app's own). *Why:* the guardrail compares the gate scripts
   exactly, and a weaker copy in a hook or CI would bypass it.
5. **Run `npm run -s check:fast` before saying a change is done, and `npm run verify` before every
   push** (the pre-push hook runs it). *Why:* `check:fast` closes the inner loop in seconds; `verify`
   is the only run that holds every gate.
6. **`verify` is one strict pipeline, cheapest first, stopping at the first failure.** Every step
   runs strict; a skill that adds a gate appends its npm script to `verifyExtraSteps` in
   `quality-gates.json`, and `test:sim` joins once a simulation exists. Never stub a target, add
   `--passWithNoTests` or drop a step to get green. *Why:* a step that can skip quietly is a gate
   that is already gone.
7. **Hooks that must reach the agent exit 2 and write to stderr.** *Why:* any other exit is a
   non-blocking error: the agent never sees it, and the user sees only a one-line notice.
8. **Start the agent at the repository root, and merge `.claude/settings.json`, never replace it.**
   *Why:* hooks and permissions load only from the folder the session starts in, and the file holds
   keys the gates do not own (plugins, skill permissions).
9. **Pin every dependency exactly, keep the 7-day release age, review install scripts before they
   run, ship only allowed licences;** each exception is a dated, agreed entry (dependency-management
   owns the procedure, `verify` enforces it). *Why:* the supply chain is code the reviewer never
   reads.
10. **When a size or complexity limit trips, split by responsibility,** never by line count, never
    by compressing code or moving logic into JSON (the limits table is typescript-and-lint-rules').
    *Why:* the limit detects a symptom; gaming it keeps the problem.
11. **Every ignore entry in `knip.json` has a written reason in `contracts/knip-ignores.json`.**
    *Why:* knip takes no comments, and an ignore entry silences it for good.
12. **No gate reaches into installed agent skills through a wildcard:** `gated-paths.ts` keeps
    `**/` patterns out of `skills/`, `.agents/`, `.claude/` and the other agent folders, the `ask`
    rules are anchored at the repo root, and `knip.json` `ignore`s the ten agent folders. *Why:*
    installed skills hold tsconfigs, snapshots, vectors and imports of uninstalled packages on
    purpose; they must not demand a trailer or a prompt, or fail `check:unused`.
13. **`SKIP` lines and `NOT APPLICABLE` count as a pass; exit 2 never does.** A checker prints
    `SKIP <file> [<rule>] not yet created (<skill> creates it)` for a gate file another skill has not
    created yet. *Why:* exit 2 means a bad command or nothing to check: fix the command, never read
    it as not applicable.

## Workflow

1. **Know the gates.** Read [references/gates-overview.md](references/gates-overview.md): what runs
   when, every canonical script and its owner, and the order and cost of `verify`.
2. **Install or repair them** (a new app, or `check-gate-wiring.mjs` reports a missing or changed
   file). In an existing app adopt the owners first ("Adopting into an existing app" in the
   overview), then copy from this skill and read
   [references/hooks-and-settings.md](references/hooks-and-settings.md):

   | From this skill | Goes to |
   |---|---|
   | `templates/package-scripts.json` | the `scripts` of the root `package.json` (merge; keep `start`, `android`, `ios`, `web`) |
   | `templates/lefthook.yml`, `templates/quality-gates.json` | the repo root |
   | `assets/claude-settings.json` | `.claude/settings.json` (merge: keep existing keys, add every deny and ask rule and both hooks) |
   | `templates/scripts/quality/`, `templates/scripts/hooks/` | the same paths (guardrail, verify plan and runner, gated paths, edit hook, tests) |
   | `templates/contracts/knip-ignores.json` (+ schema) | `contracts/` |
   | `templates/knip.json`, `templates/dot-gitignore`, `templates/dot-npmrc`, `templates/scripts/quality/device-only.ts` | only when their owner skill has not written them (synced copies of typescript-and-lint-rules', dependency-management's and unit-and-component-tests' files) |

   If an existing app's `knip.json` lacks the `"ignore"` list for the ten agent folders (the canonical
   copy in `templates/knip.json` has it), add it in the same change, each entry with its reason from
   `templates/contracts/knip-ignores.json` (rule 12).
   Then `npm run prepare` (runs `lefthook install` when the app folder is the git top level, else
   prints why it skipped) and `npx lefthook validate` (prints `All good`). The commit-msg job
   runs git-commits-and-reporting's `scripts/git/check-commit-message.ts`: install that skill's check
   in the same change, or every commit fails with `Cannot find module` (loud, and correct).
3. **Prove the wiring.** `node scripts/quality/check-quality-gates.ts` prints
   `quality-gates: all resolved configs match quality-gates.json`; then
   `node ${CLAUDE_SKILL_DIR}/scripts/check-gate-wiring.mjs .` (the repo root is the positional
   argument, never `--root`) and `npm run -s check:fast`.
4. **When a gate fails,** read [references/fixing-failures.md](references/fixing-failures.md): the
   fix for each failure and the never list. Fix, rerun the step, rerun `npm run -s check:fast`.
5. **When a gate itself looks wrong,** follow "Changing a gate" in
   [references/guardrail.md](references/guardrail.md): stop, send `assets/gate-question.md` to the
   developer, change nothing until they agree.
6. **When a skill adds a gate** (a verify step, a gated path, a knip ignore, a budget), read
   [references/root-files.md](references/root-files.md): `verifyExtraSteps`, `gatedPaths`, the knip
   reasons contract, and what `check-gate-wiring.mjs` compares.
7. **For a dependency, install-script, licence, knip or CI question,** read
   [references/supply-chain.md](references/supply-chain.md).
8. **Before calling the work done,** run `npm run -s check:fast`, then both checkers from the repo
   root. Fix every `FAIL` line (each names the file, the rule and the fix) and rerun until both print
   `RESULT: PASS`. Before a push, `npm run verify` must be green.

**When multi-package** (npm workspaces): `typecheck` takes the workspace form in
`assets/package-scripts.multi-package.json` (each workspace keeps its own `typecheck` script),
`quality-gates.json` lists each program under `typescript.projects` (a glob such as
`apps/*/tsconfig.json` covers new ones), knip gets one `workspaces` entry per package, and the one
root `jest.config.js` writes its test roots, coverage glob and logic keys in workspace form
(`<rootDir>/apps/*/src/**/`, `./packages/core/src/`), restated in `quality-gates.json` `jest` in the
same gate change. The gate files stay at the repo root; `check-gate-wiring.mjs` reads the workspaces
from `package.json` and compares the Jest scope with the canonical one, workspace prefixes stripped.

## Definition of done

- [ ] `npm run -s check:fast` is green; before any push `npm run verify` ends with
      `verify: <n> steps passed (...)`.
- [ ] The guardrail prints `quality-gates: all resolved configs match quality-gates.json`, and
      `npx lefthook validate` prints `All good`.
- [ ] No gate changed without the developer's agreement and a `Gate-Change:` trailer; no hook was
      bypassed, and no `lefthook-local.yml` or `.claude/settings.local.json` switches one off.
- [ ] `.claude/settings.json` still holds its other keys plus every deny and ask rule and both hooks.
- [ ] Every `knip.json` ignore entry has its reason in `contracts/knip-ignores.json`, and `knip.json`
      ignores every agent folder that holds code (`.claude/skills/`, `.agents/skills/`, ...).
- [ ] `node ${CLAUDE_SKILL_DIR}/scripts/check-bypasses.mjs .` prints `RESULT: PASS`.
- [ ] `node ${CLAUDE_SKILL_DIR}/scripts/check-gate-wiring.mjs .` prints `RESULT: PASS` with no SKIP
      line once every owner has run (the reference app does). Only in an app adopted step by step,
      each until its owner runs: `SKIP scripts/git/check-commit-message.ts [script-target] not yet
      created (git-commits-and-reporting creates it)`, the same for `scripts/deps/check-deps.ts`, `scripts/deps/check-npm-config.ts`, `scripts/audit/audit-licenses.ts`
      (dependency-management) and `scripts/i18n/verify-catalogs.ts` (i18n-strings-and-catalogs), and
      `[gate-file-missing]` SKIPs for `eslint.config.mjs`, `knip.json`, `.prettierrc.json`,
      `tsconfig.base.json` (typescript-and-lint-rules), `jest.config.js`,
      `scripts/quality/device-only.ts` (unit-and-component-tests) and `.npmrc` (dependency-management).

## Anti-patterns

- **Editing `quality-gates.json` to match a weakened config.** The guardrail then passes and the
  gate is gone. Restore the config; `check-gate-wiring.mjs` still fails the weaker value.
- **`// eslint-disable-next-line` "just this once".** It is inert (`noInlineConfig`) and reported;
  fix the code, or ask for a file-exact exception block (typescript-and-lint-rules).
- **Retrying a failing commit with `--no-verify`.** Denied by the permissions and found by
  `check-bypasses.mjs`; read the hook's output instead.
- **A `lefthook-local.yml` with `skip: true` "while I fix this".** It is usually git-ignored, so
  nobody sees it, and the guardrail fails it through `lefthook dump`.
- **Adding a test's path to `testPathIgnorePatterns` or a folder to `collectCoverageFrom` as `!...`.**
  It hides the failure, not fixes it; `jest-scope` and the guardrail's Jest scope both fail it.
- **Replacing `.claude/settings.json` with the asset.** That drops the plugin entry and the skill
  permissions; merge instead.
- **Splitting a 300-line file into `part-1.ts` and `part-2.ts`.** Split by responsibility, each part
  with its own test.
- **Adding `--passWithNoTests` or `-u` to a script, or a stub file behind a script, to get green.** A
  test command that finds no tests must fail; goldens change only by hand with a trailer.
- **Editing `verify-plan.ts` to add a step.** Append the script to `verifyExtraSteps` instead; the
  plan file is canonical.
- **A knip ignore entry "to silence a false positive".** Find why knip cannot see the use; an entry
  needs a tool contract, a reason in `contracts/knip-ignores.json` and the developer's agreement.
- **Guessing `--root .` for a checker.** The repo root is the first positional argument; the exit 2
  it prints is not a result.
- **Extending an expired release-age block.** Delete it; the lockfile keeps the installed versions.
- **Deleting the agent-folder `ignore` entries of `knip.json` because knip hints "Remove from
  ignore".** The hint appears while a folder does not exist; the first installed skill then fails
  `check:unused`. Keep the ten entries and their reasons.
- **Looping on the Stop hook.** When the turn ends with the gate still red, report the failing gate
  to the developer instead of working around it.

## Files in this skill

| File | What it is | Read/run when |
|---|---|---|
| [references/gates-overview.md](references/gates-overview.md) | The gate stack, every canonical script and its owner, the verify pipeline with measured timings, check:fast, what runs outside verify, adopting into an existing app, when multi-package | Workflow step 1 |
| [references/hooks-and-settings.md](references/hooks-and-settings.md) | lefthook jobs and secret globs, the commit-msg hand-off, the Claude Code hooks, permissions, merging settings, local overrides, other agents, what was verified | Workflow step 2, and when a hook misbehaves |
| [references/guardrail.md](references/guardrail.md) | What the guardrail compares, what `quality-gates.json` holds, the gated paths, how a gate may change | Workflow step 5, and before touching a gate file |
| [references/fixing-failures.md](references/fixing-failures.md) | Fix by failure, flaky tests, limits, the never list, the gate-looks-wrong note | Workflow step 4, whenever a gate fails |
| [references/root-files.md](references/root-files.md) | `verifyExtraSteps`, adding a gate from another skill, the knip reasons contract, the coverage policy, the gated-paths module, what `check-gate-wiring.mjs` compares | Workflow step 6 |
| [references/supply-chain.md](references/supply-chain.md) | The dependency steps of verify, dated release-age blocks, install scripts, licences, knip settings, CI, other package managers | Workflow step 7 |
| `templates/package-scripts.json` | The canonical root npm scripts (canonical file, owned here) | Workflow step 2 |
| `templates/lefthook.yml` | pre-commit, commit-msg and pre-push hooks (canonical, owned here) | Workflow step 2 |
| `templates/quality-gates.json` | The guardrail's expectations, budgets, verify extra steps and gated paths; the baseline `check-gate-wiring.mjs` compares with (canonical, owned here) | Workflow step 2 |
| `templates/scripts/` | The gate scripts with their tests (canonical, owned here, synced): `quality/check-quality-gates.ts` + `gate-diff.ts` (the guardrail), `quality/verify-plan.ts` + `run-verify.ts` (the verify steps and runner), `quality/gated-paths.ts` (which changed files are gates), `hooks/after-edit.ts` (the PostToolUse hook), `hooks/install-git-hooks.ts` + `hook-install-plan.ts` (`prepare`: lefthook install at the git top level only), and `quality/device-only.ts` (unit-and-component-tests' coverage helper, synced: the baseline for `gate-script-changed`) | Workflow step 2 |
| `templates/contracts/` | `knip-ignores.json` (the reason for every knip ignore entry, the ten agent folders included) and its schema | Workflow step 2, and when an ignore entry is added |
| `templates/knip.json` | typescript-and-lint-rules' knip config (synced) | Only when its owner has not written it |
| `templates/dot-gitignore` | typescript-and-lint-rules' `.gitignore` (synced; copy as `.gitignore`) | Only when its owner has not written it |
| `templates/dot-npmrc` | dependency-management's `.npmrc` policy (synced; copy as `.npmrc`) | Only when its owner has not written it |
| `assets/claude-settings.json` | The deny rules (iOS and Android keys, hook bypasses), the ask rules (gate files), the PostToolUse and Stop hooks (canonical, owned here, synced; merge into `.claude/settings.json`) | Workflow step 2 |
| `assets/jest-config-baseline.js` | unit-and-component-tests' canonical `jest.config.js` (synced, never copied into the app): the scope values `jest-scope` compares with | Read only to see the canonical scope |
| `assets/package-scripts.multi-package.json` | The scripts that change when multi-package | When package.json has workspaces |
| `assets/gate-question.md` | The one-message note to the developer when a gate looks wrong | Workflow step 5 |
| `assets/shared.json` | Declares the files synced from the library (edit the canonical copies, never these) | When adding a shared file |
| `scripts/check-gate-wiring.mjs` | Checks gate files, scripts and their targets, hooks (commands byte for byte), settings and the local overrides, `.npmrc`, linter options, thresholds and logic folder keys, the Jest coverage and test scope, verify extra steps, knip reasons, that `quality-gates.json` is no weaker than the baseline, and that no gate reaches into agent folders while knip ignores them (14 rules) | Workflow steps 3 and 8 |
| `scripts/check-bypasses.mjs` | Finds suppressions, ignore comments, hook bypasses, auto-updated snapshots, relaxed ESLint flags and stale release-age exceptions anywhere in the repo (11 rules) | Workflow step 8 and the definition of done |
| `scripts/lib/not-weaker.mjs` | The direction-aware comparison the wiring check uses | Read only to change a rule |
| `scripts/lib/workspace-scope.mjs` | The workspace form of the Jest scope (prefixes from `package.json` `workspaces`, the single-app form, logic-key counterparts, reach probes) | Read only to change the multi-package rules |
| `scripts/selftest.mjs` | Proves both checkers on good fixtures and every planted bug | After changing a checker, a template or a fixture |
| `scripts/check-lib.mjs` | Shared script helper, synced from the library (do not edit here) | Never by hand |
| `tests/bases/` | The canonical root configs (synced) and stub targets the wiring suite starts from | When adding a case |
| `tests/fixtures/` | Overlay cases for both checkers (`EXPECT.txt` plus `mutation.json`, `DELETE.txt`, `ARGS.txt` or overlay files) | When adding a rule |

## Related skills

- `typescript-and-lint-rules` - `eslint.config.mjs`, the tsconfig set, knip, Prettier and the limits table.
- `unit-and-component-tests` - the Jest configs, coverage thresholds, the device-only marker, Stryker.
- `dependency-management` - installs, `.npmrc`, `check:deps`, `audit:licenses`, release-age exceptions.
- `git-commits-and-reporting` - the commit-message check, the `Gate-Change:` trailer, the report.
- `tdd-workflow` - the test-first loop that `check:fast` closes.
- `expo-project-bootstrap` - writes these gate files into a new app.
- `ci-pipeline-and-gates` - running `npm run verify` in CI.
- `golden-and-snapshot-tests`, `seeded-determinism-and-golden-vectors` - the goldens and vectors behind gated paths.
- `accessibility-as-code`, `performance-budgets` - the `a11y` and `perf` blocks of `quality-gates.json`.
- `troubleshooting-playbook` - known tool failures behind a red gate.

Files in this skill

  • SKILL.md19.1 KB
  • assets/claude-settings.json2.2 KB
  • assets/gate-question.md658 B
  • assets/jest-config-baseline.js7.4 KB
  • assets/package-scripts.multi-package.json433 B
  • assets/shared.json2.9 KB
  • references/fixing-failures.md8 KB
  • references/gates-overview.md14.7 KB
  • references/guardrail.md13.2 KB
  • references/hooks-and-settings.md16.8 KB
  • references/root-files.md16.8 KB
  • references/supply-chain.md5.6 KB
  • scripts/check-bypasses.mjs12.3 KB
  • scripts/check-gate-wiring.mjs43.7 KB
  • scripts/lib/not-weaker.mjs4.8 KB
  • scripts/lib/workspace-scope.mjs7 KB
  • scripts/selftest.mjs2 KB
  • templates/contracts/knip-ignores.json3.2 KB
  • templates/contracts/knip-ignores.schema.json1.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.