Contributing
How to set up the workspace, run the checks, and open a PR
Thanks for considering a contribution to VernLLM! This page covers workspace setup, expectations for tests, and the pre-PR checklist.
Setup
Fork the repo and branch off main, then install the workspace:
pnpm installCoding standard
VernLLM's job is to make the call reliable, not necessarily the app. Every module in
packages/vern-llm should stay inside that lane: normalize provider quirks,
handle failure modes explicitly, and hand the caller a predictable result.
It is not the place for app-level concerns (business logic, UI state,
persistence beyond the built-in cache). If a change starts pulling in those
concerns, it's probably the wrong framework.
Anything that comes from a call, meaning a timeout, a bad response, a circuit trip, a provider error, should come out the other end as an LLMError with a clear type and code if it is possible, never a bare exception or a raw provider error. This only applies to the call path though. An internal bug, like an out of range index inside vernLLM.ts, is a plain thrown error or assertion, not an LLMError, since it isn't something a caller should branch on.
Reliability code such as circuitBreaker.ts, rateLimit.ts, and retry.utils.ts should handle the weird inputs on purpose (zero, negative, Infinity, concurrent calls) instead of assuming the happy path. Explain unusual decisions with a short comment describing why, not a restatement of the code. Provider quirks stay inside src/adapters/*; everything else works against the shared LLMClient shape and stays provider agnostic. Tests run entirely on fakes and mocks, never real network calls.
Keep types precise. any is a final escape hatch, not a shortcut. Reach for it only when a provider SDK genuinely gives you no usable type (see the Gemini adapter's thinkingLevel), and when you do, leave a comment explaining why it's there instead of a narrower type.
Tests
Add or update tests alongside any change to packages/vern-llm:
tests/unit/for isolated behaviortests/integration/for cross-cutting workflows
No real API calls are made anywhere in the suite; everything runs against fakes and mocks. See Development for the full test layout.
New or changed lines in packages/vern-llm need at least 80% test coverage; CI enforces this through Codecov's patch check. Overall project coverage is also tracked and shouldn't regress by more than a small margin. Run pnpm run test:coverage locally to see where you stand before opening a PR.
Before a change is merged, expect a personal canary test on a simple real app, like a small chat CLI, to confirm the change behaves as intended outside the test suite.
Before opening a PR
Every PR runs the following checks in CI. Run the equivalent commands locally first so nothing surprises you:
| Check | Workflow | Local command |
|---|---|---|
| Lint | lint.yml | pnpm run lint |
| Typecheck source | typecheck.yml | pnpm run typecheck:package |
| Typecheck tests | typecheck.yml | pnpm run typecheck:test |
| Unit tests + coverage | test-unit.yml | pnpm run test:unit:coverage |
| Integration tests + coverage | test-integration.yml | pnpm run test:int:coverage |
| Build, bundle size, smoke test | build-checks.yml | pnpm run build:package && pnpm --filter vern-llm run size && pnpm --filter vern-llm run smoke-test |
| Changeset present | changeset.yml | pnpm changeset status --since=origin/main |
Docs build (only if apps/docs/** changed) | docs-build.yml | pnpm --filter vern-llm-docs run lint && pnpm --filter vern-llm-docs run types:check && pnpm --filter vern-llm-docs run build |
| Coverage thresholds | Codecov | pnpm run test:coverage |
| Static analysis | codeql.yml | runs on GitHub only |
The quickest local pass before pushing:
pnpm run lint
pnpm run typecheck
pnpm run typecheck:test
pnpm run test:unit:coverage
pnpm run test:int:coverage
pnpm run build:packagebuild-checks.yml also verifies peer dependencies (pnpm peers check), checks the published bundle stays under the configured size-limit, and runs a smoke test against the built output, so if you're touching packages/vern-llm's public surface or its dependencies, run pnpm run build:package and pnpm --filter vern-llm run size before pushing.
changeset.yml blocks any PR touching packages/vern-llm that doesn't include a changeset (see below). docs-build.yml only runs when apps/docs/** or the lockfile changes, so it won't fire on pure library changes.
Changesets
Record your change with:
pnpm run changesetCI checks every PR touching packages/vern-llm for a changeset. If your change doesn't need a release, run pnpm changeset add --empty instead of skipping it.
This drives versioned release notes. Releases themselves are automated through .github/workflows/version.yml and .github/workflows/publish.yml, so there are no manual version bumps or publishing steps.
Code of Conduct
Participation in this project is governed by our Code of Conduct.
Security issues
Please don't open a public issue for a security vulnerability. See Security for how to report one privately.