agentsclimarketplace

Quality gates

Skill andymai/brepjs/.claude/skills/quality-gates

This skill should be used when a local brepjs quality gate or `npm run validate` step fails and the specific rule's fix or escape hatch is needed — ESLint errors like "no-explicit-any" or "Direct .oc access is banned", pattern-checker "check:patterns" violations (no-double-cast, max-function-lines, require-using-for-handles), a pattern-baseline update, knip flagging an unused export, or the boundary check printing "Layer boundary violations found". Also covers the pattern-baseline trap where an unbaselined violation on main fails every open PR. Not for a red CI run in general (ci-triage) or for hook orchestration and merges (git-pr-workflow).From its SKILL.md

Install
npx -y skills add andymai/brepjs --skill quality-gates

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

12.6 KB, as published. Nobody here has run it

Quality gates

Pass npm run validate, understand every gate's failure output, and reach for the right escape hatch instead of --no-verify. CLAUDE.md already lists the headline rules and hook tiers; this skill adds the failure-output recipes, escape-hatch mechanics, and the traps those summaries omit.

The gate map

GateCommandRuns in validateRuns in pre-commitElsewhere
Typechecknpm run typecheck (tsc --noEmit)1/5Tier 1 (parallel)CI typecheck
ESLintnpm run lint (eslint src/)2/5Tier 1 via lint-stagedCI lint
Boundariesnpm run check:boundaries3/5Tier 1 (:staged)CI quality
Formatnpm run format:check4/5Tier 1 via lint-stagedCI lint
Changed testsnpm run test5/5Tier 2CI test (sharded, full)
Pattern checkernpm run check:patterns—lint-staged (staged src only)CI quality
knipnpm run knip——pre-push + CI quality

validate is scripts/validate-change.sh: five steps, in order, fail-fast (set -e) — it stops at the first red step and prints nothing after it. Fix step N before step N+1 is reachable. On success it prints === All checks passed ===.

Pre-commit (.husky/pre-commit) runs Tier 1's three checks in parallel, so their output interleaves — read carefully to attribute an error to the right gate. For hook anatomy, FULL_TESTS=1, pre-push, commit-msg, and --no-verify, see the git-pr-workflow skill. (Note: scripts/pre-commit-help.sh prints "coverage thresholds enforced at push time" — that line is stale; pre-push runs only knip.)

ESLint failures

Full config: eslint.config.js (flat config, strictTypeChecked base). Rules actually hit in practice and their sanctioned escapes:

SymptomRuleFix / escape
"Unexpected any"no-explicit-anyType it. Only for a real WASM type gap: // eslint-disable-next-line @typescript-eslint/no-explicit-any -- kernel WASM binding lacks type
"Forbidden non-null assertion"no-non-null-assertionNarrow with a guard; for noUncheckedIndexedAccess array reads, add a bounds check or disable-comment
"must be imported using a type-only import"consistent-type-importsUse import type { ... }
unused varno-unused-varsPrefix with _ (argsIgnorePattern/varsIgnorePattern = ^_)
"Unnecessary conditional"no-unnecessary-conditionThe condition is provably always true/false — remove it, don't disable
switch not exhaustiveswitch-exhaustiveness-checkHandle every union case; a default counts as exhaustive
"Do not use '// @ts-ignore'"ban-ts-commentUse // @ts-expect-error -- reason (the -- reason suffix is mandatory); @ts-ignore/@ts-nocheck are fully banned
console callno-consoleOnly console.error/console.warn are allowed
export let / enumno-restricted-syntaxUse a getter fn or const; use as const object + literal union

Two Layer-2+ bans — "Direct .oc access is banned" and "Direct method calls on .wrapped are banned" — fire in the domain/high-level dirs. The fix for both is getKernel().method(shape.wrapped), never a disable-comment. The rule mechanics and the exact directory list live in the architecture-navigation skill; for the correct kernel-method call pattern see kernel-abstraction.

npm run lint only covers src/. Tests are linted at commit time via lint-staged (.lintstagedrc.json), not by npm run lint. Prettier config (.prettierrc.json): semicolons, single quotes, trailingComma: es5, width 100, LF.

Pattern checker deep-dive

scripts/check-patterns.ts — AST checks ESLint can't express. Five rules, all severity error:

Rule idFires whenThreshold / excusal
no-double-castas unknown as T, as any as T, <T><unknown>exprUse a guard, generic, or branded constructor
no-async-withkernelasync callback passed to withKernel(id, fn) (bare or member call)Use getKernel(id) for async code (kernel-abstraction skill)
require-using-for-handlescreateHandle()/createKernelHandle() not bound with usingExcused when: returned, passed directly as a call argument, or placed in an object/array literal (memory-and-disposal skill)
max-function-linesfunction body > 60 effective linesBlanks, comments, and lone-brace lines don't count; extract helpers
max-nesting-depth> 4 levels of if/for/for-in/for-of/while/do/switch/tryDepth resets inside nested function bodies; use early returns

The rule id is require-using-for-handles (not "missing using") — that exact string goes in baselines and disable-comments.

Inline disable: // brepjs-patterns-disable: <rule-id> on the line above the violation or inline on the same line. <rule-id> may be a specific rule or * for all rules on that line.

Baseline (.pattern-baseline.json, version-2): only new (non-baselined) violations fail; exit 1 if any. Currently 30 entries (15 max-function-lines, 14 no-double-cast, 1 max-nesting-depth). Fingerprints are content-based (file|rule|normalized-80char-snippet|#occurrenceIdx), so:

  • Editing code above a violation does NOT reshuffle fingerprints.
  • Editing the violating line itself (rename, signature change, cast text) changes the fingerprint → it reports as "new". This is intended: touch it, own it.

Regenerate after intentionally adding/removing violations: npm run check:patterns:baseline (= --update-baseline). Other flags: --no-baseline (report everything, ignore baseline), --json, --sarif. Only src/**/*.ts is ever scanned (no .d.ts, no src/kernel/wasm/). Console failure output ends with the two escape hatches printed verbatim: the disable-comment format and the --update-baseline command.

TRAP: an unbaselined violation on main fails every open PR

CI's quality job runs check:patterns over all of src/ and checks out the PR-merged-with-main commit. So a max-function-lines/no-double-cast violation that lands on main without a matching baseline entry fails the quality job — and thus the required ci-pass aggregate — on every open PR at once, even PRs that never touched that file.

Recovery: land a tiny baseline-bump PR first (npm run check:patterns:baseline, commit .pattern-baseline.json). That PR passes because its own merge commit contains the new baseline; once merged, the other PRs go green. This happens because lint-staged only runs the pattern checker on staged files locally, so a long function can slip in via a large refactor and only bite in full-src CI. See the ci-triage skill for other quality-job failure modes.

TRAP: function-lookup CI diff after adding a *Fns.ts export

Adding a *Fns.ts export makes CI's build job flag a docs/function-lookup.md diff unless the file is regenerated and prettier-normalized before commit — full recipe in the adding-operations skill.

knip (unused exports)

Runs on pre-push and in CI's quality job. Config: knip.config.ts.

  • Export used only by tests? knip can't trace tests/ (separate tsconfig, @/ alias). Tag it @testOnly in a JSDoc comment — tags: ['-testOnly'] treats it as used. This is the correct escape, not deleting the export.
  • ignoreExportsUsedInFile: true; duplicates and optionalPeerDependencies are off (intentional API aliases + the brepjs-opencascade optional peer).
  • Root workspace checks src/**/*.ts only. Companion workspaces (brepjs-opencascade, brepjs-voxel-wasm, brepjs-viewer, brepjs-cad, apps/playground) are fully ignored; brepjs-bim ignores examples/**. See companion-packages.

Boundary check

scripts/check-layer-boundaries.sh enforces downward-only imports (target layer ≤ source layer), handling both @/ alias and relative imports. Violation output:

Layer boundary violations found:

  VIOLATION: src/topology/foo.ts (layer 2: topology) imports from '@/sketching/bar.js' (layer 3: sketching)

The script's layer map is a superset of the CLAUDE.md table — it also places csg/voxel/implicit in Layer 2 and gear/ns/lattice in Layer 3. Unrecognized top-level dirs and root files (src/index.ts) are skipped. --staged mode (check:boundaries:staged, used by pre-commit) checks only staged files; env BOUNDARY_SRC_DIR overrides the scan root for fixture testing. For where new code belongs and how to restructure to fix a violation, see architecture-navigation.

Not gated locally (surfaces only in CI)

  • Coverage: test runs --changed (changed files, no coverage); test:full runs the whole suite with --coverage. Thresholds run on main pushes only, continue-on-error — informational, never a PR gate. See writing-tests.
  • Full test suite: only the sharded CI test job (test:ci, no coverage) runs everything. Pre-commit runs --changed only.
  • size / benchmark: PR-only CI jobs (.size-limit.json budgets; benchmark regression vs main). Not in validate.

Additional resources

Adjacent skills own the neighboring facts: git-pr-workflow (hook tiers, conventional commits, --no-verify), architecture-navigation (layer map, the .oc/.wrapped rule + directory list), kernel-abstraction (withKernel vs getKernel), memory-and-disposal (using handles), ci-triage (CI job failure modes), writing-tests (coverage and the vitest runner config), adding-operations (function-lookup gate), companion-packages (workspace layout).

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.