Skip to content

Add Husky pre-commit + pre-push hooks and a CI coverage threshold for tighter feedback #162

@dlrice

Description

@dlrice

Context
CI runs yarn test (lint + types + unit) on every push and PR, but there are no local hooks today. Lint, type, and test failures surface only after pushing — sometimes only after a PR review cycle reveals a red check. The feedback loop is workable but slow, and it's a routine source of "fix typo" / "fix lint" follow-up commits in PR history. Coverage is also tracked (vitest baseline captured in #130) but no threshold is enforced, so coverage can drift downward silently between refactors.

Task
Implement the mainstream three-tier check pattern. Pre-commit runs lint-staged on the staged files only (ESLint --fix + Prettier --write) so style and lint problems get caught before they enter history. Pre-push runs yarn test — the same lint + types + unit tests CI already runs, just shifted left so failures surface locally before the push lands. CI gains a coverage threshold check using vitest's built-in coverage.thresholds, with values seeded at the current baseline so coverage can only ratchet upward.

Scope:

  • Add husky and lint-staged as dev dependencies. Run yarn husky init to set up the .husky/ directory and the prepare script in package.json. The prepare script ensures hooks install automatically for anyone running yarn install.
  • Add .husky/pre-commit containing yarn lint-staged.
  • Add .husky/pre-push containing yarn test.
  • Add a lint-staged block to package.json covering *.{ts,tsx} (ESLint --fix + Prettier --write) and *.{md,json,yml,yaml} (Prettier --write only).
  • Add a coverage.thresholds block to the vitest config (lines, functions, branches, statements). Run yarn test:coverage once to capture the current baseline before setting thresholds 1–2 % below those numbers — the ratchet pattern. Coverage can only go up; if it drops, CI fails.
  • Add a yarn test:coverage script to package.json if it doesn't already exist.
  • Add a CI step in .github/workflows/test-and-deploy.yml that runs yarn test:coverage and fails on threshold violation — either a new job or an extra step in the existing test job.
  • Add a Hooks and feedback loop subsection to CONTRIBUTING.md describing the three tiers, the git push --no-verify escape hatch, and how to debug a hook that's gating a legitimate push.

Notes:
Husky is the de-facto JavaScript standard (~30k stars, stable API). simple-git-hooks is a lighter alternative if dependency footprint matters more than ecosystem familiarity — flag this in the PR description so reviewers can express a preference. Coverage thresholds belong in CI only: running coverage in pre-push roughly doubles test runtime, slow enough that contributors will start reaching for git push --no-verify and the hook becomes functionally dead. Threshold values must be the current baseline, not aspirational — aspirational thresholds either fail constantly (and get bypassed) or hide a smaller gap than reality (and don't catch regressions). If pre-push grows beyond ~10 seconds for typical changes, switch the pre-push hook to vitest --changed so only tests for touched files run locally; CI continues to run the full suite.

Acceptance hinges on four measurable things: pre-commit completes sub-second on a 1–3 file change; pre-push runs the full yarn test and blocks on failure; CI fails on a coverage drop below baseline with a clear message naming the offending threshold; the prepare script installs hooks automatically on a fresh clone (verify in a clean container or rm -rf node_modules .husky && yarn install).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions