Git hooks, commits, and PR flow
SkillProductivityThis 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.
No other account needed.
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
| Concern | Where it lives |
|---|---|
| Hook scripts | .husky/pre-commit, .husky/pre-push, .husky/commit-msg |
| lint-staged config | .lintstagedrc.json (NOT in package.json) |
| Commit message rules | commitlint.config.js (@commitlint/config-conventional) |
| Local full gate | npm run validate (scripts/validate-change.sh) |
| CI gate for merge | ci-pass job in .github/workflows/ci.yml — the only required status check |
| PR template | .github/pull_request_template.md |
| Merge method | Squash 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/**/*.tsgetseslint --fix+prettier --write+ the pattern checker (scripts/check-patterns.ts);tests/**/*.tsgets eslint + prettier only;*.config.tsand*.mdget prettier only. The pattern checker runs only onsrc/files.npm run typechecknpm run check:boundaries:staged— the staged variant (scripts/check-layer-boundaries.sh --staged), not plaincheck: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.tsfiles that may need updating.bash scripts/check-function-lookup.sh— fires when a staged path matchessrc/**Fns.ts(any depth) or anindex.tsat one directory level (src/index.tsorsrc/<dir>/index.ts) withoutdocs/function-lookup.md. Act on this one: runnpm run docs:generate-lookupand stage the result. The local reminder is soft and itsindex.tsmatch is shallow (a deepersrc/kernel/occt/index.tswon't trip it), but CI'sbuildjob is the authoritative gate at any depth — it regenerates, prettier-normalizes, andgit 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-msgrunscommitlint --editwith@commitlint/config-conventional. A rejected message means the format is wrong, not the content — fix thetype(scope): subjectshape..husky/pre-pushruns onlynpm run knip(unused-code detection, ~2 seconds). The full test suite is intentionally not re-run on push; CI's shardedtestjob is the full gate. If knip fails, either use the newly-flagged export or remove it — do not add it toknip.config.tswithout 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.mdshows an olderfeature/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.tsexcludes.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 mergefrom 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 treegit checkout main && git pull. The repo deletes branches on merge, so the local branch goes[gone]; prune withgit fetch --prune.
PR flow
- Push the branch and open a PR filling
.github/pull_request_template.md(what/how-to-test/checklist). - CI runs the jobs feeding
ci-pass: typecheck, lint (eslint +format:check), quality (check:boundaries+check:patterns+knip), build (including thedocs/function-lookup.mdstaleness diff), playground-build, per-package jobs (viewer/verify/sheetmetal/bim), voxel-wasm-rust, the 4-way-shardedtestjob, size, and benchmark. Thecoveragejob runs on main only,continue-on-error, and is not part ofci-pass. CI failures: see the ci-triage skill. - The benchmark job posts a PR comment comparing against main with a 25% regression threshold.
- Reviews: branch protection on
mainrequires only theci-passstatus 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 arminggh pr merge --auto— auto-merge armed early merges the momentci-passgoes green, and real defects have been caught in reviews that arrived post-merge. - Merge is squash-only; the squash commit title becomes the release-please changelog entry, so make the PR title a valid conventional commit.
- 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
| Symptom | Cause | Fix |
|---|---|---|
| Pre-commit fails with layer VIOLATION | Upward import across layers | architecture-navigation skill; npm run check:boundaries for the full report |
Pre-commit fails in check-patterns | New pattern violation in staged src/ file | See quality-gates (fix vs. baseline) |
Every open PR fails quality/check:patterns | Unbaselined 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.md | Stale generated lookup after *Fns.ts change | See adding-operations (function-lookup gate) |
| Commit rejected: "subject may not be empty" / "type must be one of" | Message not type(scope): subject | Rewrite per CONTRIBUTING.md commit types |
Published brepjs unexpectedly major-bumped | ! or BREAKING CHANGE: on a docs/tooling commit | Never mark non-library commits breaking; docs/ is not in root exclude-paths |
| P1 review comment appears after merge | Auto-merge armed before AI reviews landed | Wait for Greptile + cubic before gh pr merge --auto |
npm ci fails ETARGET after a release merge | Leaf release PR merged while root brepjs release was open | Merge root first; let the workflow regenerate leaf PRs |
git worktree remove or branch delete fails | Branch checked out in a worktree / merge run from inside it | Operate from the main repo path; remove the worktree first |
Local branch shows [gone] after merge | delete_branch_on_merge removed the remote branch | git 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— wrapsnpm 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