📝 Document the three-tier check system (pre-commit / check / CI)
Adds a 'CHECK TIERS' subsection under the existing HOOKS section in project-specs.md that explicitly documents: - Where each check runs (pre-commit, npm run check, CI build, CI publish) - A summary table mapping scripts to execution contexts - The rationale for each split (speed, scope, side effects) - Why check:knip is not in pre-commit (~4s, whole project) - Why check:outdated is not in pre-commit (network dep, advisory) - Why publish:* is not in check (validates dist/, needs build) - Cross-reference from the CI/CD section back to the tiers table The split is a deliberate design choice: pre-commit is the fast safety net for what you just changed, npm run check is the full local audit, CI is authoritative. Documenting it makes the rationale explicit and stops anyone from re-adding the slower checks to the hook.
This commit is contained in:
1 parent
34e9b52ee8
commit
74e4538094
1 file changed
+67
-1
+67
-1
@@ -298,6 +298,66 @@ script does not belong in the standard pipeline.
|
|||||||
- Lefthook runs the relevant `check:*` scripts on staged files in
|
- Lefthook runs the relevant `check:*` scripts on staged files in
|
||||||
parallel for pre-commit. See `lefthook.yml`.
|
parallel for pre-commit. See `lefthook.yml`.
|
||||||
|
|
||||||
|
### CHECK TIERS — what runs where and why
|
||||||
|
|
||||||
|
The `check:*` scripts are split across three execution contexts
|
||||||
|
based on **speed**, **scope** (staged files vs whole project), and
|
||||||
|
**side effects** (network, registry queries). The split is a
|
||||||
|
deliberate design choice; the rule of thumb is:
|
||||||
|
|
||||||
|
- **pre-commit hook**: fast, file-scoped, offline, deterministic.
|
||||||
|
Catches what _you just changed_ in under a second or two.
|
||||||
|
- **`npm run check`**: full project audit. Slower, may query the
|
||||||
|
network, runs everything that's not in pre-commit.
|
||||||
|
- **CI build job**: authoritative. Runs `npm run check` and
|
||||||
|
`npm run test:ci` on every push and PR. The ground truth —
|
||||||
|
if CI passes, the codebase is clean.
|
||||||
|
|
||||||
|
| Script | pre-commit | `npm run check` | CI build | CI publish |
|
||||||
|
| ----------------- | :--------: | :-------------: | :------: | :--------: |
|
||||||
|
| `check:tsc` | ✅ | ✅ | ✅ | — |
|
||||||
|
| `check:oxlint` | ✅ | ✅ | ✅ | — |
|
||||||
|
| `check:oxfmt` | ✅ | ✅ | ✅ | — |
|
||||||
|
| `check:cspell` | ✅ | ✅ | ✅ | — |
|
||||||
|
| `check:knip` | — | ✅ | ✅ | — |
|
||||||
|
| `check:outdated` | — | ✅ | ✅ | — |
|
||||||
|
| `publish:publint` | — | — | — | ✅ |
|
||||||
|
| `publish:attw` | — | — | — | ✅ |
|
||||||
|
|
||||||
|
#### Why these splits?
|
||||||
|
|
||||||
|
- **`check:tsc`, `check:oxlint`, `check:oxfmt`, `check:cspell`**
|
||||||
|
are in pre-commit because they are fast (~0.2–0.5s each), fully
|
||||||
|
offline, and naturally scope to staged files via the
|
||||||
|
`LEFTHOOK_FILES` env var convention. They give instant feedback
|
||||||
|
on what you typed.
|
||||||
|
|
||||||
|
- **`check:knip` is NOT in pre-commit** because it scans the whole
|
||||||
|
project (not staged files) and takes ~4s. Adding it would
|
||||||
|
roughly triple pre-commit time. It's still fast enough to run
|
||||||
|
locally before pushing, and CI catches it regardless.
|
||||||
|
|
||||||
|
- **`check:outdated` is NOT in pre-commit** because (a) it queries
|
||||||
|
the npm registry (~6.5s) which means network dependency in a
|
||||||
|
hook, (b) the result is advisory, not a pass/fail correctness
|
||||||
|
check, and (c) the same network call in CI or locally on demand
|
||||||
|
gives the same answer. It belongs in scheduled or on-demand
|
||||||
|
runs, not in a blocking hook.
|
||||||
|
|
||||||
|
- **`publish:publint` and `publish:attw` are NOT in `check`** —
|
||||||
|
they belong to the `publish:` prefix (see above) because they
|
||||||
|
validate the _publishable artifact_ (`dist/`) and require a
|
||||||
|
fresh build. Running them on every commit would be wasteful
|
||||||
|
(and slow, `npm pack` is involved). They run in the CI
|
||||||
|
`publish` job immediately before `npm publish`.
|
||||||
|
|
||||||
|
#### What to do before pushing
|
||||||
|
|
||||||
|
Run `npm run check` locally. If you only trust pre-commit + CI,
|
||||||
|
know that anything `check:knip` or `check:outdated` would catch
|
||||||
|
will be caught by CI on the PR before merge (assuming branch
|
||||||
|
protection is configured to require CI to pass).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Repository & CI/CD
|
## 6. Repository & CI/CD
|
||||||
@@ -309,13 +369,19 @@ script does not belong in the standard pipeline.
|
|||||||
`actions/setup-node@v4` (with `node-version-file: .node-version`,
|
`actions/setup-node@v4` (with `node-version-file: .node-version`,
|
||||||
`cache: npm`), `npm ci`, `npm run build`, `npm run check`,
|
`cache: npm`), `npm ci`, `npm run build`, `npm run check`,
|
||||||
`npm run test:ci`, then upload `coverage/` as an artifact.
|
`npm run test:ci`, then upload `coverage/` as an artifact.
|
||||||
|
Runs the **full `check` chain** (all 6 `check:*` scripts) and
|
||||||
|
the test suite with coverage. This is the authoritative gate —
|
||||||
|
if it passes, the codebase is clean. See §5 ("CHECK TIERS")
|
||||||
|
for which checks run here vs. locally vs. pre-commit.
|
||||||
- `publish` job: only on `refs/tags/*`, depends on `build`. Steps:
|
- `publish` job: only on `refs/tags/*`, depends on `build`. Steps:
|
||||||
`actions/checkout@v4`, `actions/setup-node@v4` (with
|
`actions/checkout@v4`, `actions/setup-node@v4` (with
|
||||||
`node-version-file: .node-version` and `registry-url`),
|
`node-version-file: .node-version` and `registry-url`),
|
||||||
`npm ci`, `npm run build`, `npm run publish:publint` (see
|
`npm ci`, `npm run build`, `npm run publish:publint` (see
|
||||||
§5 for why this is `publish:` and not `check:`),
|
§5 for why this is `publish:` and not `check:`),
|
||||||
`npm run publish:attw`, and `npm publish --access public`
|
`npm run publish:attw`, and `npm publish --access public`
|
||||||
with `NODE_AUTH_TOKEN` from secrets.
|
with `NODE_AUTH_TOKEN` from secrets. Runs the **publish-tier
|
||||||
|
checks** that validate the publishable artifact before
|
||||||
|
publishing to npm.
|
||||||
|
|
||||||
## 7. Versioning & Publishing
|
## 7. Versioning & Publishing
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user