Here's the full profiling report — no changes made.
# `pnpm check --no-test` performance report
## Baseline (warm, 12-core machine)
Full run: **13.0s**. Cold first run is ~worse (~20s) due to tsc/oxlint building their caches; all numbers below are warm/steady-state, which is what the agent loop actually hits.
Per-check, run in isolation:
| Check | Time | Notes |
| ------------------ | ---: | ----------------------------------------------------------- |
| **oxlint** | 6.7s | Rust binary, saturates all cores, **no file cache** |
| **type-check** | 6.4s | `generate:*` (2.8s) → `tsc --noEmit` (4.2s), **sequential** |
| lint-other | 5.1s | vitest process (4 spec files) |
| biome | 3.0s | |
| markdown-lint | 2.4s | |
| prettier | 2.0s | has `--cache` |
| doc-fence-check | 1.8s | |
| lint-documentation | 1.8s | vitest process (1 spec file) |
| test-schema | 1.8s | vitest-ish + validate-schema |
| ls-lint | 1.2s | |
| git-check | 1.1s | |
## The two bottlenecks
**1. CPU contention (~6s of waste).** All 11 checks launch simultaneously via `Promise.all` (`execution.ts:35`). The theoretical floor is the slowest single check (oxlint, 6.7s), but the actual run is 13s. Measured proof:
- oxlint + type-check *alone* in parallel → 8.3s (each individually 6.7/6.4, so contention already inflates them)
- The other 9 checks in parallel → 7.5s
oxlint is the primary offender: a native multithreaded binary that grabs all 12 cores, so everything scheduled alongside it (tsc, 3× vitest, biome, prettier) is starved.
**2. `generate:*` runs on the type-check critical path every time (2.8s, mostly waste).** `type-check` = `run-s 'generate:*' 'tsc --noEmit'` (package.json). `generate:imports` globs `lib/**`, hashes every manager, and does a runtime `import` of `lib/modules/manager/api.ts` — but `updateFile()` only *writes* generated files when content changed (`generate-imports.mjs:22-29`). So in the agent edit→check→edit loop, when managers/datasources/versioning/data aren't touched, those 2.8s produce zero file changes. Incremental tsc itself already has a hard ~4.2s floor even with a no-op change (1.6M `.cache/tsbuildinfo`, worsened by `checkJs: true` scanning all `.js`) — that part is largely intrinsic to tsc.
## Recommended changes, ranked
**A. Cache `generate:*` with a fingerprint early-exit** — *highest leverage for agents, ~2.8s off type-check.*
Add a guard at the top of `generate-imports.mjs`: hash/stat its inputs (`lib/**/*.ts`, `data/**`, and the list of existing `*.generated.ts`), store the fingerprint in `.cache/`, and `process.exit(0)` early if unchanged *and* all generated files exist. Since type-check runs generate→tsc sequentially, this drops type-check from 6.4s to ~4.2s. Risk: staleness if the fingerprint misses an input — mitigated by fingerprinting the same globs the script already reads. (Note: generated files aren't committed — 0 in git — so the cache must never skip when a generated file is missing.)
**B. Cut contention** — *reclaims part of the ~6s tax.*
Options, in order of simplicity:
- Cap oxlint with `--threads` (e.g. 4–6) so it stops starving the others. Counterintuitively can lower *wall-clock* of the whole batch even though oxlint alone gets slower.
- Or give the two tent-poles (oxlint, type-check) their own phase and run the 9 light checks in the other phase, rather than one flat `Promise.all` — schedule so two 6s jobs don't also fight nine smaller jobs. Needs per-machine tuning; the scheduler in `execution.ts` would need a concurrency/grouping concept it doesn't currently have.
**C. Merge the vitest checks** — *modest, low risk.*
`lint-documentation` + `lint-other` (+ the vitest part of `test-schema`) are 3 separate vitest boots, each spinning up its own worker pool. A single `vitest run test/docs test/other` shares startup and one pool. Saves ~1–2s and removes 2 competitors from the contention pool.
## Realistic target
A (generate cache) + B (contention) together should bring warm `--no-test` from **13s → ~7–8s**, floored by oxlint at 6.7s. Beyond that you'd need oxlint itself to get faster (no cache flag exists in 1.72; narrowing `ignorePatterns` or dropping the slower JS-resolving `import` plugin rules are the only levers, both with correctness tradeoffs).
Tell me which of A/B/C to implement and I'll do it.
One aside — `a.json` and `out.json` are untracked in your working tree (the latter is a GHSA security-advisory dump); unrelated to this work, just flagging so they don't get committed accidentally.
Given we're now running
pnpm check/pnpm check --no-testmuch more often - in agent loops or as humans - there may be some areas we can improve performance.Claude Opus 4.8 suggests: