Git hooks, commits, and PR flow

SkillProductivity

This skill should be used when committing, pushing, branching, or merging in the brepjs repository — when a task involves "pre-commit hook failed" (which tier ran, how to bypass), "commit rejected by commitlint", "subject may not be empty", the pre-push knip tier firing, "create a branch", "set up a worktree", "open a PR", "arm auto-merge", or deciding whether a commit needs a `!` breaking marker. Covers hook anatomy and tiers, conventional-commit message format, branching, worktrees, and the PR/merge process. For diagnosing a specific gate or lint error see quality-gates; for a red CI job see ci-triage.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Git hooks, commits, and PR flow skill

What this skill tells your AI

The instructions your AI receives, as published by andymai/brepjs in .claude/skills/git-pr-workflow/SKILL.md and read by ahel’s review.

Hooks are managed by husky (core.hooksPath.husky/_, installed by the prepare script in package.json). The repo CLAUDE.md "Git hooks" section summarizes the tiers; this skill covers what to do when a gate fires and the traps around commits, worktrees, and merging.

Quick map

ConcernWhere it lives
Hook scripts.husky/pre-commit, .husky/pre-push, .husky/commit-msg
lint-staged config.lintstagedrc.json (NOT in package.json)
Commit message rulescommitlint.config.js (@commitlint/config-conventional)
Local full gatenpm run validate (scripts/validate-change.sh)
CI gate for mergeci-pass job in .github/workflows/ci.yml — the only required status check
PR template.github/pull_request_template.md
Merge methodSquash only; branch auto-deleted on merge

Pre-commit anatomy

.husky/pre-commit runs three tiers; a trap prints scripts/pre-commit-help.sh on any failure.

Tier 1 (parallel):

  • npx lint-staged — per .lintstagedrc.json: src/**/*.ts gets eslint --fix + prettier --write + the pattern checker (scripts/check-patterns.ts); tests/**/*.ts gets eslint + prettier only; *.config.ts and *.md get prettier only. The pattern checker runs only on src/ files.
  • npm run typecheck
  • npm run check:boundaries:staged — the staged variant (scripts/check-layer-boundaries.sh --staged), not plain check:boundaries. On failure, see the architecture-navigation skill.

Tier 2: npm run test — changed-file tests on the occt-wasm kernel, no coverage thresholds (vitest run --project occt-wasm --changed). Set FULL_TESTS=1 to run npm run test:full (full suite with coverage) instead. Test failures: see the writing-tests skill.

Tier 3 (non-blocking, always exit 0):

  • npm run check:readme-reminders — lists READMEs adjacent to staged .ts files that may need updating.
  • bash scripts/check-function-lookup.sh — fires when a staged path matches src/**Fns.ts (any depth) or an index.ts at one directory level (src/index.ts or src/<dir>/index.ts) without docs/function-lookup.md. Act on this one: run npm run docs:generate-lookup and stage the result. The local reminder is soft and its index.ts match is shallow (a deeper src/kernel/occt/index.ts won't trip it), but CI's build job is the authoritative gate at any depth — it regenerates, prettier-normalizes, and git diff --exit-codes the file.

Before committing multi-file changes, prefer npm run validate (typecheck → lint → boundaries → format:check → changed tests) and read its output — do not commit on a partially green tree.

Bypass with git commit --no-verify only as a last resort; CI runs strictly more than the hook, so a bypassed failure just moves to the PR.

Note: scripts/pre-commit-help.sh and CONTRIBUTING.md still say coverage thresholds are enforced "at push time" / "in pre-commit hooks". That is stale — coverage thresholds run only in the main-branch-only, non-blocking coverage CI job. Locally they run only via npm run test:full.

commit-msg and pre-push

  • .husky/commit-msg runs commitlint --edit with @commitlint/config-conventional. A rejected message means the format is wrong, not the content — fix the type(scope): subject shape.
  • .husky/pre-push runs only npm run knip (unused-code detection, ~2 seconds). The full test suite is intentionally not re-run on push; CI's sharded test job is the full gate. If knip fails, either use the newly-flagged export or remove it — do not add it to knip.config.ts without cause.
  • If a push looked odd (interrupted terminal, unusual delay), verify it landed: git ls-remote origin <branch>.

Commits

Format: type(scope): subject. Types and examples: CONTRIBUTING.md "Commit Conventions" (feat, fix, docs, style, refactor, perf, test, chore).

The ! breaking-marker trap. In release-please-config.json, the root brepjs package excludes apps and every packages/* workspace, so satellite-only commits never touch the root version. Everything else at the repo root — including docs/, scripts/, and CI config — feeds the root release. Any commit with ! (or a BREAKING CHANGE: footer) touching those paths major-bumps the published brepjs library. Never put ! on site, docs, or tooling commits. When a change genuinely is breaking, confirm it touches the library surface (src/) before marking it. Full release pipeline: release-publishing skill.

Branches and worktrees

  • Branch naming: <type>/<kebab-description> where type is the conventional-commit type of the work — feat/judge-graded-reference-verdict, fix/memory-leak, docs/api-examples. (CONTRIBUTING.md shows an older feature/ prefix; current practice uses the commit type.)

  • Worktrees for parallel branches go under .worktrees/<branch> inside the repo — gitignored (.gitignore) and excluded from the root vitest suite (vitest.config.ts excludes .worktrees/** and .claude/worktrees/**, because a stale worktree copy without WASM set up would otherwise fail the root suite):

    git worktree add .worktrees/feat-my-change feat/my-change
    
  • Run gh pr merge from the main repo path, never from inside the worktree — deleting the branch while its worktree has it checked out fails.

  • After merge: git worktree remove .worktrees/<branch>, then in the main tree git checkout main && git pull. The repo deletes branches on merge, so the local branch goes [gone]; prune with git fetch --prune.

PR flow

  1. Push the branch and open a PR filling .github/pull_request_template.md (what/how-to-test/checklist).
  2. CI runs the jobs feeding ci-pass: typecheck, lint (eslint + format:check), quality (check:boundaries + check:patterns + knip), build (including the docs/function-lookup.md staleness diff), playground-build, per-package jobs (viewer/verify/sheetmetal/bim), voxel-wasm-rust, the 4-way-sharded test job, size, and benchmark. The coverage job runs on main only, continue-on-error, and is not part of ci-pass. CI failures: see the ci-triage skill.
  3. The benchmark job posts a PR comment comparing against main with a 25% regression threshold.
  4. Reviews: branch protection on main requires only the ci-pass status check — zero required approvals. Two AI reviewers (Greptile, configured in .greptile/config.json, and cubic) review every PR but are not required checks. Wait for both AI reviews to land before arming gh pr merge --auto — auto-merge armed early merges the moment ci-pass goes green, and real defects have been caught in reviews that arrived post-merge.
  5. Merge is squash-only; the squash commit title becomes the release-please changelog entry, so make the PR title a valid conventional commit.
  6. After merge: checkout main and pull immediately (the remote branch is auto-deleted).

Release PRs (release-please--* head refs) skip the code-CI path and are auto-merged by .github/workflows/release-please.yml with strict ordering: the root brepjs release PR merges first; leaf release PRs (cad/bim/sheetmetal) are held while root is open, because the node-workspace plugin pins leaves to the pending root version — merging a leaf early leaves main pinned to an unpublished version and breaks npm ci with ETARGET. brepjs-opencascade is permanently held for manual merge. Do not manually merge release PRs out of this order.

Symptom → cause → fix

SymptomCauseFix
Pre-commit fails with layer VIOLATIONUpward import across layersarchitecture-navigation skill; npm run check:boundaries for the full report
Pre-commit fails in check-patternsNew pattern violation in staged src/ fileSee quality-gates (fix vs. baseline)
Every open PR fails quality/check:patternsUnbaselined violation reached main (CI checks the PR merged with main)See quality-gates (baseline-bump-first recovery)
CI build fails on git diff --exit-code docs/function-lookup.mdStale generated lookup after *Fns.ts changeSee adding-operations (function-lookup gate)
Commit rejected: "subject may not be empty" / "type must be one of"Message not type(scope): subjectRewrite per CONTRIBUTING.md commit types
Published brepjs unexpectedly major-bumped! or BREAKING CHANGE: on a docs/tooling commitNever mark non-library commits breaking; docs/ is not in root exclude-paths
P1 review comment appears after mergeAuto-merge armed before AI reviews landedWait for Greptile + cubic before gh pr merge --auto
npm ci fails ETARGET after a release mergeLeaf release PR merged while root brepjs release was openMerge root first; let the workflow regenerate leaf PRs
git worktree remove or branch delete failsBranch checked out in a worktree / merge run from inside itOperate from the main repo path; remove the worktree first
Local branch shows [gone] after mergedelete_branch_on_merge removed the remote branchgit checkout main && git pull && git fetch --prune, delete the local branch

Additional resources

  • CLAUDE.md — "Git hooks" and "Commits" sections (concise summary of the above)
  • CONTRIBUTING.md — Development Workflow, Commit Conventions, Pull Request Process
  • .claude/commands/verify.md — wraps npm run validate + the function-lookup reminder
  • Sibling skills: architecture-navigation (boundary failures), writing-tests (test failures), quality-gates (lint/pattern/knip details), ci-triage (CI job debugging), release-publishing (release-please pipeline)

Signals

GitHub stars
101
Forks
9
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
git-pr-workflow
Source
github.com/andymai/brepjs